提交 f2f1e52a 编写于 作者: M manishv

Add few more Roslyn diagnostic rules for analyzers:

1) Analyzer has an expensive compilation end action: The compilation end actions registered on CompilationStartAnalysisContext can be executed only after all other actions registered on it have been executed on the entire compilation. This can hurt the typing performance when the analyzer is executed in the Visual Studio IDE. If the analysis done within your compilation end action is independent of analyses done in other actions registered on CompilationStartAnalysisContext, then consider registering this end action on AnalysisContext in Initialize method instead of registering it here. This should improve the IDE performance for your analyzer.

2) Recommend adding language support to diagnostic analyzer: If the analyzer supports just one of C#/VB languages, but the analyzer assembly doesn't reference either C# or VB CodeAnalysis assemblies, then the analyzer is pretty likely a language-agnostic analyzer and can support both languages. Consider either removing the argument to DiagnosticAnalyzerAttribute or adding a new DiagnosticAnalyzerAttribute for 'XXX' language support.

3) ReportDiagnostic invoked with an unsupported DiagnosticDescriptor: Only supported descriptors returned from DiagnosticAnalyzer.SupportedDiagnostics should be used for diagnostics reported by ReportDiagnostic. This is a common mistake when adding new rules to an existing analyzer, the diagnostic author might forget to update SupportedDiagnostics. (changeset 1391218)
上级 94711411
......@@ -54,7 +54,9 @@ public override void Initialize(AnalysisContext analysisContext)
if (disposableType != null)
{
AbstractAnalyzer analyzer = GetAnalyzer(context, disposableType);
#pragma warning disable RS1004
context.RegisterCompilationEndAction(analyzer.AnalyzeCompilation);
#pragma warning restore RS1004
context.RegisterSymbolAction(analyzer.AnalyzeSymbol, SymbolKind.Field);
}
});
......
......@@ -71,6 +71,8 @@
<ItemGroup>
<Compile Include="ApiDesign\CancellationTokenMustBeLastCodeFixProvider.cs" />
<Compile Include="Documentation\CSharpDoNotUseVerbatimCrefsAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpReportDiagnosticAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpCompilationEndActionAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CSharpRegisterActionAnalyzer.cs" />
<Compile Include="MetaAnalyzers\Fixers\CSharpApplyDiagnosticAnalyzerAttributeFix.cs" />
<Compile Include="Reliability\CSharpConsumePreserveSigAnalyzer.cs" />
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers.CSharp
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CSharpCompilationEndActionAnalyzer : CompilationEndActionAnalyzer<ClassDeclarationSyntax, InvocationExpressionSyntax>
{
}
}
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers.CSharp
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CSharpReportDiagnosticAnalyzer : ReportDiagnosticAnalyzer<ClassDeclarationSyntax, InvocationExpressionSyntax, IdentifierNameSyntax>
{
protected override ReportDiagnosticCompilationAnalyzer GetAnalyzer(ImmutableHashSet<INamedTypeSymbol> contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
return new CSharpReportDiagnosticCompilationAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute);
}
private sealed class CSharpReportDiagnosticCompilationAnalyzer : ReportDiagnosticCompilationAnalyzer
{
public CSharpReportDiagnosticCompilationAnalyzer(ImmutableHashSet<INamedTypeSymbol> contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
: base(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute)
{
}
protected override IEnumerable<SyntaxNode> GetArgumentExpressions(InvocationExpressionSyntax invocation)
{
if (invocation.ArgumentList != null)
{
return invocation.ArgumentList.Arguments.Select(a => a.Expression);
}
return null;
}
}
}
}
using System;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
{
public abstract class CompilationEndActionAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax> : DiagnosticAnalyzerCorrectnessAnalyzer
where TClassDeclarationSyntax : SyntaxNode
where TInvocationExpressionSyntax : SyntaxNode
{
private static LocalizableString localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExpensiveEndActionTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExpensiveEndActionMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources), nameof(AnalysisContext), nameof(CompilationStartAnalysisContext));
private static LocalizableString localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExpensiveEndActionDescription), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources), nameof(CompilationStartAnalysisContext), nameof(AnalysisContext), nameof(DiagnosticAnalyzer.Initialize));
public static DiagnosticDescriptor ExpensiveCompilationEndActionRule = new DiagnosticDescriptor(
RoslynDiagnosticIds.ExpensiveCompilationEndActionRuleId,
localizableTitle,
localizableMessage,
"AnalyzerPerformance",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.Telemetry);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(ExpensiveCompilationEndActionRule);
}
}
protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
var compilationStartAnalysisContext = compilation.GetTypeByMetadataName(CompilationStartAnalysisContextFullName);
if (compilationStartAnalysisContext == null)
{
return null;
}
return new EndActionCompilationAnalyzer(compilationStartAnalysisContext, diagnosticAnalyzer, diagnosticAnalyzerAttribute);
}
private sealed class EndActionCompilationAnalyzer : InvocationCompilationAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax>
{
private readonly INamedTypeSymbol compilationStartAnalysisContext;
private ImmutableDictionary<INamedTypeSymbol, ActionsInfo> actionsInfoMap;
private struct ActionsInfo
{
public bool HasEndAction;
public bool HasNonEndAction;
public TInvocationExpressionSyntax EndActionInvocation;
}
public EndActionCompilationAnalyzer(INamedTypeSymbol compilationStartAnalysisContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
: base(diagnosticAnalyzer, diagnosticAnalyzerAttribute)
{
this.compilationStartAnalysisContext = compilationStartAnalysisContext;
this.actionsInfoMap = ImmutableDictionary<INamedTypeSymbol, ActionsInfo>.Empty;
}
protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext)
{
base.AnalyzeDiagnosticAnalyzer(symbolContext);
var namedType = (INamedTypeSymbol)symbolContext.Symbol;
ActionsInfo result;
if (actionsInfoMap.TryGetValue(namedType, out result))
{
if (result.HasEndAction && result.HasNonEndAction)
{
var diagnostic = Diagnostic.Create(ExpensiveCompilationEndActionRule, result.EndActionInvocation.GetLocation());
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
protected override void AnalyzeInvocation(SymbolAnalysisContext symbolContext, TInvocationExpressionSyntax invocation, ISymbol symbol, SemanticModel semanticModel)
{
if (!compilationStartAnalysisContext.Equals(symbol.ContainingType) || symbol.Kind != SymbolKind.Method)
{
return;
}
var isRegisterEndAction = symbol.Name.Equals(RegisterCompilationEndActionName, StringComparison.OrdinalIgnoreCase);
var isRegisterNonEndAction = !isRegisterEndAction && symbol.Name.StartsWith("Register", StringComparison.OrdinalIgnoreCase);
if (isRegisterEndAction || isRegisterNonEndAction)
{
var result = ImmutableInterlocked.GetOrAdd(ref actionsInfoMap, (INamedTypeSymbol)symbolContext.Symbol, _ => new ActionsInfo());
result.HasEndAction |= isRegisterEndAction;
result.HasNonEndAction |= isRegisterNonEndAction;
if (isRegisterEndAction)
{
result.EndActionInvocation = invocation;
}
}
}
}
}
}
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
......@@ -6,19 +10,37 @@ namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
[DiagnosticAnalyzer]
public sealed class DiagnosticAnalyzerAttributeAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer
{
private static LocalizableString localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingDiagnosticAnalyzerAttributeTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingAttributeMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources), DiagnosticAnalyzerTypeFullName);
private static LocalizableString localizableTitleMissingAttribute = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingDiagnosticAnalyzerAttributeTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessageMissingAttribute = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingAttributeMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources), DiagnosticAnalyzerTypeFullName);
public static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
public static DiagnosticDescriptor MissingDiagnosticAnalyzerAttributeRule = new DiagnosticDescriptor(
RoslynDiagnosticIds.MissingDiagnosticAnalyzerAttributeRuleId,
localizableTitle,
localizableMessage,
localizableTitleMissingAttribute,
localizableMessageMissingAttribute,
"AnalyzerCorrectness",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.Telemetry);
protected override DiagnosticDescriptor Descriptor { get { return Rule; } }
private static LocalizableString localizableTitleAddLanguageSupportToAnalyzer = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.AddLanguageSupportToAnalyzerTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessageAddLanguageSupportToAnalyzer = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.AddLanguageSupportToAnalyzerMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
public static DiagnosticDescriptor AddLanguageSupportToAnalyzerRule = new DiagnosticDescriptor(
RoslynDiagnosticIds.AddLanguageSupportToAnalyzerRuleId,
localizableTitleAddLanguageSupportToAnalyzer,
localizableMessageAddLanguageSupportToAnalyzer,
"AnalyzerCorrectness",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.Telemetry);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(MissingDiagnosticAnalyzerAttributeRule, AddLanguageSupportToAnalyzerRule);
}
}
protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
......@@ -27,6 +49,9 @@ protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compil
private sealed class AttributeAnalyzer : CompilationAnalyzer
{
private static readonly string csharpCodeAnalysisAssembly = @"Microsoft.CodeAnalysis.CSharp.dll";
private static readonly string basicCodeAnalysisAssembly = @"Microsoft.CodeAnalysis.VisualBasic.dll";
public AttributeAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
: base(diagnosticAnalyzer, diagnosticAnalyzerAttribute)
{
......@@ -40,17 +65,76 @@ protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolCo
return;
}
// 1) MissingDiagnosticAnalyzerAttributeRule: DiagnosticAnalyzer has no DiagnosticAnalyzerAttribute.
// 2) AddLanguageSupportToAnalyzerRule: For analyzer supporting only one of C# or VB languages, detect if it can support the other language.
var hasAttribute = false;
var hasMultipleAttributes = false;
SyntaxNode attributeSyntax = null;
string supportedLanguage = null;
var namedTypeAttributes = AttributeHelpers.GetApplicableAttributes(namedType);
foreach (var attribute in namedTypeAttributes)
{
if (AttributeHelpers.DerivesFrom(attribute.AttributeClass, DiagnosticAnalyzerAttribute))
{
return;
hasMultipleAttributes |= hasAttribute;
hasAttribute = true;
if (!hasMultipleAttributes)
{
foreach (var arg in attribute.ConstructorArguments)
{
if (arg.Kind == TypedConstantKind.Primitive &&
arg.Type != null &&
arg.Type.SpecialType == SpecialType.System_String)
{
supportedLanguage = (string)arg.Value;
attributeSyntax = attribute.ApplicationSyntaxReference.GetSyntax(symbolContext.CancellationToken);
}
}
}
}
}
var diagnostic = Diagnostic.Create(Rule, namedType.Locations[0]);
symbolContext.ReportDiagnostic(diagnostic);
if (!hasAttribute)
{
var diagnostic = Diagnostic.Create(MissingDiagnosticAnalyzerAttributeRule, namedType.Locations[0]);
symbolContext.ReportDiagnostic(diagnostic);
}
else if (!hasMultipleAttributes && supportedLanguage != null)
{
Debug.Assert(attributeSyntax != null);
var supportsCSharp = supportedLanguage == LanguageNames.CSharp;
var supportsVB = supportedLanguage == LanguageNames.VisualBasic;
if (supportsCSharp || supportsVB)
{
// If the analyzer assembly doesn't reference either C# or VB CodeAnalysis assemblies,
// then the analyzer is pretty likely a language-agnostic analyzer.
var assemblyReferenceToCheck = supportsCSharp ? csharpCodeAnalysisAssembly : basicCodeAnalysisAssembly;
var referenceFound = false;
foreach (var reference in symbolContext.Compilation.References)
{
if (reference.Display != null)
{
var fileName = Path.GetFileName(reference.Display);
if (fileName.Equals(assemblyReferenceToCheck, StringComparison.OrdinalIgnoreCase))
{
referenceFound = true;
break;
}
}
}
if (!referenceFound)
{
var missingLanguage = supportsCSharp ? LanguageNames.VisualBasic : LanguageNames.CSharp;
var diagnostic = Diagnostic.Create(AddLanguageSupportToAnalyzerRule, attributeSyntax.GetLocation(), namedType.Name, missingLanguage);
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
......
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
{
public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer
{
protected abstract class CompilationAnalyzer
{
private readonly INamedTypeSymbol diagnosticAnalyzer;
private readonly INamedTypeSymbol diagnosticAnalyzerAttribute;
public CompilationAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
this.diagnosticAnalyzer = diagnosticAnalyzer;
this.diagnosticAnalyzerAttribute = diagnosticAnalyzerAttribute;
}
protected INamedTypeSymbol DiagnosticAnalyzer { get { return this.diagnosticAnalyzer; } }
protected INamedTypeSymbol DiagnosticAnalyzerAttribute { get { return this.diagnosticAnalyzerAttribute; } }
protected bool IsDiagnosticAnalyzer(INamedTypeSymbol type)
{
return SymbolEquivalenceComparer.Instance.Equals(type, this.diagnosticAnalyzer);
}
internal void AnalyzeSymbol(SymbolAnalysisContext symbolContext)
{
var namedType = (INamedTypeSymbol)symbolContext.Symbol;
if (namedType.GetBaseTypes().Any(IsDiagnosticAnalyzer))
{
AnalyzeDiagnosticAnalyzer(symbolContext);
}
}
protected abstract void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext);
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
{
public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer
{
protected abstract class InvocationCompilationAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax> : CompilationAnalyzer
where TClassDeclarationSyntax : SyntaxNode
where TInvocationExpressionSyntax : SyntaxNode
{
protected InvocationCompilationAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
: base(diagnosticAnalyzer, diagnosticAnalyzerAttribute)
{
}
internal IEnumerable<TClassDeclarationSyntax> GetClassDeclarationNodes(INamedTypeSymbol namedType, CancellationToken cancellationToken)
{
foreach (var syntax in namedType.DeclaringSyntaxReferences.Select(s => s.GetSyntax(cancellationToken)))
{
if (syntax != null)
{
var classDecl = syntax.FirstAncestorOrSelf<TClassDeclarationSyntax>(ascendOutOfTrivia: false);
if (classDecl != null)
{
yield return classDecl;
}
}
}
}
protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext)
{
var namedType = (INamedTypeSymbol)symbolContext.Symbol;
var classDecls = GetClassDeclarationNodes(namedType, symbolContext.CancellationToken);
foreach (var classDecl in classDecls)
{
var invocations = classDecl.DescendantNodes().OfType<TInvocationExpressionSyntax>();
if (invocations.Any())
{
var semanticModel = symbolContext.Compilation.GetSemanticModel(classDecl.SyntaxTree);
foreach (var invocation in invocations)
{
var symbol = semanticModel.GetSymbolInfo(invocation, symbolContext.CancellationToken).Symbol;
if (symbol != null)
{
AnalyzeInvocation(symbolContext, invocation, symbol, semanticModel);
}
}
}
}
}
protected abstract void AnalyzeInvocation(SymbolAnalysisContext symbolContext, TInvocationExpressionSyntax invocation, ISymbol invocationSymbol, SemanticModel semanticModel);
}
}
}
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
{
public abstract class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer
public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer
{
protected static readonly string DiagnosticAnalyzerTypeFullName = typeof(DiagnosticAnalyzer).FullName;
internal static readonly string DiagnosticAnalyzerTypeFullName = typeof(DiagnosticAnalyzer).FullName;
internal static readonly string DiagnosticAnalyzerAttributeFullName = typeof(DiagnosticAnalyzerAttribute).FullName;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(Descriptor);
}
}
protected abstract DiagnosticDescriptor Descriptor { get; }
internal static readonly string DiagnosticFullName = typeof(Diagnostic).FullName;
internal static readonly string DiagnosticDescriptorFullName = typeof(DiagnosticDescriptor).FullName;
internal static readonly string AnalysisContextFullName = typeof(AnalysisContext).FullName;
internal static readonly string CompilationStartAnalysisContextFullName = typeof(CompilationStartAnalysisContext).FullName;
internal static readonly string CompilationEndAnalysisContextFullName = typeof(CompilationEndAnalysisContext).FullName;
internal static readonly string SemanticModelAnalysisContextFullName = typeof(SemanticModelAnalysisContext).FullName;
internal static readonly string SymbolAnalysisContextFullName = typeof(SymbolAnalysisContext).FullName;
internal static readonly string SyntaxNodeAnalysisContextFullName = typeof(SyntaxNodeAnalysisContext).FullName;
internal static readonly string SyntaxTreeAnalysisContextFullName = typeof(SyntaxTreeAnalysisContext).FullName;
internal static readonly string CodeBlockStartAnalysisContextFullName = typeof(CodeBlockStartAnalysisContext<>).FullName;
internal static readonly string CodeBlockEndAnalysisContextFullName = typeof(CodeBlockEndAnalysisContext).FullName;
internal static readonly string SymbolKindFullName = typeof(SymbolKind).FullName;
internal static readonly string RegisterSyntaxNodeActionName = nameof(AnalysisContext.RegisterSyntaxNodeAction);
internal static readonly string RegisterSymbolActionName = nameof(AnalysisContext.RegisterSymbolAction);
internal static readonly string RegisterCompilationEndActionName = nameof(CompilationStartAnalysisContext.RegisterCompilationEndAction);
internal static readonly string ReportDiagnosticName = nameof(CompilationEndAnalysisContext.ReportDiagnostic);
internal static readonly string SupportedDiagnosticsName = nameof(DiagnosticAnalyzer.SupportedDiagnostics);
public override void Initialize(AnalysisContext context)
{
......@@ -46,36 +52,5 @@ public override void Initialize(AnalysisContext context)
}
protected abstract CompilationAnalyzer GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute);
protected abstract class CompilationAnalyzer
{
private readonly INamedTypeSymbol diagnosticAnalyzer;
private readonly INamedTypeSymbol diagnosticAnalyzerAttribute;
public CompilationAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
this.diagnosticAnalyzer = diagnosticAnalyzer;
this.diagnosticAnalyzerAttribute = diagnosticAnalyzerAttribute;
}
protected INamedTypeSymbol DiagnosticAnalyzer { get { return this.diagnosticAnalyzer; } }
protected INamedTypeSymbol DiagnosticAnalyzerAttribute { get { return this.diagnosticAnalyzerAttribute; } }
protected bool IsDiagnosticAnalyzer(INamedTypeSymbol type)
{
return SymbolEquivalenceComparer.Instance.Equals(type, this.diagnosticAnalyzer);
}
internal void AnalyzeSymbol(SymbolAnalysisContext symbolContext)
{
var namedType = (INamedTypeSymbol)symbolContext.Symbol;
if (namedType.GetBaseTypes().Any(IsDiagnosticAnalyzer))
{
AnalyzeDiagnosticAnalyzer(symbolContext);
}
}
protected abstract void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext);
}
}
}
......@@ -13,11 +13,6 @@ public abstract class RegisterActionAnalyzer<TClassDeclarationSyntax, TInvocatio
where TInvocationExpressionSyntax : SyntaxNode
where TLanguageKindEnum : struct
{
internal static readonly string AnalysisContextFullName = typeof(AnalysisContext).FullName;
internal static readonly string CompilationStartAnalysisContextFullName = typeof(CompilationStartAnalysisContext).FullName;
internal static readonly string CodeBlockStartAnalysisContextFullName = typeof(CodeBlockStartAnalysisContext<>).FullName;
internal static readonly string SymbolKindFullName = typeof(SymbolKind).FullName;
private static LocalizableString localizableTitleMissingKindArgument = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingKindArgumentToRegisterActionTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessageMissingKindArgument = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.MissingKindArgumentToRegisterActionMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
......@@ -42,7 +37,13 @@ public abstract class RegisterActionAnalyzer<TClassDeclarationSyntax, TInvocatio
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.Telemetry);
protected override DiagnosticDescriptor Descriptor { get { return MissingKindArgumentRule; } }
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(MissingKindArgumentRule, UnsupportedSymbolKindArgumentRule);
}
}
protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
......@@ -75,11 +76,8 @@ protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compil
protected abstract RegisterActionCompilationAnalyzer GetAnalyzer(INamedTypeSymbol analysisContext, INamedTypeSymbol compilationStartAnalysisContext, INamedTypeSymbol codeBlockStartAnalysisContext, INamedTypeSymbol symbolKind, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute);
protected abstract class RegisterActionCompilationAnalyzer : CompilationAnalyzer
protected abstract class RegisterActionCompilationAnalyzer : InvocationCompilationAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax>
{
internal static readonly string RegisterSyntaxNodeActionName = nameof(AnalysisContext.RegisterSyntaxNodeAction);
internal static readonly string RegisterSymbolActionName = nameof(AnalysisContext.RegisterSymbolAction);
private readonly INamedTypeSymbol analysisContext;
private readonly INamedTypeSymbol compilationStartAnalysisContext;
private readonly INamedTypeSymbol codeBlockStartAnalysisContext;
......@@ -111,95 +109,63 @@ protected abstract class RegisterActionCompilationAnalyzer : CompilationAnalyzer
protected abstract IEnumerable<SyntaxNode> GetArgumentExpressions(TInvocationExpressionSyntax invocation);
internal IEnumerable<TClassDeclarationSyntax> GetClassDeclarationNodes(INamedTypeSymbol namedType, CancellationToken cancellationToken)
protected override void AnalyzeInvocation(SymbolAnalysisContext symbolContext, TInvocationExpressionSyntax invocation, ISymbol symbol, SemanticModel semanticModel)
{
foreach (var syntax in namedType.DeclaringSyntaxReferences.Select(s => s.GetSyntax(cancellationToken)))
{
if (syntax != null)
{
var classDecl = syntax.FirstAncestorOrSelf<TClassDeclarationSyntax>(ascendOutOfTrivia: false);
if (classDecl != null)
{
yield return classDecl;
}
}
}
}
var isRegisterSymbolAction = symbol.Name.Equals(RegisterSymbolActionName, StringComparison.OrdinalIgnoreCase) &&
symbol.Kind == SymbolKind.Method &&
(symbol.ContainingType.Equals(analysisContext) ||
symbol.ContainingType.Equals(compilationStartAnalysisContext));
protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext)
{
var namedType = (INamedTypeSymbol)symbolContext.Symbol;
var classDecls = GetClassDeclarationNodes(namedType, symbolContext.CancellationToken);
foreach (var classDecl in classDecls)
var isRegisterSyntaxNodeAction = !isRegisterSymbolAction &&
symbol.Name.Equals(RegisterSyntaxNodeActionName, StringComparison.OrdinalIgnoreCase) &&
symbol.Kind == SymbolKind.Method &&
(symbol.ContainingType.Equals(analysisContext) ||
symbol.ContainingType.Equals(compilationStartAnalysisContext) ||
symbol.ContainingType.Equals(codeBlockStartAnalysisContext));
if (isRegisterSymbolAction || isRegisterSyntaxNodeAction)
{
var invocations = classDecl.DescendantNodes().OfType<TInvocationExpressionSyntax>();
if (invocations.Any())
var method = (IMethodSymbol)symbol;
if (method.Parameters.Length == 2 && method.Parameters[1].IsParams)
{
var semanticModel = symbolContext.Compilation.GetSemanticModel(classDecl.SyntaxTree);
foreach (var invocation in invocations)
var arguments = GetArgumentExpressions(invocation);
if (arguments != null)
{
var symbol = semanticModel.GetSymbolInfo(invocation, symbolContext.CancellationToken).Symbol;
if (symbol != null)
var argumentCount = arguments.Count();
if (argumentCount >= 1)
{
var isRegisterSymbolAction = symbol.Name.Equals(RegisterSymbolActionName) &&
symbol.Kind == SymbolKind.Method &&
(symbol.ContainingType.Equals(analysisContext) ||
symbol.ContainingType.Equals(compilationStartAnalysisContext));
var isRegisterSyntaxNodeAction = !isRegisterSymbolAction &&
symbol.Name.Equals(RegisterSyntaxNodeActionName) &&
symbol.Kind == SymbolKind.Method &&
(symbol.ContainingType.Equals(analysisContext) ||
symbol.ContainingType.Equals(compilationStartAnalysisContext) ||
symbol.ContainingType.Equals(codeBlockStartAnalysisContext));
if (isRegisterSymbolAction || isRegisterSyntaxNodeAction)
var type = semanticModel.GetTypeInfo(arguments.First(), symbolContext.CancellationToken).ConvertedType;
if (type == null || type.Name.Equals(nameof(Action)))
{
var method = (IMethodSymbol)symbol;
if (method.Parameters.Length == 2 && method.Parameters[1].IsParams)
if (argumentCount == 1)
{
string arg1, arg2;
if (isRegisterSymbolAction)
{
arg1 = nameof(SymbolKind);
arg2 = "symbol";
}
else
{
arg1 = "SyntaxKind";
arg2 = "syntax";
}
var diagnostic = Diagnostic.Create(MissingKindArgumentRule, invocation.GetLocation(), arg1, arg2);
symbolContext.ReportDiagnostic(diagnostic);
}
else if (isRegisterSymbolAction)
{
var arguments = GetArgumentExpressions(invocation);
if (arguments != null)
foreach (var argument in arguments.Skip(1))
{
var argumentCount = arguments.Count();
if (argumentCount >= 1)
symbol = semanticModel.GetSymbolInfo(argument, symbolContext.CancellationToken).Symbol;
if (symbol != null &&
symbol.Kind == SymbolKind.Field &&
symbolKind.Equals(symbol.ContainingType) &&
!supportedSymbolKinds.Contains(symbol.Name))
{
var type = semanticModel.GetTypeInfo(arguments.First(), symbolContext.CancellationToken).ConvertedType;
if (type == null || type.Name.Equals(nameof(Action)))
{
if (argumentCount == 1)
{
string arg1, arg2;
if (isRegisterSymbolAction)
{
arg1 = nameof(SymbolKind);
arg2 = "symbol";
}
else
{
arg1 = nameof(TLanguageKindEnum);
arg2 = "syntax";
}
var diagnostic = Diagnostic.Create(MissingKindArgumentRule, invocation.GetLocation(), arg1, arg2);
symbolContext.ReportDiagnostic(diagnostic);
}
else if (isRegisterSymbolAction)
{
foreach (var argument in arguments.Skip(1))
{
symbol = semanticModel.GetSymbolInfo(argument, symbolContext.CancellationToken).Symbol;
if (symbol != null &&
symbol.Kind == SymbolKind.Field &&
symbolKind.Equals(symbol.ContainingType) &&
!supportedSymbolKinds.Contains(symbol.Name))
{
var diagnostic = Diagnostic.Create(UnsupportedSymbolKindArgumentRule, argument.GetLocation(), symbol.Name);
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
}
var diagnostic = Diagnostic.Create(UnsupportedSymbolKindArgumentRule, argument.GetLocation(), symbol.Name);
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
......
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers
{
public abstract class ReportDiagnosticAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax, TIdentifierNameSyntax> : DiagnosticAnalyzerCorrectnessAnalyzer
where TClassDeclarationSyntax : SyntaxNode
where TInvocationExpressionSyntax : SyntaxNode
where TIdentifierNameSyntax : SyntaxNode
{
private static LocalizableString localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.InvalidReportDiagnosticTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
private static LocalizableString localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsResources.InvalidReportDiagnosticMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources));
public static DiagnosticDescriptor InvalidReportDiagnosticRule = new DiagnosticDescriptor(
RoslynDiagnosticIds.InvalidReportDiagnosticRuleId,
localizableTitle,
localizableMessage,
"AnalyzerCorrectness",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.Telemetry);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(InvalidReportDiagnosticRule);
}
}
protected override CompilationAnalyzer GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
{
var compilationEndAnalysisContext = compilation.GetTypeByMetadataName(CompilationEndAnalysisContextFullName);
if (compilationEndAnalysisContext == null)
{
return null;
}
var codeBlockEndAnalysisContext = compilation.GetTypeByMetadataName(CodeBlockEndAnalysisContextFullName);
if (codeBlockEndAnalysisContext == null)
{
return null;
}
var semanticModelAnalysisContext = compilation.GetTypeByMetadataName(SemanticModelAnalysisContextFullName);
if (semanticModelAnalysisContext == null)
{
return null;
}
var symbolAnalysisContext = compilation.GetTypeByMetadataName(SymbolAnalysisContextFullName);
if (symbolAnalysisContext == null)
{
return null;
}
var syntaxNodeAnalysisContext = compilation.GetTypeByMetadataName(SyntaxNodeAnalysisContextFullName);
if (syntaxNodeAnalysisContext == null)
{
return null;
}
var syntaxTreeAnalysisContext = compilation.GetTypeByMetadataName(SyntaxTreeAnalysisContextFullName);
if (syntaxTreeAnalysisContext == null)
{
return null;
}
var diagnosticType = compilation.GetTypeByMetadataName(DiagnosticFullName);
if (diagnosticType == null)
{
return null;
}
var diagnosticDescriptorType = compilation.GetTypeByMetadataName(DiagnosticDescriptorFullName);
if (diagnosticDescriptorType == null)
{
return null;
}
var contextTypes = ImmutableHashSet.Create(compilationEndAnalysisContext, codeBlockEndAnalysisContext,
semanticModelAnalysisContext, symbolAnalysisContext, syntaxNodeAnalysisContext, syntaxTreeAnalysisContext);
return GetAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute);
}
protected abstract ReportDiagnosticCompilationAnalyzer GetAnalyzer(ImmutableHashSet<INamedTypeSymbol> contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute);
protected abstract class ReportDiagnosticCompilationAnalyzer : InvocationCompilationAnalyzer<TClassDeclarationSyntax, TInvocationExpressionSyntax>
{
private readonly ImmutableHashSet<INamedTypeSymbol> contextTypes;
private readonly INamedTypeSymbol diagnosticType;
private readonly INamedTypeSymbol diagnosticDescriptorType;
private ImmutableDictionary<INamedTypeSymbol, ImmutableArray<IFieldSymbol>> supportedDescriptorFieldsMap;
public ReportDiagnosticCompilationAnalyzer(ImmutableHashSet<INamedTypeSymbol> contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute)
: base(diagnosticAnalyzer, diagnosticAnalyzerAttribute)
{
this.contextTypes = contextTypes;
this.diagnosticType = diagnosticType;
this.diagnosticDescriptorType = diagnosticDescriptorType;
this.supportedDescriptorFieldsMap = ImmutableDictionary<INamedTypeSymbol, ImmutableArray<IFieldSymbol>>.Empty;
}
protected abstract IEnumerable<SyntaxNode> GetArgumentExpressions(TInvocationExpressionSyntax invocation);
protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext)
{
var descriptorFields = GetSupportedDescriptors(symbolContext.Compilation, (INamedTypeSymbol)symbolContext.Symbol, symbolContext.CancellationToken);
if (!descriptorFields.IsDefault)
{
base.AnalyzeDiagnosticAnalyzer(symbolContext);
}
}
private ImmutableArray<IFieldSymbol> GetSupportedDescriptors(Compilation compilation, INamedTypeSymbol analyzer, CancellationToken cancellationToken)
{
ImmutableArray<IFieldSymbol> descriptorFields;
if (this.supportedDescriptorFieldsMap.TryGetValue(analyzer, out descriptorFields))
{
return descriptorFields;
}
descriptorFields = default(ImmutableArray<IFieldSymbol>);
var supportedDiagnosticsProperty = analyzer.GetMembers()
.OfType<IPropertySymbol>()
.SingleOrDefault(p => p.OverriddenProperty != null &&
p.OverriddenProperty.Equals(this.DiagnosticAnalyzer.GetMembers(SupportedDiagnosticsName).Single()));
if (supportedDiagnosticsProperty != null && supportedDiagnosticsProperty.GetMethod != null)
{
var syntaxRef = supportedDiagnosticsProperty.GetMethod.DeclaringSyntaxReferences.FirstOrDefault();
if (syntaxRef != null)
{
var syntax = syntaxRef.GetSyntax(cancellationToken);
var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree);
descriptorFields = GetReferencedDescriptorFields(syntax, semanticModel);
}
}
return ImmutableInterlocked.GetOrAdd(ref supportedDescriptorFieldsMap, analyzer, descriptorFields);
}
private ImmutableArray<IFieldSymbol> GetReferencedDescriptorFields(SyntaxNode syntax, SemanticModel semanticModel)
{
var builder = ImmutableArray.CreateBuilder<IFieldSymbol>();
foreach (var identifier in syntax.DescendantNodes().OfType<TIdentifierNameSyntax>())
{
var symbol = semanticModel.GetSymbolInfo(identifier).Symbol;
if (symbol != null && symbol.Kind == SymbolKind.Field)
{
var field = (IFieldSymbol)symbol;
var fieldType = field.Type as INamedTypeSymbol;
if (fieldType != null && fieldType.GetBaseTypesAndThis().Contains(diagnosticDescriptorType))
{
builder.Add((IFieldSymbol)symbol);
}
}
}
return builder.ToImmutable();
}
protected override void AnalyzeInvocation(SymbolAnalysisContext symbolContext, TInvocationExpressionSyntax invocation, ISymbol symbol, SemanticModel semanticModel)
{
if (symbol.Kind != SymbolKind.Method ||
!symbol.Name.Equals(ReportDiagnosticName, StringComparison.OrdinalIgnoreCase) ||
!contextTypes.Contains(symbol.ContainingType))
{
return;
}
var arguments = GetArgumentExpressions(invocation);
if (arguments.Count() == 1)
{
var argument = arguments.First();
var type = semanticModel.GetTypeInfo(argument, symbolContext.CancellationToken).ConvertedType;
if (type != null && type.Equals(diagnosticType))
{
var argSymbol = semanticModel.GetSymbolInfo(argument, symbolContext.CancellationToken).Symbol;
if (argSymbol != null)
{
SyntaxNode diagnosticInitializerOpt = null;
var local = argSymbol as ILocalSymbol;
if (local != null)
{
var syntaxRef = local.DeclaringSyntaxReferences.FirstOrDefault();
if (syntaxRef != null)
{
diagnosticInitializerOpt = syntaxRef.GetSyntax(symbolContext.CancellationToken);
}
}
else
{
var method = argSymbol as IMethodSymbol;
if (method != null &&
method.ContainingType.Equals(diagnosticType) &&
method.Name.Equals(nameof(Diagnostic.Create), StringComparison.OrdinalIgnoreCase))
{
diagnosticInitializerOpt = argument;
}
}
if (diagnosticInitializerOpt != null)
{
var descriptorFields = GetReferencedDescriptorFields(diagnosticInitializerOpt, semanticModel);
if (descriptorFields.Length == 1 &&
!this.supportedDescriptorFieldsMap[(INamedTypeSymbol)symbolContext.Symbol].Contains(descriptorFields[0]))
{
var diagnostic = Diagnostic.Create(InvalidReportDiagnosticRule, invocation.GetLocation(), descriptorFields[0].Name);
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
}
}
}
......@@ -70,6 +70,10 @@
<Compile Include="CodeFixProviderBase.cs" />
<Compile Include="Documentation\DoNotUseVerbatimCrefsAnalyzer.cs" />
<Compile Include="DocumentChangedAction.cs" />
<Compile Include="MetaAnalyzers\ReportDiagnosticAnalyzer.cs" />
<Compile Include="MetaAnalyzers\DiagnosticAnalyzerCorrectnessAnalyzer.CompilationAnalyzer.cs" />
<Compile Include="MetaAnalyzers\DiagnosticAnalyzerCorrectnessAnalyzer.InvocationCompilationAnalyzer.cs" />
<Compile Include="MetaAnalyzers\CompilationEndActionAnalyzer.cs" />
<Compile Include="MetaAnalyzers\RegisterActionAnalyzer.cs" />
<Compile Include="MetaAnalyzers\DiagnosticAnalyzerAttributeAnalyzer.cs" />
<Compile Include="MetaAnalyzers\DiagnosticAnalyzerCorrectnessAnalyzer.cs" />
......
......@@ -26,5 +26,8 @@ internal static class RoslynDiagnosticIds
public const string MissingDiagnosticAnalyzerAttributeRuleId = "RS1001";
public const string MissingKindArgumentToRegisterActionRuleId = "RS1002";
public const string UnsupportedSymbolKindArgumentRuleId = "RS1003";
public const string ExpensiveCompilationEndActionRuleId = "RS1004";
public const string AddLanguageSupportToAnalyzerRuleId = "RS1005";
public const string InvalidReportDiagnosticRuleId = "RS1006";
}
}
\ No newline at end of file
......@@ -60,6 +60,24 @@ internal class RoslynDiagnosticsResources {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;{0}&apos; seems to be a language-agnostic diagnostic analyzer. Consider either removing the argument to DiagnosticAnalyzerAttribute or adding a new DiagnosticAnalyzerAttribute for &apos;{1}&apos; language support..
/// </summary>
internal static string AddLanguageSupportToAnalyzerMessage {
get {
return ResourceManager.GetString("AddLanguageSupportToAnalyzerMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Recommend adding language support to diagnostic analyzer..
/// </summary>
internal static string AddLanguageSupportToAnalyzerTitle {
get {
return ResourceManager.GetString("AddLanguageSupportToAnalyzerTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Apply language-agnostic DiagnosticAnalyzer attribute..
/// </summary>
......@@ -222,6 +240,35 @@ internal class RoslynDiagnosticsResources {
}
}
/// <summary>
/// Looks up a localized string similar to The compilation end actions registered on &apos;{0}&apos; can be executed only after all other actions registered on it have been executed on the entire compilation. This can hurt the typing performance when the analyzer is executed in the Visual Studio IDE.
///If the analysis done within your compilation end action is independent of analyses done in other actions registered on &apos;{0}&apos;, then consider registering this end action on &apos;{1}&apos; in &apos;{2}&apos; method instead of registering it here.
///This should improve the IDE performa [rest of string was truncated]&quot;;.
/// </summary>
internal static string ExpensiveEndActionDescription {
get {
return ResourceManager.GetString("ExpensiveEndActionDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Consider registering the compilation end action on &apos;{0}&apos; instead of &apos;{1}&apos; if the end action is independent of other actions registered on &apos;{1}&apos;..
/// </summary>
internal static string ExpensiveEndActionMessage {
get {
return ResourceManager.GetString("ExpensiveEndActionMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer has an expensive compilation end action..
/// </summary>
internal static string ExpensiveEndActionTitle {
get {
return ResourceManager.GetString("ExpensiveEndActionTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Implement IEquatable&lt;T&gt; when overriding Object.Equals.
/// </summary>
......@@ -240,6 +287,24 @@ internal class RoslynDiagnosticsResources {
}
}
/// <summary>
/// Looks up a localized string similar to ReportDiagnostic invoked with an unsupported DiagnosticDescriptor &apos;{0}&apos;..
/// </summary>
internal static string InvalidReportDiagnosticMessage {
get {
return ResourceManager.GetString("InvalidReportDiagnosticMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ReportDiagnostic invoked with an unsupported DiagnosticDescriptor..
/// </summary>
internal static string InvalidReportDiagnosticTitle {
get {
return ResourceManager.GetString("InvalidReportDiagnosticTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Missing &apos;{0}&apos; attribute..
/// </summary>
......
......@@ -225,6 +225,12 @@
<data name="MissingDiagnosticAnalyzerAttributeTitle" xml:space="preserve">
<value>Missing diagnostic analyzer attribute.</value>
</data>
<data name="AddLanguageSupportToAnalyzerMessage" xml:space="preserve">
<value>'{0}' seems to be a language-agnostic diagnostic analyzer. Consider either removing the argument to DiagnosticAnalyzerAttribute or adding a new DiagnosticAnalyzerAttribute for '{1}' language support.</value>
</data>
<data name="AddLanguageSupportToAnalyzerTitle" xml:space="preserve">
<value>Recommend adding language support to diagnostic analyzer.</value>
</data>
<data name="ApplyDiagnosticAnalyzerAttribute_1" xml:space="preserve">
<value>Apply language-agnostic DiagnosticAnalyzer attribute.</value>
</data>
......@@ -246,4 +252,22 @@
<data name="UnsupportedSymbolKindArgumentToRegisterActionTitle" xml:space="preserve">
<value>Unsupported SymbolKind argument while registering a symbol analyzer action.</value>
</data>
<data name="ExpensiveEndActionMessage" xml:space="preserve">
<value>Consider registering the compilation end action on '{0}' instead of '{1}' if the end action is independent of other actions registered on '{1}'.</value>
</data>
<data name="ExpensiveEndActionTitle" xml:space="preserve">
<value>Analyzer has an expensive compilation end action.</value>
</data>
<data name="ExpensiveEndActionDescription" xml:space="preserve">
<value>The compilation end actions registered on '{0}' can be executed only after all other actions registered on it have been executed on the entire compilation. This can hurt the typing performance when the analyzer is executed in the Visual Studio IDE.
If the analysis done within your compilation end action is independent of analyses done in other actions registered on '{0}', then consider registering this end action on '{1}' in '{2}' method instead of registering it here.
This should improve the IDE performance for your analyzer.</value>
</data>
<data name="InvalidReportDiagnosticMessage" xml:space="preserve">
<value>ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}'.</value>
</data>
<data name="InvalidReportDiagnosticTitle" xml:space="preserve">
<value>ReportDiagnostic invoked with an unsupported DiagnosticDescriptor.</value>
</data>
</root>
\ No newline at end of file
......@@ -94,6 +94,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Documentation\BasicDoNotUseVerbatimCrefsAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicReportDiagnosticAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicCompilationEndActionAnalyzer.vb" />
<Compile Include="MetaAnalyzers\BasicRegisterActionAnalyzer.vb" />
<Compile Include="MetaAnalyzers\Fixers\BasicApplyDiagnosticAnalyzerAttributeFix.vb" />
<Compile Include="Performance\BasicDiagnosticDescriptorAccessAnalyzer.vb" />
......
' Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers.VisualBasic
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Public Class BasicCompilationEndActionAnalyzer
Inherits CompilationEndActionAnalyzer(Of ClassBlockSyntax, InvocationExpressionSyntax)
End Class
End Namespace
' Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Roslyn.Diagnostics.Analyzers.MetaAnalyzers.VisualBasic
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Public Class BasicReportDiagnosticAnalyzer
Inherits ReportDiagnosticAnalyzer(Of ClassBlockSyntax, InvocationExpressionSyntax, IdentifierNameSyntax)
Protected Overrides Function GetAnalyzer(contextTypes As ImmutableHashSet(Of INamedTypeSymbol),
diagnosticType As INamedTypeSymbol,
diagnosticDescriptorType As INamedTypeSymbol,
diagnosticAnalyzer As INamedTypeSymbol,
diagnosticAnalyzerAttribute As INamedTypeSymbol) As ReportDiagnosticCompilationAnalyzer
Return New BasicReportDiagnosticCompilationAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute)
End Function
Private NotInheritable Class BasicReportDiagnosticCompilationAnalyzer
Inherits ReportDiagnosticCompilationAnalyzer
Public Sub New(contextTypes As ImmutableHashSet(Of INamedTypeSymbol),
diagnosticType As INamedTypeSymbol,
diagnosticDescriptorType As INamedTypeSymbol,
diagnosticAnalyzer As INamedTypeSymbol,
diagnosticAnalyzerAttribute As INamedTypeSymbol)
MyBase.New(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute)
End Sub
Protected Overrides Function GetArgumentExpressions(invocation As InvocationExpressionSyntax) As IEnumerable(Of SyntaxNode)
If invocation.ArgumentList IsNot Nothing Then
Return invocation.ArgumentList.Arguments.Select(Function(a) a.GetExpression)
End If
Return Nothing
End Function
End Class
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册