Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Analyzers;

namespace Microsoft.CodeAnalysis.CSharp.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CSharpSymbolIsBannedInAnalyzersAnalyzer : SymbolIsBannedInAnalyzersAnalyzer<SyntaxKind>
{
protected override SyntaxKind XmlCrefSyntaxKind => SyntaxKind.XmlCrefAttribute;

protected override SymbolDisplayFormat SymbolDisplayFormat => SymbolDisplayFormat.CSharpShortErrorMessageFormat;

protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
T:System.IO.File; Do not do file IO in analyzers
T:System.IO.Directory; Do not do file IO in analyzers
M:System.IO.Path.GetTempPath; Do not do file IO in analyzers
T:System.Environment; Analyzers should not read their settings directly from environment variables
M:System.Reflection.Assembly.Load(System.Byte[]); Analyzers should only load their dependencies via standard runtime mechanisms
M:System.Reflection.Assembly.Load(System.String); Analyzers should only load their dependencies via standard runtime mechanisms
M:System.Reflection.Assembly.Load(System.Reflection.AssemblyName); Analyzers should only load their dependencies via standard runtime mechanisms
M:System.Reflection.Assembly.Load(System.Byte[],System.Byte[]); Analyzers should only load their dependencies via standard runtime mechanisms
P:System.Globalization.CultureInfo.CurrentCulture; Analyzers should use the locale given by the compiler command line arguments, not the CurrentCulture
P:System.Globalization.CultureInfo.CurrentUICulture; Analyzers should use the locale given by the compiler command line arguments, not the CurrentUICulture
Comment on lines +1 to +10

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RikkiGibson Should all "analyzers" in these messages be updated to "analyzers or generators"

Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
; Please do not edit this file manually, it should only be updated through code fix application.

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RS1035 | MicrosoftCodeAnalysisCorrectness | Error | SymbolIsBannedInAnalyzersAnalyzer
RS1036 | MicrosoftCodeAnalysisCorrectness | Warning | SymbolIsBannedInAnalyzersAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -529,4 +529,22 @@
<data name="PreferIsKindFix" xml:space="preserve">
<value>Use 'IsKind' instead of 'Kind'</value>
</data>
<data name="SymbolIsBannedInAnalyzersDescription" xml:space="preserve">
<value>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</value>
</data>
<data name="SymbolIsBannedInAnalyzersMessage" xml:space="preserve">
<value>The symbol '{0}' is banned for use by analyzers{1}</value>
</data>
<data name="SymbolIsBannedInAnalyzersTitle" xml:space="preserve">
<value>Do not use APIs banned for analyzers</value>
</data>
<data name="NoSettingSpecifiedSymbolIsBannedInAnalyzersDescription" xml:space="preserve">
<value>A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'.</value>
</data>
<data name="NoSettingSpecifiedSymbolIsBannedInAnalyzersMessage" xml:space="preserve">
<value>'{0}': A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'</value>
</data>
<data name="NoSettingSpecifiedSymbolIsBannedInAnalyzersTitle" xml:space="preserve">
<value>Specify analyzer banned API enforcement setting</value>
</data>
</root>
2 changes: 2 additions & 0 deletions src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ internal static class DiagnosticIds
public const string DefineDiagnosticMessageCorrectlyRuleId = "RS1032";
public const string DefineDiagnosticDescriptionCorrectlyRuleId = "RS1033";
public const string PreferIsKindRuleId = "RS1034";
public const string SymbolIsBannedInAnalyzersRuleId = "RS1035";
public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036";

// Release tracking analyzer IDs
public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis.BannedApiAnalyzers;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Analyzers
{
using static CodeAnalysisDiagnosticsResources;

internal static class SymbolIsBannedInAnalyzersAnalyzer
{
public static readonly DiagnosticDescriptor SymbolIsBannedRule = new(
id: DiagnosticIds.SymbolIsBannedInAnalyzersRuleId,
title: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersTitle)),
messageFormat: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersMessage)),
category: DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);

public static readonly DiagnosticDescriptor NoSettingSpecifiedSymbolIsBannedRule = new(
id: DiagnosticIds.NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId,
title: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersTitle)),
messageFormat: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersMessage)),
category: DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
}

public abstract class SymbolIsBannedInAnalyzersAnalyzer<TSyntaxKind> : SymbolIsBannedAnalyzerBase<TSyntaxKind>
where TSyntaxKind : struct
{
public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(SymbolIsBannedInAnalyzersAnalyzer.SymbolIsBannedRule, SymbolIsBannedInAnalyzersAnalyzer.NoSettingSpecifiedSymbolIsBannedRule);

protected sealed override DiagnosticDescriptor SymbolIsBannedRule => SymbolIsBannedInAnalyzersAnalyzer.SymbolIsBannedRule;

#pragma warning disable RS1025, RS1026 // Configure generated code analysis, Enable concurrent execution. Base Initialize handles these.
public sealed override void Initialize(AnalysisContext context)
{
base.Initialize(context);

context.RegisterCompilationStartAction(analyzeAnalyzersAndGeneratorsIfPropertyNotSpecified);
void analyzeAnalyzersAndGeneratorsIfPropertyNotSpecified(CompilationStartAnalysisContext context)
{
var propertyValue = context.Options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.EnforceExtendedAnalyzerRules, context.Compilation);
// Note: in absence of any value for this property in the project, the generated editorconfig will have an entry like:
// `build_property.EnforceExtendedAnalyzerRules = `
if (!string.IsNullOrEmpty(propertyValue))
{
return;
}

var provider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
var diagnosticAnalyzerAttributeType = provider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute);
var generatorAttributeType = provider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisGeneratorAttribute);
context.RegisterSymbolAction(analyzePossibleAnalyzerOrGenerator, SymbolKind.NamedType);

void analyzePossibleAnalyzerOrGenerator(SymbolAnalysisContext symbolAnalysisContext)
{
var symbol = symbolAnalysisContext.Symbol;

var attributes = symbol.GetAttributes();
if (attributes.Any(shouldReportNotSpecifiedEnforceAnalyzerBannedApisSetting))
{
symbolAnalysisContext.ReportDiagnostic(symbol.Locations.CreateDiagnostic(SymbolIsBannedInAnalyzersAnalyzer.NoSettingSpecifiedSymbolIsBannedRule, symbol));
}

bool shouldReportNotSpecifiedEnforceAnalyzerBannedApisSetting(AttributeData attributeData)
{
return attributeData.AttributeClass.Equals(diagnosticAnalyzerAttributeType, SymbolEqualityComparer.Default)
|| attributeData.AttributeClass.Equals(generatorAttributeType, SymbolEqualityComparer.Default);
}
}
}
}

#pragma warning disable RS1012 // 'compilationContext' does not register any analyzer actions. Consider moving actions registered in 'Initialize' that depend on this start action to 'compilationContext'.
protected sealed override Dictionary<ISymbol, BanFileEntry>? ReadBannedApis(CompilationStartAnalysisContext compilationContext)
{
var propertyValue = compilationContext.Options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.EnforceExtendedAnalyzerRules, compilationContext.Compilation);
if (propertyValue != "true")
{
return null;
}

const string fileName = "Microsoft.CodeAnalysis.Analyzers.AnalyzerBannedSymbols.txt";
using var stream = typeof(SymbolIsBannedInAnalyzersAnalyzer<>).Assembly.GetManifestResourceStream(fileName);
var source = SourceText.From(stream);
var result = new Dictionary<ISymbol, BanFileEntry>(SymbolEqualityComparer.Default);
foreach (var line in source.Lines)
{
var text = line.ToString();
if (string.IsNullOrWhiteSpace(text))
{
continue;
}

var entry = new BanFileEntry(text, line.Span, source, fileName);
var symbols = DocumentationCommentId.GetSymbolsForDeclarationId(entry.DeclarationId, compilationContext.Compilation);
if (!symbols.IsDefaultOrEmpty)
{
foreach (var symbol in symbols)
{
result.Add(symbol, entry);
}
}
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<PackageId>*$(MSBuildProjectFile)*</PackageId>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Microsoft.CodeAnalysis.BannedApiAnalyzers\Core\SymbolIsBannedAnalyzerBase.cs" Link="SymbolIsBannedAnalyzerBase.cs" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to move this file into the shared utilities project, similar to the common base type shared across analyzer packages here: https://github.com/dotnet/roslyn-analyzers/blob/main/src/Utilities/Compiler/DoNotCatchGeneralUnlessRethrown.cs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to make this work. I might not have gotten the configuration of the shproj or some #if directives set up properly.

<EmbeddedResource Include="AnalyzerBannedSymbols.txt" />

<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.Analyzers" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.Analyzers" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Analyzers.UnitTests" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,21 @@
<target state="translated">Při registraci analyzátoru syntaxe, symbolů nebo operací musíte zadat minimálně jeden druh syntaxe, symbolu nebo operace. V opačném případě se registrovaná akce nikdy při analýze nevyvolá.</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersDescription">
<source>A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'.</source>
<target state="new">A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'.</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersMessage">
<source>'{0}': A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'</source>
<target state="new">'{0}': A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersTitle">
<source>Specify analyzer banned API enforcement setting</source>
<target state="new">Specify analyzer banned API enforcement setting</target>
<note />
</trans-unit>
<trans-unit id="PreferIsKindDescription">
<source>Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios.</source>
<target state="translated">Při kontrole typů syntaxe upřednostňovat syntax.IsKind(kind) před syntax.Kind() == kind. Kód, který používá IsKind, je za běhu o něco efektivnější, proto konzistentní používání tohoto tvaru tam, kde je to možné, pomáhá zlepšit výkon ve scénářích komplexní analýzy.</target>
Expand Down Expand Up @@ -352,6 +367,21 @@
<target state="translated">Nepřidávat odebraná ID diagnostiky analyzátoru do nevydané verze analyzátoru</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="new">The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersMessage">
<source>The symbol '{0}' is banned for use by analyzers{1}</source>
<target state="new">The symbol '{0}' is banned for use by analyzers{1}</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersTitle">
<source>Do not use APIs banned for analyzers</source>
<target state="new">Do not use APIs banned for analyzers</target>
<note />
</trans-unit>
<trans-unit id="UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdDescription">
<source>Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers.</source>
<target state="translated">ID diagnostiky označená jako odebraná v souboru vydané verze analyzátoru by analyzátory neměly ohlašovat</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,21 @@
<target state="translated">Sie müssen mindestens eine Variante für Syntax, Symbol oder Vorgang angeben, wenn Sie eine Syntax-, Symbol- oder Vorgangsanalyseaktion registrieren. Andernfalls wird die registrierte Aktion während der Analyse nicht aufgerufen.</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersDescription">
<source>A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'.</source>
<target state="new">A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'.</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersMessage">
<source>'{0}': A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'</source>
<target state="new">'{0}': A project containing analyzers or source generators should specify the property '&lt;EnforceExtendedAnalyzerRules&gt;true&lt;/EnforceExtendedAnalyzerRules&gt;'</target>
<note />
</trans-unit>
<trans-unit id="NoSettingSpecifiedSymbolIsBannedInAnalyzersTitle">
<source>Specify analyzer banned API enforcement setting</source>
<target state="new">Specify analyzer banned API enforcement setting</target>
<note />
</trans-unit>
<trans-unit id="PreferIsKindDescription">
<source>Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios.</source>
<target state="translated">Bei der Überprüfung von Syntaxarten ist "syntax.IsKind(kind)" der Syntax "syntax.Kind() == kind" vorzuziehen. Der Code mit "IsKind" ist zur Laufzeit etwas effizienter, daher trägt die konsistente Verwendung dieser Form in komplexen Analyseszenarios zur Verbesserung der Leistung bei.</target>
Expand Down Expand Up @@ -352,6 +367,21 @@
<target state="translated">Fügen Sie einem nicht veröffentlichten Analysetoolrelease keine entfernten Analysetooldiagnose-IDs hinzu.</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="new">The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersMessage">
<source>The symbol '{0}' is banned for use by analyzers{1}</source>
<target state="new">The symbol '{0}' is banned for use by analyzers{1}</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersTitle">
<source>Do not use APIs banned for analyzers</source>
<target state="new">Do not use APIs banned for analyzers</target>
<note />
</trans-unit>
<trans-unit id="UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdDescription">
<source>Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers.</source>
<target state="translated">Diagnose-IDs, die in der Analysetool-Releasedatei als entfernt markiert sind, dürfen von Analysetools nicht gemeldet werden.</target>
Expand Down
Loading