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;
                    }
                }
            }
        }
Example #2
0
        public List <MemberReference> GetRequiredSymbolList(string symbol)
        {
            List <MemberReference> rv;

            if (!RequiredSymbols.TryGetValue(symbol, out rv))
            {
                required_symbols [symbol] = rv = new List <MemberReference> ();
            }
            return(rv);
        }
            public static bool TryGetSymbols(Compilation compilation, [NotNullWhen(true)] out RequiredSymbols?symbols)
            {
                symbols = default;

                var stringType = compilation.GetSpecialType(SpecialType.System_String);
                var boolType   = compilation.GetSpecialType(SpecialType.System_Boolean);

                if (stringType is null || boolType is null)
                {
                    return(false);
                }

                if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemStringComparison, out var stringComparisonType))
                {
                    return(false);
                }

                var compareMethods = stringType.GetMembers(nameof(string.Compare))
                                     .OfType <IMethodSymbol>()
                                     .Where(x => x.IsStatic);
                var compareStringString                 = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType);
                var compareStringStringBool             = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, boolType);
                var compareStringStringStringComparison = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, stringComparisonType);

                var equalsMethods = stringType.GetMembers(nameof(string.Equals))
                                    .OfType <IMethodSymbol>()
                                    .Where(x => x.IsStatic);
                var equalsStringString = equalsMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType);
                var equalsStringStringStringComparison = equalsMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, stringComparisonType);

                // Bail if we do not have at least one complete pair of Compare-Equals methods in the compilation.
                if ((compareStringString is null || equalsStringString is null) &&
                    (compareStringStringBool is null || equalsStringStringStringComparison is null) &&
                    (compareStringStringStringComparison is null || equalsStringStringStringComparison is null))
                {
                    return(false);
                }

                symbols = new RequiredSymbols(
                    stringType, boolType, stringComparisonType,
                    compareStringString, compareStringStringBool, compareStringStringStringComparison,
                    equalsStringString, equalsStringStringStringComparison);
                return(true);
            }
Example #4
0
            public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols symbols)
            {
                var stringType = compilation.GetSpecialType(SpecialType.System_String);
                var charType   = compilation.GetSpecialType(SpecialType.System_Char);

                if (stringType is null || charType is null)
                {
                    symbols = default;
                    return(false);
                }

                var readOnlySpanOfCharType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1)?.Construct(charType);
                var memoryExtensionsType   = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemMemoryExtensions);

                if (readOnlySpanOfCharType is null || memoryExtensionsType is null)
                {
                    symbols = default;
                    return(false);
                }

                var intParamInfo    = ParameterInfo.GetParameterInfo(compilation.GetSpecialType(SpecialType.System_Int32));
                var stringParamInfo = ParameterInfo.GetParameterInfo(stringType);

                var substringMembers     = stringType.GetMembers(nameof(string.Substring)).OfType <IMethodSymbol>();
                var substringStart       = substringMembers.GetFirstOrDefaultMemberWithParameterInfos(intParamInfo);
                var substringStartLength = substringMembers.GetFirstOrDefaultMemberWithParameterInfos(intParamInfo, intParamInfo);

                var asSpanMembers     = memoryExtensionsType.GetMembers(nameof(MemoryExtensions.AsSpan)).OfType <IMethodSymbol>();
                var asSpanStart       = asSpanMembers.GetFirstOrDefaultMemberWithParameterInfos(stringParamInfo, intParamInfo)?.ReduceExtensionMethod(stringType);
                var asSpanStartLength = asSpanMembers.GetFirstOrDefaultMemberWithParameterInfos(stringParamInfo, intParamInfo, intParamInfo)?.ReduceExtensionMethod(stringType);

                if (substringStart is null || substringStartLength is null || asSpanStart is null || asSpanStartLength is null)
                {
                    symbols = default;
                    return(false);
                }

                symbols = new RequiredSymbols(
                    stringType, readOnlySpanOfCharType,
                    substringStart, substringStartLength,
                    asSpanStart, asSpanStartLength);
                return(true);
            }
        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));
                }
            }
        }
Example #7
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));
            }
        }