internal static bool TryGetReturnExpression(this BlockSyntax body, SemanticModel semanticModel, CancellationToken cancellationToken, out ExpressionSyntax returnValue) { return(ReturnValueWalker.TrygetSingle(body, semanticModel, cancellationToken, out returnValue)); }
private static bool CanIgnore(InvocationExpressionSyntax invocation, SemanticModel semanticModel, CancellationToken cancellationToken) { var method = (IMethodSymbol)semanticModel.GetSymbolSafe(invocation, cancellationToken); if (method == null || method.ReturnsVoid) { return(true); } if (!(invocation.Parent is StatementSyntax)) { return(true); } if (invocation.Parent is ExpressionStatementSyntax && invocation.Parent.Parent is BlockSyntax) { if (method == KnownSymbol.StringBuilder.Append || method == KnownSymbol.StringBuilder.AppendLine || method == KnownSymbol.StringBuilder.AppendFormat || method == KnownSymbol.IList.Add || method == KnownSymbol.IList.Remove) { return(true); } if (method.TryGetSingleDeclaration(cancellationToken, out MethodDeclarationSyntax declaration)) { using (var pooled = ReturnValueWalker.Create(declaration, Search.Recursive, semanticModel, cancellationToken)) { if (method.IsExtensionMethod) { var identifier = declaration.ParameterList.Parameters[0].Identifier; foreach (var returnValue in pooled.Item) { if ((returnValue as IdentifierNameSyntax)?.Identifier.ValueText != identifier.ValueText) { return(false); } } return(true); } foreach (var returnValue in pooled.Item) { if (!returnValue.IsKind(SyntaxKind.ThisExpression)) { return(false); } } return(true); } } return(method.ReturnsVoid); } return(true); }
private static bool CanIgnore(InvocationExpressionSyntax invocation, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!(invocation.Parent is StatementSyntax)) { return(true); } if (invocation.Parent is ExpressionStatementSyntax && invocation.Parent.Parent is BlockSyntax && semanticModel.GetSymbolSafe(invocation, cancellationToken) is IMethodSymbol method) { if (method.ReturnsVoid) { return(true); } if (ReferenceEquals(method.ContainingType, method.ReturnType)) { if (method.ContainingType == KnownSymbol.StringBuilder) { return(true); } } if (method == KnownSymbol.IList.Add || method == KnownSymbol.IList.Remove) { return(true); } if (method.TryGetSingleDeclaration(cancellationToken, out MethodDeclarationSyntax declaration)) { using (var walker = ReturnValueWalker.Borrow(declaration, Search.Recursive, semanticModel, cancellationToken)) { if (method.IsExtensionMethod) { var identifier = declaration.ParameterList.Parameters[0].Identifier; foreach (var returnValue in walker) { if ((returnValue as IdentifierNameSyntax)?.Identifier.ValueText != identifier.ValueText) { return(false); } } return(true); } foreach (var returnValue in walker) { if (!returnValue.IsKind(SyntaxKind.ThisExpression)) { return(false); } } return(true); } } return(method.ReturnsVoid); } return(true); }
private static void Handle(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } if (context.Node is PropertyDeclarationSyntax propertyDeclaration && context.ContainingSymbol is IPropertySymbol property) { { if (propertyDeclaration.ExpressionBody is ArrowExpressionClauseSyntax expressionBody) { if (property.Type.IsReferenceType && expressionBody.Expression is ObjectCreationExpressionSyntax) { context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, expressionBody.GetLocation())); } else if (expressionBody.Expression is MemberAccessExpressionSyntax memberAccess && IsRelayReturn(memberAccess, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, memberAccess.GetLocation())); } } else if (propertyDeclaration.TryGetGetter(out var getter)) { using (var walker = ReturnValueWalker.Borrow(getter, Search.Recursive, context.SemanticModel, context.CancellationToken)) { if (walker.TrySingle(out var returnValue)) { if (property.Type.IsReferenceType && returnValue is ObjectCreationExpressionSyntax) { if (getter.Contains(returnValue) && returnValue.FirstAncestor <ReturnStatementSyntax>() is ReturnStatementSyntax returnStatement) { context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, returnStatement.GetLocation())); } else { context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, getter.GetLocation())); } } else if (returnValue is MemberAccessExpressionSyntax memberAccess && IsRelayReturn(memberAccess, context.SemanticModel, context.CancellationToken)) { if (getter.Contains(returnValue) && returnValue.FirstAncestor <ReturnStatementSyntax>() is ReturnStatementSyntax returnStatement) { context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, returnStatement.GetLocation())); } else { context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, getter.GetLocation())); } } } } } } } }
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); }
private static bool IsReturned(ILocalSymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var pooled = ReturnValueWalker.Create(block, Search.TopLevel, semanticModel, cancellationToken)) { foreach (var value in pooled.Item) { 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)) { var body = (argument.Expression as ParenthesizedLambdaExpressionSyntax)?.Body; using (var pooledInvocations = InvocationWalker.Create(body)) { foreach (var candidate in pooledInvocations.Item.Invocations) { if (Disposable.IsDisposing(candidate, symbol, semanticModel, cancellationToken)) { return(true); } } } } } } } return(false); }