提交 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" />
......
// 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<string> 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<string> 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<string> 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<string> 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)
}
};
}
}
}
......@@ -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.
先完成此消息的编辑!
想要评论请 注册