private static void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out var symbols)) { return; } context.RegisterOperationAction(AnalyzeOperation, OperationKind.Binary); return; // Local functions void AnalyzeOperation(OperationAnalysisContext context) { var operation = (IBinaryOperation)context.Operation; foreach (var selector in CaseSelectors) { if (selector(operation, symbols)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); return; } } } }
private static void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out RequiredSymbols symbols)) { return; } context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; // Local functions void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { var invocations = PooledConcurrentSet <IInvocationOperation> .GetInstance(); context.RegisterOperationAction(context => { var argument = (IArgumentOperation)context.Operation; if (symbols.IsAnySubstringInvocation(argument.Value.WalkDownConversion(c => c.IsImplicit)) && argument.Parent is IInvocationOperation invocation) { invocations.Add(invocation); } }, OperationKind.Argument); context.RegisterOperationBlockEndAction(context => { foreach (var invocation in invocations) { // We search for an overload of the invoked member whose signature matches the signature of // the invoked member, except with ReadOnlySpan<char> substituted in for some of the // arguments that are Substring invocations. if (!GetBestSpanBasedOverloads(symbols, invocation, context.CancellationToken).IsEmpty) { Diagnostic diagnostic = invocation.CreateDiagnostic(Rule); context.ReportDiagnostic(diagnostic); } } invocations.Free(context.CancellationToken); }); } }
private static void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out var symbols)) { return; } context.RegisterOperationAction(AnalyzeOperation, OperationKind.Conditional); return; void AnalyzeOperation(OperationAnalysisContext context) { var conditional = (IConditionalOperation)context.Operation; if (symbols.IsSimpleAffirmativeCheck(conditional, out _) || symbols.IsNegatedCheckWithThrowingElseClause(conditional, out _)) { context.ReportDiagnostic(conditional.CreateDiagnostic(Rule)); } } }
private void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out RequiredSymbols symbols)) { return; } context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; // Local functions void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { // Maintain set of all top-most concat operations so we don't report sub-expressions of an // already-reported violation. // We also don't report any diagnostic if the concat operation has too many operands for the span-based // Concat overloads to handle. var topMostConcatOperations = PooledConcurrentSet <IBinaryOperation> .GetInstance(); context.RegisterOperationAction(PopulateTopMostConcatOperations, OperationKind.Binary); context.RegisterOperationBlockEndAction(ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls); void PopulateTopMostConcatOperations(OperationAnalysisContext context) { // If the current operation is a string-concatenation operation, walk up to the top-most concat // operation and add it to the set. var binary = (IBinaryOperation)context.Operation; if (!TryGetTopMostConcatOperation(binary, out var topMostConcatOperation)) { return; } topMostConcatOperations.Add(topMostConcatOperation); } void ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls(OperationBlockAnalysisContext context) { // We report diagnostics for all top-most concat operations that contain // direct or conditional substring invocations when there is an applicable span-based overload of // the string.Concat method. // We don't report when the concatenation contains anything other than strings or character literals. foreach (var operation in topMostConcatOperations) { if (ShouldBeReported(operation)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); } } topMostConcatOperations.Free(context.CancellationToken); } } bool ShouldBeReported(IBinaryOperation topMostConcatOperation) { var concatOperands = FlattenBinaryOperation(topMostConcatOperation); // Bail if no suitable overload of 'string.Concat' exists. if (!symbols.TryGetRoscharConcatMethodWithArity(concatOperands.Length, out _)) { return(false); } bool anySubstringInvocations = false; foreach (var operand in concatOperands) { var value = WalkDownBuiltInImplicitConversionOnConcatOperand(operand); switch (value.Type?.SpecialType) { // Report diagnostics only when operands are exclusively strings and character literals. case SpecialType.System_String: case SpecialType.System_Char when value is ILiteralOperation: if (IsAnyDirectOrConditionalSubstringInvocation(value)) { anySubstringInvocations = true; } break; default: return(false); } } return(anySubstringInvocations); } bool IsAnyDirectOrConditionalSubstringInvocation(IOperation operation) { if (operation is IConditionalAccessOperation conditionallAccessOperation) { operation = conditionallAccessOperation.WhenNotNull; } return(operation is IInvocationOperation invocation && symbols.IsAnySubstringMethod(invocation.TargetMethod)); } }