public StringFormatInfo(Compilation compilation) { ImmutableDictionary <IMethodSymbol, Info> .Builder builder = ImmutableDictionary.CreateBuilder <IMethodSymbol, Info>(); INamedTypeSymbol console = WellKnownTypes.Console(compilation); AddStringFormatMap(builder, console, "Write"); AddStringFormatMap(builder, console, "WriteLine"); INamedTypeSymbol @string = WellKnownTypes.String(compilation); AddStringFormatMap(builder, @string, "Format"); _map = builder.ToImmutable(); String = @string; Object = WellKnownTypes.Object(compilation); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol localizableStateAttributeSymbol = WellKnownTypes.LocalizableAttribute(compilationContext.Compilation); INamedTypeSymbol conditionalAttributeSymbol = WellKnownTypes.ConditionalAttribute(compilationContext.Compilation); INamedTypeSymbol systemConsoleSymbol = WellKnownTypes.Console(compilationContext.Compilation); ImmutableHashSet <INamedTypeSymbol> typesToIgnore = GetTypesToIgnore(compilationContext.Compilation); compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext => { if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod)) { return; } var lazyValueContentResult = new Lazy <DataFlowAnalysisResult <ValueContentBlockAnalysisResult, ValueContentAbstractValue> >( valueFactory: ComputeValueContentAnalysisResult, isThreadSafe: false); operationBlockStartContext.RegisterOperationAction(operationContext => { var argument = (IArgumentOperation)operationContext.Operation; IMethodSymbol targetMethod = null; switch (argument.Parent) { case IInvocationOperation invocation: targetMethod = invocation.TargetMethod; break; case IObjectCreationOperation objectCreation: targetMethod = objectCreation.Constructor; break; } if (ShouldAnalyze(targetMethod)) { AnalyzeArgument(argument.Parameter, containingPropertySymbolOpt: null, operation: argument, reportDiagnostic: operationContext.ReportDiagnostic); } }, OperationKind.Argument); operationBlockStartContext.RegisterOperationAction(operationContext => { var propertyReference = (IPropertyReferenceOperation)operationContext.Operation; if (propertyReference.Parent is IAssignmentOperation assignment && assignment.Target == propertyReference && !propertyReference.Property.IsIndexer && propertyReference.Property.SetMethod?.Parameters.Length == 1 && ShouldAnalyze(propertyReference.Property)) { IParameterSymbol valueSetterParam = propertyReference.Property.SetMethod.Parameters[0]; AnalyzeArgument(valueSetterParam, propertyReference.Property, assignment, operationContext.ReportDiagnostic); } }, OperationKind.PropertyReference); return; // Local functions bool ShouldAnalyze(ISymbol symbol) => symbol != null && !symbol.IsConfiguredToSkipAnalysis(operationBlockStartContext.Options, Rule, operationBlockStartContext.Compilation, operationBlockStartContext.CancellationToken); void AnalyzeArgument(IParameterSymbol parameter, IPropertySymbol containingPropertySymbolOpt, IOperation operation, Action <Diagnostic> reportDiagnostic) { if (ShouldBeLocalized(parameter.OriginalDefinition, containingPropertySymbolOpt?.OriginalDefinition, localizableStateAttributeSymbol, conditionalAttributeSymbol, systemConsoleSymbol, typesToIgnore) && lazyValueContentResult.Value != null) { ValueContentAbstractValue stringContentValue = lazyValueContentResult.Value[operation.Kind, operation.Syntax]; if (stringContentValue.IsLiteralState) { Debug.Assert(stringContentValue.LiteralValues.Count > 0); if (stringContentValue.LiteralValues.Any(l => !(l is string))) { return; } var stringLiteralValues = stringContentValue.LiteralValues.Select(l => (string)l); // FxCop compat: Do not fire if the literal value came from a default parameter value if (stringContentValue.LiteralValues.Count == 1 && parameter.IsOptional && parameter.ExplicitDefaultValue is string defaultValue && defaultValue == stringLiteralValues.Single()) { return; } // FxCop compat: Do not fire if none of the string literals have any non-control character. if (!LiteralValuesHaveNonControlCharacters(stringLiteralValues)) { return; } // FxCop compat: Filter out xml string literals. var filteredStrings = stringLiteralValues.Where(literal => !LooksLikeXmlTag(literal)); if (filteredStrings.Any()) { // Method '{0}' passes a literal string as parameter '{1}' of a call to '{2}'. Retrieve the following string(s) from a resource table instead: "{3}". var arg1 = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg2 = parameter.Name; var arg3 = parameter.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg4 = FormatLiteralValues(filteredStrings); var diagnostic = operation.CreateDiagnostic(Rule, arg1, arg2, arg3, arg4); reportDiagnostic(diagnostic); } } } } DataFlowAnalysisResult <ValueContentBlockAnalysisResult, ValueContentAbstractValue> ComputeValueContentAnalysisResult() { var cfg = operationBlockStartContext.OperationBlocks.GetControlFlowGraph(); if (cfg != null) { var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationBlockStartContext.Compilation); return(ValueContentAnalysis.TryGetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, operationBlockStartContext.Options, Rule, operationBlockStartContext.CancellationToken)); } return(null); } }); }); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol localizableStateAttributeSymbol = WellKnownTypes.LocalizableAttribute(compilationContext.Compilation); INamedTypeSymbol conditionalAttributeSymbol = WellKnownTypes.ConditionalAttribute(compilationContext.Compilation); INamedTypeSymbol systemConsoleSymbol = WellKnownTypes.Console(compilationContext.Compilation); ImmutableHashSet <INamedTypeSymbol> typesToIgnore = GetTypesToIgnore(compilationContext.Compilation); compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext => { if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod)) { return; } var lazyStringContentResult = new Lazy <DataFlowAnalysisResult <StringContentBlockAnalysisResult, StringContentAbstractValue> >( valueFactory: ComputeStringContentAnalysisResult, isThreadSafe: false); operationBlockStartContext.RegisterOperationAction(operationContext => { var argument = (IArgumentOperation)operationContext.Operation; switch (argument.Parent?.Kind) { case OperationKind.Invocation: case OperationKind.ObjectCreation: AnalyzeArgument(argument.Parameter, containingPropertySymbolOpt: null, operation: argument, reportDiagnostic: operationContext.ReportDiagnostic); return; } }, OperationKind.Argument); operationBlockStartContext.RegisterOperationAction(operationContext => { var propertyReference = (IPropertyReferenceOperation)operationContext.Operation; if (propertyReference.Parent is IAssignmentOperation assignment && assignment.Target == propertyReference && !propertyReference.Property.IsIndexer && propertyReference.Property.SetMethod?.Parameters.Length == 1) { IParameterSymbol valueSetterParam = propertyReference.Property.SetMethod.Parameters[0]; AnalyzeArgument(valueSetterParam, propertyReference.Property, assignment, operationContext.ReportDiagnostic); } }, OperationKind.PropertyReference); void AnalyzeArgument(IParameterSymbol parameter, IPropertySymbol containingPropertySymbolOpt, IOperation operation, Action <Diagnostic> reportDiagnostic) { if (ShouldBeLocalized(parameter, containingPropertySymbolOpt, localizableStateAttributeSymbol, conditionalAttributeSymbol, systemConsoleSymbol, typesToIgnore)) { StringContentAbstractValue stringContentValue = lazyStringContentResult.Value[operation]; if (stringContentValue.IsLiteralState) { Debug.Assert(stringContentValue.LiteralValues.Count > 0); // FxCop compat: Do not fire if the literal value came from a default parameter value if (stringContentValue.LiteralValues.Count == 1 && parameter.IsOptional && parameter.ExplicitDefaultValue is string defaultValue && defaultValue == stringContentValue.LiteralValues.Single()) { return; } // FxCop compat: Do not fire if none of the string literals have any non-control character. if (!LiteralValuesHaveNonControlCharacters(stringContentValue.LiteralValues)) { return; } // FxCop compat: Filter out xml string literals. var filteredStrings = stringContentValue.LiteralValues.Where(literal => !LooksLikeXmlTag(literal)); if (filteredStrings.Any()) { // Method '{0}' passes a literal string as parameter '{1}' of a call to '{2}'. Retrieve the following string(s) from a resource table instead: "{3}". var arg1 = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg2 = parameter.Name; var arg3 = parameter.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg4 = FormatLiteralValues(filteredStrings); var diagnostic = operation.CreateDiagnostic(Rule, arg1, arg2, arg3, arg4); reportDiagnostic(diagnostic); } } } } DataFlowAnalysisResult <StringContentBlockAnalysisResult, StringContentAbstractValue> ComputeStringContentAnalysisResult() { foreach (var operationRoot in operationBlockStartContext.OperationBlocks) { IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null) { var cfg = ControlFlowGraph.Create(topmostBlock); var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationBlockStartContext.Compilation); var pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider); var copyAnalysisResult = CopyAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, pointsToAnalysisResultOpt: pointsToAnalysisResult); // Do another analysis pass to improve the results from PointsTo and Copy analysis. pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, pointsToAnalysisResult, copyAnalysisResult); return(StringContentAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, copyAnalysisResult, pointsToAnalysisResult)); } } return(null); } }); }); }