diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index af8b99d17bd12..d198c83c08815 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -847,18 +847,21 @@ internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo HasFlag(DeclarationModifiers.Unsafe); - internal SyntaxTree AssociatedSyntaxTree => declaration.Declarations[0].Location.SourceTree; + /// + /// If this type is file-local, the syntax tree in which the type is declared. Otherwise, null. + /// + 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); } } @@ -1309,6 +1312,11 @@ private Dictionary, ImmutableArray> 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); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs index 97684fbc0b1ce..aae6fe6db6107 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs @@ -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 '' already contains a definition for 'D' + // public class D { } + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "D").WithArguments("D", "").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 '' already contains a definition for 'E' + // public enum E { } + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "E").WithArguments("E", "").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 '' already contains a definition for 'D' + // public delegate void D(); + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "D").WithArguments("D", "").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() { @@ -605,6 +833,8 @@ file class C verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); + verify(); + void verify() { comp.VerifyDiagnostics(); @@ -685,6 +915,8 @@ file class C verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); + verify(); + void verify() { comp.VerifyDiagnostics(); @@ -717,6 +949,8 @@ file class C verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); + verify(); + void verify() { comp.VerifyEmitDiagnostics();