提交 3e580ad4 编写于 作者: N Neal Gafter 提交者: GitHub

Expression variables declared in a local function parameter default are given a scope (#16467)

Fixes #16167
上级 584731ef
......@@ -14,8 +14,8 @@ This document reflects the status, and planned work, for the compiler team. It
| [Tuples](https://github.com/dotnet/roslyn/issues/347) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [vsadov](https://github.com/vsadov), [jcouv](https://github.com/jcouv) | [madstorgersen](https://github.com/MadsTorgersen) |
| [Out var](features/outvar.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [alekseyts](https://github.com/alekseyts) | [gafter](https://github.com/gafter) |
| [ValueTask<T>](https://github.com/ljw1004/roslyn/blob/features/async-return/docs/specs/feature%20-%20arbitrary%20async%20returns.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [alekseyts](https://github.com/alekseyts) | [lucian](https://github.com/ljw1004) |
| [Throw Expr](features/throwexpr.md) | [features/throwexpr](https://github.com/dotnet/roslyn/tree/features/throwexpr) | Prototyping | [gafter](https://github.com/gafter), [agocke](https://github.com/agocke), [tyoverby](https://github.com/tyoverby) | [gafter](https://github.com/gafter) |
| [Expression-Bodied Everything](https://github.com/dotnet/roslyn/issues/7881) | [features/exprbody](https://github.com/dotnet/roslyn/tree/features/exprbody) | Prototyping | [MgSam](https://github.com/MgSam), [gafter](https://github.com/gafter) | [madstorgersen](https://github.com/MadsTorgersen) |
| [Throw Expr](features/throwexpr.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [gafter](https://github.com/gafter), [agocke](https://github.com/agocke), [tyoverby](https://github.com/tyoverby) | [gafter](https://github.com/gafter) |
| [Expression-Bodied Everything](https://github.com/dotnet/roslyn/issues/7881) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [MgSam](https://github.com/MgSam), [gafter](https://github.com/gafter) | [madstorgersen](https://github.com/MadsTorgersen) |
## (C# 7.0 and VB 15) + 1
......@@ -24,7 +24,7 @@ This document reflects the status, and planned work, for the compiler team. It
| Address of Static | none | Feature Specification | | [jaredpar](https://github.com/jaredpar) |
| [Async Main](https://github.com/dotnet/roslyn/issues/7476) | none | Feature Specification | [tyoverby](https://github.com/tyoverby), [agocke](https://github.com/agocke) | [stephentoub](https://github.com/stephentoub) |
| [Source Generation](https://github.com/dotnet/roslyn/blob/master/docs/features/generators.md) | [master](https://github.com/dotnet/roslyn/tree/features/generators) | Prototyping | [cston](https://github.com/cston), [vsadov](https://github.com/vsadov), [agocke](https://github.com/agocke) | [mattwar](https://github.com/mattwar) |
| [private protected](https://github.com/dotnet/roslyn/blob/features/privateProtected/docs/features/private-protected.md) | [features/privateProtected](https://github.com/dotnet/roslyn/tree/features/privateProtected) | Prototyping | | [gafter](https://github.com/gafter) |
| [private protected](https://github.com/dotnet/roslyn/blob/features/privateProtected/docs/features/private-protected.md) | [features/privateProtected](https://github.com/dotnet/roslyn/tree/features/privateProtected) | Ready to integrate | | [gafter](https://github.com/gafter) |
| [Non-null Ref Types](https://github.com/dotnet/roslyn/blob/features/NullableReferenceTypes/docs/features/NullableReferenceTypes/Nullable%20reference%20types.md) | [features/NullableReferenceTypes](https://github.com/dotnet/roslyn/tree/features/NullableReferenceTypes) | Prototyping | [alekseyts](https://github.com/alekseyts) | [mattwar](https://github.com/mattwar) |
| [Bestest Betterness](https://github.com/dotnet/roslyn/issues/250) | none | Feature Specification | | [gafter](https://github.com/gafter) |
| [Records](https://github.com/dotnet/roslyn/blob/features/records/docs/features/records.md) | [features/records](https://github.com/dotnet/roslyn/tree/features/records) | Feature Specification | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) |
......
......@@ -224,6 +224,12 @@ 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
......
......@@ -238,8 +238,16 @@ 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(binderForDefault.InParameterDefaultValue);
Debug.Assert(binderForDefault.ContainingMemberOrLambda == ContainingSymbol);
BoundExpression valueBeforeConversion;
var convertedExpression = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax).BindParameterDefaultValue(defaultSyntax, parameterType, diagnostics, out valueBeforeConversion);
var convertedExpression = binderForDefault.BindParameterDefaultValue(defaultSyntax, parameterType, diagnostics, out valueBeforeConversion);
bool hasErrors = ParameterHelpers.ReportDefaultParameterErrors(binder, ContainingSymbol, parameterSyntax, this, valueBeforeConversion, diagnostics);
if (hasErrors)
......
......@@ -73,6 +73,7 @@
<Compile Include="FlowAnalysis\RegionAnalysisTests.cs" />
<Compile Include="FlowAnalysis\StructTests.cs" />
<Compile Include="FlowAnalysis\TryLockUsingStatementTests.cs" />
<Compile Include="Semantics\FuzzTests.cs" />
<Compile Include="Semantics\PatternMatchingTests_Global.cs" />
<Compile Include="Semantics\BindingAsyncTasklikeMoreTests.cs" />
<Compile Include="Semantics\BindingAsyncTasklikeTests.cs" />
......
// 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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using System.Linq;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
{
/// <summary>
/// Tests for problems discovered feeding the compiler random trash.
/// </summary>
public class FuzzTests : CompilingTestBase
{
[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")]
public void CompileXmlAsSource()
{
var text = @"
using System;
using System.Collections.Generic;
using System.Linq;
class C
{
public static void Main()
{
<summary>Copies a range of elements from an <see cref=""T:System.Array"" /> starting at the first element and pastes them into another <see cref=""T:System.Array"" /> starting at the first element. The length is specified as a 32-bit integer.</summary>
<param name=""sourceArray"">The <see cref=""T:System.Array"" /> that contains the data to copy.</param>
<param name=""destinationArray"">The <see cref=""T:System.Array"" /> that receives the data.</param>
<param name=""length"">A 32-bit integer that represents the number of elements to copy.</param>
<exception cref=""T:System.ArgumentNullException"">
<paramref name=""sourceArray"" /> is null.-or-<paramref name=""destinationArray"" /> is null.</exception>
<exception cref=""T:System.RankException"">
<paramref name=""sourceArray"" /> and <paramref name=""destinationArray"" /> have different ranks.</exception>
<exception cref=""T:System.ArrayTypeMismatchException"">
<paramref name=""sourceArray"" /> and <paramref name=""destinationArray"" /> are of incompatible types.</exception>
<exception cref=""T:System.InvalidCastException"">At least one element in <paramref name=""sourceArray"" /> cannot be cast to the type of <paramref name=""destinationArray"" />.</exception>
<exception cref=""T:System.ArgumentOutOfRangeException"">
<paramref name=""length"" /> is less than zero.</exception>
<exception cref=""T:System.ArgumentException"">
<paramref name=""length"" /> is greater than the number of elements in <paramref name=""sourceArray"" />.-or-<paramref name=""length"" /> is greater than the number of elements in <paramref name=""destinationArray"" />.</exception>
}
}
";
var compilation = CreateCompilationWithMscorlib45(text);
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
foreach (var node in tree.GetRoot().DescendantNodes())
{
var _ = model.GetSymbolInfo(node);
}
}
}
}
......@@ -6,6 +6,8 @@
using Roslyn.Test.Utilities;
using System;
using Xunit;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
......@@ -2159,5 +2161,219 @@ async Task<IntPtr> Local()
// await Task.Delay(2);
Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13));
}
[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")]
public void DeclarationInLocalFuncionParameterDefault()
{
var text = @"
class C
{
public static void Main(int arg)
{
void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
void Local2(bool b = M(M(out int z2), z2), int s2 = z2) {}
void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
void Local5(bool b = M(M(out var z5), z5), int s2 = z5) {}
void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
int t = z1 + z2 + z3 + z4 + z5 + z6;
}
static bool M(out int z) // needed to infer type of z5
{
z = 1;
return true;
}
static bool M(params object[] args) => true;
}
namespace System
{
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
}
}
";
// the scope of an expression variable introduced in the default expression
// of a local function parameter is that default expression.
var compilation = CreateCompilationWithMscorlib45(text);
compilation.VerifyDiagnostics(
// (6,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(arg is int z1, z1)").WithArguments("b").WithLocation(6, 30),
// (6,61): error CS0103: The name 'z1' does not exist in the current context
// void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(6, 61),
// (6,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s1").WithArguments("?", "int").WithLocation(6, 56),
// (7,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local2(bool b = M(M(out int z2), z2), int s2 = z2) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M(out int z2), z2)").WithArguments("b").WithLocation(7, 30),
// (7,61): error CS0103: The name 'z2' does not exist in the current context
// void Local2(bool b = M(M(out int z2), z2), int s2 = z2) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(7, 61),
// (7,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local2(bool b = M(M(out int z2), z2), int s2 = z2) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s2").WithArguments("?", "int").WithLocation(7, 56),
// (8,35): error CS8185: A declaration is not allowed in this context.
// void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int z3").WithLocation(8, 35),
// (8,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M((int z3, int a2) = (1, 2)), z3)").WithArguments("b").WithLocation(8, 30),
// (8,76): error CS0103: The name 'z3' does not exist in the current context
// void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z3").WithArguments("z3").WithLocation(8, 76),
// (8,71): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "a3").WithArguments("?", "int").WithLocation(8, 71),
// (10,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(arg is var z4, z4)").WithArguments("b").WithLocation(10, 30),
// (10,61): error CS0103: The name 'z4' does not exist in the current context
// void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z4").WithArguments("z4").WithLocation(10, 61),
// (10,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s1").WithArguments("?", "int").WithLocation(10, 56),
// (11,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local5(bool b = M(M(out var z5), z5), int s2 = z5) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M(out var z5), z5)").WithArguments("b").WithLocation(11, 30),
// (11,61): error CS0103: The name 'z5' does not exist in the current context
// void Local5(bool b = M(M(out var z5), z5), int s2 = z5) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z5").WithArguments("z5").WithLocation(11, 61),
// (11,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local5(bool b = M(M(out var z5), z5), int s2 = z5) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s2").WithArguments("?", "int").WithLocation(11, 56),
// (12,35): error CS8185: A declaration is not allowed in this context.
// void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var z6").WithLocation(12, 35),
// (12,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M((var z6, int a2) = (1, 2)), z6)").WithArguments("b").WithLocation(12, 30),
// (12,76): error CS0103: The name 'z6' does not exist in the current context
// void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
Diagnostic(ErrorCode.ERR_NameNotInContext, "z6").WithArguments("z6").WithLocation(12, 76),
// (12,71): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "a3").WithArguments("?", "int").WithLocation(12, 71),
// (14,17): error CS0103: The name 'z1' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(14, 17),
// (14,22): error CS0103: The name 'z2' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(14, 22),
// (14,27): error CS0103: The name 'z3' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z3").WithArguments("z3").WithLocation(14, 27),
// (14,32): error CS0103: The name 'z4' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z4").WithArguments("z4").WithLocation(14, 32),
// (14,37): error CS0103: The name 'z5' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z5").WithArguments("z5").WithLocation(14, 37),
// (14,42): error CS0103: The name 'z6' does not exist in the current context
// int t = z1 + z2 + z3 + z4 + z5 + z6;
Diagnostic(ErrorCode.ERR_NameNotInContext, "z6").WithArguments("z6").WithLocation(14, 42),
// (6,14): warning CS0168: The variable 'Local1' is declared but never used
// void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local1").WithArguments("Local1").WithLocation(6, 14),
// (7,14): warning CS0168: The variable 'Local2' is declared but never used
// void Local2(bool b = M(M(out int z2), z2), int s2 = z2) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local2").WithArguments("Local2").WithLocation(7, 14),
// (8,14): warning CS0168: The variable 'Local3' is declared but never used
// void Local3(bool b = M(M((int z3, int a2) = (1, 2)), z3), int a3 = z3) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local3").WithArguments("Local3").WithLocation(8, 14),
// (10,14): warning CS0168: The variable 'Local4' is declared but never used
// void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local4").WithArguments("Local4").WithLocation(10, 14),
// (11,14): warning CS0168: The variable 'Local5' is declared but never used
// void Local5(bool b = M(M(out var z5), z5), int s2 = z5) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local5").WithArguments("Local5").WithLocation(11, 14),
// (12,14): warning CS0168: The variable 'Local6' is declared but never used
// void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local6").WithArguments("Local6").WithLocation(12, 14)
);
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
var descendents = tree.GetRoot().DescendantNodes();
for (int i = 1; i <= 6; i++)
{
var name = $"z{i}";
var designation = descendents.OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == name).Single();
var symbol = (ILocalSymbol)model.GetDeclaredSymbol(designation);
Assert.NotNull(symbol);
Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString());
var refs = descendents.OfType<IdentifierNameSyntax>().Where(n => n.Identifier.ValueText == name).ToArray();
Assert.Equal(3, refs.Length);
Assert.Equal(symbol, model.GetSymbolInfo(refs[0]).Symbol);
Assert.Null(model.GetSymbolInfo(refs[1]).Symbol);
Assert.Null(model.GetSymbolInfo(refs[2]).Symbol);
}
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
public void RecursiveParameterDefault()
{
var text = @"
class C
{
public static void Main(int arg)
{
int Local(int x = Local()) => 2;
}
}
";
var compilation = CreateCompilationWithMscorlib45(text);
compilation.VerifyDiagnostics(
);
}
[Fact]
public void LocalFunctionParameterDefaultUsingConst()
{
var source = @"
class C
{
public static void Main(string[] args)
{
const int N = 2;
void Local1(int n = N) { System.Console.Write(n); }
Local1();
Local1(3);
}
}
";
CompileAndVerify(source, expectedOutput: "23", sourceSymbolValidator: m =>
{
var compilation = m.DeclaringCompilation;
// See https://github.com/dotnet/roslyn/issues/16454; this should actually produce no errors
compilation.VerifyDiagnostics(
// (6,19): warning CS0219: The variable 'N' is assigned but its value is never used
// const int N = 2;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "N").WithArguments("N").WithLocation(6, 19)
);
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
var descendents = tree.GetRoot().DescendantNodes();
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());
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);
});
}
}
}
......@@ -937,6 +937,17 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
VerifyModelForOutVar(model, decl, isDelegateCreation: false, isExecutableCode: false, isShadowed: false, verifyDataFlow: true, references: references);
}
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);
}
private static void VerifyModelForOutVar(
SemanticModel model,
DeclarationExpressionSyntax decl,
......@@ -944,6 +955,7 @@ 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);
......@@ -981,7 +993,7 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
Assert.Equal(local.Type, model.GetSymbolInfo(typeSyntax).Symbol);
}
AssertInfoForDeclarationExpressionSyntax(model, decl, expectedSymbol: local, expectedType: local.Type);
AssertInfoForDeclarationExpressionSyntax(model, decl, expectedSymbol: local, expectedType: local.Type, boundNodesMissing: boundNodesMissing);
foreach (var reference in references)
{
......@@ -997,7 +1009,13 @@ private static void VerifyModelForOutVarInNotExecutableCode(SemanticModel model,
}
}
private static void AssertInfoForDeclarationExpressionSyntax(SemanticModel model, DeclarationExpressionSyntax decl, Symbol expectedSymbol = null, TypeSymbol expectedType = null)
private static void AssertInfoForDeclarationExpressionSyntax(
SemanticModel model,
DeclarationExpressionSyntax decl,
Symbol expectedSymbol = null,
TypeSymbol expectedType = null,
bool boundNodesMissing = false
)
{
var symbolInfo = model.GetSymbolInfo(decl);
Assert.Equal(expectedSymbol, symbolInfo.Symbol);
......@@ -1006,8 +1024,19 @@ private static void AssertInfoForDeclarationExpressionSyntax(SemanticModel model
Assert.Equal(symbolInfo, ((CSharpSemanticModel)model).GetSymbolInfo(decl));
var typeInfo = model.GetTypeInfo(decl);
Assert.Equal(expectedType, typeInfo.Type);
Assert.Equal(expectedType, typeInfo.ConvertedType);
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(typeInfo, ((CSharpSemanticModel)model).GetTypeInfo(decl));
var conversion = model.ClassifyConversion(decl, model.Compilation.ObjectType, false);
......@@ -28794,6 +28823,83 @@ public static class S
Diagnostic(ErrorCode.ERR_BadArgType, "A _").WithArguments("2", "out A", "out B").WithLocation(8, 18)
);
}
[Fact]
public void DeclarationInLocalFunctionParameterDefault()
{
var text = @"
class C
{
public static void Main(int arg)
{
void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
int x = z1 + z2;
}
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 local function parameter is that default expression.
var compilation = CreateCompilationWithMscorlib45(text);
compilation.VerifyDiagnostics(
// (6,75): error CS0103: The name 'z1' does not exist in the current context
// void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(6, 75),
// (6,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M(out int z1), z1)").WithArguments("b").WithLocation(6, 30),
// (6,61): error CS0103: The name 'z1' does not exist in the current context
// void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
Diagnostic(ErrorCode.ERR_NameNotInContext, "z1").WithArguments("z1").WithLocation(6, 61),
// (6,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s2").WithArguments("?", "int").WithLocation(6, 56),
// (7,75): error CS0103: The name 'z2' does not exist in the current context
// void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(7, 75),
// (7,30): error CS1736: Default parameter value for 'b' must be a compile-time constant
// void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M(M(out var z2), z2)").WithArguments("b").WithLocation(7, 30),
// (7,61): error CS0103: The name 'z2' does not exist in the current context
// void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
Diagnostic(ErrorCode.ERR_NameNotInContext, "z2").WithArguments("z2").WithLocation(7, 61),
// (7,56): error CS1750: A value of type '?' cannot be used as a default parameter because there are no standard conversions to type 'int'
// void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s2").WithArguments("?", "int").WithLocation(7, 56),
// (9,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(9, 17),
// (9,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(9, 22),
// (6,14): warning CS0168: The variable 'Local2' is declared but never used
// void Local2(bool b = M(M(out int z1), z1), int s2 = z1) { var t = z1; }
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local2").WithArguments("Local2").WithLocation(6, 14),
// (7,14): warning CS0168: The variable 'Local5' is declared but never used
// void Local5(bool b = M(M(out var z2), z2), int s2 = z2) { var t = z2; }
Diagnostic(ErrorCode.WRN_UnreferencedVar, "Local5").WithArguments("Local5").WithLocation(7, 14)
);
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
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);
var boundNodesMissing = i == 2; // See https://github.com/dotnet/roslyn/issues/16374
VerifyModelForOutVarInNotExecutableCode(model, decl, boundNodesMissing: boundNodesMissing, reference: 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());
}
}
}
internal static class OutVarTestsExtensions
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册