Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a helper diagnostic for enforcing IDE0005 (remove unnecessary usi… #58835

Merged
merged 11 commits into from
Apr 25, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ private static ImmutableArray<DiagnosticDescriptor> GetDescriptors(LocalizableSt
protected override void InitializeWorker(AnalysisContext context)
{
context.RegisterSemanticModelAction(AnalyzeSemanticModel);
context.RegisterCompilationAction(AnalyzeCompilation);
}

private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
{
var tree = context.SemanticModel.SyntaxTree;
var cancellationToken = context.CancellationToken;
var language = context.SemanticModel.Language;

var unnecessaryImports = UnnecessaryImportsProvider.GetUnnecessaryImports(context.SemanticModel, cancellationToken);
if (unnecessaryImports.Any())
Expand All @@ -90,10 +90,8 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
// for us appropriately.
var mergedImports = MergeImports(unnecessaryImports);

var descriptor = GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, cancellationToken)
? _generatedCodeClassificationIdDescriptor
: _classificationIdDescriptor;
var contiguousSpans = GetContiguousSpans(mergedImports);
var descriptor = GetDescriptor(tree, cancellationToken);
var diagnostics =
CreateClassificationDiagnostics(contiguousSpans, tree, descriptor, cancellationToken).Concat(
CreateFixableDiagnostics(mergedImports, tree, cancellationToken));
Expand All @@ -102,23 +100,35 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
{
context.ReportDiagnostic(diagnostic);
}
}
}

// Due to https://github.com/dotnet/roslyn/issues/41640, enabling this analyzer (IDE0005) on build requires users
// to enable generation of XML documentation comments. We detect if generation of XML documentation comments
// is disabled for this tree and IDE0005 diagnostics are being reported with effective severity "Warning" or "Error".
// If so, we report a special diagnostic that recommends the users to set "GenerateDocumentationFile" to "true"
// in their project file to enable IDE0005 on build.
if (tree.Options.DocumentationMode == DocumentationMode.Parse)
{
var effectiveSeverity = descriptor.GetEffectiveSeverity(context.SemanticModel.Compilation.Options, tree, context.Options);
if (effectiveSeverity is ReportDiagnostic.Warn or ReportDiagnostic.Error)
{
context.ReportDiagnostic(Diagnostic.Create(s_enableGenerateDocumentationFileIdDescriptor, diagnostics.First().Location));
}
}
private void AnalyzeCompilation(CompilationAnalysisContext context)
{
// Due to https://github.com/dotnet/roslyn/issues/41640, enabling this analyzer (IDE0005) on build requires users
// to enable generation of XML documentation comments. We detect if generation of XML documentation comments
// is disabled for this tree and IDE0005 diagnostics are being reported with effective severity "Warning" or "Error".
// If so, we report a special diagnostic that recommends the users to set "GenerateDocumentationFile" to "true"
// in their project file to enable IDE0005 on build.

var compilation = context.Compilation;
var tree = compilation.SyntaxTrees.FirstOrDefault();
if (tree is null || tree.Options.DocumentationMode != DocumentationMode.None)
mavasani marked this conversation as resolved.
Show resolved Hide resolved
return;

var descriptor = GetDescriptor(tree, context.CancellationToken);
var effectiveSeverity = descriptor.GetEffectiveSeverity(compilation.Options, tree, context.Options);
if (effectiveSeverity is ReportDiagnostic.Warn or ReportDiagnostic.Error)
{
context.ReportDiagnostic(Diagnostic.Create(s_enableGenerateDocumentationFileIdDescriptor, Location.None));
}
}

private DiagnosticDescriptor GetDescriptor(SyntaxTree tree, CancellationToken cancellationToken)
=> GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, cancellationToken)
? _generatedCodeClassificationIdDescriptor
: _classificationIdDescriptor;

private IEnumerable<TextSpan> GetContiguousSpans(ImmutableArray<SyntaxNode> nodes)
{
var syntaxFacts = this.SyntaxFacts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,18 @@ public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor de
{
// First check for tree-level analyzer config options.
var analyzerConfigOptions = analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree);
mavasani marked this conversation as resolved.
Show resolved Hide resolved
var severityInEditorConfig = descriptor.GetEffectiveSeverity(analyzerConfigOptions);
var providerAndTree = compilationOptions.SyntaxTreeOptionsProvider != null
? (compilationOptions.SyntaxTreeOptionsProvider, tree)
: default;
var severityInEditorConfig = descriptor.GetEffectiveSeverity(analyzerConfigOptions, providerAndTree);
if (severityInEditorConfig != ReportDiagnostic.Default)
{
effectiveSeverity = severityInEditorConfig;
}
else
{
// If not found, check for global analyzer config options.
var severityInGlobalConfig = descriptor.GetEffectiveSeverity(analyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions);
var severityInGlobalConfig = descriptor.GetEffectiveSeverity(analyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions, providerAndTree);
if (severityInGlobalConfig != ReportDiagnostic.Default)
{
effectiveSeverity = severityInGlobalConfig;
Expand Down Expand Up @@ -133,14 +136,33 @@ public static bool IsDefinedInEditorConfig(this DiagnosticDescriptor descriptor,
return false;
}

public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor descriptor, AnalyzerConfigOptions analyzerConfigOptions)
public static ReportDiagnostic GetEffectiveSeverity(
mavasani marked this conversation as resolved.
Show resolved Hide resolved
this DiagnosticDescriptor descriptor,
AnalyzerConfigOptions analyzerConfigOptions,
(SyntaxTreeOptionsProvider provider, SyntaxTree tree)? providerAndTree)
{
ReportDiagnostic severity;
string? value;

// Check if the option is defined explicitly in the editorconfig
var diagnosticKey = $"{DotnetDiagnosticPrefix}.{descriptor.Id}.{SeveritySuffix}";
if (analyzerConfigOptions.TryGetValue(diagnosticKey, out var value) &&
EditorConfigSeverityStrings.TryParse(value, out var severity))
if (providerAndTree.HasValue)
{
return severity;
var provider = providerAndTree.Value.provider;
var tree = providerAndTree.Value.tree;
if (provider.TryGetDiagnosticValue(tree, descriptor.Id, CancellationToken.None, out severity) ||
provider.TryGetGlobalDiagnosticValue(descriptor.Id, CancellationToken.None, out severity))
{
return severity;
}
}
else
{
var diagnosticKey = $"{DotnetDiagnosticPrefix}.{descriptor.Id}.{SeveritySuffix}";
if (analyzerConfigOptions.TryGetValue(diagnosticKey, out value) &&
EditorConfigSeverityStrings.TryParse(value, out severity))
{
return severity;
}
}

// Check if the option is defined as part of a bulk configuration
Expand Down