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)); } } }
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 Result IsCreationCore(RecursiveValues values, SemanticModel semanticModel, CancellationToken cancellationToken) { values.Reset(); var result = Result.No; while (values.MoveNext()) { switch (IsCreationCore(values.Current, semanticModel, cancellationToken)) { case Result.Unknown: if (result == Result.No) { result = Result.Unknown; } break; case Result.Yes: return(Result.Yes); case Result.No: break; case Result.Maybe: result = Result.Maybe; break; default: throw new ArgumentOutOfRangeException(); } } return(result); }
private static Result IsAssignedWithCreated(RecursiveValues walker, SemanticModel semanticModel, CancellationToken cancellationToken) { if (walker.Count == 0) { return(Result.No); } return(IsCreationCore(walker, semanticModel, cancellationToken)); }
internal static Result IsAnyCreation(RecursiveValues walker, SemanticModel semanticModel, CancellationToken cancellationToken) { if (walker.Count == 0) { return(Result.No); } return(IsCreationCore(walker, semanticModel, cancellationToken)); }
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)); } } }
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 Result IsAssignedWithCreated(ISymbol symbol, SyntaxNode context, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var pooled = AssignedValueWalker.Create(symbol, context, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(pooled.Item, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }
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 pooled = AssignedValueWalker.Create(field, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(pooled.Item, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }
internal static Result IsAssignedWithCreated(IPropertySymbol property, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!IsPotentiallyAssignableTo(property?.Type)) { return(Result.No); } using (var assignedValues = AssignedValueWalker.Borrow(property, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }
internal static Result IsAnyCreation(RecursiveValues values, SemanticModel semanticModel, CancellationToken cancellationToken) { if (values.IsEmpty) { return(Result.No); } values.Reset(); var result = Result.No; while (values.MoveNext()) { switch (IsCreationCore(values.Current, semanticModel, cancellationToken)) { case Result.Unknown: if (result == Result.No) { result = Result.Unknown; } break; case Result.Yes: return(Result.Yes); case Result.AssumeYes: result = Result.AssumeYes; break; case Result.No: break; case Result.AssumeNo: if (result == Result.No) { result = Result.AssumeNo; } break; default: throw new ArgumentOutOfRangeException(); } } return(result); }
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)); } } }
/// <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 IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == "value" && candidate.FirstAncestor <AccessorDeclarationSyntax>() is AccessorDeclarationSyntax accessor && accessor.IsKind(SyntaxKind.SetAccessorDeclaration)) { return(Result.No); } if (candidate is ObjectCreationExpressionSyntax) { return(Result.Yes); } using (var walker = ReturnValueWalker.Borrow(candidate, Search.Recursive, semanticModel, cancellationToken)) { if (walker.Count == 0) { var symbol = semanticModel.GetSymbolSafe(candidate, cancellationToken); if (symbol != null && symbol.DeclaringSyntaxReferences.Length == 0) { return(IsCreationCore(symbol)); } using (var recursive = RecursiveValues.Create(new[] { candidate }, semanticModel, cancellationToken)) { return(IsCreationCore(recursive, semanticModel, cancellationToken)); } } using (var recursive = RecursiveValues.Create(walker, semanticModel, cancellationToken)) { return(IsCreationCore(recursive, semanticModel, cancellationToken)); } } }
/// <summary> /// Check if any path returns a created IDisposable. /// </summary> internal static Result IsCreation(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken) { if (candidate == null) { return(Result.Unknown); } switch (candidate.Kind()) { case SyntaxKind.NullLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.NumericLiteralExpression: case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: case SyntaxKind.BaseExpression: case SyntaxKind.ThisExpression: return(Result.No); } if (!IsPotentiallyAssignableFrom(candidate, semanticModel, cancellationToken)) { return(Result.No); } if (candidate is IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == "value" && candidate.FirstAncestor <AccessorDeclarationSyntax>() is AccessorDeclarationSyntax accessor && accessor.IsKind(SyntaxKind.SetAccessorDeclaration)) { return(Result.No); } if (candidate is ObjectCreationExpressionSyntax) { return(Result.Yes); } using (var recursive = RecursiveValues.Borrow(new[] { candidate }, semanticModel, cancellationToken)) { return(IsAnyCreation(recursive, semanticModel, cancellationToken)); } }
/// <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; } }
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)); } } }
/// <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)); } } }
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); }
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); } }
/// <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 AccessorDeclarationSyntax setter)) { using (var pooledSet = SetPool <ISymbol> .Create()) { using (var pooledAssigned = Assignment.Create(setter, Search.Recursive, semanticModel, cancellationToken)) { foreach (var assigned in pooledAssigned.Item.Assignments) { var symbol = semanticModel.GetSymbolSafe(assigned.Left, cancellationToken); if (IsPotentiallyAssignableTo(assigned.Left, semanticModel, cancellationToken) && (symbol is IFieldSymbol || symbol is IPropertySymbol)) { pooledSet.Item.Add(symbol).IgnoreReturnValue(); } } } assignedSymbol = null; var result = Result.No; foreach (var symbol in pooledSet.Item) { 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.No: break; case Result.Maybe: assignedSymbol = symbol; result = Result.Maybe; break; default: throw new ArgumentOutOfRangeException(); } } return(result); } } using (var pooled = AssignedValueWalker.Create(disposable, semanticModel, cancellationToken)) { using (var recursive = RecursiveValues.Create(pooled.Item, semanticModel, cancellationToken)) { assignedSymbol = pooled.Item.CurrentSymbol; return(IsAssignedWithCreated(recursive, semanticModel, cancellationToken)); } } }