diff --git a/.nuget/packages.config b/.nuget/packages.config index be56510f9a04dd2a048d9b388e0265f0aebaa90e..055afc39827712ac9eaec92fe7d31f30f20928d3 100644 --- a/.nuget/packages.config +++ b/.nuget/packages.config @@ -10,4 +10,5 @@ + diff --git a/build/Targets/VSL.Imports.targets b/build/Targets/VSL.Imports.targets index 0b4c4999d5bcf362fa3556de55f25cc632bc7a45..339e2a2ae61251f2307f1105628744139e9dd59d 100644 --- a/build/Targets/VSL.Imports.targets +++ b/build/Targets/VSL.Imports.targets @@ -55,6 +55,17 @@ + + + $(FinalDefineConstants.Replace('"', '')) + + $(BuildDependsOn); diff --git a/cibuild.sh b/cibuild.sh index 8711c207ecec6ae3110d20599901923d47ff2901..647e7b3e709d4dd846f21d2875b500d95e0df87d 100755 --- a/cibuild.sh +++ b/cibuild.sh @@ -52,7 +52,7 @@ done run_xbuild() { - xbuild /v:m /p:SignAssembly=false /p:DebugSymbols=false "$@" + mono packages/Microsoft.Build.Mono.Debug.14.1.0.0-prerelease/lib/MSBuild.exe /v:m /p:SignAssembly=false /p:DebugSymbols=false "$@" if [ $? -ne 0 ]; then echo Compilation failed exit 1 @@ -115,7 +115,7 @@ save_toolset() clean_roslyn() { echo Cleaning the enlistment - xbuild /v:m /t:Clean build/Toolset.sln /p:Configuration=$BUILD_CONFIGURATION + mono packages/Microsoft.Build.Mono.Debug.14.1.0.0-prerelease/lib/MSBuild.exe /v:m /t:Clean build/Toolset.sln /p:Configuration=$BUILD_CONFIGURATION rm -rf Binaries/$BUILD_CONFIGURATION } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs index a4a438bf8d4bec1ad9305f590175b9414e161dee..5d697aef15bf70e157036f3080da11f0b584b272 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs @@ -238,7 +238,16 @@ private void CheckBinaryOperator(BoundBinaryOperator node) private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) { - CheckForBitwiseOrSignExtend(node, node.Operator.Kind, node.Left, node.Right); + BoundExpression left = node.Left; + + if (!node.Operator.Kind.IsDynamic() && !node.LeftConversion.IsIdentity && node.LeftConversion.Exists) + { + // Need to represent the implicit conversion as a node in order to be able to produce correct diagnostics. + left = new BoundConversion(left.Syntax, left, node.LeftConversion, node.Operator.Kind.IsChecked(), + explicitCastInCode: false, constantValueOpt: null, type: node.Operator.LeftType); + } + + CheckForBitwiseOrSignExtend(node, node.Operator.Kind, left, node.Right); CheckLiftedCompoundAssignment(node); if (_inExpressionLambda) diff --git a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj index 352f200506d47404b114590a2ec1eb109729d6e9..3bf0586d1fe28d510d3d8b5b81c5e455dbd8997e 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj +++ b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj @@ -53,8 +53,12 @@ - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTestResources.resx b/src/Compilers/CSharp/Test/CommandLine/CommandLineTestResources.resx index 5548f2e7ba2beb9dbd66bfd952abace0fd347741..2315d56ec5c53e8687e35115dcbdcbe04286a267 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTestResources.resx +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTestResources.resx @@ -119,6 +119,6 @@ - ..\..\csc\csc.rsp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ../../csc/csc.rsp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 408e96b29c61cf910bdbc3b7ea1f360bb6704820..7011cdfdaf6d689d3402636e16d95be32db963f4 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -52,9 +52,15 @@ - - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\..\..\..\packages\Microsoft.DiaSymReader.1.0.5\lib\net45\Microsoft.DiaSymReader.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll @@ -206,4 +212,4 @@ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 1ff0ba54003e2f9f936d6612d6f25c4e60a3ae37..f4899206a24e05f41cca2e2896626c9d4c152790 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -48,7 +48,9 @@ - + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index 16065dd8d5f907b68ffc8e2c423da80d2de7921a..e8cd779bb5e18d81844b2f9cdbac05b15cafaa2f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs @@ -1105,6 +1105,20 @@ void Method() TestGenericNameCore(source, new AnalyzerWithNoActions(), new CSharpGenericNameAnalyzer()); } + [Fact, WorkItem(4055, "https://github.com/dotnet/roslyn/issues/4055")] + public void TestAnalyzerWithNoSupportedDiagnostics() + { + var source = @" +class MyClass +{ +}"; + // Ensure that adding a dummy analyzer with no supported diagnostics doesn't bring down entire analysis. + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerWithNoSupportedDiagnostics() }; + CreateCompilationWithMscorlib45(source) + .VerifyDiagnostics() + .VerifyAnalyzerDiagnostics(analyzers); + } + private static void TestEffectiveSeverity( DiagnosticSeverity defaultSeverity, ReportDiagnostic expectedEffectiveSeverity, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs index 1af7d96bc178e1f42cedc2d1c49b558773cab407..27d6b222b643edf8ae2c639444bf0cbac149e6cb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs @@ -8728,5 +8728,26 @@ operator int (IntHolder ih) operator IntHolder(int i) 'y' is 5"); } + + [Fact, WorkItem(4027, "https://github.com/dotnet/roslyn/issues/4027")] + public void NotSignExtendedOperand() + { + string source = @" +class MainClass +{ + public static void Main () + { + short a = 0; + int b = 0; + a |= (short)b; + a = (short)(a | (short)b); + } +} +"; + + var compilation = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll); + + compilation.VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj index adc4bdfef5d04a368d24691fd10ea9110d39788e..683e78cbfea589ffe010a3ac902f445cbb38f7d8 100644 --- a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj +++ b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj @@ -52,9 +52,15 @@ False ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.1.0-beta1-20150716-08\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll - - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\..\..\..\packages\Microsoft.DiaSymReader.1.0.5\lib\net45\Microsoft.DiaSymReader.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj index 065203645410506cfec94eaec267f54e74d6f1c5..82a7d226a64f025516b621dec5d8dcc18584c549 100644 --- a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj +++ b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj @@ -22,7 +22,9 @@ - + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj index 0aff7e6c42c62bdb37caed05cd2d718fd34e56cb..9f010880f136e38603e8715e2097fb331a99596c 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj @@ -70,8 +70,12 @@ - - + + ..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll @@ -174,10 +178,10 @@ - - - - + + + + @@ -187,4 +191,4 @@ - \ No newline at end of file + diff --git a/src/Compilers/Core/CodeAnalysisTest/Resources.resx b/src/Compilers/Core/CodeAnalysisTest/Resources.resx index cccb42b13904444576b512ef20c9c26ff4110ebf..092240b59238d75c309aa65bc3c0c47507931ad1 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Resources.resx +++ b/src/Compilers/Core/CodeAnalysisTest/Resources.resx @@ -121,15 +121,15 @@ - Resources\default.win32manifest;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Resources/default.win32manifest;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Resources\VerResourceBuiltByRC.RES;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Resources/VerResourceBuiltByRC.RES;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Resources\nativeWithStringIDsAndTypesAndIntTypes.res;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Resources/nativeWithStringIDsAndTypesAndIntTypes.res;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Resources\Roslyn.ico.blah;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Resources/Roslyn.ico.blah;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 6782537f66e3c49a9e3c4e9633c3d79f74b0a7bd..6139e70ed3b7a4f51f5b26fa176b71b3efea59f3 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -308,6 +308,45 @@ private ImmutableArray CalculateAssemblyReferenceAli #region Synthesized Members +#if DEBUG + /// + /// The queue of synthesized members of a type should be deterministic, as the members are + /// emitted in the order in which they are added to the queue. Therefore for debug purposes + /// we have a custom version of ConcurrentQueue that detects attempted concurrent adds. + /// + /// + private class ConcurrentQueue : System.Collections.Concurrent.ConcurrentQueue + { + // A count of the number of concurrent queue operations in progress. Should always be zero or one, + // as synthetic members should be added by the compiler to a given type in a well-defined sequential + // order. + int queueing; + + // A short delay to increase the chance that concurrent Enqueue operation will be diagnosed. + static readonly TimeSpan shortDelay = new TimeSpan(2); + + /// + /// Adds an object to the end of the ConcurrentQueue. + /// + /// + /// The object to add to the end of the ConcurrentQueue. + /// The value can be a null reference for reference types. + /// + public new void Enqueue(T item) + { + if (Interlocked.Increment(ref queueing) != 1) + { + throw new System.InvalidOperationException("Concurrent use of " + nameof(SynthesizedDefinitions)); + } + base.Enqueue(item); + // To increase the chance of catching concurrency issues, we add a delay to each queued item + // so that another thread has a chance to add at the same time. + System.Threading.Tasks.Task.Delay(shortDelay).Wait(); + Interlocked.Decrement(ref queueing); + } + } +#endif + /// /// Captures the set of synthesized definitions that should be added to a type /// during emit process. diff --git a/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj b/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj index c1c233e969cf649fa45f37b8cacad53c4f14434f..15a0f4fbde31b19dc7eae8c3de864a362edf6ac0 100644 --- a/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj +++ b/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj @@ -27,8 +27,12 @@ - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/VisualBasic/Portable/Errors/LocalizableErrorArgument.vb b/src/Compilers/VisualBasic/Portable/Errors/LocalizableErrorArgument.vb index 586304f1b621cbefb0309d23d8256fe396649adc..26e5aae000035cac5efd83bd7ee073116926417a 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/LocalizableErrorArgument.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/LocalizableErrorArgument.vb @@ -21,7 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic '''Creates a string representing the unformatted LocalizableErrorArgument instance. ''' Public Overrides Function ToString() As String - Return ToString(Nothing) + Return ToString_IFormattable(Nothing, Nothing) End Function ''' diff --git a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj index 9cdee313b9ed373677758f6c82500bedbc09bfdc..f64d32968d9e4bf422141a6dd1727e53c74f4e38 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj +++ b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj @@ -58,8 +58,12 @@ False ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.1.0-beta1-20150716-08\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTestResources.resx b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTestResources.resx index a6bf0ec678094b3d3a8e8a9de090567e8cb2fdc3..53f9b20569841116d12f321453dfca99d0ceca37 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTestResources.resx +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTestResources.resx @@ -119,6 +119,6 @@ - ..\..\vbc\vbc.rsp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ../../vbc/vbc.rsp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index 50380e3911867f16ab116b4a660bbe2d470c224c..08ba6bef891cad6ee2650b4f496c02314e46f80e 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -46,9 +46,15 @@ - - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\..\..\..\packages\Microsoft.DiaSymReader.1.0.5\lib\net45\Microsoft.DiaSymReader.dll + true diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index e13951834685169053d1cbdead34389ff00fe0fa..6ae88d3dbf5f892377ca45b4ae406cac030f63b3 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -21,6 +21,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class CompilationAPITests Inherits BasicTestBase + + Public Sub LocalizableErrorArgumentToStringDoesntStackOverflow() + ' Error ID is arbitrary + Dim arg = New LocalizableErrorArgument(ERRID.IDS_ProjectSettingsLocationName) + Assert.NotNull(arg.ToString()) + End Sub + diff --git a/src/Compilers/VisualBasic/Test/Semantic/Resource.resx b/src/Compilers/VisualBasic/Test/Semantic/Resource.resx index 27a695e1bf235b1acecf596fe006461fa677cc34..f332c999c9c04bb6ee786509ce6373060ebbc5dc 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Resource.resx +++ b/src/Compilers/VisualBasic/Test/Semantic/Resource.resx @@ -119,37 +119,37 @@ - Semantics\Async_Overload_Change_3.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/Async_Overload_Change_3.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestBaseline1.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestBaseline1.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestBaseline2.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestBaseline2.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestBaseline3.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestBaseline3.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestBaseline4.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestBaseline4.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestBaseline5.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestBaseline5.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestSource1.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestSource1.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestSource2.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestSource2.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestSource3.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestSource3.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestSource4.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestSource4.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\BinaryOperatorsTestSource5.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/BinaryOperatorsTestSource5.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 <Global> @@ -182,21 +182,21 @@ </Global> - Semantics\LongTypeNameNative.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/LongTypeNameNative.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\LongTypeName.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/LongTypeName.vb.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\OverloadResolutionTestSource.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/OverloadResolutionTestSource.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Semantics\PrintResultTestSource.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Semantics/PrintResultTestSource.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - Binding\T_1247520.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + Binding/T_1247520.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - Binding\T_68086.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + Binding/T_68086.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj index fa99c04ac914d999060562ed6053ab2a0db0c9cb..f6152ad35e74420e75527e97b495c9e168c17d4a 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj @@ -46,8 +46,12 @@ - - + + ..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj index 33160ebab45d12436a78199e827f05d6124a4696..ef3343e61dc1c5b23078b3dbfdca38f690601636 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj @@ -20,7 +20,9 @@ - + + ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + diff --git a/src/Compilers/VisualBasic/Test/Syntax/Resource.resx b/src/Compilers/VisualBasic/Test/Syntax/Resource.resx index be56e337fe1818903a8bc4a9ec204ee630b4f4c9..512e357af6e427c40cc0c9ef57c997e0695da6f5 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Resource.resx +++ b/src/Compilers/VisualBasic/Test/Syntax/Resource.resx @@ -121,6 +121,6 @@ - Resources\VBAllInOne.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Resources/VBAllInOne.vb;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 diff --git a/src/Debugging/Microsoft.DiaSymReader.PortablePdb/Microsoft.DiaSymReader.PortablePdb.csproj b/src/Debugging/Microsoft.DiaSymReader.PortablePdb/Microsoft.DiaSymReader.PortablePdb.csproj index 927f3c9be52883ea29913976e20aac511740dc6b..eb24fc9bb06c0af067e4efa342ae6e7ad1b32d7a 100644 --- a/src/Debugging/Microsoft.DiaSymReader.PortablePdb/Microsoft.DiaSymReader.PortablePdb.csproj +++ b/src/Debugging/Microsoft.DiaSymReader.PortablePdb/Microsoft.DiaSymReader.PortablePdb.csproj @@ -29,27 +29,21 @@ False ..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - - + + ..\..\..\packages\Microsoft.DiaSymReader.1.0.5\lib\net45\Microsoft.DiaSymReader.dll + + + ..\..\..\packages\System.IO.FileSystem.4.0.0-beta-22816\lib\portable-wpa81+wp80+win80+net45+aspnetcore50\System.IO.FileSystem.dll False - + + ..\..\..\packages\System.IO.FileSystem.Primitives.4.0.0-beta-22816\lib\portable-wpa81+wp80+win80+net45+aspnetcore50\System.IO.FileSystem.Primitives.dll False False - - - PreserveNewest - false - - - PreserveNewest - false - - @@ -85,4 +79,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 2aafbb6a0b2cb7767e74b5157c1e903954924d14..aaa94efb96207eb8d14e3b96dfc479680998370e 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -794,6 +794,8 @@ class AnonymousFunctions Using workspace = TestWorkspaceFactory.CreateWorkspace(test) Dim project = workspace.CurrentSolution.Projects.Single() + + ' Test partial type diagnostic reported on user file. Dim analyzer = New PartialTypeDiagnosticAnalyzer(indexOfDeclToReportDiagnostic:=1) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) @@ -813,6 +815,52 @@ class AnonymousFunctions End Using End Sub + + Public Sub TestDiagnosticsReportedOnAllPartialDefinitions() + Dim test = + + + public partial class Foo { } + + + public partial class Foo { } + + + + + Using workspace = TestWorkspaceFactory.CreateWorkspace(test) + Dim project = workspace.CurrentSolution.Projects.Single() + + ' Test partial type diagnostic reported on all source files. + Dim analyzer = New PartialTypeDiagnosticAnalyzer(indexOfDeclToReportDiagnostic:=Nothing) + Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) + project = project.AddAnalyzerReference(analyzerReference) + + Dim diagnosticService = New TestDiagnosticAnalyzerService() + + Dim descriptorsMap = diagnosticService.GetDiagnosticDescriptors(project) + Assert.Equal(1, descriptorsMap.Count) + + ' Verify project diagnostics contains diagnostics reported on both partial definitions. + Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnostics = diagnosticService.GetDiagnosticsAsync(project.Solution, project.Id).WaitAndGetResult(CancellationToken.None) + Assert.Equal(2, diagnostics.Count()) + Dim file1HasDiag = False, file2HasDiag = False + For Each diagnostic In diagnostics + Assert.Equal(PartialTypeDiagnosticAnalyzer.DiagDescriptor.Id, diagnostic.Id) + Dim document = project.GetDocument(diagnostic.DocumentId) + If document.Name = "Test1.cs" Then + file1HasDiag = True + ElseIf document.Name = "Test2.cs" + file2HasDiag = True + End If + Next + + Assert.True(file1HasDiag) + Assert.True(file2HasDiag) + End Using + End Sub + Private Sub TestCodeBlockAnalyzersForExpressionBody() Dim test = @@ -1228,8 +1276,8 @@ public class B Private Class PartialTypeDiagnosticAnalyzer Inherits DiagnosticAnalyzer - Private ReadOnly _indexOfDeclToReportDiagnostic As Integer - Public Sub New(indexOfDeclToReportDiagnostic As Integer) + Private ReadOnly _indexOfDeclToReportDiagnostic As Integer? + Public Sub New(indexOfDeclToReportDiagnostic As Integer?) Me._indexOfDeclToReportDiagnostic = indexOfDeclToReportDiagnostic End Sub @@ -1246,7 +1294,15 @@ public class B End Sub Private Sub AnalyzeSymbol(context As SymbolAnalysisContext) - context.ReportDiagnostic(Diagnostic.Create(DiagDescriptor, context.Symbol.Locations.ElementAt(Me._indexOfDeclToReportDiagnostic))) + Dim index = 0 + For Each location In context.Symbol.Locations + If Not Me._indexOfDeclToReportDiagnostic.HasValue OrElse Me._indexOfDeclToReportDiagnostic.Value = index Then + context.ReportDiagnostic(Diagnostic.Create(DiagDescriptor, location)) + End If + + index += 1 + Next + End Sub End Class @@ -1685,5 +1741,43 @@ namespace ConsoleApplication1 ' See https//github.com/dotnet/roslyn/issues/2980 for details. TestGenericNameCore(test, CSharpGenericNameAnalyzer.Message, CSharpGenericNameAnalyzer.DiagnosticId, New AnalyzerWithNoActions, New CSharpGenericNameAnalyzer) End Sub + + + Public Sub TestAnalyzerWithNoSupportedDiagnostics() + Dim test = + + + + + + + ' Ensure that adding a dummy analyzer with no supported diagnostics doesn't bring down entire analysis. + Using workspace = TestWorkspaceFactory.CreateWorkspace(test) + Dim project = workspace.CurrentSolution.Projects.Single() + + ' Add analyzer + Dim analyzer = New AnalyzerWithNoSupportedDiagnostics() + Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) + project = project.AddAnalyzerReference(analyzerReference) + + Dim diagnosticService = New TestDiagnosticAnalyzerService() + Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + + ' Verify available diagnostic descriptors/analyzers + Dim descriptorsMap = diagnosticService.GetDiagnosticDescriptors(project) + Assert.Equal(1, descriptorsMap.Count) + Assert.Equal(0, descriptorsMap.First().Value.Length) + + Dim document = project.Documents.Single() + Dim diagnostics = diagnosticService.GetDiagnosticsForSpanAsync(document, + document.GetSyntaxRootAsync().WaitAndGetResult(CancellationToken.None).FullSpan, + CancellationToken.None).WaitAndGetResult(CancellationToken.None) + + Assert.Equal(0, diagnostics.Count()) + End Using + End Sub End Class End Namespace diff --git a/src/Features/Core/Completion/CompletionOptions.cs b/src/Features/Core/Completion/CompletionOptions.cs index 971ab21c073f5c32cf8e12f3f14a4c000a71de0f..3bffded10c939765cecb321b1ca09ac57d6bca60 100644 --- a/src/Features/Core/Completion/CompletionOptions.cs +++ b/src/Features/Core/Completion/CompletionOptions.cs @@ -8,16 +8,9 @@ internal static class CompletionOptions { public const string FeatureName = "Completion"; - [ExportOption] public static readonly PerLanguageOption HideAdvancedMembers = new PerLanguageOption(FeatureName, "HideAdvancedMembers", defaultValue: false); - - [ExportOption] public static readonly PerLanguageOption IncludeKeywords = new PerLanguageOption(FeatureName, "IncludeKeywords", defaultValue: true); - - [ExportOption] public static readonly PerLanguageOption TriggerOnTyping = new PerLanguageOption(FeatureName, "TriggerOnTyping", defaultValue: true); - - [ExportOption] public static readonly PerLanguageOption TriggerOnTypingLetters = new PerLanguageOption(FeatureName, "TriggerOnTypingLetters", defaultValue: true); } } diff --git a/src/Features/Core/Completion/CompletionOptionsProvider.cs b/src/Features/Core/Completion/CompletionOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..771c41b9c3fe80b917999be59ce88b4c5b92361c --- /dev/null +++ b/src/Features/Core/Completion/CompletionOptionsProvider.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Completion +{ + [ExportOptionProvider, Shared] + internal class CompletionOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + CompletionOptions.HideAdvancedMembers, + CompletionOptions.IncludeKeywords, + CompletionOptions.TriggerOnTyping, + CompletionOptions.TriggerOnTypingLetters + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/Diagnostics/InternalDiagnosticsOptions.cs b/src/Features/Core/Diagnostics/InternalDiagnosticsOptions.cs index 675dd1765572ae96c183b8554a6bc90bb185c14e..e6d1396de5329d89e1f1173df92b75105665f3e5 100644 --- a/src/Features/Core/Diagnostics/InternalDiagnosticsOptions.cs +++ b/src/Features/Core/Diagnostics/InternalDiagnosticsOptions.cs @@ -8,28 +8,13 @@ internal static class InternalDiagnosticsOptions { public const string OptionName = "InternalDiagnosticsOptions"; - [ExportOption] public static readonly Option BlueSquiggleForBuildDiagnostic = new Option(OptionName, "Blue Squiggle For Build Diagnostic", defaultValue: false); - - [ExportOption] public static readonly Option UseDiagnosticEngineV2 = new Option(OptionName, "Use Diagnostic Engine V2", defaultValue: false); - - [ExportOption] public static readonly Option CompilationEndCodeFix = new Option(OptionName, "Enable Compilation End Code Fix", defaultValue: true); - - [ExportOption] public static readonly Option UseCompilationEndCodeFixHeuristic = new Option(OptionName, "Enable Compilation End Code Fix With Heuristic", defaultValue: true); - - [ExportOption] public static readonly Option BuildErrorIsTheGod = new Option(OptionName, "Make build errors to take over everything", defaultValue: false); - - [ExportOption] public static readonly Option ClearLiveErrorsForProjectBuilt = new Option(OptionName, "Clear all live errors of projects that got built", defaultValue: false); - - [ExportOption] public static readonly Option PreferLiveErrorsOnOpenedFiles = new Option(OptionName, "Live errors will be preferred over errors from build on opened files from same analyzer", defaultValue: true); - - [ExportOption] public static readonly Option PreferBuildErrorsOverLiveErrors = new Option(OptionName, "Errors from build will be preferred over live errors from same analyzer", defaultValue: true); } } diff --git a/src/Features/Core/Diagnostics/InternalDiagnosticsOptionsProvider.cs b/src/Features/Core/Diagnostics/InternalDiagnosticsOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..a783f7cbe8464468fdafaeaad74bcc94d24a4b81 --- /dev/null +++ b/src/Features/Core/Diagnostics/InternalDiagnosticsOptionsProvider.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + [ExportOptionProvider, Shared] + internal class InternalDiagnosticsOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + InternalDiagnosticsOptions.BlueSquiggleForBuildDiagnostic, + InternalDiagnosticsOptions.UseDiagnosticEngineV2, + InternalDiagnosticsOptions.CompilationEndCodeFix, + InternalDiagnosticsOptions.UseCompilationEndCodeFixHeuristic, + InternalDiagnosticsOptions.BuildErrorIsTheGod, + InternalDiagnosticsOptions.ClearLiveErrorsForProjectBuilt, + InternalDiagnosticsOptions.PreferLiveErrorsOnOpenedFiles, + InternalDiagnosticsOptions.PreferBuildErrorsOverLiveErrors + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/ExtractMethod/ExtractMethodOptions.cs b/src/Features/Core/ExtractMethod/ExtractMethodOptions.cs index ec863d4244c6e9c1f6fe4c436f1728d767e24ea8..05ba16f83699865fcdac9cb8e8af173e24a125e4 100644 --- a/src/Features/Core/ExtractMethod/ExtractMethodOptions.cs +++ b/src/Features/Core/ExtractMethod/ExtractMethodOptions.cs @@ -8,13 +8,8 @@ internal static class ExtractMethodOptions { public const string FeatureName = "ExtractMethod"; - [ExportOption] public static readonly PerLanguageOption AllowBestEffort = new PerLanguageOption(FeatureName, "Allow Best Effort", defaultValue: false); - - [ExportOption] public static readonly PerLanguageOption DontPutOutOrRefOnStruct = new PerLanguageOption(FeatureName, "Don't Put Out Or Ref On Strcut", defaultValue: true); - - [ExportOption] public static readonly PerLanguageOption AllowMovingDeclaration = new PerLanguageOption(FeatureName, "Allow Moving Declaration", defaultValue: false); } } diff --git a/src/Features/Core/ExtractMethod/ExtractMethodOptionsProvider.cs b/src/Features/Core/ExtractMethod/ExtractMethodOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..d009d4763f5219710243bce1c128b87706964bfc --- /dev/null +++ b/src/Features/Core/ExtractMethod/ExtractMethodOptionsProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.ExtractMethod +{ + [ExportOptionProvider, Shared] + internal class ExtractMethodOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + ExtractMethodOptions.AllowBestEffort, + ExtractMethodOptions.DontPutOutOrRefOnStruct, + ExtractMethodOptions.AllowMovingDeclaration + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/Features.csproj b/src/Features/Core/Features.csproj index eae56c20eabd91313a93d4411ed69ebf0b55ac20..73b2d3ccee136f3ddb07425ae424a4f7fc39bbec 100644 --- a/src/Features/Core/Features.csproj +++ b/src/Features/Core/Features.csproj @@ -47,10 +47,6 @@ {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} CodeAnalysis - - {2e87fa96-50bb-4607-8676-46521599f998} - Workspaces.Desktop - {5F8D2414-064A-4B3A-9B42-8E2A04246BE5} Workspaces @@ -159,6 +155,7 @@ + @@ -187,6 +184,7 @@ + @@ -235,6 +233,7 @@ + True True @@ -252,7 +251,10 @@ + + + @@ -512,6 +514,7 @@ + @@ -539,8 +542,6 @@ - - diff --git a/src/Features/Core/Shared/Options/OrganizerOptions.cs b/src/Features/Core/Shared/Options/OrganizerOptions.cs index 2f6da3d9219f4f29505bc968d5a73cc1351f704a..fa5cf5d16dc3b285ef8dff25ab3c636230d27d63 100644 --- a/src/Features/Core/Shared/Options/OrganizerOptions.cs +++ b/src/Features/Core/Shared/Options/OrganizerOptions.cs @@ -11,7 +11,7 @@ internal partial class OrganizerOptions public static PerLanguageOption PlaceSystemNamespaceFirst { - get { return Microsoft.CodeAnalysis.Editing.GenerationOptions.PlaceSystemNamespaceFirst; } + get { return Editing.GenerationOptions.PlaceSystemNamespaceFirst; } } /// @@ -20,7 +20,6 @@ public static PerLanguageOption PlaceSystemNamespaceFirst /// maintain any customized value for this setting, even through versions that have not /// implemented this feature yet. /// - [ExportOption] public static readonly PerLanguageOption WarnOnBuildErrors = new PerLanguageOption(FeatureName, "WarnOnBuildErrors", defaultValue: true); } } diff --git a/src/Features/Core/Shared/Options/OrganizerOptionsProvider.cs b/src/Features/Core/Shared/Options/OrganizerOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..8786fc18f1476ccfe6dbca3216f09d0215a86979 --- /dev/null +++ b/src/Features/Core/Shared/Options/OrganizerOptionsProvider.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Shared.Options +{ + [ExportOptionProvider, Shared] + internal class OrganizerOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + OrganizerOptions.WarnOnBuildErrors + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/Shared/Options/ServiceComponentOnOffOptions.cs b/src/Features/Core/Shared/Options/ServiceComponentOnOffOptions.cs index 6fb708ac1caa527f4fa6bd38c2b0230ee0301091..fe20cbbde4157df9895fe849bc5fd3a020075cba 100644 --- a/src/Features/Core/Shared/Options/ServiceComponentOnOffOptions.cs +++ b/src/Features/Core/Shared/Options/ServiceComponentOnOffOptions.cs @@ -11,7 +11,6 @@ internal static class ServiceComponentOnOffOptions { public const string OptionName = "FeatureManager/Components"; - [ExportOption] public static readonly Option DiagnosticProvider = new Option(OptionName, "Diagnostic Provider", defaultValue: true); } } diff --git a/src/Features/Core/Shared/Options/ServiceComponentOnOffOptionsProvider.cs b/src/Features/Core/Shared/Options/ServiceComponentOnOffOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..673da97568c1a692c307aa8bac8f49f316d0b5bf --- /dev/null +++ b/src/Features/Core/Shared/Options/ServiceComponentOnOffOptionsProvider.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Shared.Options +{ + [ExportOptionProvider, Shared] + internal class ServiceComponentOnOffOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + ServiceComponentOnOffOptions.DiagnosticProvider + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptions.cs b/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptions.cs index 9368f9d34cf0e4b7bc826ae6dbf242f7bf13eb93..5f443e8162529e28a3afdf64588cfac26e429907 100644 --- a/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptions.cs +++ b/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptions.cs @@ -8,7 +8,6 @@ internal static class ServiceFeatureOnOffOptions { public const string OptionName = "ServiceFeaturesOnOff"; - [ExportOption] public static readonly PerLanguageOption ClosedFileDiagnostic = new PerLanguageOption(OptionName, "Closed File Diagnostic", defaultValue: true); } } diff --git a/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptionsProvider.cs b/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..50351f662f2dbef28c535e3271f65931b3ec9920 --- /dev/null +++ b/src/Features/Core/Shared/Options/ServiceFeatureOnOffOptionsProvider.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Shared.Options +{ + [ExportOptionProvider, Shared] + internal class ServiceFeatureOnOffOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + ServiceFeatureOnOffOptions.ClosedFileDiagnostic + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptions.cs b/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptions.cs index 312e87eb53b2a1adb7980ea7090c24ef07ebcb00..fbe63d865364d8bca851842a92818312b6faaf24 100644 --- a/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptions.cs +++ b/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptions.cs @@ -8,25 +8,12 @@ internal static class InternalSolutionCrawlerOptions { public const string OptionName = "SolutionCrawler"; - [ExportOption] public static readonly Option SolutionCrawler = new Option("FeatureManager/Components", "Solution Crawler", defaultValue: true); - - [ExportOption] public static readonly Option ActiveFileWorkerBackOffTimeSpanInMS = new Option(OptionName, "Active file worker backoff timespan in ms", defaultValue: 800); - - [ExportOption] public static readonly Option AllFilesWorkerBackOffTimeSpanInMS = new Option(OptionName, "All files worker backoff timespan in ms", defaultValue: 1500); - - [ExportOption] public static readonly Option EntireProjectWorkerBackOffTimeSpanInMS = new Option(OptionName, "Entire project analysis worker backoff timespan in ms", defaultValue: 5000); - - [ExportOption] public static readonly Option SemanticChangeBackOffTimeSpanInMS = new Option(OptionName, "Semantic change backoff timespan in ms", defaultValue: 100); - - [ExportOption] public static readonly Option ProjectPropagationBackOffTimeSpanInMS = new Option(OptionName, "Project propagation backoff timespan in ms", defaultValue: 500); - - [ExportOption] public static readonly Option PreviewBackOffTimeSpanInMS = new Option(OptionName, "Preview backoff timespan in ms", defaultValue: 500); } } diff --git a/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs b/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..053cc1f283e863a065ea4f212060486dcd768539 --- /dev/null +++ b/src/Features/Core/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.SolutionCrawler +{ + [ExportOptionProvider, Shared] + internal class InternalSolutionCrawlerOptionsProvider : IOptionProvider + { + private readonly IEnumerable _options = new List + { + InternalSolutionCrawlerOptions.SolutionCrawler, + InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS, + InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS, + InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS, + InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS, + InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS, + InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS + }.ToImmutableArray(); + + public IEnumerable GetOptions() + { + return _options; + } + } +} diff --git a/src/Features/Core/Workspace/FileTracker.cs b/src/Features/Core/Workspace/FileTracker.cs deleted file mode 100644 index a2869f67f4190f98e1aeb76807b6c4970bb80552..0000000000000000000000000000000000000000 --- a/src/Features/Core/Workspace/FileTracker.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Host -{ - /// - /// A class that tracks file changes on disk and invokes user actions - /// when changes happen. - /// - internal class FileTracker - { - // guards watchers and actions - private readonly NonReentrantLock _guard = new NonReentrantLock(); - - private readonly Dictionary _watchers = - new Dictionary(); - - private readonly Dictionary _fileActionsMap = - new Dictionary(); - - public FileTracker() - { - } - - private FileActions GetFileActions_NoLock(string path) - { - _guard.AssertHasLock(); - - FileActions actions; - - if (!_fileActionsMap.TryGetValue(path, out actions)) - { - actions = new FileActions(this, path); - _fileActionsMap.Add(path, actions); - } - - return actions; - } - - public bool IsTracking(string path) - { - if (path == null) - { - return false; - } - - using (_guard.DisposableWait()) - { - return _fileActionsMap.ContainsKey(path); - } - } - - public void Track(string path, Action action) - { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - - using (_guard.DisposableWait()) - { - var fileActions = this.GetFileActions_NoLock(path); - fileActions.AddAction_NoLock(action); - - var directory = Path.GetDirectoryName(path); - if (!_watchers.ContainsKey(directory)) - { - var watcher = new FileSystemWatcher(directory); - watcher.Changed += OnFileChanged; - watcher.EnableRaisingEvents = true; - } - } - } - - public void StopTracking(string path) - { - if (path != null) - { - using (_guard.DisposableWait()) - { - _fileActionsMap.Remove(path); - } - } - } - - private void OnFileChanged(object sender, FileSystemEventArgs args) - { - FileActions actions; - - using (_guard.DisposableWait()) - { - actions = this.GetFileActions_NoLock(args.FullPath); - } - - actions.InvokeActions(); - } - - public void Dispose() - { - using (_guard.DisposableWait()) - { - foreach (var watcher in _watchers.Values) - { - watcher.Dispose(); - } - - _watchers.Clear(); - _fileActionsMap.Clear(); - } - } - - private class FileActions - { - private readonly FileTracker _tracker; - private readonly string _path; - private ImmutableArray _actions; - private Task _invokeTask; - - public FileActions(FileTracker tracker, string path) - { - _tracker = tracker; - _path = path; - _actions = ImmutableArray.Create(); - } - - public void AddAction_NoLock(Action action) - { - _tracker._guard.AssertHasLock(); - - _actions = _actions.Add(action); - } - - public void InvokeActions() - { - using (_tracker._guard.DisposableWait()) - { - // only start invoke task if one is not already running - if (_invokeTask == null) - { - _invokeTask = Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current); - _invokeTask.ContinueWithAfterDelay(() => TryInvokeActions(_actions), CancellationToken.None, 100, TaskContinuationOptions.None, TaskScheduler.Current); - } - } - } - - private void TryInvokeActions(ImmutableArray actions) - { - if (actions.Length == 0) - { - return; - } - - // only invoke actions if the writer that caused the event is done - // determine this by checking to see if we can read the file - using (var stream = Kernel32File.Open(_path, FileAccess.Read, FileMode.Open, throwException: false)) - { - if (stream != null) - { - stream.Close(); - - foreach (var action in actions) - { - action(); - } - - // clear invoke task so any following changes get additional invocations - using (_tracker._guard.DisposableWait()) - { - _invokeTask = null; - } - } - else - { - // try again after a short delay - using (_tracker._guard.DisposableWait()) - { - _invokeTask.ContinueWithAfterDelay(() => TryInvokeActions(_actions), CancellationToken.None, 100, TaskContinuationOptions.None, TaskScheduler.Current); - } - } - } - } - } - } -} diff --git a/src/Features/Core/Workspace/Kernel32File.cs b/src/Features/Core/Workspace/Kernel32File.cs deleted file mode 100644 index 311a360e6e041e89244413cc7e5f45b99c048af0..0000000000000000000000000000000000000000 --- a/src/Features/Core/Workspace/Kernel32File.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; -using IO = System.IO; - -namespace Microsoft.CodeAnalysis.Host -{ - internal static class Kernel32File - { - internal static IO.FileStream Open(string path, IO.FileAccess access, IO.FileMode mode, IO.FileShare share = IO.FileShare.None, bool throwException = true) - { - var fileHandle = CreateFile(path, GetFileAccess(access), GetFileShare(share), default(IntPtr), GetFileMode(mode)); - - if (fileHandle.IsInvalid) - { - if (throwException) - { - HandleCOMError(Marshal.GetLastWin32Error()); - } - else - { - return null; - } - } - - return new IO.FileStream(fileHandle, access); - } - - [Flags] - private enum FileShare - { - FILE_SHARE_NONE = 0x00, - FILE_SHARE_READ = 0x01, - FILE_SHARE_WRITE = 0x02, - FILE_SHARE_DELETE = 0x04 - } - - private enum FileMode - { - CREATE_NEW = 1, - CREATE_ALWAYS = 2, - OPEN_EXISTING = 3, - OPEN_ALWAYS = 4, - TRUNCATE_EXISTING = 5 - } - - private enum FileAccess - { - GENERIC_READ = unchecked((int)0x80000000), - GENERIC_WRITE = 0x40000000 - } - - [DllImport("kernel32", SetLastError = true)] - private static extern SafeFileHandle CreateFile(string filename, - FileAccess desiredAccess, - FileShare shareMode, - IntPtr attributes, - FileMode creationDisposition, - uint flagsAndAttributes = 0, - IntPtr templateFile = default(IntPtr)); - - private static void HandleCOMError(int error) - { - throw new System.ComponentModel.Win32Exception(error); - } - - private static FileMode GetFileMode(IO.FileMode mode) - { - if (mode != IO.FileMode.Append) - { - return (FileMode)(int)mode; - } - else - { - return (FileMode)(int)IO.FileMode.OpenOrCreate; - } - } - - private static FileAccess GetFileAccess(IO.FileAccess access) - { - return access == IO.FileAccess.Read ? - FileAccess.GENERIC_READ : - FileAccess.GENERIC_WRITE; - } - - private static FileShare GetFileShare(IO.FileShare share) - { - return (FileShare)(int)share; - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/AsyncLambdaAnalyzer.cs b/src/Samples/CSharp/AsyncPackage/AsyncLambdaAnalyzer.cs deleted file mode 100644 index bc6f8793da819ce03d367ebfe083375165716498..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/AsyncLambdaAnalyzer.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace AsyncPackage -{ - /// - /// Analyzer that examines async lambdas and checks if they are being passed or stored as void-returning delegate types. - /// - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class AsyncLambdaAnalyzer : DiagnosticAnalyzer - { - internal const string AsyncLambdaId1 = "Async003"; - internal const string AsyncLambdaId2 = "Async004"; - - internal static DiagnosticDescriptor Rule1 = new DiagnosticDescriptor(id: AsyncLambdaId1, - title: "Don't Pass Async Lambdas as Void Returning Delegate Types", - messageFormat: "This async lambda is passed as a void-returning delegate type", - category: "Usage", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - internal static DiagnosticDescriptor Rule2 = new DiagnosticDescriptor(id: AsyncLambdaId2, - title: "Don't Store Async Lambdas as Void Returning Delegate Types", - messageFormat: "This async lambda is stored as a void-returning delegate type", - category: "Usage", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.SimpleLambdaExpression, SyntaxKind.AnonymousMethodExpression); - } - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule1, Rule2); } } - - private void AnalyzeNode(SyntaxNodeAnalysisContext context) - { - var symbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol; - - var methodLambda = symbol as IMethodSymbol; - - if (methodLambda != null && methodLambda.IsAsync) - { - var type = context.SemanticModel.GetTypeInfo(context.Node); - if (this.CheckIfVoidReturningDelegateType(type.ConvertedType)) - { - // check if the lambda is being assigned to a variable. This has a code fix. - var parent = context.Node.Parent; - - while (parent != null && !(parent is InvocationExpressionSyntax)) - { - if (parent is VariableDeclarationSyntax) - { - context.ReportDiagnostic(Diagnostic.Create(Rule2, parent.GetLocation())); - return; - } - - parent = parent.Parent; - } - - // if not, add the normal diagnostic - context.ReportDiagnostic(Diagnostic.Create(Rule1, context.Node.GetLocation())); - return; - } - } - - return; - } - - /// - /// Check if the method is a void returning delegate type - /// - /// - /// - /// Returns false if analysis failed or if not a void-returning delegate type - /// Returns true if the inputted node has a converted type that is a void-returning delegate type - /// - private bool CheckIfVoidReturningDelegateType(ITypeSymbol convertedType) - { - if (convertedType != null && convertedType.TypeKind.Equals(TypeKind.Delegate)) - { - var invoke = convertedType.GetMembers("Invoke").FirstOrDefault() as IMethodSymbol; - - if (invoke != null) - { - return invoke.ReturnsVoid; - } - } - - return false; - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/AsyncLambdaVariableCodeFix.cs b/src/Samples/CSharp/AsyncPackage/AsyncLambdaVariableCodeFix.cs deleted file mode 100644 index e7a8fa687c3e5d62d663c0c6c0d9b6326012bda0..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/AsyncLambdaVariableCodeFix.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; - -namespace AsyncPackage -{ - /// - /// Codefix that changes the type of a variable to be Func of Task instead of a void-returning delegate type. - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = AsyncLambdaAnalyzer.AsyncLambdaId1), Shared] - public class AsyncLambdaVariableCodeFix : CodeFixProvider - { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(AsyncLambdaAnalyzer.AsyncLambdaId1); } - } - - public sealed override FixAllProvider GetFixAllProvider() - { - return null; - } - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; - - Debug.Assert(root != null); - var parent = root.FindToken(diagnosticSpan.Start).Parent; - if (parent != null) - { - // Find the type declaration identified by the diagnostic. - var variableDeclaration = parent.FirstAncestorOrSelf(); - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new AsyncLambdaVariableCodeAction("Async lambdas should not be stored in void-returning delegates", - c => ChangeToFunc(context.Document, variableDeclaration, c)), - diagnostic); - } - } - - private async Task ChangeToFunc(Document document, VariableDeclarationSyntax variableDeclaration, CancellationToken cancellationToken) - { - // Change the variable declaration - var newDeclaration = variableDeclaration.WithType(SyntaxFactory.ParseTypeName("System.Func").WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation) - .WithLeadingTrivia(variableDeclaration.Type.GetLeadingTrivia()).WithTrailingTrivia(variableDeclaration.Type.GetTrailingTrivia())); - - var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = oldRoot.ReplaceNode(variableDeclaration, newDeclaration); - var newDocument = document.WithSyntaxRoot(newRoot); - - // Return document with transformed tree. - return newDocument; - } - - private class AsyncLambdaVariableCodeAction : CodeAction - { - private Func> _generateDocument; - private string _title; - - public AsyncLambdaVariableCodeAction(string title, Func> generateDocument) - { - _title = title; - _generateDocument = generateDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _generateDocument(cancellationToken); - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/AsyncPackage.csproj b/src/Samples/CSharp/AsyncPackage/AsyncPackage.csproj deleted file mode 100644 index 21c408935b53ac9c4ece4e75df68340ff748b052..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/AsyncPackage.csproj +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - Debug - AnyCPU - 2.0 - {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {68D3FDD2-DA02-453B-9DF9-022F65F9265E} - Library - Properties - AsyncPackage - AsyncPackage - v4.6 - false - true - true - false - RoslynDev - 14.0 - - - 4.0 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - true - ..\..\..\ - SAK - SAK - SAK - SAK - - - true - full - false - bin\Debug\ - prompt - 4 - - - pdbonly - true - bin\Release\ - prompt - 4 - - - Program - $(DevEnvDir)devenv.exe - /rootsuffix RoslynDev /log - - - - $(VSLOutDir)\Microsoft.CodeAnalysis.dll - false - - - $(VSLOutDir)\Microsoft.CodeAnalysis.CSharp.dll - false - - - $(VSLOutDir)\Microsoft.CodeAnalysis.CSharp.Workspaces.dll - false - - - $(VSLOutDir)\Microsoft.CodeAnalysis.VisualBasic.dll - false - - - $(VSLOutDir)\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll - false - - - $(VSLOutDir)\Microsoft.CodeAnalysis.Workspaces.dll - false - - - - - False - ..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - - - - False - ..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll - - - False - ..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll - - - False - ..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll - - - False - ..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll - - - False - ..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll - - - - - - False - ..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll - - - - - - - - - - - - - - - - - - - - Designer - - - Designer - - - - - Designer - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - - 11.0 - $(VisualStudioVersion) - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - diff --git a/src/Samples/CSharp/AsyncPackage/AsyncVoidAnalyzer.cs b/src/Samples/CSharp/AsyncPackage/AsyncVoidAnalyzer.cs deleted file mode 100644 index c019568b7e6bb13a3b2b935ed96f80fc4afcc5a9..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/AsyncVoidAnalyzer.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace AsyncPackage -{ - /// - /// This Analyzer determines if a method is Async and needs to be returning a Task instead of having a void return type. - /// - [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public class AsyncVoidAnalyzer : DiagnosticAnalyzer - { - internal const string AsyncVoidId = "Async001"; - - internal static DiagnosticDescriptor VoidReturnType = new DiagnosticDescriptor(id: AsyncVoidId, - title: "Avoid Async Void", - messageFormat: "This method has the async keyword but it returns void", - category: "Usage", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); - } - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(VoidReturnType); } } - - private void AnalyzeSymbol(SymbolAnalysisContext context) - { - // Filter out methods that do not use Async and that do not have exactly two parameters - var methodSymbol = (IMethodSymbol)context.Symbol; - - var eventType = context.Compilation.GetTypeByMetadataName("System.EventArgs"); - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - if (methodSymbol.Parameters.Length == 2) - { - var firstParam = methodSymbol.Parameters[0]; - var secondParam = methodSymbol.Parameters[1]; - - if (firstParam is object) - { - // Check each parameter for EventHandler shape and return if it matches. - if (firstParam.Name.ToLower().Equals("sender") && secondParam.Type == eventType) - { - return; - } - else - { - // Check if the second parameter implements EventArgs. If it does; return. - var checkForEventType = secondParam.Type.BaseType; - while (checkForEventType.OriginalDefinition != context.Compilation.GetTypeByMetadataName("System.Object")) - { - if (checkForEventType == eventType) - { - return; - } - - checkForEventType = checkForEventType.BaseType; - } - } - } - } - - context.ReportDiagnostic(Diagnostic.Create(VoidReturnType, methodSymbol.Locations[0], methodSymbol.Name)); - return; - } - - return; - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/AsyncVoidCodeFix.cs b/src/Samples/CSharp/AsyncPackage/AsyncVoidCodeFix.cs deleted file mode 100644 index 3c4c99652ce76926ee0e2f18b9336641012c0eb5..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/AsyncVoidCodeFix.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Simplification; - -namespace AsyncPackage -{ - /// - /// This codefix replaces the void return type with Task in any method declaration the AsyncVoidAnalyzer catches - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = AsyncVoidAnalyzer.AsyncVoidId), Shared] - public class AsyncVoidCodeFix : CodeFixProvider - { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(AsyncVoidAnalyzer.AsyncVoidId); } - } - - public sealed override FixAllProvider GetFixAllProvider() - { - return null; - } - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; - - // Find the type declaration identified by the diagnostic. - var methodDeclaration = root.FindToken(diagnosticSpan.Start).Parent.FirstAncestorOrSelf(); - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new AsyncVoidCodeAction("Async methods should not return void", - c => VoidToTaskAsync(context.Document, methodDeclaration, c)), - diagnostic); - } - - private async Task VoidToTaskAsync(Document document, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) - { - // The Task object must be parsed from a string using the Syntax Factory - var newType = SyntaxFactory.ParseTypeName("System.Threading.Tasks.Task").WithAdditionalAnnotations(Simplifier.Annotation).WithTrailingTrivia(methodDeclaration.ReturnType.GetTrailingTrivia()); - - var newMethodDeclaration = methodDeclaration.WithReturnType(newType); - - var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = oldRoot.ReplaceNode(methodDeclaration, newMethodDeclaration); - var newDocument = document.WithSyntaxRoot(newRoot); - - // Return document with transformed tree. - return newDocument; - } - - private class AsyncVoidCodeAction : CodeAction - { - private Func> _createDocument; - private string _title; - - public AsyncVoidCodeAction(string title, Func> createDocument) - { - _title = title; - _createDocument = createDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _createDocument(cancellationToken); - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/BlockingAsyncAnalyzer.cs b/src/Samples/CSharp/AsyncPackage/BlockingAsyncAnalyzer.cs deleted file mode 100644 index 3982a1ba127bcd6270cc2bf4550eeb7caa7a9cfc..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/BlockingAsyncAnalyzer.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace AsyncPackage -{ - /// - /// This analyzer checks to see if asynchronous and synchronous code is mixed. - /// This causes blocking and deadlocks. The analyzer will check when async - /// methods are used and then checks if synchronous code is used within the method. - /// A codefix will then change that synchronous code to its asynchronous counterpart. - /// - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class BlockingAsyncAnalyzer : DiagnosticAnalyzer - { - internal const string BlockingAsyncId = "Async006"; - - internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(id: BlockingAsyncId, - title: "Don't Mix Blocking and Async", - messageFormat: "This method is blocking on async code", - category: "Usage", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.SimpleMemberAccessExpression); - } - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } - - private void AnalyzeNode(SyntaxNodeAnalysisContext context) - { - var method = context.SemanticModel.GetEnclosingSymbol(context.Node.SpanStart) as IMethodSymbol; - - if (method != null && method.IsAsync) - { - var invokeMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol; - - if (invokeMethod != null && !invokeMethod.IsExtensionMethod) - { - // Checks if the Wait method is called within an async method then creates the diagnostic. - if (invokeMethod.OriginalDefinition.Name.Equals("Wait")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.Parent.GetLocation())); - return; - } - - // Checks if the WaitAny method is called within an async method then creates the diagnostic. - if (invokeMethod.OriginalDefinition.Name.Equals("WaitAny")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.Parent.GetLocation())); - return; - } - - // Checks if the WaitAll method is called within an async method then creates the diagnostic. - if (invokeMethod.OriginalDefinition.Name.Equals("WaitAll")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.Parent.GetLocation())); - return; - } - - // Checks if the Sleep method is called within an async method then creates the diagnostic. - if (invokeMethod.OriginalDefinition.Name.Equals("Sleep")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.Parent.GetLocation())); - return; - } - - // Checks if the GetResult method is called within an async method then creates the diagnostic. - if (invokeMethod.OriginalDefinition.Name.Equals("GetResult")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.Parent.GetLocation())); - return; - } - } - - var property = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IPropertySymbol; - - // Checks if the Result property is called within an async method then creates the diagnostic. - if (property != null && property.OriginalDefinition.Name.Equals("Result")) - { - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation())); - return; - } - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/BlockingAsyncCodeFix.cs b/src/Samples/CSharp/AsyncPackage/BlockingAsyncCodeFix.cs deleted file mode 100644 index 9bc0240388825c7be529f105eb93103eb28da3eb..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/BlockingAsyncCodeFix.cs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; - -namespace AsyncPackage -{ - /// - /// Codefix changes the synchronous operations to it's asynchronous equivalent. - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = BlockingAsyncAnalyzer.BlockingAsyncId), Shared] - public class BlockingAsyncCodeFix : CodeFixProvider - { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(BlockingAsyncAnalyzer.BlockingAsyncId); } - } - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; - - // Find the type declaration identified by the diagnostic. - var invocation = root.FindToken(diagnosticSpan.Start).Parent.FirstAncestorOrSelf(); - var invokemethod = root.FindToken(diagnosticSpan.Start).Parent.FirstAncestorOrSelf(); - - var semanticmodel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - - var method = semanticmodel.GetEnclosingSymbol(invocation.SpanStart) as IMethodSymbol; - - if (method != null && method.IsAsync) - { - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("Wait")) - { - var name = invokemethod.Name.Identifier.Text; - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionChangetoAwaitAsync("Change synchronous operation to asynchronous counterpart", - c => ChangetoAwaitAsync(context.Document, invocation, name, c)), - diagnostic); - return; - } - - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("GetAwaiter")) - { - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionChangetoAwaitGetAwaiterAsync("Change synchronous operation to asynchronous counterpart", - c => ChangetoAwaitGetAwaiterAsync(context.Document, invocation, c)), - diagnostic); - return; - } - - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("Result")) - { - var name = invokemethod.Name.Identifier.Text; - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionChangetoAwaitAsync("Change synchronous operation to asynchronous counterpart", - c => ChangetoAwaitAsync(context.Document, invocation, name, c)), - diagnostic); - return; - } - - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("WaitAny")) - { - var name = invokemethod.Name.Identifier.Text; - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionToDelayWhenAnyWhenAllAsync("Change synchronous operation to asynchronous counterpart", - c => ToDelayWhenAnyWhenAllAsync(context.Document, invocation, name, c)), - diagnostic); - return; - } - - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("WaitAll")) - { - var name = invokemethod.Name.Identifier.Text; - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionToDelayWhenAnyWhenAllAsync("Change synchronous operation to asynchronous counterpart", - c => ToDelayWhenAnyWhenAllAsync(context.Document, invocation, name, c)), - diagnostic); - return; - } - - if (invokemethod != null && invokemethod.Name.Identifier.Text.Equals("Sleep")) - { - var name = invokemethod.Name.Identifier.Text; - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CodeActionToDelayWhenAnyWhenAllAsync("Change synchronous operation to asynchronous counterpart", - c => ToDelayWhenAnyWhenAllAsync(context.Document, invocation, name, c)), - diagnostic); - return; - } - } - } - - private async Task ToDelayWhenAnyWhenAllAsync(Document document, InvocationExpressionSyntax invocation, string name, CancellationToken cancellationToken) - { - var simpleExpression = SyntaxFactory.ParseName(""); - if (name.Equals("WaitAny")) - { - simpleExpression = SyntaxFactory.ParseName("System.Threading.Tasks.Task.WhenAny").WithAdditionalAnnotations(Simplifier.Annotation); - } - else if (name.Equals("WaitAll")) - { - simpleExpression = SyntaxFactory.ParseName("System.Threading.Tasks.Task.WhenAll").WithAdditionalAnnotations(Simplifier.Annotation); - } - else if (name.Equals("Sleep")) - { - simpleExpression = SyntaxFactory.ParseName("System.Threading.Tasks.Task.Delay").WithAdditionalAnnotations(Simplifier.Annotation); - } - - SyntaxNode oldExpression = invocation; - var expression = invocation.WithExpression(simpleExpression).WithLeadingTrivia(invocation.GetLeadingTrivia()).WithTrailingTrivia(invocation.GetTrailingTrivia()); - - var newExpression = SyntaxFactory.PrefixUnaryExpression(SyntaxKind.AwaitExpression, expression.WithLeadingTrivia(SyntaxFactory.Space)).WithTrailingTrivia(invocation.GetTrailingTrivia()).WithLeadingTrivia(invocation.GetLeadingTrivia()); - - var oldroot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newroot = oldroot.ReplaceNode(oldExpression, newExpression); - - var newDocument = document.WithSyntaxRoot(newroot); - - return newDocument; - } - - private async Task ChangetoAwaitAsync(Document document, InvocationExpressionSyntax invocation, string name, CancellationToken cancellationToken) - { - SyntaxNode oldExpression = invocation; - SyntaxNode newExpression = null; - - if (name.Equals("Wait")) - { - oldExpression = invocation.FirstAncestorOrSelf(); - var identifier = (invocation.Expression as MemberAccessExpressionSyntax).Expression as IdentifierNameSyntax; - newExpression = SyntaxFactory.PrefixUnaryExpression( - SyntaxKind.AwaitExpression, - identifier).WithAdditionalAnnotations(Formatter.Annotation); - } - - if (name.Equals("Result")) - { - oldExpression = invocation.Parent.FirstAncestorOrSelf(); - newExpression = SyntaxFactory.PrefixUnaryExpression( - SyntaxKind.AwaitExpression, - invocation).WithAdditionalAnnotations(Formatter.Annotation); - } - - var oldroot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newroot = oldroot.ReplaceNode(oldExpression, newExpression); - - var newDocument = document.WithSyntaxRoot(newroot); - - return newDocument; - } - - private async Task ChangetoAwaitGetAwaiterAsync(Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationTkn) - { - SyntaxNode expression = invocation; - while (!(expression is ExpressionStatementSyntax)) - { - expression = expression.Parent; - } - - var oldExpression = expression as ExpressionStatementSyntax; - var awaitedInvocation = SyntaxFactory.PrefixUnaryExpression(SyntaxKind.AwaitExpression, invocation.WithLeadingTrivia(SyntaxFactory.Space)).WithLeadingTrivia(invocation.GetLeadingTrivia()); - var newExpression = oldExpression.WithExpression(awaitedInvocation); - - var oldroot = await document.GetSyntaxRootAsync(cancellationTkn).ConfigureAwait(false); - var newroot = oldroot.ReplaceNode(oldExpression, newExpression); - var newDocument = document.WithSyntaxRoot(newroot); - - return newDocument; - } - - public sealed override FixAllProvider GetFixAllProvider() - { - return null; - } - - private class CodeActionToDelayWhenAnyWhenAllAsync : CodeAction - { - private Func> _generateDocument; - private string _title; - - public CodeActionToDelayWhenAnyWhenAllAsync(string title, Func> generateDocument) - { - _title = title; - _generateDocument = generateDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _generateDocument(cancellationToken); - } - } - - private class CodeActionChangetoAwaitAsync : CodeAction - { - private Func> _generateDocument; - private string _title; - - public CodeActionChangetoAwaitAsync(string title, Func> generateDocument) - { - _title = title; - _generateDocument = generateDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _generateDocument(cancellationToken); - } - } - - private class CodeActionChangetoAwaitGetAwaiterAsync : CodeAction - { - private Func> _generateDocument; - private string _title; - - public CodeActionChangetoAwaitGetAwaiterAsync(string title, Func> generateDocument) - { - _title = title; - _generateDocument = generateDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _generateDocument(cancellationToken); - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/CancellationAnalyzer.cs b/src/Samples/CSharp/AsyncPackage/CancellationAnalyzer.cs deleted file mode 100644 index 4ad1df5e1d881cd0c26fc40fd2a9f988d7e2a26b..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/CancellationAnalyzer.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace AsyncPackage -{ - /// - /// This analyzer check to see if there are Cancellation Tokens that can be propagated through async method calls - /// - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class CancellationAnalyzer : DiagnosticAnalyzer - { - internal const string CancellationId = "Async005"; - - internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(id: CancellationId, - title: "Propagate CancellationTokens When Possible", - messageFormat: "This method can take a CancellationToken", - category: "Library", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override void Initialize(AnalysisContext context) - { - context.RegisterCodeBlockStartAction(CreateAnalyzerWithinCodeBlock); - } - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } - - private void CreateAnalyzerWithinCodeBlock(CodeBlockStartAnalysisContext context) - { - var methodDeclaration = context.OwningSymbol as IMethodSymbol; - - if (methodDeclaration != null) - { - ITypeSymbol cancellationTokenType = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken"); - - var paramTypes = methodDeclaration.Parameters.Select(x => x.Type); - - if (paramTypes.Contains(cancellationTokenType)) - { - // Analyze the inside of the code block for invocationexpressions - context.RegisterSyntaxNodeAction(new CancellationAnalyzer_Inner().AnalyzeNode, SyntaxKind.InvocationExpression); - } - } - } - - internal class CancellationAnalyzer_Inner - { - public void AnalyzeNode(SyntaxNodeAnalysisContext context) - { - var invokeMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol; - - if (invokeMethod != null) - { - ITypeSymbol cancellationTokenType = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken"); - - var invokeParams = invokeMethod.Parameters.Select(x => x.Type); - - if (invokeParams.Contains(cancellationTokenType)) - { - var passedToken = false; - - foreach (var arg in ((InvocationExpressionSyntax)context.Node).ArgumentList.Arguments) - { - var thisArgType = context.SemanticModel.GetTypeInfo(arg.Expression).Type; - - if (thisArgType != null && thisArgType.Equals(cancellationTokenType)) - { - passedToken = true; - } - } - - if (!passedToken) - { - context.ReportDiagnostic(Diagnostic.Create(CancellationAnalyzer.Rule, context.Node.GetLocation())); - } - } - } - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/CancellationCodeFix.cs b/src/Samples/CSharp/AsyncPackage/CancellationCodeFix.cs deleted file mode 100644 index 8fc2264c482b586e6a5ae42b29f7d5b0167789bb..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/CancellationCodeFix.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace AsyncPackage -{ - /// - /// Codefix that changes the type of a variable to be Func of Task instead of a void-returning delegate type. - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = CancellationAnalyzer.CancellationId), Shared] - public class CancellationCodeFix : CodeFixProvider - { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(CancellationAnalyzer.CancellationId); } - } - - public sealed override FixAllProvider GetFixAllProvider() - { - return null; - } - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; - - // Find the type declaration identified by the diagnostic. - var invocation = root.FindToken(diagnosticSpan.Start).Parent.FirstAncestorOrSelf(); - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new CancellationCodeAction("Propagate CancellationTokens when possible", - c => AddCancellationTokenAsync(context.Document, invocation, c)), - diagnostic); - } - - private async Task AddCancellationTokenAsync(Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) - { - var semanticModel = await document.GetSemanticModelAsync().ConfigureAwait(false); - - ITypeSymbol cancellationTokenType = semanticModel.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken"); - - var invocationSymbol = semanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol; - - // Step up through the syntax tree to get the Method Declaration of the invocation - var parent = invocation.Parent; - parent = parent.FirstAncestorOrSelf(); - - var containingMethod = semanticModel.GetDeclaredSymbol(parent) as IMethodSymbol; - - // Get the CancellationToken from the containing method - var tokens = containingMethod.Parameters.Where(x => x.Type.Equals(cancellationTokenType)); - - var firstToken = tokens.FirstOrDefault(); - - // Get what slot to put it in - var cancelSlots = invocationSymbol.Parameters.Where(x => x.Type.Equals(cancellationTokenType)); - - if (cancelSlots.FirstOrDefault() == null) - { - return document; - } - - var firstSlotIndex = invocationSymbol.Parameters.IndexOf(cancelSlots.FirstOrDefault()); - var newIdentifier = SyntaxFactory.IdentifierName(firstToken.Name.ToString()); - var newArgs = invocation.ArgumentList.Arguments; - - if (firstSlotIndex == 0) - { - newArgs = newArgs.Insert(firstSlotIndex, SyntaxFactory.Argument(newIdentifier).WithLeadingTrivia()); - } - else - { - newArgs = invocation.ArgumentList.Arguments.Insert(firstSlotIndex, SyntaxFactory.Argument(newIdentifier).WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticSpace))); - } - - var newArgsList = SyntaxFactory.ArgumentList(newArgs); - var newInvocation = invocation.WithArgumentList(newArgsList); - - var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = oldRoot.ReplaceNode(invocation, newInvocation); - var newDocument = document.WithSyntaxRoot(newRoot); - - // Return document with transformed tree. - return newDocument; - } - - private class CancellationCodeAction : CodeAction - { - private Func> _createDocument; - private string _title; - - public CancellationCodeAction(string title, Func> createDocument) - { - _title = title; - _createDocument = createDocument; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - return _createDocument(cancellationToken); - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Diagnostic.nuspec b/src/Samples/CSharp/AsyncPackage/Diagnostic.nuspec deleted file mode 100644 index 2f44ea885aadae58eb0d73733d70b184c88797b8..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Diagnostic.nuspec +++ /dev/null @@ -1,22 +0,0 @@ - - - - AsyncPackage - 1.0.0.0 - AsyncPackage - AsyncPackage - AsyncPackage - false - AsyncPackage - Summary of changes made in this release of the package. - Copyright - Tag1 Tag2 - - - - - - - - - \ No newline at end of file diff --git a/src/Samples/CSharp/AsyncPackage/Properties/AssemblyInfo.cs b/src/Samples/CSharp/AsyncPackage/Properties/AssemblyInfo.cs deleted file mode 100644 index 67888827ec575609a4eb96fb1e60ef0d7bf8b989..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Reflection; - -[assembly: AssemblyTitle("AsyncPackage")] diff --git a/src/Samples/CSharp/AsyncPackage/ReadMe.txt b/src/Samples/CSharp/AsyncPackage/ReadMe.txt deleted file mode 100644 index 3dbb3a23a15af423273686bbf6dc3b5326f8239c..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/ReadMe.txt +++ /dev/null @@ -1,45 +0,0 @@ - -Building this project will produce an analyzer .dll, as well as the -following two ways you may wish to package that analyzer: - * A NuGet package (.nupkg file) that will add your assembly as a - project-local analyzer that participates in builds. - * A VSIX extension (.vsix file) that will apply your analyzer to all projects - and works just in the IDE. - -Starting this project will deploy the analyzer as a VSIX into another copy of -Visual Studio, which is useful for debugging, even if you intend to produce a -NuGet package. - - -GETTING STARTED WITH NUGET - -Before you can produce a NuGet package, you must enable NuGet Package Restore: - * Right-click the solution in Solution Explorer and choose Enable NuGet - Package Restore. - -This downloads the NuGet binaries that will be used to pack your analyzer into -a NuGet package. For more information, see -http://go.microsoft.com/fwlink/?LinkID=322105. - -If you do not wish to produce a NuGet package, you can delete the -Diagnostic.nuspec file to silence the Package Restore warning. - - -TRYING OUT YOUR NUGET PACKAGE - -To try out the NuGet package: - 1. Create a local NuGet feed by following the instructions here: - > http://docs.nuget.org/docs/creating-packages/hosting-your-own-nuget-feeds - 2. Copy the .nupkg file into that folder. - 3. Open the target project in Visual Studio "14". - 4. Right-click on the project node in Solution Explorer and choose Manage - NuGet Packages. - 5. Select the NuGet feed you created on the left. - 6. Choose your analyzer from the list and click Install. - -If you want to automatically deploy the .nupkg file to the local feed folder -when you build this project, follow these steps: - 1. Right-click on this project in Solution Explorer and choose Properties. - 2. Go to the Build Events tab. - 3. In the "Post-build event command line" box, change the -OutputDirectory - path to point to your local NuGet feed folder. diff --git a/src/Samples/CSharp/AsyncPackage/RenameAsyncAnalyzer.cs b/src/Samples/CSharp/AsyncPackage/RenameAsyncAnalyzer.cs deleted file mode 100644 index 01a29ef7a0acfdc8f42584647ea16c4aecde8ac6..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/RenameAsyncAnalyzer.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace AsyncPackage -{ - /// - /// This analyzer will run a codefix on any method that qualifies as async that renames it to follow naming conventions - /// - [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public class RenameAsyncAnalyzer : DiagnosticAnalyzer - { - internal const string RenameAsyncId = "Async002"; - - internal static DiagnosticDescriptor RenameAsyncMethod = new DiagnosticDescriptor(id: RenameAsyncId, - title: "Async Method Names Should End in Async", - messageFormat: "This method is async but the method name does not end in Async", - category: "Naming", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); - } - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(RenameAsyncMethod); } } - - private static void AnalyzeSymbol(SymbolAnalysisContext context) - { - // Filter out methods that do not use Async and make sure to include methods that return a Task - var methodSymbol = (IMethodSymbol)context.Symbol; - - // Check if method name is an override or virtual class. If it is disregard it. - // (This assumes if a method is virtual the programmer will not want to change the name) - // Check if the method returns a Task or Task - if ((methodSymbol.ReturnType == context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task") - || methodSymbol.ReturnType.OriginalDefinition == context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1").OriginalDefinition) - && !methodSymbol.Name.EndsWith("Async") && !methodSymbol.IsOverride && !methodSymbol.IsVirtual) - { - context.ReportDiagnostic(Diagnostic.Create(RenameAsyncMethod, methodSymbol.Locations[0], methodSymbol.Name)); - return; - } - - return; - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/RenameAsyncCodeFix.cs b/src/Samples/CSharp/AsyncPackage/RenameAsyncCodeFix.cs deleted file mode 100644 index 313ce16332e5b69130ab48e79600b5b7d1dd6e54..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/RenameAsyncCodeFix.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Rename; - -namespace AsyncPackage -{ - /// - /// This codefix adds "Async" to the end of the Method Identifier and does a basic spellcheck in case the user had already tried to type Async - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = RenameAsyncAnalyzer.RenameAsyncId), Shared] - public class RenameAsyncCodeFix : CodeFixProvider - { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(RenameAsyncAnalyzer.RenameAsyncId); } - } - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; - - // Find the type declaration identified by the diagnostic. - var methodDeclaration = root.FindToken(diagnosticSpan.Start).Parent.FirstAncestorOrSelf(); - - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - new RenameAsyncCodeAction("Add Async to the end of the method name", - c => RenameMethodAsync(context.Document, methodDeclaration, c)), - diagnostic); - } - - private async Task RenameMethodAsync(Document document, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) - { - var model = await document.GetSemanticModelAsync().ConfigureAwait(false); - var oldSolution = document.Project.Solution; - - var symbol = model.GetDeclaredSymbol(methodDeclaration); - - var oldName = methodDeclaration.Identifier.ToString(); - var newName = string.Empty; - - // Check to see if name already contains Async at the end - if (HasAsyncSuffix(oldName)) - { - newName = oldName.Substring(0, oldName.Length - 5) + "Async"; - } - else - { - newName = oldName + "Async"; - } - - var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, symbol, newName, document.Project.Solution.Workspace.Options).ConfigureAwait(false); - - // Gets all identifiers to check for renaming conflicts - var syntaxTree = await document.GetSyntaxTreeAsync().ConfigureAwait(false); - var descendentTokens = syntaxTree.GetRoot().DescendantTokens(); - - var usedNames = descendentTokens.Where(token => token.IsKind(SyntaxKind.IdentifierToken)).Select(token => token.Value); - - while (usedNames.Contains(newName)) - { - // No codefix should be offered if the name conflicts with another method. - return oldSolution; - } - - return newSolution; - } - - /// - /// This spellchecker obviously has limitations, but it may be helpful to some. - /// - /// - /// Returns a boolean of whether or not "Async" may have been in the Method name already but was mispelled. - public bool HasAsyncSuffix(string oldName) - { - if (oldName.Length >= 5) - { - var last5letters = oldName.Substring(oldName.Length - 5); - - // Check case. The A in Async must be capitalized - if (last5letters.Contains("async") || last5letters.Contains("asinc")) - { - return true; - } - else if (((last5letters.Contains("A") || last5letters.Contains("a")) && last5letters.Contains("s") && last5letters.Contains("y") - && last5letters.Contains("n") && last5letters.Contains("c")) && !last5letters.ToLower().Equals("scany")) - { - return true; // Basic spellchecker. This is obviously not conclusive, but it may catch a small error if the letters are simply switched around. - } - } - - return false; - } - - public sealed override FixAllProvider GetFixAllProvider() - { - return null; - } - - private class RenameAsyncCodeAction : CodeAction - { - private Func> _generateSolution; - private string _title; - - public RenameAsyncCodeAction(string title, Func> generateSolution) - { - _title = title; - _generateSolution = generateSolution; - } - - public override string Title { get { return _title; } } - - protected override Task GetChangedSolutionAsync(CancellationToken cancellationToken) - { - return base.GetChangedSolutionAsync(cancellationToken); - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/AsyncPackage.Test.csproj b/src/Samples/CSharp/AsyncPackage/Test/AsyncPackage.Test.csproj deleted file mode 100644 index c7159ea9ecff2c937f9c4beec051a5e049e1214c..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/AsyncPackage.Test.csproj +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - Debug - AnyCPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6} - Library - Properties - Tests - Tests - v4.6 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - SAK - SAK - SAK - SAK - ..\..\..\ - - - true - full - false - bin\Debug\ - prompt - 4 - - - pdbonly - true - bin\Release\ - prompt - 4 - - - - $(VSLOutDir)\Microsoft.CodeAnalysis.dll - false - - - False - (VSLOutDir)\Microsoft.CodeAnalysis.CSharp.dll - false - - - False - (VSLOutDir)\Microsoft.CodeAnalysis.CSharp.Workspaces.dll - false - - - (VSLOutDir)\Microsoft.CodeAnalysis.VisualBasic.dll - false - - - (VSLOutDir)\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll - false - - - False - (VSLOutDir)\Microsoft.CodeAnalysis.Workspaces.dll - false - - - False - (VSLOutDir)\Microsoft.CodeAnalysis.Workspaces.Desktop.dll - false - - - - False - ..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - - - - - - false - - - - - false - - - - - - - - - - - - - - - - - - - {68d3fdd2-da02-453b-9df9-022f65f9265e} - AsyncPackage - - - - - - - - - - False - - - False - - - False - - - False - - - - - - - - - diff --git a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncLambdaTests.cs b/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncLambdaTests.cs deleted file mode 100644 index f6f75600630ad736b53233a810ca26a973088fc2..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncLambdaTests.cs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TestTemplate; - -namespace AsyncPackage.Tests -{ - /// - /// Unit Tests for the AsyncLambdaDiagnosticAnalzer and AsyncLambdaVariableCodeFix - /// - [TestClass] - public class AsyncLambdaTests : CodeFixVerifier - { - private const string DefaultHeader = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; - -namespace ConsoleApplication1 -{ - class Program - { "; - - private const string DefaultFooter = @" - } -}"; - - private DiagnosticResult expected003 = new DiagnosticResult - { - Id = "Async003", - Message = "This async lambda is passed as a void-returning delegate type", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - private DiagnosticResult expected004 = new DiagnosticResult - { - Id = "Async004", - Message = "This async lambda is stored as a void-returning delegate type", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new AsyncLambdaVariableCodeFix(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new AsyncLambdaAnalyzer(); - } - - [TestMethod] - public void AL001_None_NoMethodNoDiagnostics() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [TestMethod] - public void AL002_DACF_AssignVoidableVariable() - { - var body = @" - public void method() - { - //assign to voidable variable - Action thing = async () => - { - await Task.Delay(500); - }; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected004.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 17) }; - - VerifyCSharpDiagnostic(test, expected004); - - var fix = @" - public void method() - { - //assign to voidable variable - Func thing = async () => - { - await Task.Delay(500); - }; - }"; - - var testfix = DefaultHeader + fix + DefaultFooter; - - // Causes new unused using directory error because the code is not using System.Action anymore - VerifyCSharpFix(test, testfix, null, true); - } - - // Task function assigned to lambda - no diagnostic should show - [TestMethod] - public void AL003_None_TaskReturningDelegateType() - { - var body = @" - static void Main(string[] args) - { - //assign as correct variable - Func thing2 = async () => - { - await Task.Delay(666); - }; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Method with void-returning delegate type - diagnostic should show on the lambda - [TestMethod] - public void AL004_DA_PassAsVoidReturningDelagateType() - { - var body = @" - //bad implementation - public static double BadMethod(Action action) - { - return 0; - } - - //pass as void-returning delegate type - double num = BadMethod(async () => - { - await Task.Delay(1000); - }); - "; - var test = DefaultHeader + body + DefaultFooter; - - expected003.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 20, 32) }; - - VerifyCSharpDiagnostic(test, expected003); - } - - // Method with correct delegate type - no diagnostic should show - [TestMethod] - public void AL005_None_PassAsTaskReturningDelagateType() - { - var body = @" - //good implementation - public static double GoodMethod(Func func) - { - return 0; - } - - //pass as void-returning delegate type - double num = GoodMethod(async () => - { - await Task.Delay(1000); - }); - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Lambda overrides a Task function - no diagnostic should show - [TestMethod] - public void AL006_None_TaskOverrideDelegateType() - { - var body = @" - static void Main(string[] args) - { - //use correct override - Task.Factory.StartNew(async () => - { - await Task.Delay(12345); - }); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Test delegate vs => correct - no diagnostic should show - [TestMethod] - public void AL007_None_DelegateLambdaTaskReturn() - { - var body = @" - //good implementation - public static double GoodMethod(Func func) - { - return 0; - } - - //pass as void-returning delegate type - double num = GoodMethod(async delegate () - { - await Task.Delay(222); - }); - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Test delegate vs => incorrect - 1 diagnostic should show - [TestMethod] - public void AL008_DA_DelegateLambdaVoidReturn() - { - var body = @" - //good implementation - public static double BadMethod(Action a) - { - return 0; - } - - //pass as void-returning delegate type - double num = BadMethod(async delegate () - { - await Task.Delay(100); - }); - "; - - var test = DefaultHeader + body + DefaultFooter; - - expected003.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 20, 32) }; - - VerifyCSharpDiagnostic(test, expected003); - } - - // Test simple lambda correct - no diagnostic should show - [TestMethod] - public void AL009_None_SimpleLambdaTaskReturn() - { - var body = @" - //good implementation - public static double GoodMethod(Func func) - { - return 0; - } - - //pass as void-returning delegate type - double num = GoodMethod(async () => await Task.Delay(10)); - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Test simple lambda incorrect - 1 diagnostic should show - [TestMethod] - public void AL010_DA_SimpleLambdaVoidReturn() - { - var body = @" - //good implementation - public static double BadMethod(Action a) - { - return 0; - } - - //pass as void-returning delegate type - double num = BadMethod(async () => await Task.Delay(10)); - "; - var test = DefaultHeader + body + DefaultFooter; - - expected003.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 20, 32) }; - - VerifyCSharpDiagnostic(test, expected003); - } - - // Missing necessary using directives - 1 diagnostic - [TestMethod] - public void AL011_DA_MissingUsings() - { - var body = @" - using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Diagnostics; - -namespace ConsoleApplication1 -{ - class Program - { - //bad implementation - public static double BadMethod(System.Action action) - { - return 0; - } - - //pass as void-returning delegate type - double num = BadMethod(async () => - { - await System.Threading.Tasks.Task.Delay(1000); - }); - - } -}"; - var test = body; - - expected003.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 18, 32) }; - - VerifyCSharpDiagnostic(test, expected003); - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncVoidTests.cs b/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncVoidTests.cs deleted file mode 100644 index 0ab39ffb707485a14e2c60eb25e7e65681c1f398..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/AsyncVoidTests.cs +++ /dev/null @@ -1,688 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TestTemplate; - -namespace AsyncPackage.Tests -{ - /// - /// Unit Tests for the AsyncVoidAnalyzer and AsyncVoidCodeFix - /// - [TestClass] - public class AsyncVoidTests : CodeFixVerifier - { - private const string DefaultHeader = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; - -namespace ConsoleApplication1 -{ - class Program - { "; - - private const string DefaultFooter = @" - } -}"; - - private DiagnosticResult expected001 = new DiagnosticResult - { - Id = "Async001", - Message = "This method has the async keyword but it returns void", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new AsyncVoidCodeFix(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new AsyncVoidAnalyzer(); - } - - protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return new AsyncVoidAnalyzer(); - } - - // No methods - no diagnostic should show - [TestMethod] - public void AV001_None_NoMethodNoDiagnostics() - { - var body = @""; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - single request - should show no diagnostic - [TestMethod] - public void AV002_None_TaskReturnAsyncSingleRequest() - { - var body = @" - public static async Task DelayAsync() - { - await Task.Delay(1000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - serial requests - should show no diagnostic - [TestMethod] - public void AV003_None_TaskReturnAsyncSerialRequests() - { - var body = @" - public static async Task DelayMultipleTimesAsync() - { - await Task.Delay(1000); - await Task.Delay(1000); - await Task.Delay(1000); - await Task.Delay(1000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - parallel requests - should show no diagnostic - [TestMethod] - public void AV004_None_TaskReturnAsyncParallelRequests() - { - var body = @" - public async Task SomeAsyncTask() - { int value = 708; - await ValuePlusOne(value); - await ValuePlusTwo(value); - await ValuePlusThree(value); - } - - public async Task ValuePlusOne(int num) - { - await Task.Delay(90); - - return num; - } - - public async Task ValuePlusTwo(int num) - { - await Task.Delay(990); - - return num; - } - - public async Task ValuePlusThree(int num) - { - await Task.Delay(9990); - - return num; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - Single request - should show no diagnostic - [TestMethod] - public void AV005_None_TaskIntReturnAsyncSingleRequest() - { - var body = @" - public static async Task MultiplyfactorsAsync(int factor1, int factor2) - { - await Task.Delay(5000); - //delay once - - return (factor1 * factor2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - serial requests - should show no diagnostic - [TestMethod] - public void AV006_None_TaskIntReturnWithAsync() - { - var body = @" - private async Task MathAsync(int num1) - { - //delays - await Task.Delay(3000); - await Task.Delay(1000); - await Task.Delay(num1 * 15); - - return (num1); - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - single request - should show no diagnostic - [TestMethod] - public void AV007_None_TaskStringReturnAsyncSingleRequest() - { - var body = @" - public static Task ReturnAAAAsync() - { - return Task.Run(() => - { return (""AAA""); }); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - serial requests - should show no diagnostic - [TestMethod] - public void AV008_None_TaskStringReturnAsyncSerialRequests() - { - var body = @" - public async static Task LongTaskAAsync() - { - await Task.Delay(2000); - await Task.Delay(2000); - await Task.Delay(2000); - return await Task.Run(() => - { - return (""AAA""); - }); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - serial requests - should show no diagnostic - [TestMethod] - public void AV009_None_TaskIntReturnAsyncSerialRequests() - { - var body = @" - public static async Task MultiplyAsync(int factor1, int factor2) - { - //delay three times - await Task.Delay(100); - await Task.Delay(100); - await Task.Delay(100); - - return (factor1 * factor2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - single request - should show no diagnostic - [TestMethod] - public void AV010_None_TaskReturnNoAsyncSingleRequest() - { - var body = @" - public static async Task Delay() - { - await Task.Delay(1000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task Return Async Method - serial requests - should show no diagnostic - [TestMethod] - public void AV011_None_TaskIntReturnNoAsyncSerialRequests() - { - var body = @" - public static async Task Multiply(int factor1, int factor2) - { - //delay three times - await Task.Delay(100); - await Task.Delay(100); - await Task.Delay(100); - - return (factor1 * factor2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task async method - no diagnostic - [TestMethod] - public void AV012_None_TaskBoolReturnAsync() - { - var body = @" - private async Task TrueorFalseAsync(bool trueorfalse) - { - await Task.Delay(700); - - return !(trueorfalse); - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Void Return Async Method with Event handling - should show no diagnostic - [TestMethod] - public void AV013_None_AsyncVoidWithEventHandling() - { - var body = @" - private async void button1_Click(object sender, EventArgs e) - { - await Button1ClickAsync(); - } - public async Task Button1ClickAsync() - { - // Do asynchronous work. - await Task.Delay(1000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Void Return Async Method - parallel requests - should show 1 diagnostic - [TestMethod] - public void AV014_DACF_VoidReturnAsyncParallelRequests() - { - var body = @" - public async void SomeAsyncTask() - { - int value = 708; - await ValuePlusOne(value); - await ValuePlusTwo(value); - await ValuePlusThree(value); - } - - public async Task ValuePlusOne(int num) - { - await Task.Delay(90); - - return num; - } - - public async Task ValuePlusTwo(int num) - { - await Task.Delay(990); - - return num; - } - - public async Task ValuePlusThree(int num) - { - await Task.Delay(9990); - - return num; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected001.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 27) }; - - VerifyCSharpDiagnostic(test, expected001); - - var fixbody = @" - public async Task SomeAsyncTask() - { - int value = 708; - await ValuePlusOne(value); - await ValuePlusTwo(value); - await ValuePlusThree(value); - } - - public async Task ValuePlusOne(int num) - { - await Task.Delay(90); - - return num; - } - - public async Task ValuePlusTwo(int num) - { - await Task.Delay(990); - - return num; - } - - public async Task ValuePlusThree(int num) - { - await Task.Delay(9990); - - return num; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // No diagnostic shown during async void return when asynchronous event handler is used - parallel requests - [TestMethod] - public void AV015_None_VoidReturnaSyncWithEventParallel() - { - var body = @" - private async void button1_Click(object sender, EventArgs e) - { - await Button1ClickAsync(); - await Button2ClickAsync(); - } - public async Task Button1ClickAsync() - { - // Do asynchronous work. - await Task.Delay(1000); - } - public async Task Button2ClickAsync() - { - //Do asynchronous work. - await Task.Delay(3000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // No diagnostic shown during async void return when asynchronous event handler is used - serial requests - [TestMethod] - public void AV016_None_VoidReturnaSyncWithEventSerial() - { - var body = @" - private async void button1_Click(object sender, EventArgs e) - { - await Button1ClickAsync(); - await Button1ClickAsync(); - await Button1ClickAsync(); - } - public async Task Button1ClickAsync() - { - // Do asynchronous work. - await Task.Delay(1000); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Void Return Async Method with no event handlers - 1 diagnostic should show - [TestMethod] - public void AV017_DACF_AsyncVoidWithOutEvent1() - { - var body = @" - public async void AsyncVoidReturnTaskT() - { - Console.WriteLine(""Begin Program""); - Console.WriteLine(await AsyncTaskReturnT()); - Console.WriteLine(""End Program""); - } - - private async Task AsyncTaskReturnT() - { - await Task.Delay(65656565); - - return ""Program is processing....""; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected001.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 27) }; - - VerifyCSharpDiagnostic(test, expected001); - - var fixbody = @" - public async Task AsyncVoidReturnTaskT() - { - Console.WriteLine(""Begin Program""); - Console.WriteLine(await AsyncTaskReturnT()); - Console.WriteLine(""End Program""); - } - - private async Task AsyncTaskReturnT() - { - await Task.Delay(65656565); - - return ""Program is processing....""; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Void Return Async Method with no event handlers - 1 diagnostic should show - [TestMethod] - public void AV018_DACF_AsyncVoidWithOutEvent2() - { - var body = @" - private async void WaitForIt(int value) - { - await Task.Delay(value); - } - private void DoSomethingSilent(int value) - { - WaitForIt(value); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected001.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 28) }; - - VerifyCSharpDiagnostic(test, expected001); - - var fixbody = @" - private async Task WaitForIt(int value) - { - await Task.Delay(value); - } - private void DoSomethingSilent(int value) - { - WaitForIt(value); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Check to make sure the diagnostic still shows for methods with two parameters that are not Event Handlers - [TestMethod] - public void AV019_DACF_AsyncVoidTwoParams() - { - var body = @" - private async void WaitForIt(int value, int value2) - { - await Task.Delay(value+value2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected001.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 28) }; - - VerifyCSharpDiagnostic(test, expected001); - - var fixbody = @" - private async Task WaitForIt(int value, int value2) - { - await Task.Delay(value+value2); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Check to make sure the diagnostic still shows for methods with a parameter that implements Event Handler - [TestMethod] - public void AV020_DA_AsyncVoidImplementsEvent() - { - var body = @" - using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading; -using System.Threading.Tasks; - -namespace TestingTemplate -{ - [TestClass] - public class UnitTest1 - { - //Show no diagnostic for a param that implements an EventArgs - public async void EventHandlerImplementAsync(object sender, ImplementsEvents e) - { - await Task.Delay(1000); - } - } - public class ImplementsEvents : EventArgs - { - public ImplementsEvents() { } - } -}"; - var test = body; - VerifyCSharpDiagnostic(test); - } - - // Check to make sure the diagnostic still shows for methods with a parameter that implements Event Handler - [TestMethod] - public void AV021_DA_AsyncVoidParentImplementsEvent() - { - var body = @" - using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading; -using System.Threading.Tasks; - -namespace TestingTemplate -{ - [TestClass] - public class UnitTest1 - { - //Show no diagnostic for a param that implements an EventArgs - public async void EventHandlerImplementAsync(object sender, ParentImplementsEvents e) - { - await Task.Delay(1000); - } - } - public class ImplementsEvents : EventArgs - { - public ImplementsEvents() { } - } - - public class ParentImplementsEvents : ImplementsEvents - { - public ParentImplementsEvents() { } - } -} -"; - var test = body; - VerifyCSharpDiagnostic(test); - } - - // Check to make sure the diagnostic still shows for methods with a parameter that implements Event Handler - [TestMethod] - public void AV022_DA_AsyncVoidGrandfatherImplementsEvent() - { - var body = @" - using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading; -using System.Threading.Tasks; - -namespace TestingTemplate -{ - [TestClass] - public class UnitTest1 - { - //Show no diagnostic for a param that implements an EventArgs - public async void EventHandlerImplementAsync(object sender, ParentParentImplementsEvents e) - { - await Task.Delay(1000); - } - } - public class ImplementsEvents : EventArgs - { - public ImplementsEvents() { } - } - - public class ParentImplementsEvents : ImplementsEvents - { - public ParentImplementsEvents() { } - } - public class ParentParentImplementsEvents : ParentImplementsEvents - { - public ParentParentImplementsEvents() { } - } -}"; - var test = body; - VerifyCSharpDiagnostic(test); - } - - #region VB Tests - - // Check to make sure the diagnostic appears for VB - [TestMethod] - public void AV023_DA_VBDiagnosticSimple() - { - var body = @" -Module Module1 - - Async Sub example() - Await Task.Delay(100) - End Sub - -End Module -"; - var test = body; - - DiagnosticResult expected001vb = new DiagnosticResult - { - Id = "Async001", - Message = "This method has the async keyword but it returns void", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.vb", 4, 15) } - }; - - VerifyBasicDiagnostic(test, expected001vb); - } - - // Check to make sure the diagnostic does not appear for the case of VB event handlers - [TestMethod] - public void AV024_None_VBEventHandler() - { - var body = @" -imports System - -Module Module1 - - Async Sub event_method(sender As Object, e As EventArgs) - Await Task.Delay(100) - End Sub - -End Module -"; - var test = body; - VerifyBasicDiagnostic(test); - } - - #endregion - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/BlockingAsyncTests.cs b/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/BlockingAsyncTests.cs deleted file mode 100644 index efa6cfc669b2ca1685ffc5cfedb1f9e4d3996fdf..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/BlockingAsyncTests.cs +++ /dev/null @@ -1,714 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TestTemplate; - -namespace AsyncPackage.Tests -{ - /// - /// Unit Tests for the BlockingAsyncAnalyzer and BlockingAsyncCodeFix - /// - [TestClass] - public class BlockingAsyncTests : CodeFixVerifier - { - private const string DefaultHeader = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { "; - - private const string DefaultFooter = @" - } -}"; - - private DiagnosticResult expected = new DiagnosticResult - { - Id = "Async006", - Message = "This method is blocking on async code", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new BlockingAsyncCodeFix(); - } - - protected override CodeFixProvider GetBasicCodeFixProvider() - { - return new BlockingAsyncCodeFix(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new BlockingAsyncAnalyzer(); - } - - protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return new BlockingAsyncAnalyzer(); - } - - // No methods - no diagnostic should show - [TestMethod] - public void BA001_None_NoMethodNoDiagnostics() - { - var body = @""; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Wait block on async code - 1 diagnostic - code fix - [TestMethod] - public void BA002_DACF_WaitBlockAsyncCode() - { - var body = @" - async Task fooAsync(int somenumber) - { - foo1Async(somenumber).Wait(); - return somenumber; - } - - async Task foo1Async(int value) - { - await Task.Yield(); - return value; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task fooAsync(int somenumber) - { - await foo1Async(somenumber); - return somenumber; - } - - async Task foo1Async(int value) - { - await Task.Yield(); - return value; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Result block on async code - 1 diagnostic - code fix - [TestMethod] - public void BA003_DACF_ResultBlockAsyncCode() - { - var body = @" - async Task foo2Async(int somenumber) - { - var temp = foo3Async(somenumber).Result; - } - - async Task foo3Async(int value) - { - await Task.Yield(); - return value; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 24) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task foo2Async(int somenumber) - { - var temp = await foo3Async(somenumber); - } - - async Task foo3Async(int value) - { - await Task.Yield(); - return value; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Invocation expression assigned to variable - 1 diagnostic - [TestMethod] - public void BA004_DA_InvocationExpressionVariable() - { - var body = @" - async Task> SomeMethodAsync() - { - var t = addAsync(25, 50); - t.Wait(); - - return t; - } - - async Task addAsync(int num1, int num2) - { - await Task.Delay(29292); - return (num1 + num2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 17, 13) }; - - var fixbody = @" - async Task> SomeMethodAsync() - { - var t = addAsync(25, 50); - await t; - - return t; - } - - async Task addAsync(int num1, int num2) - { - await Task.Delay(29292); - return (num1 + num2); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpDiagnostic(test, expected); - VerifyCSharpFix(test, fixtest); - } - - // Async method using waitall on task - 1 diagnostic - code fix - [TestMethod] - public void BA005_DACF_WaitAllBlockAsyncCode() - { - var body = @" - async Task WaitAllAsync() - //Source: http://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx - { - Func action = (object obj) => - { - int i = (int)obj; - - // The tasks that receive an argument between 2 and 5 throw exceptions - if (2 <= i && i <= 5) - { - throw new InvalidOperationException(); - } - - int tickCount = Environment.TickCount; - - return tickCount; - }; - - const int n = 10; - - // Construct started tasks - Task[] tasks = new Task[n]; - for (int i = 0; i.Factory.StartNew(action, i); - } - - // Exceptions thrown by tasks will be propagated to the main thread - // while it waits for the tasks. The actual exceptions will be wrapped in AggregateException. - try - { - // Wait for all the tasks to finish. - Task.WaitAll(tasks); - - // We should never get to this point - } - catch (AggregateException e) - { - for (int j = 0; j action = (object obj) => - { - int i = (int)obj; - - // The tasks that receive an argument between 2 and 5 throw exceptions - if (2 <= i && i <= 5) - { - throw new InvalidOperationException(); - } - - int tickCount = Environment.TickCount; - - return tickCount; - }; - - const int n = 10; - - // Construct started tasks - Task[] tasks = new Task[n]; - for (int i = 0; i.Factory.StartNew(action, i); - } - - // Exceptions thrown by tasks will be propagated to the main thread - // while it waits for the tasks. The actual exceptions will be wrapped in AggregateException. - try - { - // Wait for all the tasks to finish. - await Task.WhenAll(tasks); - - // We should never get to this point - } - catch (AggregateException e) - { - for (int j = 0; j SleepHeadAsync(string phrase) - { - Thread.Sleep(9999); - - return phrase; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task SleepHeadAsync(string phrase) - { - await Task.Delay(9999); - - return phrase; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Thread.sleep within loop - 1 diagnostic - code fix - [TestMethod] - public void BA007_DACF_ThreadSleepInLoop() - { - var body = @" - async Task SleepAsync() - { - for (int i = 0; i < 5; i++) - { - Thread.Sleep(3000); - } - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 18, 17) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task SleepAsync() - { - for (int i = 0; i < 5; i++) - { - await Task.Delay(3000); - } - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Thread.sleep(timespan) - 1 diagnostic - code fix - [TestMethod] - public void BA008_DACF_AsyncWithThreadSleepTimeSpan() - { - var body = @" - async Task TimeSpanAsync() - { - TimeSpan time = new TimeSpan(1000); - Thread.Sleep(time); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 17, 13) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task TimeSpanAsync() - { - TimeSpan time = new TimeSpan(1000); - await Task.Delay(time); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Thread.sleep(TimeSpan) within loop - 1 diagnostic - code fix - [TestMethod] - public void BA009_DACF_ThreadSleepTimeSpanInForLoop() - { - var body = @" - async Task TimespaninForloopAsync() - { - TimeSpan interval = new TimeSpan(2, 20, 55, 33, 0); - - for (int i = 0; i < 5; i++) - { - Thread.Sleep(interval); - } - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 20, 17) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task TimespaninForloopAsync() - { - TimeSpan interval = new TimeSpan(2, 20, 55, 33, 0); - - for (int i = 0; i < 5; i++) - { - await Task.Delay(interval); - } - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Thread.sleep(timespan) within while loop - 1 diagnostic - code fix - [TestMethod] - public void BA010_DACF_ThreadSleepTimeSpanInWhileLoop() - { - var body = @" - async Task TimeSpanWhileLoop() - { - bool trueorfalse = true; - TimeSpan interval = new TimeSpan(0, 0, 2); - while (trueorfalse) - { - for (int i = 0; i < 5; i++) - { - Thread.Sleep(interval); - } - trueorfalse = false; - } - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected.Id = "Async006"; - expected.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 22, 21) }; - - VerifyCSharpDiagnostic(test, expected); - - var fixbody = @" - async Task TimeSpanWhileLoop() - { - bool trueorfalse = true; - TimeSpan interval = new TimeSpan(0, 0, 2); - while (trueorfalse) - { - for (int i = 0; i < 5; i++) - { - await Task.Delay(interval); - } - trueorfalse = false; - } - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest, null, true); - } - - // Async with task.delay - no diagnostic - [TestMethod] - public void BA011_None_AsyncWithAsync() - { - var body = @" - async Task SleepNowAsync() - { - await Task.Delay(1000); - }"; - var test = DefaultHeader + body + DefaultFooter; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a function with thread.sleep in it - out of scope, no diagnostic - [TestMethod] - public void BA012_None_HiddenSyncCode() - { - var body = @" - async Task CallAnotherMethodAsync() - { - SleepAlittle(5888); - } - - public void SleepAlittle(int value) - { - Thread.Sleep(value); - }"; - var test = DefaultHeader + body + DefaultFooter; - VerifyCSharpDiagnostic(test); - } - - // Non async method calls thread.wait - no diagnostic - [TestMethod] - public void BA013_None_NonAsyncWait() - { - var body = @" - Task Example() - { - Task.Wait(); - }"; - var test = DefaultHeader + body + DefaultFooter; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a different Task method - no diagnostic - [TestMethod] - public void BA014_None_AsyncWithOtherMethod() - { - var body = @" - async Task Example() - { - await Task.Delay(100); - }"; - var test = DefaultHeader + body + DefaultFooter; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a Wait method on a Task, but not the same Wait - no diagnostic - [TestMethod] - public void BA015_None_AsyncCallsOtherWait() - { - var test = @" -using System; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - async Task Example(Action a, string s) - { - await Task.Delay(100); - - var t = new Task(a); - t.Wait(s); - } - - } - - public static class MyTaskExtensions - { - public static void Wait(this Task t, string s) - { - s = s + 1; - } - } -}"; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a WaitAll method on a Task, but not the same WaitAll - no diagnostic - [TestMethod] - public void BA016_None_AsyncCallsOtherWaitAll() - { - var test = @" -using System; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - async Task Example(Action a, string s) - { - await Task.Delay(100); - - var t = new Task(a); - t.WaitAll(s); - } - - } - - public static class MyTaskExtensions - { - public static void WaitAll(this Task t, string s) - { - s = s + 1; - } - } -}"; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a WaitAny method on a Task, but not the same WaitAny - no diagnostic - [TestMethod] - public void BA017_None_AsyncCallsOtherWaitAny() - { - var test = @" -using System; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - async Task Example(Action a, string s) - { - await Task.Delay(100); - - var t = new Task(a); - t.WaitAny(s); - } - - } - - public static class MyTaskExtensions - { - public static void WaitAny(this Task t, string s) - { - s = s + 1; - } - } -}"; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a Sleep method on a Task, but not the same Sleep - no diagnostic - [TestMethod] - public void BA018_None_AsyncCallsOtherSleep() - { - var test = @" -using System; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - async Task Example(Action a, string s) - { - await Task.Delay(100); - - var t = new Task(a); - t.Sleep(s); - } - - } - - public static class MyTaskExtensions - { - public static void Sleep(this Task t, string s) - { - s = s + 1; - } - } -}"; - VerifyCSharpDiagnostic(test); - } - - // Async method calls a GetResult method on a Task, but not the same GetResult - no diagnostic - [TestMethod] - public void BA019_None_AsyncCallsOtherGetResult() - { - var test = @" -using System; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - async Task Example(Action a, string s) - { - await Task.Delay(100); - - var t = new Task(a); - t.GetResult(s); - } - - } - - public static class MyTaskExtensions - { - public static void GetResult(this Task t, string s) - { - s = s + 1; - } - } -}"; - VerifyCSharpDiagnostic(test); - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/CancellationTests.cs b/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/CancellationTests.cs deleted file mode 100644 index 7e429c0490042fa0001d8f729468c00b05f71c0b..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/CancellationTests.cs +++ /dev/null @@ -1,697 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TestTemplate; - -namespace AsyncPackage.Tests -{ - /// - /// Unit Tests for the CancellationAnalyzer and CancellationCodeFix - /// - [TestClass] - public class CancellationTests : CodeFixVerifier - { - private const string DefaultHeader = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { "; - - private const string DefaultFooter = @" - } -}"; - - private DiagnosticResult expected005 = new DiagnosticResult - { - Id = "Async005", - Message = "This method can take a CancellationToken", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new CancellationCodeFix(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new CancellationAnalyzer(); - } - - // No methods - no diagnostic should show - [TestMethod] - public void C001_None_NoMethodNoDiagnostics() - { - var body = @""; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // One method can pass a CT to another and does - no diagnostic - [TestMethod] - public void C002_None_BasicCasePass() - { - var body = @" - public void BigMethod(CancellationToken cancellationToken) - { - SmallMethod(cancellationToken); - return; - } - - public void SmallMethod(CancellationToken cancellationToken = default(CancellationToken)){ - return; - } - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // One method can pass a CT to another and does not - [TestMethod] - public void C003_DACF_BasicCaseNoPass() - { - var body = @" - public void BigMethod(CancellationToken cancellationToken) - { - SmallMethod(); - return; - } - - public void SmallMethod(CancellationToken cancellationToken = default(CancellationToken)) - { - return; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(CancellationToken cancellationToken) - { - SmallMethod(cancellationToken); - return; - } - - public void SmallMethod(CancellationToken cancellationToken = default(CancellationToken)) - { - return; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // One method can pass a CT to another and instead passes CancellationToken.None - no diagnostic - [TestMethod] - public void C004_None_BasicCaseNone() - { - var body = @" - public void BigMethod(CancellationToken cancellationToken) - { - SmallMethod(CancellationToken.None); - return; - } - - public void SmallMethod(CancellationToken cancellationToken = default(CancellationToken)){ - return; - } - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Both methods have multiple parameters - no diagnostic - [TestMethod] - public void C005_None_ManyParamsPass() - { - var body = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, cancellationToken, c); - return; - } - - public void SmallMethod(string b, CancellationToken cancellationToken = default(CancellationToken), double c = 0.5){ - return; - } - "; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Both methods have multiple parameters - 1 diagnostic - [TestMethod] - public void C006_DACF_ManyParamsNoPass() - { - var body = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c, cancellationToken); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Containing method has many other things in it - 1 diagnostic - [TestMethod] - public void C007_DACF_OtherMiscInContainingMethodNoPass() - { - var body = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - for(int i = 0; i < a; i++){ - b = b + i; - } - - double d = c + 10.5; - - //trivia here - SmallMethod(b, d); - - string e = b.ToLower(); - - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - string f = (b+c).ToUpper(); - return; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 23, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - for(int i = 0; i < a; i++){ - b = b + i; - } - - double d = c + 10.5; - - //trivia here - SmallMethod(b, d, cancellationToken); - - string e = b.ToLower(); - - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - string f = (b+c).ToUpper(); - return; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Containing methods have multiple invocations where a token can be passed - 1 diagnostic - [TestMethod] - public void C008_DACF_MultipleInvocationsInContainingMethod() - { - var body = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c, cancellationToken); - SmallMethod(b, c); - SmallMethod(b, c, CancellationToken.None); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 17, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c, cancellationToken); - SmallMethod(b, c, cancellationToken); - SmallMethod(b, c, CancellationToken.None); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Containing methods have multiple invocations where a token can be passed - 1 diagnostic - public void C009_DACF_MultipleNoPassInContainingMethod() - { - var body = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c); - SmallMethod(b, c, CancellationToken.None); - SmallMethod(b, c); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - var test = DefaultHeader + body + DefaultFooter; - - DiagnosticResult expected1 = new DiagnosticResult - { - Id = "Async005", - Message = "This method can take a CancellationToken", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) } - }; - - DiagnosticResult expected2 = new DiagnosticResult - { - Id = "Async005", - Message = "This method can take a CancellationToken", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 18, 13) } - }; - - VerifyCSharpDiagnostic(test, expected1, expected2); - - var fixbody = @" - public void BigMethod(int a, string b, CancellationToken cancellationToken, double c) - { - SmallMethod(b, c, cancellationToken); - SmallMethod(b, c, CancellationToken.None); - SmallMethod(b, c, cancellationToken); - return; - } - - public void SmallMethod(string b, double c = 0.5, CancellationToken cancellationToken = default(CancellationToken)){ - return; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Nested invocations - 1 diagnostic - [TestMethod] - public void C010_DACF_NestedInvocation() - { - var body = @" - public void OuterInvocation(CancellationToken c) - { - var x = MiddleInvocation(InnerInvocation(0)); - } - public bool MiddleInvocation(bool t) - { - return t; - } - public bool InnerInvocation(int i, CancellationToken c = new CancellationToken()) - { - return true; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 38) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void OuterInvocation(CancellationToken c) - { - var x = MiddleInvocation(InnerInvocation(0, c)); - } - public bool MiddleInvocation(bool t) - { - return t; - } - public bool InnerInvocation(int i, CancellationToken c = new CancellationToken()) - { - return true; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Multiple tokens in scope, both passed - no diagnostic - [TestMethod] - public void C011_None_MultipleTokensInScopePass() - { - var body = @" - public void BigMethod (int s, CancellationToken ct1, CancellationToken ct2) - { - SmallMethod(s, ct1); - SmallMethod(s, ct2); - } - - public void SmallMethod (int s, CancellationToken ct = default(CancellationToken)) - { - s = s == 123 ? 456 : 789; - }"; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Multiple tokens in scope - 1 diagnostic - // Codefix should take the first token and put it in the first available slot - [TestMethod] - public void C012_DACF_MultipleTokensInScopeNoPass() - { - var body = @" - public void BigMethod (int s, CancellationToken ct1, CancellationToken ct2) - { - SmallMethod(s); - SmallMethod(s, ct1); - SmallMethod(s, ct2); - } - - public void SmallMethod (int s, CancellationToken ct = default(CancellationToken)) - { - s = s == 123 ? 456 : 789; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod (int s, CancellationToken ct1, CancellationToken ct2) - { - SmallMethod(s, ct1); - SmallMethod(s, ct1); - SmallMethod(s, ct2); - } - - public void SmallMethod (int s, CancellationToken ct = default(CancellationToken)) - { - s = s == 123 ? 456 : 789; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // No tokens to pass - no diagnostic - [TestMethod] - public void C013_None_NoTokensToPass() - { - var body = @" - public void BigMethod (int s) - { - SmallMethod(s); - } - - public void SmallMethod (int s, CancellationToken ct = default(CancellationToken)) - { - s = s == 123 ? 456 : 789; - }"; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Multiple default parameters - 1 diagnostic - // Adding CancellationToken but not other default should not cause other diagnostics - [TestMethod] - public void C014_DACF_MultipleDefaultsNoPass() - { - var body = @" - public void BigMethod(int s, CancellationToken ct) - { - SmallMethod(s); - } - - public void SmallMethod(int s, CancellationToken ct = default(CancellationToken), int x = 0) - { - s = s + 1; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(int s, CancellationToken ct) - { - SmallMethod(s, ct); - } - - public void SmallMethod(int s, CancellationToken ct = default(CancellationToken), int x = 0) - { - s = s + 1; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Multiple tokens can be passed and multiple slots to pass them in - 1 diagnostic - // Should just take the first token and put it in the first slot - anything else is out of scope - [TestMethod] - public void C015_DACF_MultipleTokensMultipleSlots() - { - var body = @" - public void BigMethod(int s, CancellationToken ct1, CancellationToken ct2, CancellationToken ct3) - { - SmallMethod(s); - } - - public void SmallMethod(int s, CancellationToken ct1 = default(CancellationToken), int x = 0, CancellationToken ct2 = default(CancellationToken)) - { - s = s + 1; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(int s, CancellationToken ct1, CancellationToken ct2, CancellationToken ct3) - { - SmallMethod(s, ct1); - } - - public void SmallMethod(int s, CancellationToken ct1 = default(CancellationToken), int x = 0, CancellationToken ct2 = default(CancellationToken)) - { - s = s + 1; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // CancellationToken can be passed, but it's a user made class called CancellationToken - no diagnostic - [TestMethod] - public void C016_None_NotSystemCancellationToken() - { - var test = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Threading; - -namespace ConsoleApplication1 -{ - class Program - { - public void BigMethod(CancellationToken ct) - { - - SmallMethod(); - - } - - public void SmallMethod(CancellationToken ct = null) - { - } - - } - - public class CancellationToken - { - - } -}"; - VerifyCSharpDiagnostic(test); - } - - // An overload of the method takes a CancellationToken - no diagnostic, out of scope - [TestMethod] - public void C017_None_OverloadWithNoPass() - { - var body = @" - public void BigMethod(CancellationToken ct) - { - SmallMethod(5); - } - - public void SmallMethod(int n) - { - - } - - public void SmallMethod(int n, CancellationToken ct) - { - - }"; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // An overload of the method uses default(CancellationToken) - no diagnostic, out of scope - [TestMethod] - public void C018_None_OverloadWithDefaultNoPass() - { - var body = @" - public void BigMethod(CancellationToken ct) - { - SmallMethod(5); - } - - public void SmallMethod(int n) - { - - } - - public void SmallMethod(int n, CancellationToken ct = default(CancellationToken)) - { - - }"; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // An overload of the method takes a CancellationToken, but the overload being used does not - no diagnostic - [TestMethod] - public void C019_None_OverloadWithTokenNotPossible() - { - var body = @" - public void BigMethod(CancellationToken ct) - { - SmallMethod(5, 4); - } - - public void SmallMethod(int n, int o) - { - - } - - public void SmallMethod(int n, CancellationToken ct = default(CancellationToken)) - { - - }"; - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // The overload explicitly being used takes a CancellationToken- 1 diagnostic - [TestMethod] - public void C020_DACF_UsedOverloadTakesTokenNoPass() - { - var body = @" - public void BigMethod(CancellationToken ct) - { - SmallMethod(5, 4); - } - - public void SmallMethod(int n) - { - - } - - public void SmallMethod(int n, int o, CancellationToken ct = default(CancellationToken)) - { - - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected005.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 13) }; - - VerifyCSharpDiagnostic(test, expected005); - - var fixbody = @" - public void BigMethod(CancellationToken ct) - { - SmallMethod(5, 4, ct); - } - - public void SmallMethod(int n) - { - - } - - public void SmallMethod(int n, int o, CancellationToken ct = default(CancellationToken)) - { - - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/RenameAsyncTests.cs b/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/RenameAsyncTests.cs deleted file mode 100644 index 844a67816eda725eed0e768502d073a674ed4a93..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Async_Tests/RenameAsyncTests.cs +++ /dev/null @@ -1,648 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TestTemplate; - -namespace AsyncPackage.Tests -{ - /// - /// Unit Tests for the RenameAsyncAnalyzer and RenameAsyncCodeFix - /// - [TestClass] - public class RenameAsyncTests : CodeFixVerifier - { - private const string DefaultHeader = @" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; - -namespace ConsoleApplication1 -{ - class Program - { "; - - private const string DefaultFooter = @" - } -}"; - - private DiagnosticResult expected002 = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - - // Location should be changed within test methods - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 10, 10) } - }; - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new RenameAsyncCodeFix(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new RenameAsyncAnalyzer(); - } - - protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return new RenameAsyncAnalyzer(); - } - - // No methods - no diagnostic should show - [TestMethod] - public void RA001_None_NoMethodNoDiagnostics() - { - var body = @""; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task async method includes "Async" - no diagnostic - [TestMethod] - public void RA002_None_TaskIntReturnWithAsync() - { - var body = @" - private async Task MathAsync(int num1) - { - await Task.Delay(3000); - - return (num1); - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Task async method includes "async" - no diagnostic - [TestMethod] - public void RA003_None_TaskBoolReturnWithAsync() - { - var body = @" - private async Task TrueorFalseAsync(bool trueorfalse) - { - await Task.Delay(700); - - return !(trueorfalse); - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Async method does not includes "Async" - 1 diagnostic - [TestMethod] - public void RA004_DACF_AsyncMethodWithoutAsync() - { - var body = @" - private async Task Math(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task MathAsync(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Async method named "async" - no diagnostic - [TestMethod] - public void RA005_None_AsyncMethodNamedAsync() - { - var body = @" - private async Task Async(bool trueorfalse) - { - await Task.Delay(700); - - return !(trueorfalse); - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Non async method named "async" - no diagnostic - [TestMethod] - public void RA006_None_NonAsyncMethodnamedAsync() - { - var body = @" - public string Async(int zipcode) - { - if (zipcode == 21206) - { - return ""Baltimore""; - } - else - return ""Unknown""; - } "; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Non-Async method that do not include async - no diagnostic - [TestMethod] - public void RA007_None_NonAsyncWithoutAsync() - { - var body = @" - private string RepeatingStrings(int num, string Message) - { - while (num > 0) - { - return RepeatingStrings(num - 1, Message); - } - return Message; - }"; - - var test = DefaultHeader + body + DefaultFooter; - - VerifyCSharpDiagnostic(test); - } - - // Adding Async would cause conflict - [TestMethod] - public void RA008_DACF_MethodNameConflict1() - { - var body = @" - private async Task DoSomeMath(int number1, int number2) - { - await Task.Delay(100); - - return (number1 + number2); - } - - private async Task DoSomeMathAsync(int number1, int number2) - { - await Task.Delay(500); - - return (number1 * number2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task DoSomeMath(int number1, int number2) - { - await Task.Delay(100); - - return (number1 + number2); - } - - private async Task DoSomeMathAsync(int number1, int number2) - { - await Task.Delay(500); - - return (number1 * number2); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Method name conflict - 1 diagnostic - [TestMethod] - public void RA009_DACF_MethodNameConflict2() - { - var body = @" - private int Math(int num1, int num2) - { - return (num1 - num2); - } - - private async Task Math(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 18, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private int Math(int num1, int num2) - { - return (num1 - num2); - } - - private async Task MathAsync(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Async keyword is at beginning - should show 1 diagnostic - [TestMethod] - public void RA010_DACF_AsyncKeywordInBeginning() - { - var body = @" - public async void AsyncVoidReturnTaskT() - { - Console.WriteLine(""Begin Program""); - Console.WriteLine(await AsyncTaskReturnT()); - Console.WriteLine(""End Program""); - } - - private async Task AsyncTaskReturnT() - { - await Task.Delay(65656565); - - return ""Program is processing....""; - }"; - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 20, 36) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - public async void AsyncVoidReturnTaskT() - { - Console.WriteLine(""Begin Program""); - Console.WriteLine(await AsyncTaskReturnTAsync()); - Console.WriteLine(""End Program""); - } - - private async Task AsyncTaskReturnTAsync() - { - await Task.Delay(65656565); - - return ""Program is processing....""; - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Misspelled async in method name - 1 diagnostic - [TestMethod] - public void RA011_DACF_MisspelledAsync() - { - var body = @" - private async Task DoSomeMathAsnyc(int number1, int number2) - { - await Task.Delay(100); - - return (number1 + number2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task DoSomeMathAsync(int number1, int number2) - { - await Task.Delay(100); - - return (number1 + number2); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // No capital A in Async method name - 1 diagnostic - [TestMethod] - public void RA012_DACF_NoCapitalAinAsync() - { - var body = @" - public static Task ReturnAAAasync() - { - return Task.Run(() => - { - return (""AAA""); - }); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 36) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - public static Task ReturnAAAAsync() - { - return Task.Run(() => - { - return (""AAA""); - }); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Method name contains async but misspelled and no capitol A - 1 diagnostic - [TestMethod] - public void RA013_DACF_MisspelledAndNoCapitolA() - { - var body = @" - public static async Task Multiplyanysc(int factor1, int factor2) - { - //delay three times - await Task.Delay(100); - await Task.Delay(100); - await Task.Delay(100); - - return (factor1 * factor2); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 39) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - public static async Task MultiplyAsync(int factor1, int factor2) - { - //delay three times - await Task.Delay(100); - await Task.Delay(100); - await Task.Delay(100); - - return (factor1 * factor2); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Misspelled Async named Async method - diagnostic should rename to Async - [TestMethod] - public void RA014_DACF_AsyncnamedAsyncMethodMisspelled() - { - var body = @" - private async Task Anysc(bool trueorfalse) - { - await Task.Delay(700); - - return !(trueorfalse); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 34) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task Async(bool trueorfalse) - { - await Task.Delay(700); - - return !(trueorfalse); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Very close spelling of async in name but wrong letters - 1 diagnostic - [TestMethod] - public void RA015_DACF_CloseSpellingOfAsyncbutNotEnough() - { - var body = @" - private async Task MathAzync(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task MathAzyncAsync(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Very close spelling of async in name but wrong letters - 1 diagnostic - [TestMethod] - public void RA016_DACF_CloseSpellingOfAsyncbutNotEnough2() - { - var body = @" - private async Task MathAsytnc(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - expected002.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 33) }; - - VerifyCSharpDiagnostic(test, expected002); - - var fixbody = @" - private async Task MathAsytncAsync(int num1) - { - await Task.Delay(3000); - - return (num1); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - // Test multiple fixes in a row - should have 4 diagnostics and fixes - [TestMethod] - public void RA017_DACF_MultipleMisspellings() - { - var body = @" - private async Task Wait10Asycn() - { - await Task.Delay(10); - } - private async Task Wait50Aysnc() - { - await Task.Delay(50); - } - private async Task Wait100Ayscn() - { - await Task.Delay(100); - } - private async Task Wait200asycn() - { - await Task.Delay(200); - }"; - - var test = DefaultHeader + body + DefaultFooter; - - DiagnosticResult expected1 = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 13, 28) } - }; - - DiagnosticResult expected2 = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 17, 28) } - }; - - DiagnosticResult expected3 = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 21, 28) } - }; - - DiagnosticResult expected4 = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.cs", 25, 28) } - }; - - VerifyCSharpDiagnostic(test, expected1, expected2, expected3, expected4); - - var fixbody = @" - private async Task Wait10Async() - { - await Task.Delay(10); - } - private async Task Wait50Async() - { - await Task.Delay(50); - } - private async Task Wait100Async() - { - await Task.Delay(100); - } - private async Task Wait200Async() - { - await Task.Delay(200); - }"; - - var fixtest = DefaultHeader + fixbody + DefaultFooter; - VerifyCSharpFix(test, fixtest); - } - - #region VB Tests - - // Simple missing "Async" test for VB - 1 diagnostic - // Codefix is out of scope - [TestMethod] - public void RA018_DA_VBRenameSimple() - { - var body = @" -imports System -imports System.Threading.Tasks - -Module Module1 - - Async Function example() As Task - End Function - -End Module -"; - - var test = body; - - DiagnosticResult expectedvb = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.vb", 7, 20) } - }; - - VerifyBasicDiagnostic(test, expectedvb); - } - - // Misspelled "Async" test for VB - 1 diagnostic - // Codefix is out of scope - [TestMethod] - public void RA019_DA_VBRenameMisspelledAsync() - { - var body = @" -imports System -imports System.Threading.Tasks - -Module Module1 - - Async Function exampleasycn() As Task - End Function - -End Module -"; - var test = body; - - DiagnosticResult expectedvb = new DiagnosticResult - { - Id = "Async002", - Message = "This method is async but the method name does not end in Async", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { new DiagnosticResultLocation("Test0.vb", 7, 20) } - }; - - VerifyBasicDiagnostic(test, expectedvb); - } - - #endregion - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Backend/CodeFixVerifier.Helper.cs b/src/Samples/CSharp/AsyncPackage/Test/Backend/CodeFixVerifier.Helper.cs deleted file mode 100644 index 4dedf66639031c3384ba19badd54bbf585bb52cc..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Backend/CodeFixVerifier.Helper.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; - -namespace TestTemplate -{ - /// - /// Diagnostic Producer class with extra methods dealing with applying codefixes - /// All methods are static - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Apply the inputted CodeAction to the inputted document. - /// Meant to be used to apply codefixes. - /// - /// The Document to apply the fix on - /// A CodeAction that will be applied to the Document. - /// A Document with the changes from the CodeAction - private static Document ApplyFix(Document document, CodeAction codeAction) - { - var operations = codeAction.GetOperationsAsync(CancellationToken.None).GetAwaiter().GetResult(); - var solution = operations.OfType().Single().ChangedSolution; - return solution.GetDocument(document.Id); - } - - /// - /// Compare two collections of Diagnostics,and return a list of any new diagnostics that appear only in the second collection. - /// Note: Considers Diagnostics to be the same if they have the same Ids. In the case of multiple diagnostics with the same Id in a row, - /// this method may not necessarily return the new one. - /// - /// The Diagnostics that existed in the code before the CodeFix was applied - /// The Diagnostics that exist in the code after the CodeFix was applied - /// A list of Diagnostics that only surfaced in the code after the CodeFix was applied - private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics) - { - var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - - int oldIndex = 0; - int newIndex = 0; - - while (newIndex < newArray.Length) - { - if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) - { - ++oldIndex; - ++newIndex; - } - else - { - yield return newArray[newIndex++]; - } - } - } - - /// - /// Get the existing compiler diagnostics on the inputted document. - /// - /// The Document to run the compiler diagnostic analyzers on - /// The compiler diagnostics that were found in the code - private static IEnumerable GetCompilerDiagnostics(Document document) - { - return document.GetSemanticModelAsync().Result.GetDiagnostics(); - } - - /// - /// Given a Document, turn it into a string based on the syntax root - /// - /// The Document to be converted to a string - /// A string containing the syntax of the Document after formatting - private static string GetStringFromDocument(Document document) - { - var simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result; - var root = simplifiedDoc.GetSyntaxRootAsync().Result; - root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace); - return root.GetText().ToString(); - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticResult.cs b/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticResult.cs deleted file mode 100644 index 5ee7d2d0cf59c2b7a21237275d9396b6d7b79ae2..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticResult.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.CodeAnalysis; - -namespace TestTemplate -{ - /// - /// Location where the diagnostic appears, as determined by path, line number, and column number. - /// - public struct DiagnosticResultLocation - { - public DiagnosticResultLocation(string path, int line, int column) - { - if (line < 0 && column < 0) - { - throw new ArgumentOutOfRangeException("At least one of line and column must be > 0"); - } - - if (line < -1 || column < -1) - { - throw new ArgumentOutOfRangeException("Both line and column must be >= -1"); - } - - this.Path = path; - this.Line = line; - this.Column = column; - } - - public string Path; - public int Line; - public int Column; - } - - /// - /// Struct that stores information about a Diagnostic appearing in a source - /// - public struct DiagnosticResult - { - private DiagnosticResultLocation[] locations; - - public DiagnosticResultLocation[] Locations - { - get - { - if (this.locations == null) - { - this.locations = Array.Empty(); - } - - return this.locations; - } - - set - { - this.locations = value; - } - } - - public DiagnosticSeverity Severity { get; set; } - - public string Id { get; set; } - - public string Message { get; set; } - - public string Path - { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Path : ""; - } - } - - public int Line - { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Line : -1; - } - } - - public int Column - { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Column : -1; - } - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticVerifier.Helper.cs b/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticVerifier.Helper.cs deleted file mode 100644 index 9be5d8935c37789715af9e67ae97eb2bb71b10f5..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Backend/DiagnosticVerifier.Helper.cs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; -using CSharp = Microsoft.CodeAnalysis.CSharp; -using VisualBasic = Microsoft.CodeAnalysis.VisualBasic; - -namespace TestTemplate -{ - /// - /// Class for turning strings into documents and getting the diagnostics on them - /// All methods are static - /// - public abstract partial class DiagnosticVerifier - { - private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); - private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); - private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); - private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); - - internal static string DefaultFilePathPrefix = "Test"; - internal static string CSharpDefaultFileExt = "cs"; - internal static string VisualBasicDefaultExt = "vb"; - internal static string CSharpDefaultFilePath = DefaultFilePathPrefix + 0 + "." + CSharpDefaultFileExt; - internal static string VisualBasicDefaultFilePath = DefaultFilePathPrefix + 0 + "." + VisualBasicDefaultExt; - internal static string TestProjectName = "TestProject"; - - #region Get Diagnostics - - /// - /// Given classes in the form of strings, their language, and an IDiagnosticAnlayzer to apply to it, return the diagnostics found in the string after converting it to a document. - /// - /// Classes in the form of strings - /// The language the source classes are in - /// The analyzer to be run on the sources - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer) - { - return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language)); - } - - /// - /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it. - /// The returned diagnostics are then ordered by location in the source document. - /// - /// The analyzer to run on the documents - /// The Documents that the analyzer will be run on - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents) - { - var projects = new HashSet(); - foreach (var document in documents) - { - projects.Add(document.Project); - } - - var diagnostics = new List(); - foreach (var project in projects) - { - var compilationWithAnalyzers = project.GetCompilationAsync().GetAwaiter().GetResult().WithAnalyzers(ImmutableArray.Create(analyzer)); - - var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().GetAwaiter().GetResult(); - foreach (var diag in diags) - { - if (diag.Location == Location.None || diag.Location.IsInMetadata) - { - diagnostics.Add(diag); - } - else - { - for (int i = 0; i < documents.Length; i++) - { - var document = documents[i]; - var tree = document.GetSyntaxTreeAsync().GetAwaiter().GetResult(); - if (tree == diag.Location.SourceTree) - { - diagnostics.Add(diag); - } - } - } - } - } - - var results = SortDiagnostics(diagnostics); - diagnostics.Clear(); - return results; - } - - /// - /// Sort diagnostics by location in source document - /// - /// The list of Diagnostics to be sorted - /// An IEnumerable containing the Diagnostics in order of Location - private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics) - { - return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - } - - #endregion - - #region Set up compilation and documents - /// - /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it. - /// - /// Classes in the form of strings - /// The language the source code is in - /// An array of Documents produced from the source strings - private static Document[] GetDocuments(string[] sources, string language) - { - if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic) - { - throw new ArgumentException("Unsupported Language"); - } - - for (int i = 0; i < sources.Length; i++) - { - string fileName = language == LanguageNames.CSharp ? "Test" + i + ".cs" : "Test" + i + ".vb"; - } - - var project = CreateProject(sources, language); - var documents = project.Documents.ToArray(); - - if (sources.Length != documents.Length) - { - throw new SystemException("Amount of sources did not match amount of Documents created"); - } - - return documents; - } - - /// - /// Create a Document from a string through creating a project that contains it. - /// - /// Classes in the form of a string - /// The language the source code is in - /// A Document created from the source string - protected static Document CreateDocument(string source, string language = LanguageNames.CSharp) - { - return CreateProject(new[] { source }, language).Documents.First(); - } - - /// - /// Create a project using the inputted strings as sources. - /// - /// Classes in the form of strings - /// The language the source code is in - /// A Project created out of the Documents created from the source strings - private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp) - { - string fileNamePrefix = DefaultFilePathPrefix; - string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt; - - var projectId = ProjectId.CreateNewId(debugName: TestProjectName); - - var solution = new AdhocWorkspace() - .CurrentSolution - .AddProject(projectId, TestProjectName, TestProjectName, language) - .AddMetadataReference(projectId, CorlibReference) - .AddMetadataReference(projectId, SystemCoreReference) - .AddMetadataReference(projectId, CSharpSymbolsReference) - .AddMetadataReference(projectId, CodeAnalysisReference); - - int count = 0; - foreach (var source in sources) - { - var newFileName = fileNamePrefix + count + "." + fileExt; - var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); - solution = solution.AddDocument(documentId, newFileName, SourceText.From(source)); - count++; - } - - return solution.GetProject(projectId); - } - #endregion - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/CodeFixVerifier.cs b/src/Samples/CSharp/AsyncPackage/Test/CodeFixVerifier.cs deleted file mode 100644 index ff64cd613b8826678cc253071c4ca59b03ec5231..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/CodeFixVerifier.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace TestTemplate -{ - /// - /// Superclass of all Unit tests made for diagnostics with codefixes. - /// Contains methods used to verify correctness of codefixes - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Returns the codefix being tested (C#) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for CSharp code - protected virtual CodeFixProvider GetCSharpCodeFixProvider() - { - return null; - } - - /// - /// Returns the codefix being tested (VB) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for VisualBasic code - protected virtual CodeFixProvider GetBasicCodeFixProvider() - { - return null; - } - - /// - /// Called to test a C# codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - protected void VerifyCSharpFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// Called to test a VB codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple in the same location - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - protected void VerifyBasicFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), GetBasicCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// General verifier for codefixes. - /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes. - /// Then gets the string after the codefix is applied and compares it with the expected result. - /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true. - /// - /// The language the source code is in - /// The analyzer to be applied to the source code - /// The codefix to be applied to the code wherever the relevant Diagnostic is found - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple in the same location - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics) - { - var document = CreateDocument(oldSource, language); - var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - var compilerDiagnostics = GetCompilerDiagnostics(document); - var attempts = analyzerDiagnostics.Length; - - for (int i = 0; i < attempts; ++i) - { - var actions = new List(); - var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); - codeFixProvider.RegisterCodeFixesAsync(context).Wait(); - if (!actions.Any()) - { - break; - } - - if (codeFixIndex != null) - { - document = ApplyFix(document, actions.ElementAt((int)codeFixIndex)); - break; - } - - document = ApplyFix(document, actions.ElementAt(0)); - analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - - var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - // Check if applying the code fix introduced any new compiler diagnostics - if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) - { - // Format and get the compiler diagnostics again so that the locations make sense in the output - document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace)); - newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - Assert.IsTrue(false, - string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n", - string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())), - document.GetSyntaxRootAsync().Result.ToFullString())); - } - - // Check if there are analyzer diagnostics left after the code fix - if (!analyzerDiagnostics.Any()) - { - break; - } - } - - // After applying all of the code fixes, compare the resulting string to the inputted one - var actual = GetStringFromDocument(document); - Assert.AreEqual(newSource, actual); - } - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/DiagnosticVerifier.cs b/src/Samples/CSharp/AsyncPackage/Test/DiagnosticVerifier.cs deleted file mode 100644 index 4e5b63effbfeaeee8fa252d41e391b1ec4dbda1d..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/DiagnosticVerifier.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace TestTemplate -{ - /// - /// Superclass of all Unit Tests for DiagnosticAnalyzers - /// - public abstract partial class DiagnosticVerifier - { - #region To be implemented by Test classes - /// - /// Get the CSharp analyzer being tested - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return null; - } - - /// - /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return null; - } - #endregion - - #region Verifier wrappers - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on - /// DiagnosticResults that should appear after the analyzer is run on the source - protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on - /// DiagnosticResults that should appear after the analyzer is run on the source - protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on - /// DiagnosticResults that should appear after the analyzer is run on the sources - protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on - /// DiagnosticResults that should appear after the analyzer is run on the sources - protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run, - /// then verifies each of them. - /// - /// An array of strings to create source documents from to run the analyzers on - /// The language of the classes represented by the source strings - /// The analyzer to be run on the source code - /// DiagnosticResults that should appear after the analyzer is run on the sources - private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected) - { - var diagnostics = GetSortedDiagnostics(sources, language, analyzer); - VerifyDiagnosticResults(diagnostics, analyzer, expected); - } - - #endregion - - #region Actual comparisons and verifications - /// - /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results. - /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic. - /// - /// The Diagnostics found by the compiler after running the analyzer on the source code - /// The analyzer that was being run on the sources - /// Diagnostic Results that should have appeared in the code - private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults) - { - int expectedCount = expectedResults.Count(); - int actualCount = actualResults.Count(); - - if (expectedCount != actualCount) - { - string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE."; - - Assert.IsTrue(false, - string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput)); - } - - for (int i = 0; i < expectedResults.Length; i++) - { - var actual = actualResults.ElementAt(i); - var expected = expectedResults[i]; - - if (expected.Line == -1 && expected.Column == -1) - { - if (actual.Location != Location.None) - { - Assert.IsTrue(false, - string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}", - FormatDiagnostics(analyzer, actual))); - } - } - else - { - VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First()); - var additionalLocations = actual.AdditionalLocations.ToArray(); - - if (additionalLocations.Length != expected.Locations.Length - 1) - { - Assert.IsTrue(false, - string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n", - expected.Locations.Length - 1, additionalLocations.Length, - FormatDiagnostics(analyzer, actual))); - } - - for (int j = 0; j < additionalLocations.Length; ++j) - { - VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]); - } - } - - if (actual.Id != expected.Id) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Id, actual.Id, FormatDiagnostics(analyzer, actual))); - } - - if (actual.Severity != expected.Severity) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual))); - } - - if (actual.GetMessage() != expected.Message) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual))); - } - } - } - - /// - /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. - /// - /// The analyzer that was being run on the sources - /// The diagnostic that was found in the code - /// The Location of the Diagnostic found in the code - /// The DiagnosticResultLocation that should have been found - private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected) - { - var actualSpan = actual.GetLineSpan(); - - Assert.IsTrue(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")), - string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); - - var actualLinePosition = actualSpan.StartLinePosition; - - // Only check line position if there is an actual line in the real diagnostic - if (actualLinePosition.Line > 0) - { - if (actualLinePosition.Line + 1 != expected.Line) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - - // Only check column position if there is an actual column position in the real diagnostic - if (actualLinePosition.Character > 0) - { - if (actualLinePosition.Character + 1 != expected.Column) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - } - #endregion - - #region Formatting Diagnostics - /// - /// Helper method to format a Diagnostic into an easily readable string - /// - /// The analyzer that this Verifier tests - /// The Diagnostics to be formatted - /// The Diagnostics formatted as a string - private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) - { - var builder = new StringBuilder(); - for (int i = 0; i < diagnostics.Length; ++i) - { - builder.AppendLine("// " + diagnostics[i].ToString()); - - var analyzerType = analyzer.GetType(); - var rules = analyzer.SupportedDiagnostics; - - foreach (var rule in rules) - { - if (rule != null && rule.Id == diagnostics[i].Id) - { - var location = diagnostics[i].Location; - if (location == Location.None) - { - builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); - } - else - { - Assert.IsTrue(location.IsInSource, - string.Format("Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata:\r\n", diagnostics[i])); - - string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt"; - var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; - - builder.AppendFormat("{0}({1}, {2}, {3}.{4})", - resultMethodName, - linePosition.Line + 1, - linePosition.Character + 1, - analyzerType.Name, - rule.Id); - } - - if (i != diagnostics.Length - 1) - { - builder.Append(','); - } - - builder.AppendLine(); - break; - } - } - } - - return builder.ToString(); - } - #endregion - } -} diff --git a/src/Samples/CSharp/AsyncPackage/Test/Properties/AssemblyInfo.cs b/src/Samples/CSharp/AsyncPackage/Test/Properties/AssemblyInfo.cs deleted file mode 100644 index c72e659c444af92b65982f9a2a5c4755e6127d93..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.InteropServices; - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d945baa9-7483-4aae-9553-6d6efabeacb0")] diff --git a/src/Samples/CSharp/AsyncPackage/Test/packages.config b/src/Samples/CSharp/AsyncPackage/Test/packages.config deleted file mode 100644 index 569e1bea867b8f83ba8a14afb0b410af4e139127..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/Test/packages.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/Samples/CSharp/AsyncPackage/packages.config b/src/Samples/CSharp/AsyncPackage/packages.config deleted file mode 100644 index 044d1574f3914cf919626f9eb5a2501a8d6fb51c..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/Samples/CSharp/AsyncPackage/source.extension.vsixmanifest b/src/Samples/CSharp/AsyncPackage/source.extension.vsixmanifest deleted file mode 100644 index f782555188ebf7b69e69b68ce76935ba842bf44a..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/source.extension.vsixmanifest +++ /dev/null @@ -1,21 +0,0 @@ - - - - - AsyncPackage - This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn"). - - - - - - - - - - - - - - - diff --git a/src/Samples/CSharp/AsyncPackage/tools/install.ps1 b/src/Samples/CSharp/AsyncPackage/tools/install.ps1 deleted file mode 100644 index 41852f5e044148d6895505b3ff87b8647c58d618..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/tools/install.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$p = Get-Project - -$analyzerPath = join-path $toolsPath "analyzers" -$analyzerFilePath = join-path $analyzerPath "AsyncPackage.dll" - -$p.Object.AnalyzerReferences.Add("$analyzerFilePath") \ No newline at end of file diff --git a/src/Samples/CSharp/AsyncPackage/tools/uninstall.ps1 b/src/Samples/CSharp/AsyncPackage/tools/uninstall.ps1 deleted file mode 100644 index 8f0e3b781a1776106578702e16c809366ab0970d..0000000000000000000000000000000000000000 --- a/src/Samples/CSharp/AsyncPackage/tools/uninstall.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$p = Get-Project - -$analyzerPath = join-path $toolsPath "analyzers" -$analyzerFilePath = join-path $analyzerPath "AsyncPackage.dll" - -$p.Object.AnalyzerReferences.Remove("$analyzerFilePath") \ No newline at end of file diff --git a/src/Samples/Samples.sln b/src/Samples/Samples.sln index 1a512a015f84ac59bc1ca9d1000f6a276a045c19..f7053193cdb23a6aa3f40492fc7263c22d364607 100644 --- a/src/Samples/Samples.sln +++ b/src/Samples/Samples.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22905.0 +VisualStudioVersion = 14.0.23017.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APISampleUnitTestsCS", "CSharp\APISampleUnitTests\APISampleUnitTestsCS.csproj", "{CFF49CC1-85B5-49F7-B14B-A6EBF2592DD2}" EndProject @@ -45,10 +45,6 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "MakeConstVB.UnitTests", "Vi EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VisualBasic", "VisualBasic", "{0A8A5052-C6C9-4E92-9027-5229466DF86C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncPackage", "CSharp\AsyncPackage\AsyncPackage.csproj", "{68D3FDD2-DA02-453B-9DF9-022F65F9265E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncPackage.Test", "CSharp\AsyncPackage\Test\AsyncPackage.Test.csproj", "{0D9287FD-F17F-4CB8-B622-904E69994AC6}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B00C02A7-7E65-46D8-B56F-2314D189E607}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.Config = .nuget\NuGet.Config @@ -207,22 +203,6 @@ Global {B9963F89-CF12-4A8D-B4BF-C2C0B9732144}.Release|Any CPU.Build.0 = Release|Any CPU {B9963F89-CF12-4A8D-B4BF-C2C0B9732144}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {B9963F89-CF12-4A8D-B4BF-C2C0B9732144}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Release|Any CPU.Build.0 = Release|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {68D3FDD2-DA02-453B-9DF9-022F65F9265E}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Release|Any CPU.Build.0 = Release|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {0D9287FD-F17F-4CB8-B622-904E69994AC6}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -246,7 +226,5 @@ Global {EF5ACC32-7644-4CF0-87FC-AE9AFD0DB4D8} = {0A8A5052-C6C9-4E92-9027-5229466DF86C} {050DC082-5E4E-4175-8F7E-723B134774BE} = {0A8A5052-C6C9-4E92-9027-5229466DF86C} {B9963F89-CF12-4A8D-B4BF-C2C0B9732144} = {0A8A5052-C6C9-4E92-9027-5229466DF86C} - {68D3FDD2-DA02-453B-9DF9-022F65F9265E} = {CC979E2E-846C-46EE-81D7-9AE234572B80} - {0D9287FD-F17F-4CB8-B622-904E69994AC6} = {CC979E2E-846C-46EE-81D7-9AE234572B80} EndGlobalSection EndGlobal diff --git a/src/Test/Utilities/CommonDiagnosticAnalyzers.cs b/src/Test/Utilities/CommonDiagnosticAnalyzers.cs index ea4831dee78f5c498b5026e86514f72894756756..4d032900a27a800d67c7cfc3641107fa105f4be7 100644 --- a/src/Test/Utilities/CommonDiagnosticAnalyzers.cs +++ b/src/Test/Utilities/CommonDiagnosticAnalyzers.cs @@ -291,6 +291,13 @@ public sealed class AnalyzerWithNoActions : DiagnosticAnalyzer public override void Initialize(AnalysisContext context) { } } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class AnalyzerWithNoSupportedDiagnostics : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } + public override void Initialize(AnalysisContext context) { } + } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AnalyzerThatThrowsInGetMessage : DiagnosticAnalyzer { diff --git a/src/Test/Utilities/TestUtilities.csproj b/src/Test/Utilities/TestUtilities.csproj index 372d2a706ac49d7fd4dc34255e928754a4faf89c..af705b25cda480bd218acf3eb274dff03267a33d 100644 --- a/src/Test/Utilities/TestUtilities.csproj +++ b/src/Test/Utilities/TestUtilities.csproj @@ -44,9 +44,15 @@ - - - + + ..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\..\packages\Microsoft.DiaSymReader.1.0.5\lib\net45\Microsoft.DiaSymReader.dll + true @@ -214,4 +220,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs b/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs index 377976381a139f6361b973df3ea3597c0d956997..fb44161ae8cf18a3e1fb131bd72b13abfddbb288 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; @@ -464,8 +463,19 @@ protected void GenerateNumber(object value, ITypeSymbol type) { using (NumberTag(GetTypeName(type))) { - // TODO(DustinCa): Add more unit tests to ensure that floats are correct. - EncodedText(Convert.ToString(value, CultureInfo.InvariantCulture)); + if (value is double) + { + // Note: use G17 for doubles to ensure that we roundtrip properly on 64-bit + EncodedText(((double)value).ToString("G17", CultureInfo.InvariantCulture)); + } + else if (value is float) + { + EncodedText(((float)value).ToString("R", CultureInfo.InvariantCulture)); + } + else + { + EncodedText(Convert.ToString(value, CultureInfo.InvariantCulture)); + } } } diff --git a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_CSAssignments.vb b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_CSAssignments.vb index b1051bc5e8e6c34f32c7c00958d654f336a60171..6b9f00afa7ca66694559b534085dc06b19197a44 100644 --- a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_CSAssignments.vb +++ b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_CSAssignments.vb @@ -1142,5 +1142,73 @@ class C Test(definition, expected) End Sub + + + Public Sub CSAssignments_RoundTrippedDoubles() + Dim definition = + + + +class C +{ + void $$M() + { + double d = 9.2233720368547758E+18D; + } +} + + + + + Dim expected = + + + System.Double + d + + + 9.2233720368547758E+18 + + + + + + Test(definition, expected) + End Sub + + + + Public Sub CSAssignments_RoundTrippedSingles() + Dim definition = + + + +class C +{ + void $$M() + { + float s = 0.333333343F; + } +} + + + + + Dim expected = + + + System.Single + s + + + 0.333333343 + + + + + + Test(definition, expected) + End Sub + End Class End Namespace diff --git a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb index 8f34363fc724fae701f5a37bd764126e4bc6fd37..18a781a087a90a95687e3c7f757534412b185184 100644 --- a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb +++ b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb @@ -990,5 +990,69 @@ End Class Test(definition, expected) End Sub + + + Public Sub VBAssignments_RoundTrippedDoubles() + Dim definition = + + + +Class C + Sub $$M() + Dim d As Double = 9.2233720368547758E+18R + End Sub +End Class + + + + + Dim expected = + + + System.Double + d + + + 9.2233720368547758E+18 + + + + + + Test(definition, expected) + End Sub + + + + Public Sub VBAssignments_RoundTrippedSingles() + Dim definition = + + + +Class C + Sub $$M() + Dim s As Single = 0.333333343F + End Sub +End Class + + + + + Dim expected = + + + System.Single + s + + + 0.333333343 + + + + + + Test(definition, expected) + End Sub + End Class End Namespace diff --git a/src/Workspaces/CSharp/Portable/Formatting/FormattingHelpers.cs b/src/Workspaces/CSharp/Portable/Formatting/FormattingHelpers.cs index 3083e7d6700a39d9e074544385524a4ea6123f98..4db6394cc8945de4ceb56aefa71b496e6ce3602d 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/FormattingHelpers.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/FormattingHelpers.cs @@ -61,6 +61,16 @@ public static bool IsValidBracePair(this ValueTuple br return true; } + public static bool IsOpenParenInParameterListOfAConversionOperatorDeclaration(this SyntaxToken token) + { + return token.IsOpenParenInParameterList() && token.Parent.IsParentKind(SyntaxKind.ConversionOperatorDeclaration); + } + + public static bool IsOpenParenInParameterListOfAOperationDeclaration(this SyntaxToken token) + { + return token.IsOpenParenInParameterList() && token.Parent.IsParentKind(SyntaxKind.OperatorDeclaration); + } + public static bool IsOpenParenInParameterList(this SyntaxToken token) { return token.Kind() == SyntaxKind.OpenParenToken && token.Parent.Kind() == SyntaxKind.ParameterList; diff --git a/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs index e461eff335bfb2c08db3ae7521dcdb1284290be5..39599ff8c1811ded50a0c0cb58a98b34e019ed66 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs @@ -31,6 +31,18 @@ public override AdjustSpacesOperation GetAdjustSpacesOperation(SyntaxToken previ return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpacingAfterMethodDeclarationName); } + // Case: public static implicit operator string(Program p) { return null; } + if (previousToken.IsKeyword() && currentToken.IsOpenParenInParameterListOfAConversionOperatorDeclaration()) + { + return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpacingAfterMethodDeclarationName); + } + + // Case: public static Program operator !(Program p) { return null; } + if (previousToken.Parent.IsKind(SyntaxKind.OperatorDeclaration) && currentToken.IsOpenParenInParameterListOfAOperationDeclaration()) + { + return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpacingAfterMethodDeclarationName); + } + if (previousToken.IsOpenParenInParameterList() && currentToken.IsCloseParenInParameterList()) { return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceBetweenEmptyMethodDeclarationParentheses); diff --git a/src/Workspaces/CSharp/Portable/Formatting/Rules/TokenBasedFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/Rules/TokenBasedFormattingRule.cs index bf6ae49bb7ceccca518336cbc8774fa685ae9299..427e4eb061ae84515c67ef003c109a7f7c088565 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/Rules/TokenBasedFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/Rules/TokenBasedFormattingRule.cs @@ -200,7 +200,6 @@ public override AdjustSpacesOperation GetAdjustSpacesOperation(SyntaxToken previ previousToken.Kind() == SyntaxKind.BaseKeyword || previousToken.Kind() == SyntaxKind.ThisKeyword || previousToken.Kind() == SyntaxKind.NewKeyword || - previousToken.Parent.Kind() == SyntaxKind.OperatorDeclaration || previousToken.IsGenericGreaterThanToken() || currentToken.IsParenInArgumentList()) { diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 2fcacbd3167a96a20b4996e06b37f2415b6915e8..9afadc991634ff57bf4fc970c356223d2ce971de 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -6231,6 +6231,46 @@ void Main() AssertFormat(expected, code); } + [WorkItem(4240, "https://github.com/dotnet/roslyn/issues/4240")] + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public void VerifySpacingAfterMethodDeclarationName_Default() + { + var code = @"class Program +{ + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } +}"; + var expected = @"class Program +{ + public static Program operator +(Program p1, Program p2) { return null; } + public static implicit operator string(Program p) { return null; } + public static void M() { } +}"; + AssertFormat(expected, code); + } + + [WorkItem(4240, "https://github.com/dotnet/roslyn/issues/4240")] + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public void VerifySpacingAfterMethodDeclarationName_NonDefault() + { + var changingOptions = new Dictionary(); + changingOptions.Add(CSharpFormattingOptions.SpacingAfterMethodDeclarationName, true); + var code = @"class Program +{ + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } +}"; + var expected = @"class Program +{ + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } +}"; + AssertFormat(expected, code, changedOptionSet: changingOptions); + } + [WorkItem(939, "https://github.com/dotnet/roslyn/issues/939")] [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void DontFormatInsideArrayInitializers() diff --git a/src/Workspaces/Core/Desktop/PublicAPI.txt b/src/Workspaces/Core/Desktop/PublicAPI.txt index a2b9ac45a707c3000dbba084d6393b1c53e91088..f296949281930864eaf35fe83580e4231ad0f6ad 100644 --- a/src/Workspaces/Core/Desktop/PublicAPI.txt +++ b/src/Workspaces/Core/Desktop/PublicAPI.txt @@ -7,6 +7,16 @@ Microsoft.CodeAnalysis.Host.Mef.DesktopMefHostServices Microsoft.CodeAnalysis.Host.Mef.MefV1HostServices Microsoft.CodeAnalysis.Host.Mef.MefV1HostServices.GetExports() -> System.Collections.Generic.IEnumerable> Microsoft.CodeAnalysis.Host.Mef.MefV1HostServices.GetExports() -> System.Collections.Generic.IEnumerable> +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.AssociateFileExtensionWithLanguage(string projectFileExtension, string language) -> void +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadMetadataForReferencedProjects.get -> bool +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadMetadataForReferencedProjects.set -> void +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadProjectInfoAsync(string projectFilePath, System.Collections.Generic.IReadOnlyDictionary projectPathToProjectIdMap = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task> +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadSolutionInfoAsync(string solutionFilePath, System.Collections.Generic.IReadOnlyDictionary projectPathToProjectIdMap = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.MSBuildProjectLoader(Microsoft.CodeAnalysis.Workspace workspace, System.Collections.Immutable.ImmutableDictionary properties = null) -> void +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.Properties.get -> System.Collections.Immutable.ImmutableDictionary +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.SkipUnrecognizedProjects.get -> bool +Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.SkipUnrecognizedProjects.set -> void Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.AssociateFileExtensionWithLanguage(string projectFileExtension, string language) -> void Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.CloseSolution() -> void @@ -28,4 +38,4 @@ static Microsoft.CodeAnalysis.Host.Mef.MefV1HostServices.Create(System.Component static Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create() -> Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace static Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create(System.Collections.Generic.IDictionary properties) -> Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace static Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create(System.Collections.Generic.IDictionary properties, Microsoft.CodeAnalysis.Host.HostServices hostServices) -> Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.Workspace workspace) -> Microsoft.CodeAnalysis.Text.SourceText +virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.Workspace workspace) -> Microsoft.CodeAnalysis.Text.SourceText \ No newline at end of file diff --git a/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs new file mode 100644 index 0000000000000000000000000000000000000000..be933abc6e7408bcc94a5a6af021dd9f9d55282f --- /dev/null +++ b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs @@ -0,0 +1,784 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +#if !MSBUILD12 +using Microsoft.Build.Construction; +#endif +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.MSBuild +{ + /// + /// An API for loading msbuild project files. + /// + public class MSBuildProjectLoader + { + // the workspace that the projects and solutions are intended to be loaded into. + private readonly Workspace _workspace; + + // used to protect access to the following mutable state + private readonly NonReentrantLock _dataGuard = new NonReentrantLock(); + private ImmutableDictionary _properties; + private readonly Dictionary _extensionToLanguageMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Create a new instance of an . + /// + public MSBuildProjectLoader(Workspace workspace, ImmutableDictionary properties = null) + { + _workspace = workspace; + _properties = properties ?? ImmutableDictionary.Empty; + } + + /// + /// The MSBuild properties used when interpreting project files. + /// These are the same properties that are passed to msbuild via the /property:<n>=<v> command line argument. + /// + public ImmutableDictionary Properties + { + get { return _properties; } + } + + /// + /// Determines if metadata from existing output assemblies is loaded instead of opening referenced projects. + /// If the referenced project is already opened, the metadata will not be loaded. + /// If the metadata assembly cannot be found the referenced project will be opened instead. + /// + public bool LoadMetadataForReferencedProjects { get; set; } = false; + + /// + /// Determines if unrecognized projects are skipped when solutions or projects are opened. + /// + /// An project is unrecognized if it either has + /// a) an invalid file path, + /// b) a non-existent project file, + /// c) has an unrecognized file extension or + /// d) a file extension associated with an unsupported language. + /// + /// If unrecognized projects cannot be skipped a corresponding exception is thrown. + /// + public bool SkipUnrecognizedProjects { get; set; } = true; + + /// + /// Associates a project file extension with a language name. + /// + public void AssociateFileExtensionWithLanguage(string projectFileExtension, string language) + { + if (language == null) + { + throw new ArgumentNullException(nameof(language)); + } + + if (projectFileExtension == null) + { + throw new ArgumentNullException(nameof(projectFileExtension)); + } + + using (_dataGuard.DisposableWait()) + { + _extensionToLanguageMap[projectFileExtension] = language; + } + } + + private const string SolutionDirProperty = "SolutionDir"; + + private void SetSolutionProperties(string solutionFilePath) + { + if (!string.IsNullOrEmpty(solutionFilePath)) + { + // When MSBuild is building an individual project, it doesn't define $(SolutionDir). + // However when building an .sln file, or when working inside Visual Studio, + // $(SolutionDir) is defined to be the directory where the .sln file is located. + // Some projects out there rely on $(SolutionDir) being set (although the best practice is to + // use MSBuildProjectDirectory which is always defined). + if (!string.IsNullOrEmpty(solutionFilePath)) + { + string solutionDirectory = Path.GetDirectoryName(solutionFilePath); + if (!solutionDirectory.EndsWith(@"\", StringComparison.Ordinal)) + { + solutionDirectory += @"\"; + } + + if (Directory.Exists(solutionDirectory)) + { + _properties = _properties.SetItem(SolutionDirProperty, solutionDirectory); + } + } + } + } + + /// + /// Loads the for the specified solution file, including all projects referenced by the solution file and + /// all the projects referenced by the project files. + /// + public async Task LoadSolutionInfoAsync( + string solutionFilePath, + IReadOnlyDictionary projectPathToProjectIdMap = null, + CancellationToken cancellationToken = default(CancellationToken)) + { + if (solutionFilePath == null) + { + throw new ArgumentNullException(nameof(solutionFilePath)); + } + + var absoluteSolutionPath = this.GetAbsoluteSolutionPath(solutionFilePath, Directory.GetCurrentDirectory()); + using (_dataGuard.DisposableWait(cancellationToken)) + { + this.SetSolutionProperties(absoluteSolutionPath); + } + + VersionStamp version = default(VersionStamp); + +#if !MSBUILD12 + Microsoft.Build.Construction.SolutionFile solutionFile = Microsoft.Build.Construction.SolutionFile.Parse(absoluteSolutionPath); + var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; + + // a list to accumulate all the loaded projects + var loadedProjects = new LoadState(null); + + // load all the projects + foreach (var project in solutionFile.ProjectsInOrder) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (project.ProjectType != SolutionProjectType.SolutionFolder) + { + var projectAbsolutePath = TryGetAbsolutePath(project.AbsolutePath, reportMode); + if (projectAbsolutePath != null) + { + IProjectFileLoader loader; + if (TryGetLoaderFromProjectPath(projectAbsolutePath, reportMode, out loader)) + { + // projects get added to 'loadedProjects' as side-effect + // never prefer metadata when loading solution, all projects get loaded if they can. + var tmp = await GetOrLoadProjectAsync(projectAbsolutePath, loader, preferMetadata: false, loadedProjects: loadedProjects, cancellationToken: cancellationToken).ConfigureAwait(false); + } + } + } + } +#else + SolutionFile solutionFile = null; + + using (var reader = new StreamReader(absoluteSolutionPath)) + { + version = VersionStamp.Create(File.GetLastWriteTimeUtc(absoluteSolutionPath)); + var text = await reader.ReadToEndAsync().ConfigureAwait(false); + solutionFile = SolutionFile.Parse(new StringReader(text)); + } + + var solutionFolder = Path.GetDirectoryName(absoluteSolutionPath); + + // a list to accumulate all the loaded projects + var loadedProjects = new LoadState(null); + + var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; + + // load all the projects + foreach (var projectBlock in solutionFile.ProjectBlocks) + { + cancellationToken.ThrowIfCancellationRequested(); + + string absoluteProjectPath; + if (TryGetAbsoluteProjectPath(projectBlock.ProjectPath, solutionFolder, reportMode, out absoluteProjectPath)) + { + IProjectFileLoader loader; + if (TryGetLoaderFromProjectPath(absoluteProjectPath, reportMode, out loader)) + { + // projects get added to 'loadedProjects' as side-effect + // never prefer metadata when loading solution, all projects get loaded if they can. + var tmp = await GetOrLoadProjectAsync(absoluteProjectPath, loader, preferMetadata: false, loadedProjects: loadedProjects, cancellationToken: cancellationToken).ConfigureAwait(false); + } + } + } +#endif + + // construct workspace from loaded project infos + return SolutionInfo.Create(SolutionId.CreateNewId(debugName: absoluteSolutionPath), version, absoluteSolutionPath, loadedProjects.Projects); + } + + internal string GetAbsoluteSolutionPath(string path, string baseDirectory) + { + string absolutePath; + + try + { + absolutePath = GetAbsolutePath(path, baseDirectory); + } + catch (Exception) + { + throw new InvalidOperationException(string.Format(WorkspacesResources.InvalidSolutionFilePath, path)); + } + + if (!File.Exists(absolutePath)) + { + throw new FileNotFoundException(string.Format(WorkspacesResources.SolutionFileNotFound, absolutePath)); + } + + return absolutePath; + } + + /// + /// Loads the from the specified project file and all referenced projects. + /// The first in the result corresponds to the specified project file. + /// + public async Task> LoadProjectInfoAsync( + string projectFilePath, + IReadOnlyDictionary projectPathToProjectIdMap = null, + CancellationToken cancellationToken = default(CancellationToken)) + { + if (projectFilePath == null) + { + throw new ArgumentNullException(nameof(projectFilePath)); + } + + string fullPath; + this.TryGetAbsoluteProjectPath(projectFilePath, Directory.GetCurrentDirectory(), ReportMode.Throw, out fullPath); + + IProjectFileLoader loader; + this.TryGetLoaderFromProjectPath(projectFilePath, ReportMode.Throw, out loader); + + var loadedProjects = new LoadState(projectPathToProjectIdMap); + var id = await this.LoadProjectAsync(fullPath, loader, this.LoadMetadataForReferencedProjects, loadedProjects, cancellationToken).ConfigureAwait(false); + + var result = loadedProjects.Projects.Reverse().ToImmutableArray(); + Debug.Assert(result[0].Id == id); + return result; + } + + private class LoadState + { + private Dictionary _projetIdToProjectInfoMap + = new Dictionary(); + + private List _projectInfoList + = new List(); + + private readonly Dictionary _projectPathToProjectIdMap + = new Dictionary(); + + public LoadState(IReadOnlyDictionary projectPathToProjectIdMap) + { + if (projectPathToProjectIdMap != null) + { + _projectPathToProjectIdMap.AddRange(projectPathToProjectIdMap); + } + } + + public void Add(ProjectInfo info) + { + _projetIdToProjectInfoMap.Add(info.Id, info); + _projectInfoList.Add(info); + } + + public bool TryGetValue(ProjectId id, out ProjectInfo info) + { + return _projetIdToProjectInfoMap.TryGetValue(id, out info); + } + + public IReadOnlyList Projects + { + get { return _projectInfoList; } + } + + public ProjectId GetProjectId(string fullProjectPath) + { + ProjectId id; + _projectPathToProjectIdMap.TryGetValue(fullProjectPath, out id); + return id; + } + + public ProjectId GetOrCreateProjectId(string fullProjectPath) + { + ProjectId id; + if (!_projectPathToProjectIdMap.TryGetValue(fullProjectPath, out id)) + { + id = ProjectId.CreateNewId(debugName: fullProjectPath); + _projectPathToProjectIdMap.Add(fullProjectPath, id); + } + + return id; + } + } + + private async Task GetOrLoadProjectAsync(string projectFilePath, IProjectFileLoader loader, bool preferMetadata, LoadState loadedProjects, CancellationToken cancellationToken) + { + var projectId = loadedProjects.GetProjectId(projectFilePath); + if (projectId == null) + { + projectId = await this.LoadProjectAsync(projectFilePath, loader, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); + } + + return projectId; + } + + private async Task LoadProjectAsync(string projectFilePath, IProjectFileLoader loader, bool preferMetadata, LoadState loadedProjects, CancellationToken cancellationToken) + { + Debug.Assert(projectFilePath != null); + Debug.Assert(loader != null); + + var projectId = loadedProjects.GetOrCreateProjectId(projectFilePath); + + var projectName = Path.GetFileNameWithoutExtension(projectFilePath); + + var projectFile = await loader.LoadProjectFileAsync(projectFilePath, _properties, cancellationToken).ConfigureAwait(false); + var projectFileInfo = await projectFile.GetProjectFileInfoAsync(cancellationToken).ConfigureAwait(false); + + var projectDirectory = Path.GetDirectoryName(projectFilePath); + var outputFilePath = projectFileInfo.OutputFilePath; + var outputDirectory = Path.GetDirectoryName(outputFilePath); + + VersionStamp version; + if (!string.IsNullOrEmpty(projectFilePath) && File.Exists(projectFilePath)) + { + version = VersionStamp.Create(File.GetLastWriteTimeUtc(projectFilePath)); + } + else + { + version = VersionStamp.Create(); + } + + // translate information from command line args + var commandLineParser = _workspace.Services.GetLanguageServices(loader.Language).GetService(); + var metadataService = _workspace.Services.GetService(); + var analyzerService = _workspace.Services.GetService(); + + var commandLineArgs = commandLineParser.Parse( + arguments: projectFileInfo.CommandLineArgs, + baseDirectory: projectDirectory, + isInteractive: false, + sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); + + var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); + var metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, metadataService.GetProvider())); + + var analyzerLoader = analyzerService.GetLoader(); + foreach (var path in commandLineArgs.AnalyzerReferences.Select(r => r.FilePath)) + { + analyzerLoader.AddDependencyLocation(path); + } + + var analyzerReferences = commandLineArgs.ResolveAnalyzerReferences(analyzerLoader); + + var defaultEncoding = commandLineArgs.Encoding; + + // docs & additional docs + var docFileInfos = projectFileInfo.Documents.ToImmutableArrayOrEmpty(); + var additionalDocFileInfos = projectFileInfo.AdditionalDocuments.ToImmutableArrayOrEmpty(); + + // check for duplicate documents + var allDocFileInfos = docFileInfos.AddRange(additionalDocFileInfos); + CheckDocuments(allDocFileInfos, projectFilePath, projectId); + + var docs = new List(); + foreach (var docFileInfo in docFileInfos) + { + string name; + ImmutableArray folders; + GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); + + docs.Add(DocumentInfo.Create( + DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), + name, + folders, + projectFile.GetSourceCodeKind(docFileInfo.FilePath), + new FileTextLoader(docFileInfo.FilePath, defaultEncoding), + docFileInfo.FilePath, + docFileInfo.IsGenerated)); + } + + var additionalDocs = new List(); + foreach (var docFileInfo in additionalDocFileInfos) + { + string name; + ImmutableArray folders; + GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); + + additionalDocs.Add(DocumentInfo.Create( + DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), + name, + folders, + SourceCodeKind.Regular, + new FileTextLoader(docFileInfo.FilePath, defaultEncoding), + docFileInfo.FilePath, + docFileInfo.IsGenerated)); + } + + // project references + var resolvedReferences = await this.ResolveProjectReferencesAsync( + projectId, projectFilePath, projectFileInfo.ProjectReferences, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); + + // add metadata references for project refs converted to metadata refs + metadataReferences = metadataReferences.Concat(resolvedReferences.MetadataReferences); + + // if the project file loader couldn't figure out an assembly name, make one using the project's file path. + var assemblyName = commandLineArgs.CompilationName; + if (string.IsNullOrWhiteSpace(assemblyName)) + { + assemblyName = Path.GetFileNameWithoutExtension(projectFilePath); + + // if this is still unreasonable, use a fixed name. + if (string.IsNullOrWhiteSpace(assemblyName)) + { + assemblyName = "assembly"; + } + } + + // make sure that doc-comments at least get parsed. + var parseOptions = commandLineArgs.ParseOptions; + if (parseOptions.DocumentationMode == DocumentationMode.None) + { + parseOptions = parseOptions.WithDocumentationMode(DocumentationMode.Parse); + } + + // add all the extra options that are really behavior overrides + var compOptions = commandLineArgs.CompilationOptions + .WithXmlReferenceResolver(new XmlFileResolver(projectDirectory)) + .WithSourceReferenceResolver(new SourceFileResolver(ImmutableArray.Empty, projectDirectory)) + .WithMetadataReferenceResolver( + new AssemblyReferenceResolver( + new MetadataFileReferenceResolver(ImmutableArray.Empty, projectDirectory), + MetadataFileReferenceProvider.Default)) + .WithStrongNameProvider(new DesktopStrongNameProvider(ImmutableArray.Create(projectDirectory, outputFilePath))) + .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); + + loadedProjects.Add( + ProjectInfo.Create( + projectId, + version, + projectName, + assemblyName, + loader.Language, + projectFilePath, + outputFilePath, + compilationOptions: compOptions, + parseOptions: parseOptions, + documents: docs, + projectReferences: resolvedReferences.ProjectReferences, + metadataReferences: metadataReferences, + analyzerReferences: analyzerReferences, + additionalDocuments: additionalDocs, + isSubmission: false, + hostObjectType: null)); + + return projectId; + } + + private static readonly char[] s_directorySplitChars = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + private static void GetDocumentNameAndFolders(string logicalPath, out string name, out ImmutableArray folders) + { + var pathNames = logicalPath.Split(s_directorySplitChars, StringSplitOptions.RemoveEmptyEntries); + if (pathNames.Length > 0) + { + if (pathNames.Length > 1) + { + folders = pathNames.Take(pathNames.Length - 1).ToImmutableArray(); + } + else + { + folders = ImmutableArray.Create(); + } + + name = pathNames[pathNames.Length - 1]; + } + else + { + name = logicalPath; + folders = ImmutableArray.Create(); + } + } + + private void CheckDocuments(IEnumerable docs, string projectFilePath, ProjectId projectId) + { + var paths = new HashSet(); + foreach (var doc in docs) + { + if (paths.Contains(doc.FilePath)) + { + _workspace.OnWorkspaceFailed(new ProjectDiagnostic(WorkspaceDiagnosticKind.Warning, string.Format(WorkspacesResources.DuplicateSourceFileInProject, doc.FilePath, projectFilePath), projectId)); + } + + paths.Add(doc.FilePath); + } + } + + private class ResolvedReferences + { + public readonly List ProjectReferences = new List(); + public readonly List MetadataReferences = new List(); + } + + private async Task ResolveProjectReferencesAsync( + ProjectId thisProjectId, + string thisProjectPath, + IReadOnlyList projectFileReferences, + bool preferMetadata, + LoadState loadedProjects, + CancellationToken cancellationToken) + { + var resolvedReferences = new ResolvedReferences(); + var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; + + foreach (var projectFileReference in projectFileReferences) + { + string fullPath; + + if (TryGetAbsoluteProjectPath(projectFileReference.Path, Path.GetDirectoryName(thisProjectPath), reportMode, out fullPath)) + { + // if the project is already loaded, then just reference the one we have + var existingProjectId = loadedProjects.GetProjectId(fullPath); + if (existingProjectId != null) + { + resolvedReferences.ProjectReferences.Add(new ProjectReference(existingProjectId, projectFileReference.Aliases)); + continue; + } + + IProjectFileLoader loader; + TryGetLoaderFromProjectPath(fullPath, ReportMode.Ignore, out loader); + + // get metadata if preferred or if loader is unknown + if (preferMetadata || loader == null) + { + var projectMetadata = await this.GetProjectMetadata(fullPath, projectFileReference.Aliases, _properties, cancellationToken).ConfigureAwait(false); + if (projectMetadata != null) + { + resolvedReferences.MetadataReferences.Add(projectMetadata); + continue; + } + } + + // must load, so we really need loader + if (TryGetLoaderFromProjectPath(fullPath, reportMode, out loader)) + { + // load the project + var projectId = await this.GetOrLoadProjectAsync(fullPath, loader, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); + + // If that other project already has a reference on us, this will cause a circularity. + // This check doesn't need to be in the "already loaded" path above, since in any circularity this path + // must be taken at least once. + if (ProjectAlreadyReferencesProject(loadedProjects, projectId, targetProject: thisProjectId)) + { + // We'll try to make this metadata if we can + var projectMetadata = await this.GetProjectMetadata(fullPath, projectFileReference.Aliases, _properties, cancellationToken).ConfigureAwait(false); + if (projectMetadata != null) + { + resolvedReferences.MetadataReferences.Add(projectMetadata); + } + continue; + } + else + { + resolvedReferences.ProjectReferences.Add(new ProjectReference(projectId, projectFileReference.Aliases)); + continue; + } + } + } + else + { + fullPath = projectFileReference.Path; + } + + // cannot find metadata and project cannot be loaded, so leave a project reference to a non-existent project. + var id = loadedProjects.GetOrCreateProjectId(fullPath); + resolvedReferences.ProjectReferences.Add(new ProjectReference(id, projectFileReference.Aliases)); + } + + return resolvedReferences; + } + + /// + /// Returns true if the project identified by has a reference (even indirectly) + /// on the project identified by . + /// + private bool ProjectAlreadyReferencesProject(LoadState loadedProjects, ProjectId fromProject, ProjectId targetProject) + { + ProjectInfo info; + + return loadedProjects.TryGetValue(fromProject, out info) && info.ProjectReferences.Any(pr => pr.ProjectId == targetProject || + ProjectAlreadyReferencesProject(loadedProjects, pr.ProjectId, targetProject)); + } + + /// + /// Gets a MetadataReference to a project's output assembly. + /// + private async Task GetProjectMetadata(string projectFilePath, ImmutableArray aliases, IDictionary globalProperties, CancellationToken cancellationToken) + { + // use loader service to determine output file for project if possible + string outputFilePath = null; + + try + { + outputFilePath = await ProjectFileLoader.GetOutputFilePathAsync(projectFilePath, globalProperties, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + _workspace.OnWorkspaceFailed(new WorkspaceDiagnostic(WorkspaceDiagnosticKind.Failure, e.Message)); + } + + if (outputFilePath != null && File.Exists(outputFilePath)) + { + if (Workspace.TestHookStandaloneProjectsDoNotHoldReferences) + { + var documentationService = _workspace.Services.GetService(); + var docProvider = documentationService.GetDocumentationProvider(outputFilePath); + var metadata = AssemblyMetadata.CreateFromImage(File.ReadAllBytes(outputFilePath)); + + return metadata.GetReference( + documentation: docProvider, + aliases: aliases, + display: outputFilePath); + } + else + { + var metadataService = _workspace.Services.GetService(); + return metadataService.GetReference(outputFilePath, new MetadataReferenceProperties(MetadataImageKind.Assembly, aliases)); + } + } + + return null; + } + + private string TryGetAbsolutePath(string path, ReportMode mode) + { + try + { + path = Path.GetFullPath(path); + } + catch (Exception) + { + ReportFailure(mode, string.Format(WorkspacesResources.InvalidProjectFilePath, path)); + return null; + } + + if (!File.Exists(path)) + { + ReportFailure( + mode, + string.Format(WorkspacesResources.ProjectFileNotFound, path), + msg => new FileNotFoundException(msg)); + return null; + } + + return path; + } + + internal bool TryGetLoaderFromProjectPath(string projectFilePath, out IProjectFileLoader loader) + { + return TryGetLoaderFromProjectPath(projectFilePath, ReportMode.Ignore, out loader); + } + + private bool TryGetLoaderFromProjectPath(string projectFilePath, ReportMode mode, out IProjectFileLoader loader) + { + using (_dataGuard.DisposableWait()) + { + // otherwise try to figure it out from extension + var extension = Path.GetExtension(projectFilePath); + if (extension.Length > 0 && extension[0] == '.') + { + extension = extension.Substring(1); + } + + string language; + if (_extensionToLanguageMap.TryGetValue(extension, out language)) + { + if (_workspace.Services.SupportedLanguages.Contains(language)) + { + loader = _workspace.Services.GetLanguageServices(language).GetService(); + } + else + { + loader = null; + this.ReportFailure(mode, string.Format(WorkspacesResources.CannotOpenProjectUnsupportedLanguage, projectFilePath, language)); + return false; + } + } + else + { + loader = ProjectFileLoader.GetLoaderForProjectFileExtension(_workspace, extension); + + if (loader == null) + { + this.ReportFailure(mode, string.Format(WorkspacesResources.CannotOpenProjectUnrecognizedFileExtension, projectFilePath, Path.GetExtension(projectFilePath))); + return false; + } + } + + return loader != null; + } + } + + private bool TryGetAbsoluteProjectPath(string path, string baseDirectory, ReportMode mode, out string absolutePath) + { + try + { + absolutePath = GetAbsolutePath(path, baseDirectory); + } + catch (Exception) + { + ReportFailure(mode, string.Format(WorkspacesResources.InvalidProjectFilePath, path)); + absolutePath = null; + return false; + } + + if (!File.Exists(absolutePath)) + { + ReportFailure( + mode, + string.Format(WorkspacesResources.ProjectFileNotFound, absolutePath), + msg => new FileNotFoundException(msg)); + return false; + } + + return true; + } + + private static string GetAbsolutePath(string path, string baseDirectoryPath) + { + return Path.GetFullPath(FileUtilities.ResolveRelativePath(path, baseDirectoryPath) ?? path); + } + + private enum ReportMode + { + Throw, + Log, + Ignore + } + + private void ReportFailure(ReportMode mode, string message, Func createException = null) + { + switch (mode) + { + case ReportMode.Throw: + if (createException != null) + { + throw createException(message); + } + else + { + throw new InvalidOperationException(message); + } + + case ReportMode.Log: + _workspace.OnWorkspaceFailed(new WorkspaceDiagnostic(WorkspaceDiagnosticKind.Failure, message)); + break; + + case ReportMode.Ignore: + default: + break; + } + } + } +} \ No newline at end of file diff --git a/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildWorkspace.cs index 1f1d19072a2c8562315e0709ab253ddce741097a..f0000ccc0a80ba2228d6394722206b9cd7b7554f 100644 --- a/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildWorkspace.cs @@ -32,26 +32,14 @@ public sealed class MSBuildWorkspace : Workspace // used to serialize access to public methods private readonly NonReentrantLock _serializationLock = new NonReentrantLock(); - // used to protect access to mutable state - private readonly NonReentrantLock _dataGuard = new NonReentrantLock(); - - private readonly Dictionary _extensionToLanguageMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary _projectPathToProjectIdMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary _projectPathToLoaderMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private string _solutionFilePath; - private ImmutableDictionary _properties; + private MSBuildProjectLoader _loader; private MSBuildWorkspace( HostServices hostServices, ImmutableDictionary properties) : base(hostServices, "MSBuildWorkspace") { - // always make a copy of these build properties (no mutation please!) - _properties = properties ?? ImmutableDictionary.Empty; - this.SetSolutionProperties(solutionFilePath: null); - this.LoadMetadataForReferencedProjects = false; - this.SkipUnrecognizedProjects = true; + _loader = new MSBuildProjectLoader(this, properties); } /// @@ -99,7 +87,7 @@ public static MSBuildWorkspace Create(IDictionary properties, Ho /// public ImmutableDictionary Properties { - get { return _properties; } + get { return _loader.Properties; } } /// @@ -107,7 +95,11 @@ public static MSBuildWorkspace Create(IDictionary properties, Ho /// If the referenced project is already opened, the metadata will not be loaded. /// If the metadata assembly cannot be found the referenced project will be opened instead. /// - public bool LoadMetadataForReferencedProjects { get; set; } + public bool LoadMetadataForReferencedProjects + { + get { return _loader.LoadMetadataForReferencedProjects; } + set { _loader.LoadMetadataForReferencedProjects = value; } + } /// /// Determines if unrecognized projects are skipped when solutions or projects are opened. @@ -120,27 +112,18 @@ public static MSBuildWorkspace Create(IDictionary properties, Ho /// /// If unrecognized projects cannot be skipped a corresponding exception is thrown. /// - public bool SkipUnrecognizedProjects { get; set; } + public bool SkipUnrecognizedProjects + { + get { return _loader.SkipUnrecognizedProjects; } + set { _loader.SkipUnrecognizedProjects = value; } + } /// /// Associates a project file extension with a language name. /// public void AssociateFileExtensionWithLanguage(string projectFileExtension, string language) { - if (language == null) - { - throw new ArgumentNullException(nameof(language)); - } - - if (projectFileExtension == null) - { - throw new ArgumentNullException(nameof(projectFileExtension)); - } - - using (_dataGuard.DisposableWait()) - { - _extensionToLanguageMap[projectFileExtension] = language; - } + _loader.AssociateFileExtensionWithLanguage(projectFileExtension, language); } /// @@ -154,199 +137,6 @@ public void CloseSolution() } } - protected override void ClearSolutionData() - { - base.ClearSolutionData(); - - using (_dataGuard.DisposableWait()) - { - this.SetSolutionProperties(solutionFilePath: null); - - // clear project related data - _projectPathToProjectIdMap.Clear(); - _projectPathToLoaderMap.Clear(); - } - } - - private const string SolutionDirProperty = "SolutionDir"; - - private void SetSolutionProperties(string solutionFilePath) - { - _solutionFilePath = solutionFilePath; - - if (!string.IsNullOrEmpty(solutionFilePath)) - { - // When MSBuild is building an individual project, it doesn't define $(SolutionDir). - // However when building an .sln file, or when working inside Visual Studio, - // $(SolutionDir) is defined to be the directory where the .sln file is located. - // Some projects out there rely on $(SolutionDir) being set (although the best practice is to - // use MSBuildProjectDirectory which is always defined). - if (!string.IsNullOrEmpty(solutionFilePath)) - { - string solutionDirectory = Path.GetDirectoryName(solutionFilePath); - if (!solutionDirectory.EndsWith(@"\", StringComparison.Ordinal)) - { - solutionDirectory += @"\"; - } - - if (Directory.Exists(solutionDirectory)) - { - _properties = _properties.SetItem(SolutionDirProperty, solutionDirectory); - } - } - } - } - - private ProjectId GetProjectId(string fullProjectPath) - { - using (_dataGuard.DisposableWait()) - { - ProjectId id; - _projectPathToProjectIdMap.TryGetValue(fullProjectPath, out id); - return id; - } - } - - private ProjectId GetOrCreateProjectId(string fullProjectPath) - { - using (_dataGuard.DisposableWait()) - { - ProjectId id; - if (!_projectPathToProjectIdMap.TryGetValue(fullProjectPath, out id)) - { - id = ProjectId.CreateNewId(debugName: fullProjectPath); - _projectPathToProjectIdMap.Add(fullProjectPath, id); - } - - return id; - } - } - - private bool TryGetLoaderFromProjectPath(string projectFilePath, ReportMode mode, out IProjectFileLoader loader) - { - using (_dataGuard.DisposableWait()) - { - // check to see if we already know the loader - if (!_projectPathToLoaderMap.TryGetValue(projectFilePath, out loader)) - { - // otherwise try to figure it out from extension - var extension = Path.GetExtension(projectFilePath); - if (extension.Length > 0 && extension[0] == '.') - { - extension = extension.Substring(1); - } - - string language; - if (_extensionToLanguageMap.TryGetValue(extension, out language)) - { - if (this.Services.SupportedLanguages.Contains(language)) - { - loader = this.Services.GetLanguageServices(language).GetService(); - } - else - { - this.ReportFailure(mode, string.Format(WorkspacesResources.CannotOpenProjectUnsupportedLanguage, projectFilePath, language)); - return false; - } - } - else - { - loader = ProjectFileLoader.GetLoaderForProjectFileExtension(this, extension); - - if (loader == null) - { - this.ReportFailure(mode, string.Format(WorkspacesResources.CannotOpenProjectUnrecognizedFileExtension, projectFilePath, Path.GetExtension(projectFilePath))); - return false; - } - } - - if (loader != null) - { - _projectPathToLoaderMap[projectFilePath] = loader; - } - } - - return loader != null; - } - } - - private bool TryGetAbsoluteProjectPath(string path, string baseDirectory, ReportMode mode, out string absolutePath) - { - try - { - absolutePath = this.GetAbsolutePath(path, baseDirectory); - } - catch (Exception) - { - ReportFailure(mode, string.Format(WorkspacesResources.InvalidProjectFilePath, path)); - absolutePath = null; - return false; - } - - if (!File.Exists(absolutePath)) - { - ReportFailure( - mode, - string.Format(WorkspacesResources.ProjectFileNotFound, absolutePath), - msg => new FileNotFoundException(msg)); - return false; - } - - return true; - } - - private string GetAbsoluteSolutionPath(string path, string baseDirectory) - { - string absolutePath; - - try - { - absolutePath = GetAbsolutePath(path, baseDirectory); - } - catch (Exception) - { - throw new InvalidOperationException(string.Format(WorkspacesResources.InvalidSolutionFilePath, path)); - } - - if (!File.Exists(absolutePath)) - { - throw new FileNotFoundException(string.Format(WorkspacesResources.SolutionFileNotFound, absolutePath)); - } - - return absolutePath; - } - - private enum ReportMode - { - Throw, - Log, - Ignore - } - - private void ReportFailure(ReportMode mode, string message, Func createException = null) - { - switch (mode) - { - case ReportMode.Throw: - if (createException != null) - { - throw createException(message); - } - else - { - throw new InvalidOperationException(message); - } - - case ReportMode.Log: - this.OnWorkspaceFailed(new WorkspaceDiagnostic(WorkspaceDiagnosticKind.Failure, message)); - break; - - case ReportMode.Ignore: - default: - break; - } - } - private string GetAbsolutePath(string path, string baseDirectoryPath) { return Path.GetFullPath(FileUtilities.ResolveRelativePath(path, baseDirectoryPath) ?? path); @@ -365,130 +155,10 @@ public async Task OpenSolutionAsync(string solutionFilePath, Cancellat this.ClearSolution(); - var absoluteSolutionPath = this.GetAbsoluteSolutionPath(solutionFilePath, Directory.GetCurrentDirectory()); - - using (_dataGuard.DisposableWait(cancellationToken)) - { - this.SetSolutionProperties(absoluteSolutionPath); - } - - VersionStamp version = default(VersionStamp); - -#if !MSBUILD12 - Microsoft.Build.Construction.SolutionFile solutionFile = Microsoft.Build.Construction.SolutionFile.Parse(absoluteSolutionPath); - var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; - var invalidProjects = new List(); - - // seed loaders from known project types - using (_dataGuard.DisposableWait(cancellationToken)) - { - foreach (var project in solutionFile.ProjectsInOrder) - { - if (project.ProjectType == SolutionProjectType.SolutionFolder) - { - continue; - } - - var projectAbsolutePath = TryGetAbsolutePath(project.AbsolutePath, reportMode); - if (projectAbsolutePath != null) - { - var extension = Path.GetExtension(projectAbsolutePath); - if (extension.Length > 0 && extension[0] == '.') - { - extension = extension.Substring(1); - } - - var loader = ProjectFileLoader.GetLoaderForProjectFileExtension(this, extension); - if (loader != null) - { - _projectPathToLoaderMap[projectAbsolutePath] = loader; - } - } - else - { - invalidProjects.Add(project); - } - } - } - - // a list to accumulate all the loaded projects - var loadedProjects = new Dictionary(); - - // load all the projects - foreach (var project in solutionFile.ProjectsInOrder) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (project.ProjectType != SolutionProjectType.SolutionFolder && !invalidProjects.Contains(project)) - { - var projectAbsolutePath = TryGetAbsolutePath(project.AbsolutePath, reportMode); - if (projectAbsolutePath != null) - { - IProjectFileLoader loader; - if (TryGetLoaderFromProjectPath(projectAbsolutePath, reportMode, out loader)) - { - // projects get added to 'loadedProjects' as side-effect - // never prefer metadata when loading solution, all projects get loaded if they can. - var tmp = await GetOrLoadProjectAsync(projectAbsolutePath, loader, preferMetadata: false, loadedProjects: loadedProjects, cancellationToken: cancellationToken).ConfigureAwait(false); - } - } - } - } -#else - SolutionFile solutionFile = null; - - using (var reader = new StreamReader(absoluteSolutionPath)) - { - version = VersionStamp.Create(File.GetLastWriteTimeUtc(absoluteSolutionPath)); - var text = await reader.ReadToEndAsync().ConfigureAwait(false); - solutionFile = SolutionFile.Parse(new StringReader(text)); - } - - var solutionFolder = Path.GetDirectoryName(absoluteSolutionPath); - - // seed loaders from known project types - using (_dataGuard.DisposableWait()) - { - foreach (var projectBlock in solutionFile.ProjectBlocks) - { - string absoluteProjectPath; - if (TryGetAbsoluteProjectPath(projectBlock.ProjectPath, solutionFolder, ReportMode.Ignore, out absoluteProjectPath)) - { - var loader = ProjectFileLoader.GetLoaderForProjectTypeGuid(this, projectBlock.ProjectTypeGuid); - if (loader != null) - { - _projectPathToLoaderMap[absoluteProjectPath] = loader; - } - } - } - } - - // a list to accumulate all the loaded projects - var loadedProjects = new Dictionary(); - - var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; - - // load all the projects - foreach (var projectBlock in solutionFile.ProjectBlocks) - { - cancellationToken.ThrowIfCancellationRequested(); - - string absoluteProjectPath; - if (TryGetAbsoluteProjectPath(projectBlock.ProjectPath, solutionFolder, reportMode, out absoluteProjectPath)) - { - IProjectFileLoader loader; - if (TryGetLoaderFromProjectPath(absoluteProjectPath, reportMode, out loader)) - { - // projects get added to 'loadedProjects' as side-effect - // never prefer metadata when loading solution, all projects get loaded if they can. - var tmp = await GetOrLoadProjectAsync(absoluteProjectPath, loader, preferMetadata: false, loadedProjects: loadedProjects, cancellationToken: cancellationToken).ConfigureAwait(false); - } - } - } -#endif + var solutionInfo = await _loader.LoadSolutionInfoAsync(solutionFilePath, cancellationToken: cancellationToken).ConfigureAwait(false); // construct workspace from loaded project infos - this.OnSolutionAdded(SolutionInfo.Create(SolutionId.CreateNewId(debugName: absoluteSolutionPath), version, absoluteSolutionPath, loadedProjects.Values)); + this.OnSolutionAdded(solutionInfo); this.UpdateReferencesAfterAdd(); @@ -505,458 +175,26 @@ public async Task OpenProjectAsync(string projectFilePath, Cancellation throw new ArgumentNullException(nameof(projectFilePath)); } - string fullPath; - if (this.TryGetAbsoluteProjectPath(projectFilePath, Directory.GetCurrentDirectory(), ReportMode.Throw, out fullPath)) - { - IProjectFileLoader loader; - if (this.TryGetLoaderFromProjectPath(projectFilePath, ReportMode.Throw, out loader)) - { - var loadedProjects = new Dictionary(); - var projectId = await GetOrLoadProjectAsync(fullPath, loader, this.LoadMetadataForReferencedProjects, loadedProjects, cancellationToken).ConfigureAwait(false); - - // add projects to solution - foreach (var project in loadedProjects.Values) - { - this.OnProjectAdded(project); - } - - this.UpdateReferencesAfterAdd(); - - return this.CurrentSolution.GetProject(projectId); - } - } - - // unreachable - return null; - } - - private string TryGetAbsolutePath(string path, ReportMode mode) - { - try - { - path = Path.GetFullPath(path); - } - catch (Exception) - { - ReportFailure(mode, string.Format(WorkspacesResources.InvalidProjectFilePath, path)); - return null; - } - - if (!File.Exists(path)) - { - ReportFailure( - mode, - string.Format(WorkspacesResources.ProjectFileNotFound, path), - msg => new FileNotFoundException(msg)); - return null; - } - - return path; - } - - private void UpdateReferencesAfterAdd() - { - using (_serializationLock.DisposableWait()) - { - var oldSolution = this.CurrentSolution; - var newSolution = this.UpdateReferencesAfterAdd(oldSolution); - - if (newSolution != oldSolution) - { - newSolution = this.SetCurrentSolution(newSolution); - var ignore = this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); - } - } - } - - // Updates all projects to properly reference other existing projects via project references instead of using references to built metadata. - private Solution UpdateReferencesAfterAdd(Solution solution) - { - // Build map from output assembly path to ProjectId - // Use explicit loop instead of ToDictionary so we don't throw if multiple projects have same output assembly path. - var outputAssemblyToProjectIdMap = new Dictionary(); - foreach (var p in solution.Projects) - { - if (!string.IsNullOrEmpty(p.OutputFilePath)) - { - outputAssemblyToProjectIdMap[p.OutputFilePath] = p.Id; - } - } - - // now fix each project if necessary - foreach (var pid in solution.ProjectIds) - { - var project = solution.GetProject(pid); - - // convert metadata references to project references if the metadata reference matches some project's output assembly. - foreach (var meta in project.MetadataReferences) - { - var pemeta = meta as PortableExecutableReference; - if (pemeta != null) - { - ProjectId matchingProjectId; + var projects = await _loader.LoadProjectInfoAsync(projectFilePath, GetCurrentProjectMap(), cancellationToken).ConfigureAwait(false); - // check both Display and FilePath. FilePath points to the actually bits, but Display should match output path if - // the metadata reference is shadow copied. - if ((!string.IsNullOrEmpty(pemeta.Display) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.Display, out matchingProjectId)) || - (!string.IsNullOrEmpty(pemeta.FilePath) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.FilePath, out matchingProjectId))) - { - var newProjRef = new ProjectReference(matchingProjectId, pemeta.Properties.Aliases, pemeta.Properties.EmbedInteropTypes); - - if (!project.ProjectReferences.Contains(newProjRef)) - { - project = project.WithProjectReferences(project.ProjectReferences.Concat(newProjRef)); - } - - project = project.WithMetadataReferences(project.MetadataReferences.Where(mr => mr != meta)); - } - } - } - - solution = project.Solution; - } - - return solution; - } - - private async Task GetOrLoadProjectAsync(string projectFilePath, IProjectFileLoader loader, bool preferMetadata, Dictionary loadedProjects, CancellationToken cancellationToken) - { - var projectId = GetProjectId(projectFilePath); - if (projectId == null) + // add projects to solution + foreach (var project in projects) { - projectId = await this.LoadProjectAsync(projectFilePath, loader, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); + this.OnProjectAdded(project); } - return projectId; - } - - private async Task LoadProjectAsync(string projectFilePath, IProjectFileLoader loader, bool preferMetadata, Dictionary loadedProjects, CancellationToken cancellationToken) - { - Debug.Assert(projectFilePath != null); - Debug.Assert(loader != null); - - var projectId = this.GetOrCreateProjectId(projectFilePath); - - var projectName = Path.GetFileNameWithoutExtension(projectFilePath); - - var projectFile = await loader.LoadProjectFileAsync(projectFilePath, _properties, cancellationToken).ConfigureAwait(false); - var projectFileInfo = await projectFile.GetProjectFileInfoAsync(cancellationToken).ConfigureAwait(false); - - var projectDirectory = Path.GetDirectoryName(projectFilePath); - var outputFilePath = projectFileInfo.OutputFilePath; - var outputDirectory = Path.GetDirectoryName(outputFilePath); - - VersionStamp version; - if (!string.IsNullOrEmpty(projectFilePath) && File.Exists(projectFilePath)) - { - version = VersionStamp.Create(File.GetLastWriteTimeUtc(projectFilePath)); - } - else - { - version = VersionStamp.Create(); - } - - // translate information from command line args - var commandLineParser = this.Services.GetLanguageServices(loader.Language).GetService(); - var metadataService = this.Services.GetService(); - var analyzerService = this.Services.GetService(); - - var commandLineArgs = commandLineParser.Parse( - arguments: projectFileInfo.CommandLineArgs, - baseDirectory: projectDirectory, - isInteractive: false, - sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); - - var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); - var metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, metadataService.GetProvider())); - - var analyzerLoader = analyzerService.GetLoader(); - foreach (var path in commandLineArgs.AnalyzerReferences.Select(r => r.FilePath)) - { - analyzerLoader.AddDependencyLocation(path); - } - - var analyzerReferences = commandLineArgs.ResolveAnalyzerReferences(analyzerLoader); - - var defaultEncoding = commandLineArgs.Encoding; - - // docs & additional docs - var docFileInfos = projectFileInfo.Documents.ToImmutableArrayOrEmpty(); - var additionalDocFileInfos = projectFileInfo.AdditionalDocuments.ToImmutableArrayOrEmpty(); - - // check for duplicate documents - var allDocFileInfos = docFileInfos.AddRange(additionalDocFileInfos); - CheckDocuments(allDocFileInfos, projectFilePath, projectId); - - var docs = new List(); - foreach (var docFileInfo in docFileInfos) - { - string name; - ImmutableArray folders; - GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); - - docs.Add(DocumentInfo.Create( - DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), - name, - folders, - projectFile.GetSourceCodeKind(docFileInfo.FilePath), - new FileTextLoader(docFileInfo.FilePath, defaultEncoding), - docFileInfo.FilePath, - docFileInfo.IsGenerated)); - } - - var additionalDocs = new List(); - foreach (var docFileInfo in additionalDocFileInfos) - { - string name; - ImmutableArray folders; - GetDocumentNameAndFolders(docFileInfo.LogicalPath, out name, out folders); - - additionalDocs.Add(DocumentInfo.Create( - DocumentId.CreateNewId(projectId, debugName: docFileInfo.FilePath), - name, - folders, - SourceCodeKind.Regular, - new FileTextLoader(docFileInfo.FilePath, defaultEncoding), - docFileInfo.FilePath, - docFileInfo.IsGenerated)); - } - - // project references - var resolvedReferences = await this.ResolveProjectReferencesAsync( - projectId, projectFilePath, projectFileInfo.ProjectReferences, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); - - // add metadata references for project refs converted to metadata refs - metadataReferences = metadataReferences.Concat(resolvedReferences.MetadataReferences); - - // if the project file loader couldn't figure out an assembly name, make one using the project's file path. - var assemblyName = commandLineArgs.CompilationName; - if (string.IsNullOrWhiteSpace(assemblyName)) - { - assemblyName = Path.GetFileNameWithoutExtension(projectFilePath); - - // if this is still unreasonable, use a fixed name. - if (string.IsNullOrWhiteSpace(assemblyName)) - { - assemblyName = "assembly"; - } - } - - // make sure that doc-comments at least get parsed. - var parseOptions = commandLineArgs.ParseOptions; - if (parseOptions.DocumentationMode == DocumentationMode.None) - { - parseOptions = parseOptions.WithDocumentationMode(DocumentationMode.Parse); - } - - // add all the extra options that are really behavior overrides - var compOptions = commandLineArgs.CompilationOptions - .WithXmlReferenceResolver(new XmlFileResolver(projectDirectory)) - .WithSourceReferenceResolver(new SourceFileResolver(ImmutableArray.Empty, projectDirectory)) - .WithMetadataReferenceResolver( - new AssemblyReferenceResolver( - new MetadataFileReferenceResolver(ImmutableArray.Empty, projectDirectory), - MetadataFileReferenceProvider.Default)) - .WithStrongNameProvider(new DesktopStrongNameProvider(ImmutableArray.Create(projectDirectory, outputFilePath))) - .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); - - loadedProjects.Add( - projectId, - ProjectInfo.Create( - projectId, - version, - projectName, - assemblyName, - loader.Language, - projectFilePath, - outputFilePath, - compilationOptions: compOptions, - parseOptions: parseOptions, - documents: docs, - projectReferences: resolvedReferences.ProjectReferences, - metadataReferences: metadataReferences, - analyzerReferences: analyzerReferences, - additionalDocuments: additionalDocs, - isSubmission: false, - hostObjectType: null)); - - return projectId; - } - - private static readonly char[] s_directorySplitChars = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; - - private static void GetDocumentNameAndFolders(string logicalPath, out string name, out ImmutableArray folders) - { - var pathNames = logicalPath.Split(s_directorySplitChars, StringSplitOptions.RemoveEmptyEntries); - if (pathNames.Length > 0) - { - if (pathNames.Length > 1) - { - folders = pathNames.Take(pathNames.Length - 1).ToImmutableArray(); - } - else - { - folders = ImmutableArray.Create(); - } - - name = pathNames[pathNames.Length - 1]; - } - else - { - name = logicalPath; - folders = ImmutableArray.Create(); - } - } - - private void CheckDocuments(IEnumerable docs, string projectFilePath, ProjectId projectId) - { - var paths = new HashSet(); - foreach (var doc in docs) - { - if (paths.Contains(doc.FilePath)) - { - this.OnWorkspaceFailed(new ProjectDiagnostic(WorkspaceDiagnosticKind.Warning, string.Format(WorkspacesResources.DuplicateSourceFileInProject, doc.FilePath, projectFilePath), projectId)); - } - - paths.Add(doc.FilePath); - } - } - - private class ResolvedReferences - { - public readonly List ProjectReferences = new List(); - public readonly List MetadataReferences = new List(); - } - - private async Task ResolveProjectReferencesAsync( - ProjectId thisProjectId, - string thisProjectPath, - IReadOnlyList projectFileReferences, - bool preferMetadata, - Dictionary loadedProjects, - CancellationToken cancellationToken) - { - var resolvedReferences = new ResolvedReferences(); - var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw; - - foreach (var projectFileReference in projectFileReferences) - { - string fullPath; - - if (TryGetAbsoluteProjectPath(projectFileReference.Path, Path.GetDirectoryName(thisProjectPath), reportMode, out fullPath)) - { - // if the project is already loaded, then just reference the one we have - var existingProjectId = this.GetProjectId(fullPath); - if (existingProjectId != null) - { - resolvedReferences.ProjectReferences.Add(new ProjectReference(existingProjectId, projectFileReference.Aliases)); - continue; - } - - IProjectFileLoader loader; - TryGetLoaderFromProjectPath(fullPath, ReportMode.Ignore, out loader); - - // get metadata if preferred or if loader is unknown - if (preferMetadata || loader == null) - { - var projectMetadata = await this.GetProjectMetadata(fullPath, projectFileReference.Aliases, _properties, cancellationToken).ConfigureAwait(false); - if (projectMetadata != null) - { - resolvedReferences.MetadataReferences.Add(projectMetadata); - continue; - } - } - - // must load, so we really need loader - if (TryGetLoaderFromProjectPath(fullPath, reportMode, out loader)) - { - // load the project - var projectId = await this.GetOrLoadProjectAsync(fullPath, loader, preferMetadata, loadedProjects, cancellationToken).ConfigureAwait(false); - - // If that other project already has a reference on us, this will cause a circularity. - // This check doesn't need to be in the "already loaded" path above, since in any circularity this path - // must be taken at least once. - if (ProjectAlreadyReferencesProject(loadedProjects, projectId, targetProject: thisProjectId)) - { - // We'll try to make this metadata if we can - var projectMetadata = await this.GetProjectMetadata(fullPath, projectFileReference.Aliases, _properties, cancellationToken).ConfigureAwait(false); - if (projectMetadata != null) - { - resolvedReferences.MetadataReferences.Add(projectMetadata); - } - continue; - } - else - { - resolvedReferences.ProjectReferences.Add(new ProjectReference(projectId, projectFileReference.Aliases)); - continue; - } - } - } - else - { - fullPath = projectFileReference.Path; - } - - // cannot find metadata and project cannot be loaded, so leave a project reference to a non-existent project. - var id = this.GetOrCreateProjectId(fullPath); - resolvedReferences.ProjectReferences.Add(new ProjectReference(id, projectFileReference.Aliases)); - } + this.UpdateReferencesAfterAdd(); - return resolvedReferences; + return this.CurrentSolution.GetProject(projects[0].Id); } - /// - /// Returns true if the project identified by has a reference (even indirectly) - /// on the project identified by . - /// - private bool ProjectAlreadyReferencesProject(Dictionary loadedProjects, ProjectId fromProject, ProjectId targetProject) + private Dictionary GetCurrentProjectMap() { - ProjectInfo info; - - return loadedProjects.TryGetValue(fromProject, out info) && info.ProjectReferences.Any(pr => pr.ProjectId == targetProject || - ProjectAlreadyReferencesProject(loadedProjects, pr.ProjectId, targetProject)); + return this.CurrentSolution.Projects + .Where(p => !string.IsNullOrEmpty(p.FilePath)) + .ToDictionary(p => p.FilePath, p => p.Id); } - /// - /// Gets a MetadataReference to a project's output assembly. - /// - private async Task GetProjectMetadata(string projectFilePath, ImmutableArray aliases, IDictionary globalProperties, CancellationToken cancellationToken) - { - // use loader service to determine output file for project if possible - string outputFilePath = null; - - try - { - outputFilePath = await ProjectFileLoader.GetOutputFilePathAsync(projectFilePath, globalProperties, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) - { - this.OnWorkspaceFailed(new WorkspaceDiagnostic(WorkspaceDiagnosticKind.Failure, e.Message)); - } - - if (outputFilePath != null && File.Exists(outputFilePath)) - { - if (Workspace.TestHookStandaloneProjectsDoNotHoldReferences) - { - var documentationService = this.Services.GetService(); - var docProvider = documentationService.GetDocumentationProvider(outputFilePath); - var metadata = AssemblyMetadata.CreateFromImage(File.ReadAllBytes(outputFilePath)); - - return metadata.GetReference( - documentation: docProvider, - aliases: aliases, - display: outputFilePath); - } - else - { - var metadataService = this.Services.GetService(); - return metadataService.GetReference(outputFilePath, new MetadataReferenceProperties(MetadataImageKind.Assembly, aliases)); - } - } - - return null; - } #endregion #region Apply Changes @@ -1014,11 +252,11 @@ protected override void ApplyProjectChanges(ProjectChanges projectChanges) { var projectPath = project.FilePath; IProjectFileLoader loader; - if (this.TryGetLoaderFromProjectPath(projectPath, ReportMode.Ignore, out loader)) + if (_loader.TryGetLoaderFromProjectPath(projectPath, out loader)) { try { - _applyChangesProjectFile = loader.LoadProjectFileAsync(projectPath, _properties, CancellationToken.None).Result; + _applyChangesProjectFile = loader.LoadProjectFileAsync(projectPath, _loader.Properties, CancellationToken.None).Result; } catch (System.IO.IOException exception) { @@ -1096,7 +334,7 @@ protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) var project = this.CurrentSolution.GetProject(info.Id.ProjectId); IProjectFileLoader loader; - if (this.TryGetLoaderFromProjectPath(project.FilePath, ReportMode.Ignore, out loader)) + if (_loader.TryGetLoaderFromProjectPath(project.FilePath, out loader)) { var extension = _applyChangesProjectFile.GetDocumentExtension(info.SourceCodeKind); var fileName = Path.ChangeExtension(info.Name, extension); @@ -1255,5 +493,5 @@ protected override void ApplyAnalyzerReferenceRemoved(ProjectId projectId, Analy this.OnAnalyzerReferenceRemoved(projectId, analyzerReference); } } - #endregion +#endregion } diff --git a/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj b/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj index a68662b72671ca79d5c8d423ae5c63e2e84b8752..24f23e7b3a92c2bfece13496e9b2866ce824c236 100644 --- a/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj +++ b/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj @@ -127,6 +127,7 @@ + diff --git a/src/Workspaces/Core/Portable/PublicAPI.txt b/src/Workspaces/Core/Portable/PublicAPI.txt index f7187ff40f0197e6c2d5fb3eefc81c9b95fc5b51..a92579ef8b79233e712db106e86253f82cc22e1d 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.txt @@ -787,6 +787,7 @@ Microsoft.CodeAnalysis.Workspace.ScheduleTask(System.Func func, string tas Microsoft.CodeAnalysis.Workspace.Services.get -> Microsoft.CodeAnalysis.Host.HostWorkspaceServices Microsoft.CodeAnalysis.Workspace.SetCurrentSolution(Microsoft.CodeAnalysis.Solution solution) -> Microsoft.CodeAnalysis.Solution Microsoft.CodeAnalysis.Workspace.UnregisterText(Microsoft.CodeAnalysis.Text.SourceTextContainer textContainer) -> void +Microsoft.CodeAnalysis.Workspace.UpdateReferencesAfterAdd() -> void Microsoft.CodeAnalysis.Workspace.Workspace(Microsoft.CodeAnalysis.Host.HostServices host, string workspaceKind) -> void Microsoft.CodeAnalysis.Workspace.WorkspaceChanged -> System.EventHandler Microsoft.CodeAnalysis.Workspace.WorkspaceFailed -> System.EventHandler diff --git a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs index 1c56b68c72067c0efaac408b49de7f1871edffae..41a8048620f767b87ba031c015bb8fd417cae6ec 100644 --- a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs @@ -61,6 +61,8 @@ public Solution AddSolution(SolutionInfo solutionInfo) this.OnSolutionAdded(solutionInfo); + this.UpdateReferencesAfterAdd(); + return this.CurrentSolution; } @@ -85,6 +87,8 @@ public Project AddProject(ProjectInfo projectInfo) this.OnProjectAdded(projectInfo); + this.UpdateReferencesAfterAdd(); + return this.CurrentSolution.GetProject(projectInfo.Id); } @@ -103,6 +107,8 @@ public void AddProjects(IEnumerable projectInfos) { this.OnProjectAdded(info); } + + this.UpdateReferencesAfterAdd(); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 25e3e03ef30e3c455ee6b5d75dee23135bf69c46..ff80db0a212ccc1100fde57c3adaf650a716de71 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -799,6 +799,73 @@ protected internal void OnAdditionalDocumentRemoved(DocumentId documentId) } } + /// + /// Updates all projects to properly reference other projects as project references instead of metadata references. + /// + protected void UpdateReferencesAfterAdd() + { + using (_serializationLock.DisposableWait()) + { + var oldSolution = this.CurrentSolution; + var newSolution = this.UpdateReferencesAfterAdd(oldSolution); + + if (newSolution != oldSolution) + { + newSolution = this.SetCurrentSolution(newSolution); + var ignore = this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); + } + } + } + + private Solution UpdateReferencesAfterAdd(Solution solution) + { + // Build map from output assembly path to ProjectId + // Use explicit loop instead of ToDictionary so we don't throw if multiple projects have same output assembly path. + var outputAssemblyToProjectIdMap = new Dictionary(); + foreach (var p in solution.Projects) + { + if (!string.IsNullOrEmpty(p.OutputFilePath)) + { + outputAssemblyToProjectIdMap[p.OutputFilePath] = p.Id; + } + } + + // now fix each project if necessary + foreach (var pid in solution.ProjectIds) + { + var project = solution.GetProject(pid); + + // convert metadata references to project references if the metadata reference matches some project's output assembly. + foreach (var meta in project.MetadataReferences) + { + var pemeta = meta as PortableExecutableReference; + if (pemeta != null) + { + ProjectId matchingProjectId; + + // check both Display and FilePath. FilePath points to the actually bits, but Display should match output path if + // the metadata reference is shadow copied. + if ((!string.IsNullOrEmpty(pemeta.Display) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.Display, out matchingProjectId)) || + (!string.IsNullOrEmpty(pemeta.FilePath) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.FilePath, out matchingProjectId))) + { + var newProjRef = new ProjectReference(matchingProjectId, pemeta.Properties.Aliases, pemeta.Properties.EmbedInteropTypes); + + if (!project.ProjectReferences.Contains(newProjRef)) + { + project = project.WithProjectReferences(project.ProjectReferences.Concat(newProjRef)); + } + + project = project.WithMetadataReferences(project.MetadataReferences.Where(mr => mr != meta)); + } + } + } + + solution = project.Solution; + } + + return solution; + } + #endregion #region Apply Changes diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 5c0673336df72c4b1bc96b881f825279ddbff92d..83dbc0ac96fd7542ee8c0ebf098aebf248652da3 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -19,7 +19,9 @@ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - + + ..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\\System.Collections.Immutable.dll + False ..\..\..\..\packages\Microsoft.Composition.$(MicrosoftCompositionVersion)\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll