提交 f3717ae2 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #16659 from VSadov/fix14600

Implements GetDeclaredSymbol for tuple literals and GetSymbolInfo for elements of tuple literals.
......@@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -353,12 +354,34 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup
if (targetType.IsTupleType)
{
var destTupleType = (TupleTypeSymbol)targetType;
// do not lose the original element names in the literal if different from names in the target
TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType, sourceTuple, diagnostics);
// Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
// do not lose the original element names and locations in the literal if different from names in the target
//
// the tuple has changed the type of elements due to target-typing,
// but element names has not changed and locations of their declarations
// should not be confused with element locations on the target type.
var sourceType = sourceTuple.Type as TupleTypeSymbol;
if ((object)sourceType != null)
{
targetType = sourceType.WithUnderlyingType(destTupleType.UnderlyingNamedType);
}
else
{
var tupleSyntax = (TupleExpressionSyntax)sourceTuple.Syntax;
var locationBuilder = ArrayBuilder<Location>.GetInstance();
foreach (var argument in tupleSyntax.Arguments)
{
locationBuilder.Add(argument.NameColon?.Name.Location);
}
targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt,
tupleSyntax.Location,
locationBuilder.ToImmutableAndFree());
}
}
var arguments = sourceTuple.Arguments;
......
......@@ -1205,6 +1205,24 @@ public static INamedTypeSymbol GetDeclaredSymbol(this SemanticModel semanticMode
return csmodel?.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
/// <summary>
/// Given a syntax node of tuple expression, get the tuple type symbol.
/// </summary>
public static INamedTypeSymbol GetDeclaredSymbol(this SemanticModel semanticModel, TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
var csmodel = semanticModel as CSharpSemanticModel;
return csmodel?.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
/// <summary>
/// Given a syntax node of a tuple argument, get the tuple element symbol.
/// </summary>
public static ISymbol GetDeclaredSymbol(this SemanticModel semanticModel, ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
var csmodel = semanticModel as CSharpSemanticModel;
return csmodel?.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
/// <summary>
/// Given a syntax node that declares a property or member accessor, get the corresponding symbol.
/// </summary>
......
......@@ -2679,6 +2679,59 @@ internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax exp
#endregion
// Anonymous types and Tuple expressions are an interesting case here because they declare their own types
//
// In both cases there is no distinct syntax that creates the type and the syntax that describes the type is the literal itself.
// Surely - if you need to modify the anonymous type or a type of a tuple literal, you would be modifying these expressions.
//
// As a result we support GetDeclaredSymbol on the whole AnonymousObjectCreationExpressionSyntax/TupleExpressionSyntax.
// The implementation returns the type of the expression.
//
// In addition to that GetDeclaredSymbol works on the AnonymousObjectMemberDeclaratorSyntax/ArgumentSyntax
// The implementation returns the property/field symbol that is declared by the corresponding syntax.
//
// Example:
// GetDeclaredSymbol => Type: (int Alice, int Bob)
// _____ |__________
// [ ]
// var tuple = (Alice: 1, Bob: 2);
// [ ]
// \GetDeclaredSymbol => Field: (int Alice, int Bob).Bob
//
// A special note must be made about the locations of the corresponding symbols - they refer to the actual syntax
// of the literal or the anonymous type creation expression
//
// This way IDEs can unambiguously implement such services as "Go to definition"
//
// I.E. GetSymbolInfo for "Bob" in "tuple.Bob" should point to the same field as returned by GetDeclaredSymbol when applied to
// the ArgumentSyntax "Bob: 2", since that is where the field was declared, where renames should be applied and so on.
//
//
// In comparison to anonymous types, tuples have one special behavior.
// It is permitted for tuple literals to not have a natural type as long as there is a target type which determines the types of the fields.
// As, such for the purpose of GetDeclaredSymbol, the type symbol that is returned for tuple literals has target-typed fields,
// but yet with the original names.
//
// GetDeclaredSymbol => Type: (string Alice, short Bob)
// ________ |__________
// [ ]
// (string, short) tuple = (Alice: null, Bob: 2);
// [ ]
// \GetDeclaredSymbol => Field: (string Alice, short Bob).Alice
//
// In partiucular, the location of the field declaration is "Alice: null" and not the "string"
// the location of the type is "(Alice: null, Bob: 2)" and not the "(string, short)"
//
// The reason for this behavior is that, even though there might not be other references to "Alice" field in the code,
// the name "Alice" itself evidently refers to something named "Alice" and should still work with
// all the related APIs and services such as "Find all References", "Go to definition", "symbolic rename" etc...
//
// GetSymbolInfo => Field: (string Alice, short Bob).Alice
// __ |__
// [ ]
// (string, short) tuple = (Alice: null, Bob: 2);
//
/// <summary>
/// Given a syntax node of anonymous object creation initializer, get the anonymous object property symbol.
/// </summary>
......@@ -2695,6 +2748,27 @@ internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax exp
/// <returns>The symbol that was declared.</returns>
public abstract INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Given a syntax node of a tuple expression, get the tuple type symbol.
/// </summary>
/// <param name="declaratorSyntax">The tuple expression node.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The symbol that was declared.</returns>
public abstract INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Given a syntax node of an argument expression, get the declared symbol.
/// </summary>
/// <param name="declaratorSyntax">The argument syntax node.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The symbol that was declared.</returns>
/// <remarks>
/// Generally ArgumentSyntax nodes do not declare symbols, except when used as aarguments of a tuple literal.
/// Example: var x = (Alice: 1, Bob: 2);
/// ArgumentSyntax "Alice: 1" declares a tuple element field "(int Alice, int Bob).Alice"
/// </remarks>
public abstract ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Given a syntax node that declares a property or member accessor, get the corresponding
/// symbol.
......@@ -3972,7 +4046,17 @@ private SymbolInfo GetNamedArgumentSymbolInfo(IdentifierNameSyntax identifierNam
if (argumentName.Length == 0)
return SymbolInfo.None; // missing name.
CSharpSyntaxNode containingInvocation = identifierNameSyntax.Parent.Parent.Parent.Parent;
// argument could be an argument of a tuple expression
// var x = (Identifier: 1, AnotherIdentifier: 2);
var parent3 = identifierNameSyntax.Parent.Parent.Parent;
if (parent3.IsKind(SyntaxKind.TupleExpression))
{
var tupleArgument = (ArgumentSyntax)identifierNameSyntax.Parent.Parent;
var tupleElement = GetDeclaredSymbol(tupleArgument, cancellationToken);
return (object)tupleElement == null ? SymbolInfo.None : new SymbolInfo(tupleElement, ImmutableArray<ISymbol>.Empty, CandidateReason.None);
}
CSharpSyntaxNode containingInvocation = parent3.Parent;
SymbolInfo containingInvocationInfo = GetSymbolInfoWorker(containingInvocation, SymbolInfoOptions.PreferConstructorsToType | SymbolInfoOptions.ResolveAliases, cancellationToken);
......@@ -4618,6 +4702,10 @@ protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode declaration,
return this.GetDeclaredSymbol((AnonymousObjectCreationExpressionSyntax)node, cancellationToken);
case SyntaxKind.AnonymousObjectMemberDeclarator:
return this.GetDeclaredSymbol((AnonymousObjectMemberDeclaratorSyntax)node, cancellationToken);
case SyntaxKind.TupleExpression:
return this.GetDeclaredSymbol((TupleExpressionSyntax)node, cancellationToken);
case SyntaxKind.Argument:
return this.GetDeclaredSymbol((ArgumentSyntax)node, cancellationToken);
case SyntaxKind.VariableDeclarator:
return this.GetDeclaredSymbol((VariableDeclaratorSyntax)node, cancellationToken);
case SyntaxKind.SingleVariableDesignation:
......
......@@ -885,6 +885,47 @@ public override INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpres
return (bound == null) ? null : bound.Type as NamedTypeSymbol;
}
public override INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
return GetTypeOfTupleLiteral(declaratorSyntax);
}
public override ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var tupleLiteral = declaratorSyntax?.Parent as TupleExpressionSyntax;
// for now only arguments of a tuple literal may declare symbols
if (tupleLiteral == null)
{
return null;
}
var tupleLiteralType = GetTypeOfTupleLiteral(tupleLiteral);
if ((object)tupleLiteralType != null)
{
var elements = tupleLiteralType.TupleElements;
if(!elements.IsDefault)
{
var idx = tupleLiteral.Arguments.IndexOf(declaratorSyntax);
return elements[idx];
}
}
return null;
}
private NamedTypeSymbol GetTypeOfTupleLiteral(TupleExpressionSyntax declaratorSyntax)
{
var bound = this.GetLowerBoundNode(declaratorSyntax);
return (bound as BoundTupleExpression)?.Type as NamedTypeSymbol;
}
public override SyntaxTree SyntaxTree
{
get
......
......@@ -437,6 +437,20 @@ public override INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpres
return (model == null) ? null : model.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
public override INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var model = this.GetMemberModel(declaratorSyntax);
return (model == null) ? null : model.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
public override ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var model = this.GetMemberModel(declaratorSyntax);
return (model == null) ? null : model.GetDeclaredSymbol(declaratorSyntax, cancellationToken);
}
public override IRangeVariableSymbol GetDeclaredSymbol(QueryClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(node);
......
......@@ -22,4 +22,6 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "<Pending>", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax,System.Threading.CancellationToken)~Microsoft.CodeAnalysis.ISymbol")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "<Pending>", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedVariableComponent(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.VariableComponentSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedVariableComponentSyntax")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "<Pending>", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedVariableDesignation(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.VariableDesignationSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedVariableDesignationSyntax")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "<Pending>", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax,System.Threading.CancellationToken)~Microsoft.CodeAnalysis.INamedTypeSymbol")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "<Pending>", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax,System.Threading.CancellationToken)~Microsoft.CodeAnalysis.ISymbol")]
......@@ -129,8 +129,8 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithThrowKeyword(Micr
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Type.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax.AddArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax
......@@ -254,8 +254,10 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.Accept(Microsoft.C
override Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.Accept<TResult>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult> visitor) -> TResult
override Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Accept<TResult>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult> visitor) -> TResult
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax declaratorSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.ISymbol
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax designationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.ISymbol
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax declarationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.ISymbol
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax declaratorSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.INamedTypeSymbol
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetForEachStatementInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.CommonForEachStatementSyntax forEachStatement) -> Microsoft.CodeAnalysis.CSharp.ForEachStatementInfo
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax
......
......@@ -216,24 +216,15 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType)
/// <summary>
/// Copy this tuple, but modify it to use the new element names.
/// Also applies new location of the whole tuple as well as each element.
/// </summary>
internal TupleTypeSymbol WithElementNames(ImmutableArray<string> newElementNames)
internal TupleTypeSymbol WithElementNames(ImmutableArray<string> newElementNames,
Location newLocation,
ImmutableArray<Location> newElementLocations)
{
Debug.Assert(newElementNames.IsDefault || this._elementTypes.Length == newElementNames.Length);
if (this._elementNames.IsDefault)
{
if (newElementNames.IsDefault)
{
return this;
}
}
else if (!newElementNames.IsDefault && this._elementNames.SequenceEqual(newElementNames))
{
return this;
}
return new TupleTypeSymbol(null, _underlyingType, default(ImmutableArray<Location>), newElementNames, _elementTypes);
return new TupleTypeSymbol(newLocation, _underlyingType, newElementLocations, newElementNames, _elementTypes);
}
/// <summary>
......
......@@ -248,27 +248,48 @@ public static bool IsNamedArgumentName(SyntaxNode node)
// Invocation, ObjectCreation, ObjectInitializer, or ElementAccess.
if (!node.IsKind(IdentifierName))
{
return false;
}
var parent1 = node.Parent;
if (parent1 == null || !parent1.IsKind(NameColon))
{
return false;
}
var parent2 = parent1.Parent;
if (parent2 == null || !(parent2.IsKind(Argument) || parent2.IsKind(AttributeArgument)))
{
return false;
}
var parent3 = parent2.Parent;
if (parent3 == null || !(parent3 is BaseArgumentListSyntax || parent3.IsKind(AttributeArgumentList)))
if (parent3 == null)
{
return false;
}
if (parent3.IsKind(SyntaxKind.TupleExpression))
{
return true;
}
if (!(parent3 is BaseArgumentListSyntax || parent3.IsKind(AttributeArgumentList)))
{
return false;
}
var parent4 = parent3.Parent;
if (parent4 == null)
{
return false;
}
switch (parent4.Kind())
{
case InvocationExpression:
case TupleExpression:
case ObjectCreationExpression:
case ObjectInitializerExpression:
case ElementAccessExpression:
......
......@@ -15074,6 +15074,38 @@ static void Main()
Assert.Equal(ConversionKind.AnonymousFunction, model.GetConversion(n5).Kind);
}
[Fact]
[WorkItem(14600, "https://github.com/dotnet/roslyn/issues/14600")]
public void GetSymbolInfo_01()
{
var source = @"
class C
{
static void Main()
{
// error is intentional. GetSymbolInfo should still work
DummyType x1 = (Alice: 1, ""hello"");
var Alice = x1.Alice;
}
}
" + trivial2uple + trivial3uple + tupleattributes_cs;
var tree = Parse(source, options: TestOptions.Regular);
var comp = CreateCompilationWithMscorlib(tree);
var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var nc = nodes.OfType<NameColonSyntax>().ElementAt(0);
var sym = model.GetSymbolInfo(nc.Name);
Assert.Equal(SymbolKind.Field, sym.Symbol.Kind);
Assert.Equal("Alice", sym.Symbol.Name);
Assert.Equal(nc.Name.GetLocation(), sym.Symbol.Locations[0]);
}
[Fact]
public void CompileTupleLib()
{
......
......@@ -4726,6 +4726,336 @@ struct interface
Assert.Equal(TypeKind.Interface, ((TypeSymbol)interfaceSymbol).TypeKind);
}
[Fact]
public void TupleLiteral001()
{
var source =
@"
using System;
class C
{
static void Main()
{
var t = (1, 2);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (TupleExpressionSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.TupleExpression));
var model = compilation.GetSemanticModel(tree);
var type = (NamedTypeSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(int, int)");
Assert.Equal(type.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "(1, 2)");
Assert.Equal(type.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteral002()
{
var source =
@"
using System;
class C
{
static void Main()
{
var t = (Alice: 1, Bob: 2);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (TupleExpressionSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.TupleExpression));
var model = compilation.GetSemanticModel(tree);
var type = (NamedTypeSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(int Alice, int Bob)");
Assert.Equal(type.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "(Alice: 1, Bob: 2)");
Assert.Equal(type.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteral003()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short Alice, int Bob) t = (1, 1);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (TupleExpressionSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.TupleExpression));
var model = compilation.GetSemanticModel(tree);
var type = (NamedTypeSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short, int)");
Assert.Equal(type.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "(1, 1)");
Assert.Equal(type.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteral004()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short Alice, string Bob) t = (1, null);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (TupleExpressionSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.TupleExpression));
var model = compilation.GetSemanticModel(tree);
var type = (NamedTypeSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short, string)");
Assert.Equal(type.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "(1, null)");
Assert.Equal(type.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteral005()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short, string) t = (Alice:1, Bob:null);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (TupleExpressionSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.TupleExpression));
var model = compilation.GetSemanticModel(tree);
var type = (NamedTypeSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short Alice, string Bob)");
Assert.Equal(type.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "(Alice:1, Bob:null)");
Assert.Equal(type.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement001()
{
var source =
@"
using System;
class C
{
static void Main()
{
var t = (Alice: 1, Bob: 2);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(int Alice, int Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement002()
{
var source =
@"
using System;
class C
{
static void Main()
{
(int X, short Y) t = (Alice: 1, Bob: 2);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(int Alice, short Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement003()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short X, string Y) t = (Alice: 1, Bob: null);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short Alice, string Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement004()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short X, string Y) = (Alice: 1, Bob: null);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short Alice, string Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement005()
{
var source =
@"
using System;
class C
{
static void Main()
{
ValueTuple<short, string> vt = (Alice: 1, Bob: null);
}
}
namespace System
{
// struct with two values
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override string ToString()
{
return '{' + Item1?.ToString() + "", "" + Item2?.ToString() + '}';
}
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short Alice, string Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TupleLiteralElement006()
{
var source =
@"
using System;
class C
{
static void Main()
{
(short X, string) t = (1, Bob: null);
}
}
";
var compilation = CreateCompilationWithMscorlib(source);
var tree = compilation.SyntaxTrees[0];
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument));
var model = compilation.GetSemanticModel(tree);
var element = (FieldSymbol)model.GetDeclaredSymbol(decl);
Assert.Equal(element.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), "(short, string Bob).Bob");
Assert.Equal(element.DeclaringSyntaxReferences.Single().GetSyntax().ToString(), "Bob");
Assert.Equal(element.Locations.Single().IsInSource, true);
}
[Fact]
public void TestIncompleteMemberNode_Visitor()
{
......
......@@ -194,6 +194,7 @@
<Compile Include="Diagnostics\NamingStyles\NamingStyleTests.IdentifierCreation.Casing.vb" />
<Compile Include="Diagnostics\UseAutoProperty\UseAutoPropertyTests.vb" />
<Compile Include="Expansion\LambdaParameterExpansionTests.vb" />
<Compile Include="FindReferences\FindReferencesTests.Tuples.vb" />
<Compile Include="GoToImplementation\GoToImplementationTests.vb" />
<Compile Include="IntelliSense\CompletionServiceTests.vb" />
<Compile Include="InteractivePaste\InteractivePasteCommandHandlerTests.vb" />
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading.Tasks
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Partial Public Class FindReferencesTests
Dim tuple2Doc As XElement =
<Document>
namespace System
{
// struct with two values
public struct ValueTuple&lt;T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override string ToString()
{
return '{' + Item1?.ToString() + "", "" + Item2?.ToString() + '}';
}
}
}
</Document>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuples01() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:[|Alice|]|}:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);
var z = x.[|$$Alice|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuples02() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:[|$$Alice|]|}:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);
var z = x.[|Alice|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuples03() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ([|Alice|]:1, Bob: 2);
var y = ({|Definition:[|$$Alice|]|}:1, Bob: 2);
var z = x.[|Alice|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuplesMultidocument() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program1
{
static void Main(string[] args)
{
// this is odd that these are considered references, but that is
// how it works for now.
// NOTE: types like (int Alice, int Bob) may have distinct declaring references,
// but are also equal, in the symbol equality sense, types.
var x = ([|Alice|]:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);
var z = x.[|Alice|];
}
}
]]>
</Document>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ([|Alice|]:1, Bob: 2);
var y = ({|Definition:[|$$Alice|]|}:1, Bob: 2);
var z = x.[|Alice|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldDifferentTuples01() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:[|Alice|]|}:1, Bob: 2);
var y = (Alice:1.1, Bob: 2);
var z = x.[|$$Alice|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuplesMatchOuterSymbols01() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:[|Program|]|}:1, Main: 2);
var y = ([|Program|]:1, Main: 2);
var z = x.[|$$Program|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuplesMatchOuterSymbols02() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = (1,2,3,4,5,6,7,8,9,10,11,{|Definition:[|Program|]|}:1, Main: 2);
var x1 = (1,2,3,4,5,6,7,8,9,10,11,[|Program|]:1, Main: 2);
var y = (Program:1, Main: 2);
var z = x.[|$$Program|];
}
}
]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
End Class
End Namespace
......@@ -765,6 +765,245 @@ class C
Await TestAsync(workspace)
End Function
#End Region
#Region "CSharp TupleTests"
Dim tuple2Doc As XElement =
<Document>
namespace System
{
// struct with two values
public struct ValueTuple&lt;T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override string ToString()
{
return '{' + Item1?.ToString() + "", "" + Item2?.ToString() + '}';
}
}
}
</Document>
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldEqualTuples01() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = ([|Alice|]: 1, Bob: 2);
var y = (Alice: 1, Bob: 2);
var z1 = x.$$Alice;
var z2 = y.Alice;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldEqualTuples02() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<!-- intentionally not including tuple2, shoudl still work -->
<Document>
class Program
{
static void Main(string[] args)
{
var x = (Alice: 1, Bob: 2);
var y = ([|Alice|]: 1, Bob: 2);
var z1 = x.Alice;
var z2 = y.$$Alice;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldMatchToOuter01() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = ([|Program|]: 1, Main: 2);
var z = x.$$Program;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldMatchToOuter02() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = ([|Pro$$gram|]: 1, Main: 2);
var z = x.Program;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldMatchToOuter03() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = (1,2,3,4,5,6,7,8,9,10, [|Program|]: 1, Main: 2);
var z = x.$$Program;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldRedeclared01() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
(int [|Alice|], int Bob) x = (Alice: 1, Bob: 2);
var z1 = x.$$Alice;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldRedeclared02() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
(string Alice, int Bob) x = ([|Al$$ice|]: null, Bob: 2);
var z1 = x.Alice;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldItem01() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = ([|1|], Bob: 2);
var z1 = x.$$Item1;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldItem02() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
static void Main(string[] args)
{
var x = ([|Alice|]: 1, Bob: 2);
var z1 = x.$$Item1;
}
}
</Document>
</Project>
</Workspace>
Await TestAsync(workspace)
End Function
#End Region
......
......@@ -643,6 +643,60 @@ namespace System
End Sub
<WorkItem(10567, "https://github.com/dotnet/roslyn/issues/14600")>
<WpfFact>
<Trait(Traits.Feature, Traits.Features.Rename)>
<Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.Tuples)>
Public Sub RenameTupleFiledInLiteralRegress14600()
Using workspace = CreateWorkspaceWithWaiter(
<Workspace>
<Project Language="C#" CommonReferences="true" PreprocessorSymbols="__DEMO__">
<Document>
using System;
class Program
{
static void Main(string[] args)
{
var x = (Program: 1, Bob: 2);
var Alice = x.$$Program;
}
}
namespace System
{
// struct with two values
public struct ValueTuple&lt;T1, T2&gt;
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override string ToString()
{
return '{' + Item1?.ToString() + ", " + Item2?.ToString() + '}';
}
}
}
</Document>
</Project>
</Workspace>)
' NOTE: this is currently intentionally blocked
' see https://github.com/dotnet/roslyn/issues/10898
AssertTokenNotRenamable(workspace)
End Using
End Sub
#End Region
End Class
......
......@@ -592,7 +592,8 @@ private ExpressionSyntax VisitSimpleName(SimpleNameSyntax rewrittenSimpleName, S
(parent is MemberAccessExpressionSyntax && parent.Kind() != SyntaxKind.SimpleMemberAccessExpression) ||
((parent.Kind() == SyntaxKind.SimpleMemberAccessExpression || parent.Kind() == SyntaxKind.NameMemberCref) && originalSimpleName.IsRightSideOfDot()) ||
(parent.Kind() == SyntaxKind.QualifiedName && originalSimpleName.IsRightSideOfQualifiedName()) ||
(parent.Kind() == SyntaxKind.AliasQualifiedName))
(parent.Kind() == SyntaxKind.AliasQualifiedName)||
(parent.Kind() == SyntaxKind.NameColon))
{
return TryAddTypeArgumentToIdentifierName(newNode, symbol);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册