internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context) { if (!EnabledConcurrentExecution) { context.ReportDiagnostic(Diagnostic.Create(Rule, _analysisContextParameter.Locations.FirstOrDefault())); } }
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)); } }
internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context) { if (!ConfiguredGeneratedCodeAnalysis) { context.ReportDiagnostic(Diagnostic.Create(Rule, _analysisContextParameter.Locations.FirstOrDefault())); } }
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)); }
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); } }
#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}")); } }
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)); }
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))); } }
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); } }
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)); }
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); } } }
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 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); }
private void Report(OperationBlockAnalysisContext context, ILocalSymbol local, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, local.Locations.FirstOrDefault())); }
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)); }
/// <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)); }
/// <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}")); } }