private static bool ShouldCallBase(IMethodSymbol method, MethodDeclarationSyntax methodDeclaration, SyntaxNodeAnalysisContext context) { if (method.Parameters.TrySingle(out var parameter) && parameter.Type == KnownSymbol.Boolean && method.IsOverride && method.OverriddenMethod is IMethodSymbol overridden && !DisposeMethod.TryFindBaseCall(methodDeclaration, context.SemanticModel, context.CancellationToken, out _)) { if (overridden.DeclaringSyntaxReferences.Length == 0) { return(true); } else { using (var disposeWalker = DisposeWalker.Borrow(overridden, context.SemanticModel, context.CancellationToken)) { foreach (var disposeCall in disposeWalker) { if (DisposeCall.TryGetDisposed(disposeCall, context.SemanticModel, context.CancellationToken, out var disposed) && FieldOrProperty.TryCreate(disposed, out var fieldOrProperty) && !DisposableMember.IsDisposed(fieldOrProperty, method, context.SemanticModel, context.CancellationToken)) { return(true); } } } } } return(false); }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is InvocationExpressionSyntax invocation && DisposeCall.IsIDisposableDispose(invocation, context.SemanticModel, context.CancellationToken) && !invocation.TryFirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(out _) && DisposeCall.TryGetDisposedRootMember(invocation, context.SemanticModel, context.CancellationToken, out var root)) { if (Disposable.IsCachedOrInjected(root, invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP007DontDisposeInjected.Descriptor, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation())); } else if (context.SemanticModel.TryGetSymbol(root, context.CancellationToken, out ILocalSymbol local)) { if (IsUsedAfter(local, invocation, context, out var locations)) { context.ReportDiagnostic(Diagnostic.Create(IDISP016DontUseDisposedInstance.Descriptor, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation(), additionalLocations: locations)); } if (IsPreferUsing(local, invocation, context)) { context.ReportDiagnostic(Diagnostic.Create(IDISP017PreferUsing.Descriptor, invocation.GetLocation())); } } } }
internal static bool IsDisposedAfter(ISymbol local, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken) { if (location.FirstAncestorOrSelf <MemberDeclarationSyntax>() is MemberDeclarationSyntax scope) { using (var walker = InvocationWalker.Borrow(scope)) { foreach (var invocation in walker.Invocations) { if (location.IsExecutedBefore(invocation).IsEither(ExecutedBefore.Maybe, ExecutedBefore.Yes) && DisposeCall.IsDisposing(invocation, local, semanticModel, cancellationToken)) { return(true); } } } using (var walker = UsingStatementWalker.Borrow(scope)) { foreach (var usingStatement in walker.UsingStatements) { if (location.IsExecutedBefore(usingStatement) == ExecutedBefore.Yes && usingStatement.Expression is IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == local.Name) { return(true); } } } } return(false); }
private static void ReplaceWithUsing(DocumentEditor editor, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { if (DisposeCall.TryGetDisposedRootMember(invocation, editor.SemanticModel, cancellationToken, out var root) && editor.SemanticModel.TryGetSymbol(root, cancellationToken, out ILocalSymbol local) && local.TrySingleDeclaration(cancellationToken, out VariableDeclarationSyntax declaration) && invocation.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement) && declaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement) { if (expressionStatement.Parent is BlockSyntax finallyBlock && finallyBlock.Parent is FinallyClauseSyntax finallyClause && finallyClause.Parent is TryStatementSyntax tryStatement && !tryStatement.Catches.Any()) { if (declaration.Variables.TrySingle(out var declarator) && declarator.Initializer?.Value.IsKind(SyntaxKind.NullLiteralExpression) == true && tryStatement.Block.Statements.TryFirst(out var statement) && statement is ExpressionStatementSyntax assignExpressionStatement && assignExpressionStatement.Expression is AssignmentExpressionSyntax assignment) { editor.ReplaceNode( tryStatement, SyntaxFactory.UsingStatement( SyntaxFactory.VariableDeclaration( SyntaxFactory.ParseTypeName("var"), SyntaxFactory.SingletonSeparatedList( declarator.WithInitializer(SyntaxFactory.EqualsValueClause(assignment.Right)))), null, tryStatement.Block.WithStatements(tryStatement.Block.Statements.RemoveAt(0)))); editor.RemoveNode(localDeclarationStatement); }
internal Result IsMemberDisposed(ISymbol member) { foreach (var invocation in this.invocations) { if (DisposeCall.IsDisposing(invocation, member, this.SemanticModel, this.CancellationToken)) { return(Result.Yes); } } if (member is IPropertySymbol property && property.OverriddenProperty is IPropertySymbol overridden) { return(this.IsMemberDisposed(overridden)); } foreach (var name in this.identifiers) { if (member.Name == name.Identifier.ValueText && this.SemanticModel.TryGetSymbol(name, this.CancellationToken, out ISymbol candidate) && member.Equals(candidate)) { return(Result.AssumeYes); } } return(Result.No); }
internal static bool IsDisposed(FieldOrProperty member, MethodDeclarationSyntax disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken) { using var walker = DisposeWalker.Borrow(disposeMethod, semanticModel, cancellationToken); if (Disposable.IsAssignableFrom(member.Type, semanticModel.Compilation)) { foreach (var candidate in walker.Invocations) { if (DisposeCall.IsDisposing(candidate, member.Symbol, semanticModel, cancellationToken)) { return(true); } } } foreach (var candidate in walker.Identifiers) { if (candidate.Identifier.Text == member.Name && semanticModel.TryGetSymbol(candidate, cancellationToken, out var candidateSymbol) && candidateSymbol.OriginalDefinition.Equals(member.Symbol)) { return(true); } } return(false); }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is InvocationExpressionSyntax invocation && DisposeCall.IsMatchAny(invocation, context.SemanticModel, context.CancellationToken) && !invocation.TryFirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(out _) && DisposeCall.TryGetDisposedRootMember(invocation, context.SemanticModel, context.CancellationToken, out var root)) { if (Disposable.IsCachedOrInjectedOnly(root, invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP007DoNotDisposeInjected, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation())); } if (invocation.Expression is MemberAccessExpressionSyntax { Expression : IdentifierNameSyntax _ } &&
internal static bool IsDisposed(FieldOrProperty member, IMethodSymbol disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken) { if (disposeMethod == null) { return(false); } using (var walker = DisposeWalker.Borrow(disposeMethod, semanticModel, cancellationToken)) { foreach (var invocation in walker) { if (DisposeCall.IsDisposing(invocation, member.Symbol, semanticModel, cancellationToken)) { return(true); } } } return(false); }
internal static bool IsDisposedBefore(ISymbol symbol, ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken) { if (TryGetScope(expression, out var block)) { using var walker = InvocationWalker.Borrow(block); foreach (var invocation in walker.Invocations) { if (invocation.IsExecutedBefore(expression) == ExecutedBefore.No) { continue; } if (DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) && !IsReassignedAfter(block, invocation)) { return(true); } } } if (expression is AssignmentExpressionSyntax { Left : { } left } &&
internal static bool IsDisposedBefore(ISymbol symbol, ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken) { if (TryGetScope(expression, out var block)) { using (var walker = InvocationWalker.Borrow(block)) { foreach (var invocation in walker.Invocations) { if (invocation.IsExecutedBefore(expression) == ExecutedBefore.No) { continue; } if (DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) && !IsReassignedAfter(block, invocation)) { return(true); } } } } if (expression is AssignmentExpressionSyntax assignmentExpression && semanticModel.GetSymbolSafe(assignmentExpression.Left, cancellationToken) is IPropertySymbol property && property.TryGetSetter(cancellationToken, out var setter)) { using (var pooled = InvocationWalker.Borrow(setter)) { foreach (var invocation in pooled.Invocations) { if ((DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) || DisposeCall.IsDisposing(invocation, property, semanticModel, cancellationToken)) && !IsReassignedAfter(setter, invocation)) { return(true); } } } } return(false); bool TryGetScope(SyntaxNode node, out BlockSyntax result) { result = null; if (node.FirstAncestor <AnonymousFunctionExpressionSyntax>() is AnonymousFunctionExpressionSyntax lambda) { result = lambda.Body as BlockSyntax; } else if (node.FirstAncestor <AccessorDeclarationSyntax>() is AccessorDeclarationSyntax accessor) { result = accessor.Body; } else if (node.FirstAncestor <BaseMethodDeclarationSyntax>() is BaseMethodDeclarationSyntax method) { result = method.Body; } return(result != null); } bool IsReassignedAfter(SyntaxNode scope, InvocationExpressionSyntax disposeCall) { using (var walker = MutationWalker.Borrow(scope, Scope.Member, semanticModel, cancellationToken)) { foreach (var mutation in walker.All()) { if (mutation.TryFirstAncestor(out StatementSyntax statement) && disposeCall.IsExecutedBefore(statement) == ExecutedBefore.Yes && statement.IsExecutedBefore(expression) == ExecutedBefore.Yes) { return(true); } } } return(false); } }
internal static bool IsIgnored(ExpressionSyntax node, SemanticModel semanticModel, CancellationToken cancellationToken) { if (node.Parent is AnonymousFunctionExpressionSyntax || node.Parent is UsingStatementSyntax || node.Parent is EqualsValueClauseSyntax || node.Parent is ReturnStatementSyntax || node.Parent is ArrowExpressionClauseSyntax) { return(false); } if (node.Parent is StatementSyntax) { return(true); } if (node.Parent is AssignmentExpressionSyntax assignment && assignment.Left is IdentifierNameSyntax left && left.Identifier.ValueText == "_") { return(true); } if (node.Parent is ArgumentSyntax argument) { if (argument.Parent is ArgumentListSyntax argumentList && argumentList.Parent is InvocationExpressionSyntax invocation && semanticModel.TryGetSymbol(invocation, cancellationToken, out var method) && method.Name == "Add" && method.ContainingType.IsAssignableTo(KnownSymbol.IEnumerable, semanticModel.Compilation)) { if (method.ContainingType == KnownSymbol.CompositeDisposable) { return(false); } if (!method.ContainingType.TypeArguments.Any(x => x.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation))) { if (MemberPath.TryFindRoot(invocation, out var identifierName) && semanticModel.TryGetSymbol(identifierName, cancellationToken, out ISymbol symbol) && FieldOrProperty.TryCreate(symbol, out var fieldOrProperty) && argument.TryFirstAncestor(out TypeDeclarationSyntax typeDeclaration) && DisposableMember.IsDisposed(fieldOrProperty, typeDeclaration, semanticModel, cancellationToken) != Result.No) { return(false); } return(true); } return(false); } return(IsArgumentDisposedByReturnValue(argument, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo) && IsArgumentAssignedToDisposable(argument, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo)); } if (node.Parent is MemberAccessExpressionSyntax memberAccess) { if (memberAccess.Parent is InvocationExpressionSyntax invocation && DisposeCall.IsIDisposableDispose(invocation, semanticModel, cancellationToken)) { return(false); } return(IsArgumentDisposedByInvocationReturnValue(memberAccess, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo)); } return(false); }
internal static bool IsReturned(ISymbol symbol, SyntaxNode scope, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var walker = ReturnValueWalker.Borrow(scope, ReturnValueSearch.TopLevel, semanticModel, cancellationToken)) { foreach (var value in walker) { var candidate = value; switch (candidate) { case CastExpressionSyntax castExpression: candidate = castExpression.Expression; break; case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.AsExpression): candidate = binary.Left; break; } if (candidate is ObjectCreationExpressionSyntax objectCreation) { if (objectCreation.ArgumentList != null) { foreach (var argument in objectCreation.ArgumentList.Arguments) { if (semanticModel.TryGetSymbol(argument.Expression, cancellationToken, out ISymbol argumentSymbol) && symbol.Equals(argumentSymbol)) { return(true); } } } if (objectCreation.Initializer != null) { foreach (var expression in objectCreation.Initializer.Expressions) { if (semanticModel.TryGetSymbol(expression, cancellationToken, out ISymbol expressionSymbol) && symbol.Equals(expressionSymbol)) { return(true); } } } } if (semanticModel.TryGetSymbol(candidate, cancellationToken, out ISymbol returnedSymbol) && symbol.Equals(returnedSymbol)) { return(true); } if (candidate is InvocationExpressionSyntax invocation) { if (returnedSymbol == KnownSymbol.RxDisposable.Create && invocation.ArgumentList != null && invocation.ArgumentList.Arguments.TrySingle(out var argument) && argument.Expression is ParenthesizedLambdaExpressionSyntax lambda) { var body = lambda.Body; using (var pooledInvocations = InvocationWalker.Borrow(body)) { foreach (var disposeCandidate in pooledInvocations.Invocations) { if (DisposeCall.IsDisposing(disposeCandidate, symbol, semanticModel, cancellationToken)) { return(true); } } } } } } } return(false); }