protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped)
            {
                base.PostProcessArgument(operation, isEscaped);
                if (isEscaped)
                {
                    // Discover if a disposable object is being passed into the creation method for this new disposable object
                    // and if the new disposable object assumes ownership of that passed in disposable object.
                    if (IsDisposeOwnershipTransfer())
                    {
                        var pointsToValue = GetPointsToAbstractValue(operation.Value);
                        HandlePossibleEscapingOperation(operation, pointsToValue.Locations);
                        return;
                    }
                    else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse &&
                             operation.Parameter.RefKind == RefKind.Out &&
                             operation.Parent is IInvocationOperation invocation &&
                             invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean)
                    {
                        // Case 1:
                        //      // Assume 'obj' is not a valid object on the 'else' path.
                        //      if (TryXXX(out IDisposable obj))
                        //      {
                        //          obj.Dispose();
                        //      }
                        //
                        //      return;

                        // Case 2:
                        //      if (!TryXXX(out IDisposable obj))
                        //      {
                        //          return; // Assume 'obj' is not a valid object on this path.
                        //      }
                        //
                        //      obj.Dispose();

                        HandlePossibleInvalidatingOperation(operation);
                        return;
                    }
                }

                // Ref or out argument values from callee might be escaped by assigning to field.
                if (operation.Parameter.RefKind is RefKind.Out or RefKind.Ref)
                {
                    HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation));
                }

                return;

                // Local functions.
                bool IsDisposeOwnershipTransfer()
                {
                    if (operation.Parameter.RefKind == RefKind.Out)
                    {
                        // Out arguments are always owned by the caller.
                        return(false);
                    }

                    return(operation.Parent switch
                    {
                        IObjectCreationOperation _ => DisposeOwnershipTransferAtConstructor ||
                        DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type),

                        IInvocationOperation invocation => DisposeOwnershipTransferAtMethodCall ||
                        IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type),

                        _ => false,
                    });
                }
        protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation)
        {
            if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation,
                                                                  out var actualOperation, out var constraintExpression))
            {
                return;
            }

            if (actualOperation is null)
            {
                return;
            }

            var actualType = AssertHelper.GetUnwrappedActualType(actualOperation);

            if (actualType is null)
            {
                return;
            }

            foreach (var constraintPartExpression in constraintExpression.ConstraintParts)
            {
                if (constraintPartExpression.HasIncompatiblePrefixes() ||
                    HasCustomComparer(constraintPartExpression) ||
                    constraintPartExpression.HasUnknownExpressions())
                {
                    continue;
                }

                var constraintMethod = constraintPartExpression.GetConstraintMethod();
                if (constraintMethod is null)
                {
                    continue;
                }

                if (!SupportedConstraints.Contains(constraintMethod.Name))
                {
                    continue;
                }

                var expectedOperation = constraintPartExpression.GetExpectedArgument();
                if (expectedOperation is null)
                {
                    continue;
                }

                var expectedType = expectedOperation.Type;
                if (expectedType is null)
                {
                    continue;
                }

                if (actualType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
                {
                    actualType = ((INamedTypeSymbol)actualType).TypeArguments[0];
                }

                if (expectedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
                {
                    expectedType = ((INamedTypeSymbol)expectedType).TypeArguments[0];
                }

                if (actualType.SpecialType == SpecialType.System_Object ||
                    expectedType.SpecialType == SpecialType.System_Object)
                {
                    // An instance of object might not implement IComparable resulting in runtime errors.
                    context.ReportDiagnostic(Diagnostic.Create(
                                                 comparableOnObjectDescriptor,
                                                 expectedOperation.Syntax.GetLocation(),
                                                 constraintMethod.Name));
                }
                else if (!CanCompare(actualType, expectedType, context.Compilation))
                {
                    context.ReportDiagnostic(Diagnostic.Create(
                                                 comparableTypesDescriptor,
                                                 expectedOperation.Syntax.GetLocation(),
                                                 constraintMethod.Name));
                }
            }
        }
        protected override void Analyze(OperationAnalysisContext context, IInvocationOperation invocationOperation, IMethodSymbol method)
        {
            if (method.Parameters.Length != 2 ||
                !method.Parameters[0].Type.SpecialType.Equals(SpecialType.System_Int32) ||
                !method.Parameters[1].Type.SpecialType.Equals(SpecialType.System_Int32))
            {
                return;
            }

            var sizeOperation = invocationOperation.Arguments.FirstOrDefault(arg => arg.Parameter.Equals(method.Parameters[0]))?.Value;
            var sizeValue     = sizeOperation?.ConstantValue ?? default;

            if (!sizeValue.HasValue)
            {
                return;
            }

            // Make sure the first parameter really is an int before checking its value. Could for example be a char.
            if (!(sizeValue.Value is int size))
            {
                return;
            }

            if (size < 0 || size > 1 || size == 1 && method.Name != "Equal")
            {
                return;
            }

            var otherArgument = invocationOperation.Arguments.FirstOrDefault(arg => !arg.Parameter.Equals(method.Parameters[0]));

            ISymbol symbol = otherArgument?.Value switch
            {
                IInvocationOperation o => o.TargetMethod,
                IPropertyReferenceOperation p => p.Property,
                    _ => null,
            };

            if (symbol == null)
            {
                return;
            }

            if (IsCollectionsWithExceptionThrowingGetEnumeratorMethod(symbol) ||
                !IsWellKnownSizeMethod(symbol) &&
                !IsICollectionCountProperty(context, symbol) &&
                !IsICollectionOfTCountProperty(context, symbol) &&
                !IsIReadOnlyCollectionOfTCountProperty(context, symbol))
            {
                return;
            }

            var builder = ImmutableDictionary.CreateBuilder <string, string>();

            builder[MethodName] = method.Name;
            builder[SizeValue]  = size.ToString();

            context.ReportDiagnostic(
                Diagnostic.Create(
                    Descriptors.X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck,
                    invocationOperation.Syntax.GetLocation(),
                    builder.ToImmutable(),
                    SymbolDisplay.ToDisplayString(
                        method,
                        SymbolDisplayFormat.CSharpShortErrorMessageFormat.WithParameterOptions(SymbolDisplayParameterOptions.None).WithGenericsOptions(SymbolDisplayGenericsOptions.None))));
        }
 /// <summary>
 /// Checks if the given invocation has an argument that is an empty string.
 /// </summary>
 private static bool HasAnEmptyStringArgument(IInvocationOperation invocation)
 {
     return(invocation.Arguments.Any(arg => IsEmptyString(arg.Value)));
 }
Пример #5
0
 private static bool HasNonImplicitInstance(IInvocationOperation invocation)
 => invocation.Instance != null && !invocation.Instance.IsImplicit;
        private static Task <Document> RemoveRedundantCall(Document document, SyntaxNode root, SyntaxNode invocationNode, IInvocationOperation invocationOperation)
        {
            var instance    = GetInstance(invocationOperation).WithTriviaFrom(invocationNode);
            var newRoot     = root.ReplaceNode(invocationNode, instance);
            var newDocument = document.WithSyntaxRoot(newRoot);

            return(Task.FromResult(newDocument));
        }
Пример #7
0
        public sealed override void Initialize(AnalysisContext context)
        {
            ImmutableHashSet <string> cachedDeserializationMethodNames = this.DeserializationMethodNames;

            Debug.Assert(!String.IsNullOrWhiteSpace(this.DeserializerTypeMetadataName));
            Debug.Assert(!String.IsNullOrWhiteSpace(this.SerializationBinderPropertyMetadataName));
            Debug.Assert(cachedDeserializationMethodNames != null);
            Debug.Assert(!cachedDeserializationMethodNames.IsEmpty);
            Debug.Assert(this.BinderDefinitelyNotSetDescriptor != null);
            Debug.Assert(this.BinderMaybeNotSetDescriptor != null);

            context.EnableConcurrentExecution();

            // Security analyzer - analyze and report diagnostics on generated code.
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            // For PropertySetAnalysis dataflow analysis.
            PropertyMapperCollection propertyMappers = new PropertyMapperCollection(
                new PropertyMapper(
                    this.SerializationBinderPropertyMetadataName,
                    (NullAbstractValue nullAbstractValue) =>
            {
                // A null SerializationBinder is what we want to flag as hazardous.
                switch (nullAbstractValue)
                {
                case NullAbstractValue.Null:
                    return(PropertySetAbstractValueKind.Flagged);

                case NullAbstractValue.NotNull:
                    return(PropertySetAbstractValueKind.Unflagged);

                default:
                    return(PropertySetAbstractValueKind.MaybeFlagged);
                }
            }));

            HazardousUsageEvaluatorCollection hazardousUsageEvaluators =
                new HazardousUsageEvaluatorCollection(
                    cachedDeserializationMethodNames.Select(
                        methodName => new HazardousUsageEvaluator(methodName, DoNotUseInsecureDeserializerWithoutBinderBase.HazardousIfNull)));

            context.RegisterCompilationStartAction(
                (CompilationStartAnalysisContext compilationStartAnalysisContext) =>
            {
                WellKnownTypeProvider wellKnownTypeProvider =
                    WellKnownTypeProvider.GetOrCreate(compilationStartAnalysisContext.Compilation);

                if (!wellKnownTypeProvider.TryGetTypeByMetadataName(
                        this.DeserializerTypeMetadataName,
                        out INamedTypeSymbol deserializerTypeSymbol))
                {
                    return;
                }

                compilationStartAnalysisContext.RegisterOperationBlockStartAction(
                    (OperationBlockStartAnalysisContext operationBlockStartAnalysisContext) =>
                {
                    PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance();

                    operationBlockStartAnalysisContext.RegisterOperationAction(
                        (OperationAnalysisContext operationAnalysisContext) =>
                    {
                        IInvocationOperation invocationOperation =
                            (IInvocationOperation)operationAnalysisContext.Operation;
                        if (invocationOperation.Instance?.Type == deserializerTypeSymbol &&
                            cachedDeserializationMethodNames.Contains(invocationOperation.TargetMethod.Name))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot());
                            }
                        }
                    },
                        OperationKind.Invocation);

                    operationBlockStartAnalysisContext.RegisterOperationAction(
                        (OperationAnalysisContext operationAnalysisContext) =>
                    {
                        IMethodReferenceOperation methodReferenceOperation =
                            (IMethodReferenceOperation)operationAnalysisContext.Operation;
                        if (methodReferenceOperation.Instance?.Type == deserializerTypeSymbol &&
                            cachedDeserializationMethodNames.Contains(
                                methodReferenceOperation.Method.MetadataName))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot());
                            }
                        }
                    },
                        OperationKind.MethodReference);

                    operationBlockStartAnalysisContext.RegisterOperationBlockEndAction(
                        (OperationBlockAnalysisContext operationBlockAnalysisContext) =>
                    {
                        PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> allResults = null;
                        try
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                if (!rootOperationsNeedingAnalysis.Any())
                                {
                                    return;
                                }

                                // Only instantiated if there are any results to report.
                                List <ControlFlowGraph> cfgs = new List <ControlFlowGraph>();

                                var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                                    operationBlockAnalysisContext.Options, SupportedDiagnostics,
                                    defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.None,
                                    cancellationToken: operationBlockAnalysisContext.CancellationToken,
                                    defaultMaxInterproceduralMethodCallChain: 1);             // By default, we only want to track method calls one level down.

                                foreach (IOperation rootOperation in rootOperationsNeedingAnalysis)
                                {
                                    ImmutableDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> dfaResult =
                                        PropertySetAnalysis.GetOrComputeHazardousUsages(
                                            rootOperation.GetEnclosingControlFlowGraph(),
                                            operationBlockAnalysisContext.Compilation,
                                            operationBlockAnalysisContext.OwningSymbol,
                                            this.DeserializerTypeMetadataName,
                                            DoNotUseInsecureDeserializerWithoutBinderBase.ConstructorMapper,
                                            propertyMappers,
                                            hazardousUsageEvaluators,
                                            interproceduralAnalysisConfig);
                                    if (dfaResult.IsEmpty)
                                    {
                                        continue;
                                    }

                                    if (allResults == null)
                                    {
                                        allResults = PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> .GetInstance();
                                    }

                                    foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp
                                             in dfaResult)
                                    {
                                        allResults.Add(kvp.Key, kvp.Value);
                                    }
                                }
                            }

                            if (allResults == null)
                            {
                                return;
                            }

                            foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp
                                     in allResults)
                            {
                                DiagnosticDescriptor descriptor;
                                switch (kvp.Value)
                                {
                                case HazardousUsageEvaluationResult.Flagged:
                                    descriptor = this.BinderDefinitelyNotSetDescriptor;
                                    break;

                                case HazardousUsageEvaluationResult.MaybeFlagged:
                                    descriptor = this.BinderMaybeNotSetDescriptor;
                                    break;

                                default:
                                    Debug.Fail($"Unhandled result value {kvp.Value}");
                                    continue;
                                }

                                operationBlockAnalysisContext.ReportDiagnostic(
                                    Diagnostic.Create(
                                        descriptor,
                                        kvp.Key.Location,
                                        kvp.Key.Method.ToDisplayString(
                                            SymbolDisplayFormat.MinimallyQualifiedFormat)));
                            }
                        }
                        finally
                        {
                            rootOperationsNeedingAnalysis.Free();
                            allResults?.Free();
                        }
                    });
                });
            });
        }
        private static void CombineWhereWithNextMethod(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols)
        {
            if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal))
            {
                // Cannot replace Where when using Func<TSource,int,bool>
                if (operation.TargetMethod.Parameters.Length != 2)
                {
                    return;
                }

                var type = operation.TargetMethod.Parameters[1].Type as INamedTypeSymbol;
                if (type == null)
                {
                    return;
                }

                if (type.TypeArguments.Length == 3)
                {
                    return;
                }

                // Check parent methods
                var parent = GetParentLinqOperation(operation);
                if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType))
                {
                    if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.First), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.FirstOrDefault), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Last), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.LastOrDefault), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Single), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.SingleOrDefault), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Any), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.LongCount), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal))
                    {
                        var properties = CreateProperties(OptimizeLinqUsageData.CombineWhereWithNextMethod)
                                         .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                         .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                         .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                         .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                         .Add("MethodName", parent.TargetMethod.Name);

                        context.ReportDiagnostic(s_combineLinqMethodsRule, properties, parent, operation.TargetMethod.Name, parent.TargetMethod.Name);
                    }
                }
            }
        }
        private static void RemoveTwoConsecutiveOrderBy(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols)
        {
            if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) ||
                string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal) ||
                string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ThenBy), StringComparison.Ordinal) ||
                string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ThenByDescending), StringComparison.Ordinal))
            {
                var parent = GetParentLinqOperation(operation);
                if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType))
                {
                    if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) ||
                        string.Equals(parent.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal))
                    {
                        var expectedMethodName = parent.TargetMethod.Name.Replace("OrderBy", "ThenBy");
                        var properties         = CreateProperties(OptimizeLinqUsageData.DuplicatedOrderBy)
                                                 .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                                 .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                                 .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                                 .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                                 .Add("ExpectedMethodName", expectedMethodName)
                                                 .Add("MethodName", parent.TargetMethod.Name);

                        context.ReportDiagnostic(s_duplicateOrderByMethodsRule, properties, parent, operation.TargetMethod.Name, expectedMethodName);
                    }
                }
            }
        }
        private static void UseFindInsteadOfFirstOrDefault(OperationAnalysisContext context, IInvocationOperation operation)
        {
            if (!string.Equals(operation.TargetMethod.Name, nameof(Enumerable.FirstOrDefault), StringComparison.Ordinal))
            {
                return;
            }

            if (operation.Arguments.Length != 2)
            {
                return;
            }

            var listSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");

            if (GetActualType(operation.Arguments[0]).OriginalDefinition.IsEqualTo(listSymbol))
            {
                var properties = CreateProperties(OptimizeLinqUsageData.UseFindMethod);
                context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Find()", operation.TargetMethod.Name);
            }
        }
        private static void UseIndexerInsteadOfElementAt(OperationAnalysisContext context, IInvocationOperation operation)
        {
            ImmutableDictionary <string, string> properties = default;

            var argCount = -1;

            if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ElementAt), StringComparison.Ordinal))
            {
                properties = CreateProperties(OptimizeLinqUsageData.UseIndexer);
                argCount   = 2;
            }
            else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.First), StringComparison.Ordinal))
            {
                properties = CreateProperties(OptimizeLinqUsageData.UseIndexerFirst);
                argCount   = 1;
            }
            else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Last), StringComparison.Ordinal))
            {
                properties = CreateProperties(OptimizeLinqUsageData.UseIndexerLast);
                argCount   = 1;
            }

            if (argCount < 0)
            {
                return;
            }

            if (operation.Arguments.Length != argCount)
            {
                return;
            }

            var listSymbol         = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IList`1");
            var readOnlyListSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyList`1");

            if (listSymbol == null && readOnlyListSymbol == null)
            {
                return;
            }

            var actualType = GetActualType(operation.Arguments[0]);

            if (actualType.AllInterfaces.Any(i => i.OriginalDefinition.Equals(listSymbol) || i.OriginalDefinition.Equals(readOnlyListSymbol)))
            {
                context.ReportDiagnostic(s_listMethodsRule, properties, operation, "[]", operation.TargetMethod.Name);
            }
        }
        private static void UseCountPropertyInsteadOfMethod(OperationAnalysisContext context, IInvocationOperation operation)
        {
            if (operation.Arguments.Length != 1)
            {
                return;
            }

            if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal))
            {
                var collectionOfTSymbol         = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.ICollection`1");
                var readOnlyCollectionOfTSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyCollection`1");
                if (collectionOfTSymbol == null && readOnlyCollectionOfTSymbol == null)
                {
                    return;
                }

                var actualType = GetActualType(operation.Arguments[0]);
                if (actualType.TypeKind == TypeKind.Array)
                {
                    var properties = CreateProperties(OptimizeLinqUsageData.UseLengthProperty);
                    context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Length", operation.TargetMethod.Name);
                    return;
                }

                if (actualType.AllInterfaces.Any(i => i.OriginalDefinition.Equals(collectionOfTSymbol) || i.OriginalDefinition.Equals(readOnlyCollectionOfTSymbol)))
                {
                    // Ensure the Count property is not an explicit implementation
                    var count = actualType.GetMembers("Count").OfType <IPropertySymbol>().FirstOrDefault(m => m.ExplicitInterfaceImplementations.Length == 0);
                    if (count != null)
                    {
                        var properties = CreateProperties(OptimizeLinqUsageData.UseCountProperty);
                        context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Count", operation.TargetMethod.Name);
                        return;
                    }
                }
            }
            else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.LongCount), StringComparison.Ordinal))
            {
                var actualType = GetActualType(operation.Arguments[0]);
                if (actualType.TypeKind == TypeKind.Array)
                {
                    var properties = CreateProperties(OptimizeLinqUsageData.UseLongLengthProperty);
                    context.ReportDiagnostic(s_listMethodsRule, properties, operation, "LongLength", operation.TargetMethod.Name);
                }
            }
        }
        private static void WhereShouldBeBeforeOrderBy(OperationAnalysisContext context, IInvocationOperation operation, List <INamedTypeSymbol> enumerableSymbols)
        {
            if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) ||
                string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal))
            {
                var parent = GetParentLinqOperation(operation);
                if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType))
                {
                    if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal))
                    {
                        var properties = CreateProperties(OptimizeLinqUsageData.CombineWhereWithNextMethod)
                                         .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                         .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                         .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                                         .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                                         .Add("MethodName", parent.TargetMethod.Name);

                        context.ReportDiagnostic(s_optimizeWhereAndOrderByRule, properties, parent, operation.TargetMethod.Name);
                    }
                }
            }
        }
        /// <summary>
        /// Gets the value of precision used in Equal/NotEqual invocation or <c>null</c> if cannot be obtained.
        /// This has the semantic of: 1. Ensure the analysis applies, 2. Get data for further analysis.
        /// </summary>
        /// <param name="invocation"></param>
        private static (IArgumentOperation precisionArgument, int value)? GetNumericLiteralValue(IInvocationOperation invocation)
        {
            if (invocation.Arguments.Length != 3)
            {
                return(null);
            }

            var precisionParameter = invocation.TargetMethod.Parameters[2];
            var precisionArgument  = invocation.Arguments.First(arg => arg.Parameter.Equals(precisionParameter));

            if (precisionArgument is null)
            {
                return(null);
            }

            var constantValue = precisionArgument.Value.ConstantValue;

            if (!constantValue.HasValue || !(constantValue.Value is int value))
            {
                return(null);
            }

            return(precisionArgument, value);
        }
Пример #15
0
            public override DisposeAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction(IInvocationOperation operation, object argument)
            {
                var value             = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(operation, argument);
                var disposeMethodKind = operation.TargetMethod.GetDisposeMethodKind(WellKnownTypeProvider.IDisposable);

                switch (disposeMethodKind)
                {
                case DisposeMethodKind.Dispose:
                case DisposeMethodKind.DisposeBool:
                    HandleDisposingOperation(operation, operation.Instance);
                    break;

                case DisposeMethodKind.Close:
                    // FxCop compat: Calling "this.Close" shouldn't count as disposing the object within the implementation of Dispose.
                    if (operation.Instance?.Kind != OperationKind.InstanceReference)
                    {
                        goto case DisposeMethodKind.Dispose;
                    }
                    break;

                default:
                    // FxCop compat: Catches things like static calls to File.Open() and Create()
                    if (IsDisposableCreationSpecialCase(operation))
                    {
                        var instanceLocation = GetPointsToAbstractValue(operation);
                        return(HandleInstanceCreation(operation.Type, instanceLocation, value));
                    }
                    else if (operation.Arguments.Length > 0 &&
                             operation.TargetMethod.IsCollectionAddMethod(WellKnownTypeProvider.CollectionTypes))
                    {
                        // FxCop compat: The object added to a collection is considered escaped.
                        var lastArgument = operation.Arguments[operation.Arguments.Length - 1];
                        HandlePossibleEscapingOperation(operation, lastArgument.Value);
                    }

                    break;
                }

                return(value);
            }
        private static void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols)
        {
            if (!string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal))
            {
                return;
            }

            var binaryOperation = GetParentBinaryOperation(operation, out var countOperand);

            if (binaryOperation == null)
            {
                return;
            }

            if (!IsSupportedOperator(binaryOperation.OperatorKind))
            {
                return;
            }

            if (!binaryOperation.LeftOperand.Type.IsInt32() || !binaryOperation.RightOperand.Type.IsInt32())
            {
                return;
            }

            var opKind       = NormalizeOperator();
            var otherOperand = binaryOperation.LeftOperand == countOperand ? binaryOperation.RightOperand : binaryOperation.LeftOperand;

            if (otherOperand == null)
            {
                return;
            }

            string message    = null;
            var    properties = ImmutableDictionary <string, string> .Empty;

            if (otherOperand.ConstantValue.HasValue && otherOperand.ConstantValue.Value is int value)
            {
                switch (opKind)
                {
                case BinaryOperatorKind.Equals:
                    if (value < 0)
                    {
                        // expr.Count() == -1
                        message    = "Expression is always false";
                        properties = CreateProperties(OptimizeLinqUsageData.UseFalse);
                    }
                    else if (value == 0)
                    {
                        // expr.Count() == 0
                        message    = "Replace 'Count() == 0' with 'Any() == false'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseNotAny);
                    }
                    else
                    {
                        // expr.Count() == 1
                        if (!HasTake())
                        {
                            message    = Invariant($"Replace 'Count() == {value}' with 'Take({value + 1}).Count() == {value}'");
                            properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount);
                        }
                    }

                    break;

                case BinaryOperatorKind.NotEquals:
                    if (value < 0)
                    {
                        // expr.Count() != -1 is always true
                        message    = "Expression is always true";
                        properties = CreateProperties(OptimizeLinqUsageData.UseTrue);
                    }
                    else if (value == 0)
                    {
                        // expr.Count() != 0
                        message    = "Replace 'Count() != 0' with 'Any()'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseAny);
                    }
                    else
                    {
                        // expr.Count() != 1
                        if (!HasTake())
                        {
                            message    = Invariant($"Replace 'Count() != {value}' with 'Take({value + 1}).Count() != {value}'");
                            properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount);
                        }
                    }

                    break;

                case BinaryOperatorKind.LessThan:
                    if (value <= 0)
                    {
                        // expr.Count() < 0
                        message    = "Expression is always false";
                        properties = CreateProperties(OptimizeLinqUsageData.UseFalse);
                    }
                    else if (value == 1)
                    {
                        // expr.Count() < 1 ==> expr.Count() == 0
                        message    = "Replace 'Count() < 1' with 'Any() == false'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseNotAny);
                    }
                    else
                    {
                        // expr.Count() < 10
                        message    = Invariant($"Replace 'Count() < {value}' with 'Skip({value - 1}).Any() == false'");
                        properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny)
                                     .Add("SkipMinusOne", null);
                    }

                    break;

                case BinaryOperatorKind.LessThanOrEqual:
                    if (value < 0)
                    {
                        // expr.Count() <= -1
                        message    = "Expression is always false";
                        properties = CreateProperties(OptimizeLinqUsageData.UseFalse);
                    }
                    else if (value == 0)
                    {
                        // expr.Count() <= 0
                        message    = "Replace 'Count() <= 0' with 'Any() == false'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseNotAny);
                    }
                    else
                    {
                        // expr.Count() < 10
                        message    = Invariant($"Replace 'Count() <= {value}' with 'Skip({value}).Any() == false'");
                        properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny);
                    }

                    break;

                case BinaryOperatorKind.GreaterThan:
                    if (value < 0)
                    {
                        // expr.Count() > -1
                        message    = "Expression is always true";
                        properties = CreateProperties(OptimizeLinqUsageData.UseTrue);
                    }
                    else if (value == 0)
                    {
                        // expr.Count() > 0
                        message    = "Replace 'Count() > 0' with 'Any()'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseAny);
                    }
                    else
                    {
                        // expr.Count() > 1
                        message    = Invariant($"Replace 'Count() > {value}' with 'Skip({value}).Any()'");
                        properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny);
                    }

                    break;

                case BinaryOperatorKind.GreaterThanOrEqual:
                    if (value <= 0)
                    {
                        // expr.Count() >= 0
                        message    = "Expression is always true";
                        properties = CreateProperties(OptimizeLinqUsageData.UseTrue);
                    }
                    else if (value == 1)
                    {
                        // expr.Count() >= 1
                        message    = "Replace 'Count() >= 1' with 'Any()'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseAny);
                    }
                    else
                    {
                        // expr.Count() >= 2
                        message    = Invariant($"Replace 'Count() >= {value}' with 'Skip({value - 1}).Any()'");
                        properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny)
                                     .Add("SkipMinusOne", null);
                    }

                    break;
                }
            }
            else
            {
                switch (opKind)
                {
                case BinaryOperatorKind.Equals:
                    // expr.Count() == 1
                    if (!HasTake())
                    {
                        message    = "Replace 'Count() == n' with 'Take(n + 1).Count() == n'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount);
                    }

                    break;

                case BinaryOperatorKind.NotEquals:
                    // expr.Count() != 1
                    if (!HasTake())
                    {
                        message    = "Replace 'Count() != n' with 'Take(n + 1).Count() != n'";
                        properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount);
                    }

                    break;

                case BinaryOperatorKind.LessThan:
                    // expr.Count() < 10
                    message    = "Replace 'Count() < n' with 'Skip(n - 1).Any() == false'";
                    properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny)
                                 .Add("SkipMinusOne", null);
                    break;

                case BinaryOperatorKind.LessThanOrEqual:
                    // expr.Count() <= 10
                    message    = "Replace 'Count() <= n' with 'Skip(n).Any() == false'";
                    properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny);
                    break;

                case BinaryOperatorKind.GreaterThan:
                    // expr.Count() > 1
                    message    = "Replace 'Count() > n' with 'Skip(n).Any()'";
                    properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny);
                    break;

                case BinaryOperatorKind.GreaterThanOrEqual:
                    // expr.Count() >= 2
                    message    = "Replace 'Count() >= n' with 'Skip(n - 1).Any()'";
                    properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny)
                                 .Add("SkipMinusOne", null);
                    break;
                }
            }

            if (message != null)
            {
                properties = properties
                             .Add("OperandOperationStart", otherOperand.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                             .Add("OperandOperationLength", otherOperand.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture))
                             .Add("CountOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture))
                             .Add("CountOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture));

                context.ReportDiagnostic(s_optimizeCountRule, properties, binaryOperation, message);
            }
        private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements(
            IInvocationOperation operation,
            ContractMethodNames contractMethod,
            SemanticModel semanticModel,
            CancellationToken token)
        {
            var invocationExpression = operation.Syntax;

            // Getting the original predicate.
            var predicateArgumentOperation = operation.Arguments[0];
            var predicateArgument          = (ArgumentSyntax)predicateArgumentOperation.Syntax;

            ArgumentSyntax?extraForAllArgument  = null;
            int            messageArgumentIndex = 1;

            // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty

            if (contractMethod.IsNullCheck())
            {
                predicateArgument =
                    Argument(
                        // Changing NotNull(x) to x != null
                        BinaryExpression(
                            SyntaxKind.NotEqualsExpression,
                            predicateArgument.Expression,
                            LiteralExpression(SyntaxKind.NullLiteralExpression)))
                    .NormalizeWhitespace();
            }
            else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace())
            {
                var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace);

                // Targeting a full framework can cause an issue for null-ness analysis
                // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes.
                // It means that the compiler can't recognize that the following code is correct and still emit the warning:
                // if (!string.IsNullOrEmpty(str)) return str.Length;
                predicateArgument =
                    Argument(
                        PrefixUnaryExpression(
                            SyntaxKind.LogicalNotExpression,
                            InvocationExpression(
                                MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                                    IdentifierName(stringMethodName)))
                            .WithArgumentList(
                                ArgumentList(SingletonSeparatedList(predicateArgument))))
                        ).NormalizeWhitespace();
            }
            else if (contractMethod.IsForAll())
            {
                extraForAllArgument = (ArgumentSyntax)operation.Arguments[1].Syntax;
                messageArgumentIndex++;
            }

            // Detecting the following case:
            // if (predicate is false) {Contract.Assert(false, complicatedMessage);}
            var sourceNode = invocationExpression.Parent;

            if (predicateArgumentOperation.Value is ILiteralOperation lo &&
                lo.ConstantValue.HasValue && lo.ConstantValue.Value.Equals(false))
            {
                // this is Assert(false) case.
                if (operation.Parent?.Parent?.Parent is IConditionalOperation conditional &&
                    conditional.WhenFalse == null &&
                    conditional.WhenTrue.Children.Count() == 1)
                {
                    // The contract is inside the if block with a single statement.
                    sourceNode = conditional.Syntax;

                    var negatedCondition = (ExpressionSyntax?)SyntaxGeneratorExtensions.Negate(conditional.Condition.Syntax, semanticModel, token);
                    if (negatedCondition != null)
                    {
                        predicateArgument = Argument(negatedCondition);
                    }
                }
            }

            var           originalMessageArgument = operation.Arguments[messageArgumentIndex];
            Func <string> nonDefaultArgument      =
                () => contractMethod.IsForAll()
                    ? operation.Arguments[1].Syntax.ToFullString()
                    : predicateArgument.ToFullString();

            // Using an original message if provided.
            // Otherwise using a predicate as the new message.
            var messageArgument =
                originalMessageArgument.IsImplicit == false
                    ? (ArgumentSyntax)originalMessageArgument.Syntax
                    : Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(nonDefaultArgument())));

            var arguments =
                new SeparatedSyntaxList <ArgumentSyntax>()
                .Add(predicateArgument)
                .AddIfNotNull(extraForAllArgument);

            // Generating Contract.Check(predicate)?.Requires/Assert(message)

            var targetMethodName = GetTargetMethod(contractMethod);
            var checkMethodName  = GetCheckMethod(contractMethod);
            var contractCall     =
                ExpressionStatement(
                    ConditionalAccessExpression(
                        // Contract.Requires(
                        InvocationExpression(
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                IdentifierName(FluentContractNames.ContractClassName),
                                IdentifierName(checkMethodName)))
                        // (predicate)
                        .WithArgumentList(
                            ArgumentList(arguments)),
                        // ?.IsTrue(message)
                        InvocationExpression(
                            MemberBindingExpression(
                                IdentifierName(targetMethodName)))
                        .WithArgumentList(
                            ArgumentList(
                                SingletonSeparatedList(messageArgument)))
                        )
                    );

            var trivia    = sourceNode.GetLeadingTrivia();
            var finalNode = contractCall.WithLeadingTrivia(trivia);

            return(sourceNode, finalNode);
        }
Пример #18
0
 static void CaptureHashCreateInvocationOperation(DataCollector dataCollector, IInvocationOperation hashCreateInvocation)
 {
     if (TryGetVariableInitializerOperation(hashCreateInvocation.Parent, out var variableInitializerOperation))
     {
         var ownerType = hashCreateInvocation.TargetMethod.ContainingType;
         CaptureVariableDeclaratorOperation(dataCollector, ownerType, variableInitializerOperation);
     }
 }
 private static SyntaxNode GetInstance(IInvocationOperation invocationOperation)
 {
     return(invocationOperation.TargetMethod.IsExtensionMethod && invocationOperation.Language != LanguageNames.VisualBasic ?
            invocationOperation.Arguments[0].Value.Syntax :
            invocationOperation.Instance.Syntax);
 }
Пример #20
0
            static void CaptureOrReportComputeHashInvocationOperation(OperationAnalysisContext context, MethodHelper methodHelper, DataCollector dataCollector, IInvocationOperation computeHashInvocation, ComputeType computeType)
            {
                switch (computeHashInvocation.Instance)
                {
                case ILocalReferenceOperation:
                    dataCollector.CollectComputeHashInvocation(computeHashInvocation, computeType);
                    break;

                case IInvocationOperation chainedInvocationOperation when methodHelper.IsHashCreateMethod(chainedInvocationOperation):
                    ReportChainedComputeHashInvocationOperation(chainedInvocationOperation.TargetMethod.ContainingType);

                    break;

                case IObjectCreationOperation chainObjectCreationOperation when methodHelper.IsObjectCreationInheritingHashAlgorithm(chainObjectCreationOperation):
                    ReportChainedComputeHashInvocationOperation(chainObjectCreationOperation.Type);

                    break;
                }

                void ReportChainedComputeHashInvocationOperation(ITypeSymbol originalHashType)
                {
                    if (!methodHelper.TryGetHashDataMethod(originalHashType, computeType, out var staticHashMethod))
                    {
                        return;
                    }

                    var diagnostics = CreateDiagnostics(computeHashInvocation, staticHashMethod.ContainingType, computeType);

                    context.ReportDiagnostic(diagnostics);
                }
            }
        public override void Initialize(AnalysisContext analysisContext)
        {
            analysisContext.EnableConcurrentExecution();
            analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            analysisContext.RegisterCompilationStartAction(compilationContext =>
            {
                INamedTypeSymbol expectedExceptionType = WellKnownTypes.ExpectedException(compilationContext.Compilation);
                INamedTypeSymbol nunitAssertType       = WellKnownTypes.NunitAssert(compilationContext.Compilation);
                INamedTypeSymbol xunitAssertType       = WellKnownTypes.XunitAssert(compilationContext.Compilation);

                compilationContext.RegisterOperationBlockStartAction(osContext =>
                {
                    if (!(osContext.OwningSymbol is IMethodSymbol method))
                    {
                        return;
                    }

                    osContext.RegisterOperationAction(opContext =>
                    {
                        IOperation expression     = ((IExpressionStatementOperation)opContext.Operation).Operation;
                        DiagnosticDescriptor rule = null;
                        string targetMethodName   = null;
                        switch (expression.Kind)
                        {
                        case OperationKind.ObjectCreation:
                            IMethodSymbol ctor = ((IObjectCreationOperation)expression).Constructor;
                            if (ctor != null)
                            {
                                rule             = ObjectCreationRule;
                                targetMethodName = ctor.ContainingType.Name;
                            }
                            break;

                        case OperationKind.Invocation:
                            IInvocationOperation invocationExpression = ((IInvocationOperation)expression);
                            IMethodSymbol targetMethod = invocationExpression.TargetMethod;
                            if (targetMethod == null)
                            {
                                break;
                            }

                            if (IsStringCreatingMethod(targetMethod))
                            {
                                rule = StringCreationRule;
                            }
                            else if (IsTryParseMethod(targetMethod))
                            {
                                rule = TryParseRule;
                            }
                            else if (IsHResultOrErrorCodeReturningMethod(targetMethod))
                            {
                                rule = HResultOrErrorCodeRule;
                            }
                            else if (IsPureMethod(targetMethod, opContext.Compilation))
                            {
                                rule = PureMethodRule;
                            }

                            targetMethodName = targetMethod.Name;
                            break;
                        }

                        if (rule != null)
                        {
                            if (ShouldSkipAnalyzing(opContext, expectedExceptionType, xunitAssertType, nunitAssertType))
                            {
                                return;
                            }

                            Diagnostic diagnostic = Diagnostic.Create(rule, expression.Syntax.GetLocation(), method.Name, targetMethodName);
                            opContext.ReportDiagnostic(diagnostic);
                        }
                    }, OperationKind.ExpressionStatement);
                });
            });
        }
Пример #22
0
        private bool CheckForNestedStringFormat(OperationAnalysisContext operationContext, IInvocationOperation target, IInvocationOperation argument)
        {
            var targetMethod = argument.TargetMethod;

            if (!IsStringFormatMethod(targetMethod, out _, out _))
            {
                return(false);
            }

            operationContext.ReportDiagnostic(Diagnostic.Create(NestedRule, argument.Syntax.GetLocation(), targetMethod.ToDisplayString(), target.TargetMethod.ToDisplayString()));
            return(true);
        }
Пример #23
0
 public virtual void VisitInvocation(IInvocationOperation operation)
 {
     DefaultVisit(operation);
 }
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            context.RegisterCompilationStartAction(
                (CompilationStartAnalysisContext compilationContext) =>
            {
                Compilation compilation             = compilationContext.Compilation;
                TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilation);
                TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind);
                if (sourceInfoSymbolMap.IsEmpty)
                {
                    return;
                }

                TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind);
                if (sinkInfoSymbolMap.IsEmpty)
                {
                    return;
                }

                compilationContext.RegisterOperationBlockStartAction(
                    operationBlockStartContext =>
                {
                    ISymbol owningSymbol                = operationBlockStartContext.OwningSymbol;
                    AnalyzerOptions options             = operationBlockStartContext.Options;
                    CancellationToken cancellationToken = operationBlockStartContext.CancellationToken;
                    if (owningSymbol.IsConfiguredToSkipAnalysis(options, TaintedDataEnteringSinkDescriptor, compilation, cancellationToken))
                    {
                        return;
                    }

                    WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation);
                    InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create(
                        options,
                        SupportedDiagnostics,
                        owningSymbol,
                        operationBlockStartContext.Compilation,
                        defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive,
                        cancellationToken: cancellationToken);
                    Lazy <ControlFlowGraph?> controlFlowGraphFactory = new Lazy <ControlFlowGraph?>(
                        () => operationBlockStartContext.OperationBlocks.GetControlFlowGraph());
                    Lazy <PointsToAnalysisResult?> pointsToFactory = new Lazy <PointsToAnalysisResult?>(
                        () =>
                    {
                        if (controlFlowGraphFactory.Value == null)
                        {
                            return(null);
                        }

                        return(PointsToAnalysis.TryGetOrComputeResult(
                                   controlFlowGraphFactory.Value,
                                   owningSymbol,
                                   options,
                                   wellKnownTypeProvider,
                                   interproceduralAnalysisConfiguration,
                                   interproceduralAnalysisPredicateOpt: null));
                    });
                    Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)> valueContentFactory = new Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)>(
                        () =>
                    {
                        if (controlFlowGraphFactory.Value == null)
                        {
                            return(null, null);
                        }

                        ValueContentAnalysisResult?valuecontentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult(
                            controlFlowGraphFactory.Value,
                            owningSymbol,
                            options,
                            wellKnownTypeProvider,
                            interproceduralAnalysisConfiguration,
                            out _,
                            out PointsToAnalysisResult? p);

                        return(p, valuecontentAnalysisResult);
                    });

                    PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance();

                    operationBlockStartContext.RegisterOperationAction(
                        operationAnalysisContext =>
                    {
                        IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation;
                        if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add(propertyReferenceOperation.GetRoot());
                            }
                        }
                    },
                        OperationKind.PropertyReference);

                    operationBlockStartContext.RegisterOperationAction(
                        operationAnalysisContext =>
                    {
                        IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation;
                        if (sourceInfoSymbolMap.IsSourceMethod(
                                invocationOperation.TargetMethod,
                                invocationOperation.Arguments,
                                pointsToFactory,
                                valueContentFactory,
                                out _))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add(invocationOperation.GetRoot());
                            }
                        }
                    },
                        OperationKind.Invocation);

                    if (TaintedDataConfig.HasTaintArraySource(SinkKind))
                    {
                        operationBlockStartContext.RegisterOperationAction(
                            operationAnalysisContext =>
                        {
                            IArrayInitializerOperation arrayInitializerOperation = (IArrayInitializerOperation)operationAnalysisContext.Operation;
                            if (arrayInitializerOperation.GetAncestor <IArrayCreationOperation>(OperationKind.ArrayCreation)?.Type is IArrayTypeSymbol arrayTypeSymbol &&
                                sourceInfoSymbolMap.IsSourceConstantArrayOfType(arrayTypeSymbol))
                            {
                                lock (rootOperationsNeedingAnalysis)
                                {
                                    rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot());
                                }
                            }
                        },
                            OperationKind.ArrayInitializer);
                    }

                    operationBlockStartContext.RegisterOperationBlockEndAction(
                        operationBlockAnalysisContext =>
                    {
                        try
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                if (!rootOperationsNeedingAnalysis.Any())
                                {
                                    return;
                                }

                                if (controlFlowGraphFactory.Value == null)
                                {
                                    return;
                                }

                                foreach (IOperation rootOperation in rootOperationsNeedingAnalysis)
                                {
                                    TaintedDataAnalysisResult?taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult(
                                        controlFlowGraphFactory.Value,
                                        operationBlockAnalysisContext.Compilation,
                                        operationBlockAnalysisContext.OwningSymbol,
                                        operationBlockAnalysisContext.Options,
                                        TaintedDataEnteringSinkDescriptor,
                                        sourceInfoSymbolMap,
                                        taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind),
                                        sinkInfoSymbolMap,
                                        operationBlockAnalysisContext.CancellationToken);
                                    if (taintedDataAnalysisResult == null)
                                    {
                                        return;
                                    }

                                    foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks)
                                    {
                                        if (!sourceSink.SinkKinds.Contains(this.SinkKind))
                                        {
                                            continue;
                                        }

                                        foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins)
                                        {
                                            // Something like:
                                            // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'.
                                            Diagnostic diagnostic = Diagnostic.Create(
                                                this.TaintedDataEnteringSinkDescriptor,
                                                sourceSink.Sink.Location,
                                                additionalLocations: new Location[] { sourceOrigin.Location },
                                                messageArgs: new object[] {
                                                sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                                sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                                sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                                sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)
                                            });
                                            operationBlockAnalysisContext.ReportDiagnostic(diagnostic);
                                        }
                                    }
                                }
                            }
                        }
                        finally
                        {
                            rootOperationsNeedingAnalysis.Free();
                        }
                    });
                });
            });
        }
Пример #25
0
        public sealed override void Initialize(AnalysisContext context)
        {
            ImmutableHashSet <string> cachedDeserializationMethodNames = this.DeserializationMethodNames;

            Debug.Assert(!string.IsNullOrWhiteSpace(this.DeserializerTypeMetadataName));
            Debug.Assert(!string.IsNullOrWhiteSpace(this.SerializationBinderPropertyMetadataName));
            Debug.Assert(!cachedDeserializationMethodNames.IsEmpty);
            Debug.Assert(this.BinderDefinitelyNotSetDescriptor != null);
            Debug.Assert(this.BinderMaybeNotSetDescriptor != null);

            context.EnableConcurrentExecution();

            // Security analyzer - analyze and report diagnostics on generated code.
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            // For PropertySetAnalysis dataflow analysis.
            PropertyMapperCollection propertyMappers = new PropertyMapperCollection(
                new PropertyMapper(
                    this.SerializationBinderPropertyMetadataName,
                    PropertySetCallbacks.FlagIfNull));

            HazardousUsageEvaluatorCollection hazardousUsageEvaluators =
                new HazardousUsageEvaluatorCollection(
                    cachedDeserializationMethodNames.Select(
                        methodName => new HazardousUsageEvaluator(
                            methodName,
                            PropertySetCallbacks.HazardousIfAllFlaggedOrAllUnknown)));

            context.RegisterCompilationStartAction(
                (CompilationStartAnalysisContext compilationStartAnalysisContext) =>
            {
                if (!compilationStartAnalysisContext.Compilation.TryGetOrCreateTypeByMetadataName(
                        this.DeserializerTypeMetadataName,
                        out INamedTypeSymbol? deserializerTypeSymbol))
                {
                    return;
                }

                PooledHashSet <(IOperation Operation, ISymbol ContainingSymbol)> rootOperationsNeedingAnalysis = PooledHashSet <(IOperation, ISymbol)> .GetInstance();

                compilationStartAnalysisContext.RegisterOperationBlockStartAction(
                    (OperationBlockStartAnalysisContext operationBlockStartAnalysisContext) =>
                {
                    var owningSymbol = operationBlockStartAnalysisContext.OwningSymbol;

                    // TODO: Handle case when exactly one of the below rules is configured to skip analysis.
                    if (operationBlockStartAnalysisContext.Options.IsConfiguredToSkipAnalysis(BinderDefinitelyNotSetDescriptor !,
                                                                                              owningSymbol, operationBlockStartAnalysisContext.Compilation, operationBlockStartAnalysisContext.CancellationToken) &&
                        operationBlockStartAnalysisContext.Options.IsConfiguredToSkipAnalysis(BinderMaybeNotSetDescriptor !,
                                                                                              owningSymbol, operationBlockStartAnalysisContext.Compilation, operationBlockStartAnalysisContext.CancellationToken))
                    {
                        return;
                    }

                    operationBlockStartAnalysisContext.RegisterOperationAction(
                        (OperationAnalysisContext operationAnalysisContext) =>
                    {
                        IObjectCreationOperation creationOperation =
                            (IObjectCreationOperation)operationAnalysisContext.Operation;
                        if (deserializerTypeSymbol.Equals(creationOperation.Type))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol));
                            }
                        }
                    },
                        OperationKind.ObjectCreation);

                    operationBlockStartAnalysisContext.RegisterOperationAction(
                        (OperationAnalysisContext operationAnalysisContext) =>
                    {
                        IInvocationOperation invocationOperation =
                            (IInvocationOperation)operationAnalysisContext.Operation;
                        if (Equals(invocationOperation.Instance?.Type, deserializerTypeSymbol) &&
                            cachedDeserializationMethodNames.Contains(invocationOperation.TargetMethod.Name))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol));
                            }
                        }
                    },
                        OperationKind.Invocation);

                    operationBlockStartAnalysisContext.RegisterOperationAction(
                        (OperationAnalysisContext operationAnalysisContext) =>
                    {
                        IMethodReferenceOperation methodReferenceOperation =
                            (IMethodReferenceOperation)operationAnalysisContext.Operation;
                        if (Equals(methodReferenceOperation.Instance?.Type, deserializerTypeSymbol) &&
                            cachedDeserializationMethodNames.Contains(
                                methodReferenceOperation.Method.MetadataName))
                        {
                            lock (rootOperationsNeedingAnalysis)
                            {
                                rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol));
                            }
                        }
                    },
                        OperationKind.MethodReference);
                });

                compilationStartAnalysisContext.RegisterCompilationEndAction(
                    (CompilationAnalysisContext compilationAnalysisContext) =>
                {
                    PooledDictionary <(Location Location, IMethodSymbol?Method), HazardousUsageEvaluationResult>?allResults = null;
                    try
                    {
                        lock (rootOperationsNeedingAnalysis)
                        {
                            if (!rootOperationsNeedingAnalysis.Any())
                            {
                                return;
                            }

                            allResults = PropertySetAnalysis.BatchGetOrComputeHazardousUsages(
                                compilationAnalysisContext.Compilation,
                                rootOperationsNeedingAnalysis,
                                compilationAnalysisContext.Options,
                                this.DeserializerTypeMetadataName,
                                DoNotUseInsecureDeserializerWithoutBinderBase.ConstructorMapper,
                                propertyMappers,
                                hazardousUsageEvaluators,
                                InterproceduralAnalysisConfiguration.Create(
                                    compilationAnalysisContext.Options,
                                    SupportedDiagnostics,
                                    rootOperationsNeedingAnalysis.First().Operation.Syntax.SyntaxTree,
                                    compilationAnalysisContext.Compilation,
                                    defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive,
                                    cancellationToken: compilationAnalysisContext.CancellationToken));
                        }

                        if (allResults == null)
                        {
                            return;
                        }

                        foreach (KeyValuePair <(Location Location, IMethodSymbol?Method), HazardousUsageEvaluationResult> kvp
                                 in allResults)
                        {
                            DiagnosticDescriptor descriptor;
                            switch (kvp.Value)
                            {
                            case HazardousUsageEvaluationResult.Flagged:
                                descriptor = this.BinderDefinitelyNotSetDescriptor !;
                                break;

                            case HazardousUsageEvaluationResult.MaybeFlagged:
                                descriptor = this.BinderMaybeNotSetDescriptor !;
                                break;

                            default:
                                Debug.Fail($"Unhandled result value {kvp.Value}");
                                continue;
                            }

                            RoslynDebug.Assert(kvp.Key.Method != null);            // HazardousUsageEvaluations only for invocations.
                            compilationAnalysisContext.ReportDiagnostic(
                                Diagnostic.Create(
                                    descriptor,
                                    kvp.Key.Location,
                                    kvp.Key.Method.ToDisplayString(
                                        SymbolDisplayFormat.MinimallyQualifiedFormat)));
                        }
                    }
                    finally
                    {
                        rootOperationsNeedingAnalysis.Free(compilationAnalysisContext.CancellationToken);
                        allResults?.Free(compilationAnalysisContext.CancellationToken);
                    }
                });
            });
        }
        protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation)
        {
            if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation,
                                                                  out var actualOperation, out var constraintExpression))
            {
                return;
            }

            // Check if actual operation is a member access operation for either .Length or .Count
            if (actualOperation is IMemberReferenceOperation referenceOperation &&
                IsInteger(referenceOperation.Type) &&
                referenceOperation.Instance is IOperation instance &&
                (referenceOperation.Member.Name is NUnitFrameworkConstants.NameOfHasLength ||
                 referenceOperation.Member.Name is NUnitFrameworkConstants.NameOfHasCount))
            {
                // constraint operation must be Is.
                foreach (var constraintPart in constraintExpression.ConstraintParts)
                {
                    if (!constraintPart.HasIncompatiblePrefixes() &&
                        constraintPart.Root is not null &&
                        constraintPart.HelperClass?.Name == NUnitFrameworkConstants.NameOfIs)
                    {
                        // Only raise to use Has.Property if the type at hand is IEnumerable.
                        INamedTypeSymbol enumerable = context.Compilation.GetTypeByMetadataName("System.Collections.IEnumerable") !;

                        if (instance.Type?.AllInterfaces.Any(x => SymbolEqualityComparer.Default.Equals(x, enumerable)) == true)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(
                                                         descriptor,
                                                         referenceOperation.Syntax.GetLocation(),
                                                         referenceOperation.Member.Name));
                        }
                    }
                }
            }
Пример #27
0
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            context.RegisterCompilationStartAction(
                (CompilationStartAnalysisContext compilationContext) =>
            {
                TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilationContext.Compilation);
                TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind);
                if (sourceInfoSymbolMap.IsEmpty)
                {
                    return;
                }

                TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind);
                if (sinkInfoSymbolMap.IsEmpty)
                {
                    return;
                }

                compilationContext.RegisterOperationBlockStartAction(
                    operationBlockStartContext =>
                {
                    ISymbol owningSymbol = operationBlockStartContext.OwningSymbol;

                    HashSet <IOperation> rootOperationsNeedingAnalysis = new HashSet <IOperation>();

                    operationBlockStartContext.RegisterOperationAction(
                        operationAnalysisContext =>
                    {
                        IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation;
                        if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property))
                        {
                            rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot());
                        }
                    },
                        OperationKind.PropertyReference);

                    operationBlockStartContext.RegisterOperationAction(
                        operationAnalysisContext =>
                    {
                        IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation;
                        if (sourceInfoSymbolMap.IsSourceMethod(invocationOperation.TargetMethod))
                        {
                            rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot());
                        }
                    },
                        OperationKind.Invocation);

                    operationBlockStartContext.RegisterOperationBlockEndAction(
                        operationBlockAnalysisContext =>
                    {
                        if (!rootOperationsNeedingAnalysis.Any())
                        {
                            return;
                        }

                        foreach (IOperation rootOperation in rootOperationsNeedingAnalysis)
                        {
                            TaintedDataAnalysisResult taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult(
                                rootOperation.GetEnclosingControlFlowGraph(),
                                operationBlockAnalysisContext.Compilation,
                                operationBlockAnalysisContext.OwningSymbol,
                                operationBlockAnalysisContext.Options,
                                TaintedDataEnteringSinkDescriptor,
                                sourceInfoSymbolMap,
                                taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind),
                                sinkInfoSymbolMap,
                                operationBlockAnalysisContext.CancellationToken);
                            if (taintedDataAnalysisResult == null)
                            {
                                return;
                            }

                            foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks)
                            {
                                if (!sourceSink.SinkKinds.Contains(this.SinkKind))
                                {
                                    continue;
                                }

                                foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins)
                                {
                                    // Something like:
                                    // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'.
                                    Diagnostic diagnostic = Diagnostic.Create(
                                        this.TaintedDataEnteringSinkDescriptor,
                                        sourceSink.Sink.Location,
                                        additionalLocations: new Location[] { sourceOrigin.Location },
                                        messageArgs: new object[] {
                                        sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                        sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                        sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
                                        sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)
                                    });
                                    operationBlockAnalysisContext.ReportDiagnostic(diagnostic);
                                }
                            }
                        }
                    });
                });
            });
        }
Пример #28
0
 // FxCop compat: Catches things like static calls to File.Open() and Create()
 private static bool IsDisposableCreationSpecialCase(IInvocationOperation operation)
 => operation.TargetMethod.IsStatic &&
 (operation.TargetMethod.Name.StartsWith("create", StringComparison.OrdinalIgnoreCase) ||
  operation.TargetMethod.Name.StartsWith("open", StringComparison.OrdinalIgnoreCase));
        protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation)
        {
            IOperation?actualOperation;
            IOperation?expectedOperation;

            if (assertOperation.TargetMethod.Name.Equals(NUnitFrameworkConstants.NameOfAssertAreEqual, StringComparison.Ordinal) ||
                assertOperation.TargetMethod.Name.Equals(NUnitFrameworkConstants.NameOfAssertAreNotEqual, StringComparison.Ordinal))
            {
                actualOperation   = assertOperation.GetArgumentOperation(NUnitFrameworkConstants.NameOfActualParameter);
                expectedOperation = assertOperation.GetArgumentOperation(NUnitFrameworkConstants.NameOfExpectedParameter);

                CheckActualVsExpectedOperation(context, actualOperation, expectedOperation);
            }
            else
            {
                if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation,
                                                                      out actualOperation, out var constraintExpression))
                {
                    return;
                }

                foreach (var constraintPartExpression in constraintExpression.ConstraintParts)
                {
                    if (constraintPartExpression.HasIncompatiblePrefixes() ||
                        HasCustomEqualityComparer(constraintPartExpression) ||
                        constraintPartExpression.HasUnknownExpressions())
                    {
                        return;
                    }

                    var constraintMethod = constraintPartExpression.GetConstraintMethod();

                    if (constraintMethod?.Name != NUnitFrameworkConstants.NameOfIsEqualTo ||
                        constraintMethod.ReturnType?.GetFullMetadataName() != NUnitFrameworkConstants.FullNameOfEqualToConstraint)
                    {
                        continue;
                    }

                    expectedOperation = constraintPartExpression.GetExpectedArgument();

                    CheckActualVsExpectedOperation(context, actualOperation, expectedOperation);
                }
            }
        }
Пример #30
0
        private async Task <Solution> ExtractDependency(Document document,
                                                        InvocationExpressionSyntax invocationSyntax,
                                                        IdentifierNameSyntax invokedMethodIdentifierSyntax,
                                                        SemanticModel semanticModel,
                                                        CancellationToken cancellationToken, IInvocationOperation invocationOperation,
                                                        Maybe <ImmutableArray <WhatToDoWithArgument> > whatToDoWithArgsMaybe,
                                                        BaseMethodDeclarationSyntax containingMethod)
        {
            var solution = document.Project.Solution;

            if (whatToDoWithArgsMaybe.HasNoValue)
            {
                return(solution);
            }

            var whatToDoWithArgs = whatToDoWithArgsMaybe.GetValue();

            var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);


            var nodesToReplace =
                new Dictionary <Document, List <NodeChange> >();

            void AddNewChangeToDocument(Document doc, NodeChange change)
            {
                nodesToReplace.GetOrAdd(doc, () => new List <NodeChange>()).Add(change);
            }

            var argsAndWhatToDoWithThem =
                invocationOperation.Arguments
                .Zip(whatToDoWithArgs, (arg, whatTodo) => (arg, whatTodo))
                .ToImmutableArray();

            var(parameterListChange, replacementFunctionParameterName, parametersToRemove) =
                GetChangeToContainingMethodParameters(
                    document,
                    invokedMethodIdentifierSyntax,
                    semanticModel,
                    argsAndWhatToDoWithThem,
                    containingMethod,
                    documentRoot,
                    invocationOperation,
                    whatToDoWithArgs,
                    invocationSyntax);

            AddNewChangeToDocument(
                document,
                parameterListChange);

            var nodeChange =
                GetMethodInvocationChange(
                    invocationSyntax,
                    invokedMethodIdentifierSyntax,
                    argsAndWhatToDoWithThem,
                    replacementFunctionParameterName);

            AddNewChangeToDocument(document, nodeChange);

            var changesToCallers = await GetChangesToCallers(
                semanticModel,
                cancellationToken,
                containingMethod,
                solution,
                invocationOperation.TargetMethod,
                argsAndWhatToDoWithThem,
                parametersToRemove,
                invocationSyntax).ConfigureAwait(false);

            foreach (var changeToCallers in changesToCallers)
            {
                AddNewChangeToDocument(changeToCallers.document, changeToCallers.change);
            }

            return(await UpdateSolution(cancellationToken, solution, nodesToReplace).ConfigureAwait(false));
        }