提交 39b62e0c 编写于 作者: A AlekseyTs 提交者: GitHub

Merge pull request #16789 from AlekseyTs/Issue16748_2

Always create dedicated InitializerSemanticModel for parameter default value.
......@@ -360,7 +360,7 @@ internal BoundExpression BindValueAllowArgList(ExpressionSyntax node, Diagnostic
out BoundExpression valueBeforeConversion)
{
Debug.Assert(this.InParameterDefaultValue);
Debug.Assert(this.ContainingMember().Kind == SymbolKind.Method || this.ContainingMember().Kind == SymbolKind.Property);
Debug.Assert(this.ContainingMemberOrLambda.Kind == SymbolKind.Method || this.ContainingMemberOrLambda.Kind == SymbolKind.Property);
// UNDONE: The binding and conversion has to be executed in a checked context.
Binder defaultValueBinder = this.GetBinder(defaultValueSyntax);
......
......@@ -224,12 +224,6 @@ public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax no
var oldMethod = _containingMemberOrLambda;
_containingMemberOrLambda = match;
var parameterBinder = _enclosing.WithContainingMemberOrLambda(match).WithAdditionalFlags(BinderFlags.ParameterDefaultValue);
foreach (var parameter in node.ParameterList.Parameters)
{
Visit(parameter.Default, parameterBinder);
}
if (body != null)
{
Binder binder = match.IsGenericMethod
......
......@@ -102,6 +102,8 @@ public override CSharpSemanticModel ParentModel
internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
{
// We do have to override this method, but should never call it because it might not do the right thing.
Debug.Assert(false);
return IsInTree(node) ? this : null;
}
......@@ -658,10 +660,10 @@ public override IAliasSymbol GetDeclaredSymbol(ExternAliasDirectiveSyntax declar
public override IParameterSymbol GetDeclaredSymbol(ParameterSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Could be parameter of lambda.
// Could be parameter of a lambda or a local function.
CheckSyntaxNode(declarationSyntax);
return GetLambdaParameterSymbol(declarationSyntax, cancellationToken);
return GetLambdaOrLocalFunctionParameterSymbol(declarationSyntax, cancellationToken);
}
internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
......@@ -670,7 +672,7 @@ internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclaratio
return ImmutableArray.Create<ISymbol>();
}
private ParameterSymbol GetLambdaParameterSymbol(
private ParameterSymbol GetLambdaOrLocalFunctionParameterSymbol(
ParameterSyntax parameter,
CancellationToken cancellationToken)
{
......@@ -683,17 +685,25 @@ internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclaratio
}
var paramList = parameter.Parent as ParameterListSyntax;
if (paramList == null)
if (paramList == null || paramList.Parent == null)
{
return null;
}
if (paramList.Parent == null || !paramList.Parent.IsAnonymousFunction())
if (paramList.Parent.IsAnonymousFunction())
{
return null;
return GetLambdaParameterSymbol(parameter, (ExpressionSyntax)paramList.Parent, cancellationToken);
}
else if (paramList.Parent.Kind() == SyntaxKind.LocalFunctionStatement)
{
var localFunction = (MethodSymbol)GetDeclaredSymbol((LocalFunctionStatementSyntax)paramList.Parent, cancellationToken);
if ((object)localFunction != null)
{
return GetParameterSymbol(localFunction.Parameters, parameter, cancellationToken);
}
}
return GetLambdaParameterSymbol(parameter, (ExpressionSyntax)paramList.Parent, cancellationToken);
return null;
}
private ParameterSymbol GetLambdaParameterSymbol(
......
......@@ -21,6 +21,12 @@ internal partial class SyntaxTreeSemanticModel : CSharpSemanticModel
{
private readonly CSharpCompilation _compilation;
private readonly SyntaxTree _syntaxTree;
/// <summary>
/// Note, the name of this field could be somewhat confusing because it is also
/// used to store models for attributes and default parameter values, which are
/// not members.
/// </summary>
private ImmutableDictionary<CSharpSyntaxNode, MemberSemanticModel> _memberModels = ImmutableDictionary<CSharpSyntaxNode, MemberSemanticModel>.Empty;
private readonly BinderFactory _binderFactory;
......@@ -750,19 +756,13 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
switch (memberDecl.Kind())
{
case SyntaxKind.DelegateDeclaration:
{
DelegateDeclarationSyntax delegateDecl = (DelegateDeclarationSyntax)memberDecl;
return GetOrAddModelForParameterDefaultValue(delegateDecl.ParameterList.Parameters, span);
}
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.OperatorDeclaration:
{
var methodDecl = (BaseMethodDeclarationSyntax)memberDecl;
var expressionBody = methodDecl.GetExpressionBodySyntax();
return GetOrAddModelForParameterDefaultValue(methodDecl.ParameterList.Parameters, span) ??
GetOrAddModelIfContains(expressionBody, span) ??
return GetOrAddModelIfContains(expressionBody, span) ??
GetOrAddModelIfContains(methodDecl.Body, span);
}
......@@ -770,8 +770,7 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
{
ConstructorDeclarationSyntax constructorDecl = (ConstructorDeclarationSyntax)memberDecl;
var expressionBody = constructorDecl.GetExpressionBodySyntax();
return GetOrAddModelForParameterDefaultValue(constructorDecl.ParameterList.Parameters, span) ??
GetOrAddModelIfContains(constructorDecl.Initializer, span) ??
return GetOrAddModelIfContains(constructorDecl.Initializer, span) ??
GetOrAddModelIfContains(expressionBody, span) ??
GetOrAddModelIfContains(constructorDecl.Body, span);
}
......@@ -787,9 +786,7 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
case SyntaxKind.IndexerDeclaration:
{
var indexerDecl = (IndexerDeclarationSyntax)memberDecl;
return GetOrAddModelForParameterDefaultValue(
indexerDecl.ParameterList.Parameters, span) ??
GetOrAddModelIfContains(indexerDecl.ExpressionBody, span);
return GetOrAddModelIfContains(indexerDecl.ExpressionBody, span);
}
case SyntaxKind.FieldDeclaration:
......@@ -835,6 +832,9 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
case SyntaxKind.Attribute:
return GetOrAddModel(memberDecl);
case SyntaxKind.Parameter:
return GetOrAddModelForParameter((ParameterSyntax)memberDecl, span);
}
}
......@@ -854,18 +854,42 @@ private static bool IsInDocumentationComment(SyntaxNode node)
return false;
}
// Check each parameter for a default value containing span, and create an InitializerSemanticModel for binding the default value if so.
// Otherwise, return null.
private MemberSemanticModel GetOrAddModelForParameterDefaultValue(SeparatedSyntaxList<ParameterSyntax> parameterList, TextSpan span)
// Check parameter for a default value containing span, and create an InitializerSemanticModel for binding the default value if so.
// Otherwise, return model for enclosing context.
private MemberSemanticModel GetOrAddModelForParameter(ParameterSyntax paramDecl, TextSpan span)
{
foreach (ParameterSyntax paramDecl in parameterList)
EqualsValueClauseSyntax defaultValueSyntax = paramDecl.Default;
MemberSemanticModel containing = paramDecl.Parent != null ? GetMemberModel(paramDecl.Parent) : null;
if (containing == null)
{
MemberSemanticModel model = GetOrAddModelIfContains(paramDecl.Default, span);
if (model != null)
return model;
return GetOrAddModelIfContains(defaultValueSyntax, span);
}
return null;
if (defaultValueSyntax != null && defaultValueSyntax.FullSpan.Contains(span))
{
var parameterSymbol = (ParameterSymbol)containing.GetDeclaredSymbol(paramDecl);
if ((object)parameterSymbol != null)
{
return ImmutableInterlocked.GetOrAdd(ref _memberModels, defaultValueSyntax,
(equalsValue, tuple) =>
InitializerSemanticModel.Create(
tuple.compilation,
tuple.paramDecl,
tuple.parameterSymbol,
tuple.containing.GetEnclosingBinder(tuple.paramDecl.SpanStart).
CreateBinderForParameterDefaultValue(tuple.parameterSymbol,
(EqualsValueClauseSyntax)equalsValue)),
(compilation: this.Compilation,
paramDecl: paramDecl,
parameterSymbol: parameterSymbol,
containing: containing)
);
}
}
return containing;
}
private static CSharpSyntaxNode GetMemberDeclaration(SyntaxNode node)
......@@ -1138,7 +1162,8 @@ private Binder GetFieldOrPropertyInitializerBinder(FieldSymbol symbol, Binder ou
private static bool IsMemberDeclaration(CSharpSyntaxNode node)
{
return (node is MemberDeclarationSyntax) || (node is AccessorDeclarationSyntax) || (node is AttributeSyntax);
return (node is MemberDeclarationSyntax) || (node is AccessorDeclarationSyntax) ||
(node.Kind() == SyntaxKind.Attribute) || (node.Kind() == SyntaxKind.Parameter);
}
private bool IsRegularCSharp
......
......@@ -238,11 +238,9 @@ protected ConstantValue MakeDefaultExpression(DiagnosticBag diagnostics, Binder
binder = binderFactory.GetBinder(defaultSyntax);
}
Binder binderForDefault = binder.GetBinder(defaultSyntax);
if (binderForDefault == null)
{
binderForDefault = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax);
}
Debug.Assert(binder.GetBinder(defaultSyntax) == null);
Binder binderForDefault = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax);
Debug.Assert(binderForDefault.InParameterDefaultValue);
Debug.Assert(binderForDefault.ContainingMemberOrLambda == ContainingSymbol);
......
......@@ -2717,12 +2717,13 @@ public static void Main(int arg)
}
[Fact]
[WorkItem(16757, "https://github.com/dotnet/roslyn/issues/16757")]
public void LocalFunctionParameterDefaultUsingConst()
{
var source = @"
class C
{
public static void Main(string[] args)
public static void Main()
{
const int N = 2;
void Local1(int n = N) { System.Console.Write(n); }
......@@ -2744,14 +2745,18 @@ public static void Main(string[] args)
var model = compilation.GetSemanticModel(tree);
var descendents = tree.GetRoot().DescendantNodes();
var parameter = descendents.OfType<ParameterSyntax>().Single();
Assert.Equal("int n = N", parameter.ToString());
Assert.Equal("[System.Int32 n = 2]", model.GetDeclaredSymbol(parameter).ToTestDisplayString());
var name = "N";
var declarator = descendents.OfType<VariableDeclaratorSyntax>().Where(d => d.Identifier.ValueText == name).Single();
var symbol = (ILocalSymbol)model.GetDeclaredSymbol(declarator);
Assert.NotNull(symbol);
Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString());
Assert.Equal("System.Int32 N", symbol.ToTestDisplayString());
var refs = descendents.OfType<IdentifierNameSyntax>().Where(n => n.Identifier.ValueText == name).ToArray();
Assert.Equal(1, refs.Length);
Assert.Equal(symbol, model.GetSymbolInfo(refs[0]).Symbol);
Assert.Same(symbol, model.GetSymbolInfo(refs[0]).Symbol);
});
}
......
......@@ -940,12 +940,11 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
private static void VerifyModelForOutVarInNotExecutableCode(
SemanticModel model,
DeclarationExpressionSyntax decl,
bool boundNodesMissing,
IdentifierNameSyntax reference)
{
VerifyModelForOutVar(
model, decl, isDelegateCreation: false, isExecutableCode: false, isShadowed: false,
verifyDataFlow: true, boundNodesMissing: boundNodesMissing, references: reference);
verifyDataFlow: true, references: reference);
}
private static void VerifyModelForOutVar(
......@@ -955,7 +954,6 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
bool isExecutableCode,
bool isShadowed,
bool verifyDataFlow = true,
bool boundNodesMissing = false, // See https://github.com/dotnet/roslyn/issues/16374
params IdentifierNameSyntax[] references)
{
var variableDeclaratorSyntax = GetVariableDesignation(decl);
......@@ -993,7 +991,7 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
Assert.Equal(local.Type, model.GetSymbolInfo(typeSyntax).Symbol);
}
AssertInfoForDeclarationExpressionSyntax(model, decl, expectedSymbol: local, expectedType: local.Type, boundNodesMissing: boundNodesMissing);
AssertInfoForDeclarationExpressionSyntax(model, decl, expectedSymbol: local, expectedType: local.Type);
foreach (var reference in references)
{
......@@ -1013,8 +1011,7 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
SemanticModel model,
DeclarationExpressionSyntax decl,
Symbol expectedSymbol = null,
TypeSymbol expectedType = null,
bool boundNodesMissing = false
TypeSymbol expectedType = null
)
{
var symbolInfo = model.GetSymbolInfo(decl);
......@@ -1024,19 +1021,8 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
Assert.Equal(symbolInfo, ((CSharpSemanticModel)model).GetSymbolInfo(decl));
var typeInfo = model.GetTypeInfo(decl);
if (boundNodesMissing)
{
// This works around a bug that the semantic model does not cache the expression
// that is the default value of a local function parameter.
// See https://github.com/dotnet/roslyn/issues/16374
Assert.Equal(true, ((TypeSymbol)typeInfo.Type).IsErrorType());
Assert.Equal(true, ((TypeSymbol)typeInfo.ConvertedType).IsErrorType());
}
else
{
Assert.Equal(expectedType, typeInfo.Type);
Assert.Equal(expectedType, typeInfo.ConvertedType);
}
Assert.Equal(expectedType, typeInfo.Type);
Assert.Equal(expectedType, typeInfo.ConvertedType);
Assert.Equal(typeInfo, ((CSharpSemanticModel)model).GetTypeInfo(decl));
var conversion = model.ClassifyConversion(decl, model.Compilation.ObjectType, false);
......@@ -28919,8 +28905,70 @@ public static void Main(int arg)
var decl = GetOutVarDeclaration(tree, name);
var refs = GetReferences(tree, name).ToArray();
Assert.Equal(4, refs.Length);
var boundNodesMissing = i == 2; // See https://github.com/dotnet/roslyn/issues/16374
VerifyModelForOutVarInNotExecutableCode(model, decl, boundNodesMissing: boundNodesMissing, reference: refs[0]);
VerifyModelForOutVarInNotExecutableCode(model, decl, refs[0]);
VerifyNotInScope(model, refs[1]);
VerifyNotInScope(model, refs[2]);
VerifyNotInScope(model, refs[3]);
var symbol = (ILocalSymbol)model.GetDeclaredSymbol(decl.Designation);
Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString());
}
}
[Fact]
public void DeclarationInAnonymousMethodParameterDefault()
{
var text = @"
class C
{
public static void Main(int arg)
{
System.Action<bool, int> d1 = delegate (
bool b = M(M(out int z1), z1),
int s2 = z1)
{ var t = z1; };
System.Action<bool, int> d2 = delegate (
bool b = M(M(out var z2), z2),
int s2 = z2)
{ var t = z2; };
int x = z1 + z2;
d1 = d2 = null;
}
static int M(out int z) => z = 1;
static int M(int a, int b) => a+b;
}
";
// the scope of an expression variable introduced in the default expression
// of a lambda parameter is that default expression.
var compilation = CreateCompilationWithMscorlib45(text);
compilation.GetDiagnostics().Where(d => d.Code != (int)ErrorCode.ERR_DefaultValueNotAllowed).Verify(
// (9,55): error CS0103: The name 'z1' does not exist in the current context
// { var t = z1; };
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(9, 55),
// (13,55): error CS0103: The name 'z2' does not exist in the current context
// { var t = z2; };
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(13, 55),
// (15,17): error CS0103: The name 'z1' does not exist in the current context
// int x = z1 + z2;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(15, 17),
// (15,22): error CS0103: The name 'z2' does not exist in the current context
// int x = z1 + z2;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(15, 22)
);
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
var z1 = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == "z1").First();
Assert.Equal("System.Int32", model.GetTypeInfo(z1).Type.ToTestDisplayString());
for (int i = 1; i <= 2; i++)
{
var name = $"z{i}";
var decl = GetOutVarDeclaration(tree, name);
var refs = GetReferences(tree, name).ToArray();
Assert.Equal(4, refs.Length);
VerifyModelForOutVarInNotExecutableCode(model, decl, refs[0]);
VerifyNotInScope(model, refs[1]);
VerifyNotInScope(model, refs[2]);
VerifyNotInScope(model, refs[3]);
......@@ -28928,6 +28976,106 @@ public static void Main(int arg)
Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString());
}
}
[Fact]
public void Scope_LocalFunction_Attribute_01()
{
var source =
@"
public class X
{
public static void Main()
{
void Local1(
[Test(p = TakeOutParam(out int x3) && x3 > 0)]
[Test(p = x4 && TakeOutParam(out int x4))]
[Test(p = TakeOutParam(51, out int x5) &&
TakeOutParam(52, out int x5) &&
x5 > 0)]
[Test(p1 = TakeOutParam(out int x6) && x6 > 0,
p2 = TakeOutParam(out int x6) && x6 > 0)]
[Test(p = TakeOutParam(out int x7) && x7 > 0)]
[Test(p = x7 > 2)]
int p1)
{
Dummy(x7, p1);
}
Local1(1);
}
bool Dummy(params object[] x) {return true;}
static bool TakeOutParam(out int x)
{
x = 123;
return true;
}
static bool TakeOutParam(object y, out int x)
{
x = 123;
return true;
}
}
class Test : System.Attribute
{
public bool p {get; set;}
public bool p1 {get; set;}
public bool p2 {get; set;}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular);
compilation.GetDiagnostics().Where(d => d.Code != (int)ErrorCode.ERR_AttributesInLocalFuncDecl &&
d.Code != (int)ErrorCode.ERR_BadAttributeArgument).Verify(
// (18,19): error CS0103: The name 'x7' does not exist in the current context
// Dummy(x7, p1);
Diagnostic(ErrorCode.ERR_NameNotInContext, "x7").WithArguments("x7").WithLocation(18, 19),
// (8,23): error CS0841: Cannot use local variable 'x4' before it is declared
// [Test(p = x4 && TakeOutParam(out int x4))]
Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(8, 23),
// (10,48): error CS0128: A local variable or function named 'x5' is already defined in this scope
// TakeOutParam(52, out int x5) &&
Diagnostic(ErrorCode.ERR_LocalDuplicate, "x5").WithArguments("x5").WithLocation(10, 48),
// (13,45): error CS0128: A local variable or function named 'x6' is already defined in this scope
// p2 = TakeOutParam(out int x6) && x6 > 0)]
Diagnostic(ErrorCode.ERR_LocalDuplicate, "x6").WithArguments("x6").WithLocation(13, 45),
// (15,23): error CS0103: The name 'x7' does not exist in the current context
// [Test(p = x7 > 2)]
Diagnostic(ErrorCode.ERR_NameNotInContext, "x7").WithArguments("x7").WithLocation(15, 23)
);
var tree = compilation.SyntaxTrees.Single();
var model = compilation.GetSemanticModel(tree);
var x3Decl = GetOutVarDeclaration(tree, "x3");
var x3Ref = GetReference(tree, "x3");
VerifyModelForOutVarInNotExecutableCode(model, x3Decl, x3Ref);
var x4Decl = GetOutVarDeclaration(tree, "x4");
var x4Ref = GetReference(tree, "x4");
VerifyModelForOutVarInNotExecutableCode(model, x4Decl, x4Ref);
var x5Decl = GetOutVarDeclarations(tree, "x5").ToArray();
var x5Ref = GetReference(tree, "x5");
Assert.Equal(2, x5Decl.Length);
VerifyModelForOutVarInNotExecutableCode(model, x5Decl[0], x5Ref);
VerifyModelForOutVarDuplicateInSameScope(model, x5Decl[1]);
var x6Decl = GetOutVarDeclarations(tree, "x6").ToArray();
var x6Ref = GetReferences(tree, "x6").ToArray();
Assert.Equal(2, x6Decl.Length);
Assert.Equal(2, x6Ref.Length);
VerifyModelForOutVarInNotExecutableCode(model, x6Decl[0], x6Ref);
VerifyModelForOutVarDuplicateInSameScope(model, x6Decl[1]);
var x7Decl = GetOutVarDeclaration(tree, "x7");
var x7Ref = GetReferences(tree, "x7").ToArray();
Assert.Equal(3, x7Ref.Length);
VerifyModelForOutVarInNotExecutableCode(model, x7Decl, x7Ref[0]);
VerifyNotInScope(model, x7Ref[1]);
VerifyNotInScope(model, x7Ref[2]);
}
}
internal static class OutVarTestsExtensions
......@@ -531,6 +531,108 @@ class X
end class
]]></Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(16757, "https://github.com/dotnet/roslyn/issues/16757")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)>
Public Async Function TestParameterInLocalFunction1() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void Main()
{
void Local(int {|Definition:$$i|})
{
Console.WriteLine([|i|]);
}
Local(1);
}
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(16757, "https://github.com/dotnet/roslyn/issues/16757")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)>
Public Async Function TestParameterInLocalFunction2() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void Main()
{
void Local(int {|Definition:$$i|})
{
}
Local([|i|]:1);
}
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(16757, "https://github.com/dotnet/roslyn/issues/16757")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)>
Public Async Function TestParameterInLocalFunction3() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void Main()
{
void Local(int {|Definition:i|})
{
Console.WriteLine([|$$i|]);
}
Local(1);
}
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(16757, "https://github.com/dotnet/roslyn/issues/16757")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)>
Public Async Function TestParameterInLocalFunction4() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void Main()
{
void Local(int {|Definition:i|})
{
}
Local([|$$i|]:1);
}
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册