提交 b5c0889d 编写于 作者: M Manish Vasani

Symptom: Fix all not available when providing more than one code fix

User scenario: A CodeFixProvider that supports fix all occurrences code fix, gets FixAll support in IDE if it registers a single code action, but disappears as soon as fixer registers multiple code actions.

Reason: 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.
The analyzer added by this change catches violations of this requirement in the code actions registered by a CodeFixProvider that supports FixAllProvider.

Fixes #2864
上级 f0aba114
......@@ -82,7 +82,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -176,7 +176,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="xunit" version="2.0.0-alpha-build2576" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0-alpha-build2576" targetFramework="net45" />
......
......@@ -136,7 +136,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -51,7 +51,7 @@
<ItemGroup Label="File References">
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll" />
<Reference Include="..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -163,7 +163,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -54,7 +54,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary">
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=$(SystemCollectionsImmutableAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -90,7 +90,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -114,7 +114,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Collections" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="Moq" version="4.2.1402.2112" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
......
......@@ -72,7 +72,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -57,7 +57,7 @@
<ItemGroup Label="File References">
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="..\..\..\..\..\packages\System.Reflection.Metadata.$(SystemReflectionMetadataVersion)\lib\portable-net45+win8\System.Reflection.Metadata.dll" />
<Reference Include="..\..\..\..\..\packages\System.Collections.Immutable.$(SystemCollectionsImmutableVersion)\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -85,7 +85,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Roslyn.Test.Utilities, Version=42.42.42.42, Culture=neutral, PublicKeyToken=fc793a00266884fb, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -78,7 +78,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="xunit">
<HintPath>..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -80,7 +80,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -93,7 +93,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="xunit">
<HintPath>..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="xunit" version="2.0.0-alpha-build2576" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0-alpha-build2576" targetFramework="net45" />
<package id="xunit.assert" version="2.0.0-alpha-build2576" targetFramework="net45" />
......
......@@ -74,6 +74,7 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Compile Include="FixAnalyzers\CSharpFixerWithFixAllAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpDiagnosticDescriptorCreationAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpRegisterActionAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpDiagnosticAnalyzerFieldsAnalyzer.cs" />
......@@ -84,4 +85,4 @@
<Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\build\VSL.Imports.Closed.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
// 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
{
/// <summary>
/// A <see cref="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 <see cref="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.
/// This analyzer catches violations of this requirement in the code actions registered by a fixer that supports <see cref="FixAllProvider"/>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CSharpFixerWithFixAllAnalyzer : FixerWithFixAllAnalyzer<SyntaxKind>
{
protected override CompilationAnalyzer GetCompilationAnalyzer(INamedTypeSymbol codeFixProviderSymbol, IMethodSymbol getFixAllProvider, INamedTypeSymbol codeActionSymbol, ImmutableHashSet<IMethodSymbol> 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<IMethodSymbol> 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;
}
}
}
}
......@@ -64,6 +64,7 @@
</Compile>
<Compile Include="DiagnosticCategory.cs" />
<Compile Include="DiagnosticIds.cs" />
<Compile Include="FixAnalyzers\FixerWithFixAllAnalyzer.cs" />
<Compile Include="Helpers\AttributeHelpers.cs" />
<Compile Include="Helpers\DocumentChangedAction.cs" />
<Compile Include="Helpers\ITypeSymbolExtensions.cs" />
......
......@@ -106,6 +106,33 @@ internal class CodeAnalysisDiagnosticsResources {
}
}
/// <summary>
/// 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..
/// </summary>
internal static string CodeActionNeedsEquivalenceKeyDescription {
get {
return ResourceManager.GetString("CodeActionNeedsEquivalenceKeyDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Provide an explicit argument for optional parameter &apos;{0}&apos;, which is non-null and unique across all code actions created by this fixer..
/// </summary>
internal static string CreateCodeActionWithEquivalenceKeyMessage {
get {
return ResourceManager.GetString("CreateCodeActionWithEquivalenceKeyMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create code actions should have a unique EquivalenceKey for FixAll occurrences support..
/// </summary>
internal static string CreateCodeActionWithEquivalenceKeyTitle {
get {
return ResourceManager.GetString("CreateCodeActionWithEquivalenceKeyTitle", resourceCulture);
}
}
/// <summary>
/// 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 &apos;{0}.{1}&apos; API. An instance of this type will be created per-compilation and it won&apos;t outlive compilation&apos;s lifetime, hence avoiding memory leaks..
/// </summary>
......@@ -152,7 +179,7 @@ internal class CodeAnalysisDiagnosticsResources {
}
/// <summary>
/// Looks up a localized string similar to &quot;Only internal implementations of this interface are allowed.&quot;.
/// Looks up a localized string similar to Only internal implementations of this interface are allowed..
/// </summary>
internal static string InternalImplementationOnlyTitle {
get {
......@@ -316,6 +343,24 @@ internal class CodeAnalysisDiagnosticsResources {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;{0}&apos; has the default value of &apos;null&apos; for property &apos;{1}&apos;. Either override this property on &apos;{0}&apos; to return a non-null and unique value across all code actions per-fixer or use such an existing code action..
/// </summary>
internal static string OverrideCodeActionEquivalenceKeyMessage {
get {
return ResourceManager.GetString("OverrideCodeActionEquivalenceKeyMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use code actions that have a unique EquivalenceKey for FixAll occurrences support..
/// </summary>
internal static string OverrideCodeActionEquivalenceKeyTitle {
get {
return ResourceManager.GetString("OverrideCodeActionEquivalenceKeyTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SymbolKind &apos;{0}&apos; is not supported for symbol analyzer actions..
/// </summary>
......
......@@ -219,6 +219,21 @@
<value>Type {0} cannot implement interface {1} because {1} is not available for public implementation.</value>
</data>
<data name="InternalImplementationOnlyTitle" xml:space="preserve">
<value>"Only internal implementations of this interface are allowed."</value>
<value>Only internal implementations of this interface are allowed.</value>
</data>
<data name="CodeActionNeedsEquivalenceKeyDescription" xml:space="preserve">
<value>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.</value>
</data>
<data name="CreateCodeActionWithEquivalenceKeyMessage" xml:space="preserve">
<value>Provide an explicit argument for optional parameter '{0}', which is non-null and unique across all code actions created by this fixer.</value>
</data>
<data name="CreateCodeActionWithEquivalenceKeyTitle" xml:space="preserve">
<value>Create code actions should have a unique EquivalenceKey for FixAll occurrences support.</value>
</data>
<data name="OverrideCodeActionEquivalenceKeyMessage" xml:space="preserve">
<value>'{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.</value>
</data>
<data name="OverrideCodeActionEquivalenceKeyTitle" xml:space="preserve">
<value>Use code actions that have a unique EquivalenceKey for FixAll occurrences support.</value>
</data>
</root>
\ No newline at end of file
......@@ -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";
}
......
// 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
{
/// <summary>
/// A <see cref="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 <see cref="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.
/// This analyzer catches violations of this requirement in the code actions registered by a <see cref="CodeFixProvider"/> that supports <see cref="FixAllProvider"/>.
/// </summary>
public abstract class FixerWithFixAllAnalyzer<TLanguageKindEnum> : 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<DiagnosticDescriptor> 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<IMethodSymbol>().SingleOrDefault();
if (getFixAllProviderMethod == null)
{
return;
}
var codeActionSymbol = context.Compilation.GetTypeByMetadataName(CodeActionMetadataName);
if (codeActionSymbol == null)
{
return;
}
var createSymbols = codeActionSymbol.GetMembers(CreateMethodName).OfType<IMethodSymbol>();
if (createSymbols == null)
{
return;
}
var equivalenceKeyProperty = codeActionSymbol.GetMembers(EquivalenceKeyPropertyName).OfType<IPropertySymbol>().SingleOrDefault();
if (equivalenceKeyProperty == null)
{
return;
}
var compilationAnalyzer = GetCompilationAnalyzer(codeFixProviderSymbol, getFixAllProviderMethod,
codeActionSymbol, ImmutableHashSet.CreateRange(createSymbols), equivalenceKeyProperty);
context.RegisterSymbolAction(compilationAnalyzer.AnalyzeNamedTypeSymbol, SymbolKind.NamedType);
context.RegisterCodeBlockStartAction<TLanguageKindEnum>(compilationAnalyzer.CodeBlockStart);
context.RegisterCompilationEndAction(compilationAnalyzer.CompilationEnd);
}
protected abstract CompilationAnalyzer GetCompilationAnalyzer(
INamedTypeSymbol codeFixProviderSymbol,
IMethodSymbol getFixAllProvider,
INamedTypeSymbol codeActionSymbol,
ImmutableHashSet<IMethodSymbol> createMethods,
IPropertySymbol equivalenceKeyProperty);
protected abstract class CompilationAnalyzer
{
private readonly INamedTypeSymbol _codeFixProviderSymbol;
private readonly IMethodSymbol _getFixAllProvider;
private readonly INamedTypeSymbol _codeActionSymbol;
private readonly ImmutableHashSet<IMethodSymbol> _createMethods;
private readonly IPropertySymbol _equivalenceKeyProperty;
/// <summary>
/// Set of all non-abstract sub-types of <see cref="CodeFixProvider"/> in this compilation.
/// </summary>
private HashSet<INamedTypeSymbol> _codeFixProviders;
/// <summary>
/// Set of all non-abstract sub-types of <see cref="CodeAction"/> which override <see cref="CodeAction.EquivalenceKey"/> in this compilation.
/// </summary>
private HashSet<INamedTypeSymbol> _codeActionsWithEquivalenceKey;
/// <summary>
/// Map of invocations from code fix providers to invocation nodes (and symbols) that create a code action using the static "Create" methods on <see cref="CodeAction"/>.
/// </summary>
private Dictionary<INamedTypeSymbol, HashSet<NodeAndSymbol>> _codeActionCreateInvocations;
/// <summary>
/// Map of invocations from code fix providers to object creation nodes (and symbols) that create a code action using sub-types of <see cref="CodeAction"/>.
/// </summary>
private Dictionary<INamedTypeSymbol, HashSet<NodeAndSymbol>> _codeActionObjectCreations;
private struct NodeAndSymbol
{
public SyntaxNode Node { get; set; }
public IMethodSymbol Symbol { get; set; }
}
protected CompilationAnalyzer(
INamedTypeSymbol codeFixProviderSymbol,
IMethodSymbol getFixAllProvider,
INamedTypeSymbol codeActionSymbol,
ImmutableHashSet<IMethodSymbol> 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<INamedTypeSymbol>();
_codeFixProviders.Add(namedType);
}
else if (namedType.DerivesFrom(_codeActionSymbol))
{
var equivalenceKeyProperty = namedType.GetMembers(EquivalenceKeyPropertyName).OfType<IPropertySymbol>().SingleOrDefault();
if (equivalenceKeyProperty != null && equivalenceKeyProperty.IsOverride)
{
_codeActionsWithEquivalenceKey = _codeActionsWithEquivalenceKey ?? new HashSet<INamedTypeSymbol>();
_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<TLanguageKindEnum> 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<INamedTypeSymbol, HashSet<NodeAndSymbol>>();
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<INamedTypeSymbol, HashSet<NodeAndSymbol>>();
AddNodeAndSymbol(namedType, objectCreationContext.Node, constructor, _codeActionObjectCreations);
}
},
GetObjectCreationKind);
}
private static void AddNodeAndSymbol(INamedTypeSymbol namedType, SyntaxNode node, IMethodSymbol symbol, Dictionary<INamedTypeSymbol, HashSet<NodeAndSymbol>> map)
{
HashSet<NodeAndSymbol> value;
if (!map.TryGetValue(namedType, out value))
{
value = new HashSet<NodeAndSymbol>();
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<IMethodSymbol>().SingleOrDefault();
if (getFixAllProviderProperty != null && getFixAllProviderProperty.IsOverride)
{
return true;
}
}
}
return false;
}
private void AnalyzeFixerWithFixAll(INamedTypeSymbol fixer, CompilationAnalysisContext context)
{
if (_codeActionCreateInvocations != null)
{
HashSet<NodeAndSymbol> 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<NodeAndSymbol> 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);
}
}
}
}
......@@ -20,7 +20,7 @@ internal static IEnumerable<AttributeData> GetApplicableAttributes(INamedTypeSym
return attributes;
}
internal static bool DerivesFrom(INamedTypeSymbol symbol, INamedTypeSymbol candidateBaseType)
internal static bool DerivesFrom(this INamedTypeSymbol symbol, INamedTypeSymbol candidateBaseType)
{
while (symbol != null)
{
......
......@@ -91,6 +91,7 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Compile Include="FixAnalyzers\FixerWithFixAllAnalyzerTests.cs" />
<Compile Include="InternalImplementationOnlyTests.cs" />
<Compile Include="MetaAnalyzers\DoNotStorePerCompilationDataOntoFieldsRuleTests.cs" />
<Compile Include="MetaAnalyzers\InvalidReportDiagnosticRuleTests.cs" />
......
......@@ -73,6 +73,7 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Compile Include="FixAnalyzers\BasicFixerWithFixAllAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicDiagnosticDescriptorCreationAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicRegisterActionAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicDiagnosticAnalyzerFieldsAnalyzer.vb" />
......@@ -83,4 +84,4 @@
<Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\build\VSL.Imports.Closed.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
' 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
''' <summary>
''' A <see cref="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 <see cref="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.
''' This analyzer catches violations of this requirement in the code actions registered by a fixer that supports <see cref="FixAllProvider"/>.
''' </summary>
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
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
......@@ -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<int> 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<int> 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<int> M1() { return Enumerable.Empty<int>(); }
}
",
" + _csharpSpecializedCollectionsDefinition,
GetCSharpResultAt(7, 36, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule));
}
......@@ -87,7 +100,7 @@ class C
IEnumerable<int> M1() { return 0 == 1 ? new[] { 1 } : new[] { 2 }; }
IEnumerable<int> 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<int> M1() { return 0 == 1 ? Enumerable.Empty<int>() : null; }
IEnumerable<int> M2() { return null ?? Enumerable.Empty<int>(); }
}
",
" + _csharpSpecializedCollectionsDefinition,
GetCSharpResultAt(7, 45, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule),
GetCSharpResultAt(8, 44, SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule));
}
......@@ -123,7 +136,7 @@ class C
IEnumerable<int> M3() { return new[] { 1, 2 }; }
int[] M4() { return new[] { 1, 2 }; }
}
");
" + _csharpSpecializedCollectionsDefinition);
}
[Fact]
......@@ -138,7 +151,7 @@ class C
IEnumerable<int[]> M2() { return new[] { new[] { 1 } }; }
IEnumerable<int[]> M3() { return new[] { new[] { 1, 2, 3 }, new[] { 1 } }; }
}
",
" + _csharpSpecializedCollectionsDefinition,
GetCSharpResultAt(7, 38, SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule));
}
......@@ -152,7 +165,7 @@ class C
{
IEnumerable<IEnumerable<int>> 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));
}
......
......@@ -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<int>).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)
......
......@@ -14,7 +14,7 @@
<ItemGroup Label="File References">
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.Composition, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath>
......@@ -675,4 +675,4 @@
<Import Project="..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
\ No newline at end of file
</Project>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net451" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net451" />
<package id="Moq" version="4.2.1402.2112" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -21,7 +21,7 @@
<ItemGroup Label="File References">
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.Composition, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath>
......@@ -643,4 +643,4 @@
<Import Project="..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
\ No newline at end of file
</Project>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net451" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net451" />
<package id="Moq" version="4.2.1402.2112" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
......@@ -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
{
......
......@@ -89,7 +89,7 @@
<ItemGroup>
<Reference Include="Microsoft.CodeAnalysis.Test.Resources.Proprietary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc1-20150208-02\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
<HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Test.Resources.Proprietary.1.0.0-rc3-20150528-03\lib\net45\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll</HintPath>
</Reference>
<Reference Include="xunit">
<HintPath>..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc1-20150208-02" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Test.Resources.Proprietary" version="1.0.0-rc3-20150528-03" targetFramework="net45" />
<package id="Microsoft.DiaSymReader" version="1.0.4-rc2" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册