private static void HandleAssignment(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } var assignment = (AssignmentExpressionSyntax)context.Node; if (Disposable.IsCreation(assignment.Right, context.SemanticModel, context.CancellationToken) .IsEither(Result.No, Result.Unknown)) { return; } if (Disposable.IsAssignedWithCreated(assignment.Left, context.SemanticModel, context.CancellationToken, out ISymbol assignedSymbol) .IsEither(Result.No, Result.Unknown)) { return; } if (assignedSymbol == KnownSymbol.SerialDisposable.Disposable) { return; } if (IsDisposedBefore(assignedSymbol, assignment, context.SemanticModel, context.CancellationToken)) { return; } context.ReportDiagnostic(Diagnostic.Create(Descriptor, assignment.GetLocation())); }
private static void HandleDeclaration(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } var variableDeclaration = (VariableDeclarationSyntax)context.Node; foreach (var declarator in variableDeclaration.Variables) { var value = declarator.Initializer?.Value; if (value == null || !Disposable.IsPotentiallyAssignableTo(value, context.SemanticModel, context.CancellationToken)) { continue; } if (Disposable.IsCreation(value, context.SemanticModel, context.CancellationToken) .IsEither(Result.Yes, Result.Maybe)) { if (variableDeclaration.Parent is UsingStatementSyntax || variableDeclaration.Parent is AnonymousFunctionExpressionSyntax) { return; } var block = declarator.FirstAncestorOrSelf <BlockSyntax>(); if (block == null) { return; } if (context.SemanticModel.GetDeclaredSymbolSafe(declarator, context.CancellationToken) is ILocalSymbol local) { if (IsReturned(local, block, context.SemanticModel, context.CancellationToken)) { return; } if (IsAssignedToFieldOrProperty(local, block, context.SemanticModel, context.CancellationToken)) { return; } if (IsAddedToFieldOrProperty(local, block, context.SemanticModel, context.CancellationToken)) { return; } if (IsDisposedAfter(local, value, context.SemanticModel, context.CancellationToken)) { return; } } context.ReportDiagnostic(Diagnostic.Create(Descriptor, variableDeclaration.GetLocation())); } } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (context.ContainingSymbol is IMethodSymbol method && context.Node is MethodDeclarationSyntax methodDeclaration && Disposable.IsAssignableFrom(method.ReturnType, context.Compilation)) { using var walker = ReturnValueWalker.Borrow(methodDeclaration, ReturnValueSearch.RecursiveInside, context.SemanticModel, context.CancellationToken); if (walker.TryFirst(x => IsCreated(x), out _) && walker.TryFirst(x => IsCachedOrInjected(x) && !IsNop(x), out _)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP015DoNotReturnCachedAndCreated, methodDeclaration.Identifier.GetLocation())); } } bool IsCreated(ExpressionSyntax expression) { return(Disposable.IsCreation(expression, context.SemanticModel, context.CancellationToken) == Result.Yes); } bool IsCachedOrInjected(ExpressionSyntax expression) { return(Disposable.IsCachedOrInjected(expression, expression, context.SemanticModel, context.CancellationToken)); } bool IsNop(ExpressionSyntax expression) { return(Disposable.IsNop(expression, context.SemanticModel, context.CancellationToken)); } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is AssignmentExpressionSyntax { Left : { } left, Right : { } right } assignment&& !left.IsKind(SyntaxKind.ElementAccessExpression) && context.SemanticModel.TryGetSymbol(left, context.CancellationToken, out var assignedSymbol)) { if (LocalOrParameter.TryCreate(assignedSymbol, out var localOrParameter) && Disposable.IsCreation(right, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && Disposable.ShouldDispose(localOrParameter, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP001DisposeCreated, assignment.GetLocation())); } if (IsReassignedWithCreated(assignment, context)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP003DisposeBeforeReassigning, assignment.GetLocation())); } if (assignedSymbol is IParameterSymbol { RefKind : RefKind.Ref } refParameter&& refParameter.ContainingSymbol.DeclaredAccessibility != Accessibility.Private && context.SemanticModel.TryGetType(right, context.CancellationToken, out var type) && Disposable.IsAssignableFrom(type, context.Compilation)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP008DoNotMixInjectedAndCreatedForMember, context.Node.GetLocation())); } } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is ArgumentSyntax argument && argument.Parent is ArgumentListSyntax argumentList && argumentList.Parent is InvocationExpressionSyntax invocation && argument.RefOrOutKeyword.IsEither(SyntaxKind.RefKeyword, SyntaxKind.OutKeyword) && context.SemanticModel.TryGetSymbol(invocation, context.CancellationToken, out var method) && method.TrySingleDeclaration(context.CancellationToken, out BaseMethodDeclarationSyntax declaration) && method.TryFindParameter(argument, out var parameter) && Disposable.IsPotentiallyAssignableFrom(parameter.Type, context.Compilation)) { if (Disposable.IsCreation(argument, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && !Disposable.IsAddedToFieldOrProperty(parameter, declaration, context.SemanticModel, context.CancellationToken) && context.SemanticModel.TryGetSymbol(argument.Expression, context.CancellationToken, out ISymbol symbol)) { if (LocalOrParameter.TryCreate(symbol, out var localOrParameter) && Disposable.ShouldDispose(localOrParameter, argument.Expression, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP001DisposeCreated.Descriptor, argument.GetLocation())); } if (Disposable.IsAssignedWithCreated(symbol, invocation, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && !Disposable.IsDisposedBefore(symbol, invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP003DisposeBeforeReassigning.Descriptor, argument.GetLocation())); } } } }
private static void HandleArgument(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } var argument = (ArgumentSyntax)context.Node; if (argument.RefOrOutKeyword.IsKind(SyntaxKind.None)) { return; } var invocation = argument.FirstAncestor <InvocationExpressionSyntax>(); if (invocation == null) { return; } var method = context.SemanticModel.GetSymbolSafe(invocation, context.CancellationToken); if (method == null || method.DeclaringSyntaxReferences.Length == 0) { return; } if (Disposable.IsCreation(argument, context.SemanticModel, context.CancellationToken) .IsEither(Result.No, Result.Unknown)) { return; } var symbol = context.SemanticModel.GetSymbolSafe(argument.Expression, context.CancellationToken); if (Disposable.IsAssignedWithCreated(symbol, argument.FirstAncestor <InvocationExpressionSyntax>(), context.SemanticModel, context.CancellationToken) .IsEither(Result.No, Result.Unknown)) { return; } if (IsDisposedBefore(symbol, argument.Expression, context.SemanticModel, context.CancellationToken)) { return; } context.ReportDiagnostic(Diagnostic.Create(Descriptor, argument.GetLocation())); }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is LocalDeclarationStatementSyntax localDeclaration) { foreach (var declarator in localDeclaration.Declaration.Variables) { if (declarator.Initializer is EqualsValueClauseSyntax initializer && initializer.Value is ExpressionSyntax value && Disposable.IsCreation(value, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && context.SemanticModel.GetDeclaredSymbolSafe(declarator, context.CancellationToken) is ILocalSymbol local && Disposable.ShouldDispose(local, value, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, localDeclaration.GetLocation())); } } } }
private static bool IsPreferUsing(ILocalSymbol local, InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context) { return(local.TrySingleDeclaration(context.CancellationToken, out var declaration) && declaration is VariableDeclaratorSyntax declarator && declaration.TryFirstAncestor(out LocalDeclarationStatementSyntax localDeclarationStatement) && invocation.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement) && (DeclarationIsAssignment() || IsTrivialTryFinally()) && !IsMutated()); bool DeclarationIsAssignment() { return(localDeclarationStatement.Parent == expressionStatement.Parent && Disposable.IsCreation(declarator.Initializer?.Value, context.SemanticModel, context.CancellationToken) == Result.Yes); } bool IsTrivialTryFinally() { return(expressionStatement.Parent is BlockSyntax block && block.Statements.Count == 1 && block.Parent is FinallyClauseSyntax finallyClause && finallyClause.Parent is TryStatementSyntax tryStatement && !tryStatement.Catches.Any()); } bool IsMutated() { using (var walker = MutationWalker.For(local, context.SemanticModel, context.CancellationToken)) { if (declarator.Initializer?.Value.IsKind(SyntaxKind.NullLiteralExpression) == true && walker.TrySingle(out var mutation) && mutation.TryFirstAncestor(out ExpressionStatementSyntax statement) && statement.Parent is BlockSyntax block && block.Statements[0] == statement && block.Parent is TryStatementSyntax) { return(false); } return(walker.All().Any()); } } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is ObjectCreationExpressionSyntax objectCreation) { if (context.SemanticModel.TryGetType(objectCreation, context.CancellationToken, out var type) && type.IsAssignableTo(KnownSymbol.HttpClient, context.Compilation) && !IsStaticFieldInitializer(objectCreation) && !IsStaticPropertyInitializer(objectCreation) && !IsStaticCtor(context.ContainingSymbol)) { context.ReportDiagnostic(Diagnostic.Create(IDISP014UseSingleInstanceOfHttpClient.Descriptor, objectCreation.GetLocation())); } if (Disposable.IsCreation(objectCreation, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && Disposable.IsIgnored(objectCreation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP004DontIgnoreCreated.Descriptor, context.Node.GetLocation())); } } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } if (context.Node is InvocationExpressionSyntax invocation && Disposable.IsCreation(invocation, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && Disposable.IsIgnored(invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); } if (context.Node is MemberAccessExpressionSyntax memberAccess && context.SemanticModel.TryGetSymbol(memberAccess.Expression, context.CancellationToken, out IPropertySymbol property) && Disposable.IsPotentiallyAssignableFrom(property.Type, context.Compilation) && Disposable.IsCreation(memberAccess.Expression, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && Disposable.IsIgnored(memberAccess.Expression, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); } }
private static void HandleReturnValue(SyntaxNodeAnalysisContext context, ExpressionSyntax returnValue) { if (Disposable.IsCreation(returnValue, context.SemanticModel, context.CancellationToken) .IsEither(Result.Yes, Result.Maybe)) { var symbol = context.SemanticModel.GetSymbolSafe(returnValue, context.CancellationToken); if (symbol == null) { return; } foreach (var reference in symbol.DeclaringSyntaxReferences) { var node = reference.GetSyntax(context.CancellationToken); if (node?.Parent?.Parent is UsingStatementSyntax) { return; } } context.ReportDiagnostic(Diagnostic.Create(Descriptor, returnValue.GetLocation())); } }
private static void HandleReturnValue(SyntaxNodeAnalysisContext context, ExpressionSyntax returnValue) { if (Disposable.IsCreation(returnValue, context.SemanticModel, context.CancellationToken) == Result.Yes && context.SemanticModel.TryGetSymbol(returnValue, context.CancellationToken, out ISymbol returnedSymbol)) { if (IsInUsing(returnedSymbol, context.CancellationToken) || Disposable.IsDisposedBefore(returnedSymbol, returnValue, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP011DontReturnDisposed.Descriptor, returnValue.GetLocation())); } else { if (returnValue.FirstAncestor <AccessorDeclarationSyntax>() is AccessorDeclarationSyntax accessor && accessor.IsKind(SyntaxKind.GetAccessorDeclaration)) { context.ReportDiagnostic(Diagnostic.Create(IDISP012PropertyShouldNotReturnCreated.Descriptor, returnValue.GetLocation())); } if (returnValue.FirstAncestor <ArrowExpressionClauseSyntax>() is ArrowExpressionClauseSyntax arrow && arrow.Parent is PropertyDeclarationSyntax) { context.ReportDiagnostic(Diagnostic.Create(IDISP012PropertyShouldNotReturnCreated.Descriptor, returnValue.GetLocation())); } if (!IsDisposableReturnTypeOrIgnored(ReturnType(context), context.Compilation)) { context.ReportDiagnostic(Diagnostic.Create(IDISP005ReturntypeShouldBeIDisposable.Descriptor, returnValue.GetLocation())); } } } else if (returnValue is InvocationExpressionSyntax invocation && invocation.ArgumentList is ArgumentListSyntax argumentList) { foreach (var argument in argumentList.Arguments) { if (Disposable.IsCreation(argument.Expression, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) && context.SemanticModel.TryGetSymbol(argument.Expression, context.CancellationToken, out ISymbol argumentSymbol)) { if (IsInUsing(argumentSymbol, context.CancellationToken) || Disposable.IsDisposedBefore(argumentSymbol, argument.Expression, context.SemanticModel, context.CancellationToken)) { if (IsLazyEnumerable(invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(IDISP011DontReturnDisposed.Descriptor, argument.GetLocation())); } } } } } if (ReturnType(context).IsAwaitable() && returnValue.TryFirstAncestor <UsingStatementSyntax>(out var usingStatement) && usingStatement.Statement.Contains(returnValue) && !returnValue.TryFirstAncestorOrSelf <AwaitExpressionSyntax>(out _) && context.SemanticModel.TryGetType(returnValue, context.CancellationToken, out var returnValueType) && returnValueType.IsAwaitable() && ShouldAwait(context, returnValue)) { context.ReportDiagnostic(Diagnostic.Create(IDISP013AwaitInUsing.Descriptor, returnValue.GetLocation())); } }
private static bool MustBeHandled( 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 (Disposable.IsCreation(node, semanticModel, cancellationToken) .IsEither(Result.No, Result.Unknown)) { return(false); } if (node.Parent is StatementSyntax) { return(true); } if (node.Parent is ArgumentSyntax argument) { if (argument.Parent.Parent is InvocationExpressionSyntax invocation) { using (var returnWalker = ReturnValueWalker.Create(invocation, Search.Recursive, semanticModel, cancellationToken)) { foreach (var returnValue in returnWalker.Item) { if (MustBeHandled(returnValue, semanticModel, cancellationToken)) { return(true); } } } return(false); } if (argument.Parent.Parent is ObjectCreationExpressionSyntax) { if (TryGetAssignedFieldOrProperty(argument, semanticModel, cancellationToken, out ISymbol member, out IMethodSymbol ctor) && member != null) { var initializer = argument.FirstAncestorOrSelf <ConstructorInitializerSyntax>(); if (initializer != null) { if (semanticModel.GetDeclaredSymbolSafe(initializer.Parent, cancellationToken) is IMethodSymbol chainedCtor && chainedCtor.ContainingType != member.ContainingType) { if (Disposable.TryGetDisposeMethod(chainedCtor.ContainingType, Search.TopLevel, out IMethodSymbol disposeMethod)) { return(!Disposable.IsMemberDisposed(member, disposeMethod, semanticModel, cancellationToken)); } } } if (Disposable.IsMemberDisposed(member, ctor.ContainingType, semanticModel, cancellationToken) .IsEither(Result.Yes, Result.Maybe)) { return(false); } return(true); } return(ctor?.ContainingType != KnownSymbol.StreamReader && ctor?.ContainingType != KnownSymbol.CompositeDisposable); } } return(false); }