diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md index 0a644588b4..59aa9d12a5 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- RS1035 | MicrosoftCodeAnalysisCorrectness | Error | SymbolIsBannedInAnalyzersAnalyzer RS1036 | MicrosoftCodeAnalysisCorrectness | Warning | SymbolIsBannedInAnalyzersAnalyzer +RS1037 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx b/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx index 18a503334d..0fa9feb98d 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx @@ -514,6 +514,15 @@ Define diagnostic description correctly + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs b/src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs index 7a77607ffb..24dd25557a 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs @@ -40,6 +40,7 @@ internal static class DiagnosticIds public const string PreferIsKindRuleId = "RS1034"; public const string SymbolIsBannedInAnalyzersRuleId = "RS1035"; public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036"; + public const string AddCompilationEndCustomTagRuleId = "RS1037"; // Release tracking analyzer IDs public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000"; diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs b/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs index f72ad8f129..39ad993e6c 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs @@ -12,6 +12,7 @@ using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.ReleaseTracking; @@ -23,6 +24,7 @@ namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers using PooledLocalizabeStringsConcurrentDictionary = PooledConcurrentDictionary>; using PooledResourcesDataValueConcurrentDictionary = PooledConcurrentDictionary>; using PooledFieldToResourceNameAndFileNameConcurrentDictionary = PooledConcurrentDictionary; + using PooledFieldToCustomTagsConcurrentDictionary = PooledConcurrentDictionary>; /// /// RS1007 @@ -45,6 +47,7 @@ public sealed partial class DiagnosticDescriptorCreationAnalyzer : DiagnosticAna private const string IsEnabledByDefaultParameterName = "isEnabledByDefault"; private const string DefaultSeverityParameterName = "defaultSeverity"; private const string RuleLevelParameterName = "ruleLevel"; + private const string CompilationEndWellKnownDiagnosticTag = "CompilationEnd" /*WellKnownDiagnosticTags.CompilationEnd*/; internal const string DefineDescriptorArgumentCorrectlyFixValue = nameof(DefineDescriptorArgumentCorrectlyFixValue); private const string DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo = nameof(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo); @@ -156,6 +159,16 @@ public sealed partial class DiagnosticDescriptorCreationAnalyzer : DiagnosticAna isEnabledByDefault: true, customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + public static readonly DiagnosticDescriptor AddCompilationEndCustomTagRule = new( + DiagnosticIds.AddCompilationEndCustomTagRuleId, + CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagTitle)), + CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( UseLocalizableStringsInDescriptorRule, ProvideHelpUriInDescriptorRule, @@ -180,7 +193,8 @@ public sealed partial class DiagnosticDescriptorCreationAnalyzer : DiagnosticAna EnableAnalyzerReleaseTrackingRule, DefineDiagnosticTitleCorrectlyRule, DefineDiagnosticMessageCorrectlyRule, - DefineDiagnosticDescriptionCorrectlyRule); + DefineDiagnosticDescriptionCorrectlyRule, + AddCompilationEndCustomTagRule); public override void Initialize(AnalysisContext context) { @@ -191,7 +205,9 @@ public override void Initialize(AnalysisContext context) { if (!compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticDescriptor, out var diagnosticDescriptorType) || !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString, out var localizableResourceType) || - !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableResourceString, out var localizableResourceStringType)) + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableResourceString, out var localizableResourceStringType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCompilationEndAnalysisContext, out var compilationEndContextType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnostic, out var diagnosticType)) { return; } @@ -228,6 +244,7 @@ public override void Initialize(AnalysisContext context) var idToAnalyzerMap = new ConcurrentDictionary>>(); var seenRuleIds = PooledConcurrentSet.GetInstance(); + var customTagsMap = PooledFieldToCustomTagsConcurrentDictionary.GetInstance(SymbolEqualityComparer.Default); compilationContext.RegisterOperationAction(operationAnalysisContext => { var fieldInitializer = (IFieldInitializerOperation)operationAnalysisContext.Operation; @@ -244,7 +261,7 @@ public override void Initialize(AnalysisContext context) AnalyzeDescription(operationAnalysisContext, creationArguments, containingType, localizableDescriptions, resourcesDataValueMap, localizableResourceType, localizableResourceStringType); AnalyzeHelpLinkUri(operationAnalysisContext, creationArguments, out var helpLink); - AnalyzeCustomTags(operationAnalysisContext, creationArguments); + AnalyzeCustomTags(operationAnalysisContext, creationArguments, fieldInitializer, customTagsMap); var (isEnabledByDefault, defaultSeverity) = GetDefaultSeverityAndEnabledByDefault(operationAnalysisContext.Compilation, creationArguments); if (!TryAnalyzeCategory(operationAnalysisContext, creationArguments, checkCategoryAndAllowedIds, @@ -299,6 +316,132 @@ public override void Initialize(AnalysisContext context) }, SymbolKind.NamedType); } + // Flag descriptor fields that are used to report compilation end diagnostics, + // but do not have the required 'WellKnownDiagnosticTags.CompilationEnd' custom tag. + // See https://github.com/dotnet/roslyn-analyzers/issues/6282 for details. + if (compilationEndContextType.GetMembers(DiagnosticWellKnownNames.ReportDiagnosticName).FirstOrDefault() is IMethodSymbol compilationEndReportDiagnosticMethod) + { + var diagnosticCreateMethods = diagnosticType.GetMembers("Create").OfType() + .Where(m => m.IsPublic() && m.Parameters.Length > 0 && SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, diagnosticDescriptorType)) + .ToImmutableHashSet(SymbolEqualityComparer.Default); + compilationContext.RegisterSymbolStartAction(context => + { + var localsToDescriptorsMap = PooledConcurrentDictionary>.GetInstance(SymbolEqualityComparer.Default); + var localsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + var fieldsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + + context.RegisterOperationAction(context => + { + var invocation = (IInvocationOperation)context.Operation; + if (invocation.Arguments.IsEmpty) + return; + + if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, compilationEndReportDiagnosticMethod) && + invocation.Arguments[0].Value.WalkDownConversion() is ILocalReferenceOperation localReference) + { + // Code pattern such as: + // var diagnostic = Diagnostic.Create(field, ...); + // context.ReportDiagnostic(diagnostic); + localsUsedForCompilationEndReportDiagnostic.Add(localReference.Local); + } + else if (diagnosticCreateMethods.Contains(invocation.TargetMethod)) + { + if (invocation.Arguments[0].Value.WalkDownConversion() is IFieldReferenceOperation fieldReference) + { + // Code pattern such as: + // 'Diagnostic.Create(field, ...)' + if (invocation.GetAncestor(OperationKind.Invocation, + inv => SymbolEqualityComparer.Default.Equals(inv.TargetMethod, compilationEndReportDiagnosticMethod)) is not null) + { + // Code pattern such as: + // 'context.ReportDiagnostic(Diagnostic.Create(field, ...));' + fieldsUsedForCompilationEndReportDiagnostic.Add(fieldReference.Field); + } + else + { + switch (invocation.Parent) + { + case IVariableInitializerOperation variableInitializer: + // Code pattern such as: + // 'var diagnostic = Diagnostic.Create(field, ...);' + if (variableInitializer.GetAncestor(OperationKind.VariableDeclaration) is { } variableDeclaration) + { + foreach (var local in variableDeclaration.GetDeclaredVariables()) + { + AddToLocalsToDescriptorsMap(local, fieldReference.Field, localsToDescriptorsMap); + } + } + + break; + + case ISimpleAssignmentOperation simpleAssignment: + // Code pattern such as: + // 'diagnostic = Diagnostic.Create(field, ...);' + if (simpleAssignment.Target is ILocalReferenceOperation localReferenceTarget) + { + AddToLocalsToDescriptorsMap(localReferenceTarget.Local, fieldReference.Field, localsToDescriptorsMap); + } + + break; + } + } + } + + static void AddToLocalsToDescriptorsMap(ILocalSymbol local, IFieldSymbol field, PooledConcurrentDictionary> localsToDescriptorsMap) + { + localsToDescriptorsMap.AddOrUpdate(local, + addValueFactory: _ => + { + var set = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + set.Add(field); + return set; + }, + updateValueFactory: (_, fields) => + { + fields.Add(field); + return fields; + }); + } + } + }, OperationKind.Invocation); + + context.RegisterSymbolEndAction(context => + { + foreach (var local in localsUsedForCompilationEndReportDiagnostic) + { + if (localsToDescriptorsMap.TryGetValue(local, out var fields)) + { + foreach (var field in fields) + AnalyzeField(field); + } + } + + foreach (var field in fieldsUsedForCompilationEndReportDiagnostic) + { + AnalyzeField(field); + } + + foreach (var value in localsToDescriptorsMap.Values) + value.Free(context.CancellationToken); + localsToDescriptorsMap.Free(context.CancellationToken); + localsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken); + fieldsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken); + + void AnalyzeField(IFieldSymbol field) + { + if (customTagsMap.TryGetValue(field, out var customTags) && + !customTags.IsDefault && + !customTags.Contains(CompilationEndWellKnownDiagnosticTag) && + !field.Locations.IsEmpty && + field.Locations[0].IsInSource) + { + context.ReportDiagnostic(Diagnostic.Create(AddCompilationEndCustomTagRule, field.Locations[0], field.Name)); + } + } + }); + }, SymbolKind.NamedType); + } + compilationContext.RegisterCompilationEndAction(compilationEndContext => { // Report any invalid additional file diagnostics. @@ -357,6 +500,8 @@ public override void Initialize(AnalysisContext context) FreeLocalizableStringsMap(localizableDescriptions, compilationEndContext.CancellationToken); resourcesDataValueMap.Free(compilationEndContext.CancellationToken); } + + customTagsMap.Free(compilationEndContext.CancellationToken); }); }); @@ -901,25 +1046,56 @@ private static void AnalyzeHelpLinkUri( } } - private static void AnalyzeCustomTags(OperationAnalysisContext operationAnalysisContext, ImmutableArray creationArguments) + private static void AnalyzeCustomTags( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + IFieldInitializerOperation fieldInitializerOperation, + PooledFieldToCustomTagsConcurrentDictionary customTagsMap) { - // Find the matching argument for customTags - foreach (var argument in creationArguments) + // Default to indicate unknown set of custom tags. + ImmutableArray customTags = default; + + try { - if (argument.Parameter.Name.Equals(CustomTagsParameterName, StringComparison.OrdinalIgnoreCase)) + // Find the matching argument for customTags + var argument = creationArguments.FirstOrDefault( + a => a.Parameter.Name.Equals(CustomTagsParameterName, StringComparison.OrdinalIgnoreCase)); + if (argument is null || + argument.Value is not IArrayCreationOperation arrayCreation || + arrayCreation.DimensionSizes.Length != 1) { - if (argument.Value is IArrayCreationOperation arrayCreation && - arrayCreation.DimensionSizes.Length == 1 && - arrayCreation.DimensionSizes[0].ConstantValue.HasValue && - arrayCreation.DimensionSizes[0].ConstantValue.Value is int size && - size == 0) - { - Diagnostic diagnostic = argument.CreateDiagnostic(ProvideCustomTagsInDescriptorRule); - operationAnalysisContext.ReportDiagnostic(diagnostic); - } - return; } + + if (arrayCreation.DimensionSizes[0].ConstantValue.HasValue && + arrayCreation.DimensionSizes[0].ConstantValue.Value is int size && + size == 0) + { + Diagnostic diagnostic = argument.CreateDiagnostic(ProvideCustomTagsInDescriptorRule); + operationAnalysisContext.ReportDiagnostic(diagnostic); + + customTags = ImmutableArray.Empty; + } + else if (arrayCreation.Initializer is IArrayInitializerOperation arrayInitializer && + arrayInitializer.ElementValues.All(element => element.ConstantValue.HasValue && element.ConstantValue.Value is string)) + { + customTags = arrayInitializer.ElementValues.Select(element => (string)element.ConstantValue.Value).ToImmutableArray(); + } + } + finally + { + AddCustomTags(customTags, fieldInitializerOperation, customTagsMap); + } + + static void AddCustomTags( + ImmutableArray customTags, + IFieldInitializerOperation fieldInitializerOperation, + PooledFieldToCustomTagsConcurrentDictionary customTagsMap) + { + foreach (var field in fieldInitializerOperation.InitializedFields) + { + customTagsMap[field] = customTags; + } } } diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf index 7c7bb71967..10b86eb770 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Přidat položku pravidla k nevydanému souboru verze diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf index f248a7001c..bf8c8e0778 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Regeleintrag der nicht veröffentlichten Releasedatei hinzufügen diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf index 00bb024171..4861c50254 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Agregar una entrada de regla a un archivo de versión no incluido diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf index ce38e4b292..f5992632ec 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Ajouter une entrée de règle au fichier de version non fourni diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf index f6ba0a4364..c770de1cf3 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Aggiungere la voce della regola per il file di versione non distribuito diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf index a858898b70..913709b59c 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file 未出荷のリリース ファイルへのルール エントリの追加 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf index 6be7183fd5..3c3a713be1 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file 제공되지 않은 릴리스 파일에 규칙 항목 추가 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf index 75e2d1f329..25e8aea1fb 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Dodaj wpis reguły do niedostarczonego pliku wydania diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf index 033aec8345..94f44805e5 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Adicionar uma entrada de regra ao arquivo de versão não enviado diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf index e21575c4b9..4784597e2e 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Добавить запись правила в неотправленный файл выпуска diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf index 25479c6c48..ae024b3f56 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file Gönderilmeyen yayın dosyasına kural girişi ekle diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf index 8f3cf4bc26..b83c25d56a 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file 将规则项添加到未提供的版本文件 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf index d6cccc2f25..c13618bbbd 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf @@ -2,6 +2,21 @@ + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + Add rule entry to unshipped release file 將規則項目新增至未送出的版本檔案 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md b/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md index 359a0932b3..ab6a5fe858 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md @@ -448,6 +448,18 @@ A project containing analyzers or source generators should specify the property |CodeFix|False| --- +## RS1037: Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + +'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + ## [RS2000](https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Add analyzer diagnostic IDs to analyzer release All supported analyzer diagnostic IDs should be part of an analyzer release. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif b/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif index ad6219bd1f..184a1aeab4 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif +++ b/src/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif @@ -442,6 +442,24 @@ ] } }, + "RS1037": { + "id": "RS1037", + "shortDescription": "Add \"CompilationEnd\" custom tag to compilation end diagnostic descriptor", + "fullDescription": "'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag \"CompilationEnd\". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, "RS2000": { "id": "RS2000", "shortDescription": "Add analyzer diagnostic IDs to analyzer release", diff --git a/src/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md b/src/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md index 2bc9c82232..8c4c41815e 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md @@ -37,3 +37,4 @@ RS1033 | | Define diagnostic description correctly | RS1034 | | Prefer 'IsKind' for checking syntax kinds | RS1035 | | Do not use APIs banned for analyzers | RS1036 | | Specify analyzer banned API enforcement setting | +RS1037 | | Add "CompilationEnd" custom tag to compilation end diagnostic descriptor | diff --git a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs index 1ad7656a24..19f7355749 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs @@ -3855,6 +3855,382 @@ await VerifyBasicCodeFixAsync( #endregion // RS1033 (DefineDiagnosticDescriptionCorrectlyRule) + #region RS1037 (AddCompilationEndCustomTagRule) + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_WithRequiredCustomTag_NoDiagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: ""CompilationEnd"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + }); + }); + } +}", + GetRS1007ExpectedDiagnostic(0)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""CompilationEnd"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + End Function) + End Function) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_NonInlinedCustomTags_NoDiagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: tags)|}; + private static readonly string[] tags = new string[] { """" }; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + }); + }); + } +}", + GetRS1007ExpectedDiagnostic(0)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", Tags)|} + Private Shared ReadOnly Tags As String() = { """" } + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + End Function) + End Function) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_WithoutRequiredCustomTag_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + + var diag2 = Diagnostic.Create(descriptor2, Location.None); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + + Dim diag2 = Diagnostic.Create(descriptor2, Location.None) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_DiagnosticStoredInLocal_WithInitializer_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + var diag1 = Diagnostic.Create(descriptor1, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag1); + + var diag2 = Diagnostic.Create(descriptor2, Location.None); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + Dim diag1 = Diagnostic.Create(descriptor1, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag1) + + Dim diag2 = Diagnostic.Create(descriptor2, Location.None) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_DiagnosticStoredInLocal_WithAssignment_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor {|#2:descriptor2|} = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + var diag = Diagnostic.Create(descriptor1, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag); + + diag = Diagnostic.Create(descriptor2, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1037ExpectedDiagnostic(2, "descriptor2"), + GetRS1007ExpectedDiagnostic(3)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly {|#2:descriptor2|} As DiagnosticDescriptor = {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + Dim diag = Diagnostic.Create(descriptor1, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag) + + diag = Diagnostic.Create(descriptor2, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1037ExpectedDiagnostic(2, "descriptor2"), + GetRS1007ExpectedDiagnostic(3)); + } + + #endregion // RS1037 (AddCompilationEndCustomTagRule) + [Fact, WorkItem(6035, "https://github.com/dotnet/roslyn-analyzers/issues/6035")] public async Task VerifyFieldReferenceForFieldDefinedInSeparateFile() { @@ -3977,6 +4353,14 @@ private static DiagnosticResult GetRS1029ResultAt(int markupKey, string ruleId) .WithLocation(markupKey) .WithArguments(ruleId); + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1037ExpectedDiagnostic(int markupKey, string fieldName) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.AddCompilationEndCustomTagRule) + .WithLocation(markupKey) + .WithArguments(fieldName); + private static async Task VerifyCSharpAnalyzerAsync(string source, params DiagnosticResult[] expected) { var test = new VerifyCS.Test