Skip to content
67 changes: 50 additions & 17 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2108,19 +2108,18 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi
(currentType.IsInterface && (declaringType.IsObjectType() || currentType.AllInterfacesNoUseSiteDiagnostics.Contains(declaringType))))
{
bool hasErrors = false;
if (EnclosingNameofArgument != node)
if (!IsInsideNameof || (EnclosingNameofArgument != node && !node.IsFeatureEnabled(MessageID.IDS_FeatureInstanceMemberInNameof)))
{
DiagnosticInfo diagnosticInfoOpt = null;
if (InFieldInitializer && !currentType.IsScriptClass)
{
//can't access "this" in field initializers
Error(diagnostics, ErrorCode.ERR_FieldInitRefNonstatic, node, member);
hasErrors = true;
diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_FieldInitRefNonstatic, member);
}
else if (InConstructorInitializer || InAttributeArgument)
{
//can't access "this" in constructor initializers or attribute arguments
Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member);
hasErrors = true;
diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member);
}
else
{
Expand All @@ -2132,12 +2131,24 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi
if (!locationIsInstanceMember)
{
// error CS0120: An object reference is required for the non-static field, method, or property '{0}'
Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member);
hasErrors = true;
diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member);
}
}

hasErrors = hasErrors || IsRefOrOutThisParameterCaptured(node, diagnostics);
diagnosticInfoOpt ??= GetDiagnosticIfRefOrOutThisParameterCaptured();
hasErrors = diagnosticInfoOpt is not null;

if (hasErrors)
{
if (IsInsideNameof)
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics);
}
else
{
Error(diagnostics, diagnosticInfoOpt, node);
}
}
}

return ThisReference(node, currentType, hasErrors, wasCompilerGenerated: true);
Expand Down Expand Up @@ -2309,20 +2320,35 @@ private BoundThisReference ThisReference(SyntaxNode node, NamedTypeSymbol thisTy
return new BoundThisReference(node, thisTypeOpt ?? CreateErrorType(), hasErrors) { WasCompilerGenerated = wasCompilerGenerated };
}

#nullable enable
private bool IsRefOrOutThisParameterCaptured(SyntaxNodeOrToken thisOrBaseToken, BindingDiagnosticBag diagnostics)
{
ParameterSymbol thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol();
// If there is no this parameter, then it is definitely not captured and
// any diagnostic would be cascading.
if ((object)thisSymbol != null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None)
if (GetDiagnosticIfRefOrOutThisParameterCaptured() is { } diagnosticInfo)
{
Error(diagnostics, ErrorCode.ERR_ThisStructNotInAnonMeth, thisOrBaseToken);
var location = thisOrBaseToken.GetLocation();
Debug.Assert(location is not null);
Error(diagnostics, diagnosticInfo, location);
return true;
}

return false;
}

private DiagnosticInfo? GetDiagnosticIfRefOrOutThisParameterCaptured()
{
Debug.Assert(this.ContainingMemberOrLambda is not null);
ParameterSymbol? thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol();
// If there is no this parameter, then it is definitely not captured and
// any diagnostic would be cascading.
if (thisSymbol is not null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None)
{
return new CSDiagnosticInfo(ErrorCode.ERR_ThisStructNotInAnonMeth);
}

return null;
}
#nullable disable

private BoundBaseReference BindBase(BaseExpressionSyntax node, BindingDiagnosticBag diagnostics)
{
bool hasErrors = false;
Expand Down Expand Up @@ -7746,10 +7772,17 @@ private bool CheckInstanceOrStatic(
{
if (instanceReceiver == true)
{
ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ?
ErrorCode.ERR_StaticMemberInObjectInitializer :
ErrorCode.ERR_ObjectProhibited;
Error(diagnostics, errorCode, node, symbol);
if (!IsInsideNameof)
{
ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ?
ErrorCode.ERR_StaticMemberInObjectInitializer :
ErrorCode.ERR_ObjectProhibited;
Error(diagnostics, errorCode, node, symbol);
}
else if (CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics))
{
return false;
}
resultKind = LookupResultKind.StaticInstanceMismatch;
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6814,6 +6814,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_UnreadRecordParameter_Title" xml:space="preserve">
<value>Parameter is unread. Did you forget to use it to initialize the property with that name?</value>
</data>
<data name="IDS_FeatureInstanceMemberInNameof" xml:space="preserve">
<value>instance member in 'nameof'</value>
</data>
<data name="ERR_RecordAmbigCtor" xml:space="preserve">
<value>The primary constructor conflicts with the synthesized copy constructor.</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ internal enum MessageID

IDS_FeaturePrimaryConstructors = MessageBase + 12833,
IDS_FeatureUsingTypeAlias = MessageBase + 12834,

IDS_FeatureInstanceMemberInNameof = MessageBase + 12835,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -449,6 +451,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureLambdaParamsArray: // semantic check
case MessageID.IDS_FeaturePrimaryConstructors: // declaration table check
case MessageID.IDS_FeatureUsingTypeAlias: // semantic check
case MessageID.IDS_FeatureInstanceMemberInNameof: // semantic check
return LanguageVersion.Preview;

// C# 11.0 features.
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5912,5 +5912,31 @@ public static void Main()
";
CreateCompilation(source).VerifyDiagnostics();
}

[Fact]
public void NameOf_Nested()
{
var source = """
System.Console.WriteLine(C.M());
public class C
{
private C c;
public static string M() => nameof(c.c.c);
}
""";

var expectedDiagnostic =
// (4,15): warning CS0649: Field 'C.c' is never assigned to, and will always have its default value null
// private C c;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "c").WithArguments("C.c", "null").WithLocation(4, 15);

CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic);
CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic);
CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(
expectedDiagnostic,
// (5,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public static string M() => nameof(c.c.c);
Diagnostic(ErrorCode.ERR_FeatureInPreview, "c").WithArguments("instance member in 'nameof'").WithLocation(5, 40));
}
}
}
Loading