| File: src\roslyn\src\Analyzers\Core\Analyzers\ConvertTypeofToNameof\AbstractConvertTypeOfToNameOfDiagnosticAnalyzer.cs | Web Access |
| Project: src\roslyn\src\CodeStyle\Core\Analyzers\Microsoft.CodeAnalysis.CodeStyle.csproj (Microsoft.CodeAnalysis.CodeStyle) |
// 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 Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.CodeAnalysis.ConvertTypeOfToNameOf; internal abstract class AbstractConvertTypeOfToNameOfDiagnosticAnalyzer(LocalizableString title) : AbstractBuiltInCodeStyleDiagnosticAnalyzer( IDEDiagnosticIds.ConvertTypeOfToNameOfDiagnosticId, EnforceOnBuildValues.ConvertTypeOfToNameOf, option: null, title: title) { public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; protected abstract bool IsValidTypeofAction(OperationAnalysisContext context); protected override void InitializeWorker(AnalysisContext context) => context.RegisterOperationAction(AnalyzeAction, OperationKind.TypeOf); protected void AnalyzeAction(OperationAnalysisContext context) { if (ShouldSkipAnalysis(context, notification: null)) return; if (!IsValidTypeofAction(context) || !IsValidOperation(context.Operation)) return; var node = context.Operation.Syntax; var parent = node.Parent; // If the parent node is null then it cannot be a member access, so do not report a diagnostic if (parent is null) return; var location = parent.GetLocation(); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } private static bool IsValidOperation(IOperation operation) { // Cast to a typeof operation & check parent is a property reference and member access var typeofOperation = (ITypeOfOperation)operation; if (operation.Parent is not IPropertyReferenceOperation) return false; // Check Parent is a .Name access var operationParent = (IPropertyReferenceOperation)operation.Parent; var parentProperty = operationParent.Property.Name; if (parentProperty is not nameof(System.Type.Name)) return false; // If it's a generic type, do not offer the fix because nameof(T) and typeof(T).Name are not // semantically equivalent, the resulting string is formatted differently, where typeof(T).Name // return "T`1" and nameof just returns "T" if (typeofOperation.TypeOperand is IErrorTypeSymbol) return false; if (typeofOperation.TypeOperand is not INamedTypeSymbol namedType) return false; // Note: generic names like `typeof(List<int>)` are not convertible (since the name will be List`1). However, // it's fine if an outer part of the name is generic (like `typeof(List<int>.Enumerator)`). as that will be // convertible to `nameof(List<>.Enumerator)`. So we only need to check the type arguments directly on the type // here, not on any containing types. return namedType.TypeArguments.Length == 0; } }