Skip to content

Commit

Permalink
Fix missing duplicate diagnostic for enum declarations (#68967)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikkiGibson authored Jul 18, 2023
1 parent 993fcc8 commit e18fda0
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -847,18 +847,21 @@ internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo<AssemblySym

internal bool IsUnsafe => HasFlag(DeclarationModifiers.Unsafe);

internal SyntaxTree AssociatedSyntaxTree => declaration.Declarations[0].Location.SourceTree;
/// <summary>
/// If this type is file-local, the syntax tree in which the type is declared. Otherwise, null.
/// </summary>
private SyntaxTree? AssociatedSyntaxTree => IsFileLocal ? declaration.Declarations[0].Location.SourceTree : null;

internal sealed override FileIdentifier? AssociatedFileIdentifier
{
get
{
if (!IsFileLocal)
if (AssociatedSyntaxTree is not SyntaxTree syntaxTree)
{
return null;
}

return FileIdentifier.Create(AssociatedSyntaxTree);
return FileIdentifier.Create(syntaxTree);
}
}

Expand Down Expand Up @@ -1309,6 +1312,11 @@ private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> MakeTy
var conflictDict = new Dictionary<(string name, int arity, SyntaxTree? syntaxTree), SourceNamedTypeSymbol>();
try
{
// Declarations which can be merged into a single type symbol have already been merged at this phase.
// Merging behaves the same in either presence or absence of 'partial' modifiers.
// However, type declarations which can never be partial won't merge, e.g. 'enum',
// and type declarations with different kinds, e.g. 'class' and 'struct' will never merge.
// Now we want to figure out if declarations which didn't merge have name conflicts.
foreach (var childDeclaration in declaration.Children)
{
var t = new SourceNamedTypeSymbol(this, childDeclaration, diagnostics);
Expand Down
234 changes: 234 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,234 @@ void symbolValidator(ModuleSymbol symbol)
}
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void NonFileLocalClass_Duplicate()
{
var source = @"
public class D { }
public partial class C
{
public class D { }
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(2,14): error CS0101: The namespace '<global namespace>' already contains a definition for 'D'
// public class D { }
Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "D").WithArguments("D", "<global namespace>").WithLocation(2, 14),
// 1.cs(6,18): error CS0102: The type 'C' already contains a definition for 'D'
// public class D { }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "D").WithArguments("C", "D").WithLocation(6, 18));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void FileLocalClass_Duplicate()
{
var source = @"
file class D { }
public partial class C
{
file class D { } // 1
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(6,16): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file class D { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 16),
// 0.cs(6,16): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file class D { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 16));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void NonFileLocalEnum_Duplicate()
{
var source = @"
public enum E { }
public partial class C
{
public enum E { }
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(2,13): error CS0101: The namespace '<global namespace>' already contains a definition for 'E'
// public enum E { }
Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "E").WithArguments("E", "<global namespace>").WithLocation(2, 13),
// 1.cs(6,17): error CS0102: The type 'C' already contains a definition for 'E'
// public enum E { }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(6, 17));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void MixedFileLocalClass_Duplicate()
{
var source1 = @"
file class D { }
public partial class C
{
file class D { } // 1
}
";

var source2 = @"
public class D { }
public partial class C
{
public class D { }
}
";

var comp = CreateCompilation(new[] { source1, source2 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,16): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file class D { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 16));

comp = CreateCompilation(new[] { source2, source1 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,16): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file class D { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 16));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void FileLocalEnum_Duplicate()
{
var source = @"
file enum E { }
public partial class C
{
file enum E { } // 1
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(6,15): error CS9054: File-local type 'C.E' must be defined in a top level type; 'C.E' is a nested type.
// file enum E { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "E").WithArguments("C.E").WithLocation(6, 15),
// 0.cs(6,15): error CS9054: File-local type 'C.E' must be defined in a top level type; 'C.E' is a nested type.
// file enum E { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "E").WithArguments("C.E").WithLocation(6, 15));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void MixedFileLocalEnum_Duplicate()
{
var source1 = @"
file enum E { }
public partial class C
{
file enum E { } // 1
}
";

var source2 = @"
public enum E { }
public partial class C
{
public enum E { }
}
";

var comp = CreateCompilation(new[] { source1, source2 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,15): error CS9054: File-local type 'C.E' must be defined in a top level type; 'C.E' is a nested type.
// file enum E { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "E").WithArguments("C.E").WithLocation(6, 15));

comp = CreateCompilation(new[] { source2, source1 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,15): error CS9054: File-local type 'C.E' must be defined in a top level type; 'C.E' is a nested type.
// file enum E { } // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "E").WithArguments("C.E").WithLocation(6, 15));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void NonFileLocalDelegate_Duplicate()
{
var source = @"
public delegate void D();
public partial class C
{
public delegate void D();
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(2,22): error CS0101: The namespace '<global namespace>' already contains a definition for 'D'
// public delegate void D();
Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "D").WithArguments("D", "<global namespace>").WithLocation(2, 22),
// 1.cs(6,26): error CS0102: The type 'C' already contains a definition for 'D'
// public delegate void D();
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "D").WithArguments("C", "D").WithLocation(6, 26));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void MixedFileLocalDelegate_Duplicate()
{
var source1 = @"
file delegate void D();
public partial class C
{
file delegate void D(); // 1
}
";

var source2 = @"
public delegate void D();
public partial class C
{
public delegate void D();
}
";

var comp = CreateCompilation(new[] { source1, source2 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,24): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file delegate void D(); // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 24));

comp = CreateCompilation(new[] { source2, source1 });
comp.VerifyEmitDiagnostics(
// 0.cs(6,24): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file delegate void D(); // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 24));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67258")]
public void FileLocalDelegate_Duplicate()
{
var source = @"
file delegate void D();
public partial class C
{
file delegate void D(); // 1
}
";
var comp = CreateCompilation(new[] { source, source });
comp.VerifyEmitDiagnostics(
// 1.cs(6,24): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file delegate void D(); // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 24),
// 0.cs(6,24): error CS9054: File-local type 'C.D' must be defined in a top level type; 'C.D' is a nested type.
// file delegate void D(); // 1
Diagnostic(ErrorCode.ERR_FileTypeNested, "D").WithArguments("C.D").WithLocation(6, 24));
}

[Fact]
public void OtherFileUse()
{
Expand Down Expand Up @@ -605,6 +833,8 @@ file class C
verify();

comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp");
verify();

void verify()
{
comp.VerifyDiagnostics();
Expand Down Expand Up @@ -685,6 +915,8 @@ file class C<T>
verify();

comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp");
verify();

void verify()
{
comp.VerifyDiagnostics();
Expand Down Expand Up @@ -717,6 +949,8 @@ file class C
verify();

comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp");
verify();

void verify()
{
comp.VerifyEmitDiagnostics();
Expand Down

0 comments on commit e18fda0

Please sign in to comment.