/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs
5 165 строк
185 KB
Jan Jones
Validate pooled objects in compiler tests (#82784)
15 май 2026, 09:55
Не верифицирован
15 май 2026, 09:55
e141f0d
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests; public class FileModifierTests : CSharpTestBase { [Fact] public void LangVersion() { var source = """ file class C { } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (1,12): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater. // file class C { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "C").WithArguments("file types", "11.0").WithLocation(1, 12)); comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void Nested_01() { var source = """ class Outer { file class C { } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (3,16): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater. // file class C { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "C").WithArguments("file types", "11.0").WithLocation(3, 16), // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C { } Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C { } Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); } [Fact] public void Nested_02() { var source = """ file class Outer { class C { } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (1,12): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater. // file class Outer Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "Outer").WithArguments("file types", "11.0").WithLocation(1, 12)); verify(); comp = CreateCompilation(source); comp.VerifyDiagnostics(); verify(); void verify() { var outer = comp.GetMember<NamedTypeSymbol>("Outer"); Assert.Equal(Accessibility.Internal, outer.DeclaredAccessibility); Assert.True(((SourceMemberContainerTypeSymbol)outer).IsFileLocal); var classC = comp.GetMember<NamedTypeSymbol>("Outer.C"); Assert.Equal(Accessibility.Private, classC.DeclaredAccessibility); Assert.False(((SourceMemberContainerTypeSymbol)classC).IsFileLocal); } } [Fact] public void Nested_03() { var source = """ file class Outer { file class C { } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (1,12): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater. // file class Outer Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "Outer").WithArguments("file types", "11.0").WithLocation(1, 12), // (3,16): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater. // file class C { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "C").WithArguments("file types", "11.0").WithLocation(3, 16), // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C { } Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C { } Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); } [Fact] public void Nested_04() { var source = """ file class Outer { public class C { } } class D { void M(Outer.C c) { } // 1 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,10): error CS9051: File-local type 'Outer.C' cannot be used in a member signature in non-file-local type 'D'. // void M(Outer.C c) { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M").WithArguments("Outer.C", "D").WithLocation(8, 10)); } [Fact] public void Nested_05() { var source = """ file class Outer { public class C { void M1(Outer outer) { } // ok void M2(C outer) { } // ok } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void Nested_06() { var source = """ class A1 { internal class A2 { } } file class B : A1 { } class C : B.A2 // ok: base type is bound as A1.A2 { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void SameFileUse_01() { var source = """ using System; file class C { public static void M() { Console.Write(1); } } class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var symbol = comp.GetMember<NamedTypeSymbol>("C"); AssertEx.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C", symbol.MetadataName); // The qualified name here is based on `SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes`. // We don't actually look up based on the file-encoded name of the type. // This is similar to how generic types work (lookup based on 'C<T>' instead of 'C`1'). verifier.VerifyIL("C@<tree 0>.M", @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: call ""void System.Console.Write(int)"" IL_0006: ret }"); void symbolValidator(ModuleSymbol symbol) { Assert.Equal(new[] { "<Module>", "C", "Program", "Microsoft", "System" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = symbol.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); Assert.Equal(new[] { "M", ".ctor" }, classC.MemberNames); } } [Fact] public void SameFileUse_02() { var source = """ using System; file class C { public static void M() { Console.Write(1); } } class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { ("", "file1.cs"), (source, "file2.cs") }, expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var symbol = comp.GetMember<NamedTypeSymbol>("C"); AssertEx.Equal("<file2>F66382B88D8E28FDD21CEADA0DE847F8B00DA1324042DD28F8FFC58C454BD6188__C", symbol.MetadataName); // The qualified name here is based on `SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes`. // We don't actually look up based on the file-encoded name of the type. // This is similar to how generic types work (lookup based on 'C<T>' instead of 'C`1'). verifier.VerifyIL("C@file2.M", @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: call ""void System.Console.Write(int)"" IL_0006: ret }"); void symbolValidator(ModuleSymbol symbol) { Assert.Equal(new[] { "<Module>", "C", "Program", "Microsoft", "System" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = symbol.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); Assert.Equal(new[] { "M", ".ctor" }, classC.MemberNames); } } [Fact] public void FileEnum_01() { var source = """ using System; file enum E { E1, E2 } class Program { static void Main() { Console.Write(E.E2); } } """; var verifier = CompileAndVerify(source, expectedOutput: "E2", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var symbol = comp.GetMember<NamedTypeSymbol>("E"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__E", symbol.MetadataName); verifier.VerifyIL("Program.Main", @" { // Code size 12 (0xc) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: box ""E"" IL_0006: call ""void System.Console.Write(object)"" IL_000b: ret }"); void symbolValidator(ModuleSymbol symbol) { Assert.Equal(new[] { "<Module>", "E", "Program", "Microsoft", "System" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = symbol.GlobalNamespace.GetMember<NamedTypeSymbol>("E"); Assert.Equal(new[] { "value__", "E1", "E2", ".ctor" }, classC.MemberNames); } } [Fact] public void FileEnum_02() { var source = """ using System; file enum E { E1, E2 } file class Attr : Attribute { public Attr(E e) { } } [Attr(E.E2)] class Program { static void Main() { var data = typeof(Program).GetCustomAttributesData(); Console.Write(data[0].ConstructorArguments[0]); } } """; var verifier = CompileAndVerify(source, expectedOutput: "(<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__E)1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var symbol = comp.GetMember<NamedTypeSymbol>("E"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__E", symbol.MetadataName); void symbolValidator(ModuleSymbol symbol) { Assert.Equal(new[] { "<Module>", "E", "Attr", "Program", "Microsoft", "System" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = symbol.GlobalNamespace.GetMember<NamedTypeSymbol>("E"); Assert.Equal(new[] { "value__", "E1", "E2", ".ctor" }, classC.MemberNames); } } [Fact] public void FileEnum_03() { var source = """ using System; file enum E { E1, E2 } class Attr : Attribute { public Attr(E e) { } // 1 } [Attr(E.E2)] class Program { static void Main() { var data = typeof(Program).GetCustomAttributesData(); Console.Write(data[0].ConstructorArguments[0]); } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (10,12): error CS9051: File-local type 'E' cannot be used in a member signature in non-file-local type 'Attr'. // public Attr(E e) { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Attr").WithArguments("E", "Attr").WithLocation(10, 12)); } [Fact] public void FileEnum_04() { var source = """ using System; file enum E { E1, E2 } class Attr : Attribute { public Attr(object obj) { } } [Attr(E.E2)] class Program { static void Main() { var data = typeof(Program).GetCustomAttributesData(); Console.Write(data[0].ConstructorArguments[0]); } } """; var verifier = CompileAndVerify(source, expectedOutput: "(<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__E)1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var symbol = comp.GetMember<NamedTypeSymbol>("E"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__E", symbol.MetadataName); void symbolValidator(ModuleSymbol symbol) { Assert.Equal(new[] { "<Module>", "E", "Attr", "Program", "Microsoft", "System" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = symbol.GlobalNamespace.GetMember<NamedTypeSymbol>("E"); Assert.Equal(new[] { "value__", "E1", "E2", ".ctor" }, classC.MemberNames); } } [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() { var source1 = """ using System; file class C { public static void M() { Console.Write(1); } } """; var source2 = """ class Program { static void Main() { C.M(); // 1 } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (5,9): error CS0103: The name 'C' does not exist in the current context // C.M(); // 1 Diagnostic(ErrorCode.ERR_NameNotInContext, "C").WithArguments("C").WithLocation(5, 9)); } [Fact] public void Generic_01() { var source = """ using System; C<int>.M(1); file class C<T> { public static void M(T t) { Console.Write(t); } } """; var verifier = CompileAndVerify(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: "path/to/MyFile.cs", encoding: Encoding.Default), expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); verifier.VerifyIL("C<T>@MyFile.M(T)", @" { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: call ""void System.Console.Write(object)"" IL_000b: ret } "); var comp = (CSharpCompilation)verifier.Compilation; var c = comp.GetMember("C"); AssertEx.Equal("<MyFile>F5E7157F91336401EED4848664C7CEB8A5E156C0D713F4211A61BDB8932B19EF2__C`1", c.MetadataName); void symbolValidator(ModuleSymbol module) { Assert.Equal(new[] { "<Module>", "Program", "C", "Microsoft", "System" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); var classC = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); AssertEx.Equal("<MyFile>F5E7157F91336401EED4848664C7CEB8A5E156C0D713F4211A61BDB8932B19EF2__C`1", classC.MetadataName); Assert.Equal(new[] { "M", ".ctor" }, classC.MemberNames); } } [Fact] public void BadFileNames_01() { var source = """ using System; C.M(); file class C { public static void M() { Console.Write(1); } } """; const string expectedMetadataName = "<My__File>FCE8825365B7010B8DE2ACFBE270B3B795D1AB2633451F8A6C1A94FB1933D5E4E__C"; var verifier = CompileAndVerify(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: "path/to/My<>File.cs", encoding: Encoding.Default), expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var c = comp.GetMember("C"); Assert.Equal("C@My__File", c.ToTestDisplayString()); AssertEx.Equal(expectedMetadataName, c.MetadataName); void symbolValidator(ModuleSymbol module) { Assert.Equal(new[] { "<Module>", "Program", "C", "Microsoft", "System" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); var expectedSymbol = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); AssertEx.Equal(expectedMetadataName, expectedSymbol.MetadataName); Assert.Equal(new[] { "M", ".ctor" }, expectedSymbol.MemberNames); } } [Fact] public void BadFileNames_02() { var source = """ using System; C.M(); file class C { public static void M() { Console.Write(1); } } """; var verifier = CompileAndVerify(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: "path/to/MyGeneratedFile.g.cs", encoding: Encoding.Default), expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var c = comp.GetMember("C"); Assert.Equal("C@MyGeneratedFile_g", c.ToTestDisplayString()); AssertEx.Equal("<MyGeneratedFile_g>F18307E6C553D2E6465CEA162655C06E2BB2896889519559EB1EE5FA53513F0E8__C", c.MetadataName); void symbolValidator(ModuleSymbol module) { Assert.Equal(new[] { "<Module>", "Program", "C", "Microsoft", "System" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); var expectedSymbol = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); AssertEx.Equal("<MyGeneratedFile_g>F18307E6C553D2E6465CEA162655C06E2BB2896889519559EB1EE5FA53513F0E8__C", expectedSymbol.MetadataName); Assert.Equal(new[] { "M", ".ctor" }, expectedSymbol.MemberNames); } } [Theory] [InlineData(""" file class Outer1 { } file class Outer2 { } """, "Outer2")] [InlineData(""" file class Outer { } """, "Outer")] public void Determinism(string source, string fileTypeName) { var (root1, root2) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (@"q:\", @"j:\") : ("/q", "/j"); var testSource1 = CSharpTestSource.Parse(source, Path.Combine(root1, "code.cs")); var testSource2 = CSharpTestSource.Parse(source, Path.Combine(root2, "code.cs")); var options = TestOptions.DebugDll.WithDeterministic(true); var comp1 = CreateCompilation(testSource1, options: options, assemblyName: "filetypes"); var comp2 = CreateCompilation(testSource2, options: options, assemblyName: "filetypes"); var resolver = new SourceFileResolver( ImmutableArray<string>.Empty, baseDirectory: null, ImmutableArray.Create(new KeyValuePair<string, string>(root2, root1))); var comp3 = CreateCompilation(testSource2, options: options.WithSourceReferenceResolver(resolver), assemblyName: "filetypes"); var outer1 = comp1.GetMember<NamedTypeSymbol>(fileTypeName).AssociatedFileIdentifier; var outer2 = comp2.GetMember<NamedTypeSymbol>(fileTypeName).AssociatedFileIdentifier; var outer3 = comp3.GetMember<NamedTypeSymbol>(fileTypeName).AssociatedFileIdentifier; Assert.False(outer1.FilePathChecksumOpt.IsDefaultOrEmpty); Assert.False(outer2.FilePathChecksumOpt.IsDefaultOrEmpty); Assert.False(outer3.FilePathChecksumOpt.IsDefaultOrEmpty); Assert.False(outer1.FilePathChecksumOpt.SequenceEqual(outer2.FilePathChecksumOpt)); Assert.True(outer1.FilePathChecksumOpt.SequenceEqual(outer3.FilePathChecksumOpt)); var emitOptions = EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded); var bytes1 = comp1.EmitToArray(emitOptions); var bytes2 = comp2.EmitToArray(emitOptions); var bytes3 = comp3.EmitToArray(emitOptions); Assert.False(bytes1.SequenceEqual(bytes2)); Assert.True(bytes1.SequenceEqual(bytes3)); } [Fact] public void DuplicateFileNames_01() { var path = "path/to/file.cs"; var source1 = SyntaxFactory.ParseSyntaxTree(""" using System; C.M(); file class C { public static void M() { Console.Write(1); } } """, options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var source2 = SyntaxFactory.ParseSyntaxTree("", options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var comp = CreateCompilation(new[] { source1, source2 }, assemblyName: "comp"); verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); verify(); void verify() { comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // path/to/file.cs(5,12): error CS9067: File-local type 'C' must be declared in a file with a unique path. Path 'path/to/file.cs' is used in multiple files. // file class C Diagnostic(ErrorCode.ERR_FileTypeNonUniquePath, "C").WithArguments("C", "path/to/file.cs").WithLocation(5, 12)); var classC = comp.GetMember("C"); Assert.Equal(source1, classC.Locations[0].SourceTree); AssertEx.Equal("<file>F620949CDCC480533E3607E5DD92F88E866EC1D65C225D70509A32F831433D9A4__C", classC.MetadataName); } } [Fact] public void DuplicateFileNames_02() { var path = "path/to/file.cs"; var source1 = SyntaxFactory.ParseSyntaxTree(""" using System; C.M(); file class C { public static void M() { Console.Write(1); } } """, options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var source2 = SyntaxFactory.ParseSyntaxTree(""" using System; namespace NS; file class C { public static void M() { Console.Write(2); } } """, options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var comp = CreateCompilation(new[] { source1, source2 }, assemblyName: "comp"); verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); verify(); void verify() { comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // path/to/file.cs(5,12): error CS9067: File-local type 'C' must be declared in a file with a unique path. Path 'path/to/file.cs' is used in multiple files. // file class C Diagnostic(ErrorCode.ERR_FileTypeNonUniquePath, "C").WithArguments("NS.C", "path/to/file.cs").WithLocation(5, 12), // path/to/file.cs(5,12): error CS9067: File-local type 'C' must be declared in a file with a unique path. Path 'path/to/file.cs' is used in multiple files. // file class C Diagnostic(ErrorCode.ERR_FileTypeNonUniquePath, "C").WithArguments("C", "path/to/file.cs").WithLocation(5, 12)); var member = comp.GetMember("C"); Assert.Equal(source1, member.Locations[0].SourceTree); AssertEx.Equal("<file>F620949CDCC480533E3607E5DD92F88E866EC1D65C225D70509A32F831433D9A4__C", member.MetadataName); } } [Fact] public void DuplicateFileNames_03() { var path = "path/to/file.cs"; var source1 = SyntaxFactory.ParseSyntaxTree(""" using System; namespace NS1.NS2; file class C<T> { public static void M() { Console.Write(1); } } """, options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var source2 = SyntaxFactory.ParseSyntaxTree("", options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); var comp = CreateCompilation(new[] { source1, source2 }, assemblyName: "comp"); verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); verify(); void verify() { comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // path/to/file.cs(5,12): error CS9067: File-local type 'C<T>' must be declared in a file with a unique path. Path 'path/to/file.cs' is used in multiple files. // file class C<T> Diagnostic(ErrorCode.ERR_FileTypeNonUniquePath, "C").WithArguments("NS1.NS2.C<T>", "path/to/file.cs").WithLocation(5, 12)); var classC = comp.GetMember("NS1.NS2.C"); Assert.Equal(source1, classC.Locations[0].SourceTree); AssertEx.Equal("<file>F620949CDCC480533E3607E5DD92F88E866EC1D65C225D70509A32F831433D9A4__C`1", classC.MetadataName); } } [Fact] public void DuplicateFileNames_04() { var source1 = SyntaxFactory.ParseSyntaxTree(""" using System; C.M(); file class C { public static void M() { Console.Write(1); } } """, options: TestOptions.RegularPreview, path: "path/to/file.cs", encoding: Encoding.Default); var source2 = SyntaxFactory.ParseSyntaxTree("", options: TestOptions.RegularPreview, path: "path/to/File.cs", encoding: Encoding.Default); var comp = CreateCompilation(new[] { source1, source2 }, assemblyName: "comp"); verify(); comp = CreateCompilation(new[] { source2, source1 }, assemblyName: "comp"); verify(); void verify() { comp.VerifyEmitDiagnostics(); var classC = comp.GetMember("C"); Assert.Equal(source1, classC.Locations[0].SourceTree); AssertEx.Equal("<file>F620949CDCC480533E3607E5DD92F88E866EC1D65C225D70509A32F831433D9A4__C", classC.MetadataName); } } [Fact] public void DuplicateFileNames_05() { var source1 = """ using System; file class C // 1 { public static void M() { Console.Write(1); } } """; var source2 = """ class Program { static void Main() { C.M(); } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file1.cs") }); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // file1.cs(3,12): error CS9067: File-local type 'C' must be declared in a file with a unique path. Path 'file1.cs' is used in multiple files. // file class C // 1 Diagnostic(ErrorCode.ERR_FileTypeNonUniquePath, "C").WithArguments("C", "file1.cs").WithLocation(3, 12)); } // Data based on Lexer.ScanIdentifier_FastPath, excluding '/', '\', and ':' because those are path separators. [Theory] [InlineData('&')] [InlineData('\0')] [InlineData(' ')] [InlineData('\r')] [InlineData('\n')] [InlineData('\t')] [InlineData('!')] [InlineData('%')] [InlineData('(')] [InlineData(')')] [InlineData('*')] [InlineData('+')] [InlineData(',')] [InlineData('-')] [InlineData('.')] [InlineData(';')] [InlineData('<')] [InlineData('=')] [InlineData('>')] [InlineData('?')] [InlineData('[')] [InlineData(']')] [InlineData('^')] [InlineData('{')] [InlineData('|')] [InlineData('}')] [InlineData('~')] [InlineData('"')] [InlineData('\'')] [InlineData('`')] public void BadFileNames_03(char badChar) { var source = """ using System; C.M(); file class C { public static void M() { Console.Write(1); } } """; var comp = CreateCompilation(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: $"path/to/My{badChar}File.cs", encoding: Encoding.Default)); var sourceFileTypeSymbol = comp.GetMember("C"); var verifier = CompileAndVerify(comp, expectedOutput: "1", symbolValidator: symbolValidator); verifier.VerifyDiagnostics(); Assert.Equal("C@My_File", sourceFileTypeSymbol.ToTestDisplayString()); Assert.Matches(expectedRegexPattern: @"<My_File>F[\w\d]{64}__C", sourceFileTypeSymbol.MetadataName); void symbolValidator(ModuleSymbol module) { Assert.Equal(new[] { "<Module>", "Program", "C", "Microsoft", "System" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); var expectedSymbol = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); Assert.Equal(sourceFileTypeSymbol.MetadataName, expectedSymbol.MetadataName); Assert.Equal(new[] { "M", ".ctor" }, expectedSymbol.MemberNames); } } [ConditionalFact(typeof(IsEnglishLocal))] public void BadFileNames_04() { var source1 = """ new C(); // 1 file class C { } // 2 """; var comp = CreateCompilation(SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreview, path: "\uD800.cs")); comp.VerifyDiagnostics( // ?.cs(1,5): error CS0246: The type or namespace name 'C' could not be found (are you missing a using directive or an assembly reference?) // new C(); // 1 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "C").WithArguments("C").WithLocation(1, 5), // ?.cs(3,12): error CS9068: File-local type 'C' cannot be used because the containing file path cannot be converted into the equivalent UTF-8 byte representation. Unable to translate Unicode character \\uD800 at index 0 to specified code page. // file class C { } // 2 Diagnostic(ErrorCode.ERR_FilePathCannotBeConvertedToUtf8, "C") .WithArguments( "C", ExecutionConditionUtil.IsCoreClr ? @"Unable to translate Unicode character \\uD800 at index 0 to specified code page." : @"Unable to translate Unicode character \uD800 at index 0 to specified code page.") .WithLocation(3, 12) ); var classC = comp.GetMember("C"); Assert.Equal("<_>F<no checksum>__C", classC.MetadataName); Assert.Null(comp.GetTypeByMetadataName("<_>F<no checksum>__C")); } [Fact] public void Pdb_01() { var source = """ using System; C.M(); file class C { public static void M() { Console.Write(1); } } """; var verifier = CompileAndVerify(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: "path/to/My+File.cs", encoding: Encoding.Default), expectedOutput: "1", symbolValidator: validateSymbols); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var c = comp.GetMember("C"); Assert.Equal("C@My_File", c.ToTestDisplayString()); AssertEx.Equal("<My_File>FA818559F9E8E4AF40425A1819866C71357DE9017B4B7EFE1D34D9F48C0539B6E__C", c.MetadataName); void validateSymbols(ModuleSymbol module) { var type = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); Assert.NotNull(type); Assert.Equal(new[] { "M", ".ctor" }, type.MemberNames); } } #pragma warning disable format [Theory] [InlineData("file", "file", "<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C", "<file2>F66382B88D8E28FDD21CEADA0DE847F8B00DA1324042DD28F8FFC58C454BD6188__C")] [InlineData("file", "", "<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C", "C")] [InlineData("", "file", "C", "<file2>F66382B88D8E28FDD21CEADA0DE847F8B00DA1324042DD28F8FFC58C454BD6188__C")] #pragma warning restore format public void Duplication_01(string firstFileModifier, string secondFileModifier, string firstMetadataName, string secondMetadataName) { // A file-local type is allowed to have the same name as a non-file-local type from a different file. // When both a file-local type and non-file-local type with the same name are in scope, the file-local type is preferred, since it's "more local". var source1 = $$""" using System; {{firstFileModifier}} class C { public static void M() { Console.Write(1); } } """; var source2 = $$""" using System; {{secondFileModifier}} class C { public static void M() { Console.Write(2); } } """; var main = """ class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { (source1 + main, "file1.cs"), (source2, "file2.cs") }, expectedOutput: "1"); var comp = (CSharpCompilation)verifier.Compilation; var cs = comp.GetMembers("C"); var tree = comp.SyntaxTrees[0]; var expectedSymbol = cs[0]; AssertEx.Equal(firstMetadataName, expectedSymbol.MetadataName); verify(); verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2 + main, "file2.cs") }, expectedOutput: "2"); comp = (CSharpCompilation)verifier.Compilation; cs = comp.GetMembers("C"); tree = comp.SyntaxTrees[1]; expectedSymbol = cs[1]; AssertEx.Equal(secondMetadataName, expectedSymbol.MetadataName); verify(); void verify() { verifier.VerifyDiagnostics(); Assert.Equal(2, cs.Length); Assert.Equal(comp.SyntaxTrees[0], cs[0].DeclaringSyntaxReferences.Single().SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], cs[1].DeclaringSyntaxReferences.Single().SyntaxTree); var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last().Expression; var info = model.GetTypeInfo(cReference); Assert.Equal(expectedSymbol.GetPublicSymbol(), info.Type); } } [Fact] public void Duplication_02() { // As a sanity check, demonstrate that non-file classes with the same name across different files are disallowed. var source1 = """ using System; class C { public static void M() { Console.Write(1); } } """; var source2 = """ using System; class C { public static void M() { Console.Write(2); } } """; var main = """ class Program { static void Main() { C.M(); } } """; var comp = CreateCompilation(new[] { source1 + main, source2 }); verify(); comp = CreateCompilation(new[] { source1, source2 + main }); verify(); void verify() { comp.VerifyDiagnostics( // (3,7): error CS0101: The namespace '<global namespace>' already contains a definition for 'C' // class C Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C").WithArguments("C", "<global namespace>").WithLocation(3, 7), // (5,24): error CS0111: Type 'C' already defines a member called 'M' with the same parameter types // public static void M() Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "C").WithLocation(5, 24), // (14,11): error CS0121: The call is ambiguous between the following methods or properties: 'C.M() [0.cs(5)]' and 'C.M() [1.cs(5)]' // C.M(); Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M() [0.cs(5)]", "C.M() [1.cs(5)]").WithLocation(14, 11)); var cs = comp.GetMember("C"); var syntaxReferences = cs.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[1].SyntaxTree); } } [Fact] public void Duplication_03() { var source1 = """ using System; partial class C { public static void M() { Console.Write(1); } } """; var source2 = """ partial class C { } """; var main = """ using System; file class C { public static void M() { Console.Write(2); } } class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2, "file2.cs"), (main, "file3.cs") }, expectedOutput: "2"); var comp = (CSharpCompilation)verifier.Compilation; comp.VerifyDiagnostics(); var cs = comp.GetMembers("C"); Assert.Equal(2, cs.Length); var c0 = cs[0]; Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFileLocal: false }); var syntaxReferences = c0.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[1].SyntaxTree); var c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.Equal(comp.SyntaxTrees[2], c1.DeclaringSyntaxReferences.Single().SyntaxTree); var tree = comp.SyntaxTrees[2]; var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last().Expression; var info = model.GetTypeInfo(cReference); Assert.Equal(c1.GetPublicSymbol(), info.Type); } [Fact] public void Duplication_04() { var source1 = """ using System; class C { public static void M() { Console.Write(1); } } """; var main = """ using System; file partial class C { public static void M() { Console.Write(Number); } } file partial class C { private static int Number => 2; } class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (main, "file2.cs") }, expectedOutput: "2"); var comp = (CSharpCompilation)verifier.Compilation; comp.VerifyDiagnostics(); var cs = comp.GetMembers("C"); Assert.Equal(2, cs.Length); var c0 = cs[0]; Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFileLocal: false }); Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); var c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); var syntaxReferences = c1.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[1].SyntaxTree); var tree = comp.SyntaxTrees[1]; var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last().Expression; var info = model.GetTypeInfo(cReference); Assert.Equal(c1.GetPublicSymbol(), info.Type); } [Theory] [CombinatorialData] public void Duplication_05(bool firstClassIsFile) { var source1 = $$""" using System; {{(firstClassIsFile ? "file " : "")}}partial class C { public static void M() { Console.Write(1); } } """; var main = """ using System; file partial class C { public static void M() { Console.Write(2); } } class Program { static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (main, "file2.cs") }, expectedOutput: "2"); var comp = (CSharpCompilation)verifier.Compilation; comp.VerifyDiagnostics(); var cs = comp.GetMembers("C"); Assert.Equal(2, cs.Length); var c0 = cs[0]; Assert.Equal(firstClassIsFile, ((SourceMemberContainerTypeSymbol)c0).IsFileLocal); Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); var c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.Equal(comp.SyntaxTrees[1], c1.DeclaringSyntaxReferences.Single().SyntaxTree); var tree = comp.SyntaxTrees[1]; var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last().Expression; var info = model.GetTypeInfo(cReference); Assert.Equal(c1.GetPublicSymbol(), info.Type); } [Fact] public void Duplication_06() { // note: we avoid `using System;` here because we don't want to attempt to bind to `System.Number` var source1 = """ namespace NS; partial class C { public static void M() { System.Console.Write(Number); } } """; var source2 = """ namespace NS; partial class C { private static int Number => 1; } file class C { public static void M() { System.Console.Write(2); } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // file2.cs(8,12): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // file class C Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(8, 12) ); var cs = comp.GetMembers("NS.C"); Assert.Equal(2, cs.Length); var c0 = cs[0]; Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFileLocal: false }); var syntaxReferences = c0.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[1].SyntaxTree); var c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.Equal(comp.SyntaxTrees[1], c1.DeclaringSyntaxReferences.Single().SyntaxTree); comp = CreateCompilation(new[] { (source2, "file2.cs"), (source1, "file1.cs") }); comp.VerifyDiagnostics( // file1.cs(5,24): error CS0111: Type 'C' already defines a member called 'M' with the same parameter types // public static void M() Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "NS.C").WithLocation(5, 24), // file1.cs(7,30): error CS0103: The name 'Number' does not exist in the current context // System.Console.Write(Number); Diagnostic(ErrorCode.ERR_NameNotInContext, "Number").WithArguments("Number").WithLocation(7, 30), // file2.cs(8,12): error CS0260: Missing partial modifier on declaration of type 'C'; another partial declaration of this type exists // file class C Diagnostic(ErrorCode.ERR_MissingPartial, "C").WithArguments("C").WithLocation(8, 12) ); var c = comp.GetMember("NS.C"); Assert.True(c is SourceMemberContainerTypeSymbol { IsFileLocal: true }); syntaxReferences = c.DeclaringSyntaxReferences; Assert.Equal(3, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[1].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[2].SyntaxTree); } [Fact] public void Duplication_07() { var source1 = """ using System; file partial class C { public static void M() { Console.Write(1); } } """; var source2 = """ using System; file partial class C { public static void M() { Console.Write(Number); } } file class C { private static int Number => 2; } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (11,12): error CS0260: Missing partial modifier on declaration of type 'C'; another partial declaration of this type exists // file class C Diagnostic(ErrorCode.ERR_MissingPartial, "C").WithArguments("C").WithLocation(11, 12)); var cs = comp.GetMembers("C"); Assert.Equal(2, cs.Length); var c0 = cs[0]; Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); var c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); var syntaxReferences = c1.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], syntaxReferences[1].SyntaxTree); comp = CreateCompilation(new[] { (source2, "file2.cs"), (source1, "file1.cs") }); comp.VerifyDiagnostics( // (11,12): error CS0260: Missing partial modifier on declaration of type 'C'; another partial declaration of this type exists // file class C Diagnostic(ErrorCode.ERR_MissingPartial, "C").WithArguments("C").WithLocation(11, 12)); cs = comp.GetMembers("C"); Assert.Equal(2, cs.Length); c0 = cs[0]; Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); syntaxReferences = c0.DeclaringSyntaxReferences; Assert.Equal(2, syntaxReferences.Length); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[0].SyntaxTree); Assert.Equal(comp.SyntaxTrees[0], syntaxReferences[1].SyntaxTree); c1 = cs[1]; Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.Equal(comp.SyntaxTrees[1], c1.DeclaringSyntaxReferences.Single().SyntaxTree); } [Fact] public void Duplication_08() { var source1 = """ partial class Outer { file class C { public static void M() { } } } """; var source2 = """ partial class Outer { file class C { public static void M() { } } } """; var source3 = """ partial class Outer { public class C { public static void M() { } } } """; var compilation = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs"), (source3, "file3.cs") }); compilation.VerifyDiagnostics( // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16), // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); var classOuter = compilation.GetMember<NamedTypeSymbol>("Outer"); var cs = classOuter.GetMembers("C"); Assert.Equal(3, cs.Length); Assert.True(cs[0] is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.True(cs[1] is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.True(cs[2] is SourceMemberContainerTypeSymbol { IsFileLocal: false }); } [Fact] public void Duplication_09() { var source1 = """ namespace NS { file class C { public static void M() { } } } """; var source2 = """ namespace NS { file class C { public static void M() { } } } """; var source3 = """ namespace NS { public class C { public static void M() { } } } """; var compilation = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs"), (source3, "file3.cs") }); compilation.VerifyDiagnostics(); var namespaceNS = compilation.GetMember<NamespaceSymbol>("NS"); var cs = namespaceNS.GetMembers("C"); Assert.Equal(3, cs.Length); Assert.True(cs[0] is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.True(cs[1] is SourceMemberContainerTypeSymbol { IsFileLocal: true }); Assert.True(cs[2] is SourceMemberContainerTypeSymbol { IsFileLocal: false }); } [Theory] [InlineData("file", "file")] [InlineData("file", "")] [InlineData("", "file")] public void Duplication_10(string firstFileModifier, string secondFileModifier) { var source1 = $$""" using System; partial class Program { {{firstFileModifier}} class C { public static void M() { Console.Write(1); } } } """; var source2 = $$""" using System; partial class Program { {{secondFileModifier}} class C { public static void M() { Console.Write(2); } } } """; var main = """ partial class Program { static void Main() { Program.C.M(); } } """; var comp = CreateCompilation(new[] { (source1 + main, "file1.cs"), (source2, "file2.cs") }); var cs = comp.GetMembers("Program.C"); var tree = comp.SyntaxTrees[0]; var expectedSymbol = cs[0]; verify(); comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2 + main, "file2.cs") }); cs = comp.GetMembers("Program.C"); tree = comp.SyntaxTrees[1]; expectedSymbol = cs[1]; verify(); void verify() { comp.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_FileTypeNested).Verify(); Assert.Equal(2, cs.Length); Assert.Equal(comp.SyntaxTrees[0], cs[0].DeclaringSyntaxReferences.Single().SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], cs[1].DeclaringSyntaxReferences.Single().SyntaxTree); var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last(); var info = model.GetTypeInfo(cReference); Assert.Equal(expectedSymbol.GetPublicSymbol(), info.Type); } } [Theory] [InlineData("file", "file")] [InlineData("file", "")] [InlineData("", "file")] public void Duplication_11(string firstFileModifier, string secondFileModifier) { var source1 = $$""" using System; {{firstFileModifier}} partial class Outer { internal class C { public static void M() { Console.Write(1); } } } """; var source2 = $$""" using System; {{secondFileModifier}} partial class Outer { internal class C { public static void M() { Console.Write(2); } } } """; var main = """ class Program { static void Main() { Outer.C.M(); } } """; var comp = CreateCompilation(new[] { (source1 + main, "file1.cs"), (source2, "file2.cs") }, options: TestOptions.DebugExe); comp.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_FileTypeNested).Verify(); var outers = comp.GetMembers("Outer"); var cs = outers.Select(o => ((NamedTypeSymbol)o).GetMember("C")).ToArray(); var tree = comp.SyntaxTrees[0]; var expectedSymbol = cs[0]; verify(); comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2 + main, "file2.cs") }, options: TestOptions.DebugExe); comp.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_FileTypeNested).Verify(); outers = comp.GetMembers("Outer"); cs = outers.Select(o => ((NamedTypeSymbol)o).GetMember("C")).ToArray(); tree = comp.SyntaxTrees[1]; expectedSymbol = cs[1]; verify(); void verify() { Assert.Equal(2, cs.Length); Assert.Equal(comp.SyntaxTrees[0], cs[0].DeclaringSyntaxReferences.Single().SyntaxTree); Assert.Equal(comp.SyntaxTrees[1], cs[1].DeclaringSyntaxReferences.Single().SyntaxTree); var model = comp.GetSemanticModel(tree, ignoreAccessibility: true); var cReference = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Last(); var info = model.GetTypeInfo(cReference); Assert.Equal(expectedSymbol.GetPublicSymbol(), info.Type); } } [Theory] [InlineData("file ")] [InlineData("")] public void Duplication_13(string fileModifier) { var userCode = """ using System; UserCode.Print(); partial class UserCode { public static partial void Print(); private class C { public static void M() => Console.Write("Program.cs"); } } """; // A source generator must assume that partial classes and namespaces may bring user-defined types into scope. // Therefore, generators should reference types they introduce with a `global::`-qualified name. var generatedCode = $$""" using System; partial class UserCode { public static partial void Print() { global::C.M(); // binds to 'class C'/'file class C' from global namespace C.M(); // binds to class 'UserCode.C' } } {{fileModifier}}class C { public static void M() => Console.Write("OtherFile.cs"); } """; var verifier = CompileAndVerify(new[] { (userCode, "file1.cs"), (generatedCode, "file2.cs") }, expectedOutput: "OtherFile.csProgram.cs"); verifier.VerifyDiagnostics(); } [Theory] [InlineData("file ")] [InlineData("")] public void Duplication_14(string fileModifier) { var userCode = """ using System; using UserNamespace; GeneratedClass.Print(); namespace UserNamespace { class C { public static void M() => Console.Write("Program.cs"); } } """; // A source generator must assume that partial classes and namespaces may bring user-defined types into scope. // Therefore, generators should reference types they introduce with a `global::`-qualified name. var generatedCode = $$""" using System; namespace UserNamespace { class GeneratedClass { public static void Print() { global::C.M(); // binds to 'class C'/'file class C' from global namespace C.M(); // binds to class 'UserNamespace.C' } } } {{fileModifier}}class C { public static void M() => Console.Write("OtherFile.cs"); } """; var verifier = CompileAndVerify(new[] { (userCode, "file1.cs"), (generatedCode, "file2.cs") }, expectedOutput: "OtherFile.csProgram.cs"); verifier.VerifyDiagnostics(); } [Fact] public void Duplication_15() { var userCode = """ using System; using UserNamespace; GeneratedClass.Print(); namespace UserNamespace { class C { public static void M() => Console.Write("Program.cs"); } } """; // Generators can also mitigate the "nearer scope" problem by ensuring no namespace or partial class scopes lie between the declaration and usage of a file type. var generatedCode = $$""" using System; namespace UserNamespace { class GeneratedClass { public static void Print() { C.M(); // binds to 'UserNamespace.C@OtherFile' } } file class C { public static void M() => Console.Write("OtherFile.cs"); } } """; var verifier = CompileAndVerify(new[] { (userCode, "file1.cs"), (generatedCode, "file2.cs") }, expectedOutput: "OtherFile.cs"); verifier.VerifyDiagnostics(); } [Fact] public void Duplication_16() { var source = """ namespace NS; file class C { } class C { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,7): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(4, 7)); } [Fact] public void Duplication_17() { var source = """ namespace NS; class C { } file class C { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,12): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // file class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(4, 12)); } [Fact] public void Duplication_18() { var source = """ namespace NS; file class C { } class C { } class C { } """; var comp = CreateCompilation((source, "file1.cs")); comp.VerifyDiagnostics( // file1.cs(4,7): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(4, 7), // file1.cs(5,7): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(5, 7)); } [Fact] public void Duplication_19() { var source = """ namespace NS; class C { } file class C { } class C { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,12): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // file class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(4, 12), // (5,7): error CS9070: The namespace 'NS' already contains a definition for 'C' in this file. // class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "NS").WithLocation(5, 7)); } [Fact] public void SignatureUsage_01() { var source = """ file class C { } class D { public void M1(C c) { } // 1 private void M2(C c) { } // 2 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,17): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public void M1(C c) { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("C", "D").WithLocation(7, 17), // (8,18): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // private void M2(C c) { } // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M2").WithArguments("C", "D").WithLocation(8, 18)); } [Fact] public void SignatureUsage_02() { var source = """ file class C { } class D { public C M1() => new C(); // 1 private C M2() => new C(); // 2 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,14): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public C M1() => new C(); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("C", "D").WithLocation(7, 14), // (8,15): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // private C M2() => new C(); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M2").WithArguments("C", "D").WithLocation(8, 15)); } [Fact] public void SignatureUsage_03() { var source = """ file class C { } file delegate void D(); public class E { C field; // 1 C property { get; set; } // 2 object this[C c] { get => c; set { } } // 3 event D @event; // 4 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,7): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'E'. // C field; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "field").WithArguments("C", "E").WithLocation(8, 7), // (8,7): warning CS0169: The field 'E.field' is never used // C field; // 1 Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("E.field").WithLocation(8, 7), // (9,7): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'E'. // C property { get; set; } // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "property").WithArguments("C", "E").WithLocation(9, 7), // (10,12): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'E'. // object this[C c] { get => c; set { } } // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "this").WithArguments("C", "E").WithLocation(10, 12), // (11,13): error CS9051: File-local type 'D' cannot be used in a member signature in non-file-local type 'E'. // event D @event; // 4 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "@event").WithArguments("D", "E").WithLocation(11, 13), // (11,13): warning CS0067: The event 'E.event' is never used // event D @event; // 4 Diagnostic(ErrorCode.WRN_UnreferencedEvent, "@event").WithArguments("E.event").WithLocation(11, 13)); } [Fact] public void SignatureUsage_04() { var source = """ file class C { public class Inner { } public delegate void InnerDelegate(); } public class E { C.Inner field; // 1 C.Inner property { get; set; } // 2 object this[C.Inner inner] { get => inner; set { } } // 3 event C.InnerDelegate @event; // 4 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (9,13): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E'. // C.Inner field; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "field").WithArguments("C.Inner", "E").WithLocation(9, 13), // (9,13): warning CS0169: The field 'E.field' is never used // C.Inner field; // 1 Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("E.field").WithLocation(9, 13), // (10,13): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E'. // C.Inner property { get; set; } // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "property").WithArguments("C.Inner", "E").WithLocation(10, 13), // (11,12): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E'. // object this[C.Inner inner] { get => inner; set { } } // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "this").WithArguments("C.Inner", "E").WithLocation(11, 12), // (12,27): error CS9051: File-local type 'C.InnerDelegate' cannot be used in a member signature in non-file-local type 'E'. // event C.InnerDelegate @event; // 4 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "@event").WithArguments("C.InnerDelegate", "E").WithLocation(12, 27), // (12,27): warning CS0067: The event 'E.event' is never used // event C.InnerDelegate @event; // 4 Diagnostic(ErrorCode.WRN_UnreferencedEvent, "@event").WithArguments("E.event").WithLocation(12, 27)); } [Fact] public void SignatureUsage_05() { var source = """ #pragma warning disable 67, 169 // unused event, field file class C { public class Inner { } public delegate void InnerDelegate(); } file class D { public class Inner { C.Inner field; C.Inner property { get; set; } object this[C.Inner inner] { get => inner; set { } } event C.InnerDelegate @event; } } class E { public class Inner { C.Inner field; // 1 C.Inner property { get; set; } // 2 object this[C.Inner inner] { get => inner; set { } } // 3 event C.InnerDelegate @event; // 4 } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (24,17): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E.Inner'. // C.Inner field; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "field").WithArguments("C.Inner", "E.Inner").WithLocation(24, 17), // (25,17): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E.Inner'. // C.Inner property { get; set; } // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "property").WithArguments("C.Inner", "E.Inner").WithLocation(25, 17), // (26,16): error CS9051: File-local type 'C.Inner' cannot be used in a member signature in non-file-local type 'E.Inner'. // object this[C.Inner inner] { get => inner; set { } } // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "this").WithArguments("C.Inner", "E.Inner").WithLocation(26, 16), // (27,31): error CS9051: File-local type 'C.InnerDelegate' cannot be used in a member signature in non-file-local type 'E.Inner'. // event C.InnerDelegate @event; // 4 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "@event").WithArguments("C.InnerDelegate", "E.Inner").WithLocation(27, 31)); } [Fact] public void SignatureUsage_06() { var source = """ file class C { } delegate void Del1(C c); // 1 delegate C Del2(); // 2 """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,15): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'Del1'. // delegate void Del1(C c); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del1").WithArguments("C", "Del1").WithLocation(5, 15), // (6,12): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'Del2'. // delegate C Del2(); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del2").WithArguments("C", "Del2").WithLocation(6, 12)); } [Fact] public void SignatureUsage_06_2() { var source = """ file class C<T> { } delegate void Del1(C<int> c); // 1 delegate C<int> Del2(); // 2 file delegate void Del3(C<int> c); // ok file delegate C<int> Del4(); // ok """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,15): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'Del1'. // delegate void Del1(C<int> c); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del1").WithArguments("C<int>", "Del1").WithLocation(5, 15), // (6,17): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'Del2'. // delegate C<int> Del2(); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del2").WithArguments("C<int>", "Del2").WithLocation(6, 17)); var del1 = comp.GetMember<NamedTypeSymbol>("Del1"); var cInt = (ConstructedNamedTypeSymbol)del1.DelegateInvokeMethod.Parameters[0].Type; Assert.True(cInt.IsFileLocal); } [Fact] public void SignatureUsage_07() { var source = """ file class C { } class D { public static D operator +(D d, C c) => d; // 1 public static C operator -(D d1, D d2) => new C(); // 2 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,30): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public static D operator +(D d, C c) => d; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("C", "D").WithLocation(7, 30), // (8,30): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public static C operator -(D d1, D d2) => new C(); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "-").WithArguments("C", "D").WithLocation(8, 30)); } [Fact] public void SignatureUsage_08() { var source = """ file class C { } class D { public D(C c) { } // 1 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,12): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public D(C c) { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "D").WithArguments("C", "D").WithLocation(7, 12)); } [Fact] public void SignatureUsage_09() { var source = """ file class C { } class D { public C M(C c1, C c2) => c1; // 1, 2, 3 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,14): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public C M(C c1, C c2) => c1; // 1, 2, 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M").WithArguments("C", "D").WithLocation(7, 14), // (7,14): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public C M(C c1, C c2) => c1; // 1, 2, 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M").WithArguments("C", "D").WithLocation(7, 14), // (7,14): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D'. // public C M(C c1, C c2) => c1; // 1, 2, 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M").WithArguments("C", "D").WithLocation(7, 14)); } [Fact] public void SignatureUsage_10() { var source = """ #pragma warning disable 67, 169 // unused event, field file class C<T> { } file delegate void Del<T>(T input); class C1 { private C<int> F; // 1 private event Del<int> E; // 2 private void M1(C<int> input) { } // 3 private C<int> M2() => throw null!; // 4 private C<int> P { get; set; } // 5 private C<int> this[int i] => throw null!; // 6 } file class FC { private C<int> F; private event Del<int> E; private void M1(C<int> input) { } private C<int> M2() => throw null!; private C<int> P { get; set; } private C<int> this[int i] => throw null!; } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,20): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'C1'. // private C<int> F; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "F").WithArguments("C<int>", "C1").WithLocation(8, 20), // (9,28): error CS9051: File-local type 'Del<int>' cannot be used in a member signature in non-file-local type 'C1'. // private event Del<int> E; // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "E").WithArguments("Del<int>", "C1").WithLocation(9, 28), // (10,18): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'C1'. // private void M1(C<int> input) { } // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("C<int>", "C1").WithLocation(10, 18), // (11,20): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'C1'. // private C<int> M2() => throw null!; // 4 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M2").WithArguments("C<int>", "C1").WithLocation(11, 20), // (13,20): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'C1'. // private C<int> P { get; set; } // 5 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "P").WithArguments("C<int>", "C1").WithLocation(13, 20), // (14,20): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'C1'. // private C<int> this[int i] => throw null!; // 6 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "this").WithArguments("C<int>", "C1").WithLocation(14, 20)); verifyConstructedFileType(comp.GetMember<FieldSymbol>("C1.F").Type); verifyConstructedFileType(comp.GetMember<EventSymbol>("C1.E").Type); verifyConstructedFileType(comp.GetMember<MethodSymbol>("C1.M1").Parameters[0].Type); verifyConstructedFileType(comp.GetMember<MethodSymbol>("C1.M2").ReturnType); verifyConstructedFileType(comp.GetMember<PropertySymbol>("C1.P").Type); verifyConstructedFileType(comp.GetMember<PropertySymbol>("C1.this[]").Type); void verifyConstructedFileType(TypeSymbol type) { var cInt = (ConstructedNamedTypeSymbol)type; Assert.True(cInt.IsFileLocal); } } [Fact] public void AccessModifiers_01() { var source = """ public file class C { } // 1 file internal class D { } // 2 private file class E { } // 3, 4 file class F { } // ok """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (1,19): error CS9052: File-local type 'C' cannot use accessibility modifiers. // public file class C { } // 1 Diagnostic(ErrorCode.ERR_FileTypeNoExplicitAccessibility, "C").WithArguments("C").WithLocation(1, 19), // (2,21): error CS9052: File-local type 'D' cannot use accessibility modifiers. // file internal class D { } // 2 Diagnostic(ErrorCode.ERR_FileTypeNoExplicitAccessibility, "D").WithArguments("D").WithLocation(2, 21), // (3,20): error CS9052: File-local type 'E' cannot use accessibility modifiers. // private file class E { } // 3, 4 Diagnostic(ErrorCode.ERR_FileTypeNoExplicitAccessibility, "E").WithArguments("E").WithLocation(3, 20), // (3,20): error CS1527: Elements defined in a namespace cannot be explicitly declared as private, protected, protected internal, or private protected // private file class E { } // 3, 4 Diagnostic(ErrorCode.ERR_NoNamespacePrivate, "E").WithLocation(3, 20)); } [Fact] public void DuplicateModifiers_01() { var source = """ file file class C { } // 1 file readonly file struct D { } // 2 """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (1,6): error CS1004: Duplicate 'file' modifier // file file class C { } // 1 Diagnostic(ErrorCode.ERR_DuplicateModifier, "file").WithArguments("file").WithLocation(1, 6), // (2,15): error CS1004: Duplicate 'file' modifier // file readonly file struct D { } // 2 Diagnostic(ErrorCode.ERR_DuplicateModifier, "file").WithArguments("file").WithLocation(2, 15)); } [Fact] public void BaseClause_01() { var source = """ file class Base { } class Derived1 : Base { } // 1 public class Derived2 : Base { } // 2, 3 file class Derived3 : Base { } // ok """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (2,7): error CS9053: File-local type 'Base' cannot be used as a base type of non-file-local type 'Derived1'. // class Derived1 : Base { } // 1 Diagnostic(ErrorCode.ERR_FileTypeBase, "Derived1").WithArguments("Base", "Derived1").WithLocation(2, 7), // (3,14): error CS0060: Inconsistent accessibility: base class 'Base' is less accessible than class 'Derived2' // public class Derived2 : Base { } // 2, 3 Diagnostic(ErrorCode.ERR_BadVisBaseClass, "Derived2").WithArguments("Derived2", "Base").WithLocation(3, 14), // (3,14): error CS9053: File-local type 'Base' cannot be used as a base type of non-file-local type 'Derived2'. // public class Derived2 : Base { } // 2, 3 Diagnostic(ErrorCode.ERR_FileTypeBase, "Derived2").WithArguments("Base", "Derived2").WithLocation(3, 14)); } [Fact] public void BaseClause_02() { var source = """ file interface Interface { } class Derived1 : Interface { } // ok file class Derived2 : Interface { } // ok interface Derived3 : Interface { } // 1 file interface Derived4 : Interface { } // ok """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,11): error CS9053: File-local type 'Interface' cannot be used as a base type of non-file-local type 'Derived3'. // interface Derived3 : Interface { } // 1 Diagnostic(ErrorCode.ERR_FileTypeBase, "Derived3").WithArguments("Interface", "Derived3").WithLocation(6, 11)); } [Fact] public void BaseClause_03() { var source1 = """ using System; class Base { public static void M0() { Console.Write(1); } } """; var source2 = """ using System; file class Base { public static void M0() { Console.Write(2); } } file class Program : Base { static void Main() { M0(); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2, "file2.cs") }, expectedOutput: "2"); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var tree = comp.SyntaxTrees[1]; var model = comp.GetSemanticModel(tree); var fileClassBase = (NamedTypeSymbol)comp.GetMembers("Base")[1]; var expectedSymbol = fileClassBase.GetMember("M0"); var node = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Last(); var symbolInfo = model.GetSymbolInfo(node.Expression); Assert.Equal(expectedSymbol.GetPublicSymbol(), symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact] public void BaseClause_04() { var source1 = """ using System; class Base { public static void M0() { Console.Write(1); } } """; var source2 = """ file class Program : Base { static void Main() { M0(); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2, "file2.cs") }, expectedOutput: "1"); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var tree = comp.SyntaxTrees[1]; var model = comp.GetSemanticModel(tree); var expectedSymbol = comp.GetMember("Base.M0"); var node = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Last(); var symbolInfo = model.GetSymbolInfo(node.Expression); Assert.Equal(expectedSymbol.GetPublicSymbol(), symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact] public void BaseClause_05() { var source = """ interface I2 { } file interface I1 { } partial interface Derived : I1 { } // 1 partial interface Derived : I2 { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,19): error CS9053: File-local type 'I1' cannot be used as a base type of non-file-local type 'Derived'. // partial interface Derived : I1 { } // 1 Diagnostic(ErrorCode.ERR_FileTypeBase, "Derived").WithArguments("I1", "Derived").WithLocation(3, 19)); } [Fact] public void BaseClause_06() { var source = """ file class C<T> { } class D : C<int> { } // 1 file class E : C<int> { } file interface I<T> { } class F : I<int> { } // ok file class G : I<int> { } interface J : I<int> { } // 2 file interface K : I<int> { } """; var comp = CreateCompilation((source, "Program.cs")); comp.VerifyEmitDiagnostics( // Program.cs(3,7): error CS9053: File-local type 'C<int>' cannot be used as a base type of non-file-local type 'D'. // class D : C<int>, I<int> { } // 1 Diagnostic(ErrorCode.ERR_FileTypeBase, "D").WithArguments("C<int>", "D").WithLocation(3, 7), // Program.cs(11,11): error CS9053: File-local type 'I<int>' cannot be used as a base type of non-file-local type 'J'. // interface J : I<int> { } // 2 Diagnostic(ErrorCode.ERR_FileTypeBase, "J").WithArguments("I<int>", "J").WithLocation(11, 11)); var cInt = (ConstructedNamedTypeSymbol)comp.GetMember<NamedTypeSymbol>("D").BaseTypeNoUseSiteDiagnostics; Assert.True(cInt.IsFileLocal); var iInt = (ConstructedNamedTypeSymbol)comp.GetMember<NamedTypeSymbol>("F").InterfacesNoUseSiteDiagnostics()[0]; Assert.True(iInt.IsFileLocal); iInt = (ConstructedNamedTypeSymbol)comp.GetMember<NamedTypeSymbol>("J").InterfacesNoUseSiteDiagnostics()[0]; Assert.True(iInt.IsFileLocal); } [Fact] public void InterfaceImplementation_01() { var source = """ file interface I { void F(); } class C : I { public void F() { } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void InterfaceImplementation_02() { var source = """ file interface I { void F(I i); } class C : I { public void F(I i) { } // 1 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,17): error CS9051: File-local type 'I' cannot be used in a member signature in non-file-local type 'C'. // public void F(I i) { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "F").WithArguments("I", "C").WithLocation(7, 17)); } [Fact] public void InterfaceImplementation_03() { var source = """ file interface I { void F(I i); } class C : I { void I.F(I i) { } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,12): error CS9051: File-local type 'I' cannot be used in a member signature in non-file-local type 'C'. // void I.F(I i) { } Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "F").WithArguments("I", "C").WithLocation(7, 12)); } [Fact] public void InterfaceImplementation_04() { var source1 = """ file interface I { void F(); } partial class C : I { } """; var source2 = """ partial class C { public void F() { } } """; // This is similar to how a base class may not have access to an interface (by being from another assembly, etc.), // but a derived class might add that interface to its list, and a base member implicitly implements an interface member. var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics(); } [Fact] public void InterfaceImplementation_05() { var source1 = """ file interface I { void F(); } partial class C : I // 1 { } """; var source2 = """ partial class C { void I.F() { } // 2, 3 } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (3,10): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) // void I.F() { } // 2, 3 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(3, 10), // (3,10): error CS0538: 'I' in explicit interface declaration is not an interface // void I.F() { } // 2, 3 Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(3, 10), // (5,19): error CS0535: 'C' does not implement interface member 'I.F()' // partial class C : I // 1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I").WithArguments("C", "I.F()").WithLocation(5, 19)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void InterfaceImplementation_06() { // Ensure that appropriate error is given for duplicate implementations which have a type difference which is insignificant to the runtime. var source1 = """ file interface FI<T> { public T Prop { get; } } internal class C : FI<object> { object FI<object>.Prop { get; } dynamic FI<dynamic>.Prop { get; } } """; var comp = CreateCompilation(new[] { (source1, "F1.cs") }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // F1.cs(6,16): error CS8646: 'FI<object>.Prop' is explicitly implemented more than once. // internal class C : FI<object> Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("FI<object>.Prop").WithLocation(6, 16), // F1.cs(9,13): error CS0540: 'C.FI<dynamic>.Prop': containing type does not implement interface 'FI<dynamic>' // dynamic FI<dynamic>.Prop { get; } Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "FI<dynamic>").WithArguments("C.FI<dynamic>.Prop", "FI<dynamic>").WithLocation(9, 13) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void InterfaceImplementation_07() { // Ensure that appropriate error is given for duplicate implementations which have a type difference which is insignificant to the runtime. var source1 = """ using System; file interface FI<T> { public T Prop { get; } } internal class C : FI<nint>, FI<IntPtr> { nint FI<nint>.Prop { get; } IntPtr FI<IntPtr>.Prop { get; } } """; var comp = CreateCompilation(new[] { (source1, "F1.cs") }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // F1.cs(8,16): error CS8646: 'FI<nint>.Prop' is explicitly implemented more than once. // internal class C : FI<nint>, FI<IntPtr> Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("FI<nint>.Prop").WithLocation(8, 16), // F1.cs(8,30): error CS0528: 'FI<nint>' is already listed in interface list // internal class C : FI<nint>, FI<IntPtr> Diagnostic(ErrorCode.ERR_DuplicateInterfaceInBaseList, "FI<IntPtr>").WithArguments("FI<nint>").WithLocation(8, 30), // F1.cs(11,23): error CS0102: The type 'C' already contains a definition for '<F1>F2A62B10769F2595F65CAD631A41E2B54F5D1B3601B00884A41306FA9AD9BACDB__FI<nint>.Prop' // IntPtr FI<IntPtr>.Prop { get; } Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "Prop").WithArguments("C", "<F1>F2A62B10769F2595F65CAD631A41E2B54F5D1B3601B00884A41306FA9AD9BACDB__FI<nint>.Prop").WithLocation(11, 23) ); } [Fact] public void TypeArguments_01() { var source = """ file struct S { public int X; } class Container<T> { } unsafe class Program { Container<S> M1() => new Container<S>(); // 1 S[] M2() => new S[0]; // 2 (S, S) M3() => (new S(), new S()); // 3 S* M4() => null; // 4 delegate*<S, void> M5() => null; // 5 } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (1,28): warning CS0649: Field 'S.X' is never assigned to, and will always have its default value 0 // file struct S { public int X; } Diagnostic(ErrorCode.WRN_UnassignedInternalField, "X").WithArguments("S.X", "0").WithLocation(1, 28), // (5,18): error CS9051: File-local type 'Container<S>' cannot be used in a member signature in non-file-local type 'Program'. // Container<S> M1() => new Container<S>(); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("Container<S>", "Program").WithLocation(5, 18), // (6,9): error CS9051: File-local type 'S[]' cannot be used in a member signature in non-file-local type 'Program'. // S[] M2() => new S[0]; // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M2").WithArguments("S[]", "Program").WithLocation(6, 9), // (7,12): error CS9051: File-local type '(S, S)' cannot be used in a member signature in non-file-local type 'Program'. // (S, S) M3() => (new S(), new S()); // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M3").WithArguments("(S, S)", "Program").WithLocation(7, 12), // (8,8): error CS9051: File-local type 'S*' cannot be used in a member signature in non-file-local type 'Program'. // S* M4() => null; // 4 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M4").WithArguments("S*", "Program").WithLocation(8, 8), // (9,24): error CS9051: File-local type 'delegate*<S, void>' cannot be used in a member signature in non-file-local type 'Program'. // delegate*<S, void> M5() => null; // 5 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M5").WithArguments("delegate*<S, void>", "Program").WithLocation(9, 24)); } [Fact] public void Constraints_01() { var source = """ file class C { } file class D { void M<T>(T t) where T : C { } // ok } class E { void M<T>(T t) where T : C { } // 1 } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (10,30): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'E.M<T>(T)'. // void M<T>(T t) where T : C { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C").WithArguments("C", "E.M<T>(T)").WithLocation(10, 30)); } [Theory, WorkItem(62435, "https://github.com/dotnet/roslyn/issues/62435")] [InlineData("class")] [InlineData("struct")] [InlineData("interface")] [InlineData("record")] [InlineData("record struct")] public void Constraints_02(string typeKind) { var source = $$""" file class C { } file {{typeKind}} D<T> where T : C // ok { } {{typeKind}} E<T> where T : C // 1 { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,{{17 + typeKind.Length}}): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'E<T>'. // {{typeKind}} E<T> where T : C // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C").WithArguments("C", "E<T>").WithLocation(7, 17 + typeKind.Length)); } [Fact] public void Constraints_03() { var source = """ file class C { } file class D { void M() { local(new C()); void local<T>(T t) where T : C { } // ok } } class E { void M() { local(new C()); void local<T>(T t) where T : C { } // ok } } """; // Local functions aren't members, so we don't give any diagnostics when their signatures contain file types. var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void Constraints_04() { var source = """ file class C { } file delegate void D1<T>(T t) where T : C; // ok delegate void D2<T>(T t) where T : C; // 1 """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,36): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'D2<T>'. // delegate void D2<T>(T t) where T : C; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C").WithArguments("C", "D2<T>").WithLocation(5, 36)); } [Fact] public void Constraints_05() { var source = """ file class C<T> { } class D { private void M<T>(T t) where T : C<int> { } // 1 } file class E { private void M<T>(T t) where T : C<int> { } // ok } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,38): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'D.M<T>(T)'. // private void M<T>(T t) where T : C<int> { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C<int>").WithArguments("C<int>", "D.M<T>(T)").WithLocation(5, 38)); var cInt = (ConstructedNamedTypeSymbol)comp.GetMember<MethodSymbol>("D.M").TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].Type; Assert.True(cInt.IsFileLocal); } [Fact] public void Constraints_06() { var source = """ file class C<T> { } class D<T> where T : C<int> { } // 1 file class E<T> where T : C<int> { } // ok """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,22): error CS9051: File-local type 'C<int>' cannot be used in a member signature in non-file-local type 'D<T>'. // class D<T> where T : C<int> { } // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C<int>").WithArguments("C<int>", "D<T>").WithLocation(3, 22)); var cInt = (ConstructedNamedTypeSymbol)comp.GetMember<NamedTypeSymbol>("D").TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].Type; Assert.True(cInt.IsFileLocal); } [Fact] public void PrimaryConstructor_01() { var source = """ file class C { } record R1(C c); // 1 record struct R2(C c); // 2 file record R3(C c); file record struct R4(C c); """; var comp = CreateCompilation(new[] { (source, "file1.cs"), (IsExternalInitTypeDefinition, "file2.cs") }); comp.VerifyDiagnostics( // (3,8): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'R1'. // record R1(C c); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R1").WithArguments("C", "R1").WithLocation(3, 8), // (3,8): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'R1'. // record R1(C c); // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R1").WithArguments("C", "R1").WithLocation(3, 8), // (4,15): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'R2'. // record struct R2(C c); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R2").WithArguments("C", "R2").WithLocation(4, 15), // (4,15): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'R2'. // record struct R2(C c); // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R2").WithArguments("C", "R2").WithLocation(4, 15) ); } [Fact] public void Lambda_01() { var source = """ file class C { } class Program { void M() { var lambda = C (C c) => c; // ok } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void LocalFunction_01() { var source = """ file class C { } class Program { void M() { local(null!); C local(C c) => c; // ok } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void AccessThroughNamespace_01() { var source = """ using System; namespace NS { file class C { public static void M() => Console.Write(1); } } class Program { public static void Main() { NS.C.M(); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Fact] public void AccessThroughNamespace_02() { var source1 = """ using System; namespace NS { file class C { public static void M() { Console.Write(1); } } } """; var source2 = """ class Program { static void Main() { NS.C.M(); // 1 } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (5,9): error CS0234: The type or namespace name 'C' does not exist in the namespace 'NS' (are you missing an assembly reference?) // NS.C.M(); // 1 Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "NS.C").WithArguments("C", "NS").WithLocation(5, 9)); } [Fact] public void AccessThroughType_01() { var source = """ using System; class Outer { file class C // 1 { public static void M() => Console.Write(1); } } class Program { public static void Main() { Outer.C.M(); // 2 } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C // 1 Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(5, 16), // (15,15): error CS0122: 'Outer.C' is inaccessible due to its protection level // Outer.C.M(); // 2 Diagnostic(ErrorCode.ERR_BadAccess, "C").WithArguments("Outer.C").WithLocation(15, 15)); } [Fact] public void AccessThroughType_02() { var source1 = """ using System; class Outer { file class C { public static void M() { Console.Write(1); } } } """; var source2 = """ class Program { static void Main() { Outer.C.M(); // 1 } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (5,15): error CS0117: 'Outer' does not contain a definition for 'C' // Outer.C.M(); // 1 Diagnostic(ErrorCode.ERR_NoSuchMember, "C").WithArguments("Outer", "C").WithLocation(5, 15), // (5,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(5, 16)); } [Fact] public void AccessThroughGlobalUsing_01() { var usings = """ global using NS; """; var source = """ using System; namespace NS { file class C { public static void M() => Console.Write(1); } } class Program { public static void Main() { C.M(); } } """; var verifier = CompileAndVerify(new[] { (usings, "file1.cs"), (source, "file2.cs"), (IsExternalInitTypeDefinition, "file3.cs") }, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Theory] [InlineData("file ")] [InlineData("")] public void AccessThroughGlobalUsing_02(string fileModifier) { var source = $$""" using System; namespace NS { {{fileModifier}}class C { public static void M() => Console.Write(1); } } class Program { public static void Main() { C.M(); // 1 } } """; // note: 'Usings' is a legacy setting which only works in scripts. // https://github.com/dotnet/roslyn/issues/61502 var compilation = CreateCompilation(new[] { (source, "file1.cs"), (IsExternalInitTypeDefinition, "file2.cs") }, options: TestOptions.DebugExe.WithUsings("NS")); compilation.VerifyDiagnostics( // (15,9): error CS0103: The name 'C' does not exist in the current context // C.M(); // 1 Diagnostic(ErrorCode.ERR_NameNotInContext, "C").WithArguments("C").WithLocation(15, 9)); } [Fact] public void GlobalUsingStatic_01() { var source = """ global using static C; file class C { public static void M() { } } """; var main = """ class Program { public static void Main() { M(); } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // global using static C; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "global using static C;").WithLocation(1, 1), // (1,21): error CS9055: File-local type 'C' cannot be used in a 'global using static' directive. // global using static C; Diagnostic(ErrorCode.ERR_GlobalUsingStaticFileType, "C").WithArguments("C").WithLocation(1, 21), // (5,9): error CS0103: The name 'M' does not exist in the current context // M(); Diagnostic(ErrorCode.ERR_NameNotInContext, "M").WithArguments("M").WithLocation(5, 9)); } [Fact] public void GlobalUsingStatic_02() { var source = """ global using static Container<C>; public class Container<T> { } file class C { public static void M() { } } """; var main = """ class Program { public static void Main() { M(); } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // global using static Container<C>; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "global using static Container<C>;").WithLocation(1, 1), // (1,21): error CS9055: File-local type 'Container<C>' cannot be used in a 'global using static' directive. // global using static Container<C>; Diagnostic(ErrorCode.ERR_GlobalUsingStaticFileType, "Container<C>").WithArguments("Container<C>").WithLocation(1, 21), // (5,9): error CS0103: The name 'M' does not exist in the current context // M(); Diagnostic(ErrorCode.ERR_NameNotInContext, "M").WithArguments("M").WithLocation(5, 9)); } [Fact] public void GlobalUsingStatic_03() { var source = """ global using static C<int>; file class C<T> { public static void M() { } } """; var main = """ class Program { public static void Main() { M(); } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics( // file1.cs(1,1): hidden CS8019: Unnecessary using directive. // global using static C<int>; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "global using static C<int>;").WithLocation(1, 1), // file1.cs(1,21): error CS9055: File-local type 'C<int>' cannot be used in a 'global using static' directive. // global using static C<int>; Diagnostic(ErrorCode.ERR_GlobalUsingStaticFileType, "C<int>").WithArguments("C<int>").WithLocation(1, 21), // file2.cs(5,9): error CS0103: The name 'M' does not exist in the current context // M(); Diagnostic(ErrorCode.ERR_NameNotInContext, "M").WithArguments("M").WithLocation(5, 9)); } [Fact] public void UsingStatic_01() { var source = """ using System; using static C; file class C { public static void M() { Console.Write(1); } } class Program { public static void Main() { M(); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Fact] public void UsingStatic_02() { var source1 = """ using System; using static C.D; M(); file class C { public class D { public static void M() { Console.Write(1); } } } """; var source2 = """ using System; class C { public class D { public static void M() { Console.Write(2); } } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2, "file2.cs") }, expectedOutput: "1"); verifier.VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var members = comp.GetMembers("C"); Assert.Equal(2, members.Length); var expectedMember = ((NamedTypeSymbol)members[0]).GetMember<MethodSymbol>("D.M"); var invocation = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().First(); var symbolInfo = model.GetSymbolInfo(invocation.Expression); Assert.Equal(expectedMember.GetPublicSymbol(), symbolInfo.Symbol); Assert.Equal(0, symbolInfo.CandidateSymbols.Length); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Theory] [InlineData("file ")] [InlineData("")] public void UsingStatic_03(string fileModifier) { // note: the top-level `class D` "wins" the lookup in this scenario. var source1 = $$""" using System; using static C; D.M(); {{fileModifier}}class C { public class D { public static void M() { Console.Write(1); } } } """; var source2 = """ using System; class D { public static void M() { Console.Write(2); } } """; var verifier = CompileAndVerify(new[] { (source1, "file1.cs"), (source2, "file2.cs") }, expectedOutput: "2"); verifier.VerifyDiagnostics( // (2,1): hidden CS8019: Unnecessary using directive. // using static C; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static C;").WithLocation(2, 1)); var comp = (CSharpCompilation)verifier.Compilation; var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var expectedMember = comp.GetMember("D.M"); var invocation = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().First(); var symbolInfo = model.GetSymbolInfo(invocation.Expression); Assert.Equal(expectedMember.GetPublicSymbol(), symbolInfo.Symbol); Assert.Equal(0, symbolInfo.CandidateSymbols.Length); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact] public void TypeShadowing() { var source = """ using System; class Base { internal class C { public static void M() { Console.Write(1); } } } class Derived : Base { new file class C { } } """; var main = """ class Program { public static void Main() { Derived.C.M(); } } """; // 'Derived.C' is not actually accessible from 'Program', so we just bind to 'Base.C'. var compilation = CreateCompilation(new[] { (source, "file.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics( // (16,20): error CS9054: File-local type 'Derived.C' must be defined in a top level type; 'Derived.C' is a nested type. // new file class C Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Derived.C").WithLocation(16, 20)); var expected = compilation.GetMember<MethodSymbol>("Base.C.M"); var tree = compilation.SyntaxTrees[1]; var model = compilation.GetSemanticModel(tree); var invoked = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Single().Expression; var symbolInfo = model.GetSymbolInfo(invoked); Assert.Equal(expected, symbolInfo.Symbol.GetSymbol()); } [Fact] public void SemanticModel_01() { var source = """ namespace NS; file class C { public static void M() { } } class Program { public void M() { C.M(); } } """; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees[0]; var body = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Last().Body!; var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var info = model.GetSymbolInfo(((ExpressionStatementSyntax)body.Statements.First()).Expression); Assert.Equal("void NS.C@<tree 0>.M()", info.Symbol.ToTestDisplayString()); var classC = compilation.GetMember("NS.C").GetPublicSymbol(); Assert.Equal("NS.C@<tree 0>", classC.ToTestDisplayString()); // lookup with no container var symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, name: "C"); Assert.Equal(new[] { classC }, symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition); Assert.Contains(classC, symbols); // lookup with a correct container var nsSymbol = compilation.GetMember<NamespaceSymbol>("NS").GetPublicSymbol(); Assert.Equal("NS", nsSymbol.ToTestDisplayString()); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol, name: "C"); Assert.Equal(new[] { classC }, symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol); Assert.Contains(classC, symbols); // lookup with an incorrect container nsSymbol = compilation.GetMember<NamespaceSymbol>("System").GetPublicSymbol(); Assert.Equal("System", nsSymbol.ToTestDisplayString()); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol, name: "C"); Assert.Empty(symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol); Assert.DoesNotContain(classC, symbols); } [Fact] public void SemanticModel_02() { var source = """ namespace NS; file class C { public static void M() { } } """; var main = """ namespace NS; class Program { public void M() { C.M(); // 1 } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics( // (7,9): error CS0103: The name 'C' does not exist in the current context // C.M(); // 1 Diagnostic(ErrorCode.ERR_NameNotInContext, "C").WithArguments("C").WithLocation(7, 9) ); var tree = compilation.SyntaxTrees[1]; var body = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Last().Body!; var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var info = model.GetSymbolInfo(((ExpressionStatementSyntax)body.Statements.First()).Expression); Assert.Null(info.Symbol); Assert.Empty(info.CandidateSymbols); Assert.Equal(CandidateReason.None, info.CandidateReason); var classC = compilation.GetMember("NS.C").GetPublicSymbol(); Assert.Equal("NS.C@file1", classC.ToTestDisplayString()); // lookup with no container var symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, name: "C"); Assert.Empty(symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition); Assert.DoesNotContain(classC, symbols); // lookup with a correct container (still don't find the symbol due to lookup occurring in other file) var nsSymbol = compilation.GetMember<NamespaceSymbol>("NS").GetPublicSymbol(); Assert.Equal("NS", nsSymbol.ToTestDisplayString()); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol, name: "C"); Assert.Empty(symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol); Assert.DoesNotContain(classC, symbols); // lookup with an incorrect container nsSymbol = compilation.GetMember<NamespaceSymbol>("System").GetPublicSymbol(); Assert.Equal("System", nsSymbol.ToTestDisplayString()); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol, name: "C"); Assert.Empty(symbols); symbols = model.LookupSymbols(body.OpenBraceToken.EndPosition, container: nsSymbol); Assert.DoesNotContain(classC, symbols); } [Fact] public void Speculation_01() { var source = """ file class C { public static void M() { } } class Program { public void M() { } } """; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees[0]; var body = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Last().Body!; var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var newBody = body.AddStatements(SyntaxFactory.ParseStatement("C.M();")); Assert.True(model.TryGetSpeculativeSemanticModel(position: body.OpenBraceToken.EndPosition, newBody, out var speculativeModel)); var info = speculativeModel!.GetSymbolInfo(((ExpressionStatementSyntax)newBody.Statements.First()).Expression); Assert.Equal(compilation.GetMember("C.M").GetPublicSymbol(), info.Symbol); var classC = compilation.GetMember("C").GetPublicSymbol(); var symbols = speculativeModel.LookupSymbols(newBody.OpenBraceToken.EndPosition, name: "C"); Assert.Equal(new[] { classC }, symbols); symbols = speculativeModel.LookupSymbols(newBody.OpenBraceToken.EndPosition); Assert.Contains(classC, symbols); } [Fact] public void Speculation_02() { var source = """ file class C { public static void M() { } } """; var main = """ class Program { public void M() { } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees[1]; var body = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Last().Body!; var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var newBody = body.AddStatements(SyntaxFactory.ParseStatement("C.M();")); Assert.True(model.TryGetSpeculativeSemanticModel(position: body.OpenBraceToken.EndPosition, newBody, out var speculativeModel)); var info = speculativeModel!.GetSymbolInfo(((ExpressionStatementSyntax)newBody.Statements.First()).Expression); Assert.Null(info.Symbol); Assert.Empty(info.CandidateSymbols); Assert.Equal(CandidateReason.None, info.CandidateReason); var symbols = speculativeModel.LookupSymbols(newBody.OpenBraceToken.EndPosition, name: "C"); Assert.Empty(symbols); symbols = speculativeModel.LookupSymbols(newBody.OpenBraceToken.EndPosition); Assert.DoesNotContain(compilation.GetMember("C").GetPublicSymbol(), symbols); } [Fact] public void Cref_01() { var source = """ file class C { public static void M() { } } class Program { /// <summary> /// In the same file as <see cref="C"/>. /// </summary> public static void M() { } } """; var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); compilation.VerifyDiagnostics(); } [Fact] public void Cref_02() { var source = """ file class C { public static void M() { } } """; var main = """ class Program { /// <summary> /// In a different file than <see cref="C"/>. /// </summary> public static void M() { } } """; var compilation = CreateCompilation(new[] { (source, "file1.cs"), (main, "file2.cs") }, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); compilation.VerifyDiagnostics( // (4,45): warning CS1574: XML comment has cref attribute 'C' that could not be resolved // /// In a different file than <see cref="C"/>. Diagnostic(ErrorCode.WRN_BadXMLRef, "C").WithArguments("C").WithLocation(4, 45) ); } [Fact] public void TopLevelStatements() { var source = """ using System; C.M(); file class C { public static void M() { Console.Write(1); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Fact] public void StaticFileClass() { var source = """ using System; C.M(); static file class C { public static void M() { Console.Write(1); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Fact] public void ExtensionMethod_01() { var source = """ using System; "a".M(); static file class C { public static void M(this string s) { Console.Write(1); } } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyDiagnostics(); } [Fact] public void ExtensionMethod_02() { var source1 = """ "a".M(); // 1 """; var source2 = """ using System; static file class C { public static void M(this string s) { Console.Write(1); } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (1,5): error CS1061: 'string' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // "a".M(); // 1 Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("string", "M").WithLocation(1, 5)); var tree = comp.SyntaxTrees[0]; var methodNameSyntax = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Single(); var model = comp.GetSemanticModel(tree); var symbolInfo = model.GetSymbolInfo(methodNameSyntax); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); var aliasInfo = model.GetAliasInfo(methodNameSyntax); Assert.Null(aliasInfo); } [Fact] public void ExtensionMethod_03() { var source1 = """ "a".M(); // 1 """; var source2 = """ using System; file class C { static class D { public static void M(this string s) // 2 { Console.Write(1); } } } """; var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics( // (1,5): error CS1061: 'string' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // "a".M(); // 1 Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("string", "M").WithLocation(1, 5), // (7,28): error CS1109: Extension methods must be defined in a top level static class; D is a nested class // public static void M(this string s) // 2 Diagnostic(ErrorCode.ERR_ExtensionMethodsDecl, "M").WithArguments("D").WithLocation(7, 28)); } [Fact] public void Alias_01() { var source = """ namespace NS; using C1 = NS.C; file class C { } class D : C1 { } // 1 """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,7): error CS9053: File-local type 'C' cannot be used as a base type of non-file-local type 'D'. // class D : C1 { } // 1 Diagnostic(ErrorCode.ERR_FileTypeBase, "D").WithArguments("NS.C", "NS.D").WithLocation(8, 7)); } [Fact] public void SymbolDisplay() { var source1 = """ file class C1 { public static void M() { } } """; var source2 = """ file class C2 { public static void M() { } } """; var comp = CreateCompilation(new[] { SyntaxFactory.ParseSyntaxTree(source1, TestOptions.RegularPreview), SyntaxFactory.ParseSyntaxTree(source2, TestOptions.RegularPreview, path: "path/to/FileB.cs") }); comp.VerifyDiagnostics(); var c1 = comp.GetMember<NamedTypeSymbol>("C1"); var c2 = comp.GetMember<NamedTypeSymbol>("C2"); Assert.Equal("C1@<tree 0>", c1.ToTestDisplayString()); Assert.Equal("C2@FileB", c2.ToTestDisplayString()); Assert.Equal("void C1@<tree 0>.M()", c1.GetMember("M").ToTestDisplayString()); Assert.Equal("void C2@FileB.M()", c2.GetMember("M").ToTestDisplayString()); } [Fact] public void Script_01() { var source1 = """ using System; C1.M("a"); static file class C1 { public static void M(this string s) { } } """; var comp = CreateSubmission(source1, parseOptions: TestOptions.Script.WithLanguageVersion(LanguageVersion.Preview)); comp.VerifyDiagnostics( // (5,19): error CS9054: File-local type 'C1' must be defined in a top level type; 'C1' is a nested type. // static file class C1 Diagnostic(ErrorCode.ERR_FileTypeNested, "C1").WithArguments("C1").WithLocation(5, 19), // (7,24): error CS1109: Extension methods must be defined in a top level static class; C1 is a nested class // public static void M(this string s) { } Diagnostic(ErrorCode.ERR_ExtensionMethodsDecl, "M").WithArguments("C1").WithLocation(7, 24)); } [Fact] public void SystemVoid_01() { var source1 = """ using System; void M(Void v) { } namespace System { file class Void { } } """; // https://github.com/dotnet/roslyn/issues/62331 // Ideally we would give an error about use of System.Void here. var comp = CreateCompilation(source1); comp.VerifyDiagnostics( // (3,6): warning CS8321: The local function 'M' is declared but never used // void M(Void v) { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(3, 6)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var voidTypeSyntax = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single().Type!; var typeInfo = model.GetTypeInfo(voidTypeSyntax); Assert.Equal("System.Void@<tree 0>", typeInfo.Type!.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes))); } [Fact] public void GetTypeByMetadataName_01() { var source1 = """ file class C { } """; // from source var comp = CreateCompilation(source1); comp.VerifyDiagnostics(); var sourceMember = comp.GetMember<NamedTypeSymbol>("C"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C", sourceMember.MetadataName); var sourceType = comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"); Assert.Equal(sourceMember, sourceType); Assert.Null(comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__D")); Assert.Null(comp.GetTypeByMetadataName("<>F1__C")); Assert.Null(comp.GetTypeByMetadataName("F0__C")); Assert.Null(comp.GetTypeByMetadataName("<file>F0__C")); Assert.Null(comp.GetTypeByMetadataName("C")); Assert.Null(comp.GetTypeByMetadataName("C`1")); // from metadata var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); comp2.VerifyDiagnostics(); var metadataMember = comp2.GetMember<NamedTypeSymbol>("C"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C", metadataMember.MetadataName); var metadataType = comp2.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"); Assert.Equal(metadataMember, metadataType); Assert.Null(comp2.GetTypeByMetadataName("C")); } [Fact] public void GetTypeByMetadataName_02() { var source1 = """ file class C<T> { } """; // from source var comp = CreateCompilation(source1); comp.VerifyDiagnostics(); var sourceMember = comp.GetMember<NamedTypeSymbol>("C"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1", sourceMember.MetadataName); var sourceType = comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1"); Assert.Equal(sourceMember, sourceType); Assert.Null(comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C")); Assert.Null(comp.GetTypeByMetadataName("C")); Assert.Null(comp.GetTypeByMetadataName("C`1")); // from metadata var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); comp2.VerifyDiagnostics(); var metadataMember = comp2.GetMember<NamedTypeSymbol>("C"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1", metadataMember.MetadataName); var metadataType = comp2.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1"); Assert.Equal(metadataMember, metadataType); Assert.Null(comp2.GetTypeByMetadataName("C`1")); } [Fact] public void GetTypeByMetadataName_03() { var source1 = """ class Outer { file class C { } // 1 } """; // from source var comp = CreateCompilation(source1); comp.VerifyDiagnostics( // (3,16): error CS9054: File-local type 'Outer.C' must be defined in a top level type; 'Outer.C' is a nested type. // file class C { } // 1 Diagnostic(ErrorCode.ERR_FileTypeNested, "C").WithArguments("Outer.C").WithLocation(3, 16)); var sourceMember = comp.GetMember<NamedTypeSymbol>("Outer.C"); Assert.Equal("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C", sourceMember.MetadataName); var sourceType = comp.GetTypeByMetadataName("Outer.<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"); // Note: strictly speaking, it would be reasonable to return the (invalid) nested file-local type symbol here. // However, since we don't actually support nested file types, we don't think we need the API to do the additional lookup // when the requested type is nested, and so we end up giving a null here. Assert.Null(sourceType); Assert.Null(comp.GetTypeByMetadataName("Outer.C")); } [Fact] public void GetTypeByMetadataName_04() { var source1 = """ file class C { } """; var source2 = """ class C { } """; // from source var comp = CreateCompilation(new[] { (source1, "file1.cs"), (source2, "file2.cs") }); comp.VerifyDiagnostics(); var sourceMember = comp.GetMembers("C")[0]; AssertEx.Equal("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C", sourceMember.MetadataName); var sourceType = comp.GetTypeByMetadataName("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C"); Assert.Equal(sourceMember, sourceType); var sourceTypeCByMetadataName = comp.GetTypeByMetadataName("C"); Assert.NotNull(sourceTypeCByMetadataName); Assert.Equal("C", sourceTypeCByMetadataName.MetadataName); Assert.False(sourceTypeCByMetadataName is SourceMemberContainerTypeSymbol { IsFileLocal: true }); // from metadata var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); comp2.VerifyDiagnostics(); var metadataMember = comp2.GetMembers("C")[0]; Assert.Equal("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C", metadataMember.MetadataName); var metadataType = comp2.GetTypeByMetadataName("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C"); Assert.Equal(metadataMember, metadataType); var metadataTypeCByMetadataName = comp2.GetTypeByMetadataName("C"); Assert.NotNull(metadataTypeCByMetadataName); Assert.Equal("C", metadataTypeCByMetadataName.MetadataName); } [CombinatorialData] [Theory] public void GetTypeByMetadataName_05(bool firstIsMetadataReference, bool secondIsMetadataReference) { var source1 = """ file class C { } """; // Create two references containing identically-named file types var ref1 = CreateCompilation(source1, assemblyName: "ref1"); var ref2 = CreateCompilation(source1, assemblyName: "ref2"); var comp = CreateCompilation("", references: new[] { firstIsMetadataReference ? ref1.ToMetadataReference() : ref1.EmitToImageReference(), secondIsMetadataReference ? ref2.ToMetadataReference() : ref2.EmitToImageReference() }); comp.VerifyDiagnostics(); const string metadataName = "<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"; var sourceType = comp.GetTypeByMetadataName(metadataName); Assert.Null(sourceType); Assert.Null(comp.GetTypeByMetadataName("C")); var types = comp.GetTypesByMetadataName(metadataName); Assert.Equal(2, types.Length); Assert.Equal(firstIsMetadataReference ? "C@<tree 0>" : "C@<unknown>", types[0].ToTestDisplayString()); Assert.Equal(secondIsMetadataReference ? "C@<tree 0>" : "C@<unknown>", types[1].ToTestDisplayString()); Assert.NotEqual(types[0], types[1]); Assert.Empty(comp.GetTypesByMetadataName("C")); } [Fact] public void GetTypeByMetadataName_06() { var source1 = """ file class C { } file class C { } """; var comp = CreateCompilation(source1); comp.VerifyDiagnostics( // (2,12): error CS9070: The namespace '<global namespace>' already contains a definition for 'C' in this file. // file class C { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "C").WithArguments("C", "<global namespace>").WithLocation(2, 12)); const string metadataName = "<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"; var sourceType = ((Compilation)comp).GetTypeByMetadataName(metadataName); Assert.Equal("C@<tree 0>", sourceType.ToTestDisplayString()); Assert.Null(((Compilation)comp).GetTypeByMetadataName("C")); var types = comp.GetTypesByMetadataName(metadataName); Assert.Equal(1, types.Length); Assert.Same(sourceType, types[0]); Assert.Empty(comp.GetTypesByMetadataName("C")); } [Fact] public void GetTypeByMetadataName_07() { var source1 = """ file class C { } """; var comp = CreateCompilation(SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreview, path: "path/to/SomeFile.cs")); comp.VerifyDiagnostics(); const string checksum = "0146C6A4DC0D382DC3D534F34EF202BE3FAF72EE35E08C8382B730D5270B6585"; Assert.Null(comp.GetTypeByMetadataName($"<>F{checksum}__C")); Assert.Empty(comp.GetTypesByMetadataName($"<>F{checksum}__C")); Assert.Null(comp.GetTypeByMetadataName($"<WrongName>F{checksum}__C")); Assert.Empty(comp.GetTypesByMetadataName($"<WrongName>F{checksum}__C")); var sourceType = ((Compilation)comp).GetTypeByMetadataName($"<SomeFile>F{checksum}__C"); Assert.Equal("C@SomeFile", sourceType.ToTestDisplayString()); var types = comp.GetTypesByMetadataName($"<SomeFile>F{checksum}__C"); Assert.Equal(1, types.Length); Assert.Same(sourceType, types[0]); } [Fact] public void GetTypeByMetadataName_08() { var source1 = """ file class C { public static void M() { } } """; var comp = CreateCompilation(source1, targetFramework: TargetFramework.Mscorlib40); comp.VerifyDiagnostics(); const string metadataName = "<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"; var member = comp.GetMember<NamedTypeSymbol>("C"); Assert.Equal(metadataName, member.MetadataName); Assert.Null(comp.GetTypeByMetadataName("C")); Assert.Equal(member, comp.GetTypeByMetadataName(metadataName)); var source2 = """ class C2 { void M() { C.M(); } } """; var comp2 = CreateCompilation(source2, references: new[] { comp.ToMetadataReference() }, targetFramework: TargetFramework.Mscorlib461); comp2.VerifyDiagnostics( // (5,9): error CS0103: The name 'C' does not exist in the current context // C.M(); Diagnostic(ErrorCode.ERR_NameNotInContext, "C").WithArguments("C").WithLocation(5, 9) ); Assert.NotEqual(comp.Assembly.CorLibrary, comp2.Assembly.CorLibrary); var retargeted = comp2.GetMember<NamedTypeSymbol>("C"); Assert.IsType<RetargetingNamedTypeSymbol>(retargeted); Assert.Equal(metadataName, retargeted.MetadataName); Assert.Null(comp2.GetTypeByMetadataName("C")); Assert.Equal(retargeted, comp2.GetTypeByMetadataName(metadataName)); } [Fact] public void AssociatedSyntaxTree_01() { var source = """ file class C { void M(C c) { } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var expectedChecksum = new byte[] { 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55 }; var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single(); var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; Assert.Equal("C@<tree 0>", type.ToTestDisplayString()); var identifier = type.GetSymbol()!.AssociatedFileIdentifier; Assert.NotNull(identifier); AssertEx.Equal(expectedChecksum, identifier.FilePathChecksumOpt); Assert.Empty(identifier.DisplayFilePath); Assert.True(type.IsFileLocal); var referencingMetadataComp = CreateCompilation("", new[] { comp.ToMetadataReference() }); type = ((Compilation)referencingMetadataComp).GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C")!; Assert.Equal("C@<tree 0>", type.ToTestDisplayString()); identifier = type.GetSymbol()!.AssociatedFileIdentifier; Assert.NotNull(identifier); AssertEx.Equal(expectedChecksum, identifier.FilePathChecksumOpt); Assert.Empty(identifier.DisplayFilePath); Assert.True(type.IsFileLocal); var referencingImageComp = CreateCompilation("", new[] { comp.EmitToImageReference() }); type = ((Compilation)referencingImageComp).GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C")!; Assert.Equal("C@<unknown>", type.ToTestDisplayString()); identifier = type.GetSymbol()!.AssociatedFileIdentifier; Assert.NotNull(identifier); AssertEx.Equal(expectedChecksum, identifier.FilePathChecksumOpt); Assert.Empty(identifier.DisplayFilePath); Assert.False(type.IsFileLocal); } [Fact] public void AssociatedSyntaxTree_02() { var source = """ class C { void M(C c) { } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single(); var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; Assert.Equal("C", type.ToTestDisplayString()); Assert.Null(type.GetSymbol()!.AssociatedFileIdentifier); Assert.False(type.IsFileLocal); } [Fact] public void AssociatedSyntaxTree_03() { var source = """ file class C<T> { void M(C<int> c) { } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single(); var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; Assert.Equal("C<System.Int32>@<tree 0>", type.ToTestDisplayString()); var identifier = type.GetSymbol()!.AssociatedFileIdentifier; Assert.NotNull(identifier); AssertEx.Equal( new byte[] { 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55 }, identifier.FilePathChecksumOpt); Assert.Empty(identifier.DisplayFilePath); Assert.True(type.IsFileLocal); } [Theory] [CombinatorialData] public void CannotAccessFromMetadata_01(bool useMetadataReference) { // Compare to 'InternalsVisibleToAndStrongNameTests.IVTBasicMetadata' var fileTypeSource = """ [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("HasIVTAccess")] file class C1 { public static void M1() {} } """; // reused across all compilations to try and trick the binder into thinking it's binding from the same file as 'file class C'. var filePath = "file1.cs"; var comp0 = CreateCompilation((fileTypeSource, filePath), options: TestOptions.SigningReleaseDll); comp0.VerifyDiagnostics(); var reference = useMetadataReference ? comp0.ToMetadataReference() : comp0.EmitToImageReference(); var useFileTypeSource = """ class C2 { void M2() { C1.M1(); } } """; // Whether or not you have an IVT, the compiler won't bind to a file type from a different compilation. verify("DoesNotHaveIVTAccess"); verify("HasIVTAccess"); void verify(string assemblyName) { var comp1 = CreateCompilation( (useFileTypeSource, filePath), references: new[] { reference }, assemblyName: assemblyName, options: TestOptions.SigningReleaseDll); comp1.VerifyDiagnostics( // file1.cs(5,9): error CS0103: The name 'C1' does not exist in the current context // C1.M1(); Diagnostic(ErrorCode.ERR_NameNotInContext, "C1").WithArguments("C1").WithLocation(5, 9)); } } [Fact] public void CannotAccessFromMetadata_02() { var fileTypeSource = """ file class C1 { public static void M1() {} } """; // reused across all compilations to try and trick the binder into thinking it's binding from the same file var filePath = "file1.cs"; var comp0 = CreateCompilation((fileTypeSource, filePath), options: TestOptions.SigningReleaseDll); comp0.VerifyDiagnostics(); var classC1 = comp0.GetMember<NamedTypeSymbol>("C1"); Assert.True(classC1.GetPublicSymbol().IsFileLocal); var reference = comp0.ToMetadataReference(); var useFileTypeSource = """ class C2 { void M2() { C1.M1(); } } """; var comp1 = CreateCompilation( (useFileTypeSource, filePath), references: new[] { reference }, targetFramework: TargetFramework.Mscorlib461, options: TestOptions.SigningReleaseDll); comp1.VerifyDiagnostics( // file1.cs(5,9): error CS0103: The name 'C1' does not exist in the current context // C1.M1(); Diagnostic(ErrorCode.ERR_NameNotInContext, "C1").WithArguments("C1").WithLocation(5, 9)); var retargeted = comp1.GetMember<NamedTypeSymbol>("C1"); Assert.IsType<RetargetingNamedTypeSymbol>(retargeted); Assert.False(retargeted.GetPublicSymbol().IsFileLocal); var originalFileIdentifier = classC1.AssociatedFileIdentifier!; var retargetedFileIdentifier = retargeted.AssociatedFileIdentifier!; Assert.Equal(originalFileIdentifier.DisplayFilePath, retargetedFileIdentifier.DisplayFilePath); Assert.Equal((IEnumerable<byte>)originalFileIdentifier.FilePathChecksumOpt, (IEnumerable<byte>)retargetedFileIdentifier.FilePathChecksumOpt); Assert.Equal(originalFileIdentifier.EncoderFallbackErrorMessage, retargetedFileIdentifier.EncoderFallbackErrorMessage); } [Fact] [ValidatePooledObjects(LeakReason = "Tests pass invalid inputs to public API, causing expected ArgumentExceptions that leak pooled objects.")] public void SyntaxTreeAlreadyPresent() { var tree = SyntaxFactory.ParseSyntaxTree(""" partial file class C { } """, path: "file1.cs", encoding: Encoding.Default); var ex = Assert.Throws<ArgumentException>(() => CreateCompilation(new[] { tree, tree })); Assert.Equal("trees[1]", ex.ParamName); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_01() { var source0 = """ var c = new C(); c.Use1(); c.Use2(); """; var source1 = """ using System; file interface FI { void M(); } partial class C : FI { void FI.M() { Console.Write(1); } public void Use1() { ((FI)this).M(); } } """; var source2 = """ using System; file interface FI { void M(); } partial class C : FI { void FI.M() { Console.Write(2); } public void Use2() { ((FI)this).M(); } } """; var verifier = CompileAndVerify(new[] { (source0, "F0.cs"), (source1, "F1.cs"), (source2, "F2.cs") }, expectedOutput: "12"); verifier.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_02() { var source1 = """ file interface FI { void M(); } partial class C : FI { void FI.M() => throw null!; } """; // Explicit implementation of 'FI.M()' in 'source1' does not implement 'FI.M()' in 'source2'. var source2 = """ file interface FI { void M(); } partial class C : FI { } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }); comp.VerifyDiagnostics( // F2.cs(6,19): error CS0535: 'C' does not implement interface member 'FI.M()' // partial class C : FI Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "FI").WithArguments("C", "FI.M()").WithLocation(6, 19)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_03() { var source1 = """ using System.Collections.Generic; file interface I { IReadOnlyDictionary<int, string> P { get; } } internal partial class C : I { private readonly Dictionary<int, string> _p = new() { { 1, "one" }, { 2, "two" } }; IReadOnlyDictionary<int, string> I.P => _p; } """; var source2 = """ using System.Collections.Generic; file interface I { IReadOnlyDictionary<int, string> P { get; } } internal partial class C : I { IReadOnlyDictionary<int, string> I.P => _p; } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }); comp.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_04() { var source1 = """ file interface I { int P { get; } } internal partial class C : I { } """; var source2 = """ file interface I { int P { get; } } internal partial class C : I { int I.P => 1; int I.P => 2; } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }); comp.VerifyDiagnostics( // F1.cs(6,24): error CS8646: 'I.P' is explicitly implemented more than once. // internal partial class C : I Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("I.P").WithLocation(6, 24), // F1.cs(6,28): error CS0535: 'C' does not implement interface member 'I.P' // internal partial class C : I Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I").WithArguments("C", "I.P").WithLocation(6, 28), // F2.cs(9,11): error CS0102: The type 'C' already contains a definition for '<F2>F141A34209AF0D3C8CA844A7D9A360C895EB14E557F17D27626C519D9BE96AF4A__I.P' // int I.P => 2; Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "<F2>F141A34209AF0D3C8CA844A7D9A360C895EB14E557F17D27626C519D9BE96AF4A__I.P").WithLocation(9, 11)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_05() { var source0 = """ var c = new C(); c.Use1(); c.Use2(); """; var source1 = """ using System; file interface FI { int Bar { get; } } internal partial class C : FI { int FI.Bar => 1; public void Use1() => Console.Write(((FI)this).Bar); } """; var source2 = """ using System; file interface FI { int Bar { get; } } internal partial class C : FI { int FI.Bar => 2; public void Use2() => Console.Write(((FI)this).Bar); } """; var verifier = CompileAndVerify(new[] { (source0, "F0.cs"), (source1, "F1.cs"), (source2, "F2.cs") }, expectedOutput: "12"); verifier.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_06() { var source0 = """ var c = new C(); c.Use1(); c.Use2(); """; var source1 = """ using System; file interface FI { event Action E; } internal partial class C : FI { event Action FI.E { add { Console.Write(1); } remove { } } public void Use1() => ((FI)this).E += () => { }; } """; var source2 = """ using System; file interface FI { event Action E; } internal partial class C : FI { event Action FI.E { add { Console.Write(2); } remove { } } public void Use2() => ((FI)this).E += () => { }; } """; var verifier = CompileAndVerify(new[] { (source0, "F0.cs"), (source1, "F1.cs"), (source2, "F2.cs") }, expectedOutput: "12"); verifier.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_07() { var source0 = """ var c = new C(); c.Use1(); c.Use2(); """; var source1 = """ using System; file interface FI { int this[int i] { get; } } internal partial class C : FI { int FI.this[int i] => 1; public void Use1() => Console.Write(((FI)this)[0]); } """; var source2 = """ using System; file interface FI { int this[int i] { get; } } internal partial class C : FI { int FI.this[int i] => 2; public void Use2() => Console.Write(((FI)this)[0]); } """; var verifier = CompileAndVerify(new[] { (source0, "F0.cs"), (source1, "F1.cs"), (source2, "F2.cs") }, expectedOutput: "12"); verifier.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_08() { // Test explicit implementation of a file interface operator in a partial type multiple times across files. // File types can't be used in signatures of non-file types, so this scenario isn't allowed currently, // but we'd like to make sure that redundant/invalid duplicate member name diagnostics aren't given here. var source1 = """ file interface FI { static abstract int operator +(FI fi, int i); } internal partial class C : FI { static int FI.operator +(FI fi, int i) => throw null!; // 1 } """; var source2 = """ file interface FI { static abstract int operator +(FI fi, int i); } internal partial class C : FI { static int FI.operator +(FI fi, int i) => throw null!; // 2 } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // F2.cs(8,28): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.operator +(FI fi, int i) => throw null!; // 2 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("FI", "C").WithLocation(8, 28), // F1.cs(8,28): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.operator +(FI fi, int i) => throw null!; // 1 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("FI", "C").WithLocation(8, 28) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_09() { // Similar to PartialExplicitImplementation_08, except only one of the files contains duplicate operator implementations. var source1 = """ file interface FI { static abstract int operator +(FI fi, int i); } internal partial class C : FI // 1, 2 { } """; var source2 = """ file interface FI { static abstract int operator +(FI fi, int i); } internal partial class C : FI { static int FI.operator +(FI fi, int i) => throw null!; // 3 static int FI.operator +(FI fi, int i) => throw null!; // 4, 5 } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // F1.cs(6,24): error CS8646: 'FI.operator +(FI, int)' is explicitly implemented more than once. // internal partial class C : FI // 1, 2 Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("FI.operator +(FI, int)").WithLocation(6, 24), // F1.cs(6,28): error CS0535: 'C' does not implement interface member 'FI.operator +(FI, int)' // internal partial class C : FI // 1, 2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "FI").WithArguments("C", "FI.operator +(FI, int)").WithLocation(6, 28), // F2.cs(8,28): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.operator +(FI fi, int i) => throw null!; // 3 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("FI", "C").WithLocation(8, 28), // F2.cs(9,28): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.operator +(FI fi, int i) => throw null!; // 4, 5 Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("FI", "C").WithLocation(9, 28), // F2.cs(9,28): error CS0111: Type 'C' already defines a member called '<F2>F141A34209AF0D3C8CA844A7D9A360C895EB14E557F17D27626C519D9BE96AF4A__FI.op_Addition' with the same parameter types // static int FI.operator +(FI fi, int i) => throw null!; // 4, 5 Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "+").WithArguments("<F2>F141A34209AF0D3C8CA844A7D9A360C895EB14E557F17D27626C519D9BE96AF4A__FI.op_Addition", "C").WithLocation(9, 28) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_10() { // Test explicit implementation of a file interface operator in a partial type. // In another file, implement a member in a type with the same source name, but with member name using the metadata name of the same operator. // File types can't be used in signatures of non-file types, so this scenario isn't allowed currently, // but we'd like to make sure that redundant/invalid duplicate member name diagnostics aren't given here. var source1 = """ file interface FI { static abstract int operator +(FI fi, int i); } internal partial class C : FI { static int FI.operator +(FI fi, int i) => throw null!; } """; var source2 = """ file interface FI { static abstract int op_Addition(FI fi, int i); } internal partial class C : FI { static int FI.op_Addition(FI fi, int i) => throw null!; } """; var comp = CreateCompilation(new[] { (source1, "F1.cs"), (source2, "F2.cs") }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // F2.cs(8,19): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.op_Addition(FI fi, int i) => throw null!; Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "op_Addition").WithArguments("FI", "C").WithLocation(8, 19), // F1.cs(8,28): error CS9051: File-local type 'FI' cannot be used in a member signature in non-file-local type 'C'. // static int FI.operator +(FI fi, int i) => throw null!; Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "+").WithArguments("FI", "C").WithLocation(8, 28) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68219")] public void PartialExplicitImplementation_11() { var source0 = """ var c = new C(); c.Use1(); c.Use2(); interface I<T> { void M(); } """; var source1 = """ using System; file interface FI { } partial class C : I<FI> { void I<FI>.M() { Console.Write(1); } public void Use1() { ((I<FI>)this).M(); } } """; var source2 = """ using System; file interface FI { } partial class C : I<FI> { void I<FI>.M() { Console.Write(2); } public void Use2() { ((I<FI>)this).M(); } } """; var verifier = CompileAndVerify(new[] { (source0, "F0.cs"), (source1, "F1.cs"), (source2, "F2.cs") }, expectedOutput: "12"); verifier.VerifyDiagnostics(); } [Fact] public void ShadowNamespace_01() { var source1 = """ namespace App.Widget { class Inner { } } """; var source2 = """ namespace App { file class Widget { } } """; var comp = CreateCompilation(new[] { (source1, "File1.cs"), (source2, "File2.cs") }); comp.VerifyDiagnostics(); comp = CreateCompilation(source1 + source2); comp.VerifyDiagnostics( // (7,16): error CS9071: The namespace 'App' already contains a definition for 'Widget' in this file. // file class Widget { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "Widget").WithArguments("Widget", "App").WithLocation(7, 16)); comp = CreateCompilation(source2 + source1); comp.VerifyDiagnostics( // (3,16): error CS9071: The namespace 'App' already contains a definition for 'Widget' in this file. // file class Widget { } Diagnostic(ErrorCode.ERR_FileLocalDuplicateNameInNS, "Widget").WithArguments("Widget", "App").WithLocation(3, 16)); } [Theory, CombinatorialData] public void ShadowNamespace_02(bool useMetadataReference) { var source1 = """ namespace App.Widget { public class Inner { } } """; var source2 = """ namespace App { file class Widget { } } """; var comp1 = CreateCompilation(new[] { (source1, "File1.cs") }); comp1.VerifyEmitDiagnostics(); var comp2 = CreateCompilation(new[] { (source2, "File2.cs") }, references: new[] { useMetadataReference ? comp1.ToMetadataReference() : comp1.EmitToImageReference() }); comp2.VerifyEmitDiagnostics(); comp2 = CreateCompilation(new[] { (source2, "File2.cs") }); comp2.VerifyEmitDiagnostics(); comp1 = CreateCompilation(new[] { (source1, "File1.cs") }, references: new[] { useMetadataReference ? comp2.ToMetadataReference() : comp2.EmitToImageReference() }); comp1.VerifyEmitDiagnostics(); } [Fact] public void ShadowNamespace_03() { var source1 = """ namespace App.Widget { class Inner { } } class C1 { static void M1() { new App.Widget(); // 1 new App.Widget.Inner(); } } """; var source2 = """ namespace App { file class Widget { } } class C2 { static void M2() { new App.Widget(); new App.Widget.Inner(); // 2 } } """; var comp = CreateCompilation(new[] { (source1, "File1.cs"), (source2, "File2.cs") }); comp.VerifyDiagnostics( // File1.cs(10,13): error CS0118: 'App.Widget' is a namespace but is used like a type // new App.Widget(); // 1 Diagnostic(ErrorCode.ERR_BadSKknown, "App.Widget").WithArguments("App.Widget", "namespace", "type").WithLocation(10, 13), // File2.cs(11,24): error CS0426: The type name 'Inner' does not exist in the type 'Widget' // new App.Widget.Inner(); // 2 Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInAgg, "Inner").WithArguments("Inner", "App.Widget").WithLocation(11, 24)); } }