private void RegisterAnalyzer(OperationBlockStartAnalysisContext context, CompilationSecurityTypes types, Version frameworkVersion) { var analyzer = new OperationAnalyzer(types, frameworkVersion); context.RegisterOperationAction( analyzer.AnalyzeOperation, OperationKind.InvocationExpression, OperationKind.AssignmentExpression, OperationKind.VariableDeclaration, OperationKind.ObjectCreationExpression, OperationKind.FieldInitializerAtDeclaration ); context.RegisterOperationBlockEndAction( analyzer.AnalyzeOperationBlock ); }
public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer) { if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty) { return; } // Bail out in presence of conditional directives // This is a workaround for https://github.com/dotnet/roslyn/issues/31820 // Issue https://github.com/dotnet/roslyn/issues/31821 tracks // reverting this workaround. if (HasConditionalDirectives()) { return; } // All operation blocks for a symbol belong to the same tree. var firstBlock = context.OperationBlocks[0]; if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree, firstBlock.Language, context.Options, context.CancellationToken, out var options)) { return; } // Ignore methods that are just a single-throw method. These are often // in-progress pieces of work and we don't want to force the user to fixup other // issues before they've even gotten around to writing their code. if (IsSingleThrowNotImplementedOperation(firstBlock)) { return; } var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options); context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement); context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction); context.RegisterOperationAction(blockAnalyzer.AnalyzeLocalOrParameterReference, OperationKind.LocalReference, OperationKind.ParameterReference); context.RegisterOperationAction(_ => blockAnalyzer._hasInvalidOperation = true, OperationKind.Invalid); context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd); return; // Local Functions. bool HasSyntaxErrors() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors()) { return(true); } } return(false); } bool HasConditionalDirectives() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true) .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n))) { return(true); } } return(false); }
private static void AnalyzeMethod( IMethodSymbol method, OperationBlockStartAnalysisContext startOperationBlockContext, UnusedParameterDictionary unusedMethodParameters, INamedTypeSymbol eventsArgSymbol, ISet <IMethodSymbol> methodsUsedAsDelegates, ImmutableHashSet <INamedTypeSymbol> attributeSetForMethodsToIgnore) { // We only care about methods with parameters. if (method.Parameters.IsEmpty) { return; } // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat). if (method.IsImplicitlyDeclared || method.IsExtern || method.IsAbstract || method.IsVirtual || method.IsOverride || method.IsImplementationOfAnyInterfaceMember() || method.IsFinalizer()) { return; } // Ignore property accessors. if (method.IsPropertyAccessor()) { return; } // Ignore event handler methods "Handler(object, MyEventArgs)" if (eventsArgSymbol != null && method.Parameters.Length == 2 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object && method.Parameters[1].Type.Inherits(eventsArgSymbol)) { return; } // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'. if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass))) { return; } // Ignore methods that were used as delegates if (methodsUsedAsDelegates.Contains(method)) { return; } // Bail out if user has configured to skip analysis for the method. if (!method.MatchesConfiguredVisibility( startOperationBlockContext.Options, Rule, startOperationBlockContext.CancellationToken, defaultRequiredVisibility: SymbolVisibilityGroup.All)) { return; } // Initialize local mutable state in the start action. var analyzer = new UnusedParametersAnalyzer(method, unusedMethodParameters); // Register an intermediate non-end action that accesses and modifies the state. startOperationBlockContext.RegisterOperationAction(analyzer.AnalyzeParameterReference, OperationKind.ParameterReference); // Register an end action to add unused parameters to the unusedMethodParameters dictionary startOperationBlockContext.RegisterOperationBlockEndAction(analyzer.OperationBlockEndAction); }
private void OnOperationBlock(OperationBlockStartAnalysisContext context) { context.RegisterOperationAction(OnMethodReference, OperationKind.MethodReference); BlockAnalyzer.Analyze(context, this); }
private void OnMethodOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext, IMethodSymbol containingMethod) { // Shared PointsTo dataflow analysis result for all the callbacks to AnalyzeFieldReference // for this method's executable code. PointsToAnalysisResult lazyPointsToAnalysisResult = null; // If we have any potential disposable object creation descendant within the operation blocks, // register an operation action to analyze field references where field might be assigned a disposable object. if (_disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod)) { operationBlockStartContext.RegisterOperationAction(AnalyzeFieldReference, OperationKind.FieldReference); } // If this is a Dispose method, then analyze dispose invocations for fields within this method. if (_disposeAnalysisHelper.IsAnyDisposeMethod(containingMethod)) { AnalyzeDisposeMethod(); } return; // Local function void AnalyzeFieldReference(OperationAnalysisContext operationContext) { var fieldReference = (IFieldReferenceOperation)operationContext.Operation; var field = fieldReference.Field; // Check if this is a Disposable field that is not currently being tracked. if (_fieldDisposeValueMap.ContainsKey(field) || !_disposableFields.Contains(field) || _hasErrors) { return; } // Only track instance fields on the current instance. if (field.IsStatic || fieldReference.Instance?.Kind != OperationKind.InstanceReference) { return; } // We have a field reference for a disposable field. // Check if it is being assigned a locally created disposable object. if (fieldReference.Parent is ISimpleAssignmentOperation simpleAssignmentOperation && simpleAssignmentOperation.Target == fieldReference) { if (lazyPointsToAnalysisResult == null) { if (_disposeAnalysisHelper.TryGetOrComputeResult( operationBlockStartContext, containingMethod, s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: false, out _, out var pointsToAnalysisResult) && pointsToAnalysisResult != null) { Interlocked.CompareExchange(ref lazyPointsToAnalysisResult, pointsToAnalysisResult, null); } else { _hasErrors = true; return; } } var assignedPointsToValue = lazyPointsToAnalysisResult[simpleAssignmentOperation.Value.Kind, simpleAssignmentOperation.Value.Syntax]; foreach (var location in assignedPointsToValue.Locations) { if (_disposeAnalysisHelper.IsDisposableCreationOrDisposeOwnershipTransfer(location, containingMethod)) { AddOrUpdateFieldDisposedValue(field, disposed: false); break; } } } } void AnalyzeDisposeMethod() { _hasDisposeMethod = true; if (_hasErrors) { return; } // Perform dataflow analysis to compute dispose value of disposable fields at the end of dispose method. if (_disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext, containingMethod, s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: true, disposeAnalysisResult: out var disposeAnalysisResult, pointsToAnalysisResult: out var pointsToAnalysisResult)) { var exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit(); foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap) { var field = fieldWithPointsToValue.Key; var pointsToValue = fieldWithPointsToValue.Value; if (!_disposableFields.Contains(field)) { continue; } var disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data; var disposed = false; foreach (var location in pointsToValue.Locations) { if (disposeDataAtExit.TryGetValue(location, out var disposeValue)) { switch (disposeValue.Kind) { // For MaybeDisposed, conservatively mark the field as disposed as we don't support path sensitive analysis. case DisposeAbstractValueKind.MaybeDisposed: case DisposeAbstractValueKind.Unknown: case DisposeAbstractValueKind.Escaped: case DisposeAbstractValueKind.Disposed: disposed = true; AddOrUpdateFieldDisposedValue(field, disposed); break; } } if (disposed) { break; } } } }
public StartupAnalysisContext(OperationBlockStartAnalysisContext operationBlockStartAnalysisContext, StartupSymbols startupSymbols) { OperationBlockStartAnalysisContext = operationBlockStartAnalysisContext; StartupSymbols = startupSymbols; }
public static void Analyze(OperationBlockStartAnalysisContext context, SymbolStartAnalyzer symbolStartAnalyzer) { if (HasSyntaxErrors() || context.OperationBlocks.IsEmpty) { return; } // Bail out in presence of conditional directives // This is a workaround for https://github.com/dotnet/roslyn/issues/31820 // Issue https://github.com/dotnet/roslyn/issues/31821 tracks // reverting this workaround. if (HasConditionalDirectives()) { return; } // All operation blocks for a symbol belong to the same tree. var firstBlock = context.OperationBlocks[0]; if (!symbolStartAnalyzer._compilationAnalyzer.TryGetOptions(firstBlock.Syntax.SyntaxTree, firstBlock.Language, context.Options, context.CancellationToken, out var options)) { return; } var blockAnalyzer = new BlockAnalyzer(symbolStartAnalyzer, options); context.RegisterOperationAction(blockAnalyzer.AnalyzeExpressionStatement, OperationKind.ExpressionStatement); context.RegisterOperationAction(blockAnalyzer.AnalyzeDelegateCreationOrAnonymousFunction, OperationKind.DelegateCreation, OperationKind.AnonymousFunction); context.RegisterOperationAction(blockAnalyzer.AnalyzeArgument, OperationKind.Argument); context.RegisterOperationAction(blockAnalyzer.AnalyzeConversion, OperationKind.Conversion); context.RegisterOperationAction(blockAnalyzer.AnalyzeTuple, OperationKind.Tuple); context.RegisterOperationAction(blockAnalyzer.AnalyzeFieldOrPropertyReference, OperationKind.FieldReference, OperationKind.PropertyReference); context.RegisterOperationAction(blockAnalyzer.AnalyzeParameterReference, OperationKind.ParameterReference); context.RegisterOperationBlockEndAction(blockAnalyzer.AnalyzeOperationBlockEnd); return; // Local Functions. bool HasSyntaxErrors() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.GetDiagnostics().ToImmutableArrayOrEmpty().HasAnyErrors()) { return(true); } } return(false); } bool HasConditionalDirectives() { foreach (var operationBlock in context.OperationBlocks) { if (operationBlock.Syntax.DescendantNodes(descendIntoTrivia: true) .Any(n => symbolStartAnalyzer._compilationAnalyzer.IsIfConditionalDirective(n))) { return(true); } } return(false); } }
#pragma warning disable RS1012 // Start action has no registered actions. private static bool ShouldAnalyzeMethod( IMethodSymbol method, OperationBlockStartAnalysisContext startOperationBlockContext, INamedTypeSymbol?eventsArgSymbol, ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore, INamedTypeSymbol?serializationInfoType, INamedTypeSymbol?streamingContextType) #pragma warning restore RS1012 // Start action has no registered actions. { // We only care about methods with parameters. if (method.Parameters.IsEmpty) { return(false); } // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat). if (method.IsImplicitlyDeclared || method.IsExtern || method.IsAbstract || method.IsVirtual || method.IsOverride || method.IsImplementationOfAnyInterfaceMember() || method.IsFinalizer()) { return(false); } // Ignore property accessors. if (method.IsPropertyAccessor()) { return(false); } // Ignore serialization special methods if (method.IsSerializationConstructor(serializationInfoType, streamingContextType) || method.IsGetObjectData(serializationInfoType, streamingContextType)) { return(false); } // Ignore event handler methods "Handler(object, MyEventArgs)" if (method.HasEventHandlerSignature(eventsArgSymbol)) { return(false); } // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'. if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass))) { return(false); } // Bail out if user has configured to skip analysis for the method. if (!startOperationBlockContext.Options.MatchesConfiguredVisibility( Rule, method, startOperationBlockContext.Compilation, startOperationBlockContext.CancellationToken, defaultRequiredVisibility: SymbolVisibilityGroup.All)) { return(false); } // Check to see if the method just throws a NotImplementedException/NotSupportedException // We shouldn't warn about parameters in that case if (startOperationBlockContext.IsMethodNotImplementedOrSupported()) { return(false); } // Ignore generated method for top level statements if (method.IsTopLevelStatementsEntryPointMethod()) { return(false); } return(true); }
#pragma warning disable RS1012 // Start action has no registered actions. private static bool ShouldAnalyzeMethod( IMethodSymbol method, OperationBlockStartAnalysisContext startOperationBlockContext, INamedTypeSymbol?eventsArgSymbol, ImmutableHashSet <INamedTypeSymbol?> attributeSetForMethodsToIgnore, INamedTypeSymbol?serializationInfoType, INamedTypeSymbol?streamingContextType) #pragma warning restore RS1012 // Start action has no registered actions. { // We only care about methods with parameters. if (method.Parameters.IsEmpty) { return(false); } // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat). if (method.IsImplicitlyDeclared || method.IsExtern || method.IsAbstract || method.IsVirtual || method.IsOverride || method.IsImplementationOfAnyInterfaceMember() || method.IsFinalizer()) { return(false); } // Ignore property accessors. if (method.IsPropertyAccessor()) { return(false); } // Ignore serialization special methods if (method.IsSerializationConstructor(serializationInfoType, streamingContextType) || method.IsGetObjectData(serializationInfoType, streamingContextType)) { return(false); } // Ignore event handler methods "Handler(object, MyEventArgs)" if (method.Parameters.Length == 2 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object && // UWP has specific EventArgs not inheriting from System.EventArgs. It was decided to go for a suffix match rather than a whitelist. (method.Parameters[1].Type.Inherits(eventsArgSymbol) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal))) { return(false); } // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'. if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass))) { return(false); } // Bail out if user has configured to skip analysis for the method. if (!method.MatchesConfiguredVisibility( startOperationBlockContext.Options, Rule, startOperationBlockContext.Compilation, startOperationBlockContext.CancellationToken, defaultRequiredVisibility: SymbolVisibilityGroup.All)) { return(false); } // Check to see if the method just throws a NotImplementedException/NotSupportedException // We shouldn't warn about parameters in that case if (startOperationBlockContext.IsMethodNotImplementedOrSupported()) { return(false); } return(true); }