提交 99ee77cc 编写于 作者: C Charles Stoner 提交者: GitHub

Merge pull request #15868 from cston/10466

Handle additional cases in MemberSemanticModel.GetQueryEnclosingBinder
......@@ -6,6 +6,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal abstract partial class BoundNode
{
private readonly BoundKind _kind;
......@@ -162,5 +163,15 @@ internal virtual string Dump()
return TreeDumper.DumpCompact(BoundTreeDumperNodeProducer.MakeTree(this));
}
#endif
private string GetDebuggerDisplay()
{
var result = GetType().Name;
if (Syntax != null)
{
result += " " + Syntax.ToString();
}
return result;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp
{
......
......@@ -1349,21 +1349,21 @@ internal override Binder GetEnclosingBinderInternal(int position)
BoundNode boundOuterExpression = this.Bind(incrementalBinder, nodeToBind, _ignoredDiagnostics);
GuardedAddBoundTreeAndGetBoundNodeFromMap(innerLambda, boundOuterExpression);
}
}
// If there is a bug in the binder such that we "lose" a sub-expression containing a
// lambda, and never put bound state for it into the bound tree, then the bound lambda
// that comes back from the map lookup will be null. This can occur in error recovery
// situations. If it is null, we fall back to the outer binder.
// If there is a bug in the binder such that we "lose" a sub-expression containing a
// lambda, and never put bound state for it into the bound tree, then the bound lambda
// that comes back from the map lookup will be null. This can occur in error recovery
// situations. If it is null, we fall back to the outer binder.
using (_nodeMapLock.DisposableRead())
{
nodes = GuardedGetBoundNodesFromMap(innerLambda);
}
using (_nodeMapLock.DisposableRead())
{
nodes = GuardedGetBoundNodesFromMap(innerLambda);
}
if (nodes.IsDefaultOrEmpty)
{
return GetEnclosingBinder(node, position);
if (nodes.IsDefaultOrEmpty)
{
return GetEnclosingBinder(node, position);
}
}
BoundNode boundInnerLambda = GetLowerBoundNode(innerLambda);
......@@ -1394,27 +1394,20 @@ internal override Binder GetEnclosingBinderInternal(int position)
/// </remarks>
private static Binder GetQueryEnclosingBinder(int position, CSharpSyntaxNode startingNode, BoundQueryClause queryClause)
{
for (BoundNode n = queryClause.Value; n != null;)
BoundExpression node = queryClause;
do
{
switch (n.Kind)
switch (node.Kind)
{
case BoundKind.QueryClause:
queryClause = (BoundQueryClause)n;
n = queryClause.Value;
queryClause = (BoundQueryClause)node;
node = GetQueryClauseValue(queryClause);
continue;
case BoundKind.Call:
var call = (BoundCall)n;
foreach (var arg in call.Arguments)
{
if (arg.Syntax.FullSpan.Contains(position) ||
(arg as BoundQueryClause)?.Value.Syntax.FullSpan.Contains(position) == true)
{
n = arg;
break;
}
}
if (n != call)
var call = (BoundCall)node;
node = GetContainingArgument(call.Arguments, position);
if (node != null)
{
continue;
}
......@@ -1428,37 +1421,88 @@ private static Binder GetQueryEnclosingBinder(int position, CSharpSyntaxNode sta
receiver = ((BoundMethodGroup)receiver).ReceiverOpt;
}
if (receiver?.Syntax.FullSpan.Contains(position) == true ||
(receiver as BoundQueryClause)?.Value.Syntax.FullSpan.Contains(position) == true)
if (receiver != null)
{
n = receiver;
continue;
node = GetContainingExprOrQueryClause(receiver, position);
if (node != null)
{
continue;
}
}
// TODO: should we look for the "nearest" argument as a fallback?
n = call.Arguments.LastOrDefault();
node = call.Arguments.LastOrDefault();
continue;
case BoundKind.Conversion:
n = ((BoundConversion)n).Operand;
node = ((BoundConversion)node).Operand;
continue;
case BoundKind.UnboundLambda:
var unbound = (UnboundLambda)n;
var unbound = (UnboundLambda)node;
return GetEnclosingBinder(AdjustStartingNodeAccordingToNewRoot(startingNode, unbound.Syntax),
position, unbound.BindForErrorRecovery().Binder, unbound.Syntax);
case BoundKind.Lambda:
var lambda = (BoundLambda)n;
var lambda = (BoundLambda)node;
return GetEnclosingBinder(AdjustStartingNodeAccordingToNewRoot(startingNode, lambda.Body.Syntax),
position, lambda.Binder, lambda.Body.Syntax);
default:
goto done;
}
}
while (node != null);
done:
return GetEnclosingBinder(AdjustStartingNodeAccordingToNewRoot(startingNode, queryClause.Syntax),
position, queryClause.Binder, queryClause.Syntax);
}
// Return the argument containing the position. For query
// expressions, the span of an argument may include other
// arguments, so the argument with the smallest span is returned.
private static BoundExpression GetContainingArgument(ImmutableArray<BoundExpression> arguments, int position)
{
BoundExpression result = null;
TextSpan resultSpan = default(TextSpan);
foreach (var arg in arguments)
{
var expr = GetContainingExprOrQueryClause(arg, position);
if (expr != null)
{
var span = expr.Syntax.FullSpan;
if (result == null || resultSpan.Contains(span))
{
result = expr;
resultSpan = span;
}
}
}
return result;
}
// Returns the expr if the syntax span contains the position;
// returns the BoundQueryClause value if expr is a BoundQueryClause
// and the value contains the position; otherwise returns null.
private static BoundExpression GetContainingExprOrQueryClause(BoundExpression expr, int position)
{
if (expr.Kind == BoundKind.QueryClause)
{
var value = GetQueryClauseValue((BoundQueryClause)expr);
if (value.Syntax.FullSpan.Contains(position))
{
return value;
}
}
if (expr.Syntax.FullSpan.Contains(position))
{
return expr;
}
return null;
}
private static BoundExpression GetQueryClauseValue(BoundQueryClause queryClause)
{
return queryClause.UnoptimizedForm ?? queryClause.Value;
}
private static SyntaxNode AdjustStartingNodeAccordingToNewRoot(SyntaxNode startingNode, SyntaxNode root)
{
SyntaxNode result = startingNode.Contains(root) ? root : startingNode;
......
......@@ -883,13 +883,14 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
Assert.Equal(LocalDeclarationKind.RegularVariable, ((LocalSymbol)symbol).DeclarationKind);
Assert.Same(symbol, model.GetDeclaredSymbol((SyntaxNode)variableDeclaratorSyntax));
var other = model.LookupSymbols(decl.SpanStart, name: decl.Identifier().ValueText).Single();
if (isShadowed)
{
Assert.NotEqual(symbol, model.LookupSymbols(decl.SpanStart, name: decl.Identifier().ValueText).Single());
Assert.NotEqual(symbol, other);
}
else
{
Assert.Same(symbol, model.LookupSymbols(decl.SpanStart, name: decl.Identifier().ValueText).Single());
Assert.Same(symbol, other);
}
Assert.True(model.LookupNames(decl.SpanStart).Contains(decl.Identifier().ValueText));
......@@ -11209,8 +11210,7 @@ static bool TakeOutParam<T>(T y, out T x)
switch (i)
{
case 12:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAnOutLocal(model, yRef[1]);
VerifyNotAnOutLocal(model, yRef[1]);
break;
default:
VerifyNotAnOutLocal(model, yRef[1]);
......@@ -11220,8 +11220,7 @@ static bool TakeOutParam<T>(T y, out T x)
var y13Decl = GetOutVarDeclarations(tree, "y13").Single();
var y13Ref = GetReference(tree, "y13");
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForOutVar(model, y13Decl, y13Ref);
VerifyModelForOutVar(model, y13Decl, y13Ref);
}
[Fact]
......@@ -11343,8 +11342,7 @@ static bool TakeOutParam(out int x)
VerifyModelForOutVarDuplicateInSameScope(model, yDecl[1]);
break;
case 12:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForOutVar(model, yDecl[0], yRef[1]);
VerifyModelForOutVar(model, yDecl[0], yRef[1]);
VerifyModelForOutVar(model, yDecl[1], yRef[0]);
break;
......@@ -11357,7 +11355,6 @@ static bool TakeOutParam(out int x)
}
[Fact]
[WorkItem(10466, "https://github.com/dotnet/roslyn/issues/10466")]
public void Scope_Query_07()
{
var source =
......@@ -11411,9 +11408,10 @@ static bool TakeOutParam(out int x)
var yRef = GetReferences(tree, id).ToArray();
Assert.Equal(2, yDecl.Length);
Assert.Equal(2, yRef.Length);
VerifyModelForOutVar(model, yDecl[0], yRef[1]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForOutVar(model, yDecl[1], yRef[0]);
// Since the name is declared twice in the same scope,
// both references are to the same declaration.
VerifyModelForOutVar(model, yDecl[0], yRef);
VerifyModelForOutVarDuplicateInSameScope(model, yDecl[1]);
}
[Fact]
......@@ -11559,14 +11557,12 @@ static bool TakeOutParam(out int x)
switch (i)
{
case 4:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForOutVar(model, yDecl);
VerifyModelForOutVar(model, yDecl);
VerifyNotAnOutLocal(model, yRef);
break;
case 5:
VerifyModelForOutVar(model, yDecl);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAnOutLocal(model, yRef);
VerifyNotAnOutLocal(model, yRef);
break;
default:
VerifyModelForOutVar(model, yDecl);
......@@ -11726,13 +11722,11 @@ static bool TakeOutParam(out int x)
case 4:
case 6:
VerifyModelForOutVar(model, yDecl, yRef[0]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAnOutLocal(model, yRef[1]);
VerifyNotAnOutLocal(model, yRef[1]);
break;
case 8:
VerifyModelForOutVar(model, yDecl, yRef[1]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAnOutLocal(model, yRef[0]);
VerifyNotAnOutLocal(model, yRef[0]);
break;
case 10:
VerifyModelForOutVar(model, yDecl, yRef[0]);
......@@ -63,13 +63,14 @@ protected static void VerifyModelForDeclarationPattern(SemanticModel model, Sing
Assert.Equal(LocalDeclarationKind.PatternVariable, ((LocalSymbol)symbol).DeclarationKind);
Assert.Same(symbol, model.GetDeclaredSymbol((SyntaxNode)designation));
var other = model.LookupSymbols(designation.SpanStart, name: designation.Identifier.ValueText).Single();
if (isShadowed)
{
Assert.NotEqual(symbol, model.LookupSymbols(designation.SpanStart, name: designation.Identifier.ValueText).Single());
Assert.NotEqual(symbol, other);
}
else
{
Assert.Same(symbol, model.LookupSymbols(designation.SpanStart, name: designation.Identifier.ValueText).Single());
Assert.Same(symbol, other);
}
Assert.True(model.LookupNames(designation.SpanStart).Contains(designation.Identifier.ValueText));
......@@ -124,7 +125,8 @@ protected static void VerifyNotAPatternLocal(SemanticModel model, IdentifierName
Assert.NotEqual(LocalDeclarationKind.PatternVariable, ((LocalSymbol)symbol).DeclarationKind);
}
Assert.Same(symbol, model.LookupSymbols(reference.SpanStart, name: reference.Identifier.ValueText).Single());
var other = model.LookupSymbols(reference.SpanStart, name: reference.Identifier.ValueText).Single();
Assert.Same(symbol, other);
Assert.True(model.LookupNames(reference.SpanStart).Contains(reference.Identifier.ValueText));
}
......
......@@ -2178,8 +2178,7 @@ into s
case 1:
case 3:
case 12:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAPatternLocal(model, yRef[1]);
VerifyNotAPatternLocal(model, yRef[1]);
break;
default:
VerifyNotAPatternLocal(model, yRef[1]);
......@@ -2295,8 +2294,7 @@ into s
VerifyModelForDeclarationPatternDuplicateInSameScope(model, yDecl[1]);
break;
case 12:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForDeclarationPattern(model, yDecl[0], yRef[1]);
VerifyModelForDeclarationPattern(model, yDecl[0], yRef[1]);
VerifyModelForDeclarationPattern(model, yDecl[1], yRef[0]);
break;
......@@ -2309,7 +2307,6 @@ into s
}
[Fact]
[WorkItem(10466, "https://github.com/dotnet/roslyn/issues/10466")]
public void ScopeOfPatternVariables_Query_07()
{
var source =
......@@ -2351,9 +2348,10 @@ into x1
var yRef = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(name => name.Identifier.ValueText == id).ToArray();
Assert.Equal(2, yDecl.Length);
Assert.Equal(2, yRef.Length);
VerifyModelForDeclarationPattern(model, yDecl[0], yRef[1]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForDeclarationPattern(model, yDecl[1], yRef[0]);
// Since the name is declared twice in the same scope,
// both references are to the same declaration.
VerifyModelForDeclarationPattern(model, yDecl[0], yRef);
VerifyModelForDeclarationPatternDuplicateInSameScope(model, yDecl[1]);
}
[Fact]
......@@ -2487,14 +2485,12 @@ into x2
switch (i)
{
case 4:
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyModelForDeclarationPattern(model, yDecl);
VerifyModelForDeclarationPattern(model, yDecl);
VerifyNotAPatternLocal(model, yRef);
break;
case 5:
VerifyModelForDeclarationPattern(model, yDecl);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAPatternLocal(model, yRef);
VerifyNotAPatternLocal(model, yRef);
break;
default:
VerifyModelForDeclarationPattern(model, yDecl);
......@@ -2648,13 +2644,11 @@ void Test10()
case 4:
case 6:
VerifyModelForDeclarationPattern(model, yDecl, yRef[0]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAPatternLocal(model, yRef[1]);
VerifyNotAPatternLocal(model, yRef[1]);
break;
case 8:
VerifyModelForDeclarationPattern(model, yDecl, yRef[1]);
// Should be uncommented once https://github.com/dotnet/roslyn/issues/10466 is fixed.
//VerifyNotAPatternLocal(model, yRef[0]);
VerifyNotAPatternLocal(model, yRef[0]);
break;
case 10:
VerifyModelForDeclarationPattern(model, yDecl, yRef[0]);
......
......@@ -551,6 +551,7 @@ .namespace N.M
var module = comp.GetMember<NamedTypeSymbol>("A").ContainingModule;
GetAllNamespaceNames(builder, module.GlobalNamespace);
Assert.Equal(new[] { "<global namespace>", "", ".", "..N", ".N", "N", "N.M", "N.M." }, builder);
builder.Free();
}
private static void GetAllNamespaceNames(ArrayBuilder<string> builder, NamespaceSymbol @namespace)
......
......@@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class LambdaUtilitiesTests : CSharpTestBase
{
private void TestLambdaBody(string markedExpression, bool isLambdaBody)
private void TestLambdaBody(string markedExpression, bool isLambdaBody, bool isReducedLambdaBody = false)
{
string markedSource = @"
using System;
......@@ -43,8 +43,10 @@ void M()
bool expected = enclosingMethod.MethodKind == MethodKind.LambdaMethod && enclosingSyntax.Span.Contains(span.Value);
var node = tree.GetRoot().FindNode(span.Value);
Assert.Equal(expected, LambdaUtilities.IsLambdaBody(node));
Assert.Equal(isLambdaBody, expected);
Assert.False(isLambdaBody && isReducedLambdaBody);
Assert.Equal(expected, LambdaUtilities.IsLambdaBody(node, allowReducedLambdas: true));
Assert.Equal(isLambdaBody || isReducedLambdaBody, expected);
Assert.Equal(isLambdaBody, LambdaUtilities.IsLambdaBody(node));
}
[Fact]
......@@ -111,31 +113,31 @@ public void IsLambdaBody_Select1()
"from x in new[] { 1 } select [|x|]", isLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 select [|x|]", isLambdaBody: false);
"from x in new[] { 1 } where x > 0 select [|x|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 select [|@x|]", isLambdaBody: false);
"from x in new[] { 1 } where x > 0 select [|@x|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } orderby F(x), F(x) descending select [|x|]", isLambdaBody: false);
"from x in new[] { 1 } orderby F(x), F(x) descending select [|x|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } orderby x where x > 0 select [|x|]", isLambdaBody: false);
"from x in new[] { 1 } orderby x where x > 0 select [|x|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } select x into y where y > 0 select [|y|]", isLambdaBody: false);
"from x in new[] { 1 } select x into y where y > 0 select [|y|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } select x into y orderby y select [|y|]", isLambdaBody: false);
"from x in new[] { 1 } select x into y orderby y select [|y|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } select [|x|] into y where y > 0 select y", isLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 select [|x|] into y where y > 0 select y", isLambdaBody: false);
"from x in new[] { 1 } where x > 0 select [|x|] into y where y > 0 select y", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 select x into y where y > 0 select [|y|]", isLambdaBody: false);
"from x in new[] { 1 } where x > 0 select x into y where y > 0 select [|y|]", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } orderby x let z = x where x > 0 select [|x|]", isLambdaBody: true);
......@@ -154,13 +156,13 @@ public void IsLambdaBody_Select1()
public void IsLambdaBody_GroupBy1()
{
TestLambdaBody(
"from x in new[] { 1 } group [|x|] by x", isLambdaBody: false);
"from x in new[] { 1 } group [|x|] by x", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } group x by [|x|]", isLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 group [|x|] by x", isLambdaBody: false);
"from x in new[] { 1 } where x > 0 group [|x|] by x", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } where x > 0 group x by [|x|]", isLambdaBody: true);
......@@ -169,10 +171,10 @@ public void IsLambdaBody_GroupBy1()
"from x in new[] { 1 } let y = x group [|x|] by x", isLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } group [|x|] by x + 1 into y group y by y.Key + 2", isLambdaBody: false);
"from x in new[] { 1 } group [|x|] by x + 1 into y group y by y.Key + 2", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } group x by x + 1 into y group [|y|] by y.Key + 2", isLambdaBody: false);
"from x in new[] { 1 } group x by x + 1 into y group [|y|] by y.Key + 2", isLambdaBody: false, isReducedLambdaBody: true);
TestLambdaBody(
"from x in new[] { 1 } from y in new[] { 2 } group [|x|] by x", isLambdaBody: true);
......
......@@ -1217,6 +1217,7 @@ End Class
Dim [module] = comp.GetMember(Of NamedTypeSymbol)("A").ContainingModule
GetAllNamespaceNames(builder, [module].GlobalNamespace)
Assert.Equal({"Global", "", ".", "..N", ".N", "N", "N.M", "N.M."}, builder)
builder.Free()
End Sub
Private Shared Sub GetAllNamespaceNames(builder As ArrayBuilder(Of String), [namespace] As NamespaceSymbol)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册