File: InstanceInitializerAnalyzer.cs
Web Access
Project: src\tools-local\Microsoft.ML.InternalCodeAnalyzer\Microsoft.ML.InternalCodeAnalyzer.csproj (Microsoft.ML.InternalCodeAnalyzer)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
 
namespace Microsoft.ML.InternalCodeAnalyzer
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class InstanceInitializerAnalyzer : DiagnosticAnalyzer
    {
        private const string Category = "Declaration";
        internal const string DiagnosticId = "MSML_NoInstanceInitializers";
 
        private const string Title = "No initializers on instance fields or properties";
        private const string Format = "Member {0} has a {1} initializer outside the constructor";
 
        private static readonly DiagnosticDescriptor Rule =
            new DiagnosticDescriptor(DiagnosticId, Title, Format, Category,
                DiagnosticSeverity.Warning, isEnabledByDefault: true,
                description: Descriptions.InstanceInitializerInConstructor);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
            ImmutableArray.Create(Rule);
 
        public override void Initialize(AnalysisContext context)
        {
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.EnableConcurrentExecution();
 
            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
        }
 
        private static void AnalyzeField(SymbolAnalysisContext context)
        {
            var symbol = (IFieldSymbol)context.Symbol;
            // Constant or static field initializers are desirable. If implicitly
            // declared, then we can't very well ask the developer to fix.
            if (symbol.IsConst || symbol.IsStatic || symbol.IsImplicitlyDeclared)
                return;
            // Exempt argument attributes from the test. Note that because we cannot
            // depend on the Microsoft.ML source itself, we have to identify this class by name.
            if (symbol.GetAttributes().Any(i => i.AttributeClass.Name == "ArgumentAttribute"))
                return;
 
            var typeInfo = symbol.GetType().GetTypeInfo();
            var hasInitProp = typeInfo.GetDeclaredProperty("HasInitializer");
            if (hasInitProp?.PropertyType != typeof(bool))
                return;
            bool hasInit = (bool)hasInitProp.GetValue(symbol);
            if (!hasInit)
                return;
            var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name, "field");
            context.ReportDiagnostic(diagnostic);
        }
 
        private static void AnalyzeProperty(SymbolAnalysisContext context)
        {
            var symbol = (IPropertySymbol)context.Symbol;
            if (symbol.IsAbstract || symbol.IsImplicitlyDeclared || symbol.IsStatic)
                return;
            var syntaxRefs = symbol.DeclaringSyntaxReferences;
            if (syntaxRefs.IsEmpty)
                return;
            var syntax = syntaxRefs[0].GetSyntax();
            if (!syntax.ChildNodes().Any(s => s.IsKind(SyntaxKind.EqualsValueClause)))
                return;
 
            var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name, "property");
            context.ReportDiagnostic(diagnostic);
        }
    }
}