private void AnalyzeOperationBlock(OperationBlockAnalysisContext context, INamedTypeSymbol iDisposableTypeSymbol) { if (context.OwningSymbol.Kind != SymbolKind.Method) { return; } var method = (IMethodSymbol)context.OwningSymbol; // We are only interested in private explicit interface implementations within a public non-sealed type. if (method.ExplicitInterfaceImplementations.Length == 0 || method.GetResultantVisibility() != SymbolVisibility.Private || method.ContainingType.IsSealed || method.ContainingType.GetResultantVisibility() != SymbolVisibility.Public) { return; } // Avoid false reports from simple explicit implementations where the deriving type is not expected to access the base implementation. if (ShouldExcludeOperationBlock(context.OperationBlocks)) { return; } var hasPublicInterfaceImplementation = false; foreach (IMethodSymbol interfaceMethod in method.ExplicitInterfaceImplementations) { // If any one of the explicitly implemented interface methods has a visible alternate, then effectively, they all do. if (HasVisibleAlternate(method.ContainingType, interfaceMethod, iDisposableTypeSymbol)) { return; } hasPublicInterfaceImplementation = hasPublicInterfaceImplementation || interfaceMethod.ContainingType.GetResultantVisibility() == SymbolVisibility.Public; } // Even if none of the interface methods have alternates, there's only an issue if at least one of the interfaces is public. if (hasPublicInterfaceImplementation) { ReportDiagnostic(context, method.ContainingType.Name, method.Name); } }
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, s_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.GetExit(); 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 ? s_disposeObjectsBeforeLosingScopeRule : s_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); } } } } }
protected override NonCopyableWalker CreateWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) => new CSharpNonCopyableWalker(context, cache);
#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}")); } }
public void OperationBlockEndAction(OperationBlockAnalysisContext context) { // Check to see if the method just throws a NotImplementedException/NotSupportedException. If it does, // we shouldn't warn about parameters. // Note that VB method bodies with 1 action have 3 operations. // The first is the actual operation, the second is a label statement, and the third is a return // statement. The last two are implicit in these scenarios. // Filter out operation roots with no IOperation API support (OperationKind.None) var operationBlocks = context.OperationBlocks; if (operationBlocks.Any(operation => operation.IsOperationNoneRoot())) { operationBlocks = operationBlocks.Where(operation => !operation.IsOperationNoneRoot()).ToImmutableArray(); } if (operationBlocks.Length == 1 && operationBlocks[0] is IBlockOperation methodBlock) { bool IsSingleStatementBody(IBlockOperation body) { return(body.Operations.Length == 1 || (body.Operations.Length == 3 && body.Syntax.Language == LanguageNames.VisualBasic && body.Operations[1] is ILabeledOperation labeledOp && labeledOp.IsImplicit && body.Operations[2] is IReturnOperation returnOp && returnOp.IsImplicit)); } if (IsSingleStatementBody(methodBlock)) { var innerOperation = methodBlock.Operations.First(); // Because of https://github.com/dotnet/roslyn/issues/23152, there can be an expression-statement // wrapping expression-bodied throw operations. Compensate by unwrapping if necessary. if (innerOperation.Kind == OperationKind.ExpressionStatement && innerOperation is IExpressionStatementOperation exprStatement) { innerOperation = exprStatement.Operation; } if (innerOperation.Kind == OperationKind.Throw && innerOperation is IThrowOperation throwOperation && throwOperation.Exception.Kind == OperationKind.ObjectCreation && throwOperation.Exception is IObjectCreationOperation createdException) { if (_exceptionsToSkip.Contains(createdException.Type.OriginalDefinition)) { return; } } } } // Do not raise warning for unused 'this' parameter of an extension method. if (_method.IsExtensionMethod) { var thisParamter = _unusedParameters.Where(p => p.Ordinal == 0).FirstOrDefault(); _unusedParameters.Remove(thisParamter); } _finalUnusedParameters.Add(_method, _unusedParameters); }
private void ProcessOperationBlock(OperationBlockAnalysisContext obj) { }
/// <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 ReportDiagnostic(OperationBlockAnalysisContext context, params object[] messageArgs) { Diagnostic diagnostic = context.OwningSymbol.CreateDiagnostic(Rule, messageArgs); context.ReportDiagnostic(diagnostic); }
private void Report(OperationBlockAnalysisContext context, ILocalSymbol local, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, local.Locations.FirstOrDefault())); }
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); }
public ReturnValueAnalyzer(OperationBlockAnalysisContext context) { this.context = context; }
public ReturnStatementCollector([NotNull] SequenceTypeInfo sequenceTypeInfo, OperationBlockAnalysisContext context) { Guard.NotNull(sequenceTypeInfo, nameof(sequenceTypeInfo)); this.sequenceTypeInfo = sequenceTypeInfo; this.context = 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)); }
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 ) ); }
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); }
public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ISymbol symbol, params string[] messageArgs) { ReportDiagnostic(context, descriptor, ImmutableDictionary <string, string> .Empty, symbol, messageArgs); }
/// <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}")); } }
void Report(OperationBlockAnalysisContext context, ILocalSymbol local, ITypeSymbol moreSpecificType, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, local.Locations.FirstOrDefault(), local, moreSpecificType)); }
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 TrimDataFlowAnalysis(OperationBlockAnalysisContext context, ControlFlowGraph cfg) { ControlFlowGraph = new ControlFlowGraphProxy(cfg); Lattice = new (new ValueSetLattice <SingleValue> ()); Context = context; }
// CA1801: Remove unused parameters. #pragma warning disable CA1801 /// <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 { // TODO: Implement check of Finalize }
public LocalDataFlowVisitor(LocalStateLattice <TValue, TValueLattice> lattice, OperationBlockAnalysisContext context, ImmutableDictionary <CaptureId, FlowCaptureKind> lValueFlowCaptures) => (LocalStateLattice, Context, this.lValueFlowCaptures) = (lattice, context, lValueFlowCaptures);
/// <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)); }
public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken token, params string[] messageArgs) { ReportDiagnostic(context, descriptor, ImmutableDictionary <string, string?> .Empty, token.GetLocation(), messageArgs); }
// CA1801: Remove unused parameters. #pragma warning disable CA1801 /// <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 { // TODO: Implement check of Finalize }
public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary <string, string?>?properties, SyntaxNode syntax, params string[] messageArgs) { ReportDiagnostic(context, descriptor, properties, syntax.GetLocation(), messageArgs); }
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)); }
public CSharpNonCopyableWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) : base(context, cache) { }
public ReturnValueAnalyzer(OperationBlockAnalysisContext context, [NotNull] IDictionary <ILocalSymbol, EvaluationResult> variableEvaluationCache) { this.context = context; this.variableEvaluationCache = variableEvaluationCache; }
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); } }
private void AnalyzeReturnStatement([NotNull] IReturnStatement returnStatement, OperationBlockAnalysisContext context, [NotNull] IDictionary <ILocalSymbol, EvaluationResult> variableEvaluationCache) { if (!ReturnsConstant(returnStatement) && !IsYieldBreak(returnStatement)) { var analyzer = new ReturnValueAnalyzer(context, variableEvaluationCache); analyzer.Analyze(returnStatement); } }
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); } } }
/// <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}")); } }
public static bool IsMethodNotImplementedOrSupported(this OperationBlockAnalysisContext context) { // Note that VB method bodies with 1 action have 3 operations. // The first is the actual operation, the second is a label statement, and the third is a return // statement. The last two are implicit in these scenarios. var operationBlocks = context.OperationBlocks.WhereAsArray(operation => !operation.IsOperationNoneRoot()); IBlockOperation methodBlock = null; if (operationBlocks.Length == 1 && operationBlocks[0].Kind == OperationKind.Block) { methodBlock = (IBlockOperation)operationBlocks[0]; } else if (operationBlocks.Length > 1) { foreach (var block in operationBlocks) { if (block.Kind == OperationKind.Block) { methodBlock = (IBlockOperation)block; break; } } } if (methodBlock != null) { bool IsSingleStatementBody(IBlockOperation body) { return(body.Operations.Length == 1 || (body.Operations.Length == 3 && body.Syntax.Language == LanguageNames.VisualBasic && body.Operations[1] is ILabeledOperation labeledOp && labeledOp.IsImplicit && body.Operations[2] is IReturnOperation returnOp && returnOp.IsImplicit)); } if (IsSingleStatementBody(methodBlock)) { var innerOperation = methodBlock.Operations.First(); // Because of https://github.com/dotnet/roslyn/issues/23152, there can be an expression-statement // wrapping expression-bodied throw operations. Compensate by unwrapping if necessary. if (innerOperation.Kind == OperationKind.ExpressionStatement && innerOperation is IExpressionStatementOperation exprStatement) { innerOperation = exprStatement.Operation; } if (innerOperation.Kind == OperationKind.Throw && innerOperation is IThrowOperation throwOperation && throwOperation.Exception.Kind == OperationKind.ObjectCreation && throwOperation.Exception is IObjectCreationOperation createdException) { if (Equals(WellKnownTypes.NotImplementedException(context.Compilation), createdException.Type.OriginalDefinition) || Equals(WellKnownTypes.NotSupportedException(context.Compilation), createdException.Type.OriginalDefinition)) { return(true); } } } } return(false); }
private void AnalyzeOperationBlock(OperationBlockAnalysisContext context) { var method = context.OwningSymbol as IMethodSymbol; if (method == null) { return; } bool isFinalizerMethod = method.IsFinalizer(); bool isDisposeMethod = method.Name == DisposeMethodName; if (isFinalizerMethod || isDisposeMethod) { INamedTypeSymbol type = method.ContainingType; if (type != null && type.TypeKind == TypeKind.Class && !type.IsSealed && type.DeclaredAccessibility != Accessibility.Private) { if (ImplementsDisposableDirectly(type)) { IMethodSymbol disposeMethod = FindDisposeMethod(type); if (disposeMethod != null) { if (method == disposeMethod) { CheckDisposeImplementationRule(method, type, context.OperationBlocks, context); } else if (isFinalizerMethod) { // Check implementation of finalizer only if the class explicitly implements IDisposable // If class implements interface inherited from IDisposable and IDisposable is implemented in base class // then implementation of finalizer is ignored CheckFinalizeImplementationRule(method, type, context.OperationBlocks, context); } } } } } }
public LocalDataFlowVisitor(LocalStateLattice <TValue, TValueLattice> lattice, OperationBlockAnalysisContext context) => (LocalStateLattice, Context) = (lattice, context);