Beispiel #1
0
            internal void OperationBlockStart(OperationBlockStartAnalysisContext context)
            {
                if (!(context.OwningSymbol is IMethodSymbol method))
                {
                    return;
                }

                INamedTypeSymbol namedType = method.ContainingType;

                if (!namedType.DerivesFrom(_codeFixProviderSymbol))
                {
                    return;
                }

                context.RegisterOperationAction(operationContext =>
                {
                    var invocation = (IInvocationOperation)operationContext.Operation;
                    if (invocation.TargetMethod is IMethodSymbol invocationSym && _createMethods.Contains(invocationSym))
                    {
                        AddOperation(namedType, invocation, _codeActionCreateInvocations);
                    }
                },
                                                OperationKind.Invocation);

                context.RegisterOperationAction(operationContext =>
                {
                    var objectCreation        = (IObjectCreationOperation)operationContext.Operation;
                    IMethodSymbol constructor = objectCreation.Constructor;
                    if (constructor != null && constructor.ContainingType.DerivesFrom(_codeActionSymbol))
                    {
                        AddOperation(namedType, objectCreation, _codeActionObjectCreations);
                    }
                },
                                                OperationKind.ObjectCreation);
            }
Beispiel #2
0
            private void OnOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext)
            {
                if (_hasErrors)
                {
                    return;
                }

                operationBlockStartContext.RegisterOperationAction(_ => _hasErrors = true, OperationKind.Invalid);

                switch (operationBlockStartContext.OwningSymbol)
                {
                case IFieldSymbol _:
                    // Field initializer.
                    if (operationBlockStartContext.OperationBlocks.Length == 1 &&
                        operationBlockStartContext.OperationBlocks[0] is IFieldInitializerOperation fieldInitializer)
                    {
                        foreach (var field in fieldInitializer.InitializedFields)
                        {
                            if (!field.IsStatic && _disposableFields.Contains(field))
                            {
                                // Instance field initialized with a disposable object is considered a candidate.
                                AddOrUpdateFieldDisposedValue(field, disposed: false);
                            }
                        }
                    }

                    break;

                case IMethodSymbol containingMethod:
                    // Method body.
                    OnMethodOperationBlockStart(operationBlockStartContext, containingMethod);
                    break;
                }
            }
Beispiel #3
0
            internal void AnalyzeMethodBodyStart(OperationBlockStartAnalysisContext context)
            {
                var symbol = context.OwningSymbol as IMethodSymbol;

                if (symbol == null || !CanContainsYield(symbol))
                {
                    return;
                }

                var methodContext = new MethodContext(this, symbol);

                context.RegisterOperationAction(methodContext.AnalyzeYield, OperationKind.YieldReturn);
                context.RegisterOperationAction(methodContext.AnalyzeYield, OperationKind.YieldBreak);
                context.RegisterOperationAction(methodContext.AnalyzeThrow, OperationKind.Throw);
                context.RegisterOperationBlockEndAction(methodContext.OperationBlockEndAction);
            }
Beispiel #4
0
 private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext context)
 {
     if (context.OwningSymbol is IMethodSymbol method &&
         !method.IsAsync &&
         Utils.IsTask(method.ReturnType))
     {
         context.RegisterOperationAction(Utils.DebuggableWrapper(context => AnalyzerReturnOperation(context)), OperationKind.Return);
     }
 }
Beispiel #5
0
                public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer)
                {
                    if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty)
                    {
                        return;
                    }

                    // All operation blocks for a symbol belong to the same tree.
                    var firstBlock = context.OperationBlocks[0];

                    if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree,
                                                                                firstBlock.Language,
                                                                                context.Options,
                                                                                context.CancellationToken,
                                                                                out var options))
                    {
                        return;
                    }

                    var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options);

                    context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeConversion, OperationKind.Conversion);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeFieldOrPropertyReference, OperationKind.FieldReference, OperationKind.PropertyReference);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeParameterReference, OperationKind.ParameterReference);
                    context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd);

                    return;

                    // Local Functions.
                    bool HasSyntaxErrors()
                    {
                        foreach (var operationBlock in context.OperationBlocks)
                        {
                            if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors())
                            {
                                return(true);
                            }
                        }

                        return(false);
                    }
                }
Beispiel #6
0
        public static void RegisterOperationActionInternal(this OperationBlockStartAnalysisContext context, Action <OperationAnalysisContext> analyzerOperationCallback, params OperationKind[] operationKinds)
        {
            if (!ShouldExecuteOperationAnalyzers)
            {
                return;
            }

            // No feature flag check on OperationBlockStartAnalysisContext.RegisterOperationAction, so we call it directly.
            context.RegisterOperationAction(analyzerOperationCallback, operationKinds);
        }
 private void RegisterAnalyzer(OperationBlockStartAnalysisContext context, CompilationSecurityTypes types, Version frameworkVersion)
 {
     var analyzer = new OperationAnalyzer(types, frameworkVersion);
     context.RegisterOperationAction(
         analyzer.AnalyzeOperation,
         OperationKind.InvocationExpression,
         OperationKind.AssignmentExpression,
         OperationKind.VariableDeclaration,
         OperationKind.ObjectCreationExpression,
         OperationKind.FieldInitializerAtDeclaration
     );
     context.RegisterOperationBlockEndAction(
         analyzer.AnalyzeOperationBlock
     );
 }
Beispiel #8
0
        private void RegisterAnalyzer(OperationBlockStartAnalysisContext context, CompilationSecurityTypes types, Version frameworkVersion)
        {
            var analyzer = new OperationAnalyzer(types, frameworkVersion);

            context.RegisterOperationAction(
                analyzer.AnalyzeOperation,
                OperationKind.Invocation,
                OperationKind.SimpleAssignment,
                OperationKind.VariableDeclaration,
                OperationKind.ObjectCreation,
                OperationKind.FieldInitializer
                );
            context.RegisterOperationBlockEndAction(
                analyzer.AnalyzeOperationBlock
                );
        }
 private void OnOperationBlock(OperationBlockStartAnalysisContext context)
 {
     context.RegisterOperationAction(OnMethodReference, OperationKind.MethodReference);
     BlockAnalyzer.Analyze(context, this);
 }
Beispiel #10
0
            private void OnMethodOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext, IMethodSymbol containingMethod)
            {
                // Shared PointsTo dataflow analysis result for all the callbacks to AnalyzeFieldReference
                // for this method's executable code.
                PointsToAnalysisResult lazyPointsToAnalysisResult = null;

                // If we have any potential disposable object creation descendant within the operation blocks,
                // register an operation action to analyze field references where field might be assigned a disposable object.
                if (_disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod))
                {
                    operationBlockStartContext.RegisterOperationAction(AnalyzeFieldReference, OperationKind.FieldReference);
                }

                // If this is a Dispose method, then analyze dispose invocations for fields within this method.
                if (_disposeAnalysisHelper.IsAnyDisposeMethod(containingMethod))
                {
                    AnalyzeDisposeMethod();
                }

                return;

                // Local function
                void AnalyzeFieldReference(OperationAnalysisContext operationContext)
                {
                    var fieldReference = (IFieldReferenceOperation)operationContext.Operation;
                    var field          = fieldReference.Field;

                    // Check if this is a Disposable field that is not currently being tracked.
                    if (_fieldDisposeValueMap.ContainsKey(field) ||
                        !_disposableFields.Contains(field) ||
                        _hasErrors)
                    {
                        return;
                    }

                    // Only track instance fields on the current instance.
                    if (field.IsStatic || fieldReference.Instance?.Kind != OperationKind.InstanceReference)
                    {
                        return;
                    }

                    // We have a field reference for a disposable field.
                    // Check if it is being assigned a locally created disposable object.
                    if (fieldReference.Parent is ISimpleAssignmentOperation simpleAssignmentOperation &&
                        simpleAssignmentOperation.Target == fieldReference)
                    {
                        if (lazyPointsToAnalysisResult == null)
                        {
                            if (_disposeAnalysisHelper.TryGetOrComputeResult(
                                    operationBlockStartContext, containingMethod,
                                    s_disposableFieldsShouldBeDisposedRule,
                                    trackInstanceFields: false,
                                    out _, out var pointsToAnalysisResult) &&
                                pointsToAnalysisResult != null)
                            {
                                Interlocked.CompareExchange(ref lazyPointsToAnalysisResult, pointsToAnalysisResult, null);
                            }
                            else
                            {
                                _hasErrors = true;
                                return;
                            }
                        }

                        PointsToAbstractValue assignedPointsToValue = lazyPointsToAnalysisResult[simpleAssignmentOperation.Value.Kind, simpleAssignmentOperation.Value.Syntax];
                        foreach (var location in assignedPointsToValue.Locations)
                        {
                            if (_disposeAnalysisHelper.IsDisposableCreationOrDisposeOwnershipTransfer(location, containingMethod))
                            {
                                AddOrUpdateFieldDisposedValue(field, disposed: false);
                                break;
                            }
                        }
                    }
                }

                void AnalyzeDisposeMethod()
                {
                    if (_hasErrors)
                    {
                        return;
                    }

                    // Perform dataflow analysis to compute dispose value of disposable fields at the end of dispose method.
                    if (_disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext, containingMethod,
                                                                     s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: true,
                                                                     disposeAnalysisResult: out var disposeAnalysisResult,
                                                                     pointsToAnalysisResult: out var pointsToAnalysisResult))
                    {
                        BasicBlock exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit();
                        foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap)
                        {
                            IFieldSymbol          field         = fieldWithPointsToValue.Key;
                            PointsToAbstractValue pointsToValue = fieldWithPointsToValue.Value;

                            if (!_disposableFields.Contains(field))
                            {
                                continue;
                            }

                            ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data;
                            var disposed = false;
                            foreach (var location in pointsToValue.Locations)
                            {
                                if (disposeDataAtExit.TryGetValue(location, out DisposeAbstractValue disposeValue))
                                {
                                    switch (disposeValue.Kind)
                                    {
                                    // For MaybeDisposed, conservatively mark the field as disposed as we don't support path sensitive analysis.
                                    case DisposeAbstractValueKind.MaybeDisposed:
                                    case DisposeAbstractValueKind.Unknown:
                                    case DisposeAbstractValueKind.Escaped:
                                    case DisposeAbstractValueKind.Disposed:
                                        disposed = true;
                                        AddOrUpdateFieldDisposedValue(field, disposed);
                                        break;
                                    }
                                }

                                if (disposed)
                                {
                                    break;
                                }
                            }
                        }
                    }
Beispiel #11
0
        private static void AnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            UnusedParameterDictionary unusedMethodParameters,
            INamedTypeSymbol eventsArgSymbol,
            ISet <IMethodSymbol> methodsUsedAsDelegates,
            ImmutableHashSet <INamedTypeSymbol> attributeSetForMethodsToIgnore)
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return;
            }

            // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat).
            if (method.IsImplicitlyDeclared ||
                method.IsExtern ||
                method.IsAbstract ||
                method.IsVirtual ||
                method.IsOverride ||
                method.IsImplementationOfAnyInterfaceMember() ||
                method.IsFinalizer())
            {
                return;
            }

            // Ignore property accessors.
            if (method.IsPropertyAccessor())
            {
                return;
            }

            // Ignore event handler methods "Handler(object, MyEventArgs)"
            if (eventsArgSymbol != null &&
                method.Parameters.Length == 2 &&
                method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                method.Parameters[1].Type.Inherits(eventsArgSymbol))
            {
                return;
            }

            // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'.
            if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass)))
            {
                return;
            }

            // Ignore methods that were used as delegates
            if (methodsUsedAsDelegates.Contains(method))
            {
                return;
            }

            // Initialize local mutable state in the start action.
            var analyzer = new UnusedParametersAnalyzer(method, unusedMethodParameters);

            // Register an intermediate non-end action that accesses and modifies the state.
            startOperationBlockContext.RegisterOperationAction(analyzer.AnalyzeParameterReference, OperationKind.ParameterReference);

            // Register an end action to add unused parameters to the unusedMethodParameters dictionary
            startOperationBlockContext.RegisterOperationBlockEndAction(analyzer.OperationBlockEndAction);
        }
        private static void AnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            UnusedParameterDictionary unusedMethodParameters,
            INamedTypeSymbol?eventsArgSymbol,
            ISet <IMethodSymbol> methodsUsedAsDelegates,
            ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore)
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return;
            }

            // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat).
            if (method.IsImplicitlyDeclared ||
                method.IsExtern ||
                method.IsAbstract ||
                method.IsVirtual ||
                method.IsOverride ||
                method.IsImplementationOfAnyInterfaceMember() ||
                method.IsFinalizer())
            {
                return;
            }

            // Ignore property accessors.
            if (method.IsPropertyAccessor())
            {
                return;
            }

            // Ignore event handler methods "Handler(object, MyEventArgs)"
            if (method.Parameters.Length == 2 &&
                method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                // UWP has specific EventArgs not inheriting from System.EventArgs. It was decided to go for a suffix match rather than a whitelist.
                (method.Parameters[1].Type.Inherits(eventsArgSymbol) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal)))
            {
                return;
            }

            // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'.
            if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass)))
            {
                return;
            }

            // Ignore methods that were used as delegates
            if (methodsUsedAsDelegates.Contains(method))
            {
                return;
            }

            // Bail out if user has configured to skip analysis for the method.
            if (!method.MatchesConfiguredVisibility(
                    startOperationBlockContext.Options,
                    Rule,
                    startOperationBlockContext.CancellationToken,
                    defaultRequiredVisibility: SymbolVisibilityGroup.All))
            {
                return;
            }

            // Initialize local mutable state in the start action.
            var analyzer = new UnusedParametersAnalyzer(method, unusedMethodParameters);

            // Register an intermediate non-end action that accesses and modifies the state.
            startOperationBlockContext.RegisterOperationAction(analyzer.AnalyzeParameterReference, OperationKind.ParameterReference);

            // Register an end action to add unused parameters to the unusedMethodParameters dictionary
            startOperationBlockContext.RegisterOperationBlockEndAction(analyzer.OperationBlockEndAction);
        }
                public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer)
                {
                    if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty)
                    {
                        return;
                    }

                    // Bail out in presence of conditional directives
                    // This is a workaround for https://github.com/dotnet/roslyn/issues/31820
                    // Issue https://github.com/dotnet/roslyn/issues/31821 tracks
                    // reverting this workaround.
                    if (HasConditionalDirectives())
                    {
                        return;
                    }

                    // All operation blocks for a symbol belong to the same tree.
                    var firstBlock = context.OperationBlocks[0];

                    if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree,
                                                                                firstBlock.Language,
                                                                                context.Options,
                                                                                context.CancellationToken,
                                                                                out var options))
                    {
                        return;
                    }

                    var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options);

                    context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeLocalOrParameterReference, OperationKind.LocalReference, OperationKind.ParameterReference);
                    context.RegisterOperationAction(_ => blockAnalyzer._hasInvalidOperation = true, OperationKind.Invalid);
                    context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd);

                    return;

                    // Local Functions.
                    bool HasSyntaxErrors()
                    {
                        foreach (var operationBlock in context.OperationBlocks)
                        {
                            if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors())
                            {
                                return(true);
                            }
                        }

                        return(false);
                    }

                    bool HasConditionalDirectives()
                    {
                        foreach (var operationBlock in context.OperationBlocks)
                        {
                            if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true)
                                .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n)))
                            {
                                return(true);
                            }
                        }

                        return(false);
                    }
                }
                public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer)
                {
                    if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty)
                    {
                        return;
                    }

                    // Bail out in presence of conditional directives
                    // This is a workaround for https://github.com/dotnet/roslyn/issues/31820
                    // Issue https://github.com/dotnet/roslyn/issues/31821 tracks
                    // reverting this workaround.
                    if (HasConditionalDirectives())
                    {
                        return;
                    }

                    // All operation blocks for a symbol belong to the same tree.
                    var firstBlock = context.OperationBlocks[0];

                    if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree,
                                                                                context.Options,
                                                                                out var options))
                    {
                        return;
                    }

                    // Ignore methods that are just a single-throw method.  These are often
                    // in-progress pieces of work and we don't want to force the user to fixup other
                    // issues before they've even gotten around to writing their code.
                    if (IsSingleThrowNotImplementedOperation(firstBlock))
                    {
                        return;
                    }

                    var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options);

                    context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeLocalOrParameterReference, OperationKind.LocalReference, OperationKind.ParameterReference);
                    context.RegisterOperationAction(_ => blockAnalyzer._hasInvalidOperation = true, OperationKind.Invalid);
                    context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd);

                    return;

                    // Local Functions.
                    bool HasSyntaxErrors()
                    {
                        foreach (var operationBlock in context.OperationBlocks)
                        {
                            if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors())
                            {
                                return(true);
                            }
                        }

                        return(false);
                    }

                    bool HasConditionalDirectives()
                    {
                        foreach (var operationBlock in context.OperationBlocks)
                        {
                            if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true)
                                .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n)))
                            {
                                return(true);
                            }
                        }

                        return(false);
                    }