提交 489ca5f8 编写于 作者: P pgavlin

Bring simple name binding for method type parameters inline with Dev12 and the C# spec.

The compiler had two issues with simple name binding for method type parameters:
- As described in the specification (and implemented in Dev12), a simple name that binds to a method type parameter does so before member lookup happens. This means that such a simple name always binds to that type parameter whether or not it is the primary expression of an invocation expression. Instead, Roslyn always used the member lookup rules when binding simple names to method type parameters.
- As implemented in Dev12 (and somewhat fuzzily described in the specification), method type parameters are not in scope when binding simple names in a method signature rather than a method body (note that this restriction obviously does not apply when binding types or namespaces inside a method signature). Instead, Roslyn placed method type parameters in scope when binding simple names inside a method signature.

This change brings Roslyn in line with Dev12 and the spec in both cases.
***NO_CI***
 (changeset 1382599)
上级 81d51dba
......@@ -172,6 +172,21 @@ internal virtual Symbol ContainingMemberOrLambda
}
}
/// <summary>
/// Is the contained code within a member method body?
/// </summary>
/// <remarks>
/// May be false in lambdas that are outside of member method bodies, e.g. lambdas in
/// field initializers.
/// </remarks>
internal virtual bool IsInMethodBody
{
get
{
return Next.IsInMethodBody;
}
}
/// <summary>
/// Is the contained code within an iterator block?
/// </summary>
......
......@@ -846,6 +846,12 @@ private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, Diag
options |= LookupOptions.MustBeInvocableIfMember;
}
if (!IsInMethodBody)
{
Debug.Assert((options & LookupOptions.NamespacesOrTypesOnly) == 0);
options |= LookupOptions.MustNotBeMethodTypeParameter;
}
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
this.LookupSymbolsWithFallback(lookupResult, node.Identifier.ValueText, arity: arity, useSiteDiagnostics: ref useSiteDiagnostics, options: options);
diagnostics.Add(node, useSiteDiagnostics);
......
......@@ -75,6 +75,14 @@ protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax)
return false;
}
internal override bool IsInMethodBody
{
get
{
return false;
}
}
internal override bool IsDirectlyInIterator
{
get
......@@ -198,4 +206,4 @@ internal override ImmutableHashSet<Symbol> LockedOrDisposedVariables
get { return ImmutableHashSet.Create<Symbol>(); }
}
}
}
\ No newline at end of file
}
......@@ -88,6 +88,14 @@ internal override Symbol ContainingMemberOrLambda
}
}
internal override bool IsInMethodBody
{
get
{
return true;
}
}
internal void MakeIterator()
{
if (this.iteratorInfo == null)
......@@ -302,4 +310,4 @@ internal override bool EnsureSingleDefinition(Symbol symbol, string name, Locati
return false;
}
}
}
\ No newline at end of file
}
......@@ -91,6 +91,11 @@ internal enum LookupOptions
/// Consider named types of any arity when arity zero is specified. It is specifically desired for nameof in such situations: nameof(System.Collections.Generic.List)
/// </summary>
AllNamedTypesOnArityZero = 1 << 13,
/// <summary>
/// Do not consider symbols that are method type parameters.
/// </summary>
MustNotBeMethodTypeParameter = 1 << 14,
}
internal static class LookupOptionExtensions
......@@ -128,8 +133,8 @@ internal static bool AreValid(this LookupOptions options)
return false;
}
// If MustNotBeNamespace is set, neither NamespaceAliasesOnly nor NamespacesOrTypesOnly must be set.
if ((options & LookupOptions.MustNotBeNamespace) != 0 &&
// If MustNotBeNamespace or MustNotBeMethodTypeParameter is set, neither NamespaceAliasesOnly nor NamespacesOrTypesOnly must be set.
if ((options & (LookupOptions.MustNotBeNamespace | LookupOptions.MustNotBeMethodTypeParameter)) != 0 &&
(options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) != 0)
{
return false;
......@@ -156,11 +161,6 @@ static bool OnlyOneBitSet(LookupOptions o)
return (o & (o - 1)) == 0;
}
internal static bool CanConsiderTypeParameters(this LookupOptions options)
{
return (options & (LookupOptions.MustBeInvocableIfMember | LookupOptions.MustBeInstance | LookupOptions.LabelsOnly)) == 0;
}
internal static bool CanConsiderMembers(this LookupOptions options)
{
return (options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly | LookupOptions.LabelsOnly)) == 0;
......@@ -191,4 +191,4 @@ internal static bool IsVerbatimNameAttributeTypeLookup(this LookupOptions option
return (options & LookupOptions.VerbatimNameAttributeTypeOnly) == LookupOptions.VerbatimNameAttributeTypeOnly;
}
}
}
\ No newline at end of file
}
......@@ -47,7 +47,7 @@ internal override bool IsAccessible(Symbol symbol, TypeSymbol accessThroughType,
protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
if (options.CanConsiderTypeParameters())
if (CanConsiderTypeParameters(options))
{
foreach (var parameter in this.namedType.TypeParameters)
{
......
......@@ -137,7 +137,7 @@ private static void AddTypeParameters(GenericNameSyntax genericNameSyntax, Multi
protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
if (options.CanConsiderTypeParameters())
if (CanConsiderTypeParameters(options))
{
foreach (string name in TypeParameterMap.Keys)
{
......
......@@ -47,9 +47,17 @@ internal override Symbol ContainingMemberOrLambda
}
}
protected override LookupOptions LookupMask
{
get
{
return LookupOptions.NamespaceAliasesOnly | LookupOptions.MustNotBeMethodTypeParameter;
}
}
protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
if (options.CanConsiderTypeParameters())
if (CanConsiderTypeParameters(options))
{
foreach (var parameter in this.methodSymbol.TypeParameters)
{
......
......@@ -8,6 +8,10 @@
namespace Microsoft.CodeAnalysis.CSharp
{
/// <summary>
/// Binder used to place the parameters of a method, property, indexer, or delegate
/// in scope when binding &lt;param&gt; tags inside of XML documentation comments.
/// </summary>
internal sealed class WithParametersBinder : Binder
{
private readonly ImmutableArray<ParameterSymbol> parameters;
......@@ -15,7 +19,7 @@ internal sealed class WithParametersBinder : Binder
internal WithParametersBinder(ImmutableArray<ParameterSymbol> parameters, Binder next)
: base(next)
{
Debug.Assert(!parameters.IsEmpty);
Debug.Assert(!parameters.IsDefaultOrEmpty);
this.parameters = parameters;
}
......@@ -52,4 +56,4 @@ protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo res
}
}
}
}
\ No newline at end of file
}
......@@ -17,31 +17,46 @@ internal WithTypeParametersBinder(Binder next)
// TODO: Change this to a data structure that won't allocate enumerators
protected abstract MultiDictionary<string, TypeParameterSymbol> TypeParameterMap { get; }
// This is only overridden by WithMethodTypeParametersBinder.
protected virtual LookupOptions LookupMask
{
get
{
return LookupOptions.NamespaceAliasesOnly | LookupOptions.MustBeInvocableIfMember;
}
}
protected bool CanConsiderTypeParameters(LookupOptions options)
{
return (options & (LookupMask | LookupOptions.MustBeInstance | LookupOptions.LabelsOnly)) == 0;
}
internal override void LookupSymbolsInSingleBinder(
LookupResult result, string name, int arity, ConsList<Symbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
if ((options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.MustBeInvocableIfMember)) != 0)
Debug.Assert(result.IsClear);
if ((options & LookupMask) != 0)
{
return;
}
Debug.Assert(result.IsClear);
var count = TypeParameterMap.GetCountForKey(name);
var typeParameterMap = TypeParameterMap;
var count = typeParameterMap.GetCountForKey(name);
if (count == 1)
{
TypeParameterSymbol p;
TypeParameterMap.TryGetSingleValue(name, out p);
result.MergeEqual(originalBinder.CheckViability(p, arity, options, null, diagnose, ref useSiteDiagnostics));
TypeParameterSymbol typeParameter;
typeParameterMap.TryGetSingleValue(name, out typeParameter);
result.MergeEqual(originalBinder.CheckViability(typeParameter, arity, options, null, diagnose, ref useSiteDiagnostics));
}
else if (count > 1)
{
var parameters = TypeParameterMap[name];
foreach (var s in parameters)
var parameters = typeParameterMap[name];
foreach (var typeParameter in parameters)
{
result.MergeEqual(originalBinder.CheckViability(s, arity, options, null, diagnose, ref useSiteDiagnostics));
result.MergeEqual(originalBinder.CheckViability(typeParameter, arity, options, null, diagnose, ref useSiteDiagnostics));
}
}
}
}
}
\ No newline at end of file
}
......@@ -1108,6 +1108,12 @@ private static bool IsInStructuredTriviaOtherThanCrefOrNameAttribute(CSharpSynta
/// Throws an ArgumentOutOfRangeException if position is not within the root of this model.
/// </summary>
protected int CheckAndAdjustPosition(int position)
{
SyntaxToken unused;
return CheckAndAdjustPosition(position, out unused);
}
protected int CheckAndAdjustPosition(int position, out SyntaxToken token)
{
int fullStart = this.Root.Position;
int fullEnd = this.Root.FullSpan.End;
......@@ -1115,7 +1121,7 @@ protected int CheckAndAdjustPosition(int position)
if ((fullStart <= position && position < fullEnd) || atEOF) // allow for EOF
{
SyntaxToken token = (atEOF ? (CSharpSyntaxNode)this.SyntaxTree.GetRoot() : Root).FindTokenIncludingCrefAndNameAttributes(position);
token = (atEOF ? (CSharpSyntaxNode)this.SyntaxTree.GetRoot() : Root).FindTokenIncludingCrefAndNameAttributes(position);
if (position < token.SpanStart) // NB: Span, not FullSpan
{
......@@ -1133,6 +1139,7 @@ protected int CheckAndAdjustPosition(int position)
else if (fullStart == fullEnd && position == fullEnd)
{
// The root is an empty span and isn't the full compilation unit. No other choice here.
token = default(SyntaxToken);
return fullStart;
}
......@@ -1399,7 +1406,8 @@ private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax)
options.ThrowIfInvalid();
position = CheckAndAdjustPosition(position);
SyntaxToken token;
position = CheckAndAdjustPosition(position, out token);
if ((object)container == null || container.Kind == SymbolKind.Namespace)
{
......@@ -1426,6 +1434,19 @@ private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax)
container = baseType;
}
if (!binder.IsInMethodBody && (options & LookupOptions.NamespacesOrTypesOnly) == 0)
{
// Method type parameters are not in scope outside a method body unless
// the position is either:
// a) in a type-only context inside an expression, or
// b) inside of an XML name attribute in an XML doc comment.
var parentExpr = token.Parent as ExpressionSyntax;
if (parentExpr != null && !(parentExpr.Parent is XmlNameAttributeSyntax) && !SyntaxFacts.IsInTypeOnlyContext(parentExpr))
{
options |= LookupOptions.MustNotBeMethodTypeParameter;
}
}
var info = LookupSymbolsInfo.GetInstance();
if ((object)container == null)
......
......@@ -2359,5 +2359,177 @@ public void Bug1068547_02()
Assert.Null(symbolInfo.Symbol);
Assert.Equal(CandidateReason.NotReferencable, symbolInfo.CandidateReason);
}
[Fact, WorkItem(1078958, "DevDiv")]
public void Bug1078958()
{
const string source = @"
class C
{
static void Foo<T>()
{
T();
}
static void T() { }
}";
CreateCompilationWithMscorlib(source).VerifyDiagnostics(
// (6,9): error CS0119: 'T' is a type, which is not valid in the given context
// T();
Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(6, 9));
}
[Fact, WorkItem(1078958, "DevDiv")]
public void Bug1078958_2()
{
const string source = @"
class C
{
static void Foo<T>()
{
T<T>();
}
static void T() { }
static void T<U>() { }
}";
CreateCompilationWithMscorlib(source).VerifyDiagnostics();
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961()
{
const string source = @"
class C
{
const int T = 42;
static void Foo<T>(int x = T)
{
System.Console.Write(x);
}
static void Main()
{
Foo<object>();
}
}";
CompileAndVerify(source, expectedOutput: "42");
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_2()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
static void Foo<T>([A(T)] int x)
{
}
}";
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics();
var c = comp.GlobalNamespace.GetTypeMembers("C").Single();
var t = (FieldSymbol)c.GetMembers("T").Single();
var foo = (MethodSymbol)c.GetMembers("Foo").Single();
var x = foo.Parameters[0];
var a = x.GetAttributes()[0];
var i = a.ConstructorArguments.Single();
Assert.Equal((int)i.Value, (int)t.ConstantValue);
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_3()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
[A(T)]
static void Foo<T>(int x)
{
}
}";
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics();
var c = comp.GlobalNamespace.GetTypeMembers("C").Single();
var t = (FieldSymbol)c.GetMembers("T").Single();
var foo = (MethodSymbol)c.GetMembers("Foo").Single();
var a = foo.GetAttributes()[0];
var i = a.ConstructorArguments.Single();
Assert.Equal((int)i.Value, (int)t.ConstantValue);
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_4()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
static void Foo<[A(T)] T>(int x)
{
}
}";
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics();
var c = comp.GlobalNamespace.GetTypeMembers("C").Single();
var t = (FieldSymbol)c.GetMembers("T").Single();
var foo = (MethodSymbol)c.GetMembers("Foo").Single();
var tt = foo.TypeParameters[0];
var a = tt.GetAttributes()[0];
var i = a.ConstructorArguments.Single();
Assert.Equal((int)i.Value, (int)t.ConstantValue);
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_5()
{
const string source = @"
class C
{
class T { }
static void Foo<T>(T x = default(T))
{
System.Console.Write((object)x == null);
}
static void Main()
{
Foo<object>();
}
}";
CompileAndVerify(source, expectedOutput: "True");
}
}
}
......@@ -1649,6 +1649,168 @@ void M()
Assert.Equal(2, symbols.Length);
}
[Fact, WorkItem(1078958, "DevDiv")]
public void Bug1078958()
{
const string source = @"
class C
{
static void Foo<T>()
{
/*<bind>*/T/*</bind>*/();
}
static void T() { }
}";
var symbols = GetLookupSymbols(source);
Assert.True(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961()
{
const string source = @"
class C
{
const int T = 42;
static void Foo<T>(int x = /*<bind>*/T/*</bind>*/)
{
System.Console.Write(x);
}
static void Main()
{
Foo<object>();
}
}";
var symbols = GetLookupSymbols(source);
Assert.False(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_2()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
static void Foo<T>([A(/*<bind>*/T/*</bind>*/)] int x)
{
}
}";
var symbols = GetLookupSymbols(source);
Assert.False(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_3()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
[A(/*<bind>*/T/*</bind>*/)]
static void Foo<T>(int x)
{
}
}";
var symbols = GetLookupSymbols(source);
Assert.False(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_4()
{
const string source = @"
class A : System.Attribute
{
public A(int i) { }
}
class C
{
const int T = 42;
static void Foo<[A(/*<bind>*/T/*</bind>*/)] T>(int x)
{
}
}";
var symbols = GetLookupSymbols(source);
Assert.False(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_5()
{
const string source = @"
class C
{
class T { }
static void Foo<T>(T x = default(/*<bind>*/T/*</bind>*/))
{
System.Console.Write((object)x == null);
}
static void Main()
{
Foo<object>();
}
}";
var symbols = GetLookupSymbols(source);
Assert.True(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
[Fact, WorkItem(1078961, "DevDiv")]
public void Bug1078961_6()
{
const string source = @"
class C
{
class T { }
static void Foo<T>(T x = default(/*<bind>*/T/*</bind>*/))
{
System.Console.Write((object)x == null);
}
static void Main()
{
Foo<object>();
}
}";
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics();
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var position = GetPositionForBinding(tree);
var symbols = model.LookupNamespacesAndTypes(position);
Assert.True(symbols.Any(s => s.Kind == SymbolKind.TypeParameter));
}
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册