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
     );
 }
                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;
                    }

                    // 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);
                    }
        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;
            }

            // 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);
        }
예제 #4
0
 private void OnOperationBlock(OperationBlockStartAnalysisContext context)
 {
     context.RegisterOperationAction(OnMethodReference, OperationKind.MethodReference);
     BlockAnalyzer.Analyze(context, this);
 }
            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;
                            }
                        }

                        var 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()
                {
                    _hasDisposeMethod = true;

                    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))
                    {
                        var exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit();
                        foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap)
                        {
                            var field         = fieldWithPointsToValue.Key;
                            var pointsToValue = fieldWithPointsToValue.Value;

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

                            var disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data;
                            var disposed          = false;
                            foreach (var location in pointsToValue.Locations)
                            {
                                if (disposeDataAtExit.TryGetValue(location, out var 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;
                                }
                            }
                        }
                    }
 public StartupAnalysisContext(OperationBlockStartAnalysisContext operationBlockStartAnalysisContext, StartupSymbols startupSymbols)
 {
     OperationBlockStartAnalysisContext = operationBlockStartAnalysisContext;
     StartupSymbols = startupSymbols;
 }
                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.AnalyzeArgument, OperationKind.Argument);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeConversion, OperationKind.Conversion);
                    context.RegisterOperationAction(blockAnalyzer.AnalyzeTuple, OperationKind.Tuple);
                    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);
                    }

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

                        return(false);
                    }
                }
예제 #8
0
#pragma warning disable RS1012 // Start action has no registered actions.
        private static bool ShouldAnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            INamedTypeSymbol?eventsArgSymbol,
            ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore,
            INamedTypeSymbol?serializationInfoType,
            INamedTypeSymbol?streamingContextType)
#pragma warning restore RS1012 // Start action has no registered actions.
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return(false);
            }

            // 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(false);
            }

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

            // Ignore serialization special methods
            if (method.IsSerializationConstructor(serializationInfoType, streamingContextType) ||
                method.IsGetObjectData(serializationInfoType, streamingContextType))
            {
                return(false);
            }

            // Ignore event handler methods "Handler(object, MyEventArgs)"
            if (method.HasEventHandlerSignature(eventsArgSymbol))
            {
                return(false);
            }

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

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

            // Check to see if the method just throws a NotImplementedException/NotSupportedException
            // We shouldn't warn about parameters in that case
            if (startOperationBlockContext.IsMethodNotImplementedOrSupported())
            {
                return(false);
            }

            // Ignore generated method for top level statements
            if (method.IsTopLevelStatementsEntryPointMethod())
            {
                return(false);
            }

            return(true);
        }
#pragma warning disable RS1012 // Start action has no registered actions.
        private static bool ShouldAnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            INamedTypeSymbol?eventsArgSymbol,
            ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore,
            INamedTypeSymbol?serializationInfoType,
            INamedTypeSymbol?streamingContextType)
#pragma warning restore RS1012 // Start action has no registered actions.
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return(false);
            }

            // 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(false);
            }

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

            // Ignore serialization special methods
            if (method.IsSerializationConstructor(serializationInfoType, streamingContextType) ||
                method.IsGetObjectData(serializationInfoType, streamingContextType))
            {
                return(false);
            }

            // 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(false);
            }

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

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

            // Check to see if the method just throws a NotImplementedException/NotSupportedException
            // We shouldn't warn about parameters in that case
            if (startOperationBlockContext.IsMethodNotImplementedOrSupported())
            {
                return(false);
            }

            return(true);
        }