internal static bool IsNop(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken) { if (semanticModel.TryGetSymbol(candidate, cancellationToken, out ISymbol symbol) && FieldOrProperty.TryCreate(symbol, out var fieldOrProperty) && fieldOrProperty.IsStatic && IsAssignableFrom(fieldOrProperty.Type, semanticModel.Compilation)) { if (fieldOrProperty.Type == KnownSymbol.Task || symbol == KnownSymbol.RxDisposable.Empty) { return(true); } using (var walker = ReturnValueWalker.Borrow(candidate, ReturnValueSearch.Recursive, semanticModel, cancellationToken)) { if (walker.Count > 0) { return(walker.TrySingle(out var value) && semanticModel.TryGetType(value, cancellationToken, out var type) && IsNop(type)); } } using (var walker = AssignedValueWalker.Borrow(symbol, semanticModel, cancellationToken)) { return(walker.TrySingle(out var value) && semanticModel.TryGetType(value, cancellationToken, out var type) && IsNop(type)); } } return(false); bool IsNop(ITypeSymbol type) { return(type.IsSealed && type.BaseType == KnownSymbol.Object && type.TryFindSingleMethod("Dispose", out var disposeMethod) && disposeMethod.Parameters.Length == 0 && disposeMethod.TrySingleDeclaration(cancellationToken, out MethodDeclarationSyntax declaration) && declaration.Body is BlockSyntax body && body.Statements.Count == 0); } }
internal static bool IsCachedOrInjected(ExpressionSyntax value, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken) { var symbol = semanticModel.GetSymbolSafe(value, cancellationToken); if (IsInjectedCore(symbol).IsEither(Result.Yes, Result.AssumeYes)) { return(true); } if (symbol is IPropertySymbol property && !property.IsAutoProperty()) { using (var returnValues = ReturnValueWalker.Borrow(value, ReturnValueSearch.TopLevel, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Borrow(returnValues, semanticModel, cancellationToken)) { return(IsAnyCachedOrInjected(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.AssumeYes)); } } } return(IsAssignedWithInjected(symbol, location, semanticModel, cancellationToken)); }
/// <summary> /// Check if any path returns a created IDisposable /// </summary> internal static Result IsCreation(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!IsPotentiallyAssignableTo(candidate, semanticModel, cancellationToken) || candidate is ThisExpressionSyntax || candidate is BaseExpressionSyntax) { return(Result.No); } if (candidate is ObjectCreationExpressionSyntax) { return(Result.Yes); } using (var pooled = ReturnValueWalker.Create(candidate, Search.Recursive, semanticModel, cancellationToken)) { if (pooled.Item.Count == 0) { var symbol = semanticModel.GetSymbolSafe(candidate, cancellationToken); if (symbol != null && symbol.DeclaringSyntaxReferences.Length == 0) { return(IsCreationCore(candidate, semanticModel, cancellationToken)); } using (var recursive = RecursiveValues.Create(new[] { candidate }, semanticModel, cancellationToken)) { return(IsCreationCore(recursive, semanticModel, cancellationToken)); } } using (var recursive = RecursiveValues.Create(pooled.Item, semanticModel, cancellationToken)) { return(IsCreationCore(recursive, semanticModel, cancellationToken)); } } }
internal static bool IsDisposing(InvocationExpressionSyntax disposeCall, ISymbol symbol, SemanticModel semanticModel, CancellationToken cancellationToken) { if (TryGetDisposed(disposeCall, semanticModel, cancellationToken, out var disposed)) { if (disposed.Equals(symbol)) { return(true); } if (disposed is IPropertySymbol property && property.TrySingleDeclaration(cancellationToken, out var declaration)) { using (var walker = ReturnValueWalker.Borrow(declaration, ReturnValueSearch.TopLevel, semanticModel, cancellationToken)) { return(walker.TrySingle(out var returnValue) && MemberPath.TrySingle(returnValue, out var expression) && semanticModel.TryGetSymbol(expression, cancellationToken, out ISymbol nested) && nested.Equals(symbol)); } } } return(false); }
public bool TryGetValue(SyntaxNode member, out ReturnValueWalker walker) { return(this.Map.TryGetValue(member, out walker)); }
public void Add(SyntaxNode member, ReturnValueWalker walker) { this.Map.Add(member, walker); }
private static bool IsReturned(ILocalSymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var walker = ReturnValueWalker.Borrow(block, Search.TopLevel, semanticModel, cancellationToken)) { foreach (var value in walker) { var returnedSymbol = semanticModel.GetSymbolSafe(value, cancellationToken); if (SymbolComparer.Equals(symbol, returnedSymbol)) { return(true); } if (value is ObjectCreationExpressionSyntax objectCreation) { if (objectCreation.ArgumentList != null) { foreach (var argument in objectCreation.ArgumentList.Arguments) { var arg = semanticModel.GetSymbolSafe(argument.Expression, cancellationToken); if (SymbolComparer.Equals(symbol, arg)) { return(true); } } } if (objectCreation.Initializer != null) { foreach (var argument in objectCreation.Initializer.Expressions) { var arg = semanticModel.GetSymbolSafe(argument, cancellationToken); if (SymbolComparer.Equals(symbol, arg)) { return(true); } } } } if (value is InvocationExpressionSyntax invocation) { if (returnedSymbol == KnownSymbol.RxDisposable.Create && invocation.ArgumentList != null && invocation.ArgumentList.Arguments.TryGetSingle(out ArgumentSyntax argument) && argument.Expression is ParenthesizedLambdaExpressionSyntax lambda) { var body = lambda.Body; using (var pooledInvocations = InvocationWalker.Borrow(body)) { foreach (var candidate in pooledInvocations.Invocations) { if (Disposable.IsDisposing(candidate, symbol, semanticModel, cancellationToken)) { return(true); } } } } } } } return(false); }
private static Result CheckReturnValues(IParameterSymbol parameter, SyntaxNode memberAccess, SemanticModel semanticModel, CancellationToken cancellationToken, PooledHashSet <SyntaxNode> visited) { Result CheckReturnValue(ExpressionSyntax returnValue) { if (returnValue is ObjectCreationExpressionSyntax nestedObjectCreation) { if (nestedObjectCreation.TryGetMatchingArgument(parameter, out var nestedArgument)) { return(IsArgumentDisposedByReturnValue(nestedArgument, semanticModel, cancellationToken, visited)); } return(Result.No); } if (returnValue is InvocationExpressionSyntax nestedInvocation) { if (nestedInvocation.TryGetMatchingArgument(parameter, out var nestedArgument)) { return(IsArgumentDisposedByReturnValue(nestedArgument, semanticModel, cancellationToken, visited)); } return(Result.No); } if (returnValue is MemberAccessExpressionSyntax nestedMemberAccess) { return(IsArgumentDisposedByInvocationReturnValue(nestedMemberAccess, semanticModel, cancellationToken, visited)); } return(Result.Unknown); } var result = Result.No; using (var returnWalker = ReturnValueWalker.Borrow(memberAccess, Search.Recursive, semanticModel, cancellationToken)) { using (visited = PooledHashSet <SyntaxNode> .BorrowOrIncrementUsage(visited)) { if (!visited.Add(memberAccess)) { return(Result.Unknown); } foreach (var returnValue in returnWalker) { switch (CheckReturnValue(returnValue)) { case Result.Unknown: return(Result.Unknown); case Result.Yes: if (result == Result.No) { result = Result.Yes; } break; case Result.AssumeYes: result = Result.AssumeYes; break; case Result.No: return(Result.No); case Result.AssumeNo: return(Result.AssumeNo); default: throw new ArgumentOutOfRangeException(); } } } } return(result); }
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); }
private bool AddRecursiveValues(ExpressionSyntax assignedValue) { if (assignedValue == null || assignedValue.IsMissing || !this.checkedLocations.Add(assignedValue)) { return(false); } if (assignedValue is LiteralExpressionSyntax || assignedValue is DefaultExpressionSyntax || assignedValue is TypeOfExpressionSyntax || assignedValue is ObjectCreationExpressionSyntax || assignedValue is ArrayCreationExpressionSyntax || assignedValue is ImplicitArrayCreationExpressionSyntax || assignedValue is InitializerExpressionSyntax) { this.values.Add(assignedValue); return(true); } var argument = assignedValue.Parent as ArgumentSyntax; if (argument?.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword) == true) { var invocation = assignedValue.FirstAncestor <InvocationExpressionSyntax>(); var invokedMethod = this.semanticModel.GetSymbolSafe(invocation, this.cancellationToken); if (invokedMethod == null || invokedMethod.DeclaringSyntaxReferences.Length == 0) { this.values.Add(invocation); return(true); } var before = this.values.Count; foreach (var reference in invokedMethod.DeclaringSyntaxReferences) { var methodDeclaration = reference.GetSyntax(this.cancellationToken) as MethodDeclarationSyntax; if (methodDeclaration.TryGetMatchingParameter(argument, out ParameterSyntax parameter)) { using (var pooled = AssignedValueWalker.Create(this.semanticModel.GetDeclaredSymbolSafe(parameter, this.cancellationToken), this.semanticModel, this.cancellationToken)) { pooled.Item.HandleInvoke(invokedMethod, invocation.ArgumentList); return(this.AddManyRecursively(pooled.Item)); } } } return(before != this.values.Count); } if (assignedValue is BinaryExpressionSyntax binaryExpression) { switch (binaryExpression.Kind()) { case SyntaxKind.CoalesceExpression: var left = this.AddRecursiveValues(binaryExpression.Left); var right = this.AddRecursiveValues(binaryExpression.Right); return(left || right); case SyntaxKind.AsExpression: return(this.AddRecursiveValues(binaryExpression.Left)); default: return(false); } } if (assignedValue is CastExpressionSyntax cast) { return(this.AddRecursiveValues(cast.Expression)); } if (assignedValue is ConditionalExpressionSyntax conditional) { var whenTrue = this.AddRecursiveValues(conditional.WhenTrue); var whenFalse = this.AddRecursiveValues(conditional.WhenFalse); return(whenTrue || whenFalse); } if (assignedValue is AwaitExpressionSyntax @await) { using (var pooled = ReturnValueWalker.Create(@await, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(pooled.Item)); } } if (assignedValue is ElementAccessExpressionSyntax) { this.values.Add(assignedValue); return(true); } var symbol = this.semanticModel.GetSymbolSafe(assignedValue, this.cancellationToken); if (symbol == null) { return(false); } if (symbol is IFieldSymbol) { this.values.Add(assignedValue); return(true); } if (symbol is IParameterSymbol) { this.values.Add(assignedValue); using (var pooled = AssignedValueWalker.Create(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(pooled.Item)); } } if (symbol is ILocalSymbol) { using (var pooled = AssignedValueWalker.Create(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(pooled.Item)); } } if (symbol is IPropertySymbol property) { if (property.DeclaringSyntaxReferences.Length == 0) { this.values.Add(assignedValue); return(true); } using (var returnValues = ReturnValueWalker.Create(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(returnValues.Item)); } } if (symbol is IMethodSymbol method) { if (method.DeclaringSyntaxReferences.Length == 0) { this.values.Add(assignedValue); return(true); } using (var pooled = ReturnValueWalker.Create(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(pooled.Item)); } } return(false); }
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); }
internal static bool TryGetReturnExpression(this BlockSyntax body, SemanticModel semanticModel, CancellationToken cancellationToken, out ExpressionSyntax returnValue) { return(ReturnValueWalker.TryGetSingle(body, semanticModel, cancellationToken, out returnValue)); }
private bool AddRecursiveValues(ExpressionSyntax assignedValue) { if (assignedValue == null || assignedValue.IsMissing || !this.checkedLocations.Add(assignedValue)) { return(false); } if (assignedValue.Parent is ArgumentSyntax argument && argument.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword)) { if (assignedValue.TryFirstAncestor(out InvocationExpressionSyntax invocation) && this.semanticModel.TryGetSymbol(invocation, this.cancellationToken, out var target) && target.TrySingleMethodDeclaration(this.cancellationToken, out var targetDeclaration)) { if (targetDeclaration.TryFindParameter(argument, out var parameter) && this.semanticModel.TryGetSymbol(parameter, this.cancellationToken, out var parameterSymbol)) { using (var assignedValues = AssignedValueWalker.Borrow(parameterSymbol, this.semanticModel, this.cancellationToken)) { assignedValues.HandleInvoke(target, invocation.ArgumentList); return(this.AddManyRecursively(assignedValues)); } } return(false); } this.values.Add(invocation); return(true); } switch (assignedValue) { case ArrayCreationExpressionSyntax _: case DefaultExpressionSyntax _: case ElementAccessExpressionSyntax _: case ImplicitArrayCreationExpressionSyntax _: case InitializerExpressionSyntax _: case LiteralExpressionSyntax _: case ObjectCreationExpressionSyntax _: case TypeOfExpressionSyntax _: this.values.Add(assignedValue); return(true); case BinaryExpressionSyntax binaryExpression: switch (binaryExpression.Kind()) { case SyntaxKind.CoalesceExpression: var left = this.AddRecursiveValues(binaryExpression.Left); var right = this.AddRecursiveValues(binaryExpression.Right); return(left || right); case SyntaxKind.AsExpression: return(this.AddRecursiveValues(binaryExpression.Left)); default: return(false); } case CastExpressionSyntax cast: return(this.AddRecursiveValues(cast.Expression)); case ConditionalExpressionSyntax conditional: var whenTrue = this.AddRecursiveValues(conditional.WhenTrue); var whenFalse = this.AddRecursiveValues(conditional.WhenFalse); return(whenTrue || whenFalse); case AwaitExpressionSyntax awaitExpression: using (var walker = ReturnValueWalker.Borrow(awaitExpression, ReturnValueSearch.RecursiveInside, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(walker)); } } if (this.semanticModel.TryGetSymbol(assignedValue, this.cancellationToken, out ISymbol symbol)) { switch (symbol) { case ILocalSymbol _: using (var assignedValues = AssignedValueWalker.Borrow(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(assignedValues)); } case IParameterSymbol _: this.values.Add(assignedValue); using (var assignedValues = AssignedValueWalker.Borrow(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(assignedValues)); } case IFieldSymbol _: this.values.Add(assignedValue); return(true); case IPropertySymbol _: case IMethodSymbol _: if (symbol.DeclaringSyntaxReferences.Length == 0) { this.values.Add(assignedValue); return(true); } using (var returnValues = ReturnValueWalker.Borrow(assignedValue, ReturnValueSearch.RecursiveInside, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(returnValues)); } } } return(false); }