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 IsPotentiallyCachedOrInjectedCore(ExpressionSyntax value, SemanticModel semanticModel, CancellationToken cancellationToken) { var symbol = semanticModel.GetSymbolSafe(value, cancellationToken); if (IsInjectedCore(symbol) == Result.Yes) { return(true); } if (symbol is IPropertySymbol property && !property.IsAutoProperty(cancellationToken)) { using (var returnValues = ReturnValueWalker.Borrow(value, Search.TopLevel, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(returnValues, semanticModel, cancellationToken)) { return(IsInjectedCore(recursive, semanticModel, cancellationToken) .IsEither(Result.Yes, Result.Maybe)); } } } using (var assignedValues = AssignedValueWalker.Borrow(value, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsInjectedCore(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe)); } } }
internal static Result IsAssignedWithCreated(ISymbol symbol, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var assignedValues = AssignedValueWalker.Borrow(symbol, location, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken)) { return(IsAnyCreation(recursive, semanticModel, cancellationToken)); } } }
internal static Result IsAssignedWithCreated(ISymbol symbol, SyntaxNode context, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var assignedValues = AssignedValueWalker.Borrow(symbol, context, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }
private static bool IsAssignedWithInjected(ISymbol symbol, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var assignedValues = AssignedValueWalker.Borrow(symbol, location, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken)) { return(IsAnyCachedOrInjected(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.AssumeYes)); } } }
internal static bool IsCachedOrInjectedOnly(ExpressionSyntax value, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken) { if (semanticModel.TryGetSymbol(value, cancellationToken, out var symbol)) { using var assignedValues = AssignedValueWalker.Borrow(symbol, location, semanticModel, cancellationToken); using var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken); return((IsAnyCachedOrInjected(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.AssumeYes) || IsInjectedCore(symbol).IsEither(Result.Yes, Result.AssumeYes)) && !IsAnyCreation(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.AssumeYes)); } return(false); }
internal static Result IsAssignedWithCreated(IFieldSymbol field, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!IsPotentiallyAssignableTo(field?.Type)) { return(Result.No); } using (var assignedValues = AssignedValueWalker.Borrow(field, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }
private bool AddRecursiveValues(ExpressionSyntax assignedValue) { if (assignedValue is 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(assignedValue); 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 { Left: { }, OperatorToken : { ValueText : "as" } } binary :
internal static bool IsAssignedWithCreatedAndNotCachedOrInjected(IFieldSymbol field, SemanticModel semanticModel, CancellationToken cancellationToken) { if (field == null || !IsPotentiallyAssignableTo(field.Type)) { return(false); } using (var assignedValues = AssignedValueWalker.Borrow(field, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe) && !IsInjectedCore(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe)); } } }
internal static bool IsAssignedWithCreatedAndInjected(IPropertySymbol property, SemanticModel semanticModel, CancellationToken cancellationToken) { if (property == null || !IsPotentiallyAssignableTo(property.Type)) { return(false); } using (var assignedValues = AssignedValueWalker.Borrow(property, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe) && IsInjectedCore(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe)); } } }
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); } }
private static bool IsNeverNull(ISymbol member, SemanticModel semanticModel, CancellationToken cancellationToken) { if (member is IFieldSymbol field && !field.IsReadOnly) { return(false); } if (member is IPropertySymbol property && !property.IsReadOnly) { return(false); } using (var assignedValues = AssignedValueWalker.Borrow(member, semanticModel, cancellationToken)) { foreach (var value in assignedValues) { if (value is ObjectCreationExpressionSyntax objectCreation) { if (objectCreation.Parent is EqualsValueClauseSyntax equalsValueClause && equalsValueClause.Parent is VariableDeclaratorSyntax) { continue; } if (objectCreation.Parent is AssignmentExpressionSyntax assignment && assignment.Parent is ExpressionStatementSyntax statement && statement.Parent is BlockSyntax block && block.Parent is ConstructorDeclarationSyntax) { continue; } } return(false); } } return(true); }
private static bool IsNeverNull(ISymbol member, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!(member is IFieldSymbol || member is IPropertySymbol)) { return(false); } using (var assignedValues = AssignedValueWalker.Borrow(member, semanticModel, cancellationToken)) { foreach (var value in assignedValues) { if (value is ObjectCreationExpressionSyntax) { continue; } return(false); } } return(true); }
/// <summary> /// Check if any path returns a created IDisposable /// </summary> internal static bool IsPotentiallyCachedOrInjected(InvocationExpressionSyntax disposeCall, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!TryGetDisposedRootMember(disposeCall, semanticModel, cancellationToken, out ExpressionSyntax member)) { return(false); } var symbol = semanticModel.GetSymbolSafe(member, cancellationToken); if (IsInjectedCore(symbol).IsEither(Result.Yes, Result.Maybe)) { return(true); } using (var assignedValues = AssignedValueWalker.Borrow(symbol, disposeCall, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsInjectedCore(recursive, semanticModel, cancellationToken).IsEither(Result.Yes, Result.Maybe)); } } }
internal static Result IsAnyCachedOrInjected(RecursiveValues values, SemanticModel semanticModel, CancellationToken cancellationToken) { if (values.IsEmpty) { return(Result.No); } var result = Result.No; values.Reset(); while (values.MoveNext()) { if (values.Current is ElementAccessExpressionSyntax elementAccess && semanticModel.TryGetSymbol(elementAccess.Expression, cancellationToken, out var symbol)) { var isInjected = IsInjectedCore(symbol); if (isInjected == Result.Yes) { return(Result.Yes); } if (isInjected == Result.AssumeYes) { result = Result.AssumeYes; } using var assignedValues = AssignedValueWalker.Borrow(values.Current, semanticModel, cancellationToken); using var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken); isInjected = IsAnyCachedOrInjected(recursive, semanticModel, cancellationToken); if (isInjected == Result.Yes) { return(Result.Yes); } if (isInjected == Result.AssumeYes) { result = Result.AssumeYes; } }
/// <summary> /// Check if any path returns a created IDisposable /// </summary> internal static Result IsAssignedWithCreated(ExpressionSyntax disposable, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol assignedSymbol) { if (!IsPotentiallyAssignableTo(disposable, semanticModel, cancellationToken)) { assignedSymbol = null; return(Result.No); } if (semanticModel.GetSymbolSafe(disposable, cancellationToken) is IPropertySymbol property && property.TryGetSetter(cancellationToken, out var setter)) { using (var pooledSet = PooledHashSet <ISymbol> .Borrow()) { using (var pooledAssigned = AssignmentWalker.Borrow(setter, Search.Recursive, semanticModel, cancellationToken)) { foreach (var assigned in pooledAssigned.Assignments) { var symbol = semanticModel.GetSymbolSafe(assigned.Left, cancellationToken); if (IsPotentiallyAssignableTo(assigned.Left, semanticModel, cancellationToken) && (symbol is IFieldSymbol || symbol is IPropertySymbol)) { pooledSet.Add(symbol).IgnoreReturnValue(); } } } assignedSymbol = null; var result = Result.No; foreach (var symbol in pooledSet) { switch (IsAssignedWithCreated(symbol, disposable, semanticModel, cancellationToken)) { case Result.Unknown: if (result == Result.No) { assignedSymbol = symbol; result = Result.Unknown; } break; case Result.Yes: assignedSymbol = symbol; return(Result.Yes); case Result.AssumeYes: assignedSymbol = symbol; result = Result.AssumeYes; break; case Result.No: case Result.AssumeNo: break; default: throw new ArgumentOutOfRangeException(); } } return(result); } } using (var assignedValues = AssignedValueWalker.Borrow(disposable, semanticModel, cancellationToken)) { assignedSymbol = assignedValues.CurrentSymbol; if (assignedValues.Count == 1 && disposable.Parent is AssignmentExpressionSyntax assignment && assignment.Parent is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression.Parent is BinaryExpressionSyntax binary && binary.IsKind(SyntaxKind.CoalesceExpression)) { return(Result.No); } using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAnyCreation(recursive, semanticModel, cancellationToken)); } } }
internal static Result IsAlreadyAssignedWithCreated(ExpressionSyntax disposable, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol assignedSymbol) { if (!IsPotentiallyAssignableFrom(disposable, semanticModel, cancellationToken)) { assignedSymbol = null; return(Result.No); } var symbol = semanticModel.GetSymbolSafe(disposable, cancellationToken); if (symbol is IPropertySymbol property && IsAssignableFrom(property.Type, semanticModel.Compilation) && property.TryGetSetter(cancellationToken, out var setter) && (setter.ExpressionBody != null || setter.Body != null)) { using (var assignedSymbols = PooledSet <ISymbol> .Borrow()) { using (var pooledAssigned = AssignmentExecutionWalker.Borrow(setter, Scope.Recursive, semanticModel, cancellationToken)) { foreach (var assigned in pooledAssigned.Assignments) { if (assigned.Right is IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == "value" && IsPotentiallyAssignableFrom(assigned.Left, semanticModel, cancellationToken) && semanticModel.GetSymbolSafe(assigned.Left, cancellationToken) is ISymbol candidate && candidate.IsEitherKind(SymbolKind.Field, SymbolKind.Property)) { assignedSymbols.Add(candidate); } } } assignedSymbol = null; var result = Result.No; foreach (var candidate in assignedSymbols) { switch (IsAssignedWithCreated(candidate, disposable, semanticModel, cancellationToken)) { case Result.Unknown: if (result == Result.No) { assignedSymbol = candidate; result = Result.Unknown; } break; case Result.Yes: assignedSymbol = candidate; return(Result.Yes); case Result.AssumeYes: assignedSymbol = candidate; result = Result.AssumeYes; break; case Result.No: case Result.AssumeNo: break; default: throw new ArgumentOutOfRangeException(); } } return(result); } } if (symbol is IParameterSymbol parameter && disposable.TryFirstAncestor <ArrowExpressionClauseSyntax>(out _)) { assignedSymbol = null; return(Result.No); } using (var assignedValues = AssignedValueWalker.Borrow(disposable, semanticModel, cancellationToken)) { assignedSymbol = assignedValues.CurrentSymbol; if (assignedValues.Count == 1 && disposable.Parent is AssignmentExpressionSyntax assignment) { if (assignment.Parent is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression.Parent is BinaryExpressionSyntax binary && binary.IsKind(SyntaxKind.CoalesceExpression)) { // lazy return(Result.No); } } if (symbol.IsEither <IParameterSymbol, ILocalSymbol>()) { assignedValues.RemoveAll(x => IsReturnedBefore(x)); } using (var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken)) { return(IsAnyCreation(recursive, semanticModel, cancellationToken)); } } bool IsReturnedBefore(ExpressionSyntax expression) { if (expression.TryFirstAncestor(out BlockSyntax block) && block.Statements.TryFirstOfType(out ReturnStatementSyntax _)) { if (expression.TryFirstAncestor <ForEachStatementSyntax>(out _) || expression.TryFirstAncestor <ForStatementSyntax>(out _) || expression.TryFirstAncestor <WhileStatementSyntax>(out _)) { return(true); } return(!block.Contains(disposable) && block.SharesAncestor(disposable, out MemberDeclarationSyntax _)); } 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 assignedValues = AssignedValueWalker.Borrow(this.semanticModel.GetDeclaredSymbolSafe(parameter, this.cancellationToken), this.semanticModel, this.cancellationToken)) { assignedValues.HandleInvoke(invokedMethod, invocation.ArgumentList); return(this.AddManyRecursively(assignedValues)); } } } 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 walker = ReturnValueWalker.Borrow(@await, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(walker)); } } 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 assignedValues = AssignedValueWalker.Borrow(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(assignedValues)); } } if (symbol is ILocalSymbol) { using (var assignedValues = AssignedValueWalker.Borrow(assignedValue, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(assignedValues)); } } if (symbol is IPropertySymbol property) { if (property.DeclaringSyntaxReferences.Length == 0) { this.values.Add(assignedValue); return(true); } using (var returnValues = ReturnValueWalker.Borrow(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(returnValues)); } } if (symbol is IMethodSymbol method) { if (method.DeclaringSyntaxReferences.Length == 0) { this.values.Add(assignedValue); return(true); } using (var walker = ReturnValueWalker.Borrow(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken)) { return(this.AddManyRecursively(walker)); } } return(false); }
private void AddReturnValue(ExpressionSyntax value) { if (this.search.IsEither(ReturnValueSearch.Recursive, ReturnValueSearch.RecursiveInside)) { switch (value) { case InvocationExpressionSyntax invocation: if (!this.TryHandleInvocation(invocation, out var method) && method != null && method.DeclaringSyntaxReferences.Length == 0) { this.returnValues.Add(invocation); } break; case AwaitExpressionSyntax awaitExpression: this.TryHandleAwait(awaitExpression); break; case ConditionalExpressionSyntax ternary: this.AddReturnValue(ternary.WhenTrue); this.AddReturnValue(ternary.WhenFalse); break; case BinaryExpressionSyntax coalesce when coalesce.IsKind(SyntaxKind.CoalesceExpression): this.AddReturnValue(coalesce.Left); this.AddReturnValue(coalesce.Right); break; case IdentifierNameSyntax identifierName when this.semanticModel.GetSymbolSafe(identifierName, this.cancellationToken).IsEither <ILocalSymbol, IParameterSymbol>(): if (this.assignedValueWalkers.TryGetValue(identifierName, out _)) { this.returnValues.Add(value); return; } var assignedValues = AssignedValueWalker.Borrow(value, this.semanticModel, this.cancellationToken); this.assignedValueWalkers.Add(identifierName, assignedValues); if (assignedValues.Count == 0) { this.returnValues.Add(value); } else { foreach (var assignment in assignedValues) { this.AddReturnValue(assignment); } } break; case ExpressionSyntax expression when this.semanticModel.GetSymbolSafe(expression, this.cancellationToken) is IPropertySymbol: if (!this.TryHandlePropertyGet(value, out var property) && property != null && property.DeclaringSyntaxReferences.Length == 0) { this.returnValues.Add(value); } break; default: this.returnValues.Add(value); break; } } else { this.returnValues.Add(value); } }
private void AddReturnValue(ExpressionSyntax value) { if (this.awaits) { if (AsyncAwait.TryAwaitTaskRun(value, this.semanticModel, this.cancellationToken, out var awaited)) { using (var walker = this.GetRecursive(awaited)) { if (walker.values.Count == 0) { this.values.Add(awaited); } else { foreach (var returnValue in walker.values) { this.AddReturnValue(returnValue); } } } return; } if (AsyncAwait.TryAwaitTaskFromResult(value, this.semanticModel, this.cancellationToken, out awaited)) { this.AddReturnValue(awaited); return; } if (this.search == Search.Recursive && value is AwaitExpressionSyntax @await) { value = @await.Expression; } } if (this.search == Search.Recursive) { if (value is InvocationExpressionSyntax invocation) { var method = this.semanticModel.GetSymbolSafe(invocation, this.cancellationToken); if (method == null || method.DeclaringSyntaxReferences.Length == 0) { this.values.Add(value); } else { using (var walker = this.GetRecursive(value)) { foreach (var returnValue in walker.values) { this.AddReturnValue(returnValue); } } } } else if (this.recursionLoop.Add(value) && this.semanticModel.IsEither <IParameterSymbol, ILocalSymbol>(value, this.cancellationToken)) { using (var assignedValues = AssignedValueWalker.Borrow(value, this.semanticModel, this.cancellationToken)) { if (assignedValues.Count == 0) { this.values.Add(value); } else { foreach (var assignment in assignedValues) { this.AddReturnValue(assignment); } } } } else { this.values.Add(value); } } else { this.values.Add(value); } }
private static Result IsInjectedCore(RecursiveValues values, SemanticModel semanticModel, CancellationToken cancellationToken) { if (values.Count == 0) { return(Result.No); } var result = Result.No; values.Reset(); while (values.MoveNext()) { if (values.Current is ElementAccessExpressionSyntax elementAccess) { var symbol = semanticModel.GetSymbolSafe(elementAccess.Expression, cancellationToken); var isInjected = IsInjectedCore(symbol); if (isInjected == Result.Yes) { return(Result.Yes); } if (isInjected == Result.Maybe) { result = Result.Maybe; } using (var assignedValues = AssignedValueWalker.Borrow(values.Current, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { isInjected = IsInjectedCore(recursive, semanticModel, cancellationToken); if (isInjected == Result.Yes) { return(Result.Yes); } if (isInjected == Result.Maybe) { result = Result.Maybe; } } } } else { var symbol = semanticModel.GetSymbolSafe(values.Current, cancellationToken); var isInjected = IsInjectedCore(symbol); if (isInjected == Result.Yes) { return(Result.Yes); } if (isInjected == Result.Maybe) { result = Result.Maybe; } } } return(result); }
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); }