diff --git a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj index c7d366de3af2d89237dd878eb327126990475b74..238d530dac18bca06fcc02493198c0838f590ad3 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj +++ b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj @@ -82,7 +82,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/CSharp/Test/CommandLine/packages.config b/src/Compilers/CSharp/Test/CommandLine/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/CSharp/Test/CommandLine/packages.config +++ b/src/Compilers/CSharp/Test/CommandLine/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 5a978c09b4185141f0ed38cf7a4c8fd794c62981..18ed848605a507150428ed042c8f3f01fdad8a30 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -176,7 +176,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/CSharp/Test/Emit/packages.config b/src/Compilers/CSharp/Test/Emit/packages.config index 4337877d553100319d0b0413490dd856b4f28b67..82a475e0ed1b3c4c00ee04fbec961be7b550ded3 100644 --- a/src/Compilers/CSharp/Test/Emit/packages.config +++ b/src/Compilers/CSharp/Test/Emit/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 1418aca39d09afdb3ae32e5f484e04a80bd04352..a4a40486201128cc3b7913a953bef43ce52d010e 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -136,7 +136,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/CSharp/Test/Semantic/packages.config b/src/Compilers/CSharp/Test/Semantic/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/CSharp/Test/Semantic/packages.config +++ b/src/Compilers/CSharp/Test/Semantic/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj index e201c636649c3db883b30f78fe874dcb8163d0f4..e94724060bf24195291cd9853e1fee1826c0845f 100644 --- a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj +++ b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj @@ -51,7 +51,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/CSharp/Test/Symbol/packages.config b/src/Compilers/CSharp/Test/Symbol/packages.config index 3f8471f627a291dfdfe9046fd07085dd7f19b28e..65ae5e04cb66035f42ea47346e262036b2d147e0 100644 --- a/src/Compilers/CSharp/Test/Symbol/packages.config +++ b/src/Compilers/CSharp/Test/Symbol/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj index 542f3cef69b344507ff01b01d1352ab25dd9185c..71e7dd776184ab3b8edb3681d74c2416283f68e8 100644 --- a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj +++ b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj @@ -163,7 +163,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/CSharp/Test/Syntax/packages.config b/src/Compilers/CSharp/Test/Syntax/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/CSharp/Test/Syntax/packages.config +++ b/src/Compilers/CSharp/Test/Syntax/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj b/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj index 7bfb1661769b5ba08507d8248306a0512de81836..dc1f492b63e5e593f292933e7908e5c1cdf40caf 100644 --- a/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj +++ b/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj @@ -54,7 +54,7 @@ - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll False diff --git a/src/Compilers/CSharp/Test/WinRT/packages.config b/src/Compilers/CSharp/Test/WinRT/packages.config index 79ab56505880de11a9d2778efc712e9cd5d30c72..4b74a5f87bed3c59ce2635453b6476b7f702f555 100644 --- a/src/Compilers/CSharp/Test/WinRT/packages.config +++ b/src/Compilers/CSharp/Test/WinRT/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj index 9f025febdc4df336bd911f936784a83fa775ac60..6876bff74e9ede95da632a8bd431895d1f6f7d33 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj @@ -90,7 +90,7 @@ False - ..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/Core/CodeAnalysisTest/packages.config b/src/Compilers/Core/CodeAnalysisTest/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/Core/CodeAnalysisTest/packages.config +++ b/src/Compilers/Core/CodeAnalysisTest/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/Core/VBCSCompilerTests/VBCSCompilerTests.csproj b/src/Compilers/Core/VBCSCompilerTests/VBCSCompilerTests.csproj index f7d3c84ed112eb2b804067473b6f8f5497d2382c..40aa86297dc14751a2906d1ae15cbf9e7d1d2eea 100644 --- a/src/Compilers/Core/VBCSCompilerTests/VBCSCompilerTests.csproj +++ b/src/Compilers/Core/VBCSCompilerTests/VBCSCompilerTests.csproj @@ -114,7 +114,7 @@ False - ..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/Core/VBCSCompilerTests/packages.config b/src/Compilers/Core/VBCSCompilerTests/packages.config index b6253584024aa0ebfd9460d7306494516e98198a..661f7e53764592c70b317f49d93d88f5791a4890 100644 --- a/src/Compilers/Core/VBCSCompilerTests/packages.config +++ b/src/Compilers/Core/VBCSCompilerTests/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj b/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj index 100fc56dcf51ac2ebae4856d08d0086b143121c8..34f7b295c69a191fbd66d6d5df411df37fb99770 100644 --- a/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj +++ b/src/Compilers/Test/Utilities/Core2/CompilerTestUtilities2.csproj @@ -72,7 +72,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/Test/Utilities/Core2/packages.config b/src/Compilers/Test/Utilities/Core2/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/Test/Utilities/Core2/packages.config +++ b/src/Compilers/Test/Utilities/Core2/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj index 1edc1d4c6e9829aaa123ef31fe98ed2e03a3bfc6..a514f78e7e6688c955eba323180d0ac31fb3dce1 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj +++ b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj @@ -57,7 +57,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/VisualBasic/Test/CommandLine/packages.config b/src/Compilers/VisualBasic/Test/CommandLine/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/packages.config +++ b/src/Compilers/VisualBasic/Test/CommandLine/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj b/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj index 470a76bde2dac69543cc2704674b6fab91184877..3804577cdf355c7a2ebf6af554f18c581a9c7cdd 100644 --- a/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj @@ -85,7 +85,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll False diff --git a/src/Compilers/VisualBasic/Test/Emit/packages.config b/src/Compilers/VisualBasic/Test/Emit/packages.config index 3f8471f627a291dfdfe9046fd07085dd7f19b28e..65ae5e04cb66035f42ea47346e262036b2d147e0 100644 --- a/src/Compilers/VisualBasic/Test/Emit/packages.config +++ b/src/Compilers/VisualBasic/Test/Emit/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index ca80ab53ad3fb41642604ad57ecfa8d5363e6fe3..2af54644f6ad6862245b3a40bebd9703c1efd936 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -78,7 +78,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/VisualBasic/Test/Semantic/packages.config b/src/Compilers/VisualBasic/Test/Semantic/packages.config index 3f8471f627a291dfdfe9046fd07085dd7f19b28e..65ae5e04cb66035f42ea47346e262036b2d147e0 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/packages.config +++ b/src/Compilers/VisualBasic/Test/Semantic/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj index d791d8edb0c3d2c025dadb8e33c0fd78691b0d30..1ccb085de9d8e44ccf65b3cc15de116d6b249ecf 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj @@ -80,7 +80,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll diff --git a/src/Compilers/VisualBasic/Test/Symbol/packages.config b/src/Compilers/VisualBasic/Test/Symbol/packages.config index 4c9f2be0ff5f3fc6a0d644b7ce2051a58324906d..662d4e5b7cb848e4bcf66b38acf55839da33cabc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/packages.config +++ b/src/Compilers/VisualBasic/Test/Symbol/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj index dc33ce99cecf93cea6b0c2f5b4803ebae290608e..a83d622654abc519c13f6552ce6e6a1625ce433a 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj @@ -93,7 +93,7 @@ False - ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Compilers/VisualBasic/Test/Syntax/packages.config b/src/Compilers/VisualBasic/Test/Syntax/packages.config index e87ac866b43ef22e79452b682b033f3b49b1341b..67978efae66b3dfcbc12c39de2c0dd4c7428ae5b 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/packages.config +++ b/src/Compilers/VisualBasic/Test/Syntax/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Diagnostics/CodeAnalysis/CSharp/CSharpCodeAnalysisDiagnosticAnalyzers.csproj b/src/Diagnostics/CodeAnalysis/CSharp/CSharpCodeAnalysisDiagnosticAnalyzers.csproj index 1fea531c628198c625d4503f3b7c561dc5bd2c83..e22a904919ba632c20f070458e521029d737ff8d 100644 --- a/src/Diagnostics/CodeAnalysis/CSharp/CSharpCodeAnalysisDiagnosticAnalyzers.csproj +++ b/src/Diagnostics/CodeAnalysis/CSharp/CSharpCodeAnalysisDiagnosticAnalyzers.csproj @@ -74,6 +74,7 @@ + @@ -84,4 +85,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/CodeAnalysis/CSharp/FixAnalyzers/CSharpFixerWithFixAllAnalyzer.cs b/src/Diagnostics/CodeAnalysis/CSharp/FixAnalyzers/CSharpFixerWithFixAllAnalyzer.cs new file mode 100644 index 0000000000000000000000000000000000000000..20c6fc081c1b15b5ae7e9c5115e2c2bcd2dd5491 --- /dev/null +++ b/src/Diagnostics/CodeAnalysis/CSharp/FixAnalyzers/CSharpFixerWithFixAllAnalyzer.cs @@ -0,0 +1,75 @@ +// 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.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis.Analyzers.FixAnalyzers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.FixAnalyzers +{ + /// + /// A that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. + /// This enables the to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + /// This analyzer catches violations of this requirement in the code actions registered by a fixer that supports . + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpFixerWithFixAllAnalyzer : FixerWithFixAllAnalyzer + { + protected override CompilationAnalyzer GetCompilationAnalyzer(INamedTypeSymbol codeFixProviderSymbol, IMethodSymbol getFixAllProvider, INamedTypeSymbol codeActionSymbol, ImmutableHashSet createMethods, IPropertySymbol equivalenceKeyProperty) + { + return new CSharpCompilationAnalyzer(codeFixProviderSymbol, getFixAllProvider, codeActionSymbol, createMethods, equivalenceKeyProperty); + } + + private sealed class CSharpCompilationAnalyzer : CompilationAnalyzer + { + public CSharpCompilationAnalyzer( + INamedTypeSymbol codeFixProviderSymbol, + IMethodSymbol getFixAllProvider, + INamedTypeSymbol codeActionSymbol, + ImmutableHashSet createMethods, + IPropertySymbol equivalenceKeyProperty) + : base (codeFixProviderSymbol, getFixAllProvider, codeActionSymbol, createMethods, equivalenceKeyProperty) + { + } + + protected override SyntaxKind GetInvocationKind => SyntaxKind.InvocationExpression; + protected override SyntaxKind GetObjectCreationKind => SyntaxKind.ObjectCreationExpression; + + protected override bool HasNonNullArgumentForParameter(SyntaxNode node, IParameterSymbol parameter, int indexOfParameter, SemanticModel model, CancellationToken cancellationToken) + { + var invocation = (InvocationExpressionSyntax)node; + if (invocation.ArgumentList == null) + { + return false; + } + + var seenNamedArgument = false; + var indexOfArgument = 0; + foreach (var argument in invocation.ArgumentList.Arguments) + { + if (argument.NameColon != null) + { + seenNamedArgument = true; + if (parameter.Name.Equals(argument.NameColon.Name.Identifier.ValueText)) + { + return !HasNullConstantValue(argument.Expression, model, cancellationToken); + } + } + else if (!seenNamedArgument) + { + if (indexOfArgument == indexOfParameter) + { + return !HasNullConstantValue(argument.Expression, model, cancellationToken); + } + + indexOfArgument++; + } + } + + return false; + } + } + } +} diff --git a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticAnalyzers.csproj b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticAnalyzers.csproj index 9936e262b883302fa0df432af814b512dc9d178b..dff90e4196bf8015c4ef8b2e1150a57e27b768f1 100644 --- a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticAnalyzers.csproj +++ b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticAnalyzers.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.Designer.cs b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.Designer.cs index 06f5f2c665f5d2b136f03fb5fbf6f396d94cd9b7..7c1c51cdfefa121b17aa699b00d32e886143789c 100644 --- a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.Designer.cs +++ b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.Designer.cs @@ -106,6 +106,33 @@ internal class CodeAnalysisDiagnosticsResources { } } + /// + /// Looks up a localized string similar to A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action.. + /// + internal static string CodeActionNeedsEquivalenceKeyDescription { + get { + return ResourceManager.GetString("CodeActionNeedsEquivalenceKeyDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Provide an explicit argument for optional parameter '{0}', which is non-null and unique across all code actions created by this fixer.. + /// + internal static string CreateCodeActionWithEquivalenceKeyMessage { + get { + return ResourceManager.GetString("CreateCodeActionWithEquivalenceKeyMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create code actions should have a unique EquivalenceKey for FixAll occurrences support.. + /// + internal static string CreateCodeActionWithEquivalenceKeyTitle { + get { + return ResourceManager.GetString("CreateCodeActionWithEquivalenceKeyTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiatied in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks.. /// @@ -152,7 +179,7 @@ internal class CodeAnalysisDiagnosticsResources { } /// - /// Looks up a localized string similar to "Only internal implementations of this interface are allowed.". + /// Looks up a localized string similar to Only internal implementations of this interface are allowed.. /// internal static string InternalImplementationOnlyTitle { get { @@ -316,6 +343,24 @@ internal class CodeAnalysisDiagnosticsResources { } } + /// + /// Looks up a localized string similar to '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action.. + /// + internal static string OverrideCodeActionEquivalenceKeyMessage { + get { + return ResourceManager.GetString("OverrideCodeActionEquivalenceKeyMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use code actions that have a unique EquivalenceKey for FixAll occurrences support.. + /// + internal static string OverrideCodeActionEquivalenceKeyTitle { + get { + return ResourceManager.GetString("OverrideCodeActionEquivalenceKeyTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to SymbolKind '{0}' is not supported for symbol analyzer actions.. /// diff --git a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.resx b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.resx index ea7b9a5cd0bc8afe750108a56b53b4d77a538d66..5401a67db7ef63224cbda8354b4bf457bbbd4737 100644 --- a/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.resx +++ b/src/Diagnostics/CodeAnalysis/Core/CodeAnalysisDiagnosticsResources.resx @@ -219,6 +219,21 @@ Type {0} cannot implement interface {1} because {1} is not available for public implementation. - "Only internal implementations of this interface are allowed." + Only internal implementations of this interface are allowed. + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique across all code actions created by this fixer. + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support. + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support. \ No newline at end of file diff --git a/src/Diagnostics/CodeAnalysis/Core/DiagnosticIds.cs b/src/Diagnostics/CodeAnalysis/Core/DiagnosticIds.cs index 1840402b94fa698ad4ca5b0420a1ee21e8aa9ac3..4443d608f0fa73130cfa6506f7bdbadbc83ee220 100644 --- a/src/Diagnostics/CodeAnalysis/Core/DiagnosticIds.cs +++ b/src/Diagnostics/CodeAnalysis/Core/DiagnosticIds.cs @@ -13,6 +13,8 @@ internal static class DiagnosticIds public const string UseLocalizableStringsInDescriptorRuleId = "RS1007"; public const string DoNotStorePerCompilationDataOntoFieldsRuleId = "RS1008"; public const string InternalImplementationOnlyRuleId = "RS1009"; + public const string CreateCodeActionWithEquivalenceKeyRuleId = "RS1010"; + public const string OverrideCodeActionEquivalenceKeyRuleId = "RS1011"; public const string StartActionWithNoRegisteredActionsRuleId = "RS1012"; public const string StartActionWithOnlyEndActionRuleId = "RS1013"; } diff --git a/src/Diagnostics/CodeAnalysis/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs b/src/Diagnostics/CodeAnalysis/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs new file mode 100644 index 0000000000000000000000000000000000000000..f2fabe008484ac2a0a292f0c652a19f006ad1f0a --- /dev/null +++ b/src/Diagnostics/CodeAnalysis/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs @@ -0,0 +1,355 @@ +// 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.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.FixAnalyzers +{ + /// + /// A that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. + /// This enables the to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + /// This analyzer catches violations of this requirement in the code actions registered by a that supports . + /// + public abstract class FixerWithFixAllAnalyzer : DiagnosticAnalyzer + where TLanguageKindEnum: struct + { + private static string CodeFixProviderMetadataName = typeof(CodeFixProvider).FullName; + private static string CodeActionMetadataName = typeof(CodeAction).FullName; + private const string GetFixAllProviderMethodName = "GetFixAllProvider"; + private const string CreateMethodName = "Create"; + private const string EquivalenceKeyPropertyName = "EquivalenceKey"; + private const string EquivalenceKeyParameterName = "equivalenceKey"; + + private static readonly LocalizableString s_localizableCreateCodeActionWithEquivalenceKeyTitle = new LocalizableResourceString(nameof(CodeAnalysisDiagnosticsResources.CreateCodeActionWithEquivalenceKeyTitle), CodeAnalysisDiagnosticsResources.ResourceManager, typeof(CodeAnalysisDiagnosticsResources)); + private static readonly LocalizableString s_localizableCreateCodeActionWithEquivalenceKeyMessage = new LocalizableResourceString(nameof(CodeAnalysisDiagnosticsResources.CreateCodeActionWithEquivalenceKeyMessage), CodeAnalysisDiagnosticsResources.ResourceManager, typeof(CodeAnalysisDiagnosticsResources)); + + private static readonly LocalizableString s_localizableOverrideCodeActionEquivalenceKeyTitle = new LocalizableResourceString(nameof(CodeAnalysisDiagnosticsResources.OverrideCodeActionEquivalenceKeyTitle), CodeAnalysisDiagnosticsResources.ResourceManager, typeof(CodeAnalysisDiagnosticsResources)); + private static readonly LocalizableString s_localizableOverrideCodeActionEquivalenceKeyMessage = new LocalizableResourceString(nameof(CodeAnalysisDiagnosticsResources.OverrideCodeActionEquivalenceKeyMessage), CodeAnalysisDiagnosticsResources.ResourceManager, typeof(CodeAnalysisDiagnosticsResources)); + + private static readonly LocalizableString s_localizableCodeActionNeedsEquivalenceKeyDescription = new LocalizableResourceString(nameof(CodeAnalysisDiagnosticsResources.CodeActionNeedsEquivalenceKeyDescription), CodeAnalysisDiagnosticsResources.ResourceManager, typeof(CodeAnalysisDiagnosticsResources)); + + internal static readonly DiagnosticDescriptor CreateCodeActionEquivalenceKeyRule = new DiagnosticDescriptor( + DiagnosticIds.CreateCodeActionWithEquivalenceKeyRuleId, + s_localizableCreateCodeActionWithEquivalenceKeyTitle, + s_localizableCreateCodeActionWithEquivalenceKeyMessage, + "Correctness", + DiagnosticSeverity.Warning, + description: s_localizableCodeActionNeedsEquivalenceKeyDescription, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTags.Telemetry); + + internal static readonly DiagnosticDescriptor OverrideCodeActionEquivalenceKeyRule = new DiagnosticDescriptor( + DiagnosticIds.OverrideCodeActionEquivalenceKeyRuleId, + s_localizableOverrideCodeActionEquivalenceKeyTitle, + s_localizableOverrideCodeActionEquivalenceKeyMessage, + "Correctness", + DiagnosticSeverity.Warning, + description: s_localizableCodeActionNeedsEquivalenceKeyDescription, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTags.Telemetry); + + public override ImmutableArray SupportedDiagnostics + { + get { return ImmutableArray.Create(CreateCodeActionEquivalenceKeyRule, OverrideCodeActionEquivalenceKeyRule); } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(CreateAnalyzerWithinCompilation); + } + + private void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + var codeFixProviderSymbol = context.Compilation.GetTypeByMetadataName(CodeFixProviderMetadataName); + if (codeFixProviderSymbol == null) + { + return; + } + + var getFixAllProviderMethod = codeFixProviderSymbol.GetMembers(GetFixAllProviderMethodName).OfType().SingleOrDefault(); + if (getFixAllProviderMethod == null) + { + return; + } + + var codeActionSymbol = context.Compilation.GetTypeByMetadataName(CodeActionMetadataName); + if (codeActionSymbol == null) + { + return; + } + + var createSymbols = codeActionSymbol.GetMembers(CreateMethodName).OfType(); + if (createSymbols == null) + { + return; + } + + var equivalenceKeyProperty = codeActionSymbol.GetMembers(EquivalenceKeyPropertyName).OfType().SingleOrDefault(); + if (equivalenceKeyProperty == null) + { + return; + } + + var compilationAnalyzer = GetCompilationAnalyzer(codeFixProviderSymbol, getFixAllProviderMethod, + codeActionSymbol, ImmutableHashSet.CreateRange(createSymbols), equivalenceKeyProperty); + + context.RegisterSymbolAction(compilationAnalyzer.AnalyzeNamedTypeSymbol, SymbolKind.NamedType); + context.RegisterCodeBlockStartAction(compilationAnalyzer.CodeBlockStart); + context.RegisterCompilationEndAction(compilationAnalyzer.CompilationEnd); + } + + protected abstract CompilationAnalyzer GetCompilationAnalyzer( + INamedTypeSymbol codeFixProviderSymbol, + IMethodSymbol getFixAllProvider, + INamedTypeSymbol codeActionSymbol, + ImmutableHashSet createMethods, + IPropertySymbol equivalenceKeyProperty); + + protected abstract class CompilationAnalyzer + { + private readonly INamedTypeSymbol _codeFixProviderSymbol; + private readonly IMethodSymbol _getFixAllProvider; + + private readonly INamedTypeSymbol _codeActionSymbol; + private readonly ImmutableHashSet _createMethods; + private readonly IPropertySymbol _equivalenceKeyProperty; + + /// + /// Set of all non-abstract sub-types of in this compilation. + /// + private HashSet _codeFixProviders; + + /// + /// Set of all non-abstract sub-types of which override in this compilation. + /// + private HashSet _codeActionsWithEquivalenceKey; + + /// + /// Map of invocations from code fix providers to invocation nodes (and symbols) that create a code action using the static "Create" methods on . + /// + private Dictionary> _codeActionCreateInvocations; + + /// + /// Map of invocations from code fix providers to object creation nodes (and symbols) that create a code action using sub-types of . + /// + private Dictionary> _codeActionObjectCreations; + + private struct NodeAndSymbol + { + public SyntaxNode Node { get; set; } + public IMethodSymbol Symbol { get; set; } + } + + protected CompilationAnalyzer( + INamedTypeSymbol codeFixProviderSymbol, + IMethodSymbol getFixAllProvider, + INamedTypeSymbol codeActionSymbol, + ImmutableHashSet createMethods, + IPropertySymbol equivalenceKeyProperty) + { + _codeFixProviderSymbol = codeFixProviderSymbol; + _getFixAllProvider = getFixAllProvider; + _codeActionSymbol = codeActionSymbol; + _createMethods = createMethods; + _equivalenceKeyProperty = equivalenceKeyProperty; + + _codeFixProviders = null; + _codeActionsWithEquivalenceKey = null; + _codeActionCreateInvocations = null; + _codeActionObjectCreations = null; + } + + internal void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context) + { + var namedType = (INamedTypeSymbol)context.Symbol; + + if (namedType.IsAbstract) + { + return; + } + + if (namedType.DerivesFrom(_codeFixProviderSymbol)) + { + _codeFixProviders = _codeFixProviders ?? new HashSet(); + _codeFixProviders.Add(namedType); + } + else if (namedType.DerivesFrom(_codeActionSymbol)) + { + var equivalenceKeyProperty = namedType.GetMembers(EquivalenceKeyPropertyName).OfType().SingleOrDefault(); + if (equivalenceKeyProperty != null && equivalenceKeyProperty.IsOverride) + { + _codeActionsWithEquivalenceKey = _codeActionsWithEquivalenceKey ?? new HashSet(); + _codeActionsWithEquivalenceKey.Add(namedType); + } + } + } + + protected abstract TLanguageKindEnum GetInvocationKind { get; } + protected abstract TLanguageKindEnum GetObjectCreationKind { get; } + protected abstract bool HasNonNullArgumentForParameter(SyntaxNode invocation, IParameterSymbol parameter, int indexOfParameter, SemanticModel semanticModel, CancellationToken cancellationToken); + + protected bool HasNullConstantValue(SyntaxNode expression, SemanticModel model, CancellationToken cancellationToken) + { + if (expression == null) + { + return false; + } + + var constantValue = model.GetConstantValue(expression, cancellationToken); + return constantValue.HasValue && constantValue.Value == null; + } + + internal void CodeBlockStart(CodeBlockStartAnalysisContext context) + { + var method = context.OwningSymbol as IMethodSymbol; + if (method == null) + { + return; + } + + var namedType = method.ContainingType; + if (!namedType.DerivesFrom(_codeFixProviderSymbol)) + { + return; + } + + context.RegisterSyntaxNodeAction(invocationContext => + { + var invocationSym = invocationContext.SemanticModel.GetSymbolInfo(invocationContext.Node).Symbol as IMethodSymbol; + if (invocationSym != null && _createMethods.Contains(invocationSym)) + { + _codeActionCreateInvocations = _codeActionCreateInvocations ?? new Dictionary>(); + AddNodeAndSymbol(namedType, invocationContext.Node, invocationSym, _codeActionCreateInvocations); + } + }, + GetInvocationKind); + + context.RegisterSyntaxNodeAction(objectCreationContext => + { + var constructor = objectCreationContext.SemanticModel.GetSymbolInfo(objectCreationContext.Node).Symbol as IMethodSymbol; + if (constructor != null && constructor.ContainingType.DerivesFrom(_codeActionSymbol)) + { + _codeActionObjectCreations = _codeActionObjectCreations ?? new Dictionary>(); + AddNodeAndSymbol(namedType, objectCreationContext.Node, constructor, _codeActionObjectCreations); + } + }, + GetObjectCreationKind); + } + + private static void AddNodeAndSymbol(INamedTypeSymbol namedType, SyntaxNode node, IMethodSymbol symbol, Dictionary> map) + { + HashSet value; + if (!map.TryGetValue(namedType, out value)) + { + value = new HashSet(); + map[namedType] = value; + } + + value.Add(new NodeAndSymbol { Node = node, Symbol = symbol }); + } + + internal void CompilationEnd(CompilationAnalysisContext context) + { + if (_codeFixProviders == null) + { + // No fixers. + return; + } + + if (_codeActionCreateInvocations == null && _codeActionObjectCreations == null) + { + // No registered fixes. + return; + } + + // Analyze all fixers that have FixAll support. + foreach (var fixer in _codeFixProviders) + { + if (OverridesGetFixAllProvider(fixer)) + { + AnalyzeFixerWithFixAll(fixer, context); + } + } + } + + private bool OverridesGetFixAllProvider(INamedTypeSymbol fixer) + { + foreach (var type in fixer.GetBaseTypesAndThis()) + { + if (!type.Equals(_codeFixProviderSymbol)) + { + var getFixAllProviderProperty = type.GetMembers(GetFixAllProviderMethodName).OfType().SingleOrDefault(); + if (getFixAllProviderProperty != null && getFixAllProviderProperty.IsOverride) + { + return true; + } + } + } + + return false; + } + + private void AnalyzeFixerWithFixAll(INamedTypeSymbol fixer, CompilationAnalysisContext context) + { + if (_codeActionCreateInvocations != null) + { + HashSet nodeAndSymbolSet; + if (_codeActionCreateInvocations.TryGetValue(fixer, out nodeAndSymbolSet)) + { + foreach (var nodeAndSymbol in nodeAndSymbolSet) + { + var model = context.Compilation.GetSemanticModel(nodeAndSymbol.Node.SyntaxTree); + if (IsViolatingCodeActionCreateInvocation(nodeAndSymbol.Node, nodeAndSymbol.Symbol, model, context.CancellationToken)) + { + var diagnostic = Diagnostic.Create(CreateCodeActionEquivalenceKeyRule, nodeAndSymbol.Node.GetLocation(), EquivalenceKeyParameterName); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + if (_codeActionObjectCreations != null) + { + HashSet nodeAndSymbolSet; + if (_codeActionObjectCreations.TryGetValue(fixer, out nodeAndSymbolSet)) + { + foreach (var nodeAndSymbol in nodeAndSymbolSet) + { + if (IsViolatingCodeActionObjectCreation(nodeAndSymbol.Node, nodeAndSymbol.Symbol)) + { + var diagnostic = Diagnostic.Create(OverrideCodeActionEquivalenceKeyRule, nodeAndSymbol.Node.GetLocation(), nodeAndSymbol.Symbol.ContainingType, EquivalenceKeyPropertyName); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + + private bool IsViolatingCodeActionCreateInvocation(SyntaxNode invocation, IMethodSymbol invocationSym, SemanticModel model, CancellationToken cancellationToken) + { + var param = invocationSym.Parameters.SingleOrDefault(p => p.Name == EquivalenceKeyParameterName); + if (param == null) + { + return true; + } + + var index = invocationSym.Parameters.IndexOf(param); + return !HasNonNullArgumentForParameter(invocation, param, index, model, cancellationToken); + } + + private bool IsViolatingCodeActionObjectCreation(SyntaxNode objectCreation, IMethodSymbol constructor) + { + return _codeActionsWithEquivalenceKey == null || + !constructor.ContainingType.GetBaseTypesAndThis().Any(_codeActionsWithEquivalenceKey.Contains); + } + } + } +} diff --git a/src/Diagnostics/CodeAnalysis/Core/Helpers/AttributeHelpers.cs b/src/Diagnostics/CodeAnalysis/Core/Helpers/AttributeHelpers.cs index 9c1be8172edacab3145f39d92adf95702206bfe8..f99a7c4fb43d9b7aa712d07c8c323536f034c78c 100644 --- a/src/Diagnostics/CodeAnalysis/Core/Helpers/AttributeHelpers.cs +++ b/src/Diagnostics/CodeAnalysis/Core/Helpers/AttributeHelpers.cs @@ -20,7 +20,7 @@ internal static IEnumerable GetApplicableAttributes(INamedTypeSym return attributes; } - internal static bool DerivesFrom(INamedTypeSymbol symbol, INamedTypeSymbol candidateBaseType) + internal static bool DerivesFrom(this INamedTypeSymbol symbol, INamedTypeSymbol candidateBaseType) { while (symbol != null) { diff --git a/src/Diagnostics/CodeAnalysis/Test/CodeAnalysisDiagnosticAnalyzersTest.csproj b/src/Diagnostics/CodeAnalysis/Test/CodeAnalysisDiagnosticAnalyzersTest.csproj index 002170df346c7cc33508ac61b0dd7abb5d164916..4fa61de03e093464e16f43f30404351ec5ff1f5a 100644 --- a/src/Diagnostics/CodeAnalysis/Test/CodeAnalysisDiagnosticAnalyzersTest.csproj +++ b/src/Diagnostics/CodeAnalysis/Test/CodeAnalysisDiagnosticAnalyzersTest.csproj @@ -91,6 +91,7 @@ + diff --git a/src/Diagnostics/CodeAnalysis/Test/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs b/src/Diagnostics/CodeAnalysis/Test/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..1ee6660da9bbea8fe01d8c54f54c5bf4b2a18dfe --- /dev/null +++ b/src/Diagnostics/CodeAnalysis/Test/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs @@ -0,0 +1,525 @@ +// 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.Analyzers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Analyzers.FixAnalyzers; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.FixAnalyzers; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.Analyzers.FixAnalyzers +{ + public class FixerWithFixAllAnalyzerTests : CodeFixTestBase + { + + #region CSharp tests + + private static string s_CSharpCustomCodeActions = @" +public class MyCodeActionNoEquivalenceKey : CodeAction +{ + public override string Title + { + get + { + throw new NotImplementedException(); + } + } +} + +public class MyCodeActionWithEquivalenceKey : CodeAction +{ + public override string Title + { + get + { + throw new NotImplementedException(); + } + } + + public override string EquivalenceKey + { + get + { + return ""DummyEquivalenceKey""; + } + } +} +"; + private void TestCSharpCore(string source, bool withCustomCodeActions = false, params DiagnosticResult[] expected) + { + var fixAllProviderString = @"public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + }"; + + var sourceSuffix = @" +}"; + + if (withCustomCodeActions) + { + sourceSuffix = sourceSuffix + s_CSharpCustomCodeActions; + } + + // Verify expected diagnostics for fixer that supports FixAllProvider. + VerifyCSharp(source + fixAllProviderString + sourceSuffix, expected); + + // Verify no diagnostics for fixer that does not support FixAllProvider. + VerifyCSharp(source + sourceSuffix); + } + + [Fact] + public void CSharp_CodeActionCreate_VerifyDiagnostics() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Regular cases. + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + var codeAction1_2 = CodeAction.Create(""Title1_2"", createChangedDocument: _ => Task.FromResult(context.Document)); + var codeAction1_3 = CodeAction.Create(createChangedDocument: _ => Task.FromResult(context.Document), title: ""Title1_3""); + + // Null argument for equivalenceKey. + var codeAction2_1 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), null); + var codeAction2_2 = CodeAction.Create(createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: null, title: ""Title2_2""); + var codeAction2_3 = CodeAction.Create(""Title2_3"", _ => Task.FromResult(context.Document), equivalenceKey: null); + + return null; + } +"; + + var expected = new DiagnosticResult[] + { + // Test0.cs(21,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(21, 29), + // Test0.cs(22,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(22, 29), + // Test0.cs(23,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(23, 29), + // Test0.cs(26,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(26, 29), + // Test0.cs(27,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(27, 29), + // Test0.cs(28,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCodeActionCreateExpectedDiagnostic(28, 29) + }; + + TestCSharpCore(source, expected: expected); + } + + [Fact] + public void CSharp_CodeActionCreate_NoDiagnostics() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Overload resolution failure cases. + var codeAction1_1 = CodeAction.Create(""Title1_1""); + var codeAction1_2 = CodeAction.Create(createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: null); + + // Correct non-null arguments + var equivalenceKey = ""equivalenceKey""; + var codeAction2_1 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), equivalenceKey); + var codeAction2_2 = CodeAction.Create(title: ""Title2_2"", createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: equivalenceKey); + var codeAction2_3 = CodeAction.Create(equivalenceKey: equivalenceKey, title: ""Title2_3"", createChangedDocument: _ => Task.FromResult(context.Document)); + + // Conservative no diagnostic cases. + var nullKey = null; + var codeAction3_1 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), nullKey); + var codeAction3_2 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), GetKey()); + + context.RegisterCodeFix(codeAction, context.Diagnostics); + return null; + } + + private string GetKey() + { + return null; + } +"; + // Verify no diagnostics. + TestCSharpCore(source); + } + + [Fact] + public void CSharp_CustomCodeAction_VerifyDiagnostics() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction = new MyCodeActionNoEquivalenceKey(); + return null; + } +"; + + var expected = new DiagnosticResult[] + { + // Test0.cs(20,26): warning RS1011: 'MyCodeActionNoEquivalenceKey' has the default value of 'null' for property 'EquivalenceKey'. Either override this property on 'MyCodeActionNoEquivalenceKey' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + GetCSharpCustomCodeActionExpectedDiagnostic(20, 26, "MyCodeActionNoEquivalenceKey") + }; + + TestCSharpCore(source, withCustomCodeActions: true, expected: expected); + } + + [Fact] + public void CSharp_CustomCodeAction_NoDiagnostics() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction = new MyCodeActionWithEquivalenceKey(); + context.RegisterCodeFix(codeAction, context.Diagnostics); + return null; + } + + private string GetKey() + { + return null; + } +"; + // Verify no diagnostics. + TestCSharpCore(source, withCustomCodeActions: true); + } + + #endregion + + #region VisualBasic tests + + private static string s_VisualBasicCustomCodeActions = @" + +Public Class MyCodeActionNoEquivalenceKey + Inherits CodeAction + Public Overrides ReadOnly Property Title() As String + Get + Throw New NotImplementedException() + End Get + End Property +End Class + +Public Class MyCodeActionWithEquivalenceKey + Inherits CodeAction + Public Overrides ReadOnly Property Title() As String + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides ReadOnly Property EquivalenceKey() As String + Get + Return ""DummyEquivalenceKey"" + End Get + End Property +End Class +"; + private void TestBasicCore(string source, bool withCustomCodeActions = false, params DiagnosticResult[] expected) + { + var fixAllProviderString = @"Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer +End Function +"; + + var sourceSuffix = @" +End Class +"; + + if (withCustomCodeActions) + { + sourceSuffix = sourceSuffix + s_VisualBasicCustomCodeActions; + } + + // Verify expected diagnostics for fixer that supports FixAllProvider. + VerifyBasic(source + fixAllProviderString + sourceSuffix, expected); + + // Verify no diagnostics for fixer that does not support FixAllProvider. + VerifyBasic(source + sourceSuffix); + } + + [Fact] + public void VisualBasic_CodeActionCreate_VerifyDiagnostics() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(_) Task.FromResult(context.Document)) + Dim codeAction1_2 = CodeAction.Create(""Title1_2"", createChangedDocument := Function(_) Task.FromResult(context.Document)) + Dim codeAction1_3 = CodeAction.Create(createChangedDocument := Function(_) Task.FromResult(context.Document), title := ""Title1_3"") + + ' Null argument for equivalenceKey. + Dim codeAction2_1 = CodeAction.Create(""Title2_1"", Function(_) Task.FromResult(context.Document), Nothing) + Dim codeAction2_2 = CodeAction.Create(createChangedDocument := Function(_) Task.FromResult(context.Document), equivalenceKey := Nothing, title := ""Title2_2"") + Dim codeAction2_3 = CodeAction.Create(""Title2_3"", Function(_) Task.FromResult(context.Document), equivalenceKey := Nothing) + + Return Nothing + End Function +"; + + var expected = new DiagnosticResult[] + { + // Test0.vb(18,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(18, 23), + // Test0.vb(19,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(19, 23), + // Test0.vb(20,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(20, 23), + // Test0.vb(23,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(23, 23), + // Test0.vb(24,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(24, 23), + // Test0.vb(25,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCodeActionCreateExpectedDiagnostic(25, 23) + }; + + TestBasicCore(source, expected: expected); + } + + [Fact] + public void VisualBasic_CodeActionCreate_NoDiagnostics() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Overload resolution failure cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"") + Dim codeAction1_2 = CodeAction.Create(createChangedDocument := Function(_) Task.FromResult(context.Document), equivalenceKey := Nothing) + + ' Correct non-null arguments + Dim equivalenceKey = ""equivalenceKey"" + Dim codeAction2_1 = CodeAction.Create(""Title2_1"", Function(_) Task.FromResult(context.Document), equivalenceKey) + Dim codeAction2_2 = CodeAction.Create(title := ""Title2_2"", createChangedDocument := Function(_) Task.FromResult(context.Document), equivalenceKey := equivalenceKey) + Dim codeAction2_3 = CodeAction.Create(equivalenceKey := equivalenceKey, title := ""Title2_3"", createChangedDocument := Function(_) Task.FromResult(context.Document)) + + ' Conservative no diagnostic cases. + Dim nullKey = Nothing + Dim codeAction3_1 = CodeAction.Create(""Title2_1"", Function(_) Task.FromResult(context.Document), nullKey) + Dim codeAction3_2 = CodeAction.Create(""Title2_1"", Function(_) Task.FromResult(context.Document), GetKey()) + + context.RegisterCodeFix(codeAction, context.Diagnostics) + Return Nothing + End Function + + Private Function GetKey() As String + Return Nothing + End Function +"; + // Verify no diagnostics. + TestBasicCore(source); + } + + [Fact] + public void VisualBasic_CustomCodeAction_VerifyDiagnostics() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + Dim codeAction = New MyCodeActionNoEquivalenceKey() + Return Nothing + End Function +"; + + var expected = new DiagnosticResult[] + { + // Test0.vb(17,20): warning RS1011: 'MyCodeActionNoEquivalenceKey' has the default value of 'null' for property 'EquivalenceKey'. Either override this property on 'MyCodeActionNoEquivalenceKey' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + GetBasicCustomCodeActionExpectedDiagnostic(17, 20, "MyCodeActionNoEquivalenceKey") + }; + + TestBasicCore(source, withCustomCodeActions: true, expected: expected); + } + + [Fact] + public void VisualBasic_CustomCodeAction_NoDiagnostics() + { + var source = @" +using System; +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + Dim codeAction = New MyCodeActionWithEquivalenceKey() + context.RegisterCodeFix(codeAction, context.Diagnostics) + Return Nothing + End Function + + Private Function GetKey() As String + Return Nothing + End Function +"; + // Verify no diagnostics. + TestBasicCore(source, withCustomCodeActions: true); + } + + #endregion + + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return null; + } + + protected override CodeFixProvider GetBasicCodeFixProvider() + { + return null; + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new CSharpFixerWithFixAllAnalyzer(); + } + + protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() + { + return new BasicFixerWithFixAllAnalyzer(); + } + + private static DiagnosticResult GetCSharpCustomCodeActionExpectedDiagnostic(int line, int column, string customCodeActionName) + { + var message = string.Format(CodeAnalysisDiagnosticsResources.OverrideCodeActionEquivalenceKeyMessage, customCodeActionName, "EquivalenceKey"); + return GetExpectedDiagnostic(LanguageNames.CSharp, line, column, CSharpFixerWithFixAllAnalyzer.OverrideCodeActionEquivalenceKeyRule.Id, message); + } + + private static DiagnosticResult GetBasicCustomCodeActionExpectedDiagnostic(int line, int column, string customCodeActionName) + { + var message = string.Format(CodeAnalysisDiagnosticsResources.OverrideCodeActionEquivalenceKeyMessage, customCodeActionName, "EquivalenceKey"); + return GetExpectedDiagnostic(LanguageNames.VisualBasic, line, column, BasicFixerWithFixAllAnalyzer.OverrideCodeActionEquivalenceKeyRule.Id, message); + } + + private static DiagnosticResult GetCSharpCodeActionCreateExpectedDiagnostic(int line, int column) + { + var message = string.Format(CodeAnalysisDiagnosticsResources.CreateCodeActionWithEquivalenceKeyMessage, "equivalenceKey"); + return GetExpectedDiagnostic(LanguageNames.CSharp, line, column, CSharpFixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule.Id, message); + } + + private static DiagnosticResult GetBasicCodeActionCreateExpectedDiagnostic(int line, int column) + { + var message = string.Format(CodeAnalysisDiagnosticsResources.CreateCodeActionWithEquivalenceKeyMessage, "equivalenceKey"); + return GetExpectedDiagnostic(LanguageNames.VisualBasic, line, column, BasicFixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule.Id, message); + } + + private static DiagnosticResult GetExpectedDiagnostic(string language, int line, int column, string id, string message) + { + var fileName = language == LanguageNames.CSharp ? "Test0.cs" : "Test0.vb"; + return new DiagnosticResult + { + Id = id, + Message = message, + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation(fileName, line, column) + } + }; + } + } +} diff --git a/src/Diagnostics/CodeAnalysis/VisualBasic/BasicCodeAnalysisDiagnosticAnalyzers.vbproj b/src/Diagnostics/CodeAnalysis/VisualBasic/BasicCodeAnalysisDiagnosticAnalyzers.vbproj index b1e60bf81a3d3e328b39e03e8806b03dab20596f..cdd8c45fea714d8eb7c9bccd82da6d7881a4587b 100644 --- a/src/Diagnostics/CodeAnalysis/VisualBasic/BasicCodeAnalysisDiagnosticAnalyzers.vbproj +++ b/src/Diagnostics/CodeAnalysis/VisualBasic/BasicCodeAnalysisDiagnosticAnalyzers.vbproj @@ -73,6 +73,7 @@ + @@ -83,4 +84,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/CodeAnalysis/VisualBasic/FixAnalyzers/BasicFixerWithFixAllAnalyzer.vb b/src/Diagnostics/CodeAnalysis/VisualBasic/FixAnalyzers/BasicFixerWithFixAllAnalyzer.vb new file mode 100644 index 0000000000000000000000000000000000000000..5cc61d87ccd0073d6ce2faee0c2dd16a1d8e7336 --- /dev/null +++ b/src/Diagnostics/CodeAnalysis/VisualBasic/FixAnalyzers/BasicFixerWithFixAllAnalyzer.vb @@ -0,0 +1,70 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Threading +Imports Microsoft.CodeAnalysis.Analyzers.FixAnalyzers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.FixAnalyzers + + ''' + ''' A that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. + ''' This enables the to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + ''' This analyzer catches violations of this requirement in the code actions registered by a fixer that supports . + ''' + + Public NotInheritable Class BasicFixerWithFixAllAnalyzer + Inherits FixerWithFixAllAnalyzer(Of SyntaxKind) + Protected Overrides Function GetCompilationAnalyzer(codeFixProviderSymbol As INamedTypeSymbol, getFixAllProvider As IMethodSymbol, codeActionSymbol As INamedTypeSymbol, createMethods As ImmutableHashSet(Of IMethodSymbol), equivalenceKeyProperty As IPropertySymbol) As CompilationAnalyzer + Return New CSharpCompilationAnalyzer(codeFixProviderSymbol, getFixAllProvider, codeActionSymbol, createMethods, equivalenceKeyProperty) + End Function + + Private NotInheritable Class CSharpCompilationAnalyzer + Inherits CompilationAnalyzer + Public Sub New(codeFixProviderSymbol As INamedTypeSymbol, getFixAllProvider As IMethodSymbol, codeActionSymbol As INamedTypeSymbol, createMethods As ImmutableHashSet(Of IMethodSymbol), equivalenceKeyProperty As IPropertySymbol) + MyBase.New(codeFixProviderSymbol, getFixAllProvider, codeActionSymbol, createMethods, equivalenceKeyProperty) + End Sub + + Protected Overrides ReadOnly Property GetInvocationKind As SyntaxKind + Get + Return SyntaxKind.InvocationExpression + End Get + End Property + + Protected Overrides ReadOnly Property GetObjectCreationKind As SyntaxKind + Get + Return SyntaxKind.ObjectCreationExpression + End Get + End Property + + Protected Overrides Function HasNonNullArgumentForParameter(node As SyntaxNode, parameter As IParameterSymbol, indexOfParameter As Integer, model As SemanticModel, cancellationToken As CancellationToken) As Boolean + Dim invocation = DirectCast(node, InvocationExpressionSyntax) + If invocation.ArgumentList Is Nothing Then + Return False + End If + + Dim seenNamedArgument = False + Dim indexOfArgument = 0 + For Each argument In invocation.ArgumentList.Arguments + If argument.IsNamed Then + seenNamedArgument = True + Dim simpleArgument = TryCast(argument, SimpleArgumentSyntax) + If simpleArgument IsNot Nothing AndAlso parameter.Name.Equals(simpleArgument.NameColonEquals.Name.Identifier.ValueText) Then + Return Not HasNullConstantValue(simpleArgument.Expression, model, cancellationToken) + End If + ElseIf Not seenNamedArgument Then + If indexOfArgument = indexOfParameter Then + Return Not HasNullConstantValue(argument.GetExpression, model, cancellationToken) + End If + + indexOfArgument += 1 + End If + Next + + Return False + End Function + End Class + End Class +End Namespace diff --git a/src/Diagnostics/Roslyn/Test/Performance/SpecializedEnumerableCreationAnalyzerTests.cs b/src/Diagnostics/Roslyn/Test/Performance/SpecializedEnumerableCreationAnalyzerTests.cs index 99b997fb80b9b634a829c641c3af1d384a21d584..d83aa8c983adb84872212283c8565ddef7d920f2 100644 --- a/src/Diagnostics/Roslyn/Test/Performance/SpecializedEnumerableCreationAnalyzerTests.cs +++ b/src/Diagnostics/Roslyn/Test/Performance/SpecializedEnumerableCreationAnalyzerTests.cs @@ -15,6 +15,19 @@ namespace Microsoft.CodeAnalysis.UnitTests.Performance { public class SpecializedEnumerableCreationAnalyzerTests : DiagnosticAnalyzerTestBase { + private string _csharpSpecializedCollectionsDefinition = @" +namespace Roslyn.Utilities +{ + public class SpecializedCollections { } +} +"; + private string _basicSpecializedCollectionsDefinition = @" +Namespace Roslyn.Utilities + Public Class SpecializedCollections + End Class +End Namespace +"; + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() { return new CSharpSpecializedEnumerableCreationAnalyzer(); @@ -37,7 +50,7 @@ class C IEnumerable M2() { return new int[0] { }; } int[] M3() { return new int[0]; } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(6, 36, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule), GetCSharpResultAt(7, 36, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -55,7 +68,7 @@ class C IEnumerable M3() { return new[] { 1 }; } int[] M4() { return new[] { 1 }; } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(6, 36, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetCSharpResultAt(7, 36, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetCSharpResultAt(8, 36, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); @@ -72,7 +85,7 @@ class C { IEnumerable M1() { return Enumerable.Empty(); } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(7, 36, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -87,7 +100,7 @@ class C IEnumerable M1() { return 0 == 1 ? new[] { 1 } : new[] { 2 }; } IEnumerable M2() { return null ?? new int[0]; } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(6, 45, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetCSharpResultAt(6, 59, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetCSharpResultAt(7, 44, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); @@ -105,7 +118,7 @@ class C IEnumerable M1() { return 0 == 1 ? Enumerable.Empty() : null; } IEnumerable M2() { return null ?? Enumerable.Empty(); } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(7, 45, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule), GetCSharpResultAt(8, 44, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -123,7 +136,7 @@ class C IEnumerable M3() { return new[] { 1, 2 }; } int[] M4() { return new[] { 1, 2 }; } } -"); +" + _csharpSpecializedCollectionsDefinition); } [Fact] @@ -138,7 +151,7 @@ class C IEnumerable M2() { return new[] { new[] { 1 } }; } IEnumerable M3() { return new[] { new[] { 1, 2, 3 }, new[] { 1 } }; } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(7, 38, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); } @@ -152,7 +165,7 @@ class C { IEnumerable> M1() { return new[] { new[] { 1 } }; } } -", +" + _csharpSpecializedCollectionsDefinition, GetCSharpResultAt(5, 49, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetCSharpResultAt(5, 57, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); } @@ -171,7 +184,7 @@ Function M2() As IEnumerable(Of Integer) Return {} End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(6, 16, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule), GetBasicResultAt(9, 16, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -188,7 +201,7 @@ Function M1() As IEnumerable(Of Integer) Return Enumerable.Empty(Of Integer)() End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(7, 16, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -206,7 +219,7 @@ Function M2() As IEnumerable(Of Integer) Return {1} End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(6, 16, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetBasicResultAt(9, 16, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); } @@ -225,7 +238,7 @@ Function M2() As IEnumerable(Of Integer) Return If(True, {1}) End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(6, 25, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetBasicResultAt(6, 30, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetBasicResultAt(9, 25, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); @@ -246,7 +259,7 @@ Function M2() As IEnumerable(Of Integer) Return If(True, Enumerable.Empty(Of Integer)()) End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(7, 25, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule), GetBasicResultAt(10, 25, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule)); } @@ -265,7 +278,7 @@ Function M2() As IEnumerable(Of Integer) Return {1, 2} End Function End Class -"); +" + _basicSpecializedCollectionsDefinition); } [Fact] @@ -285,7 +298,7 @@ Function M3() As IEnumerable(Of Integer()) Return {({1, 2, 3}), ({1})} End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(9, 16, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); } @@ -300,7 +313,7 @@ Function M1() As IEnumerable(Of IEnumerable(Of Integer)) Return {({1})} End Function End Class -", +" + _basicSpecializedCollectionsDefinition, GetBasicResultAt(6, 16, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule), GetBasicResultAt(6, 17, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule)); } diff --git a/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs b/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs index 7c0321590e05d0f3c095b45ed6bc6dc9f923d6a5..8ba666d7491ce0c992e117bddae3349257ac1641 100644 --- a/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs +++ b/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs @@ -22,6 +22,7 @@ public abstract class DiagnosticAnalyzerTestBase private static readonly MetadataReference s_CSharpSymbolsReference = MetadataReference.CreateFromAssembly(typeof(CSharpCompilation).Assembly); private static readonly MetadataReference s_visualBasicSymbolsReference = MetadataReference.CreateFromAssembly(typeof(VisualBasicCompilation).Assembly); private static readonly MetadataReference s_codeAnalysisReference = MetadataReference.CreateFromAssembly(typeof(Compilation).Assembly); + private static readonly MetadataReference s_workspacesReference = MetadataReference.CreateFromAssembly(typeof(Workspace).Assembly); private static readonly MetadataReference s_immutableCollectionsReference = MetadataReference.CreateFromAssembly(typeof(ImmutableArray).Assembly); private static readonly CompilationOptions s_CSharpDefaultOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); private static readonly CompilationOptions s_visualBasicDefaultOptions = new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); @@ -267,7 +268,10 @@ protected static Project CreateProject(string[] sources, string language = Langu .AddMetadataReference(projectId, s_codeAnalysisReference) .AddMetadataReference(projectId, TestBase.SystemRef) .AddMetadataReference(projectId, TestBase.SystemRuntimeFacadeRef) + .AddMetadataReference(projectId, TestBase.SystemThreadingFacadeRef) + .AddMetadataReference(projectId, TestBase.SystemThreadingTaskFacadeRef) .AddMetadataReference(projectId, s_immutableCollectionsReference) + .AddMetadataReference(projectId, s_workspacesReference) .WithProjectCompilationOptions(projectId, options); if (addLanguageSpecificCodeAnalysisReference) diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 578f868840d88f59872425a9018255d6b0674f7d..7e67b6be671ee561880365344e4b817668c66529 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -14,7 +14,7 @@ False - ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll @@ -675,4 +675,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/CSharpTest/packages.config b/src/EditorFeatures/CSharpTest/packages.config index d7c8382bd468b13a175ca9bbc360319a2a23d357..f8fe393845e4f610a155f959b59003085d224adf 100644 --- a/src/EditorFeatures/CSharpTest/packages.config +++ b/src/EditorFeatures/CSharpTest/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index 5611d51b31f6eaaf22028c4610fbc5846e759e2c..216c9a225ade19fe5982d973c4ecbce04607075e 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -21,7 +21,7 @@ False - ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll @@ -643,4 +643,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/VisualBasicTest/packages.config b/src/EditorFeatures/VisualBasicTest/packages.config index d7c8382bd468b13a175ca9bbc360319a2a23d357..f8fe393845e4f610a155f959b59003085d224adf 100644 --- a/src/EditorFeatures/VisualBasicTest/packages.config +++ b/src/EditorFeatures/VisualBasicTest/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Test/Utilities/TestBase.cs b/src/Test/Utilities/TestBase.cs index 4d7af799bc58609f76e5b66e2b6947939373641c..1dd10c4e91c2bb017f00d84d91343a50ec07826d 100644 --- a/src/Test/Utilities/TestBase.cs +++ b/src/Test/Utilities/TestBase.cs @@ -456,6 +456,34 @@ public static MetadataReference SystemRuntimeFacadeRef } } + private static MetadataReference s_systemThreadingFacadeRef; + public static MetadataReference SystemThreadingFacadeRef + { + get + { + if (s_systemThreadingFacadeRef == null) + { + s_systemThreadingFacadeRef = AssemblyMetadata.CreateFromImage(ProprietaryTestResources.NetFX.ReferenceAssemblies_V45_Facades.System_Threading).GetReference(display: "System.Threading.dll"); + } + + return s_systemThreadingFacadeRef; + } + } + + private static MetadataReference s_systemThreadingTasksFacadeRef; + public static MetadataReference SystemThreadingTaskFacadeRef + { + get + { + if (s_systemThreadingTasksFacadeRef == null) + { + s_systemThreadingTasksFacadeRef = AssemblyMetadata.CreateFromImage(ProprietaryTestResources.NetFX.ReferenceAssemblies_V45_Facades.System_Threading_Tasks).GetReference(display: "System.Threading.Tasks.dll"); + } + + return s_systemThreadingTasksFacadeRef; + } + } + private static MetadataReference s_mscorlibPP7Ref; public static MetadataReference MscorlibPP7Ref { diff --git a/src/Test/Utilities/TestUtilities.csproj b/src/Test/Utilities/TestUtilities.csproj index 7524724ac87490c088a599b83b2b48da6d2a2057..d9c91f1493d95efa41654c5d0370edd77ca0d78b 100644 --- a/src/Test/Utilities/TestUtilities.csproj +++ b/src/Test/Utilities/TestUtilities.csproj @@ -89,7 +89,7 @@ False - ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll + ..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll ..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll diff --git a/src/Test/Utilities/packages.config b/src/Test/Utilities/packages.config index 3f8471f627a291dfdfe9046fd07085dd7f19b28e..65ae5e04cb66035f42ea47346e262036b2d147e0 100644 --- a/src/Test/Utilities/packages.config +++ b/src/Test/Utilities/packages.config @@ -1,6 +1,6 @@  - +