| File: Microsoft.NetCore.Analyzers\Runtime\NormalizeStringsToUppercase.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 System.Linq; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.NetAnalyzers; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA1308: Normalize strings to uppercase /// <para> /// Strings should be normalized to uppercase. A small group of characters, when they are converted to lowercase, cannot make a round trip. /// To make a round trip means to convert the characters from one locale to another locale that represents character data differently, /// and then to accurately retrieve the original characters from the converted characters. /// </para> /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class NormalizeStringsToUppercaseAnalyzer : AbstractGlobalizationDiagnosticAnalyzer { internal const string RuleId = "CA1308"; internal static readonly DiagnosticDescriptor ToUpperRule = DiagnosticDescriptorHelper.Create( RuleId, CreateLocalizableResourceString(nameof(NormalizeStringsToUppercaseTitle)), CreateLocalizableResourceString(nameof(NormalizeStringsToUppercaseMessageToUpper)), DiagnosticCategory.Globalization, RuleLevel.CandidateForRemoval, description: CreateLocalizableResourceString(nameof(NormalizeStringsToUppercaseDescription)), isPortedFxCopRule: true, isDataflowRule: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(ToUpperRule); protected override void InitializeWorker(CompilationStartAnalysisContext context) { var stringType = context.Compilation.GetSpecialType(SpecialType.System_String); var cultureInfo = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemGlobalizationCultureInfo); var invariantCulture = cultureInfo?.GetMembers("InvariantCulture").OfType<IPropertySymbol>().FirstOrDefault(); // We want to flag calls to "ToLowerInvariant" and "ToLower(CultureInfo.InvariantCulture)". var toLowerInvariant = stringType.GetMembers("ToLowerInvariant").OfType<IMethodSymbol>().FirstOrDefault(); var toLowerWithCultureInfo = cultureInfo != null ? stringType.GetMembers("ToLower").OfType<IMethodSymbol>().FirstOrDefault(m => m.Parameters.Length == 1 && Equals(m.Parameters[0].Type, cultureInfo)) : null; if (toLowerInvariant == null && toLowerWithCultureInfo == null) { return; } // We want to recommend calling "ToUpperInvariant" or "ToUpper(CultureInfo.InvariantCulture)". var toUpperInvariant = stringType.GetMembers("ToUpperInvariant").OfType<IMethodSymbol>().FirstOrDefault(); var toUpperWithCultureInfo = cultureInfo != null ? stringType.GetMembers("ToUpper").OfType<IMethodSymbol>().FirstOrDefault(m => m.Parameters.Length == 1 && Equals(m.Parameters[0].Type, cultureInfo)) : null; if (toUpperInvariant == null && toUpperWithCultureInfo == null) { return; } context.RegisterOperationAction(operationAnalysisContext => { var invocation = (IInvocationOperation)operationAnalysisContext.Operation; if (invocation.TargetMethod == null) { return; } var method = invocation.TargetMethod; if (method.Equals(toLowerInvariant) || (method.Equals(toLowerWithCultureInfo) && ((invocation.Arguments.FirstOrDefault()?.Value as IMemberReferenceOperation)?.Member.Equals(invariantCulture) ?? false))) { IMethodSymbol suggestedMethod = toUpperInvariant ?? toUpperWithCultureInfo!; // In method {0}, replace the call to {1} with {2}. var diagnostic = invocation.CreateDiagnostic(ToUpperRule, operationAnalysisContext.ContainingSymbol.Name, method.Name, suggestedMethod.Name); operationAnalysisContext.ReportDiagnostic(diagnostic); } }, OperationKind.Invocation); } } }