File: Microsoft.CodeQuality.Analyzers\ApiDesignGuidelines\ProvideObsoleteAttributeMessage.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines
{
    using static MicrosoftCodeQualityAnalyzersResources;

    /// <summary>
    /// CA1041: <inheritdoc cref="ProvideObsoleteAttributeMessageTitle"/>
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class ProvideObsoleteAttributeMessageAnalyzer : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA1041";

        internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
            RuleId,
            CreateLocalizableResourceString(nameof(ProvideObsoleteAttributeMessageTitle)),
            CreateLocalizableResourceString(nameof(ProvideObsoleteAttributeMessageMessage)),
            DiagnosticCategory.Design,
            RuleLevel.IdeSuggestion,
            description: CreateLocalizableResourceString(nameof(ProvideObsoleteAttributeMessageDescription)),
            isPortedFxCopRule: true,
            isDataflowRule: false);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(compilationContext =>
            {
                INamedTypeSymbol? obsoleteAttributeType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute);
                if (obsoleteAttributeType == null)
                {
                    return;
                }

                compilationContext.RegisterSymbolAction(sc => AnalyzeSymbol(sc, obsoleteAttributeType),
                    SymbolKind.NamedType,
                    SymbolKind.Method,
                    SymbolKind.Field,
                    SymbolKind.Property,
                    SymbolKind.Event);
            });
        }

        private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol obsoleteAttributeType)
        {
            // FxCop compat: only analyze externally visible symbols by default
            if (!context.Options.MatchesConfiguredVisibility(Rule, context.Symbol, context.Compilation))
            {
                return;
            }

            foreach (AttributeData attribute in context.Symbol.GetAttributes(obsoleteAttributeType))
            {
                // ObsoleteAttribute has a constructor that takes no params and two
                // other constructors that take a message as the first param.
                // If there are no arguments specified or if the message argument is empty
                // then report a diagnostic.
                if (attribute.ApplicationSyntaxReference != null &&
                    (attribute.ConstructorArguments.IsEmpty ||
                    string.IsNullOrEmpty(attribute.ConstructorArguments.First().Value as string)))
                {
                    SyntaxNode node = attribute.ApplicationSyntaxReference.GetSyntax(context.CancellationToken);
                    context.ReportDiagnostic(node.CreateDiagnostic(Rule, context.Symbol.Name));
                }
            }
        }
    }
}