diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 94ef707af5ba09c0bf2297ebdddeefd14419c4b7..ad04b67ce37ae87b22a04a70bd8bf9964676cd0b 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -594,7 +594,9 @@ private IObjectOrCollectionInitializerOperation CreateBoundCollectionInitializer private IOperation CreateBoundObjectInitializerMemberOperation(BoundObjectInitializerMember boundObjectInitializerMember) { - Lazy instance = boundObjectInitializerMember.MemberSymbol.IsStatic ? + Symbol memberSymbol = boundObjectInitializerMember.MemberSymbol; + + Lazy instance = memberSymbol?.IsStatic == true ? OperationFactory.NullOperation : new Lazy(() => new InstanceReferenceExpression( @@ -609,17 +611,27 @@ private IOperation CreateBoundObjectInitializerMemberOperation(BoundObjectInitia Optional constantValue = ConvertToOptional(boundObjectInitializerMember.ConstantValue); bool isImplicit = boundObjectInitializerMember.WasCompilerGenerated; - switch (boundObjectInitializerMember.MemberSymbol.Kind) + if ((object)memberSymbol == null) + { + Debug.Assert(boundObjectInitializerMember.Type.IsDynamic()); + + Lazy> arguments = new Lazy>(() => boundObjectInitializerMember.Arguments.SelectAsArray(n => Create(n))); + ImmutableArray argumentNames = boundObjectInitializerMember.ArgumentNamesOpt.NullToEmpty(); + ImmutableArray argumentRefKinds = boundObjectInitializerMember.ArgumentRefKindsOpt.NullToEmpty(); + return new LazyDynamicIndexerAccessExpression(instance, arguments, argumentNames, argumentRefKinds, _semanticModel, syntax, type, constantValue, isImplicit); + } + + switch (memberSymbol.Kind) { case SymbolKind.Field: - var field = (FieldSymbol)boundObjectInitializerMember.MemberSymbol; + var field = (FieldSymbol)memberSymbol; bool isDeclaration = false; return new LazyFieldReferenceExpression(field, isDeclaration, instance, _semanticModel, syntax, type, constantValue, isImplicit); case SymbolKind.Event: - var eventSymbol = (EventSymbol)boundObjectInitializerMember.MemberSymbol; + var eventSymbol = (EventSymbol)memberSymbol; return new LazyEventReferenceExpression(eventSymbol, instance, _semanticModel, syntax, type, constantValue, isImplicit); case SymbolKind.Property: - var property = (PropertySymbol)boundObjectInitializerMember.MemberSymbol; + var property = (PropertySymbol)memberSymbol; Lazy> arguments; if (!boundObjectInitializerMember.Arguments.Any()) { diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs index 9334b471af2c6823e45f2970aa428fb3c27c4c09..bdba3371e89833fd69e49557b620ca8f66932542 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs @@ -604,5 +604,183 @@ public void M1() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + [WorkItem(23154, "https://github.com/dotnet/roslyn/issues/23154")] + public void ObjectCreationWithDynamicMemberInitializer_01() + { + string source = @" +#pragma warning disable 0169 +class A +{ + dynamic this[int x, int y] + { + get + { + return new A(); + } + } + + dynamic this[string x, string y] + { + get + { + throw null; + } + } + + int X, Y, Z; + + static void Main() + { + dynamic x = 1; + new A {/**/[y: x, x: x] = { X = 1, Y = 1, Z = 1 }/**/ }; + } +} +"; + string expectedOperationTree = @" +IMemberInitializerOperation (OperationKind.MemberInitializer, Type: dynamic) (Syntax: '[y: x, x: x ... 1, Z = 1 }') + InitializedMember: + IDynamicIndexerAccessOperation (OperationKind.DynamicIndexerAccess, Type: dynamic) (Syntax: '[y: x, x: x]') + Expression: + IInstanceReferenceOperation (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: '[y: x, x: x]') + Arguments(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ArgumentNames(2): + ""y"" + ""x"" + ArgumentRefKinds(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: dynamic) (Syntax: '{ X = 1, Y = 1, Z = 1 }') + Initializers(3): + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: dynamic) (Syntax: 'X = 1') + Left: + IOperation: (OperationKind.None, Type: null) (Syntax: 'X') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: dynamic) (Syntax: 'Y = 1') + Left: + IOperation: (OperationKind.None, Type: null) (Syntax: 'Y') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: dynamic) (Syntax: 'Z = 1') + Left: + IOperation: (OperationKind.None, Type: null) (Syntax: 'Z') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + [WorkItem(23154, "https://github.com/dotnet/roslyn/issues/23154")] + public void ObjectCreationWithDynamicMemberInitializer_02() + { + string source = @" +#pragma warning disable 0169 +class A +{ + dynamic this[int x, int y] + { + get + { + return new A(); + } + } + + dynamic this[string x, string y] + { + get + { + throw null; + } + } + + int X, Y, Z; + + static void Main() + { + dynamic x = 1; + new A {/**/[y: x, x: x] = { }/**/ }; + } +} +"; + string expectedOperationTree = @" +IMemberInitializerOperation (OperationKind.MemberInitializer, Type: dynamic) (Syntax: '[y: x, x: x] = { }') + InitializedMember: + IDynamicIndexerAccessOperation (OperationKind.DynamicIndexerAccess, Type: dynamic) (Syntax: '[y: x, x: x]') + Expression: + IInstanceReferenceOperation (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: '[y: x, x: x]') + Arguments(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ArgumentNames(2): + ""y"" + ""x"" + ArgumentRefKinds(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: dynamic) (Syntax: '{ }') + Initializers(0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + [WorkItem(23154, "https://github.com/dotnet/roslyn/issues/23154")] + public void ObjectCreationWithDynamicMemberInitializer_03() + { + string source = @" +#pragma warning disable 0169 +class A +{ + dynamic this[int x, int y] + { + get + { + return new A(); + } + } + + dynamic this[string x, string y] + { + get + { + throw null; + } + } + + int X, Y, Z; + + static void Main() + { + dynamic x = 1; + new A {/**/[y: x, x: x]/**/ = { } }; + } +} +"; + string expectedOperationTree = @" +IDynamicIndexerAccessOperation (OperationKind.DynamicIndexerAccess, Type: dynamic) (Syntax: '[y: x, x: x]') + Expression: + IInstanceReferenceOperation (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: '[y: x, x: x]') + Arguments(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') + ArgumentNames(2): + ""y"" + ""x"" + ArgumentRefKinds(0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } } } diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs index ae8eea9f642c5c59ea794d65b38d57aa1c449c1f..35bccf4c0983a29c95cbb3a7646dc753ebdd038f 100644 --- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs +++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs @@ -879,6 +879,9 @@ public override SyntaxNode SetName(SyntaxNode node, string name) throw new ArgumentNullException(nameof(node)); } + // In all cases, the resulting syntax for the new name has elastic trivia attached, + // whether via this call to SyntaxFactory.Identifier or via explicitly added elastic + // markers. SyntaxToken newIdentifier = SyntaxFactory.Identifier(name); switch (node.Kind()) { @@ -904,13 +907,19 @@ public override SyntaxNode SetName(SyntaxNode node, string name) case SyntaxKind.Parameter: return ((ParameterSyntax)node).WithIdentifier(newIdentifier); case SyntaxKind.NamespaceDeclaration: - return ((NamespaceDeclarationSyntax)node).WithName(SyntaxFactory.IdentifierName(name)); + return ((NamespaceDeclarationSyntax)node).WithName( + SyntaxFactory.ParseName(name) + .WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)) + .WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))); case SyntaxKind.EnumMemberDeclaration: return ((EnumMemberDeclarationSyntax)node).WithIdentifier(newIdentifier); case SyntaxKind.VariableDeclarator: return ((VariableDeclaratorSyntax)node).WithIdentifier(newIdentifier); case SyntaxKind.Attribute: - return ((AttributeSyntax)node).WithName(SyntaxFactory.IdentifierName(name)); + return ((AttributeSyntax)node).WithName( + SyntaxFactory.ParseName(name) + .WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)) + .WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))); case SyntaxKind.AttributeArgument: return ((AttributeArgumentSyntax)node).WithNameEquals(SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(name))); default: diff --git a/src/VisualStudio/Core/Test/CodeModel/AbstractCodeNamespaceTests.vb b/src/VisualStudio/Core/Test/CodeModel/AbstractCodeNamespaceTests.vb index 18433eef05b54432c87e62eb16a4d8fe5596f73b..127b4c66e5bb3e35ba96e46237d352dad0dfb41b 100644 --- a/src/VisualStudio/Core/Test/CodeModel/AbstractCodeNamespaceTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/AbstractCodeNamespaceTests.vb @@ -41,7 +41,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel End Function Protected Overrides Function GetNameSetter(codeElement As EnvDTE.CodeNamespace) As Action(Of String) - Throw New NotImplementedException() + Return Sub(name) codeElement.Name = name End Function Protected Overrides Function GetParent(codeElement As EnvDTE.CodeNamespace) As Object diff --git a/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeAttributeTests.vb b/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeAttributeTests.vb index 338f40cf8e6eabd104c769022ca60c6ef7878f31..f67c59ad424681834176a09d43a417e9d06cbce4 100644 --- a/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeAttributeTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeAttributeTests.vb @@ -861,7 +861,7 @@ class CAttribute : Attribute { } #Region "Set Name tests" - Public Async Function TestSetName1() As Task + Public Async Function TestSetName_NewName() As Task Dim code = [$$Goo] @@ -876,6 +876,40 @@ class C { } Await TestSetName(code, expected, "Bar", NoThrow(Of String)()) End Function + + + Public Async Function TestSetName_SimpleNameToDottedName() As Task + Dim code = + +[$$Goo] +class C { } + + + Dim expected = + +[Bar.Baz] +class C { } + + + Await TestSetName(code, expected, "Bar.Baz", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_DottedNameToSimpleName() As Task + Dim code = + +[$$Goo.Bar] +class C { } + + + Dim expected = + +[Baz] +class C { } + + + Await TestSetName(code, expected, "Baz", NoThrow(Of String)()) + End Function #End Region #Region "Set Target tests" diff --git a/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeNamespaceTests.vb b/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeNamespaceTests.vb index e637e51e7464bb05eb5302a293cc111221c03163..cc0128adc6b27accc7b0d2ab45105421f0325cdb 100644 --- a/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeNamespaceTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/CSharp/CodeNamespaceTests.vb @@ -389,6 +389,109 @@ namespace N1 #End Region +#Region "Set Name tests" + + Public Async Function TestSetName_SameName() As Task + Dim code = + + + Dim expected = + + + Await TestSetName(code, expected, "N", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_NewName() As Task + Dim code = + + + Dim expected = + + + Await TestSetName(code, expected, "N2", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_SimpleNameToDottedName() As Task + Dim code = + + + Dim expected = + + + Await TestSetName(code, expected, "N2.N3", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_DottedNameToSimpleName() As Task + Dim code = + + + Dim expected = + + + Await TestSetName(code, expected, "N3.N4", NoThrow(Of String)()) + End Function + +#End Region + #Region "Remove tests" diff --git a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeAttributeTests.vb b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeAttributeTests.vb index 9cac7be17662c618c8f56e0aa2734cb2d238d60e..7a08444a61e3356e97c00c95c3d6010ec69039b1 100644 --- a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeAttributeTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeAttributeTests.vb @@ -1065,7 +1065,7 @@ End Class #Region "Set Name tests" - Public Async Function TestSetName1() As Task + Public Async Function TestSetName_NewName() As Task Dim code = @@ -1082,6 +1082,44 @@ End Class Await TestSetName(code, expected, "Bar", NoThrow(Of String)()) End Function + + + Public Async Function TestSetName_SimpleNameToDottedName() As Task + Dim code = + +Class C +End Class +]]> + + Dim expected = + +Class C +End Class +]]> + + Await TestSetName(code, expected, "Bar.Baz", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_DottedNameToSimpleName() As Task + Dim code = + +Class C +End Class +]]> + + Dim expected = + +Class C +End Class +]]> + + Await TestSetName(code, expected, "Bar.Baz", NoThrow(Of String)()) + End Function #End Region #Region "Set Target tests" diff --git a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeNamespaceTests.vb b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeNamespaceTests.vb index 4dc91058a9837c8cce645b9c242bed7c53129072..15c6aa339657f8ff7aabe1d387cb73cdebf0d90c 100644 --- a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeNamespaceTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/CodeNamespaceTests.vb @@ -821,6 +821,94 @@ End Namespace #End Region +#Region "Set Name tests" + + + Public Async Function TestSetName_SameName() As Task + Dim code = + +Namespace N$$ + Class C + End Class +End Namespace + + + Dim expected = + +Namespace N + Class C + End Class +End Namespace + + + Await TestSetName(code, expected, "N", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_NewName() As Task + Dim code = + +Namespace N$$ + Class C + End Class +End Namespace + + + Dim expected = + +Namespace N2 + Class C + End Class +End Namespace + + + Await TestSetName(code, expected, "N2", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_SimpleNameToDottedName() As Task + Dim code = + +Namespace N1$$ + Class C + End Class +End Namespace + + + Dim expected = + +Namespace N2.N3 + Class C + End Class +End Namespace + + + Await TestSetName(code, expected, "N2.N3", NoThrow(Of String)()) + End Function + + + Public Async Function TestSetName_DottedNameToDottedName() As Task + Dim code = + +Namespace N1.N2$$ + Class C + End Class +End Namespace + + + Dim expected = + +Namespace N3.N4 + Class C + End Class +End Namespace + + + Await TestSetName(code, expected, "N3.N4", NoThrow(Of String)()) + End Function + +#End Region + #Region "Remove tests" diff --git a/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb b/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb index f7cd42a8c3b41135b6cab06801e7386da955bb1f..8cedc9c96ce01f2b8b5852a7dc1b541e2421714a 100644 --- a/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb +++ b/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb @@ -941,11 +941,17 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel Throw New ArgumentNullException(NameOf(node)) End If + ' In all cases, the resulting syntax for the new name has elastic trivia attached, + ' whether via this call to SyntaxFactory.Identifier or via explicitly added elastic + ' markers. Dim identifier As SyntaxToken = SyntaxFactory.Identifier(name) Select Case node.Kind Case SyntaxKind.Attribute - Return DirectCast(node, AttributeSyntax).WithName(SyntaxFactory.ParseTypeName(name)) + Return DirectCast(node, AttributeSyntax).WithName( + SyntaxFactory.ParseTypeName(name) _ + .WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)) _ + .WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))) Case SyntaxKind.ClassStatement Return DirectCast(node, ClassStatementSyntax).WithIdentifier(identifier) Case SyntaxKind.InterfaceStatement @@ -960,7 +966,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel SyntaxKind.DelegateSubStatement Return DirectCast(node, DelegateStatementSyntax).WithIdentifier(identifier) Case SyntaxKind.NamespaceStatement - Return DirectCast(node, NamespaceStatementSyntax).WithName(SyntaxFactory.ParseName(name)) + Return DirectCast(node, NamespaceStatementSyntax).WithName( + SyntaxFactory.ParseName(name) _ + .WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)) _ + .WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))) Case SyntaxKind.SubStatement, SyntaxKind.FunctionStatement, SyntaxKind.SubNewStatement