Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle cases where 'field' keyword shouldn't generate a field #60806

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ protected SourcePropertySymbolBase(
_lazyExplicitInterfaceImplementations = ImmutableArray<PropertySymbol>.Empty;
}

bool isIndexer = IsIndexer;
isAutoProperty = isAutoProperty && !(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer;
isAutoProperty = isAutoProperty && CanHaveBackingField();

if (isAutoProperty)
{
Expand Down Expand Up @@ -162,7 +161,7 @@ protected SourcePropertySymbolBase(
_propertyFlags |= Flags.IsInitOnly;
}

if (isIndexer)
if (IsIndexer)
{
if (indexerNameAttributeLists.Count == 0 || isExplicitInterfaceImplementation)
{
Expand Down Expand Up @@ -216,8 +215,20 @@ protected SourcePropertySymbolBase(
return (SynthesizedBackingFieldSymbol?)_lazyBackingFieldSymbol;
}

private bool CanHaveBackingField()
{
return !(_containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !IsIndexer;
}

internal SynthesizedBackingFieldSymbol? GetOrCreateBackingFieldForFieldKeyword()
{
Debug.Assert(!IsIndexer);
if (!CanHaveBackingField())
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
{
MarkBackingFieldAsCalculated();
return (SynthesizedBackingFieldSymbol?)_lazyBackingFieldSymbol;
}

return GetOrCreateBackingField(isCreatedForFieldKeyword: true, isEarlyConstructed: false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,119 @@ private void VerifyTypeIL(CSharpCompilation compilation, string typeName, string
CompileAndVerify(compilation).VerifyTypeIL(typeName, expected);
}

[Fact]
public void TestInInterface()
{
var comp = CreateCompilation(@"
public interface I
{
public int P { get => field; }
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
}
", targetFramework: TargetFramework.NetCoreApp); // setting TargetFramework for DefaultImplementationsOfInterfaces to exist.

var accessorBindingData = new SourcePropertySymbolBase.AccessorBindingData();
comp.TestOnlyCompilationData = accessorBindingData;
comp.VerifyDiagnostics(
// (4,27): error CS0103: The name 'field' does not exist in the current context
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
// public int P { get => field; }
Diagnostic(ErrorCode.ERR_NameNotInContext, "field").WithArguments("field").WithLocation(4, 27)
);
var @interface = comp.GetTypeByMetadataName("I");
Assert.Empty(@interface.GetMembers().OfType<FieldSymbol>());
Assert.Empty(@interface.GetFieldsToEmit());
Assert.Equal(0, accessorBindingData.NumberOfPerformedAccessorBinding);
}

[Fact]
public void TestStaticInInterface()
{
var comp = CreateCompilation(@"
public interface I
{
public static int P { get => field; }
}
", targetFramework: TargetFramework.NetCoreApp); // setting TargetFramework for DefaultImplementationsOfInterfaces to exist.

var accessorBindingData = new SourcePropertySymbolBase.AccessorBindingData();
comp.TestOnlyCompilationData = accessorBindingData;
comp.VerifyDiagnostics();
VerifyTypeIL(comp, "I", @"
.class interface public auto ansi abstract I
{
// Fields
.field private static initonly int32 '<P>k__BackingField'
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public hidebysig specialname static
int32 get_P () cil managed
{
// Method begins at RVA 0x2050
// Code size 6 (0x6)
.maxstack 8
IL_0000: ldsfld int32 I::'<P>k__BackingField'
IL_0005: ret
} // end of method I::get_P
// Properties
.property int32 P()
{
.get int32 I::get_P()
}
} // end of class I
");
var @interface = comp.GetTypeByMetadataName("I");
Assert.Equal("System.Int32 I.<P>k__BackingField", @interface.GetFieldsToEmit().Single().ToTestDisplayString());
Assert.Empty(@interface.GetMembers().OfType<FieldSymbol>());
Assert.Equal(0, accessorBindingData.NumberOfPerformedAccessorBinding);
}

[Fact]
public void TestAbstract()
{
var comp = CreateCompilation(@"
public abstract class C
{
public abstract int P { get => field; }
}
"); // setting TargetFramework for DefaultImplementationsOfInterfaces to exist.

var accessorBindingData = new SourcePropertySymbolBase.AccessorBindingData();
comp.TestOnlyCompilationData = accessorBindingData;
comp.VerifyDiagnostics(
// (4,29): error CS0500: 'C.P.get' cannot declare a body because it is marked abstract
// public abstract int P { get => field; }
Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("C.P.get").WithLocation(4, 29)
);
var @class = comp.GetTypeByMetadataName("C");
Assert.Empty(@class.GetMembers().OfType<FieldSymbol>());
Assert.Empty(@class.GetFieldsToEmit());
Assert.Equal(0, accessorBindingData.NumberOfPerformedAccessorBinding);
}

[Fact]
public void TestExtern()
{
var comp = CreateCompilation(@"
public class C
{
public extern int P { get => field; }
}
");

var accessorBindingData = new SourcePropertySymbolBase.AccessorBindingData();
comp.TestOnlyCompilationData = accessorBindingData;
comp.VerifyDiagnostics(
// (4,27): error CS0179: 'C.P.get' cannot be extern and declare a body
// public extern int P { get => field; }
Diagnostic(ErrorCode.ERR_ExternHasBody, "get").WithArguments("C.P.get").WithLocation(4, 27)
);
var @class = comp.GetTypeByMetadataName("C");
Assert.Empty(@class.GetMembers().OfType<FieldSymbol>());
Assert.Empty(@class.GetFieldsToEmit());
Assert.Equal(0, accessorBindingData.NumberOfPerformedAccessorBinding);
}

[Fact]
public void TestNameOfFieldInAttribute()
{
Expand Down