public static void FieldCtorArgInNested(SearchScope scope) { var code = @" namespace N { using System.IO; internal class C { private StreamReader reader; internal C(Stream stream) { this.reader = new StreamReader(stream); } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("stream"); var ctor = syntaxTree.FindConstructorDeclaration("C(Stream stream)"); var symbol = semanticModel.GetDeclaredSymbol(value, CancellationToken.None); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.reader = new StreamReader(stream)", result.ToString()); using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None); Assert.AreEqual("this.reader = new StreamReader(stream)", walker.Assignments.Single().ToString()); }
public void FieldCtorArg(Scope scope) { var testCode = @" namespace RoslynSandbox { internal class C { private readonly int value; internal C(int arg) { this.value = arg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("arg"); var ctor = syntaxTree.FindConstructorDeclaration("C(int arg)"); Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol)); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out AssignmentExpressionSyntax result)); Assert.AreEqual("this.value = arg", result.ToString()); using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString()); } }
public static void GenericFieldTypedCtorArg(SearchScope scope) { var code = @" namespace N { internal class C<T> { private readonly T value; internal C(T arg) { this.value = arg; } public static C<int> Create(int n) => new C<int>(n); } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var argument = syntaxTree.FindArgument("n"); Assert.AreEqual(true, semanticModel.TryGetSymbol(argument.Parent.Parent, CancellationToken.None, out IMethodSymbol method)); Assert.AreEqual(true, method.TryFindParameter(argument, out var symbol)); var ctor = syntaxTree.FindConstructorDeclaration("C(T arg)"); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = arg", result.ToString()); using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None); Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString()); }
public void FieldCtorArg(Search search) { var testCode = @" namespace RoslynSandbox { internal class Foo { private readonly int value; internal Foo(int arg) { this.value = arg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindAssignmentExpression("this.value = arg").Right; var ctor = syntaxTree.FindConstructorDeclaration("Foo(int arg)"); var arg = semanticModel.GetSymbolSafe(value, CancellationToken.None); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(arg, ctor, search, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = arg", result?.ToString()); }
public static void FieldPrivateCtorCalledByInitializer(SearchScope scope) { var code = @" namespace N { internal class C { public static readonly C Default = new C(); private readonly int value; private C() { this.value = 1; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var field = semanticModel.GetDeclaredSymbolSafe(syntaxTree.FindFieldDeclaration("private readonly int value"), CancellationToken.None); var bar = syntaxTree.FindTypeDeclaration("C"); Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, bar, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = 1", result.ToString()); Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, bar, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.value = 1", result.ToString()); using (var walker = AssignmentExecutionWalker.For(field, bar, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.value = 1", walker.Assignments.Single().ToString()); } }
public static void GenericFieldCtorArg(SearchScope scope) { var code = @" namespace N { internal class C<T> { private readonly T value; internal C(T arg) { this.value = arg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, Settings.Default.MetadataReferences); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("arg"); var ctor = syntaxTree.FindConstructorDeclaration("C(T arg)"); Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol)); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = arg", result.ToString()); using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None); Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString()); }
public static void FieldWithCtorArg(SearchScope scope) { var code = @" namespace N { internal class C { private readonly int value; internal C(int arg) { this.value = arg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindMemberAccessExpression("this.value"); var ctor = syntaxTree.FindConstructorDeclaration("C(int arg)"); var field = semanticModel.GetSymbolSafe(value, CancellationToken.None); Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = arg", result.ToString()); Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.value = arg", result.ToString()); using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString()); } }
private static bool TryGetAssignedFieldOrProperty(ArgumentSyntax argument, IMethodSymbol method, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol member) { member = null; if (method == null) { return(false); } if (method.TrySingleDeclaration(cancellationToken, out BaseMethodDeclarationSyntax methodDeclaration) && methodDeclaration.TryFindParameter(argument, out var parameter)) { var parameterSymbol = semanticModel.GetDeclaredSymbolSafe(parameter, cancellationToken); if (AssignmentExecutionWalker.FirstWith(parameterSymbol, methodDeclaration.Body, Scope.Member, semanticModel, cancellationToken, out var assignment)) { member = semanticModel.GetSymbolSafe(assignment.Left, cancellationToken); if (member is IFieldSymbol || member is IPropertySymbol) { return(true); } } if (methodDeclaration is ConstructorDeclarationSyntax ctor && ctor.Initializer is ConstructorInitializerSyntax initializer && initializer.ArgumentList != null && initializer.ArgumentList.Arguments.TrySingle(x => x.Expression is IdentifierNameSyntax identifier && identifier.Identifier.ValueText == parameter.Identifier.ValueText, out var chainedArgument)) { var chained = semanticModel.GetSymbolSafe(ctor.Initializer, cancellationToken); return(TryGetAssignedFieldOrProperty(chainedArgument, chained, semanticModel, cancellationToken, out member)); } } return(false); }
public static void FieldCtorArgViaLocal(SearchScope scope) { var code = @" namespace N { internal class C { private readonly int value; internal C(int arg) { var temp = arg; this.value = temp; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var ctor = syntaxTree.FindConstructorDeclaration("C(int arg)"); var symbol = semanticModel.GetDeclaredSymbol(syntaxTree.FindParameter("int arg"), CancellationToken.None); Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = temp", result.ToString()); using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None); Assert.AreEqual("this.value = temp", walker.Assignments.Single().ToString()); }
private static bool TryGetParameterName(ISymbol member, TypeDeclarationSyntax typeDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out string parameterName) { parameterName = null; using (var walker = AssignmentExecutionWalker.For(member, typeDeclaration, Scope.Member, semanticModel, cancellationToken)) { foreach (var assignment in walker.Assignments) { if (assignment.Right is IdentifierNameSyntax identifierName && assignment.TryFirstAncestor <ConstructorDeclarationSyntax>(out var ctor) && ctor.TryFindParameter(identifierName.Identifier.ValueText, out _)) { if (parameterName == null) { parameterName = identifierName.Identifier.ValueText; } else if (parameterName != identifierName.Identifier.ValueText) { parameterName = null; return(false); } } } } return(parameterName != null); }
public void FieldInPropertyExpressionBody(Search search) { var testCode = @" namespace RoslynSandbox { internal class Foo { private int number; internal Foo() { var i = this.Number; } public int Number => this.number = 3; } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindMemberAccessExpression("this.number"); var ctor = syntaxTree.FindConstructorDeclaration("Foo()"); AssignmentExpressionSyntax result; var field = semanticModel.GetSymbolSafe(value, CancellationToken.None); if (search == Search.Recursive) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstForSymbol(field, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.number = 3", result?.ToString()); } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstForSymbol(field, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out result)); } }
internal static bool IsAssignedAndDisposedInSetupAndTearDown(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken) { if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, Scope.Member, semanticModel, cancellationToken, out var assignment) && assignment.FirstAncestor <MethodDeclarationSyntax>() is MethodDeclarationSyntax methodDeclaration) { if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitSetUpAttribute, semanticModel, cancellationToken, out _)) { if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive(x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitTearDownAttribute), out var tearDown)) { return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken)); } } if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitOneTimeSetUpAttribute, semanticModel, cancellationToken, out _)) { if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive( x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitOneTimeTearDownAttribute), out var tearDown)) { return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken)); } } } return(false); }
private static bool TryGetParameterName(ISymbol member, TypeDeclarationSyntax typeDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out string?parameterName) { using (var walker = AssignmentExecutionWalker.For(member, typeDeclaration, SearchScope.Member, semanticModel, cancellationToken)) { foreach (var assignment in walker.Assignments) { if (assignment.Right is IdentifierNameSyntax { Identifier: { } identifier } &&
internal static bool IsAssignedInInitializeAndDisposedInCleanup(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken) { if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, SearchScope.Member, semanticModel, cancellationToken, out var assignment) && assignment.FirstAncestor <MethodDeclarationSyntax>() is { } methodDeclaration) { var cleanup = TearDown(KnownSymbol.NUnitSetUpAttribute, KnownSymbol.NUnitTearDownAttribute) ?? TearDown(KnownSymbol.NUnitOneTimeSetUpAttribute, KnownSymbol.NUnitOneTimeTearDownAttribute) ?? TearDown(KnownSymbol.TestInitializeAttribute, KnownSymbol.TestCleanupAttribute) ?? TearDown(KnownSymbol.ClassInitializeAttribute, KnownSymbol.ClassCleanupAttribute); return(cleanup is { } &&
internal static bool IsAssignedInSetUp(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken, out AttributeSyntax attribute) { if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, Scope.Member, semanticModel, cancellationToken, out var assignment) && assignment.FirstAncestor <MethodDeclarationSyntax>() is MethodDeclarationSyntax methodDeclaration) { return(Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitSetUpAttribute, semanticModel, cancellationToken, out attribute) || Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitOneTimeSetUpAttribute, semanticModel, cancellationToken, out attribute)); } attribute = null; return(false); }
public static void FieldWithCtorArgViaProperty(SearchScope scope) { var code = @" namespace N { internal class C { private int number; internal C(int arg) { this.Number = arg; } public int Number { get { return this.number; } set { this.number = value; } } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("arg"); var ctor = syntaxTree.FindConstructorDeclaration("C(int arg)"); var symbol = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None); if (scope == SearchScope.Member) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.Number = arg", result.ToString()); using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.Number = arg", string.Join(", ", walker.Assignments)); } } else { Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.number = value", result.ToString()); using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.number = value", string.Join(", ", walker.Assignments)); } } }
public void FieldWithCtorArgViaProperty(Scope scope) { var testCode = @" namespace RoslynSandbox { internal class Foo { private int number; internal Foo(int arg) { this.Number = arg; } public int Number { get { return this.number; } set { this.number = value; } } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindMemberAccessExpression("this.number"); var ctor = syntaxTree.FindConstructorDeclaration("Foo(int arg)"); AssignmentExpressionSyntax result; var field = semanticModel.GetSymbolSafe(value, CancellationToken.None); if (scope != Scope.Member) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.number = value", result.ToString()); Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.number = value", result.ToString()); using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.number = value", walker.Assignments.Single().ToString()); } } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); } }
public static void ChainedCtorArg(SearchScope scope) { var code = @" namespace N { internal class C { private readonly int value; public C(int arg) : this(arg, 1) { } internal C(int chainedArg, int _) { this.value = chainedArg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var ctor = syntaxTree.FindConstructorDeclaration("C(int arg)"); var symbol = semanticModel.GetDeclaredSymbolSafe(syntaxTree.FindParameter("arg"), CancellationToken.None); if (scope != SearchScope.Member) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result)); Assert.AreEqual("this.value = chainedArg", result.ToString()); using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.value = chainedArg", walker.Assignments.Single().ToString()); } } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out _)); using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual(0, walker.Assignments.Count); } } }
internal static bool IsAssignedToFieldOrProperty(ISymbol symbol, SyntaxNode scope, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <ISymbol> visited = null) { if (AssignmentExecutionWalker.FirstWith(symbol, scope, Scope.Instance, semanticModel, cancellationToken, out var assignment) && semanticModel.TryGetSymbol(assignment.Left, cancellationToken, out ISymbol left)) { if (left.IsEither <IParameterSymbol, ILocalSymbol>()) { using (visited = visited.IncrementUsage()) { return(visited.Add(left) && IsAssignedToFieldOrProperty(left, scope, semanticModel, cancellationToken, visited)); } } return(left.IsEitherKind(SymbolKind.Field, SymbolKind.Property, SymbolKind.ArrayType)); } return(false); }
internal static bool AssignsSymbolInSetter(IPropertySymbol property, ISymbol symbol, SemanticModel semanticModel, CancellationToken cancellationToken) { var setMethod = property?.SetMethod; if (setMethod == null || setMethod.DeclaringSyntaxReferences.Length == 0) { return(false); } if (TryGetSetter(property, cancellationToken, out AccessorDeclarationSyntax setter)) { if (AssignmentExecutionWalker.FirstFor(symbol, setter, Scope.Instance, semanticModel, cancellationToken, out _)) { return(true); } } return(false); }
public static void FieldInPropertyExpressionBody(SearchScope scope) { var code = @" namespace N { internal class C { private int number; internal C() { var i = this.Number; } public int Number => this.number = 3; } }"; var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindMemberAccessExpression("this.number"); var ctor = syntaxTree.FindConstructorDeclaration("C()"); AssignmentExpressionSyntax result; var field = semanticModel.GetSymbolSafe(value, CancellationToken.None); if (scope != SearchScope.Member) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.number = 3", result.ToString()); Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.number = 3", result.ToString()); using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None)) { Assert.AreEqual("this.number = 3", walker.Assignments.Single().ToString()); } } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out _)); } }
public void FieldWithCtorArgViaProperty(Search search) { var testCode = @" namespace RoslynSandbox { internal class Foo { private int number; internal Foo(int arg) { this.Number = arg; } public int Number { get { return this.number; } set { this.number = value; } } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("arg"); var ctor = syntaxTree.FindConstructorDeclaration("Foo(int arg)"); AssignmentExpressionSyntax result; var symbol = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None); if (search == Search.Recursive) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.Number = arg", result?.ToString()); } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstForSymbol(symbol, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out result)); } }
public void ChainedCtorArg(Search search) { var testCode = @" namespace RoslynSandbox { internal class Foo { private readonly int value; public Foo(int arg) : this(arg, 1) { } internal Foo(int chainedArg, int _) { this.value = chainedArg; } } }"; var syntaxTree = CSharpSyntaxTree.ParseText(testCode); var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes()); var semanticModel = compilation.GetSemanticModel(syntaxTree); var value = syntaxTree.FindParameter("arg"); var ctor = syntaxTree.FindConstructorDeclaration("Foo(int arg)"); AssignmentExpressionSyntax result; var symbol = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None); if (search == Search.Recursive) { Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result)); Assert.AreEqual("this.value = chainedArg", result?.ToString()); } else { Assert.AreEqual(false, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out 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); } }
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;
internal static bool IsValueValidForRegisteredType(ExpressionSyntax value, ITypeSymbol registeredType, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <SyntaxNode>?visited = null) { switch (value) { case ConditionalExpressionSyntax conditional: return(IsValueValidForRegisteredType(conditional.WhenTrue, registeredType, semanticModel, cancellationToken, visited) && IsValueValidForRegisteredType(conditional.WhenFalse, registeredType, semanticModel, cancellationToken, visited)); case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression): return(IsValueValidForRegisteredType(binary.Left, registeredType, semanticModel, cancellationToken, visited) && IsValueValidForRegisteredType(binary.Right, registeredType, semanticModel, cancellationToken, visited)); } if (registeredType.TypeKind == TypeKind.Enum) { return(semanticModel.TryGetType(value, cancellationToken, out var valueType) && valueType.MetadataName == registeredType.MetadataName && Equals(valueType.ContainingType, registeredType.ContainingType) && NamespaceSymbolComparer.Equals(valueType.ContainingNamespace, registeredType.ContainingNamespace)); } if (semanticModel.IsRepresentationPreservingConversion(value, registeredType)) { return(true); } if (semanticModel.TryGetSymbol(value, cancellationToken, out var symbol)) { if (symbol is IFieldSymbol field) { if (field.TrySingleDeclaration(cancellationToken, out var fieldDeclaration)) { if (fieldDeclaration.Declaration is { } variableDeclaration&& variableDeclaration.Variables.TryLast(out var variable) && variable.Initializer is { } initializer&& !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited)) { return(false); } return(IsAssignedValueOfRegisteredType(symbol, fieldDeclaration)); } return(field.Type == KnownSymbols.Object); } if (symbol is IPropertySymbol property) { if (property.TrySingleDeclaration(cancellationToken, out PropertyDeclarationSyntax? propertyDeclaration)) { if (propertyDeclaration.Initializer is { } initializer&& !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited)) { return(false); } if (property.SetMethod == null && property.GetMethod is { } getMethod) { return(IsReturnValueOfRegisteredType(getMethod)); } return(IsAssignedValueOfRegisteredType(symbol, propertyDeclaration)); } return(property.Type == KnownSymbols.Object); } if (symbol is IMethodSymbol method) { return(IsReturnValueOfRegisteredType(method)); } } return(false); bool IsAssignedValueOfRegisteredType(ISymbol memberSymbol, MemberDeclarationSyntax declaration) { if (declaration.TryFirstAncestor(out TypeDeclarationSyntax? typeDeclaration)) { using (var walker = AssignmentExecutionWalker.For(memberSymbol, typeDeclaration, SearchScope.Type, semanticModel, cancellationToken)) { foreach (var assignment in walker.Assignments) { if (!IsValueValidForRegisteredType(assignment.Right, registeredType, semanticModel, cancellationToken, visited)) { return(false); } } } } return(true); } bool IsReturnValueOfRegisteredType(IMethodSymbol method) { if (method.TrySingleMethodDeclaration(cancellationToken, out var target)) { #pragma warning disable IDISP003 // Dispose previous before re-assigning. using (visited = visited.IncrementUsage()) #pragma warning restore IDISP003 // Dispose previous before re-assigning. { if (visited.Add(target)) { using (var walker = ReturnValueWalker.Borrow(target)) { foreach (var returnValue in walker.ReturnValues) { if (!IsValueValidForRegisteredType(returnValue, registeredType, semanticModel, cancellationToken, visited)) { return(false); } } } } return(true); } } return(method.ReturnType == KnownSymbols.Object); } }
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); } }
private static bool IsDefaultValueOfRegisteredType(ExpressionSyntax defaultValue, ITypeSymbol registeredType, SyntaxNodeAnalysisContext context, PooledSet <SyntaxNode> visited = null) { switch (defaultValue) { case ConditionalExpressionSyntax conditional: #pragma warning disable IDISP003 // Dispose previous before re-assigning. using (visited = visited.IncrementUsage()) #pragma warning restore IDISP003 // Dispose previous before re-assigning. { return(visited.Add(defaultValue) && IsDefaultValueOfRegisteredType(conditional.WhenTrue, registeredType, context, visited) && IsDefaultValueOfRegisteredType(conditional.WhenFalse, registeredType, context, visited)); } case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression): #pragma warning disable IDISP003 // Dispose previous before re-assigning. using (visited = visited.IncrementUsage()) #pragma warning restore IDISP003 // Dispose previous before re-assigning. { return(visited.Add(defaultValue) && IsDefaultValueOfRegisteredType(binary.Left, registeredType, context, visited) && IsDefaultValueOfRegisteredType(binary.Right, registeredType, context, visited)); } } if (context.SemanticModel.IsRepresentationPreservingConversion(defaultValue, registeredType, context.CancellationToken)) { return(true); } if (context.SemanticModel.TryGetSymbol(defaultValue, context.CancellationToken, out ISymbol symbol)) { if (symbol is IFieldSymbol field) { if (field.TrySingleDeclaration(context.CancellationToken, out var fieldDeclaration)) { #pragma warning disable IDISP003 // Dispose previous before re-assigning. using (visited = visited.IncrementUsage()) #pragma warning restore IDISP003 // Dispose previous before re-assigning. { if (fieldDeclaration.Declaration is VariableDeclarationSyntax variableDeclaration && variableDeclaration.Variables.TryLast(out var variable) && variable.Initializer is EqualsValueClauseSyntax initializer) { return(visited.Add(initializer.Value) && IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited)); } return(fieldDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) && AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) && visited.Add(assignedValue) && IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited)); } } return(field.Type == KnownSymbol.Object); } if (symbol is IPropertySymbol property) { if (property.TrySingleDeclaration(context.CancellationToken, out PropertyDeclarationSyntax propertyDeclaration)) { #pragma warning disable IDISP003 // Dispose previous before re-assigning. using (visited = visited.IncrementUsage()) #pragma warning restore IDISP003 // Dispose previous before re-assigning. { if (propertyDeclaration.Initializer is EqualsValueClauseSyntax initializer) { return(visited.Add(initializer.Value) && IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited)); } if (property.SetMethod == null && property.GetMethod is IMethodSymbol getMethod) { return(IsReturnValueOfRegisteredType(getMethod, visited)); } return(propertyDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) && AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) && visited.Add(assignedValue) && IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited)); } } return(property.Type == KnownSymbol.Object); } if (symbol is IMethodSymbol method) { return(IsReturnValueOfRegisteredType(method, visited)); } } return(false); bool IsReturnValueOfRegisteredType(IMethodSymbol method, PooledSet <SyntaxNode> v) { if (method.TrySingleMethodDeclaration(context.CancellationToken, out var target)) { using (var walker = ReturnValueWalker.Borrow(target)) { foreach (var returnValue in walker.ReturnValues) { if (!IsDefaultValueOfRegisteredType(returnValue, registeredType, context, v)) { return(false); } } return(true); } } return(method.ReturnType == KnownSymbol.Object); } }
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); }