internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context)
 {
     if (!EnabledConcurrentExecution)
     {
         context.ReportDiagnostic(Diagnostic.Create(Rule, _analysisContextParameter.Locations.FirstOrDefault()));
     }
 }
예제 #2
0
 public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary <string, string> properties, ISymbol symbol, params string[] messageArgs)
 {
     foreach (var location in symbol.Locations)
     {
         context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
     }
 }
 internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context)
 {
     if (!ConfiguredGeneratedCodeAnalysis)
     {
         context.ReportDiagnostic(_analysisContextParameter.CreateDiagnostic(Rule));
     }
 }
 internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context)
 {
     if (!EnabledConcurrentExecution)
     {
         context.ReportDiagnostic(_analysisContextParameter.CreateDiagnostic(Rule));
     }
 }
예제 #5
0
 internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context)
 {
     if (!ConfiguredGeneratedCodeAnalysis)
     {
         context.ReportDiagnostic(Diagnostic.Create(Rule, _analysisContextParameter.Locations.FirstOrDefault()));
     }
 }
예제 #6
0
        public void AnalyzeEnd(OperationBlockAnalysisContext context)
        {
            if (_canBeReadOnly)
            {
                if (context.OwningSymbol is IMethodSymbol method && method.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)
                {
                    var parent = context.OperationBlocks.FirstOrDefault()?.Syntax.Parent;
                    if (parent?.IsKind(SyntaxKind.PropertyDeclaration) == true)
                    {
                        context.ReportDiagnostic(s_rule, ((PropertyDeclarationSyntax)parent).Identifier, context.OwningSymbol.Name);
                        return;
                    }
                }

                context.ReportDiagnostic(s_rule, context.OwningSymbol, context.OwningSymbol.Name);
            }
        }
 public void OperationBlockEndAction(OperationBlockAnalysisContext context)
 {
     // Report diagnostics for unused parameters.
     foreach (IParameterSymbol parameter in _unusedParameters)
     {
         Diagnostic diagnostic = Diagnostic.Create(Rule, parameter.Locations[0], parameter.Name, parameter.ContainingSymbol.Name);
         context.ReportDiagnostic(diagnostic);
     }
 }
            /// <summary>
            /// Checks rule: Modify {0} so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns.
            /// </summary>
            private void CheckDisposeImplementationRule(IMethodSymbol method, INamedTypeSymbol type, ImmutableArray <IOperation> operationBlocks, OperationBlockAnalysisContext context)
            {
                var validator = new DisposeImplementationValidator(_suppressFinalizeMethod, type);

                if (!validator.Validate(operationBlocks))
                {
                    context.ReportDiagnostic(method.CreateDiagnostic(DisposeImplementationRule, $"{type.Name}.{method.Name}"));
                }
            }
        private static void ReportMember(OperationBlockAnalysisContext context, int statementCount)
        {
            ISymbol containingMember = context.OwningSymbol.GetContainingMember();

            string   memberName = containingMember.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);
            Location location   = containingMember.Locations[0];

            context.ReportDiagnostic(Diagnostic.Create(Rule, location, containingMember.Kind, memberName, statementCount));
        }
예제 #10
0
            internal void OperationBlockEndAction(OperationBlockAnalysisContext context)
            {
                if (_lastThrowIndex >= 0 && _firstYieldIndex != int.MaxValue && _lastThrowIndex < _firstYieldIndex)
                {
                    var properties = ImmutableDictionary.Create <string, string>()
                                     .Add("Index", _lastThrowIndex.ToString(CultureInfo.InvariantCulture));

                    context.ReportDiagnostic(s_rule, properties, _symbol);
                }
            }
예제 #11
0
#pragma warning disable CA1801 // Review unused parameters
            /// <summary>
            /// Checks rule: Modify {0} so that it calls Dispose(false) and then returns.
            /// </summary>
            private static void CheckFinalizeImplementationRule(IMethodSymbol method, INamedTypeSymbol type, ImmutableArray <IOperation> operationBlocks, OperationBlockAnalysisContext context)
#pragma warning restore CA1801 // Review unused parameters
            {
                var validator = new FinalizeImplementationValidator(type);

                if (!validator.Validate(operationBlocks))
                {
                    context.ReportDiagnostic(method.CreateDiagnostic(FinalizeImplementationRule, $"{type.Name}.{method.Name}"));
                }
            }
예제 #12
0
 private void Report(
     OperationBlockAnalysisContext context,
     ILocalSymbol local,
     DiagnosticDescriptor descriptor
     )
 {
     context.ReportDiagnostic(
         Diagnostic.Create(descriptor, local.Locations.FirstOrDefault())
         );
 }
        private void AnalyzeOperationBlock(Analyzer analyzer, OperationBlockAnalysisContext context)
        {
            if (context.OperationBlocks.Length != 1)
            {
                return;
            }

            var owningSymbol = context.OwningSymbol;
            var operation    = context.OperationBlocks[0];

            var(accessesBase, hashedMembers, statements) = analyzer.GetHashedMembers(owningSymbol, operation);
            var elementCount = (accessesBase ? 1 : 0) + (hashedMembers.IsDefaultOrEmpty ? 0 : hashedMembers.Length);

            // No members to call into HashCode.Combine with.  Don't offer anything here.
            if (elementCount == 0)
            {
                return;
            }

            // Just one member to call into HashCode.Combine. Only offer this if we have multiple statements that we can
            // reduce to a single statement.  It's not worth it to offer to replace:
            //
            //      `return x.GetHashCode();` with `return HashCode.Combine(x);`
            //
            // But it is work it to offer to replace:
            //
            //      `return (a, b).GetHashCode();` with `return HashCode.Combine(a, b);`
            if (elementCount == 1 && statements.Length < 2)
            {
                return;
            }

            // We've got multiple members to hash, or multiple statements that can be reduced at this point.
            Debug.Assert(elementCount >= 2 || statements.Length >= 2);

            var syntaxTree        = operation.Syntax.SyntaxTree;
            var cancellationToken = context.CancellationToken;

            var option = context.Options.GetOption(CodeStyleOptions2.PreferSystemHashCode, operation.Language, syntaxTree, cancellationToken);

            if (option?.Value != true)
            {
                return;
            }

            var operationLocation   = operation.Syntax.GetLocation();
            var declarationLocation = context.OwningSymbol.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).GetLocation();

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         owningSymbol.Locations[0],
                                         option.Notification.Severity,
                                         new[] { operationLocation, declarationLocation },
                                         ImmutableDictionary <string, string> .Empty));
        }
예제 #14
0
 public void OperationBlockEndAction(OperationBlockAnalysisContext context)
 {
     // Check for absence of GC.SuppressFinalize
     if (!_suppressFinalizeCalled && _expectedUsage == SuppressFinalizeUsage.MustCall)
     {
         var descriptor = _containingMethodSymbol.ContainingType.HasFinalizer() ? NotCalledWithFinalizerRule : NotCalledRule;
         context.ReportDiagnostic(_containingMethodSymbol.CreateDiagnostic(
                                      descriptor,
                                      _containingMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat),
                                      _gcSuppressFinalizeMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat)));
     }
 }
예제 #15
0
            public void AnalyzeOperationBlock(OperationBlockAnalysisContext context)
            {
                foreach (KeyValuePair <ISymbol, XmlDocumentEnvironment> p in _xmlDocumentEnvironments)
                {
                    XmlDocumentEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver))
                    {
                        context.ReportDiagnostic(env.XmlDocumentDefinition.CreateDiagnostic(RuleXmlDocumentWithNoSecureResolver));
                    }
                }

                foreach (KeyValuePair <ISymbol, XmlTextReaderEnvironment> p in _xmlTextReaderEnvironments)
                {
                    XmlTextReaderEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver) ||
                        !(env.IsDtdProcessingSet | env.IsDtdProcessingDisabled))
                    {
                        context.ReportDiagnostic(env.XmlTextReaderDefinition.CreateDiagnostic(RuleXmlTextReaderConstructedWithNoSecureResolution));
                    }
                }
            }
        private void ReportDiagnosticAt([NotNull] IReturnStatement returnStatement, [NotNull] string operationName,
                                        OperationBlockAnalysisContext context)
        {
            Location location         = returnStatement.GetLocationForKeyword();
            ISymbol  containingMember = context.OwningSymbol.GetContainingMember();
            string   memberName       = containingMember.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);

            Diagnostic diagnostic = operationName == QueryOperationName
                ? Diagnostic.Create(QueryRule, location, containingMember.Kind, memberName)
                : Diagnostic.Create(OperationRule, location, containingMember.Kind, memberName, operationName);

            context.ReportDiagnostic(diagnostic);
        }
            public void AnalyzeOperationBlock(OperationBlockAnalysisContext context)
            {
                foreach (KeyValuePair <ISymbol, XmlDocumentEnvironment> p in _xmlDocumentEnvironments)
                {
                    XmlDocumentEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver))
                    {
                        Diagnostic diag = Diagnostic.Create(
                            RuleDoNotUseInsecureDtdProcessing,
                            env.XmlDocumentDefinition.GetLocation(),
                            SecurityDiagnosticHelpers.GetLocalizableResourceString(
                                nameof(MicrosoftNetFrameworkAnalyzersResources.XmlDocumentWithNoSecureResolverMessage)
                                )
                            );
                        context.ReportDiagnostic(diag);
                    }
                }


                foreach (KeyValuePair <ISymbol, XmlTextReaderEnvironment> p in _xmlTextReaderEnvironments)
                {
                    XmlTextReaderEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver) ||
                        !(env.IsDtdProcessingSet | env.IsDtdProcessingDisabled))
                    {
                        Diagnostic diag = Diagnostic.Create(
                            RuleDoNotUseInsecureDtdProcessing,
                            env.XmlTextReaderDefinition.GetLocation(),
                            SecurityDiagnosticHelpers.GetLocalizableResourceString(
                                nameof(MicrosoftNetFrameworkAnalyzersResources.XmlTextReaderConstructedWithNoSecureResolutionMessage)
                                )
                            );

                        context.ReportDiagnostic(diag);
                    }
                }
            }
            /// <summary>
            /// Checks rule: Modify {0} so that it calls Dispose(false) and then returns.
            /// </summary>
            private static void CheckFinalizeImplementationRule(IMethodSymbol method, INamedTypeSymbol type, ImmutableArray <IOperation> operationBlocks, OperationBlockAnalysisContext context)
            {
                // Bail out if any base type also provides a finalizer - we will fire CheckFinalizeOverrideRule for that case.
                if (GetFirstBaseTypeWithFinalizerOrDefault(type) != null)
                {
                    return;
                }

                var validator = new FinalizeImplementationValidator(type);

                if (!validator.Validate(operationBlocks))
                {
                    context.ReportDiagnostic(method.CreateDiagnostic(FinalizeImplementationRule, $"{type.Name}.{method.Name}"));
                }
            }
        private static void ReportDiagnosticAt([NotNull] IReturnOperation returnStatement, [NotNull] string operationName,
                                               OperationBlockAnalysisContext context)
        {
            Location location = returnStatement.TryGetLocationForKeyword();

            if (location != null)
            {
                ISymbol containingMember = context.OwningSymbol.GetContainingMember();
                string  memberName       = containingMember.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);

                (DiagnosticDescriptor rule, object[] messageArguments) =
                    GetArgumentsForReport(operationName, containingMember, memberName);

                Diagnostic diagnostic = Diagnostic.Create(rule, location, messageArguments);
                context.ReportDiagnostic(diagnostic);
            }
        }
예제 #20
0
        private void AnalyzeOperationBlock(Analyzer analyzer, OperationBlockAnalysisContext context)
        {
            if (context.OperationBlocks.Length != 1)
            {
                return;
            }

            var owningSymbol = context.OwningSymbol;
            var operation    = context.OperationBlocks[0];

            var(accessesBase, hashedMembers) = analyzer.GetHashedMembers(owningSymbol, operation);
            if (!accessesBase && hashedMembers.IsDefaultOrEmpty)
            {
                return;
            }

            var cancellationToken = context.CancellationToken;
            var optionSet         = context.Options.GetDocumentOptionSetAsync(operation.Syntax.SyntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var option = optionSet.GetOption(CodeStyleOptions.PreferSystemHashCode, operation.Language);

            if (!option.Value)
            {
                return;
            }

            var operationLocation   = operation.Syntax.GetLocation();
            var declarationLocation = context.OwningSymbol.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).GetLocation();

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         this.Descriptor,
                                         owningSymbol.Locations[0],
                                         option.Notification.Severity,
                                         new[] { operationLocation, declarationLocation },
                                         ImmutableDictionary <string, string> .Empty));
        }
예제 #21
0
            private void AnalyzeIfElseIfConstruct([NotNull] IIfStatement topIfStatement)
            {
                Location topIfKeywordLocation = topIfStatement.GetLocationForKeyword();

                IIfStatement ifStatement = topIfStatement;

                while (true)
                {
                    context.CancellationToken.ThrowIfCancellationRequested();

                    IOperation falseBlock = ifStatement.IfFalseStatement;

                    if (falseBlock == null)
                    {
                        // no else clause
                        context.ReportDiagnostic(Diagnostic.Create(Rule, topIfKeywordLocation));

                        Remove(ifStatement, ifStatementsLeftToAnalyze);
                        break;
                    }

                    var ifElseStatement = falseBlock as IIfStatement;
                    if (ifElseStatement != null)
                    {
                        // else-if
                        Remove(ifElseStatement, ifStatementsLeftToAnalyze);

                        ifStatement = ifElseStatement;
                    }
                    else
                    {
                        // unconditional else
                        break;
                    }
                }
            }
            public void AnalyzeOperationBlock(OperationBlockAnalysisContext context)
            {
                foreach (KeyValuePair<ISymbol, XmlDocumentEnvironment> p in _xmlDocumentEnvironments)
                {
                    XmlDocumentEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver))
                    {
                        Diagnostic diag = Diagnostic.Create(
                            RuleDoNotUseInsecureDtdProcessing,
                            env.XmlDocumentDefinition.GetLocation(),
                            SecurityDiagnosticHelpers.GetLocalizableResourceString(
                                nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage)
                            )
                        );
                        context.ReportDiagnostic(diag);
                    }
                }


                foreach (KeyValuePair<ISymbol, XmlTextReaderEnvironment> p in _xmlTextReaderEnvironments)
                {
                    XmlTextReaderEnvironment env = p.Value;
                    if (!(env.IsXmlResolverSet | env.IsSecureResolver) ||
                        !(env.IsDtdProcessingSet | env.IsDtdProcessingDisabled))
                    {
                        Diagnostic diag = Diagnostic.Create(
                            RuleDoNotUseInsecureDtdProcessing,
                            env.XmlTextReaderDefinition.GetLocation(),
                            SecurityDiagnosticHelpers.GetLocalizableResourceString(
                                nameof(DesktopAnalyzersResources.XmlTextReaderConstructedWithNoSecureResolutionMessage)
                            )
                        );

                        context.ReportDiagnostic(diag);
                    }
                }
            }
예제 #23
0
                private void AnalyzeUnusedValueAssignments(
                    OperationBlockAnalysisContext context,
                    bool isComputingUnusedParams,
                    PooledHashSet <SymbolUsageResult> symbolUsageResultsBuilder,
                    out bool hasBlockWithAllUsedSymbolWrites)
                {
                    hasBlockWithAllUsedSymbolWrites = false;

                    foreach (var operationBlock in context.OperationBlocks)
                    {
                        if (!ShouldAnalyze(operationBlock, context.OwningSymbol))
                        {
                            continue;
                        }

                        // First perform the fast, aggressive, imprecise operation-tree based analysis.
                        // This analysis might flag some "used" symbol writes as "unused", but will not miss reporting any truly unused symbol writes.
                        // This initial pass helps us reduce the number of methods for which we perform the slower second pass.
                        // We perform the first fast pass only if there are no delegate creations/lambda methods.
                        // This is due to the fact that tracking which local/parameter points to which delegate creation target
                        // at any given program point needs needs flow analysis (second pass).
                        if (!_hasDelegateCreationOrAnonymousFunction)
                        {
                            var resultFromOperationBlockAnalysis = SymbolUsageAnalysis.Run(operationBlock, context.OwningSymbol, context.CancellationToken);
                            if (!resultFromOperationBlockAnalysis.HasUnreadSymbolWrites())
                            {
                                // Assert that even slow pass (dataflow analysis) would have yielded no unused symbol writes.
                                Debug.Assert(!SymbolUsageAnalysis.Run(context.GetControlFlowGraph(operationBlock), context.OwningSymbol, context.CancellationToken)
                                             .HasUnreadSymbolWrites());

                                hasBlockWithAllUsedSymbolWrites = true;
                                continue;
                            }
                        }

                        // Now perform the slower, precise, CFG based dataflow analysis to identify the actual unused symbol writes.
                        var controlFlowGraph  = context.GetControlFlowGraph(operationBlock);
                        var symbolUsageResult = SymbolUsageAnalysis.Run(controlFlowGraph, context.OwningSymbol, context.CancellationToken);
                        symbolUsageResultsBuilder.Add(symbolUsageResult);

                        foreach (var(symbol, unreadWriteOperation) in symbolUsageResult.GetUnreadSymbolWrites())
                        {
                            if (unreadWriteOperation == null)
                            {
                                // Null operation is used for initial write for the parameter from method declaration.
                                // So, the initial value of the parameter is never read in this operation block.
                                // However, we do not report this as an unused parameter here as a different operation block
                                // might be reading the initial parameter value.
                                // For example, a constructor with both a constructor initializer and body will have two different operation blocks
                                // and a parameter must be unused across both these blocks to be marked unused.

                                // However, we do report unused parameters for local function here.
                                // Local function parameters are completely scoped to this operation block, and should be reported per-operation block.
                                var unusedParameter = (IParameterSymbol)symbol;
                                if (isComputingUnusedParams &&
                                    unusedParameter.ContainingSymbol.IsLocalFunction())
                                {
                                    var hasReference = symbolUsageResult.SymbolsRead.Contains(unusedParameter);
                                    _symbolStartAnalyzer.ReportUnusedParameterDiagnostic(unusedParameter, hasReference, context.ReportDiagnostic, context.Options, context.CancellationToken);
                                }

                                continue;
                            }

                            if (ShouldReportUnusedValueDiagnostic(symbol, unreadWriteOperation, symbolUsageResult, out var properties))
                            {
                                var diagnostic = DiagnosticHelper.Create(s_valueAssignedIsUnusedRule,
                                                                         _symbolStartAnalyzer._compilationAnalyzer.GetDefinitionLocationToFade(unreadWriteOperation),
                                                                         _options.UnusedValueAssignmentSeverity,
                                                                         additionalLocations: null,
                                                                         properties,
                                                                         symbol.Name);
                                context.ReportDiagnostic(diagnostic);
                            }
                        }
                    }

                    return;

                    // Local functions.
                    bool ShouldReportUnusedValueDiagnostic(
                        ISymbol symbol,
                        IOperation unreadWriteOperation,
                        SymbolUsageResult resultFromFlowAnalysis,
                        out ImmutableDictionary <string, string> properties)
                    {
                        properties = null;
                        if (_options.UnusedValueAssignmentSeverity == ReportDiagnostic.Suppress ||
                            symbol.GetSymbolType().IsErrorType())
                        {
                            return(false);
                        }

                        // Flag to indicate if the symbol has no reads.
                        var isUnusedLocalAssignment = symbol is ILocalSymbol localSymbol &&
                                                      !resultFromFlowAnalysis.SymbolsRead.Contains(localSymbol);

                        var isRemovableAssignment = IsRemovableAssignmentWithoutSideEffects(unreadWriteOperation);

                        if (isUnusedLocalAssignment &&
                            !isRemovableAssignment &&
                            _options.UnusedValueAssignmentPreference == UnusedValuePreference.UnusedLocalVariable)
                        {
                            // Meets current user preference of using unused local symbols for storing computation result.
                            // Skip reporting diagnostic.
                            return(false);
                        }

                        properties = s_propertiesMap[(_options.UnusedValueAssignmentPreference, isUnusedLocalAssignment, isRemovableAssignment)];
                        return(true);
                    }
        private void PerformFlowAnalysisOnOperationBlock(
            OperationBlockAnalysisContext operationBlockContext,
            DisposeAnalysisHelper disposeAnalysisHelper,
            ConcurrentDictionary <Location, bool> reportedLocations,
            IMethodSymbol containingMethod)
        {
            // We can skip interprocedural analysis for certain invocations.
            var interproceduralAnalysisPredicateOpt = new InterproceduralAnalysisPredicate(
                skipAnalysisForInvokedMethodPredicateOpt: SkipInterproceduralAnalysis,
                skipAnalysisForInvokedLambdaOrLocalFunctionPredicateOpt: null,
                skipAnalysisForInvokedContextPredicateOpt: null);

            // Compute dispose dataflow analysis result for the operation block.
            if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockContext, containingMethod,
                                                            _disposeObjectsBeforeLosingScopeRule,
                                                            InterproceduralAnalysisKind.ContextSensitive,
                                                            trackInstanceFields: false,
                                                            out var disposeAnalysisResult, out var pointsToAnalysisResult,
                                                            interproceduralAnalysisPredicateOpt))
            {
                var notDisposedDiagnostics = ArrayBuilder <Diagnostic> .GetInstance();

                var mayBeNotDisposedDiagnostics = ArrayBuilder <Diagnostic> .GetInstance();

                try
                {
                    // Compute diagnostics for undisposed objects at exit block.
                    var exitBlock         = disposeAnalysisResult.ControlFlowGraph.ExitBlock();
                    var disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data;
                    ComputeDiagnostics(disposeDataAtExit, notDisposedDiagnostics, mayBeNotDisposedDiagnostics,
                                       disposeAnalysisResult, pointsToAnalysisResult);

                    if (disposeAnalysisResult.ControlFlowGraph.OriginalOperation.HasAnyOperationDescendant(o => o.Kind == OperationKind.None))
                    {
                        // Workaround for https://github.com/dotnet/roslyn/issues/32100
                        // Bail out in presence of OperationKind.None - not implemented IOperation.
                        return;
                    }

                    // Report diagnostics preferring *not* disposed diagnostics over may be not disposed diagnostics
                    // and avoiding duplicates.
                    foreach (var diagnostic in notDisposedDiagnostics.Concat(mayBeNotDisposedDiagnostics))
                    {
                        if (reportedLocations.TryAdd(diagnostic.Location, true))
                        {
                            operationBlockContext.ReportDiagnostic(diagnostic);
                        }
                    }
                }
                finally
                {
                    notDisposedDiagnostics.Free();
                    mayBeNotDisposedDiagnostics.Free();
                }
            }

            return;

            // Local functions.
            bool SkipInterproceduralAnalysis(IMethodSymbol invokedMethod)
            {
                // Skip interprocedural analysis if we are invoking a method and not passing any disposable object as an argument
                // and not receiving a disposable object as a return value.
                // We also check that we are not passing any object type argument which might hold disposable object
                // and also check that we are not passing delegate type argument which can
                // be a lambda or local function that has access to disposable object in current method's scope.

                if (CanBeDisposable(invokedMethod.ReturnType))
                {
                    return(false);
                }

                foreach (var p in invokedMethod.Parameters)
                {
                    if (CanBeDisposable(p.Type))
                    {
                        return(false);
                    }
                }

                return(true);

                bool CanBeDisposable(ITypeSymbol type)
                => type.SpecialType == SpecialType.System_Object ||
                type.IsDisposable(disposeAnalysisHelper.IDisposableType) ||
                type.TypeKind == TypeKind.Delegate;
            }

            void ComputeDiagnostics(
                ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeData,
                ArrayBuilder <Diagnostic> notDisposedDiagnostics,
                ArrayBuilder <Diagnostic> mayBeNotDisposedDiagnostics,
                DisposeAnalysisResult disposeAnalysisResult,
                PointsToAnalysisResult pointsToAnalysisResult)
            {
                foreach (var kvp in disposeData)
                {
                    var location     = kvp.Key;
                    var disposeValue = kvp.Value;

                    // Ignore non-disposable locations and locations without a Creation operation.
                    if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposable ||
                        location.CreationOpt == null)
                    {
                        continue;
                    }

                    // Check if the disposable creation is definitely not disposed or may be not disposed.
                    var isNotDisposed = disposeValue.Kind == DisposeAbstractValueKind.NotDisposed ||
                                        (disposeValue.DisposingOrEscapingOperations.Count > 0 &&
                                         disposeValue.DisposingOrEscapingOperations.All(d => d.IsInsideCatchRegion(disposeAnalysisResult.ControlFlowGraph) && !location.CreationOpt.IsInsideCatchRegion(disposeAnalysisResult.ControlFlowGraph)));
                    var isMayBeNotDisposed = !isNotDisposed &&
                                             (disposeValue.Kind == DisposeAbstractValueKind.MaybeDisposed || disposeValue.Kind == DisposeAbstractValueKind.NotDisposedOrEscaped);

                    if (isNotDisposed || isMayBeNotDisposed)
                    {
                        var syntax = location.TryGetNodeToReportDiagnostic(pointsToAnalysisResult);
                        if (syntax == null)
                        {
                            continue;
                        }

                        var rule = isNotDisposed ? _disposeObjectsBeforeLosingScopeRule : _useRecommendedDisposePatternRule;

                        // Ensure that we do not include multiple lines for the object creation expression in the diagnostic message.
                        var objectCreationText = syntax.ToString();
                        var indexOfNewLine     = objectCreationText.IndexOf(Environment.NewLine);
                        if (indexOfNewLine > 0)
                        {
                            objectCreationText = objectCreationText.Substring(0, indexOfNewLine);
                        }

                        var diagnostic = Diagnostic.Create(
                            rule,
                            syntax.GetLocation(),
                            additionalLocations: null,
                            properties: null,
                            objectCreationText);

                        if (isNotDisposed)
                        {
                            notDisposedDiagnostics.Add(diagnostic);
                        }
                        else
                        {
                            mayBeNotDisposedDiagnostics.Add(diagnostic);
                        }
                    }
                }
            }
        }
 public void OperationBlockEndAction(OperationBlockAnalysisContext context)
 {
     // Report diagnostics for unused parameters.
     foreach (var parameter in _unusedParameters)
     {
         var diagnostic = Diagnostic.Create(Rule, parameter.Locations[0], parameter.Name, parameter.ContainingSymbol.Name);
         context.ReportDiagnostic(diagnostic);
     }
 }
 public void OperationBlockEndAction(OperationBlockAnalysisContext context)
 {
     // Check for absence of GC.SuppressFinalize
     if (!_suppressFinalizeCalled && _expectedUsage == SuppressFinalizeUsage.MustCall)
     {
         var descriptor = _containingMethodSymbol.ContainingType.HasFinalizer() ? NotCalledWithFinalizerRule : NotCalledRule;
         context.ReportDiagnostic(_containingMethodSymbol.CreateDiagnostic(
             descriptor,
             _containingMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat),
             _gcSuppressFinalizeMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat)));
     }
 }
예제 #27
0
 public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary <string, string?>?properties, Location location, params string[] messageArgs)
 {
     context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
 }
        private static void ReportDiagnostic(OperationBlockAnalysisContext context, params object[] messageArgs)
        {
            Diagnostic diagnostic = context.OwningSymbol.CreateDiagnostic(Rule, messageArgs);

            context.ReportDiagnostic(diagnostic);
        }
                private void AnalyzeUnusedValueAssignments(
                    OperationBlockAnalysisContext context,
                    bool isComputingUnusedParams,
                    PooledHashSet <SymbolUsageResult> symbolUsageResultsBuilder,
                    out bool hasBlockWithAllUsedSymbolWrites,
                    out bool hasOperationNoneDescendant)
                {
                    hasBlockWithAllUsedSymbolWrites = false;
                    hasOperationNoneDescendant      = false;

                    foreach (var operationBlock in context.OperationBlocks)
                    {
                        if (!ShouldAnalyze(operationBlock, context.OwningSymbol, ref hasOperationNoneDescendant))
                        {
                            continue;
                        }

                        // First perform the fast, aggressive, imprecise operation-tree based analysis.
                        // This analysis might flag some "used" symbol writes as "unused", but will not miss reporting any truly unused symbol writes.
                        // This initial pass helps us reduce the number of methods for which we perform the slower second pass.
                        // We perform the first fast pass only if there are no delegate creations/lambda methods.
                        // This is due to the fact that tracking which local/parameter points to which delegate creation target
                        // at any given program point needs needs flow analysis (second pass).
                        if (!_hasDelegateCreationOrAnonymousFunction)
                        {
                            var resultFromOperationBlockAnalysis = SymbolUsageAnalysis.Run(operationBlock, context.OwningSymbol, context.CancellationToken);
                            if (!resultFromOperationBlockAnalysis.HasUnreadSymbolWrites())
                            {
                                // Assert that even slow pass (dataflow analysis) would have yielded no unused symbol writes.
                                Debug.Assert(!SymbolUsageAnalysis.Run(context.GetControlFlowGraph(operationBlock), context.OwningSymbol, context.CancellationToken)
                                             .HasUnreadSymbolWrites());

                                hasBlockWithAllUsedSymbolWrites = true;
                                continue;
                            }
                        }

                        // Now perform the slower, precise, CFG based dataflow analysis to identify the actual unused symbol writes.
                        var controlFlowGraph  = context.GetControlFlowGraph(operationBlock);
                        var symbolUsageResult = SymbolUsageAnalysis.Run(controlFlowGraph, context.OwningSymbol, context.CancellationToken);
                        symbolUsageResultsBuilder.Add(symbolUsageResult);

                        foreach (var(symbol, unreadWriteOperation) in symbolUsageResult.GetUnreadSymbolWrites())
                        {
                            if (unreadWriteOperation == null)
                            {
                                // Null operation is used for initial write for the parameter from method declaration.
                                // So, the initial value of the parameter is never read in this operation block.
                                // However, we do not report this as an unused parameter here as a different operation block
                                // might be reading the initial parameter value.
                                // For example, a constructor with both a constructor initializer and body will have two different operation blocks
                                // and a parameter must be unused across both these blocks to be marked unused.

                                // However, we do report unused parameters for local function here.
                                // Local function parameters are completely scoped to this operation block, and should be reported per-operation block.
                                var unusedParameter = (IParameterSymbol)symbol;
                                if (isComputingUnusedParams &&
                                    unusedParameter.ContainingSymbol.IsLocalFunction())
                                {
                                    var hasReference = symbolUsageResult.SymbolsRead.Contains(unusedParameter);

                                    bool shouldReport;
                                    switch (unusedParameter.RefKind)
                                    {
                                    case RefKind.Out:
                                        // Do not report out parameters of local functions.
                                        // If they are unused in the caller, we will flag the
                                        // out argument at the local function callsite.
                                        shouldReport = false;
                                        break;

                                    case RefKind.Ref:
                                        // Report ref parameters only if they have no read/write references.
                                        // Note that we always have one write for the parameter input value from the caller.
                                        shouldReport = !hasReference && symbolUsageResult.GetSymbolWriteCount(unusedParameter) == 1;
                                        break;

                                    default:
                                        shouldReport = true;
                                        break;
                                    }

                                    if (shouldReport)
                                    {
                                        _symbolStartAnalyzer.ReportUnusedParameterDiagnostic(unusedParameter, hasReference, context.ReportDiagnostic, context.Options, context.CancellationToken);
                                    }
                                }

                                continue;
                            }

                            if (ShouldReportUnusedValueDiagnostic(symbol, unreadWriteOperation, symbolUsageResult, out var properties))
                            {
                                var diagnostic = DiagnosticHelper.Create(s_valueAssignedIsUnusedRule,
                                                                         _symbolStartAnalyzer._compilationAnalyzer.GetDefinitionLocationToFade(unreadWriteOperation),
                                                                         _options.UnusedValueAssignmentSeverity,
                                                                         additionalLocations: null,
                                                                         properties,
                                                                         symbol.Name);
                                context.ReportDiagnostic(diagnostic);
                            }
                        }
                    }

                    return;

                    // Local functions.
                    bool ShouldReportUnusedValueDiagnostic(
                        ISymbol symbol,
                        IOperation unreadWriteOperation,
                        SymbolUsageResult resultFromFlowAnalysis,
                        out ImmutableDictionary <string, string> properties)
                    {
                        Debug.Assert(!(symbol is ILocalSymbol local) || !local.IsRef);

                        properties = null;

                        // Bail out in following cases:
                        //   1. End user has configured the diagnostic to be suppressed.
                        //   2. Symbol has error type, hence the diagnostic could be noised
                        //   3. Static local symbols. Assignment to static locals
                        //      is not unnecessary as the assigned value can be used on the next invocation.
                        //   4. Ignore special discard symbol names (see https://github.com/dotnet/roslyn/issues/32923).
                        if (_options.UnusedValueAssignmentSeverity == ReportDiagnostic.Suppress ||
                            symbol.GetSymbolType().IsErrorType() ||
                            (symbol.IsStatic && symbol.Kind == SymbolKind.Local) ||
                            IsSymbolWithSpecialDiscardName(symbol))
                        {
                            return(false);
                        }

                        // Flag to indicate if the symbol has no reads.
                        var isUnusedLocalAssignment = symbol is ILocalSymbol localSymbol &&
                                                      !resultFromFlowAnalysis.SymbolsRead.Contains(localSymbol);

                        var isRemovableAssignment = IsRemovableAssignmentWithoutSideEffects(unreadWriteOperation);

                        if (isUnusedLocalAssignment &&
                            !isRemovableAssignment &&
                            _options.UnusedValueAssignmentPreference == UnusedValuePreference.UnusedLocalVariable)
                        {
                            // Meets current user preference of using unused local symbols for storing computation result.
                            // Skip reporting diagnostic.
                            return(false);
                        }

                        properties = s_propertiesMap[(_options.UnusedValueAssignmentPreference, isUnusedLocalAssignment, isRemovableAssignment)];
                        return(true);
                    }
예제 #30
0
 private void Report(OperationBlockAnalysisContext context, ILocalSymbol local, DiagnosticDescriptor descriptor)
 {
     context.ReportDiagnostic(Diagnostic.Create(descriptor, local.Locations.FirstOrDefault()));
 }
예제 #31
0
 void Report(OperationBlockAnalysisContext context, ILocalSymbol local, ITypeSymbol moreSpecificType, DiagnosticDescriptor descriptor)
 {
     context.ReportDiagnostic(Diagnostic.Create(descriptor, local.Locations.FirstOrDefault(), local, moreSpecificType));
 }
 /// <summary>
 /// Creates a new instance of the <see cref="ContextualDiagnosticReceiver{T}"/> class that accepts only <see cref="OperationBlockAnalysisContext"/>.
 /// </summary>
 /// <param name="context">Context of this <see cref="ContextualDiagnosticReceiver{T}"/>.</param>
 public static ContextualDiagnosticReceiver <OperationBlockAnalysisContext> OperationBlock(OperationBlockAnalysisContext context)
 {
     return(new ContextualDiagnosticReceiver <OperationBlockAnalysisContext>((context, diag) => context.ReportDiagnostic(diag), context));
 }
예제 #33
0
 /// <summary>
 /// Creates a new instance of the <see cref="ContextualLoggableGeneratorDiagnosticReceiver{T}"/> class that accepts only <see cref="OperationBlockAnalysisContext"/>.
 /// </summary>
 /// <param name="generator">Target <see cref="LoggableSourceGenerator"/>.</param>
 /// <param name="context">Context of this <see cref="ContextualDiagnosticReceiver{T}"/>.</param>
 public static ContextualLoggableGeneratorDiagnosticReceiver <OperationBlockAnalysisContext> OperationBlock(LoggableSourceGenerator generator, OperationBlockAnalysisContext context)
 {
     return(new ContextualLoggableGeneratorDiagnosticReceiver <OperationBlockAnalysisContext>(generator, (context, diag) => context.ReportDiagnostic(diag), context));
 }
 private static void ReportDiagnostic(OperationBlockAnalysisContext context, params object[] messageArgs)
 {
     Diagnostic diagnostic = context.OwningSymbol.CreateDiagnostic(Rule, messageArgs);
     context.ReportDiagnostic(diagnostic);
 }
 /// <summary>
 /// Checks rule: Modify {0} so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns.
 /// </summary>
 private void CheckDisposeImplementationRule(IMethodSymbol method, INamedTypeSymbol type, ImmutableArray<IOperation> operationBlocks, OperationBlockAnalysisContext context)
 {
     var validator = new DisposeImplementationValidator(_suppressFinalizeMethod, type);
     if (!validator.Validate(operationBlocks))
     {
         context.ReportDiagnostic(method.CreateDiagnostic(DisposeImplementationRule, $"{type.Name}.{method.Name}"));
     }
 }