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));
                }
            }
        }
Exemple #4
0
        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));
            }
        }