| File: CoalesceAnalyzer.cs | Web Access |
| Project: src\src\Analyzers\Microsoft.Analyzers.Extra\Microsoft.Analyzers.Extra.csproj (Microsoft.Analyzers.Extra) |
// 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 Microsoft.CodeAnalysis.Operations; using Microsoft.Extensions.ExtraAnalyzers.Utilities; namespace Microsoft.Extensions.ExtraAnalyzers; /// <summary> /// C# analyzer that recommends removing superfluous uses of ?? and ??=. /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class CoalesceAnalyzer : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagDescriptors.CoalesceAssignment, DiagDescriptors.Coalesce); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(compilationStartContext => { // only report diagnostics on .NET 6, since previous runtimes don't have good enough nullability annotations if (compilationStartContext.Compilation.IsNet6OrGreater()) { compilationStartContext.RegisterOperationAction(operationAnalysisContext => { var op = (ICoalesceAssignmentOperation)operationAnalysisContext.Operation; var type = op.Target.Type; if (type != null && type.NullableAnnotation == NullableAnnotation.NotAnnotated && type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) { if (op.Target.Kind == OperationKind.ParameterReference) { var pr = (IParameterReferenceOperation)op.Target; var method = pr.Parameter.ContainingSymbol as IMethodSymbol; if (pr.Parameter.ContainingSymbol.IsExternallyVisible() || (method != null && method.ImplementsPublicInterface())) { // this is a ??= applied to a parameter of a public method, let it slide... return; } } var diagnostic = Diagnostic.Create(DiagDescriptors.CoalesceAssignment, op.Syntax.GetLocation()); operationAnalysisContext.ReportDiagnostic(diagnostic); } }, OperationKind.CoalesceAssignment); compilationStartContext.RegisterOperationAction(operationAnalysisContext => { var op = (ICoalesceOperation)operationAnalysisContext.Operation; var type = op.Value.Type; if (type != null && type.NullableAnnotation == NullableAnnotation.NotAnnotated && type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) { if (op.Value.Kind == OperationKind.ParameterReference) { var pr = (IParameterReferenceOperation)op.Value; var method = pr.Parameter.ContainingSymbol as IMethodSymbol; if (pr.Parameter.ContainingSymbol.IsExternallyVisible() || (method != null && method.ImplementsPublicInterface())) { // this is a ?? applied to a parameter of a public method, let it slide... return; } } var diagnostic = Diagnostic.Create(DiagDescriptors.Coalesce, op.Syntax.GetLocation()); operationAnalysisContext.ReportDiagnostic(diagnostic); } }, OperationKind.Coalesce); } }); } }