internal static bool IsAssignedAndDisposedInSetupAndTearDown(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken) { if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, Scope.Member, semanticModel, cancellationToken, out var assignment) && assignment.FirstAncestor <MethodDeclarationSyntax>() is MethodDeclarationSyntax methodDeclaration) { if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitSetUpAttribute, semanticModel, cancellationToken, out _)) { if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive(x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitTearDownAttribute), out var tearDown)) { return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken)); } } if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitOneTimeSetUpAttribute, semanticModel, cancellationToken, out _)) { if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive( x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitOneTimeTearDownAttribute), out var tearDown)) { return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken)); } } } return(false); }
private static void HandleFieldOrProperty(SyntaxNodeAnalysisContext context, FieldOrProperty fieldOrProperty) { using (var assignedValues = AssignedValueWalker.Borrow(fieldOrProperty.Symbol, context.SemanticModel, context.CancellationToken)) { using (var recursive = RecursiveValues.Borrow(assignedValues, context.SemanticModel, context.CancellationToken)) { if (Disposable.IsAnyCreation(recursive, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes)) { if (Disposable.IsAnyCachedOrInjected(recursive, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) || IsMutableFromOutside(fieldOrProperty)) { context.ReportDiagnostic(Diagnostic.Create(IDISP008DontMixInjectedAndCreatedForMember.Descriptor, context.Node.GetLocation())); } else if (context.Node.TryFirstAncestorOrSelf <TypeDeclarationSyntax>(out var typeDeclaration) && DisposableMember.IsDisposed(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken).IsEither(Result.No, Result.AssumeNo) && !TestFixture.IsAssignedAndDisposedInSetupAndTearDown(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP002DisposeMember.Descriptor, context.Node.GetLocation())); if (!DisposeMethod.TryFindFirst(fieldOrProperty.ContainingType, context.Compilation, Search.TopLevel, out _) && !TestFixture.IsAssignedInSetUp(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken, out _)) { context.ReportDiagnostic(Diagnostic.Create(IDISP006ImplementIDisposable.Descriptor, context.Node.GetLocation())); } } } } } }
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); }
internal static Result IsArgumentDisposedByReturnValue(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <SyntaxNode> visited = null) { if (argument?.Parent is ArgumentListSyntax argumentList) { if (argumentList.Parent is InvocationExpressionSyntax invocation && semanticModel.GetSymbolSafe(invocation, cancellationToken) is IMethodSymbol method) { if (method.ContainingType.DeclaringSyntaxReferences.Length == 0) { return(method.ReturnsVoid || !IsAssignableFrom(method.ReturnType, semanticModel.Compilation) ? Result.No : Result.AssumeYes); } if (method.TryFindParameter(argument, out var parameter)) { return(CheckReturnValues(parameter, invocation, semanticModel, cancellationToken, visited)); } return(Result.Unknown); } if (argumentList.Parent is ObjectCreationExpressionSyntax || argumentList.Parent is ConstructorInitializerSyntax) { if (TryGetAssignedFieldOrProperty(argument, semanticModel, cancellationToken, out var member, out var ctor) && FieldOrProperty.TryCreate(member, out var fieldOrProperty)) { var initializer = argument.FirstAncestorOrSelf <ConstructorInitializerSyntax>(); if (initializer != null) { if (semanticModel.GetDeclaredSymbolSafe(initializer.Parent, cancellationToken) is IMethodSymbol chainedCtor && chainedCtor.ContainingType != member.ContainingType) { if (DisposeMethod.TryFindFirst(chainedCtor.ContainingType, semanticModel.Compilation, Search.TopLevel, out var disposeMethod)) { return(DisposableMember.IsDisposed(fieldOrProperty, disposeMethod, semanticModel, cancellationToken) ? Result.Yes : Result.No); } } } return(DisposableMember.IsDisposed(fieldOrProperty, ctor.ContainingType, semanticModel, cancellationToken)); } if (ctor == null) { return(Result.AssumeYes); } if (ctor.ContainingType.DeclaringSyntaxReferences.Length == 0) { return(IsAssignableFrom(ctor.ContainingType, semanticModel.Compilation) ? Result.AssumeYes : Result.No); } if (ctor.ContainingType.IsAssignableTo(KnownSymbol.NinjectStandardKernel, semanticModel.Compilation)) { return(Result.Yes); } return(Result.No); } } return(Result.Unknown); }
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); }