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 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); }