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); } }
public static void Properties() { var syntaxTree = CSharpSyntaxTree.ParseText(@" namespace N { public class C1 { private C bar1; private C bar2; public C2 P1 { get { return this.bar1; } set { if (Equals(value, this.bar1)) { return; } if (value != null && this.bar2 != null) { this.P2 = null; } if (this.bar1 != null) { this.bar1.Selected = false; } this.bar1 = value; if (this.bar1 != null) { this.bar1.Selected = true; } } } public C2 P2 { get { return this.bar2; } set { if (Equals(value, this.bar2)) { return; } if (value != null && this.bar1 != null) { this.P1 = null; } if (this.bar2 != null) { this.bar2.Selected = false; } this.bar2 = value; if (this.bar2 != null) { this.bar2.Selected = true; } } } } public class C2 { public bool Selected { get; set; } } }"); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, Settings.Default.MetadataReferences); var semanticModel = compilation.GetSemanticModel(syntaxTree); var setter = syntaxTree.FindPropertyDeclaration("P2").Find <AccessorDeclarationSyntax>("set"); using var assignedValues = AssignmentExecutionWalker.Borrow(setter, SearchScope.Recursive, semanticModel, CancellationToken.None); var actual = string.Join(", ", assignedValues.Assignments); var expected = "this.P2 = null, this.P1 = null, this.P2 = null, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true, this.bar2.Selected = false, this.bar2 = value, this.bar2.Selected = true, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true"; Assert.AreEqual(expected, actual); }
internal static bool TryGetConversionTypes(ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out ITypeSymbol sourceType, out ITypeSymbol targetType) { sourceType = null; targetType = null; if (classDeclaration.TryFindMethod("Convert", out var convertMethod) && convertMethod.ReturnType is PredefinedTypeSyntax returnType && returnType.Keyword.ValueText == "object" && convertMethod.ParameterList != null && convertMethod.ParameterList.Parameters.Count == 4 && convertMethod.ParameterList.Parameters.TryFirst(out var valueParameter)) { using (var returnValues = ReturnValueWalker.Borrow(convertMethod)) { using (var returnTypes = PooledSet <ITypeSymbol> .Borrow()) { foreach (var returnValue in returnValues.ReturnValues) { AddReturnType(returnTypes, returnValue); } return(returnTypes.TrySingle(out targetType) && ConversionWalker.TryGetCommonBase( convertMethod, semanticModel.GetDeclaredSymbolSafe(valueParameter, cancellationToken), semanticModel, cancellationToken, out sourceType)); } } } return(false); void AddReturnType(PooledSet <ITypeSymbol> returnTypes, ExpressionSyntax returnValue) { switch (returnValue) { case LiteralExpressionSyntax literal when literal.IsKind(SyntaxKind.NullLiteralExpression): break; case ConditionalExpressionSyntax ternary: AddReturnType(returnTypes, ternary.WhenTrue); AddReturnType(returnTypes, ternary.WhenFalse); break; case BinaryExpressionSyntax coalesce when coalesce.IsKind(SyntaxKind.CoalesceExpression): AddReturnType(returnTypes, coalesce.Left); AddReturnType(returnTypes, coalesce.Right); break; case IdentifierNameSyntax _: case MemberAccessExpressionSyntax _: var type = semanticModel.GetTypeInfoSafe(returnValue, cancellationToken).Type; if (type == KnownSymbol.Object && semanticModel.GetSymbolSafe(returnValue, cancellationToken) is ISymbol symbol && symbol.IsEither <IFieldSymbol, IPropertySymbol>()) { switch (symbol) { case IFieldSymbol field: if (field.Type == KnownSymbol.Object && field.DeclaredAccessibility == Accessibility.Private && returnValue.FirstAncestor <TypeDeclarationSyntax>() is TypeDeclarationSyntax typeDeclaration) { using (var walker = AssignmentExecutionWalker.Borrow(typeDeclaration, Scope.Instance, semanticModel, cancellationToken)) { foreach (var assignment in walker.Assignments) { if (semanticModel.TryGetSymbol(assignment.Left, cancellationToken, out IFieldSymbol assigned) && FieldSymbolComparer.Equals(assigned, field)) { returnTypes.Add(semanticModel.GetTypeInfoSafe(assignment.Right, cancellationToken).Type); } } } } else { returnTypes.Add(field.Type); } return; case IPropertySymbol property: returnTypes.Add(property.Type); return; } } else { returnTypes.Add(type); } break;
public void Recursive() { var syntaxTree = CSharpSyntaxTree.ParseText(@" namespace RoslynSandbox { public class Foo { private Bar bar1; private Bar bar2; public Bar Bar1 { get { return this.bar1; } set { if (Equals(value, this.bar1)) { return; } if (value != null && this.bar2 != null) { this.Bar2 = null; } if (this.bar1 != null) { this.bar1.Selected = false; } this.bar1 = value; if (this.bar1 != null) { this.bar1.Selected = true; } } } public Bar Bar2 { get { return this.bar2; } set { if (Equals(value, this.bar2)) { return; } if (value != null && this.bar1 != null) { this.Bar1 = null; } if (this.bar2 != null) { this.bar2.Selected = false; } this.bar2 = value; if (this.bar2 != null) { this.bar2.Selected = true; } } } } public class Bar { public bool Selected { get; set; } } }"); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var setter = syntaxTree.FindPropertyDeclaration("Bar2").Find <AccessorDeclarationSyntax>("set"); using (var assignedValues = AssignmentExecutionWalker.Borrow(setter, Scope.Recursive, semanticModel, CancellationToken.None)) { var actual = string.Join(", ", assignedValues.Assignments); var expected = "this.Bar2 = null, this.Bar1 = null, this.Bar2 = null, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true, this.bar2.Selected = false, this.bar2 = value, this.bar2.Selected = true, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true"; Assert.AreEqual(expected, actual); } }