internal void OperationBlockStart(OperationBlockStartAnalysisContext context) { if (!(context.OwningSymbol is IMethodSymbol method)) { return; } INamedTypeSymbol namedType = method.ContainingType; if (!namedType.DerivesFrom(_codeFixProviderSymbol)) { return; } context.RegisterOperationAction(operationContext => { var invocation = (IInvocationOperation)operationContext.Operation; if (invocation.TargetMethod is IMethodSymbol invocationSym && _createMethods.Contains(invocationSym)) { AddOperation(namedType, invocation, _codeActionCreateInvocations); } }, OperationKind.Invocation); context.RegisterOperationAction(operationContext => { var objectCreation = (IObjectCreationOperation)operationContext.Operation; IMethodSymbol constructor = objectCreation.Constructor; if (constructor != null && constructor.ContainingType.DerivesFrom(_codeActionSymbol)) { AddOperation(namedType, objectCreation, _codeActionObjectCreations); } }, OperationKind.ObjectCreation); }
private void OnOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext) { if (_hasErrors) { return; } operationBlockStartContext.RegisterOperationAction(_ => _hasErrors = true, OperationKind.Invalid); switch (operationBlockStartContext.OwningSymbol) { case IFieldSymbol _: // Field initializer. if (operationBlockStartContext.OperationBlocks.Length == 1 && operationBlockStartContext.OperationBlocks[0] is IFieldInitializerOperation fieldInitializer) { foreach (var field in fieldInitializer.InitializedFields) { if (!field.IsStatic && _disposableFields.Contains(field)) { // Instance field initialized with a disposable object is considered a candidate. AddOrUpdateFieldDisposedValue(field, disposed: false); } } } break; case IMethodSymbol containingMethod: // Method body. OnMethodOperationBlockStart(operationBlockStartContext, containingMethod); break; } }
internal void AnalyzeMethodBodyStart(OperationBlockStartAnalysisContext context) { var symbol = context.OwningSymbol as IMethodSymbol; if (symbol == null || !CanContainsYield(symbol)) { return; } var methodContext = new MethodContext(this, symbol); context.RegisterOperationAction(methodContext.AnalyzeYield, OperationKind.YieldReturn); context.RegisterOperationAction(methodContext.AnalyzeYield, OperationKind.YieldBreak); context.RegisterOperationAction(methodContext.AnalyzeThrow, OperationKind.Throw); context.RegisterOperationBlockEndAction(methodContext.OperationBlockEndAction); }
private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext context) { if (context.OwningSymbol is IMethodSymbol method && !method.IsAsync && Utils.IsTask(method.ReturnType)) { context.RegisterOperationAction(Utils.DebuggableWrapper(context => AnalyzerReturnOperation(context)), OperationKind.Return); } }
public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer) { if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty) { return; } // All operation blocks for a symbol belong to the same tree. var firstBlock = context.OperationBlocks[0]; if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree, firstBlock.Language, context.Options, context.CancellationToken, out var options)) { return; } var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options); context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement); context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction); context.RegisterOperationAction(blockAnalyzer.AnalyzeConversion, OperationKind.Conversion); context.RegisterOperationAction(blockAnalyzer.AnalyzeFieldOrPropertyReference, OperationKind.FieldReference, OperationKind.PropertyReference); context.RegisterOperationAction(blockAnalyzer.AnalyzeParameterReference, OperationKind.ParameterReference); context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd); return; // Local Functions. bool HasSyntaxErrors() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors()) { return(true); } } return(false); } }
public static void RegisterOperationActionInternal(this OperationBlockStartAnalysisContext context, Action <OperationAnalysisContext> analyzerOperationCallback, params OperationKind[] operationKinds) { if (!ShouldExecuteOperationAnalyzers) { return; } // No feature flag check on OperationBlockStartAnalysisContext.RegisterOperationAction, so we call it directly. context.RegisterOperationAction(analyzerOperationCallback, operationKinds); }
private void RegisterAnalyzer(OperationBlockStartAnalysisContext context, CompilationSecurityTypes types, Version frameworkVersion) { var analyzer = new OperationAnalyzer(types, frameworkVersion); context.RegisterOperationAction( analyzer.AnalyzeOperation, OperationKind.InvocationExpression, OperationKind.AssignmentExpression, OperationKind.VariableDeclaration, OperationKind.ObjectCreationExpression, OperationKind.FieldInitializerAtDeclaration ); context.RegisterOperationBlockEndAction( analyzer.AnalyzeOperationBlock ); }
private void RegisterAnalyzer(OperationBlockStartAnalysisContext context, CompilationSecurityTypes types, Version frameworkVersion) { var analyzer = new OperationAnalyzer(types, frameworkVersion); context.RegisterOperationAction( analyzer.AnalyzeOperation, OperationKind.Invocation, OperationKind.SimpleAssignment, OperationKind.VariableDeclaration, OperationKind.ObjectCreation, OperationKind.FieldInitializer ); context.RegisterOperationBlockEndAction( analyzer.AnalyzeOperationBlock ); }
private void OnOperationBlock(OperationBlockStartAnalysisContext context) { context.RegisterOperationAction(OnMethodReference, OperationKind.MethodReference); BlockAnalyzer.Analyze(context, this); }
private void OnMethodOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext, IMethodSymbol containingMethod) { // Shared PointsTo dataflow analysis result for all the callbacks to AnalyzeFieldReference // for this method's executable code. PointsToAnalysisResult lazyPointsToAnalysisResult = null; // If we have any potential disposable object creation descendant within the operation blocks, // register an operation action to analyze field references where field might be assigned a disposable object. if (_disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod)) { operationBlockStartContext.RegisterOperationAction(AnalyzeFieldReference, OperationKind.FieldReference); } // If this is a Dispose method, then analyze dispose invocations for fields within this method. if (_disposeAnalysisHelper.IsAnyDisposeMethod(containingMethod)) { AnalyzeDisposeMethod(); } return; // Local function void AnalyzeFieldReference(OperationAnalysisContext operationContext) { var fieldReference = (IFieldReferenceOperation)operationContext.Operation; var field = fieldReference.Field; // Check if this is a Disposable field that is not currently being tracked. if (_fieldDisposeValueMap.ContainsKey(field) || !_disposableFields.Contains(field) || _hasErrors) { return; } // Only track instance fields on the current instance. if (field.IsStatic || fieldReference.Instance?.Kind != OperationKind.InstanceReference) { return; } // We have a field reference for a disposable field. // Check if it is being assigned a locally created disposable object. if (fieldReference.Parent is ISimpleAssignmentOperation simpleAssignmentOperation && simpleAssignmentOperation.Target == fieldReference) { if (lazyPointsToAnalysisResult == null) { if (_disposeAnalysisHelper.TryGetOrComputeResult( operationBlockStartContext, containingMethod, s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: false, out _, out var pointsToAnalysisResult) && pointsToAnalysisResult != null) { Interlocked.CompareExchange(ref lazyPointsToAnalysisResult, pointsToAnalysisResult, null); } else { _hasErrors = true; return; } } PointsToAbstractValue assignedPointsToValue = lazyPointsToAnalysisResult[simpleAssignmentOperation.Value.Kind, simpleAssignmentOperation.Value.Syntax]; foreach (var location in assignedPointsToValue.Locations) { if (_disposeAnalysisHelper.IsDisposableCreationOrDisposeOwnershipTransfer(location, containingMethod)) { AddOrUpdateFieldDisposedValue(field, disposed: false); break; } } } } void AnalyzeDisposeMethod() { if (_hasErrors) { return; } // Perform dataflow analysis to compute dispose value of disposable fields at the end of dispose method. if (_disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext, containingMethod, s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: true, disposeAnalysisResult: out var disposeAnalysisResult, pointsToAnalysisResult: out var pointsToAnalysisResult)) { BasicBlock exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit(); foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap) { IFieldSymbol field = fieldWithPointsToValue.Key; PointsToAbstractValue pointsToValue = fieldWithPointsToValue.Value; if (!_disposableFields.Contains(field)) { continue; } ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data; var disposed = false; foreach (var location in pointsToValue.Locations) { if (disposeDataAtExit.TryGetValue(location, out DisposeAbstractValue disposeValue)) { switch (disposeValue.Kind) { // For MaybeDisposed, conservatively mark the field as disposed as we don't support path sensitive analysis. case DisposeAbstractValueKind.MaybeDisposed: case DisposeAbstractValueKind.Unknown: case DisposeAbstractValueKind.Escaped: case DisposeAbstractValueKind.Disposed: disposed = true; AddOrUpdateFieldDisposedValue(field, disposed); break; } } if (disposed) { break; } } } }
private static void AnalyzeMethod( IMethodSymbol method, OperationBlockStartAnalysisContext startOperationBlockContext, UnusedParameterDictionary unusedMethodParameters, INamedTypeSymbol eventsArgSymbol, ISet <IMethodSymbol> methodsUsedAsDelegates, ImmutableHashSet <INamedTypeSymbol> attributeSetForMethodsToIgnore) { // We only care about methods with parameters. if (method.Parameters.IsEmpty) { return; } // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat). if (method.IsImplicitlyDeclared || method.IsExtern || method.IsAbstract || method.IsVirtual || method.IsOverride || method.IsImplementationOfAnyInterfaceMember() || method.IsFinalizer()) { return; } // Ignore property accessors. if (method.IsPropertyAccessor()) { return; } // Ignore event handler methods "Handler(object, MyEventArgs)" if (eventsArgSymbol != null && method.Parameters.Length == 2 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object && method.Parameters[1].Type.Inherits(eventsArgSymbol)) { return; } // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'. if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass))) { return; } // Ignore methods that were used as delegates if (methodsUsedAsDelegates.Contains(method)) { return; } // Initialize local mutable state in the start action. var analyzer = new UnusedParametersAnalyzer(method, unusedMethodParameters); // Register an intermediate non-end action that accesses and modifies the state. startOperationBlockContext.RegisterOperationAction(analyzer.AnalyzeParameterReference, OperationKind.ParameterReference); // Register an end action to add unused parameters to the unusedMethodParameters dictionary startOperationBlockContext.RegisterOperationBlockEndAction(analyzer.OperationBlockEndAction); }
private static void AnalyzeMethod( IMethodSymbol method, OperationBlockStartAnalysisContext startOperationBlockContext, UnusedParameterDictionary unusedMethodParameters, INamedTypeSymbol?eventsArgSymbol, ISet <IMethodSymbol> methodsUsedAsDelegates, ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore) { // We only care about methods with parameters. if (method.Parameters.IsEmpty) { return; } // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat). if (method.IsImplicitlyDeclared || method.IsExtern || method.IsAbstract || method.IsVirtual || method.IsOverride || method.IsImplementationOfAnyInterfaceMember() || method.IsFinalizer()) { return; } // Ignore property accessors. if (method.IsPropertyAccessor()) { return; } // Ignore event handler methods "Handler(object, MyEventArgs)" if (method.Parameters.Length == 2 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object && // UWP has specific EventArgs not inheriting from System.EventArgs. It was decided to go for a suffix match rather than a whitelist. (method.Parameters[1].Type.Inherits(eventsArgSymbol) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal))) { return; } // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'. if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass))) { return; } // Ignore methods that were used as delegates if (methodsUsedAsDelegates.Contains(method)) { return; } // Bail out if user has configured to skip analysis for the method. if (!method.MatchesConfiguredVisibility( startOperationBlockContext.Options, Rule, startOperationBlockContext.CancellationToken, defaultRequiredVisibility: SymbolVisibilityGroup.All)) { return; } // Initialize local mutable state in the start action. var analyzer = new UnusedParametersAnalyzer(method, unusedMethodParameters); // Register an intermediate non-end action that accesses and modifies the state. startOperationBlockContext.RegisterOperationAction(analyzer.AnalyzeParameterReference, OperationKind.ParameterReference); // Register an end action to add unused parameters to the unusedMethodParameters dictionary startOperationBlockContext.RegisterOperationBlockEndAction(analyzer.OperationBlockEndAction); }
public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer) { if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty) { return; } // Bail out in presence of conditional directives // This is a workaround for https://github.com/dotnet/roslyn/issues/31820 // Issue https://github.com/dotnet/roslyn/issues/31821 tracks // reverting this workaround. if (HasConditionalDirectives()) { return; } // All operation blocks for a symbol belong to the same tree. var firstBlock = context.OperationBlocks[0]; if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree, firstBlock.Language, context.Options, context.CancellationToken, out var options)) { return; } var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options); context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement); context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction); context.RegisterOperationAction(blockAnalyzer.AnalyzeLocalOrParameterReference, OperationKind.LocalReference, OperationKind.ParameterReference); context.RegisterOperationAction(_ => blockAnalyzer._hasInvalidOperation = true, OperationKind.Invalid); context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd); return; // Local Functions. bool HasSyntaxErrors() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors()) { return(true); } } return(false); } bool HasConditionalDirectives() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true) .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n))) { return(true); } } return(false); } }
public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer) { if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty) { return; } // Bail out in presence of conditional directives // This is a workaround for https://github.com/dotnet/roslyn/issues/31820 // Issue https://github.com/dotnet/roslyn/issues/31821 tracks // reverting this workaround. if (HasConditionalDirectives()) { return; } // All operation blocks for a symbol belong to the same tree. var firstBlock = context.OperationBlocks[0]; if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree, context.Options, out var options)) { return; } // Ignore methods that are just a single-throw method. These are often // in-progress pieces of work and we don't want to force the user to fixup other // issues before they've even gotten around to writing their code. if (IsSingleThrowNotImplementedOperation(firstBlock)) { return; } var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options); context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement); context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction); context.RegisterOperationAction(blockAnalyzer.AnalyzeLocalOrParameterReference, OperationKind.LocalReference, OperationKind.ParameterReference); context.RegisterOperationAction(_ => blockAnalyzer._hasInvalidOperation = true, OperationKind.Invalid); context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd); return; // Local Functions. bool HasSyntaxErrors() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors()) { return(true); } } return(false); } bool HasConditionalDirectives() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true) .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n))) { return(true); } } return(false); }