diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 85417b7843492..4f99b63cec964 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -1,5 +1,18 @@ # This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7. +## Types cannot be named `file` + +***Introduced in Visual Studio 2022 version 17.4.*** Starting in C# 11, types cannot be named `file`. The compiler will report an error on all such type names. To work around this, the type name and all usages must be escaped with an `@`: + +```csharp +class file {} // Error CS9056 +class @file {} // No error +``` + +This was done as `file` is now a modifier for type declarations. + +You can learn more about this change in the associated [csharplang issue](https://github.com/dotnet/csharplang/issues/6011). + ## Required spaces in #line span directives ***Introduced in .NET SDK 6.0.400, Visual Studio 2022 version 17.3.*** @@ -426,4 +439,4 @@ class @required {} // No error This was done as `required` is now a member modifier for properties and fields. -You can learn more about this change in the associated [csharplang issue](https://github.com/dotnet/csharplang/issues/3630). +You can learn more about this change in the associated [csharplang issue](https://github.com/dotnet/csharplang/issues/3630). \ No newline at end of file diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md index f287fc02e3f49..a3d0b6b4f463a 100644 --- a/docs/contributing/Compiler Test Plan.md +++ b/docs/contributing/Compiler Test Plan.md @@ -38,6 +38,7 @@ This document provides guidance for thinking about language interactions and tes # Type and members - Access modifiers (public, protected, internal, protected internal, private protected, private), static, ref - type declarations (class, record class/struct with or without positional members, struct, interface, type parameter) +- file-local types - methods - fields (required and not) - properties (including get/set/init accessors, required and not) diff --git a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs index 143b42bf932bb..75a4ca3e5a51c 100644 --- a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs +++ b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs @@ -611,5 +611,71 @@ internal class C : I await test.RunAsync(); } + + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)] + [InlineData("class")] + [InlineData("struct")] + [InlineData("record")] + [InlineData("record struct")] + [InlineData("interface")] + [InlineData("enum")] + [WorkItem(62259, "https://github.com/dotnet/roslyn/issues/62259")] + public async Task TestFileDeclaration(string declarationKind) + { + var source = $"file {declarationKind} C {{ }}"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = source, + LanguageVersion = LanguageVersion.Preview, + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)] + [WorkItem(62259, "https://github.com/dotnet/roslyn/issues/62259")] + public async Task TestFileDelegate() + { + var source = "file delegate void M();"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = source, + LanguageVersion = LanguageVersion.Preview, + }.RunAsync(); + } + + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)] + [InlineData("class")] + [InlineData("struct")] + [InlineData("record")] + [InlineData("record struct")] + [InlineData("interface")] + [InlineData("enum")] + [WorkItem(62259, "https://github.com/dotnet/roslyn/issues/62259")] + public async Task TestNestedFileDeclaration(string declarationKind) + { + var source = $$""" + file class C1 + { + {{declarationKind}} [|C2|] { } + } + """; + + var fixedSource = $$""" + file class C1 + { + private {{declarationKind}} C2 { } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = LanguageVersion.Preview, + }.RunAsync(); + } } } diff --git a/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt b/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt index 691883be01a72..4c8f5c4788aaa 100644 --- a/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt +++ b/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt @@ -42,6 +42,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsPartial.get -> bo Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsReadOnly.get -> bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRef.get -> bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRequired.get -> bool +Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsFile.get -> bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsSealed.get -> bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsStatic.get -> bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsUnsafe.get -> bool @@ -58,6 +59,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsOverride(bool Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsReadOnly(bool isReadOnly) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRef(bool isRef) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRequired(bool isRequired) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers +Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsFile(bool isFile) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsSealed(bool isSealed) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsStatic(bool isStatic) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsUnsafe(bool isUnsafe) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers @@ -87,6 +89,7 @@ static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Partial.get static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.ReadOnly.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Ref.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Required.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers +static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Sealed.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Static.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.TryParse(string value, out Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers modifiers) -> bool diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs index 7c45b4cd1e836..b9ae6f4c7b29b 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs @@ -75,7 +75,7 @@ internal BinderFactory(CSharpCompilation compilation, SyntaxTree syntaxTree, boo // more than 50 items added before getting collected. _binderCache = new ConcurrentCache(50); - _buckStopsHereBinder = new BuckStopsHereBinder(compilation); + _buckStopsHereBinder = new BuckStopsHereBinder(compilation, syntaxTree); } internal SyntaxTree SyntaxTree diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs index 7f81b7826e5bb..a827850296103 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs @@ -410,6 +410,12 @@ private static void CheckConstraintTypeVisibility( // "Inconsistent accessibility: constraint type '{1}' is less accessible than '{0}'" diagnostics.Add(ErrorCode.ERR_BadVisBound, location, containingSymbol, constraintType.Type); } + + if (constraintType.Type.IsFileTypeOrUsesFileTypes() && !containingSymbol.ContainingType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, location, constraintType.Type, containingSymbol); + } + diagnostics.Add(location, useSiteInfo); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 7dffbb5ccce28..47bd4d832fccd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1306,6 +1306,38 @@ internal static ImmutableArray GetCandidateMembers(NamespaceOrTypeSymbol } } + private bool IsInScopeOfAssociatedSyntaxTree(Symbol symbol) + { + while (symbol is not null and not SourceMemberContainerTypeSymbol { IsFile: true }) + { + symbol = symbol.ContainingType; + } + + if (symbol is null) + { + // the passed-in symbol was not contained in a file type. + return true; + } + + var tree = getSyntaxTreeForFileTypes(); + return symbol.IsDefinedInSourceTree(tree, definedWithinSpan: null); + + SyntaxTree getSyntaxTreeForFileTypes() + { + for (var binder = this; binder != null; binder = binder.Next) + { + if (binder is BuckStopsHereBinder lastBinder) + { + Debug.Assert(lastBinder.AssociatedSyntaxTree is not null); + return lastBinder.AssociatedSyntaxTree; + } + } + + Debug.Assert(false); + return null; + } + } + /// /// Distinguish from , which performs an analogous task for Add*LookupSymbolsInfo*. /// @@ -1322,8 +1354,12 @@ internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptio ? ((AliasSymbol)symbol).GetAliasTarget(basesBeingResolved) : symbol; + if (!IsInScopeOfAssociatedSyntaxTree(unwrappedSymbol)) + { + return LookupResult.Empty(); + } // Check for symbols marked with 'Microsoft.CodeAnalysis.Embedded' attribute - if (!this.Compilation.SourceModule.Equals(unwrappedSymbol.ContainingModule) && unwrappedSymbol.IsHiddenByCodeAnalysisEmbeddedAttribute()) + else if (!this.Compilation.SourceModule.Equals(unwrappedSymbol.ContainingModule) && unwrappedSymbol.IsHiddenByCodeAnalysisEmbeddedAttribute()) { return LookupResult.Empty(); } @@ -1522,6 +1558,10 @@ internal bool CanAddLookupSymbolInfo(Symbol symbol, LookupOptions options, Looku { return false; } + else if (!IsInScopeOfAssociatedSyntaxTree(symbol)) + { + return false; + } else if ((options & LookupOptions.MustBeInstance) != 0 && !IsInstance(symbol)) { return false; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index caded00d3be20..ee5c62da9ec38 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1816,6 +1816,16 @@ Symbol resultSymbol( Debug.Assert(!Symbol.Equals(first, second, TypeCompareKind.ConsiderEverything) || !Symbol.Equals(originalSymbols[best.Index], originalSymbols[secondBest.Index], TypeCompareKind.ConsiderEverything), "Why does the LookupResult contain the same symbol twice?"); + if (best.IsFromFile && !secondBest.IsFromFile) + { + // a lookup of a file type is "better" than a lookup of a non-file type; no need to further diagnose + // https://github.com/dotnet/roslyn/issues/62331 + // some "single symbol" diagnostics are missed here for similar reasons + // that make us miss diagnostics when reporting WRN_SameFullNameThisAggAgg. + // + return first; + } + CSDiagnosticInfo info; bool reportError; @@ -2133,6 +2143,7 @@ private static AssemblySymbol GetContainingAssembly(Symbol symbol) private enum BestSymbolLocation { None, + FromFile, FromSourceModule, FromAddedModule, FromReferencedAssembly, @@ -2180,6 +2191,14 @@ public bool IsFromCompilation } } + public bool IsFromFile + { + get + { + return _location == BestSymbolLocation.FromFile; + } + } + public bool IsNone { get @@ -2281,6 +2300,11 @@ private BestSymbolInfo GetBestSymbolInfo(ArrayBuilder symbols, out BestS private static BestSymbolLocation GetLocation(CSharpCompilation compilation, Symbol symbol) { + if (symbol is SourceMemberContainerTypeSymbol { IsFile: true }) + { + return BestSymbolLocation.FromFile; + } + var containingAssembly = symbol.ContainingAssembly; if (containingAssembly == compilation.SourceAssembly) { @@ -2470,7 +2494,8 @@ protected AssemblySymbol GetForwardedToAssembly(string name, int arity, ref Name // NOTE: This won't work if the type isn't using CLS-style generic naming (i.e. `arity), but this code is // only intended to improve diagnostic messages, so false negatives in corner cases aren't a big deal. - var metadataName = MetadataHelpers.ComposeAritySuffixedMetadataName(name, arity); + // File types can't be forwarded, so we won't attempt to determine a file identifier to attach to the metadata name. + var metadataName = MetadataHelpers.ComposeAritySuffixedMetadataName(name, arity, associatedFileIdentifier: null); var fullMetadataName = MetadataHelpers.BuildQualifiedName(qualifierOpt?.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat), metadataName); var result = GetForwardedToAssembly(fullMetadataName, diagnostics, location); if ((object)result != null) diff --git a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs index 9c3a274d28542..caa0df8490196 100644 --- a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs @@ -15,11 +15,21 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal class BuckStopsHereBinder : Binder { - internal BuckStopsHereBinder(CSharpCompilation compilation) + internal BuckStopsHereBinder(CSharpCompilation compilation, SyntaxTree? associatedSyntaxTree) : base(compilation) { + this.AssociatedSyntaxTree = associatedSyntaxTree; } + /// + /// In non-speculative scenarios, the syntax tree being bound. + /// In speculative scenarios, the syntax tree from the original compilation used as the speculation context. + /// This is in some scenarios, such as the binder used for + /// or the binder used to bind usings in . + /// https://github.com/dotnet/roslyn/issues/62332: what about in EE scenarios? + /// + internal readonly SyntaxTree? AssociatedSyntaxTree; + internal override ImportChain? ImportChain { get diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index d020c8770a693..e07d03a21ee8c 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7124,6 +7124,24 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{2}' cannot satisfy the 'new()' constraint on parameter '{1}' in the generic type or or method '{0}' because '{2}' has required members. + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + File type '{0}' cannot use accessibility modifiers. + + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + File type '{0}' cannot be used in a 'global using static' directive. + + + Types and aliases cannot be named 'file'. + unsigned right shift @@ -7157,4 +7175,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations + + file types + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.UsingsFromOptionsAndDiagnostics.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.UsingsFromOptionsAndDiagnostics.cs index 2848b30694105..09f02a1f032e3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.UsingsFromOptionsAndDiagnostics.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.UsingsFromOptionsAndDiagnostics.cs @@ -34,7 +34,7 @@ public static UsingsFromOptionsAndDiagnostics FromOptions(CSharpCompilation comp } var diagnostics = new DiagnosticBag(); - var usingsBinder = new InContainerBinder(compilation.GlobalNamespace, new BuckStopsHereBinder(compilation)); + var usingsBinder = new InContainerBinder(compilation.GlobalNamespace, new BuckStopsHereBinder(compilation, associatedSyntaxTree: null)); var boundUsings = ArrayBuilder.GetInstance(); var uniqueUsings = PooledHashSet.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 022eb88de3f7a..b3e13fc59505b 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -82,7 +82,7 @@ internal Conversions Conversions { if (_conversions == null) { - Interlocked.CompareExchange(ref _conversions, new BuckStopsHereBinder(this).Conversions, null); + Interlocked.CompareExchange(ref _conversions, new BuckStopsHereBinder(this, associatedSyntaxTree: null).Conversions, null); } return _conversions; diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs index ad5544fa88802..ca66c4ca60479 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs @@ -536,7 +536,7 @@ private void BindName(XAttribute attribute, CSharpSyntaxNode originatingSyntax, "Why are we processing a documentation comment that is not attached to a member declaration?"); var nameDiagnostics = BindingDiagnosticBag.GetInstance(_diagnostics); - Binder binder = MakeNameBinder(isParameter, isTypeParameterRef, _memberSymbol, _compilation); + Binder binder = MakeNameBinder(isParameter, isTypeParameterRef, _memberSymbol, _compilation, originatingSyntax.SyntaxTree); DocumentationCommentCompiler.BindName(attrSyntax, binder, _memberSymbol, ref _documentedParameters, ref _documentedTypeParameters, nameDiagnostics); RecordBindingDiagnostics(nameDiagnostics, sourceLocation); // Respects DocumentationMode. nameDiagnostics.Free(); @@ -545,9 +545,9 @@ private void BindName(XAttribute attribute, CSharpSyntaxNode originatingSyntax, // NOTE: We're not sharing code with the BinderFactory visitor, because we already have the // member symbol in hand, which makes things much easier. - private static Binder MakeNameBinder(bool isParameter, bool isTypeParameterRef, Symbol memberSymbol, CSharpCompilation compilation) + private static Binder MakeNameBinder(bool isParameter, bool isTypeParameterRef, Symbol memberSymbol, CSharpCompilation compilation, SyntaxTree syntaxTree) { - Binder binder = new BuckStopsHereBinder(compilation); + Binder binder = new BuckStopsHereBinder(compilation, syntaxTree); // All binders should have a containing symbol. Symbol containingSymbol = memberSymbol.ContainingSymbol; diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs index a63ef846b95fb..dc617f84502b4 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs @@ -37,9 +37,10 @@ internal enum DeclarationModifiers : uint Ref = 1 << 21, // used only for structs Required = 1 << 22, // Used only for properties and fields Scoped = 1 << 23, + File = 1 << 24, // used only for types - All = (1 << 24) - 1, // all modifiers - Unset = 1 << 24, // used when a modifiers value hasn't yet been computed + All = (1 << 25) - 1, // all modifiers + Unset = 1 << 25, // used when a modifiers value hasn't yet been computed AccessibilityMask = PrivateProtected | Private | Protected | Internal | ProtectedInternal | Public, } diff --git a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs index bacdf2682c2d7..36ed4323bf2a9 100644 --- a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs @@ -241,6 +241,14 @@ public bool Equals(TypeDeclarationIdentity other) return false; } + if ((object)thisDecl.Location.SourceTree != otherDecl.Location.SourceTree + && ((thisDecl.Modifiers & DeclarationModifiers.File) != 0 + || (otherDecl.Modifiers & DeclarationModifiers.File) != 0)) + { + // declarations of 'file' types are only the same type if they are in the same file + return false; + } + if (thisDecl._kind == DeclarationKind.Enum || thisDecl._kind == DeclarationKind.Delegate) { // oh, so close, but enums and delegates cannot be partial diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs index a4eeac4890e69..4d5b6f4b2c18b 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs @@ -40,6 +40,16 @@ bool Cci.INamedTypeReference.MangleName } } +#nullable enable + string? Cci.INamedTypeReference.AssociatedFileIdentifier + { + get + { + return UnderlyingNamedType.AssociatedFileIdentifier(); + } + } +#nullable disable + string Cci.INamedEntity.Name { get diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index 377645bb42318..ea7196e6596e2 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -756,6 +756,16 @@ bool Cci.INamedTypeReference.MangleName } } +#nullable enable + string? Cci.INamedTypeReference.AssociatedFileIdentifier + { + get + { + return AdaptedNamedTypeSymbol.AssociatedFileIdentifier(); + } + } +#nullable disable + string Cci.INamedEntity.Name { get diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index ad7b7dfddbc90..13803de6d1fff 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -660,7 +660,7 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e // exported types are not emitted in EnC deltas (hence generation 0): string fullEmittedName = MetadataHelpers.BuildQualifiedName( ((Cci.INamespaceTypeReference)type.GetCciAdapter()).NamespaceName, - Cci.MetadataWriter.GetMangledName(type.GetCciAdapter(), generation: 0)); + Cci.MetadataWriter.GetMetadataName(type.GetCciAdapter(), generation: 0)); // First check against types declared in the primary module if (ContainsTopLevelType(fullEmittedName)) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index c5e76deb24497..f5c0e9941c3d1 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2099,6 +2099,13 @@ internal enum ErrorCode ERR_FixedFieldMustNotBeRef = 9049, ERR_RefFieldCannotReferToRefStruct = 9050, + ERR_FileTypeDisallowedInSignature = 9051, + ERR_FileTypeNoExplicitAccessibility = 9052, + ERR_FileTypeBase = 9053, + ERR_FileTypeNested = 9054, + ERR_GlobalUsingStaticFileType = 9055, + ERR_FileTypeNameDisallowed = 9056, + #endregion // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index a6a514693b458..0cb52e6c2fea5 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -253,9 +253,11 @@ internal enum MessageID IDS_FeatureUnsignedRightShift = MessageBase + 12823, IDS_FeatureExtendedNameofScope = MessageBase + 12824, + IDS_FeatureRelaxedShiftOperator = MessageBase + 12825, IDS_FeatureRequiredMembers = MessageBase + 12826, IDS_FeatureRefFields = MessageBase + 12827, + IDS_FeatureFileTypes = MessageBase + 12828, } // Message IDs may refer to strings that need to be localized. @@ -380,6 +382,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureExtendedNameofScope: // semantic check case MessageID.IDS_FeatureRelaxedShiftOperator: // semantic check case MessageID.IDS_FeatureRefFields: // semantic check + case MessageID.IDS_FeatureFileTypes: // semantic check return LanguageVersion.Preview; // C# 10.0 features. diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index ad82fa00e9719..672775a68ae60 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -99,6 +99,7 @@ modifier | 'async' | 'const' | 'extern' + | 'file' | 'fixed' | 'internal' | 'new' diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index c8ea1ae07a6ec..761d756887d6f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -125,7 +125,7 @@ internal BoundExpression MakeInvocationExpression( private sealed class SyntheticBinderImpl : BuckStopsHereBinder { private readonly SyntheticBoundNodeFactory _factory; - internal SyntheticBinderImpl(SyntheticBoundNodeFactory factory) : base(factory.Compilation) + internal SyntheticBinderImpl(SyntheticBoundNodeFactory factory) : base(factory.Compilation, associatedSyntaxTree: null) { _factory = factory; } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 5d25277ad7c14..b16fda396c9e7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -1167,6 +1167,8 @@ internal static DeclarationModifiers GetModifier(SyntaxKind kind, SyntaxKind con return DeclarationModifiers.Required; case SyntaxKind.ScopedKeyword: return DeclarationModifiers.Scoped; + case SyntaxKind.FileKeyword: + return DeclarationModifiers.File; } goto default; @@ -1248,8 +1250,18 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors, bool fo break; } + case DeclarationModifiers.File: + if ((!IsFeatureEnabled(MessageID.IDS_FeatureFileTypes) || forTopLevelStatements) && !ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + { + return; + } + + // LangVersion errors for 'file' modifier are given during binding. + modTok = ConvertToKeyword(EatToken()); + break; + case DeclarationModifiers.Async: - if (!ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + if (!ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: false)) { return; } @@ -1263,7 +1275,7 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors, bool fo // machinery to make a conservative guess as to whether the user meant required to be a keyword, so that they get a good langver // diagnostic and all the machinery to upgrade their project kicks in. The only exception to this rule is top level statements, // where the user could conceivably have a local named required. For these locations, we need to disambiguate as well. - if ((!IsFeatureEnabled(MessageID.IDS_FeatureRequiredMembers) || forTopLevelStatements) && !ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + if ((!IsFeatureEnabled(MessageID.IDS_FeatureRequiredMembers) || forTopLevelStatements) && !ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: false)) { return; } @@ -1273,7 +1285,7 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors, bool fo break; case DeclarationModifiers.Scoped: - if (!ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + if (!ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: false)) { return; } @@ -1308,16 +1320,16 @@ bool isStructOrRecordKeyword(SyntaxToken token) } } - private bool ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(bool parsingStatementNotDeclaration) + private bool ShouldContextualKeywordBeTreatedAsModifier(bool parsingStatementNotDeclaration) { - Debug.Assert(this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.RequiredKeyword or SyntaxKind.ScopedKeyword); + Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IdentifierToken && GetModifier(this.CurrentToken) != DeclarationModifiers.None); // Adapted from CParser::IsAsyncMethod. if (IsNonContextualModifier(PeekToken(1))) { // If the next token is a (non-contextual) modifier keyword, then this token is - // definitely the async or required keyword + // definitely a modifier return true; } @@ -1328,7 +1340,7 @@ private bool ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(bool parsingStatem try { - this.EatToken(); //move past contextual 'async' or 'required' + this.EatToken(); //move past contextual token if (!parsingStatementNotDeclaration && (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)) @@ -1336,13 +1348,12 @@ private bool ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(bool parsingStatem this.EatToken(); // "partial" doesn't affect our decision, so look past it. } - // Comment directly from CParser::IsAsyncMethod. - // ... 'async' [partial] ... - // ... 'async' [partial] ... - // ... 'async' [partial] ... - // ... 'async' [partial] ... - // ... 'async' [partial] ... - // ... 'async' [partial] ... + // ... 'TOKEN' [partial] ... + // ... 'TOKEN' [partial] ... + // ... 'TOKEN' [partial] ... + // ... 'TOKEN' [partial] ... + // ... 'TOKEN' [partial] ... + // ... 'TOKEN' [partial] ... // DEVNOTE: Although we parse async user defined conversions, operators, etc. here, // anything other than async methods are detected as erroneous later, during the define phase // The comments in general were not updated to add "async or required" everywhere to preserve @@ -1361,56 +1372,56 @@ private bool ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(bool parsingStatem if (ScanType() != ScanTypeFlags.NotType) { - // We've seen "async TypeName". Now we have to determine if we should we treat - // 'async' as a modifier. Or is the user actually writing something like - // "public async Goo" where 'async' is actually the return type. + // We've seen "TOKEN TypeName". Now we have to determine if we should we treat + // 'TOKEN' as a modifier. Or is the user actually writing something like + // "public TOKEN Goo" where 'TOKEN' is actually the return type. if (IsPossibleMemberName()) { - // we have: "async Type X" or "async Type this", 'async' is definitely a + // we have: "TOKEN Type X" or "TOKEN Type this", 'TOKEN' is definitely a // modifier here. return true; } var currentTokenKind = this.CurrentToken.Kind; - // The file ends with "async TypeName", it's not legal code, and it's much + // The file ends with "TOKEN TypeName", it's not legal code, and it's much // more likely that this is meant to be a modifier. if (currentTokenKind == SyntaxKind.EndOfFileToken) { return true; } - // "async TypeName }". In this case, we just have an incomplete member, and - // we should definitely default to 'async' being considered a return type here. + // "TOKEN TypeName }". In this case, we just have an incomplete member, and + // we should definitely default to 'TOKEN' being considered a return type here. if (currentTokenKind == SyntaxKind.CloseBraceToken) { return true; } - // "async TypeName void". In this case, we just have an incomplete member before - // an existing member. Treat this 'async' as a keyword. + // "TOKEN TypeName void". In this case, we just have an incomplete member before + // an existing member. Treat this 'TOKEN' as a keyword. if (SyntaxFacts.IsPredefinedType(this.CurrentToken.Kind)) { return true; } - // "async TypeName public". In this case, we just have an incomplete member before - // an existing member. Treat this 'async' as a keyword. + // "TOKEN TypeName public". In this case, we just have an incomplete member before + // an existing member. Treat this 'TOKEN' as a keyword. if (IsNonContextualModifier(this.CurrentToken)) { return true; } - // "async TypeName class". In this case, we just have an incomplete member before - // an existing type declaration. Treat this 'async' as a keyword. + // "TOKEN TypeName class". In this case, we just have an incomplete member before + // an existing type declaration. Treat this 'TOKEN' as a keyword. if (IsTypeDeclarationStart()) { return true; } - // "async TypeName namespace". In this case, we just have an incomplete member before - // an existing namespace declaration. Treat this 'async' as a keyword. + // "TOKEN TypeName namespace". In this case, we just have an incomplete member before + // an existing namespace declaration. Treat this 'TOKEN' as a keyword. if (currentTokenKind == SyntaxKind.NamespaceKeyword) { return true; @@ -2688,7 +2699,7 @@ static bool isAcceptableNonDeclarationStatement(StatementSyntax statement, bool private bool IsMisplacedModifier(SyntaxListBuilder modifiers, SyntaxList attributes, TypeSyntax type, out MemberDeclarationSyntax result) { if (GetModifier(this.CurrentToken) != DeclarationModifiers.None && - this.CurrentToken.ContextualKind is not (SyntaxKind.PartialKeyword or SyntaxKind.AsyncKeyword or SyntaxKind.RequiredKeyword or SyntaxKind.ScopedKeyword) && + this.CurrentToken.ContextualKind is not (SyntaxKind.PartialKeyword or SyntaxKind.AsyncKeyword or SyntaxKind.RequiredKeyword or SyntaxKind.ScopedKeyword or SyntaxKind.FileKeyword) && IsComplete(type)) { var misplacedModifier = this.CurrentToken; @@ -8099,13 +8110,13 @@ private bool IsPossibleLocalDeclarationStatement(bool isGlobalScriptLevel) tk = this.CurrentToken.ContextualKind; if (tk == SyntaxKind.ScopedKeyword && - ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(parsingStatementNotDeclaration: true)) + ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: true)) { return true; } var isPossibleAttributeOrModifier = (IsAdditionalLocalFunctionModifier(tk) || tk == SyntaxKind.OpenBracketToken) - && (tk != SyntaxKind.AsyncKeyword || ShouldAsyncOrRequiredOrScopedBeTreatedAsModifier(parsingStatementNotDeclaration: true)); + && (tk != SyntaxKind.AsyncKeyword || ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: true)); if (isPossibleAttributeOrModifier) { return true; diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 623654949ba86..bfbee81018f48 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -5,6 +5,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedSingleLineRawStringStartTok Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineRawStringLiteralToken = 8519 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.RequiredKeyword = 8447 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.ScopedKeyword = 8448 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.FileKeyword = 8449 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineRawStringLiteralToken = 8518 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax? initializer, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! body) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorDeclarationSyntax! *REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax! initializer, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! body) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorDeclarationSyntax! diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index aeecef424f270..6bad689cf0e76 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -6,9 +6,11 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SymbolDisplay; using Roslyn.Utilities; @@ -179,6 +181,30 @@ public override void VisitNamedType(INamedTypeSymbol symbol) { VisitNamedTypeWithoutNullability(symbol); AddNullableAnnotations(symbol); + + if ((format.CompilerInternalOptions & SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes) != 0 + && symbol is Symbols.PublicModel.Symbol { UnderlyingSymbol: NamedTypeSymbol { AssociatedSyntaxTree: SyntaxTree tree } internalSymbol }) + { + var fileDescription = getDisplayFileName(tree) is { Length: not 0 } path + ? path + : $""; + + builder.Add(CreatePart(SymbolDisplayPartKind.Punctuation, symbol, "@")); + builder.Add(CreatePart(SymbolDisplayPartKind.ModuleName, symbol, fileDescription)); + } + + static string getDisplayFileName(SyntaxTree tree) + { + if (tree.FilePath.Length == 0) + { + return ""; + } + + var pooledBuilder = PooledStringBuilder.GetInstance(); + var sb = pooledBuilder.Builder; + GeneratedNames.AppendFileName(tree.FilePath, sb); + return pooledBuilder.ToStringAndFree(); + } } private void VisitNamedTypeWithoutNullability(INamedTypeSymbol symbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs index 36e8bb11c2d01..ca8f87f34c1f7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs @@ -73,6 +73,8 @@ internal sealed override bool MangleName get { return false; } } + internal sealed override SyntaxTree? AssociatedSyntaxTree => null; + public sealed override int Arity { get { return 0; } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs index 7f335aea333b8..db12a912c9cfa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs @@ -237,6 +237,8 @@ internal sealed override bool MangleName get { return this.Arity > 0; } } + internal sealed override SyntaxTree? AssociatedSyntaxTree => null; + internal sealed override ImmutableArray TypeArgumentsWithAnnotationsNoUseSiteDiagnostics { get { return GetTypeParametersAsTypeArguments(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index 286394b84209e..c786c186d65ce 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -578,6 +578,8 @@ internal override bool MangleName get { return _originalDefinition.MangleName; } } + internal override SyntaxTree? AssociatedSyntaxTree => _originalDefinition.AssociatedSyntaxTree; + internal override DiagnosticInfo? ErrorInfo { get { return _originalDefinition.ErrorInfo; } diff --git a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs index 08ce38645e2c7..1f141ab0f5a38 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs @@ -145,6 +145,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + public override Symbol? ContainingSymbol { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 10850e7a37030..11b8ccaf7c630 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -376,6 +376,8 @@ internal abstract override bool MangleName get; } + internal override SyntaxTree AssociatedSyntaxTree => null; + internal abstract int MetadataArity { get; diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs index 2dfb36efb2f81..f81ebf3f77af2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs @@ -47,6 +47,9 @@ internal override bool MangleName return mangleName; } } + + internal override SyntaxTree? AssociatedSyntaxTree => null; + /// /// Get the arity of the missing type. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index d3543d2c098d1..ba0392ec81bbb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -479,6 +479,7 @@ public virtual bool IsImplicitClass /// public abstract override string Name { get; } +#nullable enable /// /// Return the name including the metadata arity suffix. /// @@ -486,14 +487,30 @@ public override string MetadataName { get { - return MangleName ? MetadataHelpers.ComposeAritySuffixedMetadataName(Name, Arity) : Name; + var fileIdentifier = this.AssociatedFileIdentifier(); + // If we have a fileIdentifier, the type will definitely use CLS arity encoding for nonzero arity. + Debug.Assert(!(fileIdentifier != null && !MangleName && Arity > 0)); + return fileIdentifier != null || MangleName + ? MetadataHelpers.ComposeAritySuffixedMetadataName(Name, Arity, fileIdentifier) + : Name; } } + /// + /// If this type is a file type, returns the syntax tree where this type is visible. Otherwise, returns null. + /// + internal abstract SyntaxTree? AssociatedSyntaxTree { get; } +#nullable disable + /// /// Should the name returned by Name property be mangled with [`arity] suffix in order to get metadata name. /// Must return False for a type with Arity == 0. /// + /// + /// Some types with Arity > 0 still have MangleName == false. For example, EENamedTypeSymbol. + /// Note that other differences between source names and metadata names exist and are not controlled by this property, + /// such as the 'AssociatedFileIdentifier' prefix for file types. + /// internal abstract bool MangleName { // Intentionally no default implementation to force consideration of appropriate implementation for each new subclass diff --git a/src/Compilers/CSharp/Portable/Symbols/NamespaceOrTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamespaceOrTypeSymbol.cs index c6f9cfe5caaac..5271e6e4a4ec2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamespaceOrTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamespaceOrTypeSymbol.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -243,6 +246,7 @@ internal virtual NamedTypeSymbol LookupMetadataType(ref MetadataTypeName emitted Debug.Assert(!emittedTypeName.IsNull); NamespaceOrTypeSymbol scope = this; + Debug.Assert(scope is not MergedNamespaceSymbol); if (scope.Kind == SymbolKind.ErrorType) { @@ -326,6 +330,35 @@ internal virtual NamedTypeSymbol LookupMetadataType(ref MetadataTypeName emitted } Done: + if (isTopLevel + && scope is not PENamespaceSymbol + && (emittedTypeName.ForcedArity == -1 || emittedTypeName.ForcedArity == emittedTypeName.InferredArity) + && GeneratedNameParser.TryParseFileTypeName( + emittedTypeName.UnmangledTypeName, + out string? displayFileName, + out int ordinal, + out string? sourceName)) + { + // also do a lookup for file types from source. + namespaceOrTypeMembers = scope.GetTypeMembers(sourceName); + foreach (var named in namespaceOrTypeMembers) + { + if (named.AssociatedSyntaxTree is SyntaxTree tree + && getDisplayName(tree) == displayFileName + && named.DeclaringCompilation.GetSyntaxTreeOrdinal(tree) == ordinal + && named.Arity == emittedTypeName.InferredArity) + { + if ((object?)namedType != null) + { + namedType = null; + break; + } + + namedType = named; + } + } + } + if ((object?)namedType == null) { if (isTopLevel) @@ -339,6 +372,13 @@ internal virtual NamedTypeSymbol LookupMetadataType(ref MetadataTypeName emitted } return namedType; + + static string getDisplayName(SyntaxTree tree) + { + var sb = PooledStringBuilder.GetInstance(); + GeneratedNames.AppendFileName(tree.FilePath, sb); + return sb.ToStringAndFree(); + } } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/NoPiaAmbiguousCanonicalTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NoPiaAmbiguousCanonicalTypeSymbol.cs index 0b578b6715240..a1b600de31315 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NoPiaAmbiguousCanonicalTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NoPiaAmbiguousCanonicalTypeSymbol.cs @@ -49,6 +49,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + public AssemblySymbol EmbeddingAssembly { get diff --git a/src/Compilers/CSharp/Portable/Symbols/NoPiaIllegalGenericInstantiationSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NoPiaIllegalGenericInstantiationSymbol.cs index f8247bddef230..6161835bfa505 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NoPiaIllegalGenericInstantiationSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NoPiaIllegalGenericInstantiationSymbol.cs @@ -41,6 +41,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + public NamedTypeSymbol UnderlyingSymbol { get diff --git a/src/Compilers/CSharp/Portable/Symbols/NoPiaMissingCanonicalTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NoPiaMissingCanonicalTypeSymbol.cs index c50c29cbe0fad..2b31203cb7a28 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NoPiaMissingCanonicalTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NoPiaMissingCanonicalTypeSymbol.cs @@ -73,6 +73,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + public string Guid { get diff --git a/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs index 3c8eb1aa884d2..3178fe12f7762 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs @@ -48,6 +48,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + internal override DiagnosticInfo? ErrorInfo { get diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs index f5173fe3d8665..6f09cef0e4714 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs @@ -194,6 +194,8 @@ INamedTypeSymbol INamedTypeSymbol.TupleUnderlyingType bool INamedTypeSymbol.IsSerializable => UnderlyingNamedTypeSymbol.IsSerializable; + bool INamedTypeSymbol.IsFile => UnderlyingNamedTypeSymbol.AssociatedSyntaxTree is not null; + INamedTypeSymbol INamedTypeSymbol.NativeIntegerUnderlyingType => UnderlyingNamedTypeSymbol.NativeIntegerUnderlyingType.GetPublicSymbol(); #region ISymbol Members diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index 169f4b8b1da7a..86180610ecd95 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -103,6 +103,11 @@ internal static DeclarationModifiers CheckModifiers( modifierErrors |= checkFeature(DeclarationModifiers.PrivateProtected, MessageID.IDS_FeaturePrivateProtected) | checkFeature(DeclarationModifiers.Required, MessageID.IDS_FeatureRequiredMembers); + if ((result & DeclarationModifiers.File) != 0) + { + modifierErrors |= !Binder.CheckFeatureAvailability(errorLocation.SourceTree, MessageID.IDS_FeatureFileTypes, diagnostics, errorLocation); + } + return result; bool checkFeature(DeclarationModifiers modifier, MessageID featureID) @@ -316,6 +321,8 @@ internal static string ConvertSingleModifierToSyntaxText(DeclarationModifiers mo return SyntaxFacts.GetText(SyntaxKind.RequiredKeyword); case DeclarationModifiers.Scoped: return SyntaxFacts.GetText(SyntaxKind.ScopedKeyword); + case DeclarationModifiers.File: + return SyntaxFacts.GetText(SyntaxKind.FileKeyword); default: throw ExceptionUtilities.UnexpectedValue(modifier); } @@ -367,6 +374,8 @@ private static DeclarationModifiers ToDeclarationModifier(SyntaxKind kind) return DeclarationModifiers.Required; case SyntaxKind.ScopedKeyword: return DeclarationModifiers.Scoped; + case SyntaxKind.FileKeyword: + return DeclarationModifiers.File; default: throw ExceptionUtilities.UnexpectedValue(kind); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index 56a79864e1e76..49d06f3996bf4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -73,6 +73,7 @@ protected sealed override void MethodChecks(BindingDiagnosticBag diagnostics) } this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); + this.CheckFileTypeUsage(_lazyReturnType, _lazyParameters, diagnostics); if (_lazyIsVararg && (IsGenericMethod || ContainingType.IsGenericType || _lazyParameters.Length > 0 && _lazyParameters[_lazyParameters.Length - 1].IsParams)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index f229cde04f7a5..987d3a7e93160 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -96,6 +96,12 @@ internal static void AddDelegateMembers( diagnostics.Add(ErrorCode.ERR_BadVisDelegateReturn, delegateType.Locations[0], delegateType, invoke.ReturnType); } + var delegateTypeIsFile = delegateType.IsFileTypeOrUsesFileTypes(); + if (!delegateTypeIsFile && invoke.ReturnType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, delegateType.Locations[0], invoke.ReturnType, delegateType); + } + for (int i = 0; i < invoke.Parameters.Length; i++) { var parameterSymbol = invoke.Parameters[i]; @@ -104,6 +110,10 @@ internal static void AddDelegateMembers( // Inconsistent accessibility: parameter type '{1}' is less accessible than delegate '{0}' diagnostics.Add(ErrorCode.ERR_BadVisDelegateParam, delegateType.Locations[0], delegateType, parameterSymbol.Type); } + else if (!delegateTypeIsFile && parameterSymbol.Type.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, delegateType.Locations[0], parameterSymbol.Type, delegateType); + } } diagnostics.Add(delegateType.Locations[0], useSiteInfo); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 6652539dc7891..eb9c4c3be96b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -274,7 +274,9 @@ private DeclarationModifiers MakeModifiers(TypeKind typeKind, BindingDiagnosticB { Symbol containingSymbol = this.ContainingSymbol; DeclarationModifiers defaultAccess; - var allowedModifiers = DeclarationModifiers.AccessibilityMask; + + // note: we give a specific diagnostic when a file type is nested + var allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.File; if (containingSymbol.Kind == SymbolKind.Namespace) { @@ -414,6 +416,10 @@ private DeclarationModifiers MakeAndCheckTypeModifiers( { result |= defaultAccess; } + else if ((result & DeclarationModifiers.File) != 0) + { + diagnostics.Add(ErrorCode.ERR_FileTypeNoExplicitAccessibility, Locations[0], this); + } if (missingPartial) { @@ -471,7 +477,8 @@ internal static void ReportReservedTypeName(string? name, CSharpCompilation comp } if (reportIfContextual(SyntaxKind.RecordKeyword, MessageID.IDS_FeatureRecords, ErrorCode.WRN_RecordNamedDisallowed) - || reportIfContextual(SyntaxKind.RequiredKeyword, MessageID.IDS_FeatureRequiredMembers, ErrorCode.ERR_RequiredNameDisallowed)) + || reportIfContextual(SyntaxKind.RequiredKeyword, MessageID.IDS_FeatureRequiredMembers, ErrorCode.ERR_RequiredNameDisallowed) + || reportIfContextual(SyntaxKind.FileKeyword, MessageID.IDS_FeatureFileTypes, ErrorCode.ERR_FileTypeNameDisallowed)) { return; } @@ -819,6 +826,10 @@ internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo HasFlag(DeclarationModifiers.New); + internal bool IsFile => HasFlag(DeclarationModifiers.File); + + internal sealed override SyntaxTree? AssociatedSyntaxTree => IsFile ? declaration.Declarations[0].Location.SourceTree : null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool HasFlag(DeclarationModifiers flag) => (_declModifiers & flag) != 0; @@ -1262,7 +1273,7 @@ private Dictionary> GetTypeMembersDictio private Dictionary> MakeTypeMembers(BindingDiagnosticBag diagnostics) { var symbols = ArrayBuilder.GetInstance(); - var conflictDict = new Dictionary<(string, int), SourceNamedTypeSymbol>(); + var conflictDict = new Dictionary<(string name, int arity, SyntaxTree? syntaxTree), SourceNamedTypeSymbol>(); try { foreach (var childDeclaration in declaration.Children) @@ -1270,7 +1281,7 @@ private Dictionary> MakeTypeMembers(Bind var t = new SourceNamedTypeSymbol(this, childDeclaration, diagnostics); this.CheckMemberNameDistinctFromType(t, diagnostics); - var key = (t.Name, t.Arity); + var key = (t.Name, t.Arity, t.AssociatedSyntaxTree); SourceNamedTypeSymbol? other; if (conflictDict.TryGetValue(key, out other)) { @@ -1733,6 +1744,11 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) } } + if (IsFile && (object?)ContainingType != null) + { + diagnostics.Add(ErrorCode.ERR_FileTypeNested, location, this); + } + return; bool hasBaseTypeOrInterface(Func predicate) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 0148108694fce..791b88c38b64d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -46,7 +46,11 @@ protected sealed override DeclarationModifiers Modifiers protected void TypeChecks(TypeSymbol type, BindingDiagnosticBag diagnostics) { - if (type.IsStatic) + if (type.IsFileTypeOrUsesFileTypes() && !ContainingType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, this.ErrorLocation, type, ContainingType); + } + else if (type.IsStatic) { // Cannot declare a variable of static type '{0}' diagnostics.Add(ErrorCode.ERR_VarDeclIsStaticClass, this.ErrorLocation, type); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 6d84ea23d1b5b..225c383d4f001 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -278,6 +278,27 @@ protected void CheckEffectiveAccessibility(TypeWithAnnotations returnType, Immut diagnostics.Add(Locations[0], useSiteInfo); } + protected void CheckFileTypeUsage(TypeWithAnnotations returnType, ImmutableArray parameters, BindingDiagnosticBag diagnostics) + { + if (ContainingType.IsFileTypeOrUsesFileTypes()) + { + return; + } + + if (returnType.Type.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Locations[0], returnType.Type, ContainingType); + } + + foreach (var param in parameters) + { + if (param.Type.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Locations[0], param.Type, ContainingType); + } + } + } + protected void MakeFlags( MethodKind methodKind, DeclarationModifiers declarationModifiers, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs index 2f98d0400d534..93fe92389fec6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs @@ -384,6 +384,11 @@ static bool containsOnlyOblivious(TypeSymbol type) // Inconsistent accessibility: base class '{1}' is less accessible than class '{0}' diagnostics.Add(ErrorCode.ERR_BadVisBaseClass, baseTypeLocation, this, baseType); } + + if (baseType.IsFileTypeOrUsesFileTypes() && !this.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeBase, baseTypeLocation, baseType, this); + } } var baseInterfacesRO = baseInterfaces.ToImmutableAndFree(); @@ -396,6 +401,11 @@ static bool containsOnlyOblivious(TypeSymbol type) // Inconsistent accessibility: base interface '{1}' is less accessible than interface '{0}' diagnostics.Add(ErrorCode.ERR_BadVisBaseInterface, interfaceLocations[i], this, i); } + + if (i.IsFileTypeOrUsesFileTypes() && !this.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeBase, interfaceLocations[i], i, this); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs index a3d202bebc10e..b1098e3ea273f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs @@ -757,6 +757,11 @@ UsingsAndDiagnostics buildUsings( else { var importedType = (NamedTypeSymbol)imported; + if (usingDirective.GlobalKeyword != default(SyntaxToken) && importedType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_GlobalUsingStaticFileType, usingDirective.Name.Location, imported); + } + if (!getOrCreateUniqueUsings(ref uniqueUsings, globalUsingNamespacesOrTypes).Add(importedType)) { diagnostics.Add(!globalUsingNamespacesOrTypes.IsEmpty && getOrCreateUniqueGlobalUsingsNotInTree(ref uniqueGlobalUsings, globalUsingNamespacesOrTypes, declarationSyntax.SyntaxTree).Contains(imported) ? diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs index a1b1c4e391b10..5bba097c91837 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs @@ -372,13 +372,17 @@ private static void CheckMembers(NamespaceSymbol @namespace, Dictionary (object)loc.SourceTree == leftTree, leftTree)) + { + return false; + } + + return true; + } } private NamespaceOrTypeSymbol BuildSymbol(MergedNamespaceOrTypeDeclaration declaration, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs index 5b541f2af2fe0..c952b7a10b125 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -46,6 +46,7 @@ public sealed override bool ReturnsVoid this.SetReturnsVoid(_lazyReturnType.IsVoidType()); this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); + this.CheckFileTypeUsage(_lazyReturnType, _lazyParameters, diagnostics); var location = locations[0]; // Checks taken from MemberDefiner::defineMethod diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 4f18ea1b33a84..781b6bac6a5c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -451,6 +451,11 @@ private TypeWithAnnotations ComputeType(Binder binder, SyntaxNode syntax, Bindin diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type); } + if (type.Type.IsFileTypeOrUsesFileTypes() && !ContainingType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, type.Type, ContainingType); + } + diagnostics.Add(Location, useSiteInfo); if (type.IsVoidType()) @@ -528,6 +533,10 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type); } + else if (param.Type.IsFileTypeOrUsesFileTypes() && !this.ContainingType.IsFileTypeOrUsesFileTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, param.Type, this.ContainingType); + } else if (SetMethod is object && param.Name == ParameterSymbol.ValueParameterName) { diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.Locations.FirstOrDefault() ?? Location, param.Name); diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 4f1a4d2545fea..a589243a8359f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -873,7 +873,7 @@ public virtual string GetDocumentationCommentXml( SymbolDisplayFormat.TestFormat .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier | SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier) - .WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.None); + .WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes); internal virtual string GetDebuggerDisplay() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs index 66a294e654bad..1404bc8a80496 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs @@ -30,6 +30,7 @@ internal enum GeneratedNameKind ReusableHoistedLocalField = '7', LambdaCacheField = '9', FixedBufferField = 'e', + FileType = 'F', AnonymousType = 'f', TransparentIdentifier = 'h', AnonymousTypeField = 'i', diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameParser.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameParser.cs index b9d6d4b44fa23..55f6d2b85c572 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameParser.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameParser.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Text.RegularExpressions; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -170,5 +171,35 @@ internal static bool TryParseAnonymousTypeParameterName(string typeParameterName propertyName = null; return false; } + + // A full metadata name for a generic file type looks like: + // FN__ClassName`A + // where 'N' is the syntax tree ordinal, 'A' is the arity, + // and 'ClassName' is the source name of the type. + // + // The "unmangled" name of a generic file type looks like: + // FN__ClassName + private static readonly Regex s_fileTypeOrdinalPattern = new Regex(@"<([a-zA-Z_0-9]*)>F(\d)+__", RegexOptions.Compiled); + + /// + /// This method will work with either unmangled or mangled type names as input, but it does not remove any arity suffix if present. + /// + internal static bool TryParseFileTypeName(string generatedName, [NotNullWhen(true)] out string? displayFileName, out int ordinal, [NotNullWhen(true)] out string? originalTypeName) + { + if (s_fileTypeOrdinalPattern.Match(generatedName) is Match { Success: true, Groups: var groups, Index: var index, Length: var length } + && int.TryParse(groups[2].Value, out ordinal)) + { + displayFileName = groups[1].Value; + + var prefixEndsAt = index + length; + originalTypeName = generatedName.Substring(prefixEndsAt); + return true; + } + + ordinal = -1; + displayFileName = null; + originalTypeName = null; + return false; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 18dfeff343fe8..2466b3268be33 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -494,5 +494,38 @@ internal static string LambdaCopyParameterName(int ordinal) { return ""; } + + internal static string MakeFileIdentifier(string filePath, int ordinal) + { + var pooledBuilder = PooledStringBuilder.GetInstance(); + var sb = pooledBuilder.Builder; + sb.Append('<'); + AppendFileName(filePath, sb); + sb.Append('>'); + sb.Append((char)GeneratedNameKind.FileType); + sb.Append(ordinal); + sb.Append("__"); + return pooledBuilder.ToStringAndFree(); + } + + internal static void AppendFileName(string? filePath, StringBuilder sb) + { + var fileName = FileNameUtilities.GetFileName(filePath, includeExtension: false); + if (fileName is null) + { + return; + } + + foreach (var ch in fileName) + { + sb.Append(ch switch + { + >= 'a' and <= 'z' => ch, + >= 'A' and <= 'Z' => ch, + >= '0' and <= '9' => ch, + _ => '_' + }); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index dd677239e1078..12ec75a9c91ca 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -165,6 +165,10 @@ internal override IEnumerable GetFieldsToEmit() internal override bool MangleName => Arity > 0; +#nullable enable + internal sealed override SyntaxTree? AssociatedSyntaxTree => null; +#nullable disable + public override bool IsImplicitlyDeclared => true; internal override bool ShouldAddWinRTMembers => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index 72a47ac9eccea..9e7e9370e689c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -99,6 +99,8 @@ public SynthesizedEmbeddedAttributeSymbolBase( internal override bool MangleName => false; + internal override SyntaxTree AssociatedSyntaxTree => null; + internal override bool HasCodeAnalysisEmbeddedAttribute => true; internal override bool IsInterpolatedStringHandlerType => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 70fb7b7600ebd..69d0a214d5b41 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -453,7 +453,7 @@ internal override BoundBlock CreateBody(BindingDiagnosticBag diagnostics) // Creates a new top-level binder that just contains the global imports for the compilation. // The imports are required if a consumer of the scripting API is using a Task implementation // that uses extension methods. - Binder binder = WithUsingNamespacesAndTypesBinder.Create(compilation.GlobalImports, next: new BuckStopsHereBinder(compilation), withImportChainEntry: true); + Binder binder = WithUsingNamespacesAndTypesBinder.Create(compilation.GlobalImports, next: new BuckStopsHereBinder(compilation, null), withImportChainEntry: true); binder = new InContainerBinder(compilation.GlobalNamespace, binder); var ctor = _containingType.GetScriptConstructor(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index 1dda4db66ed28..50b6d3fc9634c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -219,10 +219,10 @@ private ExecutableCodeBinder CreateBodyBinder(bool ignoreAccessibility) { CSharpCompilation compilation = DeclaringCompilation; - Binder result = new BuckStopsHereBinder(compilation); + var syntaxNode = SyntaxNode; + Binder result = new BuckStopsHereBinder(compilation, syntaxNode.SyntaxTree); var globalNamespace = compilation.GlobalNamespace; var declaringSymbol = (SourceNamespaceSymbol)compilation.SourceModule.GlobalNamespace; - var syntaxNode = SyntaxNode; result = WithExternAndUsingAliasesBinder.Create(declaringSymbol, syntaxNode, WithUsingNamespacesAndTypesBinder.Create(declaringSymbol, syntaxNode, result)); result = new InContainerBinder(globalNamespace, result); result = new InContainerBinder(ContainingType, result); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 2f4f42572a34c..c754ed3038577 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Text; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -1360,6 +1361,22 @@ public static bool IsPartial(this TypeSymbol type) return type is SourceNamedTypeSymbol { IsPartial: true }; } + public static bool IsFileTypeOrUsesFileTypes(this TypeSymbol type) + { + var foundType = type.VisitType(predicate: (type, _, _) => type is SourceMemberContainerTypeSymbol { IsFile: true }, arg: (object?)null); + return foundType is not null; + } + + internal static string? AssociatedFileIdentifier(this NamedTypeSymbol type) + { + if (type.AssociatedSyntaxTree is not SyntaxTree tree) + { + return null; + } + var ordinal = type.DeclaringCompilation.GetSyntaxTreeOrdinal(tree); + return GeneratedNames.MakeFileIdentifier(tree.FilePath, ordinal); + } + public static bool IsPointerType(this TypeSymbol type) { return type is PointerTypeSymbol; diff --git a/src/Compilers/CSharp/Portable/Symbols/UnboundGenericType.cs b/src/Compilers/CSharp/Portable/Symbols/UnboundGenericType.cs index 30e4a67d87744..1ee9dbccbb271 100644 --- a/src/Compilers/CSharp/Portable/Symbols/UnboundGenericType.cs +++ b/src/Compilers/CSharp/Portable/Symbols/UnboundGenericType.cs @@ -91,6 +91,8 @@ internal override bool MangleName } } + internal override SyntaxTree? AssociatedSyntaxTree => null; + internal override DiagnosticInfo ErrorInfo { get diff --git a/src/Compilers/CSharp/Portable/Symbols/UnsupportedMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/UnsupportedMetadataTypeSymbol.cs index e4ca579dc1b86..80b81912ae070 100644 --- a/src/Compilers/CSharp/Portable/Symbols/UnsupportedMetadataTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/UnsupportedMetadataTypeSymbol.cs @@ -37,5 +37,7 @@ internal override bool MangleName return false; } } + + internal override SyntaxTree? AssociatedSyntaxTree => null; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs index 48a0389a946be..aa26a0c66f5c8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs @@ -95,6 +95,8 @@ internal override bool MangleName } } + internal override SyntaxTree AssociatedSyntaxTree => _underlyingType.AssociatedSyntaxTree; + public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { return _underlyingType.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken); diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 99d3d48bf3055..8b7d6a6ffce69 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -408,6 +408,8 @@ public enum SyntaxKind : ushort RequiredKeyword = 8447, /// Represents . ScopedKeyword = 8448, + /// Represents . + FileKeyword = 8449, // when adding a contextual keyword following functions must be adapted: // diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index db242c0548924..d6624d597b04b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -1138,7 +1138,7 @@ public static SyntaxKind GetPreprocessorKeywordKind(string text) public static IEnumerable GetContextualKeywordKinds() { - for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.ScopedKeyword; i++) + for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.FileKeyword; i++) { yield return (SyntaxKind)i; } @@ -1193,6 +1193,7 @@ public static bool IsContextualKeyword(SyntaxKind kind) case SyntaxKind.UnmanagedKeyword: case SyntaxKind.RequiredKeyword: case SyntaxKind.ScopedKeyword: + case SyntaxKind.FileKeyword: return true; default: return false; @@ -1316,6 +1317,8 @@ public static SyntaxKind GetContextualKeywordKind(string text) return SyntaxKind.RequiredKeyword; case "scoped": return SyntaxKind.ScopedKeyword; + case "file": + return SyntaxKind.FileKeyword; default: return SyntaxKind.None; } @@ -1759,6 +1762,8 @@ public static string GetText(SyntaxKind kind) return "required"; case SyntaxKind.ScopedKeyword: return "scoped"; + case SyntaxKind.FileKeyword: + return "file"; default: return string.Empty; } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 764cfc8702fbb..5e39ad0819175 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -582,6 +582,31 @@ Obor názvů pro celý soubor musí předcházet všem ostatním členům v souboru. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Globální direktiva using musí předcházet všem direktivám using, které nejsou globální. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Příkaz goto nemůže přejít na místo před deklarací using ve stejném bloku. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + Levá strana přiřazení odkazu musí být lokální proměnná nebo parametr odkazu. @@ -1602,6 +1632,11 @@ obor názvů pro celý soubor + + file types + file types + + generic attributes obecné atributy @@ -1719,7 +1754,7 @@ pattern matching ReadOnly/Span<char> on constant string - vzor odpovídající ReadOnly/Span<char> na konstantním řetězci + pattern matching ReadOnly/Span<char> on constant string @@ -2250,7 +2285,7 @@ -instrument:TestCoverage Vytvoří sestavení instrumentované ke shromažďování informací o pokrytí. -sourcelink:<file> Informace o zdrojovém odkazu vkládané do souboru PDB.. - + - CHYBY A UPOZORNĚNÍ - -warnaserror[+|-] Hlásí všechna upozornění jako chyby. -warnaserror[+|-]:<warn list> Hlásí zadaná upozornění jako chyby. @@ -6552,7 +6587,7 @@ Pokud se taková třída používá jako základní třída a pokud odvozující Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Nejde vyhledávat člena v {0}, protože se jedná o parametr typu. @@ -11099,7 +11134,7 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Parametr nejde vrátit pomocí odkazu {0}, protože nejde o parametr Ref nebo Out. @@ -11719,7 +11754,7 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Operátory převodu, rovnosti nebo nerovnosti deklarované v rozhraních musí být abstraktní. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 79eb2afa300c9..d85bca7010f4c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -582,6 +582,31 @@ Der Dateibereichsnamespace muss allen anderen Elementen in einer Datei vorangestellt sein. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Eine globale using-Anweisung muss allen nicht globalen using-Anweisungen vorangehen. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Mit "goto" kann nicht an eine Position vor einer using-Deklaration im selben Block gesprungen werden. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + Die linke Seite einer ref-Zuweisung muss ein lokaler Verweis oder ein Parameter sein. @@ -1602,6 +1632,11 @@ Dateibereichsnamespace + + file types + file types + + generic attributes Generische Attribute @@ -2253,7 +2288,7 @@ – FEHLER UND WARNUNGEN – -warnaserror[+|-] Meldet alle Warnungen als Fehler. --warnaserror[+|-]:<Warnungsliste> Meldet bestimmte Warnungen als Fehler +-warnaserror[+|-]:<Warnungsliste> Meldet bestimmte Warnungen als Fehler (Verwendung von "nullable" für alle Warnungen zur NULL-Zulässigkeit). -warn:<n> Legt die Warnstufe fest (0 oder höher) (Kurzform: -w). -nowarn:<Warnungsliste> Deaktiviert bestimmte Warnmeldungen @@ -6552,7 +6587,7 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + In "{0}" kann kein Memberlookup ausgeführt werden, da es sich um einen Typparameter handelt. @@ -11099,7 +11134,7 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Ein Parameter kann nicht als Verweis "{0}" zurückgegeben werden, weil es sich nicht um einen ref- oder out-Parameter handelt. @@ -11719,7 +11754,7 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Konvertierungs-, Gleichheits- oder Ungleichheitsoperatoren, die in Schnittstellen deklariert sind, müssen abstrakt sein. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index f72e80aed08d9..5bda9f5c4481f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -582,6 +582,31 @@ El espacio de nombres con ámbito de archivo debe preceder a todos los demás miembros de un archivo. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Una directiva de uso global debe ser anterior a todas las directivas no globales que no son de uso. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Una instrucción goto no puede saltar a una ubicación antes que una declaración using dentro del mismo bloque. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + La parte izquierda de una asignación de referencias debe ser una referencia local o un parámetro. @@ -1602,6 +1632,11 @@ espacio de nombres con ámbito de archivo + + file types + file types + + generic attributes atributos genéricos @@ -2188,7 +2223,7 @@ Opciones del compilador de Visual C# - ARCHIVOS DE SALIDA - --out:<archivo> Especifica el nombre del archivo de salida (el valor predeterminado: nombre base del +-out:<archivo> Especifica el nombre del archivo de salida (el valor predeterminado: nombre base del archivo con la clase principal o el primer archivo) -target:exe Compila un archivo ejecutable de consola (predeterminado) (forma corta: -t:exe) @@ -2250,7 +2285,7 @@ -instrument:TestCoverage Produce un ensamblado instrumentado para recopilar información de cobertura. -sourcelink:<archivo> Información del vínculo de origen para insertar en el PDB. - + - ERRORES Y ADVERTENCIAS - -warnaserror[+|-] Notifica todas las advertencias como errores. -warnaserror[+|-]:<lista de advertencias > Notifica advertencias específicas como errores @@ -2322,7 +2357,7 @@ -pdb:<archivo> Especifica el nombre de archivo de información de depuración (valor predeterminado: nombre de archivo de salida con la extensión .pdb). -errorendlocation Línea y columna de salida de la ubicación final de - cada error. + cada error. -preferreduilang Especifica el nombre del lenguaje de salida preferido. -nosdkpath Deshabilita la búsqueda de la ruta del SDK predeterminada para los ensamblados de biblioteca estándar. -nostdlib[+|-] No hace referencia a la biblioteca estándar (mscorlib.dll). @@ -6552,7 +6587,7 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + No se pueden buscar miembros en '{0}' porque es un parámetro de tipo @@ -11099,7 +11134,7 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + No se pude devolver por referencia un parámetro '{0}' porque no es de tipo ref o out. @@ -11719,7 +11754,7 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Los operadores de conversión, igualdad o desigualdad declarados en interfaces deben ser abstractos diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 8f4a217d4e23c..6d8f3c6681898 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -582,6 +582,31 @@ Un espace de noms de portée de fichier doit précéder tous les autres membres d’un fichier. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Une directive using globale doit précéder toutes les directives using non globales. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Un goto ne peut pas accéder à un emplacement avant une déclaration using dans le même bloc. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + La partie gauche d'une assignation par référence doit être une variable locale ou un paramètre ref. @@ -1602,6 +1632,11 @@ espace de noms inclus dans l'étendue de fichier + + file types + file types + + generic attributes attributs génériques @@ -2250,7 +2285,7 @@ -instrument:TestCoverage Produire un assembly instrumenté pour collecter les informations de couverture -sourcelink:<fichier> Informations du lien source à incorporer dans le fichier PDB. - + - ERREURS ET AVERTISSEMENTS - -warnaserror[+|-] Signaler tous les avertissements comme des erreurs -warnaserror[+|-]:<avertiss.> Signaler des avertissements spécifiques comme des erreurs @@ -6552,7 +6587,7 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Impossible de rechercher un membre dans '{0}', car il s'agit d'un paramètre de type @@ -11099,7 +11134,7 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Impossible de retourner un paramètre '{0}' par référence, car il ne s'agit pas d'un paramètre ref ou out @@ -11719,7 +11754,7 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Les opérateurs de conversion, d’égalité ou d’inégalité déclarés dans les interfaces doivent être abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 06b82ec51a64e..ca711e6b4ba31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -582,6 +582,31 @@ Lo spazio dei nomi con ambito file deve precedere tutti gli altri membri di un file. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Una direttiva sull'utilizzo globale deve precedere tutte le direttiva non sull'uso non globale. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Un'istruzione goto non può passare a una posizione che precede una dichiarazione using all'interno dello stesso blocco. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + La parte sinistra di un'assegnazione ref deve essere un parametro o una variabile locale ref. @@ -1602,6 +1632,11 @@ spazio dei nomi con ambito file + + file types + file types + + generic attributes attributi generici @@ -2213,7 +2248,7 @@ ai caratteri jolly specificati -reference:<alias>=<file> Crea un riferimento ai metadati dal file di assembly specificato usando l'alias indicato. Forma breve: -r --reference:<elenco file> Crea un riferimento ai metadati dai file di assembly +-reference:<elenco file> Crea un riferimento ai metadati dai file di assembly specificati. Forma breve: -r -addmodule:<elenco file> Collega i moduli specificati in questo assembly -link:<elenco file> Incorpora metadati dai file di assembly di @@ -2250,7 +2285,7 @@ -instrument:TestCoverage Produce un assembly instrumentato per raccogliere informazioni sul code coverage -sourcelink:<file> Informazioni sul collegamento all'origine da incorporare nel file PDB. - + - ERRORI E AVVISI - -warnaserror[+|-] Segnala tutti gli avvisi come errori -warnaserror[+|-]:<elenco avvisi> Segnala determinati avvisi come errori @@ -2304,7 +2339,7 @@ - AVANZATE - -baseaddress:<indirizzo> Indirizzo di base della libreria da compilare --checksumalgorithm:<alg> Consente di specificare l'algoritmo per calcolare il checksum +-checksumalgorithm:<alg> Consente di specificare l'algoritmo per calcolare il checksum del file di origine archiviato nel file PDB. I valori supportati sono: SHA1 o SHA256 (impostazione predefinita). -codepage:<n> Consente di specificare la tabella codici da usare all'apertura dei file @@ -6552,7 +6587,7 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Non è possibile eseguire la ricerca di membri in '{0}' perché è un parametro di tipo @@ -11099,7 +11134,7 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Non è possibile restituire un parametro '{0}' per riferimento perché non è un parametro out o ref @@ -11719,7 +11754,7 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Gli operatori di conversione, uguaglianza o disuguaglianza dichiarati nelle interfacce devono essere astratti diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index da1d58c2872f8..5b05461c673de 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -582,6 +582,31 @@ ファイルスコープの名前空間は、ファイル内の他のすべてのメンバーの前に指定する必要があります。 + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ グローバル using ディレクティブは、すべての非グローバル using ディレクティブの前に指定する必要があります。 + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. goto は同じブロック内の using 宣言より前の位置にはジャンプできません。 @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + ref 代入の左辺は、ref ローカルまたはパラメーターにする必要があります。 @@ -1602,6 +1632,11 @@ ファイルスコープの名前空間 + + file types + file types + + generic attributes 汎用属性 @@ -2250,7 +2285,7 @@ -instrument:TestCoverage カバレッジ情報を収集するようにインストルメント化された アセンブリを生成します -sourcelink:<file> PDB に埋め込むソース リンク情報。 - + - エラーと警告 - -warnaserror[+|-] すべての警告をエラーとして報告します -warnaserror[+|-]:<warn list> 特定の警告をエラーとして報告します @@ -6552,7 +6587,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + 型パラメーターであるため、'{0}' でメンバーの照合を行えません @@ -11099,7 +11134,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + ref パラメーターでも out パラメーターでもないため、パラメーターを参照 '{0}' 渡しで返すことはできません @@ -11719,7 +11754,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + インターフェイスで宣言された変換演算子、等値演算子、または非等値演算子は抽象的である必要があります diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 421e5bbe87ea8..dfa5c51e9556a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -582,6 +582,31 @@ 파일 범위 네임스페이스는 파일의 다른 모든 멤버보다 앞에 와야 합니다. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ 전역 using 지시문은 전역이 아닌 모든 using 지시문 앞에 있어야 합니다. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. goto는 동일한 블록 내의 using 선언 앞 위치로 이동할 수 없습니다. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다. @@ -1602,6 +1632,11 @@ 파일 범위 네임스페이스 + + file types + file types + + generic attributes 제네릭 특성 @@ -2196,7 +2231,7 @@ -target:library 라이브러리를 빌드합니다. (약식: -t:library) -target:module 다른 어셈블리에 추가할 수 있는 모듈을 빌드합니다. (약식: -t:module) --target:appcontainerexe Appcontainer 실행 파일을 빌드합니다. (약식: +-target:appcontainerexe Appcontainer 실행 파일을 빌드합니다. (약식: -t:appcontainerexe) -target:winmdobj WinMDExp에서 사용되는 Windows 런타임 중간 파일을 빌드합니다. (약식: -t:winmdobj) @@ -2310,7 +2345,7 @@ 지정합니다. -utf8output 컴파일러 메시지를 UTF-8 인코딩으로 출력합니다. -main:<type> 진입점이 포함된 형식을 지정합니다(다른 - 모든 가능한 진입점 무시). + 모든 가능한 진입점 무시). (약식: -m) -fullpaths 컴파일러가 정규화된 경로를 생성합니다. -filealign:<n> 출력 파일 섹션에 사용되는 맞춤을 @@ -6551,7 +6586,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + '{0}'은(는) 형식 매개 변수이므로 멤버를 조회할 수 없습니다. @@ -11098,7 +11133,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + '{0}' 매개 변수는 ref 또는 out 매개 변수가 아니므로 참조로 반환할 수 없습니다. @@ -11718,7 +11753,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + 인터페이스에 선언된 변환, 같음 또는 부등식 연산자는 추상이어야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index b973cbb6d8317..0c0d0fefb9c9b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -582,6 +582,31 @@ Przestrzeń nazw z określonym zakresem plików musi poprzedzać wszystkie inne składowe w pliku. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Globalne używające dyrektywy muszą poprzedzać wszystkie nieglobalne używające dyrektywy. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Instrukcja goto nie może przechodzić do lokalizacji występującej przed deklaracją using w tym samym bloku. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + Lewa strona przypisania odwołania musi być odwołaniem lokalnym lub parametrem. @@ -1602,6 +1632,11 @@ przestrzeń nazw z określonym zakresem plików + + file types + file types + + generic attributes atrybuty ogólne @@ -1719,7 +1754,7 @@ pattern matching ReadOnly/Span<char> on constant string - dopasowanie wzorca ReadOnly/Span<char> w ciągu stałym + pattern matching ReadOnly/Span<char> on constant string @@ -2188,35 +2223,35 @@ Opcje kompilatora Visual C# - PLIKI WYJŚCIOWE - --out:<plik> Określ nazwę pliku wyjściowego (domyślnie: nazwa podstawowa +-out:<plik> Określ nazwę pliku wyjściowego (domyślnie: nazwa podstawowa pliku z klasą główną lub pierwszego pliku) --target:exe Kompiluj plik wykonywalny konsoli (domyślnie) (krótka +-target:exe Kompiluj plik wykonywalny konsoli (domyślnie) (krótka wersja: -t:exe) --target:winexe Kompiluj plik wykonywalny systemu Windows (krótka wersja: +-target:winexe Kompiluj plik wykonywalny systemu Windows (krótka wersja: -t:winexe) -target:library Kompiluj bibliotekę (krótka wersja: -t:library) --target:module Kompiluj moduł, który można dodać do innego +-target:module Kompiluj moduł, który można dodać do innego zestawu (krótka wersja: -t:module) --target:appcontainerexe Kompiluj plik wykonywalny kontenera aplikacji (krótka wersja: +-target:appcontainerexe Kompiluj plik wykonywalny kontenera aplikacji (krótka wersja: -t:appcontainerexe) --target:winmdobj Kompiluj plik pośredni środowiska uruchomieniowego systemu Windows +-target:winmdobj Kompiluj plik pośredni środowiska uruchomieniowego systemu Windows przeznaczony dla narzędzia WinMDExp (krótka wersja: -t:winmdobj) -doc:<plik> Plik dokumentacji XML do wygenerowania -refout:<plik> Dane wyjściowe zestawu odwołania do wygenerowania -platform:<ciąg> Ogranicz platformy, na których można uruchamiać ten kod: x86, - Itanium, x64, arm, arm64, anycpu32bitpreferred lub + Itanium, x64, arm, arm64, anycpu32bitpreferred lub anycpu. Wartość domyślna to anycpu. - PLIKI WEJŚCIOWE - --recurse:<symbol wieloznaczny> Uwzględnij wszystkie pliki zawarte w bieżącym katalogu i - podkatalogach zgodnie ze specyfikacją określoną przy użyciu +-recurse:<symbol wieloznaczny> Uwzględnij wszystkie pliki zawarte w bieżącym katalogu i + podkatalogach zgodnie ze specyfikacją określoną przy użyciu symboli wieloznacznych --reference:<alias>=<plik> Odwołuj się do metadanych z określonego pliku +-reference:<alias>=<plik> Odwołuj się do metadanych z określonego pliku zestawu przy użyciu podanego aliasu (krótka wersja: -r) --reference:<lista_plików> Odwołuj się do metadanych z określonych +-reference:<lista_plików> Odwołuj się do metadanych z określonych plików zestawów (krótka wersja: -r) -addmodule:<lista plików> Połącz określone moduły z tym zestawem --link:<lista_plików> Osadź metadane z określonych plików +-link:<lista_plików> Osadź metadane z określonych plików zestawów międzyoperacyjnych (krótka wersja: -l) -analyzer:<lista_plików> Uruchom analizatory z tego zestawu (krótka wersja: -a) @@ -2232,16 +2267,16 @@ -win32manifest:<plik> Określ plik manifestu środowiska Win32 (xml) -nowin32manifest Nie dołączaj domyślnego manifestu środowiska Win32 -resource:<informacje_o_zasobie> Osadź określony zasób (krótka wersja: -res) --linkresource:<informacje_o_zasobie> Połącz określony zasób z tym zestawem - (krótka wersja: -linkres), gdzie format informacji o zasobie +-linkresource:<informacje_o_zasobie> Połącz określony zasób z tym zestawem + (krótka wersja: -linkres), gdzie format informacji o zasobie to <plik>[,<nazwa ciągu>[,public|private]] - GENEROWANIE KODU - -debug[+|-] Emituj informacje o debugowaniu -debug:{full|pdbonly|portable|embedded} - Określ typ debugowania (wartość domyślna to „full”, + Określ typ debugowania (wartość domyślna to „full”, wartość „portable” to format międzyplatformowy, - a wartość „embedded” to format międzyplatformowy wbudowany w + a wartość „embedded” to format międzyplatformowy wbudowany w docelowym pliku dll lub exe) -optimize[+|-] Włącz optymalizacje (krótka wersja: -o) -deterministic Utwórz zestaw deterministyczny @@ -2268,11 +2303,11 @@ -reportanalyzer Zgłaszaj dodatkowe informacje analizatora, takie jak czas wykonywania. -skipanalyzers[+|-] Pomiń wykonywanie analizatorów diagnostycznych. - + -JĘZYK - -checked[+|-] Generuj operacje sprawdzenia przepełnienia -unsafe[+|-] Zezwalaj na niebezpieczny kod --define:<lista symboli> Zdefiniuj symbole kompilacji warunkowej (krótka +-define:<lista symboli> Zdefiniuj symbole kompilacji warunkowej (krótka wersja: -d) -langversion:? Wyświetl dozwolone wartości dla wersji języka -langversion:<ciąg> Określ wersję języka, na przykład @@ -2286,7 +2321,7 @@ Określ opcję kontekstu dopuszczającego wartość null: enable|disable|warnings|annotations. - ZABEZPIECZENIA - --delaysign[+|-] Podpisz z opóźnieniem zestaw, używając tylko +-delaysign[+|-] Podpisz z opóźnieniem zestaw, używając tylko części publicznej klucza o silnej nazwie -publicsign[+|-] Podpisz publicznie zestaw, używając tylko części publicznej klucza o silnej nazwie @@ -2307,34 +2342,34 @@ -checksumalgorithm:<algorytm> Określ algorytm do obliczania sumy kontrolnej pliku źródłowego przechowywanej w pliku PDB. Obsługiwane wartości: SHA1 lub SHA256 (domyślnie). --codepage:<n> Określ stronę kodową do użycia podczas otwierania +-codepage:<n> Określ stronę kodową do użycia podczas otwierania plików źródłowych -utf8output Wyprowadź komunikaty kompilatora przy użyciu kodowania UTF-8 --main:<typ> Określ typ zawierający punkt wejścia - (zignoruj wszystkie pozostałe możliwe punkty wejścia) (krótka +-main:<typ> Określ typ zawierający punkt wejścia + (zignoruj wszystkie pozostałe możliwe punkty wejścia) (krótka wersja: -m) -fullpaths Kompilator generuje w pełni kwalifikowane ścieżki --filealign:<n> Określ wyrównanie stosowane dla sekcji +-filealign:<n> Określ wyrównanie stosowane dla sekcji plików wyjściowych --pathmap:<K1>=<W1>,<K2>=<W2>,... - Określ mapowanie dla nazw ścieżek źródłowych wyprowadzanych przez +-pathmap:<K1>=<W1>,<K2>=<W2>,... + Określ mapowanie dla nazw ścieżek źródłowych wyprowadzanych przez kompilator. --pdb:<plik> Określ nazwę pliku z informacjami o debugowaniu (domyślnie: +-pdb:<plik> Określ nazwę pliku z informacjami o debugowaniu (domyślnie: nazwa pliku wyjściowego z rozszerzeniem pdb) --errorendlocation Wyprowadź wiersz i kolumnę lokalizacji końcowej dla +-errorendlocation Wyprowadź wiersz i kolumnę lokalizacji końcowej dla każdego błędu -preferreduilang Określ nazwę preferowanego języka wyjściowego. -nosdkpath Wyłącz przeszukiwanie domyślnej ścieżki zestawu SDK dla zestawów biblioteki standardowej. -nostdlib[+|-] Nie odwołuj się do biblioteki standardowej (mscorlib.dll) -subsystemversion:<ciąg> Określ wersję podsystemu tego zestawu --lib:<lista plików> Określ dodatkowe katalogi do przeszukania pod kątem +-lib:<lista plików> Określ dodatkowe katalogi do przeszukania pod kątem odwołań --errorreport:<ciąg> Określ, w jaki sposób obsługiwać wewnętrzne błędy kompilatora: - prompt, send, queue lub none. Wartość domyślna to +-errorreport:<ciąg> Określ, w jaki sposób obsługiwać wewnętrzne błędy kompilatora: + prompt, send, queue lub none. Wartość domyślna to queue. --appconfig:<plik> Określ plik konfiguracji aplikacji +-appconfig:<plik> Określ plik konfiguracji aplikacji zawierający ustawienia powiązania zestawu --moduleassemblyname:<ciąg> Nazwa zestawu, którego częścią +-moduleassemblyname:<ciąg> Nazwa zestawu, którego częścią ma być ten moduł -modulename:<ciąg> Określ nazwę modułu źródłowego -generatedfilesout:<katalog> Umieść pliki wygenerowane podczas kompilacji @@ -6552,7 +6587,7 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Nie można wyszukać składowej w elemencie „{0}”, ponieważ to jest parametr typu @@ -11099,7 +11134,7 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Nie można zwrócić parametru „{0}” przez referencję, ponieważ to nie jest parametr ref ani out @@ -11719,7 +11754,7 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Operatory konwersji, równości lub nierówności zadeklarowane w interfejsach muszą być abstrakcyjne diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 781137b12aebf..9bd8242b1e766 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -582,6 +582,31 @@ O namespace de escopo de arquivo deve preceder todos os outros membros em um arquivo. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Uma diretiva de uso global deve preceder todas as diretivas de uso não global. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Um goto não pode saltar para um local antes de uma declaração using no mesmo bloco. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + O lado esquerdo da atribuição ref precisa ser um parâmetro ou local ref. @@ -1602,6 +1632,11 @@ namespace de escopo de arquivo + + file types + file types + + generic attributes atributos genéricos @@ -2213,7 +2248,7 @@ curinga -reference:<alias>=<file> Metadados de referência do arquivo de assembly especificado usando o alias fornecido (Forma abreviada: -r) --reference:<file list> Metadados de referência dos arquivos de assembly +-reference:<file list> Metadados de referência dos arquivos de assembly especificados (Forma abreviada: -r) -addmodule:<file list> Vincular o módulo especificado a este assembly -link:<file list> Inserir os metadados dos arquivos de assembly de @@ -2241,7 +2276,7 @@ -debug:{full|pdbonly|portable|embedded} Especificar o tipo de depuração ('full' é o padrão, 'portable' é um formato multiplataforma, - 'embedded' é um formato multiplataforma inserido no + 'embedded' é um formato multiplataforma inserido no .dll ou no .exe de destino) -optimize[+|-] Habilitar as otimizações (Forma abreviada: -o) -deterministic Produzir um assembly determinístico @@ -2275,7 +2310,7 @@ -define:<symbol list> Definir os símbolos de compilação condicional (Forma abreviada: -d) -langversion:? Exibir os valores permitidos para a versão da linguagem --langversion:<string> Especificar a versão da linguagem, como +-langversion:<string> Especificar a versão da linguagem, como `latest` (última versão, incluindo as versões secundárias), `default` (igual a `latest`), `latestmajor` (última versão, excluindo as versões secundárias), @@ -2292,7 +2327,7 @@ da chave de nome forte -keyfile:<file> Especificar a arquivo de chave de nome forte -keycontainer:<string> Especificar o contêiner de chave de nome forte --highentropyva[+|-] Habilitar a ASLR de alta entropia +-highentropyva[+|-] Habilitar a ASLR de alta entropia – DIVERSOS – @<file> Ler o arquivo de resposta de mais opções @@ -6552,7 +6587,7 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Não é possível fazer pesquisa de membro em "{0}" porque ele é um parâmetro de tipo @@ -11099,7 +11134,7 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Não é possível retornar um parâmetro por referência '{0}', pois ele não é um parâmetro ref ou out @@ -11719,7 +11754,7 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Operadores de conversão, igualdade ou desigualdade declarados em interfaces devem ser abstratos diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 77aee07f3a514..7d0ac87fca09c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -582,6 +582,31 @@ Пространство имен с файловой областью должно быть раньше всех остальных элементов в файле. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Глобальная директива using должна предшествовать всем неглобальным директивам using. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Оператор goto не может переходить к расположению раньше объявления using в том же блоке. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром. @@ -1602,6 +1632,11 @@ пространство имен с файловой областью + + file types + file types + + generic attributes универсальные атрибуты @@ -2250,7 +2285,7 @@ -instrument:TestCoverage Создать сборку, инструментированную для сбора сведений об объеме протестированного кода -sourcelink:<file> Данные о ссылке на исходные файлы для внедрения в PDB. - + — Ошибки и предупреждения - -warnaserror[+|-] Регистрировать все предупреждения как ошибки -warnaserror[+|-]:<warn list> Регистрировать указанные предупреждения как ошибки @@ -6552,7 +6587,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + Не удается выполнить поиск члена в "{0}", так как это параметр типа. @@ -11099,7 +11134,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + Невозможно вернуть параметр "{0}" по ссылке, так как это не параметр ref или out @@ -11719,7 +11754,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Операторы преобразования, равенства или неравенства, объявленные в интерфейсах, должны быть абстрактными diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index ba28aaf8a3815..3083e5bfd941f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -582,6 +582,31 @@ Dosya kapsamlı ad alanı bir dosyadaki diğer tüm üyelerin önünde olmalıdır. + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ Genel bir using yönergesi genel olmayan tüm using yönergelerinden önce gelmelidir. + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. Bir goto, aynı blok içinde yer alan using bildiriminden önceki bir konuma atlayamaz. @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + ref atamasının sol tarafı, yerel ref veya parametresi olmalıdır. @@ -1602,6 +1632,11 @@ dosya kapsamlı ad alanı + + file types + file types + + generic attributes genel öznitelikler @@ -6552,7 +6587,7 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + '{0}' bir tür parametresi olduğundan burada üye araması yapılamıyor @@ -11099,7 +11134,7 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + '{0}' parametresi bir ref veya out parametresi olmadığından başvuru ile döndürülemez @@ -11719,7 +11754,7 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + Arabirimlerde bildirilen dönüştürme, eşitlik veya eşitsizlik işleçleri soyut olmalıdır diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 92d9f98d5f6d7..30de34a5fe134 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -582,6 +582,31 @@ 文件范围内的命名空间必须位于文件中所有其他成员之前。 + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ 全局 using 指令必须位于所有非全局 using 指令之前。 + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. goto 无法跳转到同一块中 using 声明之前的某个位置。 @@ -1229,7 +1259,7 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + ref 赋值左侧必须为 ref 本地函数或参数。 @@ -1602,6 +1632,11 @@ 文件范围内的命名空间 + + file types + file types + + generic attributes 通用属性 @@ -2203,12 +2238,12 @@ (短格式: - t:winmdobj) -doc:<file> 要生成的 XML 文档文件 -refout:<file> 要生成的引用程序集输出 - + -platform:<string> 限制可以运行此代码的平台: x86、 Itanium、x64、arm、arm64、 anycpu32bitpreferred 或 anycpu。默认值是 anycpu。 - - 输入文件 - + - 输入文件 - -recurse:<wildcard> 根据通配符规范包括当前目录和 子目录中的 所有文件 @@ -2222,7 +2257,7 @@ -analyzer:<file list> 从此程序集运行分析器 (短格式: -a) -additionalfile:<file list> 不直接影响代码生成 - 但由分析器用于生成错误或警报 + 但由分析器用于生成错误或警报 的其他文件。 -embed 在 PDB 中嵌入所有源文件。 -embed:<file list> 在 PDB 中嵌入特定文件。 @@ -2235,7 +2270,7 @@ -resource:<resinfo> 嵌入指定的资源(短格式: -res) - linkresource:<resinfo> 将指定资源关联到此程序集 (短格式: -linkres),其中 resinfo 格式 - + 是 <file>[,<string name>[,public|private]] - 代码生成 - @@ -2252,16 +2287,16 @@ -instrument:TestCoverage 生成 已检测的程序集以收集 覆盖范围信息 -sourcelink:<file> 要嵌入 PDB 的源链接信息。 - + - 错误和警报 - -warnaserror[+|-] 将所有警报报告为错误 -warnaserror[+|-]:<warn list> 将特定警报报告为错误 (将 "nullable" 用于所有可为 null 的警报) -warn:<n> 设置警报级别(0 或更高) (短格式: -w) - + -nowarn:<warn list> 禁用特定警报消息 (将 "nullable" 用于所有可为 null 的警报) - + -ruleset:<file> 指定禁用特定诊断的规则集 文件。 -errorlog:<file> [,version=<sarif_version>] @@ -2270,7 +2305,7 @@ sarif_version:{1|2|2.1} 默认值是 1. 2 和 2.1 这两者 都是指 SARIF 版本 2.1.0。 -reportanalyzer 报告其他分析器信息,例如 - 执行时间。 + 执行时间。 -skipanalyzers[+|-] 跳过诊断分析器的执行。 - 语言 - @@ -2311,7 +2346,7 @@ -checksumalgorithm:<alg> 指定算法以计算存储在 PDB 中的源文件 校验和。支持的值是: SHA1 或 SHA256 (默认)。 - + -codepage:<n> 指定打开源文件时使用的 代码页 -utf8output 采用 UTF-8 编码的输出编译器消息 @@ -6557,7 +6592,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + “{0}”是一个类型参数,无法在其中执行成员查找 @@ -11104,7 +11139,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + 无法通过引用“{0}”返回参数,因为它不是 ref 或 out 参数 @@ -11724,7 +11759,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + 在接口中声明的转换、等式或不等式运算符必须是抽象的 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 6f0d0627cd955..4d565648e74a0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -139,12 +139,12 @@ The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it - 多載移位 (Shift) 運算子的第一個運算元的類型必須和包含的類型相同,或是其型別參數受限於該運算子 + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it A static virtual or abstract interface member can be accessed only on a type parameter. - 只能在型別參數上存取靜態虛擬或抽象介面成員。 + A static virtual or abstract interface member can be accessed only on a type parameter. @@ -249,7 +249,7 @@ Cannot convert &method group '{0}' to delegate type '{1}'. - 無法將方法群組 '{0}' 轉換成委派類型 '{1}'(&M)。 + Cannot convert &method group '{0}' to delegate type '{1}'. @@ -304,12 +304,12 @@ This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. - 此建構函式必須新增 'SetsRequiredMembers',因為它鏈結至具有該屬性的建構函式。 + This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. The operator '{0}' requires a matching non-checked version of the operator to also be defined - 運算子 '{0}' 需要也同時定義運算子的相符未檢查版本 + The operator '{0}' requires a matching non-checked version of the operator to also be defined @@ -469,7 +469,7 @@ Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead. - 請勿使用 'System.Runtime.CompilerServices.RequiredMemberAttribute'。請改為在必要的欄位和屬性上使用 'required' 關鍵字。 + Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead. @@ -489,7 +489,7 @@ An expression tree may not contain an access of static virtual or abstract interface member - 運算式樹狀架構不可包含靜態虛擬或抽象介面成員的存取權 + An expression tree may not contain an access of static virtual or abstract interface member @@ -582,6 +582,31 @@ 以檔為範圍的命名空間必須在檔案中的所有其他成員之前。 + + File type '{0}' cannot be used as a base type of non-file type '{1}'. + File type '{0}' cannot be used as a base type of non-file type '{1}'. + + + + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + File type '{0}' cannot be used in a member signature in non-file type '{1}'. + + + + Types and aliases cannot be named 'file'. + Types and aliases cannot be named 'file'. + + + + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + File type '{0}' must be defined in a top level type; '{0}' is a nested type. + + + + File type '{0}' cannot use accessibility modifiers. + File type '{0}' cannot use accessibility modifiers. + + A fixed field must not be a ref field. A fixed field must not be a ref field. @@ -627,6 +652,11 @@ 全域 using 指示詞必須在所有非全域 using 指示詞之前。 + + File type '{0}' cannot be used in a 'global using static' directive. + File type '{0}' cannot be used in a 'global using static' directive. + + A goto cannot jump to a location before a using declaration within the same block. 在相同區塊內,goto 不可跳到 using 宣告前的位置。 @@ -654,7 +684,7 @@ An 'implicit' user-defined conversion operator cannot be declared checked - 無法將「隱式」使用者定義轉換運算子宣告為已檢查 + An 'implicit' user-defined conversion operator cannot be declared checked @@ -889,7 +919,7 @@ Unexpected keyword 'unchecked' - 未預期的關鍵字 'unchecked' + Unexpected keyword 'unchecked' @@ -954,7 +984,7 @@ '{2}' cannot satisfy the 'new()' constraint on parameter '{1}' in the generic type or or method '{0}' because '{2}' has required members. - '{2}' 無法滿足泛型型別或方法 '{0}' 中參數 '{1}' 的 'new()' 限制式,因為 '{2}' 具有必要的成員。 + '{2}' cannot satisfy the 'new()' constraint on parameter '{1}' in the generic type or or method '{0}' because '{2}' has required members. @@ -1074,7 +1104,7 @@ User-defined operator '{0}' cannot be declared checked - 使用者定義的運算子 '{0}' 無法宣告為已檢查 + User-defined operator '{0}' cannot be declared checked @@ -1089,7 +1119,7 @@ '{0}' must be required because it overrides required member '{1}' - '{0}' 必須為必要項目,因為它會覆蓋必要的成員 '{1}' + '{0}' must be required because it overrides required member '{1}' @@ -1104,7 +1134,7 @@ The 'parameter null-checking' feature is not supported. - 不支援 'parameter null-checking' 功能。 + The 'parameter null-checking' feature is not supported. @@ -1154,7 +1184,7 @@ A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. - 不支援字串 'null' 常數做為 '{0}' 的模式。請改為使用空字串。 + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. @@ -1229,12 +1259,12 @@ The left-hand side of a ref assignment must be a ref variable. - The left-hand side of a ref assignment must be a ref variable. + 參考指派的左側必須為參考本機或參數。 Ref returning properties cannot be required. - 無法要求 Ref 傳回屬性。 + Ref returning properties cannot be required. @@ -1244,42 +1274,42 @@ Required member '{0}' cannot be hidden by '{1}'. - '{1}' 無法隱藏必要成員 '{0}'。 + Required member '{0}' cannot be hidden by '{1}'. Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. - 必要成員 '{0}' 可見度不能較低,或 setter 的可見程度低於包含的類型 '{1}'。 + Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. Required member '{0}' must be set in the object initializer or attribute constructor. - 必須在物件初始設定式或屬性建構函式中設定必要的成員 '{0}'。 + Required member '{0}' must be set in the object initializer or attribute constructor. Required member '{0}' must be settable. - 必要的成員 '{0}' 必須可設定。 + Required member '{0}' must be settable. The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. - 基底類型 '{0}' 所需的成員清單格式錯誤,無法解譯。若要使用此建構函式,請套用 'SetsRequiredMembers' 屬性。 + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. The required members list for '{0}' is malformed and cannot be interpreted. - '{0}' 的必要成員清單格式錯誤,無法解譯。 + The required members list for '{0}' is malformed and cannot be interpreted. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. - 必要的成員 '{0}' 必須指派值,它無法使用巢狀成員或集合初始設定式。 + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Types and aliases cannot be named 'required'. - 類型和別名不能命名為 'required'。 + Types and aliases cannot be named 'required'. @@ -1494,12 +1524,12 @@ Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. - 在控制項傳回呼叫者之前,必須先完全指派自動實作屬性 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設屬性。 + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field. - 在控制項傳回呼叫者之前,必須先完全指派欄位 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設欄位。 + Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field. @@ -1514,7 +1544,7 @@ '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. - '{0}' 需要編譯器功能 '{1}',此版本的 C# 編譯器不支援此功能。 + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. @@ -1534,17 +1564,17 @@ Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. - 使用可能未指派的欄位 '{0}'。請考慮更新語言版本 '{1}' 以自動預設欄位。 + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property. - 使用可能未指派的自動實作屬性 '{0}'。請考慮更新語言版本 '{1}' 以自動預設屬性。 + Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property. The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields. - 在指派 'this' 物件的所有欄位之前,無法使用該物件。請考慮更新語言版本 '{0}',以自動預設未指派的欄位。 + The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields. @@ -1569,12 +1599,12 @@ auto default struct fields - 自動預設結構欄位 + auto default struct fields checked user-defined operators - 已檢查使用者定義的運算子 + checked user-defined operators @@ -1602,6 +1632,11 @@ 以檔案為範圍的命名空間 + + file types + file types + + generic attributes 一般屬性 @@ -1674,12 +1709,12 @@ relaxed shift operator - 寬鬆移位 (Shift) 運算子 + relaxed shift operator required members - 必要成員 + required members @@ -1694,7 +1729,7 @@ unsigned right shift - 未簽署右移位 + unsigned right shift @@ -1719,7 +1754,7 @@ pattern matching ReadOnly/Span<char> on constant string - 常數字串上的模式比對 ReadOnly/Span<char> + pattern matching ReadOnly/Span<char> on constant string @@ -1854,12 +1889,12 @@ Required member '{0}' should not be attributed with 'ObsoleteAttribute' unless the containing type is obsolete or all constructors are obsolete. - 除非包含的類型已過時或所有建構函式已過時,否則必要成員 '{0}' 的屬性不應為 'ObsoleteAttribute'。 + Required member '{0}' should not be attributed with 'ObsoleteAttribute' unless the containing type is obsolete or all constructors are obsolete. Members attributed with 'ObsoleteAttribute' should not be required unless the containing type is obsolete or all constructors are obsolete. - 除非包含的類型已過時或所有建構函式已過時,否則不應要求具有 'ObsoleteAttribute' 屬性的成員。 + Members attributed with 'ObsoleteAttribute' should not be required unless the containing type is obsolete or all constructors are obsolete. @@ -1974,7 +2009,7 @@ 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions. - 'UnmanagedCallersOnly' 僅適用於一般靜態非抽象方法、非虛擬方法或靜態區域函式。 + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions. UnmanagedCallersOnly is not localizable. @@ -2250,7 +2285,7 @@ -instrument:TestCoverage 產生經檢測的組件,以收集 涵蓋範圍資訊 -sourcelink:<file> 要內嵌至 PDB 的來源連結資訊。 - + - 錯誤與警告 - -warnaserror[+|-] 將所有警告回報為錯誤 -warnaserror[+|-]:<warn list> 將特定警告回報為錯誤 @@ -3758,42 +3793,42 @@ Control is returned to caller before auto-implemented property '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派自動實作屬性 '{0}' 之前會先將控制項傳回呼叫者,導致先前隱含的指派為 'default'。 + Control is returned to caller before auto-implemented property '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'. Control is returned to caller before auto-implemented property is explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派自動實作屬性之前會先將控制項傳回呼叫者,導致先前隱含的指派為 'default'。 + Control is returned to caller before auto-implemented property is explicitly assigned, causing a preceding implicit assignment of 'default'. Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. - 在控制項傳回呼叫者之前,必須先完全指派自動實作屬性 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設屬性。 + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. An auto-implemented property must be fully assigned before control is returned to the caller. Consider updating the language version to auto-default the property. - 在控制項傳回呼叫者之前,必須先完全指派自動實作屬性。請考慮更新至語言版本以自動預設屬性。 + An auto-implemented property must be fully assigned before control is returned to the caller. Consider updating the language version to auto-default the property. Control is returned to caller before field '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派欄位 '{0}' 之前會先將控制項傳回呼叫者,導致先前隱含的指派為 'default'。 + Control is returned to caller before field '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'. Control is returned to caller before field is explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派欄位之前會先將控制項傳回呼叫者,導致先前隱含的指派為 'default'。 + Control is returned to caller before field is explicitly assigned, causing a preceding implicit assignment of 'default'. Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field. - 在控制項傳回呼叫者之前,必須先完全指派欄位 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設欄位。 + Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field. Fields of a struct must be fully assigned in a constructor before control is returned to the caller. Consider updating the language version to auto-default the field. - 在控制項傳回呼叫者之前,必須先在建構函式中完全指派結構的欄位。請考慮更新至語言版本以自動預設欄位。 + Fields of a struct must be fully assigned in a constructor before control is returned to the caller. Consider updating the language version to auto-default the field. @@ -3858,22 +3893,22 @@ Field '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派之前會先讀取欄位 '{0}',導致先前隱含的指派為 'default'。 + Field '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. Field is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派之前會先讀取欄位,導致先前隱含的指派為 'default'。 + Field is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. - 使用可能未指派的欄位 '{0}'。請考慮更新語言版本 '{1}' 以自動預設欄位。 + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Use of possibly unassigned field. Consider updating the language version to auto-default the field. - 使用可能未指派的欄位。請考慮更新語言版本以自動預設欄位。 + Use of possibly unassigned field. Consider updating the language version to auto-default the field. @@ -3898,22 +3933,22 @@ Auto-implemented property '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派之前會先讀取自動實作屬性 '{0}',導致先前隱含的指派為 'default'。 + Auto-implemented property '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. Auto-implemented property is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. - 在明確指派之前會先讀取自動實作屬性,導致先前隱含的指派為 'default'。 + Auto-implemented property is read before being explicitly assigned, causing a preceding implicit assignment of 'default'. Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property. - 使用可能未指派的自動實作屬性 '{0}'。請考慮更新語言版本 '{1}' 以自動預設屬性。 + Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property. Use of possibly unassigned auto-implemented property. Consider updating the language version to auto-default the property. - 使用可能未指派的自動實作屬性。請考慮更新語言版本以自動預設屬性。 + Use of possibly unassigned auto-implemented property. Consider updating the language version to auto-default the property. @@ -3923,22 +3958,22 @@ The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields. - 在指派 'this' 物件的所有欄位之前,會先讀取該物件,導致先前對未明確指派的欄位進行隱含的 'default' 指派。 + The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields. The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields. - 在指派 'this' 物件的所有欄位之前,會先讀取該物件,導致先前對未明確指派的欄位進行隱含的 'default' 指派。 + The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields. The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields. - 在指派 'this' 物件的所有欄位之前,無法使用該物件。請考慮更新語言版本 '{0}',以自動預設未指派的欄位。 + The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields. The 'this' object cannot be used in a constructor before all of its fields have been assigned. Consider updating the language version to auto-default the unassigned fields. - 在指派 'this' 物件的所有欄位之前,無法在建構函式中使用。請考慮更新語言版本,以自動預設未指派的欄位。 + The 'this' object cannot be used in a constructor before all of its fields have been assigned. Consider updating the language version to auto-default the unassigned fields. @@ -5977,7 +6012,7 @@ If such a class is used as a base class and if the deriving class defines a dest The first operand of an overloaded shift operator must have the same type as the containing type - 多載移位 (Shift) 運算子的第一個運算元的類型必須和包含類型相同 + The first operand of an overloaded shift operator must have the same type as the containing type @@ -6552,7 +6587,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot do non-virtual member lookup in '{0}' because it is a type parameter - Cannot do non-virtual member lookup in '{0}' because it is a type parameter + 無法在 '{0}' 中進行成員查詢,因為其為類型參數 @@ -11099,7 +11134,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return a parameter by reference '{0}' because it is not a ref parameter - Cannot return a parameter by reference '{0}' because it is not a ref parameter + 無法藉傳址方式 '{0}' 傳回參數,因為其非 ref 或 out 參數 @@ -11719,7 +11754,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual - Conversion, equality, or inequality operators declared in interfaces must be abstract or virtual + 在介面中宣告的轉換、等式或不等式運算子必須為抽象 diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 4a9568a43ef6e..9cdccab77612b 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -13716,5 +13716,333 @@ .maxstack 1 }) .Verify(); } + + [Fact] + public void FileTypes_01() + { + var source0 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(1); + } +}", "file1.cs"); + var source1 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(2); + } +}", "file1.cs"); + + var compilation0 = CreateCompilation(source0.Tree, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + + var method0 = compilation0.GetMember("C.M"); + var method1 = compilation1.GetMember("C.M"); + + var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped); + + v0.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +} +"); + + using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, method0, method1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + // There should be no diagnostics from rude edits + diff.EmitResult.Diagnostics.Verify(); + + diff.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + } + + [Fact] + public void FileTypes_02() + { + var source0 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(1); + } +}", "file1.cs"); + var source1 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(2); + } +}", "file1.cs"); + var source2 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(3); + } +}", "file2.cs"); + + var compilation0 = CreateCompilation(source0.Tree, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(new[] { source1.Tree, source2.Tree }); + + var cm1_gen0 = compilation0.GetMember("C.M"); + var cm1_gen1 = ((NamedTypeSymbol)compilation1.GetMembers("C")[0]).GetMember("M"); + var c2_gen1 = ((NamedTypeSymbol)compilation1.GetMembers("C")[1]); + + var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped); + + v0.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +} +"); + + using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, cm1_gen0, cm1_gen1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true), + SemanticEdit.Create(SemanticEditKind.Insert, null, c2_gen1, syntaxMap: null, preserveLocalVariables: true))); + + // There should be no diagnostics from rude edits + diff.EmitResult.Diagnostics.Verify(); + + diff.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + + diff.VerifyIL("C@file2.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.3 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + } + + [Fact] + public void FileTypes_03() + { + var source0_gen0 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(1); + } +}", "file1.cs"); + var source1_gen1 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(2); + } +}", "file2.cs"); + var source0_gen1 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(3); + } +}", "file1.cs"); + + var compilation0 = CreateCompilation(source0_gen0.Tree, options: ComSafeDebugDll); + // Because the order of syntax trees has changed here, the original type is considered deleted and the two new types are completely new, unrelated types. + + // https://github.com/dotnet/roslyn/issues/61999 + // we should handle this as a modification of an existing type rather than deletion and insertion of distinct types. + // most likely, we either need to identify file types based on something stable like the SyntaxTree.FilePath, or store a mapping of the ordinals from one generation to the next. + // although "real-world" compilations disallow duplicated file paths, duplicated or empty file paths are very common via direct use of the APIs, so there's not necessarily a single slam-dunk answer here. + var compilation1 = compilation0.WithSource(new[] { source1_gen1.Tree, source0_gen1.Tree }); + + var c1_gen0 = compilation0.GetMember("C"); + var c1_gen1 = ((NamedTypeSymbol)compilation1.GetMembers("C")[0]); + var c2_gen1 = ((NamedTypeSymbol)compilation1.GetMembers("C")[1]); + + var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped); + + v0.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +} +"); + + using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Delete, c1_gen0, null, syntaxMap: null, preserveLocalVariables: true), + SemanticEdit.Create(SemanticEditKind.Insert, null, c1_gen1, syntaxMap: null, preserveLocalVariables: true), + SemanticEdit.Create(SemanticEditKind.Insert, null, c2_gen1, syntaxMap: null, preserveLocalVariables: true))); + + // There should be no diagnostics from rude edits + diff.EmitResult.Diagnostics.Verify(); + + diff.VerifyIL("C@file1.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.3 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + + diff.VerifyIL("C@file2.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + } + + [Fact] + public void FileTypes_04() + { + var source1_gen0 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(1); + } +}", "file1.cs"); + var source2_gen0 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(2); + } +}", "file2.cs"); + var source2_gen1 = MarkedSource(@" +using System; +file class C +{ + void M() + { + Console.Write(3); + } +}", "file2.cs"); + + var compilation0 = CreateCompilation(new[] { source1_gen0.Tree, source2_gen0.Tree }, options: ComSafeDebugDll); + + var compilation1 = compilation0.WithSource(new[] { source2_gen1.Tree }); + + var c1_gen0 = ((NamedTypeSymbol)compilation0.GetMembers("C")[0]); + var c2_gen0 = ((NamedTypeSymbol)compilation0.GetMembers("C")[1]); + var c2_gen1 = compilation1.GetMember("C"); + + var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped); + + v0.VerifyIL("C@file2.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +} +"); + + using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Delete, c1_gen0, null, syntaxMap: null, preserveLocalVariables: true), + SemanticEdit.Create(SemanticEditKind.Delete, c2_gen0, null, syntaxMap: null, preserveLocalVariables: true), + SemanticEdit.Create(SemanticEditKind.Insert, null, c2_gen1, syntaxMap: null, preserveLocalVariables: true))); + + // There should be no diagnostics from rude edits + diff.EmitResult.Diagnostics.Verify(); + + diff.VerifyIL("C@file2.M", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.3 + IL_0002: call ""void System.Console.Write(int)"" + IL_0007: nop + IL_0008: ret +}"); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs index 2fcaaecdc8f09..2ccef65358122 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs @@ -23,7 +23,7 @@ public void Test1() var mscorlibRef = TestMetadata.Net40.mscorlib; var compilation = CSharpCompilation.Create("Test", references: new MetadataReference[] { mscorlibRef }); var sys = compilation.GlobalNamespace.ChildNamespace("System"); - Conversions c = new BuckStopsHereBinder(compilation).Conversions; + Conversions c = new BuckStopsHereBinder(compilation, associatedSyntaxTree: null).Conversions; var types = new TypeSymbol[] { sys.ChildType("Object"), @@ -311,7 +311,7 @@ class C Assert.True(typeIntArrayWithCustomModifiers.HasCustomModifiers(flagNonDefaultArraySizesOrLowerBounds: false)); - var conv = new BuckStopsHereBinder(compilation).Conversions; + var conv = new BuckStopsHereBinder(compilation, associatedSyntaxTree: null).Conversions; HashSet useSiteDiagnostics = null; // no custom modifiers to custom modifiers diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index d5e358d3b6939..db91e25e01b09 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -44,6 +44,8 @@ internal override bool MangleName } } + internal override SyntaxTree AssociatedSyntaxTree => null; + public override ImmutableArray TypeParameters { get diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs new file mode 100644 index 0000000000000..47029fb59b42d --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs @@ -0,0 +1,3365 @@ +// 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. + +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.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 CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class C { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").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 CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class C { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").WithLocation(3, 16), + // (3,16): error CS9054: File 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 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 CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class Outer + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Outer").WithArguments("file types").WithLocation(1, 12)); + verify(); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + verify(); + + void verify() + { + var outer = comp.GetMember("Outer"); + Assert.Equal(Accessibility.Internal, outer.DeclaredAccessibility); + Assert.True(((SourceMemberContainerTypeSymbol)outer).IsFile); + + var classC = comp.GetMember("Outer.C"); + Assert.Equal(Accessibility.Private, classC.DeclaredAccessibility); + Assert.False(((SourceMemberContainerTypeSymbol)classC).IsFile); + } + } + + [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 CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class Outer + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Outer").WithArguments("file types").WithLocation(1, 12), + // (3,16): error CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class C { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").WithLocation(3, 16), + // (3,16): error CS9054: File 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 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 type 'Outer.C' cannot be used in a member signature in non-file 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("C"); + Assert.Equal("<>F0__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' instead of 'C`1'). + verifier.VerifyIL("C@.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[] { "", "<>F0__C", "Program" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); + var classC = symbol.GlobalNamespace.GetMember("<>F0__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[] { "", source }, expectedOutput: "1", symbolValidator: symbolValidator); + verifier.VerifyDiagnostics(); + + var comp = (CSharpCompilation)verifier.Compilation; + var symbol = comp.GetMember("C"); + Assert.Equal("<>F1__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' instead of 'C`1'). + verifier.VerifyIL("C@.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[] { "", "<>F1__C", "Program" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); + var classC = symbol.GlobalNamespace.GetMember("<>F1__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("E"); + Assert.Equal("<>F0__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[] { "", "<>F0__E", "Program" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); + var classC = symbol.GlobalNamespace.GetMember("<>F0__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: "(<>F0__E)1", symbolValidator: symbolValidator); + verifier.VerifyDiagnostics(); + + var comp = (CSharpCompilation)verifier.Compilation; + var symbol = comp.GetMember("E"); + Assert.Equal("<>F0__E", symbol.MetadataName); + + void symbolValidator(ModuleSymbol symbol) + { + Assert.Equal(new[] { "", "<>F0__E", "<>F0__Attr", "Program" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); + var classC = symbol.GlobalNamespace.GetMember("<>F0__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 type 'E' cannot be used in a member signature in non-file 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: "(<>F0__E)1", symbolValidator: symbolValidator); + verifier.VerifyDiagnostics(); + + var comp = (CSharpCompilation)verifier.Compilation; + var symbol = comp.GetMember("E"); + Assert.Equal("<>F0__E", symbol.MetadataName); + + void symbolValidator(ModuleSymbol symbol) + { + Assert.Equal(new[] { "", "<>F0__E", "Attr", "Program" }, symbol.GlobalNamespace.GetMembers().Select(m => m.Name)); + var classC = symbol.GlobalNamespace.GetMember("<>F0__E"); + Assert.Equal(new[] { "value__", "E1", "E2", ".ctor" }, classC.MemberNames); + } + } + + [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, source2 }); + 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.M(1); + + file class C + { + 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@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"); + Assert.Equal("F0__C`1", c.MetadataName); + + void symbolValidator(ModuleSymbol module) + { + Assert.Equal(new[] { "", "Program", "F0__C" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); + + var classC = module.GlobalNamespace.GetMember("F0__C"); + Assert.Equal("F0__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); } + } + """; + + 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()); + Assert.Equal("F0__C", c.MetadataName); + + void symbolValidator(ModuleSymbol module) + { + Assert.Equal(new[] { "", "Program", "F0__C" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); + var expectedSymbol = module.GlobalNamespace.GetMember("F0__C"); + Assert.Equal("F0__C", 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()); + Assert.Equal("F0__C", c.MetadataName); + + void symbolValidator(ModuleSymbol module) + { + Assert.Equal(new[] { "", "Program", "F0__C" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); + var expectedSymbol = module.GlobalNamespace.GetMember("F0__C"); + Assert.Equal("F0__C", expectedSymbol.MetadataName); + Assert.Equal(new[] { "M", ".ctor" }, expectedSymbol.MemberNames); + } + } + + [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(""" + using System; + + file class C + { + public static void M() { Console.Write(2); } + } + """, options: TestOptions.RegularPreview, path: path, encoding: Encoding.Default); + + var verifier = CompileAndVerify(new[] { source1, source2 }, expectedOutput: "1", symbolValidator: symbolValidator); + verifier.VerifyDiagnostics(); + + // note that VerifyIL doesn't work in this specific scenario because the files have the same name. + + void symbolValidator(ModuleSymbol module) + { + Assert.NotNull(module.GlobalNamespace.GetMember("F0__C")); + Assert.NotNull(module.GlobalNamespace.GetMember("F1__C")); + } + } + + // 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 verifier = CompileAndVerify(SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularPreview, path: $"path/to/My{badChar}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()); + Assert.Equal("F0__C", c.MetadataName); + + void symbolValidator(ModuleSymbol module) + { + Assert.Equal(new[] { "", "Program", "F0__C" }, module.GlobalNamespace.GetMembers().Select(m => m.Name)); + var expectedSymbol = module.GlobalNamespace.GetMember("F0__C"); + Assert.Equal("F0__C", expectedSymbol.MetadataName); + Assert.Equal(new[] { "M", ".ctor" }, expectedSymbol.MemberNames); + } + } + + [Fact] + public void Pdb_01() + { + var source = """ + using System; + + C.M(); + + file class C + { + public static void M() { Console.Write(1); } + } + """; + + var expectedMetadataName = "F0__C"; + 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()); + Assert.Equal(expectedMetadataName, c.MetadataName); + + void validateSymbols(ModuleSymbol module) + { + var type = module.GlobalNamespace.GetMember(expectedMetadataName); + Assert.NotNull(type); + Assert.Equal(new[] { "M", ".ctor" }, type.MemberNames); + } + } + + [Theory] + [InlineData("file", "file", "<>F0__C", "<>F1__C")] + [InlineData("file", "", "<>F0__C", "C")] + [InlineData("", "file", "C", "<>F1__C")] + public void Duplication_01(string firstFileModifier, string secondFileModifier, string firstMetadataName, string secondMetadataName) + { + // A file type is allowed to have the same name as a non-file type from a different file. + // When both a file type and non-file type with the same name are in scope, the file 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, source2 }, expectedOutput: "1"); + var comp = (CSharpCompilation)verifier.Compilation; + var cs = comp.GetMembers("C"); + var tree = comp.SyntaxTrees[0]; + var expectedSymbol = cs[0]; + Assert.Equal(firstMetadataName, expectedSymbol.MetadataName); + verify(); + + verifier = CompileAndVerify(new[] { source1, source2 + main }, expectedOutput: "2"); + comp = (CSharpCompilation)verifier.Compilation; + cs = comp.GetMembers("C"); + tree = comp.SyntaxTrees[1]; + expectedSymbol = cs[1]; + Assert.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().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 '' already contains a definition for 'C' + // class C + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C").WithArguments("C", "").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()' and 'C.M()' + // C.M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M()", "C.M()").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, source2, main }, 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 { IsFile: 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 { IsFile: 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().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, main }, 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 { IsFile: false }); + Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); + + var c1 = cs[1]; + Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFile: 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().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, main }, 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).IsFile); + Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); + + var c1 = cs[1]; + Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFile: 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().Last().Expression; + var info = model.GetTypeInfo(cReference); + Assert.Equal(c1.GetPublicSymbol(), info.Type); + } + + [Fact] + public void Duplication_06() + { + var source1 = """ + using System; + + partial class C + { + public static void M() + { + Console.Write(Number); + } + } + """; + + var source2 = """ + using System; + + partial class C + { + private static int Number => 1; + } + + file class C + { + public static void M() + { + Console.Write(2); + } + } + """; + + var comp = CreateCompilation(new[] { source1, source2 }); + // https://github.com/dotnet/roslyn/issues/62333: should this diagnostic be more specific? + // the issue more precisely is that a definition for 'C' already exists in the current file--not that it's already in this namespace. + comp.VerifyDiagnostics( + // (8,12): error CS0101: The namespace '' already contains a definition for 'C' + // file class C + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C").WithArguments("C", "").WithLocation(8, 12)); + + var cs = comp.GetMembers("C"); + Assert.Equal(2, cs.Length); + + var c0 = cs[0]; + Assert.True(c0 is SourceMemberContainerTypeSymbol { IsFile: 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 { IsFile: true }); + Assert.Equal(comp.SyntaxTrees[1], c1.DeclaringSyntaxReferences.Single().SyntaxTree); + + + comp = CreateCompilation(new[] { source2, source1 }); + comp.VerifyDiagnostics( + // (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), + // (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("C"); + Assert.True(c is SourceMemberContainerTypeSymbol { IsFile: 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, source2 }); + 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 { IsFile: true }); + Assert.Equal(comp.SyntaxTrees[0], c0.DeclaringSyntaxReferences.Single().SyntaxTree); + + var c1 = cs[1]; + Assert.True(c1 is SourceMemberContainerTypeSymbol { IsFile: 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, source1 }); + 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 { IsFile: 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 { IsFile: 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, source2, source3 }); + compilation.VerifyDiagnostics( + // (3,16): error CS9054: File 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 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("Outer"); + var cs = classOuter.GetMembers("C"); + Assert.Equal(3, cs.Length); + Assert.True(cs[0] is SourceMemberContainerTypeSymbol { IsFile: true }); + Assert.True(cs[1] is SourceMemberContainerTypeSymbol { IsFile: true }); + Assert.True(cs[2] is SourceMemberContainerTypeSymbol { IsFile: 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, source2, source3 }); + compilation.VerifyDiagnostics(); + + var namespaceNS = compilation.GetMember("NS"); + var cs = namespaceNS.GetMembers("C"); + Assert.Equal(3, cs.Length); + Assert.True(cs[0] is SourceMemberContainerTypeSymbol { IsFile: true }); + Assert.True(cs[1] is SourceMemberContainerTypeSymbol { IsFile: true }); + Assert.True(cs[2] is SourceMemberContainerTypeSymbol { IsFile: 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, source2 }); + var cs = comp.GetMembers("Program.C"); + var tree = comp.SyntaxTrees[0]; + var expectedSymbol = cs[0]; + verify(); + + comp = CreateCompilation(new[] { source1, source2 + main }); + 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().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, source2 }, 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, source2 + main }, 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().Last(); + var info = model.GetTypeInfo(cReference); + Assert.Equal(expectedSymbol.GetPublicSymbol(), info.Type); + } + } + + [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 type 'C' cannot be used in a member signature in non-file type 'D'. + // public void M1(C c) { } // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("C", "D").WithLocation(7, 17), + // (8,18): error CS9051: File type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file type 'D'. + // public C M1() => new C(); // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("C", "D").WithLocation(7, 14), + // (8,15): error CS9051: File type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file type 'E'. + // C property { get; set; } // 2 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "property").WithArguments("C", "E").WithLocation(9, 7), + // (10,12): error CS9051: File type 'C' cannot be used in a member signature in non-file 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 type 'D' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.InnerDelegate' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.Inner' cannot be used in a member signature in non-file 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 type 'C.InnerDelegate' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file type 'Del1'. + // delegate void Del1(C c); // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del1").WithArguments("C", "Del1").WithLocation(5, 15), + // (6,12): error CS9051: File type 'C' cannot be used in a member signature in non-file type 'Del2'. + // delegate C Del2(); // 2 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "Del2").WithArguments("C", "Del2").WithLocation(6, 12)); + } + + [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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 type 'C' cannot be used in a member signature in non-file 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 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 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 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 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 type 'Base' cannot be used as a base type of non-file 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 type 'Base' cannot be used as a base type of non-file 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 type 'Interface' cannot be used as a base type of non-file 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, source2 }, 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().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, source2 }, 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().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 type 'I1' cannot be used as a base type of non-file type 'Derived'. + // partial interface Derived : I1 { } // 1 + Diagnostic(ErrorCode.ERR_FileTypeBase, "Derived").WithArguments("I1", "Derived").WithLocation(3, 19)); + } + + [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 type 'I' cannot be used in a member signature in non-file 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 type 'I' cannot be used in a member signature in non-file 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, source2 }); + 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, source2 }); + 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] + public void TypeArguments_01() + { + var source = """ + file struct S { public int X; } + class Container { } + unsafe class Program + { + Container M1() => new Container(); // 1 + S[] M2() => new S[0]; // 2 + (S, S) M3() => (new S(), new S()); // 3 + S* M4() => null; // 4 + delegate* 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 type 'Container' cannot be used in a member signature in non-file type 'Program'. + // Container M1() => new Container(); // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M1").WithArguments("Container", "Program").WithLocation(5, 18), + // (6,9): error CS9051: File type 'S[]' cannot be used in a member signature in non-file type 'Program'. + // S[] M2() => new S[0]; // 2 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M2").WithArguments("S[]", "Program").WithLocation(6, 9), + // (7,12): error CS9051: File type '(S, S)' cannot be used in a member signature in non-file 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 type 'S*' cannot be used in a member signature in non-file type 'Program'. + // S* M4() => null; // 4 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M4").WithArguments("S*", "Program").WithLocation(8, 8), + // (9,24): error CS9051: File type 'delegate*' cannot be used in a member signature in non-file type 'Program'. + // delegate* M5() => null; // 5 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M5").WithArguments("delegate*", "Program").WithLocation(9, 24)); + } + + [Fact] + public void Constraints_01() + { + var source = """ + file class C { } + + file class D + { + void M(T t) where T : C { } // ok + } + + class E + { + void M(T t) where T : C { } // 1 + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,30): error CS9051: File type 'C' cannot be used in a member signature in non-file type 'E.M(T)'. + // void M(T t) where T : C { } // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "C").WithArguments("C", "E.M(T)").WithLocation(10, 30)); + } + + [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, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics( + // (3,8): error CS9051: File type 'C' cannot be used in a member signature in non-file type 'R1'. + // record R1(C c); // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R1").WithArguments("C", "R1").WithLocation(3, 8), + // (3,8): error CS9051: File type 'C' cannot be used in a member signature in non-file type 'R1'. + // record R1(C c); // 1 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R1").WithArguments("C", "R1").WithLocation(3, 8), + // (4,15): error CS9051: File type 'C' cannot be used in a member signature in non-file type 'R2'. + // record struct R2(C c); // 2 + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "R2").WithArguments("C", "R2").WithLocation(4, 15), + // (4,15): error CS9051: File type 'C' cannot be used in a member signature in non-file 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, source2 }); + 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 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, source2 }); + 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 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, source, IsExternalInitTypeDefinition }, 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, IsExternalInitTypeDefinition }, 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, main }); + 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 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; + + public class Container + { + } + + file class C + { + public static void M() { } + } + """; + + var main = """ + class Program + { + public static void Main() + { + M(); + } + } + """; + + var compilation = CreateCompilation(new[] { source, main }); + compilation.VerifyDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // global using static Container; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "global using static Container;").WithLocation(1, 1), + // (1,21): error CS9055: File type 'Container' cannot be used in a 'global using static' directive. + // global using static Container; + Diagnostic(ErrorCode.ERR_GlobalUsingStaticFileType, "Container").WithArguments("Container").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 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, source2 }, 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("D.M"); + + var invocation = tree.GetRoot().DescendantNodes().OfType().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, source2 }, 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().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, main }); + compilation.VerifyDiagnostics( + // (16,20): error CS9054: File 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("Base.C.M"); + + var tree = compilation.SyntaxTrees[1]; + var model = compilation.GetSemanticModel(tree); + var invoked = tree.GetRoot().DescendantNodes().OfType().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().Last().Body!; + + var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); + + var info = model.GetSymbolInfo(((ExpressionStatementSyntax)body.Statements.First()).Expression); + Assert.Equal("void NS.C@.M()", info.Symbol.ToTestDisplayString()); + + var classC = compilation.GetMember("NS.C").GetPublicSymbol(); + Assert.Equal("NS.C@", 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("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("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, main }); + 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().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@", 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("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("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().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, main }); + compilation.VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees[1]; + var body = tree.GetRoot().DescendantNodes().OfType().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 + { + /// + /// In the same file as . + /// + 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 + { + /// + /// In a different file than . + /// + public static void M() + { + + } + } + """; + + var compilation = CreateCompilation(new[] { source, main }, 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 . + 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, source2 }); + 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)); + } + + [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, source2 }); + 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 type 'C' cannot be used as a base type of non-file 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("C1"); + var c2 = comp.GetMember("C2"); + Assert.Equal("C1@", c1.ToTestDisplayString()); + Assert.Equal("C2@FileB", c2.ToTestDisplayString()); + + Assert.Equal("void C1@.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 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().Single().Type!; + var typeInfo = model.GetTypeInfo(voidTypeSyntax); + Assert.Equal("System.Void@", 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("C"); + Assert.Equal("<>F0__C", sourceMember.MetadataName); + + var sourceType = comp.GetTypeByMetadataName("<>F0__C"); + Assert.Equal(sourceMember, sourceType); + + Assert.Null(comp.GetTypeByMetadataName("<>F0__D")); + Assert.Null(comp.GetTypeByMetadataName("<>F1__C")); + Assert.Null(comp.GetTypeByMetadataName("F0__C")); + Assert.Null(comp.GetTypeByMetadataName("F0__C")); + + // from metadata + var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + var metadataMember = comp2.GetMember("<>F0__C"); + Assert.Equal("<>F0__C", metadataMember.MetadataName); + + var metadataType = comp2.GetTypeByMetadataName("<>F0__C"); + Assert.Equal(metadataMember, metadataType); + } + + [Fact] + public void GetTypeByMetadataName_02() + { + var source1 = """ + file class C { } + """; + + // from source + var comp = CreateCompilation(source1); + comp.VerifyDiagnostics(); + var sourceMember = comp.GetMember("C"); + Assert.Equal("<>F0__C`1", sourceMember.MetadataName); + + var sourceType = comp.GetTypeByMetadataName("<>F0__C`1"); + Assert.Equal(sourceMember, sourceType); + Assert.Null(comp.GetTypeByMetadataName("<>F0__C")); + + // from metadata + var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + + var metadataMember = comp2.GetMember("<>F0__C"); + Assert.Equal("<>F0__C`1", metadataMember.MetadataName); + + var metadataType = comp2.GetTypeByMetadataName("<>F0__C`1"); + Assert.Equal(metadataMember, metadataType); + } + + [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 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("Outer.C"); + Assert.Equal("<>F0__C", sourceMember.MetadataName); + + var sourceType = comp.GetTypeByMetadataName("Outer.<>F0__C"); + // Note: strictly speaking, it would be reasonable to return the (invalid) nested file 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); + } + + [Fact] + public void GetTypeByMetadataName_04() + { + var source1 = """ + file class C { } + """; + + var source2 = """ + class C { } + """; + + // from source + var comp = CreateCompilation(new[] { source1, source2 }); + comp.VerifyDiagnostics(); + var sourceMember = comp.GetMembers("C")[0]; + Assert.Equal("<>F0__C", sourceMember.MetadataName); + + var sourceType = comp.GetTypeByMetadataName("<>F0__C"); + Assert.Equal(sourceMember, sourceType); + + // from metadata + var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + + var metadataMember = comp2.GetMember("<>F0__C"); + Assert.Equal("<>F0__C", metadataMember.MetadataName); + + var metadataType = comp2.GetTypeByMetadataName("<>F0__C"); + Assert.Equal(metadataMember, metadataType); + } + + [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(); + + var sourceType = comp.GetTypeByMetadataName("<>F0__C"); + Assert.Null(sourceType); + + var types = comp.GetTypesByMetadataName("<>F0__C"); + Assert.Equal(2, types.Length); + Assert.Equal(firstIsMetadataReference ? "C@" : "<>F0__C", types[0].ToTestDisplayString()); + Assert.Equal(secondIsMetadataReference ? "C@" : "<>F0__C", types[1].ToTestDisplayString()); + Assert.NotEqual(types[0], types[1]); + } + + [Fact] + public void GetTypeByMetadataName_06() + { + var source1 = """ + file class C { } + file class C { } + """; + + var comp = CreateCompilation(source1); + comp.VerifyDiagnostics( + // (2,12): error CS0101: The namespace '' already contains a definition for 'C' + // file class C { } + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C").WithArguments("C", "").WithLocation(2, 12)); + + var sourceType = ((Compilation)comp).GetTypeByMetadataName("<>F0__C"); + Assert.Equal("C@", sourceType.ToTestDisplayString()); + + var types = comp.GetTypesByMetadataName("<>F0__C"); + Assert.Equal(1, types.Length); + Assert.Same(sourceType, types[0]); + } + + [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(); + + Assert.Null(comp.GetTypeByMetadataName("<>F0__C")); + Assert.Empty(comp.GetTypesByMetadataName("<>F0__C")); + + Assert.Null(comp.GetTypeByMetadataName("F0__C")); + Assert.Empty(comp.GetTypesByMetadataName("F0__C")); + + var sourceType = ((Compilation)comp).GetTypeByMetadataName("F0__C"); + Assert.Equal("C@SomeFile", sourceType.ToTestDisplayString()); + + var types = comp.GetTypesByMetadataName("F0__C"); + Assert.Equal(1, types.Length); + Assert.Same(sourceType, types[0]); + } + + [Fact] + public void AssociatedSyntaxTree_01() + { + var source = """ + file 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().Single(); + var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; + Assert.Equal("C@", type.ToTestDisplayString()); + Assert.Equal(tree, type.GetSymbol()!.AssociatedSyntaxTree); + Assert.True(type.IsFile); + + var referencingMetadataComp = CreateCompilation("", new[] { comp.ToMetadataReference() }); + type = ((Compilation)referencingMetadataComp).GetTypeByMetadataName("<>F0__C")!; + Assert.Equal("C@", type.ToTestDisplayString()); + Assert.Equal(tree, type.GetSymbol()!.AssociatedSyntaxTree); + Assert.True(type.IsFile); + + var referencingImageComp = CreateCompilation("", new[] { comp.EmitToImageReference() }); + type = ((Compilation)referencingImageComp).GetTypeByMetadataName("<>F0__C")!; + Assert.Equal("<>F0__C", type.ToTestDisplayString()); + Assert.Null(type.GetSymbol()!.AssociatedSyntaxTree); + Assert.False(type.IsFile); + } + + [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().Single(); + var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; + Assert.Equal("C", type.ToTestDisplayString()); + Assert.Null(type.GetSymbol()!.AssociatedSyntaxTree); + Assert.False(type.IsFile); + } + + [Fact] + public void AssociatedSyntaxTree_03() + { + var source = """ + file 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().Single(); + var type = (INamedTypeSymbol)model.GetTypeInfo(node.Type!).Type!; + Assert.Equal("C@", type.ToTestDisplayString()); + Assert.Equal(tree, type.GetSymbol()!.AssociatedSyntaxTree); + Assert.True(type.IsFile); + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs new file mode 100644 index 0000000000000..4526b59797ae9 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs @@ -0,0 +1,2975 @@ +// 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. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public class FileModifierParsingTests : ParsingTests +{ + public FileModifierParsingTests(ITestOutputHelper output) : base(output) { } + + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) + { + return SyntaxFactory.ParseSyntaxTree(text, options ?? TestOptions.Regular); + } + + private void UsingNode(string text, params DiagnosticDescription[] expectedDiagnostics) + { + UsingNode(text, options: null, expectedParsingDiagnostics: expectedDiagnostics); + } + + private new void UsingNode(string text, CSharpParseOptions? options, params DiagnosticDescription[] expectedDiagnostics) + { + UsingNode(text, options, expectedParsingDiagnostics: expectedDiagnostics); + } + + private void UsingNode(string text, CSharpParseOptions? options = null, DiagnosticDescription[]? expectedParsingDiagnostics = null, DiagnosticDescription[]? expectedBindingDiagnostics = null) + { + options ??= TestOptions.RegularPreview; + expectedParsingDiagnostics ??= Array.Empty(); + expectedBindingDiagnostics ??= expectedParsingDiagnostics; + + var tree = UsingTree(text, options); + Validate(text, (CSharpSyntaxNode)tree.GetRoot(), expectedParsingDiagnostics); + + var comp = CreateCompilation(tree); + comp.VerifyDiagnostics(expectedBindingDiagnostics); + } + + [Theory] + [InlineData(SyntaxKind.ClassKeyword)] + [InlineData(SyntaxKind.StructKeyword)] + [InlineData(SyntaxKind.InterfaceKeyword)] + [InlineData(SyntaxKind.RecordKeyword)] + [InlineData(SyntaxKind.EnumKeyword)] + public void FileModifier_01(SyntaxKind typeKeyword) + { + UsingNode($$""" + file {{SyntaxFacts.GetText(typeKeyword)}} C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(SyntaxKind.FileKeyword); + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.ClassKeyword)] + [InlineData(SyntaxKind.StructKeyword)] + [InlineData(SyntaxKind.InterfaceKeyword)] + [InlineData(SyntaxKind.RecordKeyword)] + public void FileModifier_02(SyntaxKind typeKeyword) + { + UsingNode($$""" + file partial {{SyntaxFacts.GetText(typeKeyword)}} C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PartialKeyword); + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_02_Enum() + { + UsingNode($$""" + file partial enum C { } + """, + expectedParsingDiagnostics: new[] + { + // (1,6): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // file partial enum C { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(1, 6) + }, + // note: we also get duplicate ERR_PartialMisplaced diagnostics on `partial enum C { }`. + expectedBindingDiagnostics: new[] + { + // (1,6): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // file partial enum C { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(1, 6), + // (1,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // file partial enum C { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "C").WithLocation(1, 19) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.EnumDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EnumKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.ClassKeyword)] + [InlineData(SyntaxKind.StructKeyword)] + [InlineData(SyntaxKind.InterfaceKeyword)] + public void FileModifier_03(SyntaxKind typeKeyword) + { + UsingNode($$""" + partial file {{SyntaxFacts.GetText(typeKeyword)}} C { } + """, + expectedParsingDiagnostics: new[] + { + // (1,14): error CS1002: ; expected + // partial file {{SyntaxFacts.GetText(typeKeyword)}} C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, SyntaxFacts.GetText(typeKeyword)).WithLocation(1, 14) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial file interface C { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(1, 1), + // (1,9): warning CS0168: The variable 'file' is declared but never used + // partial file interface C { } + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(1, 9), + // (1,14): error CS1002: ; expected + // partial file {{SyntaxFacts.GetText(typeKeyword)}} C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, SyntaxFacts.GetText(typeKeyword)).WithLocation(1, 14) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.RecordKeyword)] + public void FileModifier_04(SyntaxKind typeKeyword) + { + UsingNode($$""" + partial file {{SyntaxFacts.GetText(typeKeyword)}} C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.FileKeyword); + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_05() + { + UsingNode($$""" + file partial record struct C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.RecordStructDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_06() + { + UsingNode($$""" + partial file record struct C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.RecordStructDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.FileKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_07_CSharp10() + { + UsingNode($$""" + file partial ref struct C { } + """, + options: TestOptions.Regular10, + expectedParsingDiagnostics: new[] + { + // (1,14): error CS1003: Syntax error, ',' expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(1, 1), + // (1,6): warning CS0168: The variable 'partial' is declared but never used + // file partial ref struct C { } + Diagnostic(ErrorCode.WRN_UnreferencedVar, "partial").WithArguments("partial").WithLocation(1, 6), + // (1,14): error CS1003: Syntax error, ',' expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_07() + { + UsingNode($$""" + file partial ref struct C { } + """, + expectedParsingDiagnostics: new[] + { + // (1,14): error CS1003: Syntax error, ',' expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(1, 1), + // (1,6): warning CS0168: The variable 'partial' is declared but never used + // file partial ref struct C { } + Diagnostic(ErrorCode.WRN_UnreferencedVar, "partial").WithArguments("partial").WithLocation(1, 6), + // (1,14): error CS1003: Syntax error, ',' expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // file partial ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_08() + { + UsingNode($$""" + partial file ref struct C { } + """, + expectedParsingDiagnostics: new[] + { + // (1,14): error CS1003: Syntax error, ',' expected + // partial file ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // partial file ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial file ref struct C { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(1, 1), + // (1,9): warning CS0168: The variable 'file' is declared but never used + // partial file ref struct C { } + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(1, 9), + // (1,14): error CS1003: Syntax error, ',' expected + // partial file ref struct C { } + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 14), + // (1,18): error CS1002: ; expected + // partial file ref struct C { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 18) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_09() + { + UsingNode($$""" + file abstract class C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.AbstractKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_10() + { + UsingNode($$""" + abstract file class C { } + """); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.AbstractKeyword); + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.ClassKeyword)] + [InlineData(SyntaxKind.StructKeyword)] + [InlineData(SyntaxKind.InterfaceKeyword)] + [InlineData(SyntaxKind.RecordKeyword)] + [InlineData(SyntaxKind.EnumKeyword)] + public void FileModifier_11(SyntaxKind typeKeyword) + { + UsingNode($$""" + public file {{SyntaxFacts.GetText(typeKeyword)}} C { } + """, + expectedBindingDiagnostics: new[] + { + // (1,20): error CS9052: File type 'C' cannot use accessibility modifiers. + // public file {{SyntaxFacts.GetText(typeKeyword)}} C { } + Diagnostic(ErrorCode.ERR_FileTypeNoExplicitAccessibility, "C").WithArguments("C") + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.FileKeyword); + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.ClassKeyword)] + [InlineData(SyntaxKind.StructKeyword)] + [InlineData(SyntaxKind.InterfaceKeyword)] + [InlineData(SyntaxKind.RecordKeyword)] + [InlineData(SyntaxKind.EnumKeyword)] + public void FileModifier_12(SyntaxKind typeKeyword) + { + UsingNode($$""" + file public {{SyntaxFacts.GetText(typeKeyword)}} C { } + """, + expectedBindingDiagnostics: new[] + { + // (1,19): error CS9052: File type 'C' cannot use accessibility modifiers. + // file public {{SyntaxFacts.GetText(typeKeyword)}} C { } + Diagnostic(ErrorCode.ERR_FileTypeNoExplicitAccessibility, "C").WithArguments("C") + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxFacts.GetBaseTypeDeclarationKind(typeKeyword)); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PublicKeyword); + N(typeKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_13() + { + UsingNode(""" + file class C { } + """, + options: TestOptions.Regular10, + expectedBindingDiagnostics: new[] + { + // (1,12): error CS8652: The feature 'file types' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // file class C { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").WithLocation(1, 12) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_14() + { + UsingNode(""" + file delegate void D(); + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.DelegateDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.DelegateKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_15() + { + UsingNode(""" + namespace NS + { + file class C { } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_16() + { + UsingNode(""" + namespace NS; + file class C { } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_17() + { + UsingNode(""" + class Outer + { + file class C { } + } + """, + expectedBindingDiagnostics: new[] + { + // (3,16): error CS9054: File 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) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Outer"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileModifier_18() + { + UsingNode(""" + class C + { + file delegate* M(); + } + """, + expectedBindingDiagnostics: new[] + { + // (3,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // file delegate* M(); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(3, 10), + // (3,31): error CS0106: The modifier 'file' is not valid for this item + // file delegate* M(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("file").WithLocation(3, 31), + // (3,31): error CS0501: 'C.M()' must declare a body because it is not marked abstract, extern, or partial + // file delegate* M(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M").WithArguments("C.M()").WithLocation(3, 31) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.FunctionPointerType); + { + N(SyntaxKind.DelegateKeyword); + N(SyntaxKind.AsteriskToken); + N(SyntaxKind.FunctionPointerParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.FunctionPointerParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.FunctionPointerParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileMember_01() + { + UsingNode(""" + class C + { + file void M() { } + } + """, + expectedBindingDiagnostics: new[] + { + // (3,15): error CS0106: The modifier 'file' is not valid for this item + // file void M() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("file").WithLocation(3, 15) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileMember_02() + { + UsingNode(""" + class C + { + file int x; + } + """, + expectedBindingDiagnostics: new[] + { + // (3,14): error CS0106: The modifier 'file' is not valid for this item + // file int x; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "x").WithArguments("file").WithLocation(3, 14), + // (3,14): warning CS0169: The field 'C.x' is never used + // file int x; + Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("C.x").WithLocation(3, 14) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileMember_03() + { + UsingNode($$""" + class C + { + file event Action x; + } + """, + expectedBindingDiagnostics: new[] + { + // (3,16): error CS0246: The type or namespace name 'Action' could not be found (are you missing a using directive or an assembly reference?) + // file event Action x; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Action").WithArguments("Action").WithLocation(3, 16), + // (3,23): error CS0106: The modifier 'file' is not valid for this item + // file event Action x; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "x").WithArguments("file").WithLocation(3, 23), + // (3,23): warning CS0067: The event 'C.x' is never used + // file event Action x; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "x").WithArguments("C.x").WithLocation(3, 23) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileMember_04() + { + var source = $$""" + class C + { + file int x { get; set; } + } + """; + + UsingNode(source, expectedBindingDiagnostics: new[] + { + // (3,14): error CS0106: The modifier 'file' is not valid for this item + // file int x { get; set; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "x").WithArguments("file").WithLocation(3, 14) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileMember_05() + { + var source = $$""" + class C + { + async file void M() { } + } + """; + + UsingNode(source, expectedBindingDiagnostics: new[] + { + // (3,21): error CS0106: The modifier 'file' is not valid for this item + // async file void M() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("file").WithLocation(3, 21), + // (3,21): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async file void M() { } + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(3, 21) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.FileKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_01() + { + UsingNode($$""" + class C + { + int file; + } + """, expectedBindingDiagnostics: new[] + { + // (3,9): warning CS0169: The field 'C.file' is never used + // int file; + Diagnostic(ErrorCode.WRN_UnreferencedField, "file").WithArguments("C.file").WithLocation(3, 9) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_02() + { + UsingNode($$""" + class C + { + int file { get; set; } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_03() + { + UsingNode($$""" + class C + { + event Action file; + } + """, + expectedBindingDiagnostics: new[] + { + // (3,11): error CS0246: The type or namespace name 'Action' could not be found (are you missing a using directive or an assembly reference?) + // event Action file; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Action").WithArguments("Action").WithLocation(3, 11), + // (3,18): warning CS0067: The event 'C.file' is never used + // event Action file; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "file").WithArguments("C.file").WithLocation(3, 18) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_04() + { + UsingNode($$""" + class C + { + void file() { } + } + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_05() + { + UsingNode($$""" + file class file { } + """, + expectedBindingDiagnostics: new[] + { + // (1,12): error CS9056: Types and aliases cannot be named 'file'. + // file class file { } + Diagnostic(ErrorCode.ERR_FileTypeNameDisallowed, "file").WithLocation(1, 12) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_06_CSharp10() + { + UsingNode($$""" + class C + { + file async; + } + """, + options: TestOptions.Regular10, + expectedBindingDiagnostics: new[] + { + // (3,5): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file async; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 5), + // (3,10): warning CS0169: The field 'C.async' is never used + // file async; + Diagnostic(ErrorCode.WRN_UnreferencedField, "async").WithArguments("C.async").WithLocation(3, 10) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "async"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_06() + { + UsingNode($$""" + class C + { + file async; + } + """, + // (3,15): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // file async; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 15), + // (3,15): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // file async; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "async"); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_07_CSharp10() + { + UsingNode($$""" + class C + { + file item; + } + """, + options: TestOptions.Regular10, + expectedBindingDiagnostics: new[] + { + // (3,5): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file item; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 5), + // (3,10): warning CS0169: The field 'C.item' is never used + // file item; + Diagnostic(ErrorCode.WRN_UnreferencedField, "item").WithArguments("C.item").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "item"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_07() + { + UsingNode($$""" + class C + { + file item; + } + """, + expectedParsingDiagnostics: new[] + { + // (3,14): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // file item; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 14), + // (3,14): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // file item; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 14) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "item"); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MemberNamedFile_08() + { + UsingNode($$""" + record file { } + """, + expectedBindingDiagnostics: new[] + { + // (1,8): error CS9056: Types and aliases cannot be named 'file'. + // record file { } + Diagnostic(ErrorCode.ERR_FileTypeNameDisallowed, "file").WithLocation(1, 8) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TypeNamedFile_01() + { + UsingNode($$""" + class file { } + """, + expectedBindingDiagnostics: new[] + { + // (1,7): error CS9056: Types and aliases cannot be named 'file'. + // class file { } + Diagnostic(ErrorCode.ERR_FileTypeNameDisallowed, "file").WithLocation(1, 7) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TypeNamedFile_01_CSharp10() + { + UsingNode($$""" + class file { } + """, + options: TestOptions.Regular10, + expectedBindingDiagnostics: new[] + { + // (1,7): warning CS8981: The type name 'file' only contains lower-cased ascii characters. Such names may become reserved for the language. + // class file { } + Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "file").WithArguments("file").WithLocation(1, 7) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "file"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void TypeNamedFile_02(LanguageVersion languageVersion) + { + UsingNode($$""" + class @file { } + """, + options: TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "@file"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_01_CSharp10() + { + UsingNode($$""" + file + """, + options: TestOptions.Regular10, + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_01() + { + UsingNode($$""" + file + """, + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_02_CSharp10() + { + UsingNode($$""" + file; + """, + options: TestOptions.Regular10, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0103: The name 'file' does not exist in the current context + // file; + Diagnostic(ErrorCode.ERR_NameNotInContext, "file").WithArguments("file").WithLocation(1, 1), + // (1,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // file; + Diagnostic(ErrorCode.ERR_IllegalStatement, "file").WithLocation(1, 1) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_02() + { + UsingNode($$""" + file; + """, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0103: The name 'file' does not exist in the current context + // file; + Diagnostic(ErrorCode.ERR_NameNotInContext, "file").WithArguments("file").WithLocation(1, 1), + // (1,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // file; + Diagnostic(ErrorCode.ERR_IllegalStatement, "file").WithLocation(1, 1) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_03_CSharp10() + { + UsingNode($$""" + file namespace NS; + """, + options: TestOptions.Regular10, + expectedParsingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1), + // (1,16): error CS8956: File-scoped namespace must precede all other members in a file. + // file namespace NS; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "NS").WithLocation(1, 16) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_03() + { + UsingNode($$""" + file namespace NS; + """, + expectedParsingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1) + }, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1), + // (1,16): error CS8956: File-scoped namespace must precede all other members in a file. + // file namespace NS; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "NS").WithLocation(1, 16) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_04_CSharp10() + { + UsingNode($$""" + file namespace NS { } + """, + options: TestOptions.Regular10, + expectedParsingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS { } + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Errors_04() + { + UsingNode($$""" + file namespace NS { } + """, + expectedParsingDiagnostics: new[] + { + // (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // file namespace NS { } + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 1) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "NS"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void File_Repeated() + { + const int FileModifiersCount = 100000; + var manyFileModifiers = string.Join(" ", Enumerable.Repeat("file", FileModifiersCount)); + UsingNode(manyFileModifiers, + expectedParsingDiagnostics: new[] + { + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "file").WithLocation(1, 499996) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + for (var i = 0; i < FileModifiersCount - 1; i++) + { + N(SyntaxKind.FileKeyword); + } + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.EndOfFileToken); + } + } + EOF(); + + UsingNode(manyFileModifiers + " class { }", + expectedParsingDiagnostics: new[] + { + Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(1, 500007) + }, + expectedBindingDiagnostics: new[] + { + Diagnostic(ErrorCode.ERR_DuplicateModifier, "file").WithArguments("file").WithLocation(1, 6), + Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(1, 500007) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + for (int i = 0; i < FileModifiersCount; i++) + { + N(SyntaxKind.FileKeyword); + } + N(SyntaxKind.ClassKeyword); + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MethodNamedRecord_01_CSharp8() + { + UsingNode(""" + class C + { + file record(); + } + """, + options: TestOptions.Regular8, + expectedBindingDiagnostics: new[] + { + // (3,5): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file record(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 5), + // (3,10): error CS0501: 'C.record()' must declare a body because it is not marked abstract, extern, or partial + // file record(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "record").WithArguments("C.record()").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.IdentifierToken, "record"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MethodNamedRecord_01_CSharpNext() + { + UsingNode(""" + class C + { + file record(); + } + """, + expectedBindingDiagnostics: new[] + { + // (3,10): error CS0106: The modifier 'file' is not valid for this item + // file record(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "record").WithArguments("file").WithLocation(3, 10), + // (3,10): error CS1520: Method must have a return type + // file record(); + Diagnostic(ErrorCode.ERR_MemberNeedsType, "record").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierToken, "record"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MethodNamedRecord_02_CSharp8() + { + UsingNode(""" + class C + { + file record() { } + } + """, + options: TestOptions.Regular8, + expectedBindingDiagnostics: new[] + { + // (3,5): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file record() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 5), + // (3,10): error CS0161: 'C.record()': not all code paths return a value + // file record() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "record").WithArguments("C.record()").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.IdentifierToken, "record"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MethodNamedRecord_02_CSharpNext() + { + UsingNode(""" + class C + { + file record() { } + } + """, expectedBindingDiagnostics: new[] + { + // (3,10): error CS0106: The modifier 'file' is not valid for this item + // file record() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "record").WithArguments("file").WithLocation(3, 10), + // (3,10): error CS1520: Method must have a return type + // file record() { } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "record").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierToken, "record"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_01_CSharp8() + { + UsingNode(""" + class C + { + file record X(); + } + """, + options: TestOptions.Regular8, + expectedBindingDiagnostics: new[] + { + // (3,10): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // file record X(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(3, 10), + // (3,17): error CS0106: The modifier 'file' is not valid for this item + // file record X(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "X").WithArguments("file").WithLocation(3, 17), + // (3,17): error CS0501: 'C.X()' must declare a body because it is not marked abstract, extern, or partial + // file record X(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "X").WithArguments("C.X()").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_01_CSharpNext() + { + UsingNode(""" + class C + { + file record X(); + } + """, + expectedBindingDiagnostics: new[] + { + // (3,17): error CS9054: File type 'C.X' must be defined in a top level type; 'C.X' is a nested type. + // file record X(); + Diagnostic(ErrorCode.ERR_FileTypeNested, "X").WithArguments("C.X").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_02_CSharp8() + { + UsingNode(""" + class C + { + file record X() { } + } + """, + options: TestOptions.Regular8, + expectedBindingDiagnostics: new[] + { + // (3,10): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // file record X() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(3, 10), + // (3,17): error CS0106: The modifier 'file' is not valid for this item + // file record X() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "X").WithArguments("file").WithLocation(3, 17), + // (3,17): error CS0161: 'C.X()': not all code paths return a value + // file record X() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "X").WithArguments("C.X()").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_02_CSharpNext() + { + UsingNode(""" + class C + { + file record X() { } + } + """, + expectedBindingDiagnostics: new[] + { + // (3,17): error CS9054: File type 'C.X' must be defined in a top level type; 'C.X' is a nested type. + // file record X() { } + Diagnostic(ErrorCode.ERR_FileTypeNested, "X").WithArguments("C.X").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_03_CSharp8() + { + UsingNode(""" + class C + { + file record X; + } + """, + options: TestOptions.Regular8, expectedBindingDiagnostics: new[] + { + // (3,10): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // file record X; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(3, 10), + // (3,17): error CS0106: The modifier 'file' is not valid for this item + // file record X; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "X").WithArguments("file").WithLocation(3, 17), + // (3,17): warning CS0169: The field 'C.X' is never used + // file record X; + Diagnostic(ErrorCode.WRN_UnreferencedField, "X").WithArguments("C.X").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_03_CSharpNext() + { + UsingNode(""" + class C + { + file record X; + } + """, + expectedBindingDiagnostics: new[] + { + // (3,17): error CS9054: File type 'C.X' must be defined in a top level type; 'C.X' is a nested type. + // file record X; + Diagnostic(ErrorCode.ERR_FileTypeNested, "X").WithArguments("C.X").WithLocation(3, 17) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileRecord_04_CSharpNext() + { + UsingNode(""" + file record X(); + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.FileKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LocalVariable_01() + { + UsingNode(""" + void M() + { + file file; + } + """, + expectedBindingDiagnostics: new[] + { + // (1,6): warning CS8321: The local function 'M' is declared but never used + // void M() + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(1, 6), + // (3,5): error CS0118: 'file' is a variable but is used like a type + // file file; + Diagnostic(ErrorCode.ERR_BadSKknown, "file").WithArguments("file", "variable", "type").WithLocation(3, 5), + // (3,10): warning CS0168: The variable 'file' is declared but never used + // file file; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(3, 10) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LocalVariable_02() + { + UsingNode(""" + void M() + { + int file; + } + """, + expectedBindingDiagnostics: new[] + { + // (1,6): warning CS8321: The local function 'M' is declared but never used + // void M() + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(1, 6), + // (3,9): warning CS0168: The variable 'file' is declared but never used + // int file; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(3, 9) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void TopLevelVariable_01(LanguageVersion languageVersion) + { + UsingNode(""" + file file; + """, + options: TestOptions.Regular.WithLanguageVersion(languageVersion), + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0118: 'file' is a variable but is used like a type + // file file; + Diagnostic(ErrorCode.ERR_BadSKknown, "file").WithArguments("file", "variable", "type").WithLocation(1, 1), + // (1,6): warning CS0168: The variable 'file' is declared but never used + // file file; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(1, 6) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void TopLevelVariable_02(LanguageVersion languageVersion) + { + UsingNode(""" + int file; + """, + options: TestOptions.Regular.WithLanguageVersion(languageVersion), + expectedBindingDiagnostics: new[] + { + // (1,5): warning CS0168: The variable 'file' is declared but never used + // int file; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "file").WithArguments("file").WithLocation(1, 5) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void TopLevelVariable_03(LanguageVersion languageVersion) + { + UsingNode(""" + bool file; + file = true; + """, + options: TestOptions.Regular.WithLanguageVersion(languageVersion), + expectedBindingDiagnostics: new[] + { + // (1,6): warning CS0219: The variable 'file' is assigned but its value is never used + // bool file; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "file").WithArguments("file").WithLocation(1, 6) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.BoolKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Variable_01() + { + UsingNode(""" + void M() + { + bool file; + file = true; + } + """, + expectedBindingDiagnostics: new[] + { + // (1,6): warning CS8321: The local function 'M' is declared but never used + // void M() + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(1, 6), + // (3,10): warning CS0219: The variable 'file' is assigned but its value is never used + // bool file; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "file").WithArguments("file").WithLocation(3, 10) + }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.BoolKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "file"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LambdaReturn() + { + UsingNode(""" + _ = file () => { }; + """, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS8183: Cannot infer the type of implicitly-typed discard. + // _ = file () => { }; + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(1, 1), + // (1,5): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // _ = file () => { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(1, 5) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LocalFunctionReturn() + { + UsingNode(""" + file local() { }; + """, + expectedBindingDiagnostics: new[] + { + // (1,1): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // file local() { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(1, 1), + // (1,6): error CS0161: 'local()': not all code paths return a value + // file local() { }; + Diagnostic(ErrorCode.ERR_ReturnExpected, "local").WithArguments("local()").WithLocation(1, 6), + // (1,6): warning CS8321: The local function 'local' is declared but never used + // file local() { }; + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(1, 6) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.IdentifierToken, "local"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ParameterModifier() + { + UsingNode(""" + class C + { + void M(file int x) { } + } + """, + expectedParsingDiagnostics: new[] + { + // (3,17): error CS1001: Identifier expected + // void M(file int x) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(3, 17), + // (3,17): error CS1003: Syntax error, ',' expected + // void M(file int x) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(3, 17) + }, + expectedBindingDiagnostics: new[] + { + // (3,12): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // void M(file int x) { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 12), + // (3,17): error CS1001: Identifier expected + // void M(file int x) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(3, 17), + // (3,17): error CS1003: Syntax error, ',' expected + // void M(file int x) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(3, 17) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ParameterType() + { + UsingNode(""" + class C + { + void M(file x) { } + } + """, + expectedBindingDiagnostics: new[] + { + // (3,12): error CS0246: The type or namespace name 'file' could not be found (are you missing a using directive or an assembly reference?) + // void M(file x) { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "file").WithArguments("file").WithLocation(3, 12) + }); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "file"); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } +} diff --git a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs index b0cb635e5360a..084f15f4153f2 100644 --- a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs +++ b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs @@ -68,7 +68,7 @@ public ImmutableDictionary GetMethodsByName() } private static readonly SymbolDisplayFormat _testDataKeyFormat = new SymbolDisplayFormat( - compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | SymbolDisplayCompilerInternalOptions.UseValueTuple, + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | SymbolDisplayCompilerInternalOptions.UseValueTuple | SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes, globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index e65375f0206bf..6d97be32cc7ab 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -596,6 +596,8 @@ public TypeDefinitionHandle TypeDef public bool MangleName => false; + public string? AssociatedFileIdentifier => null; + public virtual ushort Alignment => 0; public virtual Cci.ITypeReference GetBaseClass(EmitContext context) diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index e26ebb058b82b..bd9687d74c9a2 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -691,7 +691,7 @@ protected bool ContainsTopLevelType(string fullEmittedName) static void AddTopLevelType(HashSet names, Cci.INamespaceTypeDefinition type) // _namesOfTopLevelTypes are only used to generated exported types, which are not emitted in EnC deltas (hence generation 0): - => names?.Add(MetadataHelpers.BuildQualifiedName(type.NamespaceName, Cci.MetadataWriter.GetMangledName(type, generation: 0))); + => names?.Add(MetadataHelpers.BuildQualifiedName(type.NamespaceName, Cci.MetadataWriter.GetMetadataName(type, generation: 0))); } public virtual ImmutableArray GetAdditionalTopLevelTypes() diff --git a/src/Compilers/Core/Portable/Emit/ErrorType.cs b/src/Compilers/Core/Portable/Emit/ErrorType.cs index 79a43e38fc187..83d61eb70c3c3 100644 --- a/src/Compilers/Core/Portable/Emit/ErrorType.cs +++ b/src/Compilers/Core/Portable/Emit/ErrorType.cs @@ -55,6 +55,16 @@ bool Cci.INamedTypeReference.MangleName } } +#nullable enable + string? Cci.INamedTypeReference.AssociatedFileIdentifier + { + get + { + return null; + } + } +#nullable disable + bool Cci.ITypeReference.IsEnum { get diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs index c5bf550d98e2a..2ab12dba66c50 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs @@ -675,6 +675,16 @@ bool Cci.INamedTypeReference.MangleName } } +#nullable enable + string? Cci.INamedTypeReference.AssociatedFileIdentifier + { + get + { + return UnderlyingNamedType.AssociatedFileIdentifier; + } + } +#nullable disable + string Cci.INamedEntity.Name { get diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs index 03cfc874b2910..7a042ab03c067 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs @@ -470,10 +470,12 @@ internal static string GetAritySuffix(int arity) return (arity <= 9) ? s_aritySuffixesOneToNine[arity - 1] : string.Concat(GenericTypeNameManglingString, arity.ToString(CultureInfo.InvariantCulture)); } - internal static string ComposeAritySuffixedMetadataName(string name, int arity) +#nullable enable + internal static string ComposeAritySuffixedMetadataName(string name, int arity, string? associatedFileIdentifier) { - return arity == 0 ? name : name + GetAritySuffix(arity); + return associatedFileIdentifier + (arity == 0 ? name : name + GetAritySuffix(arity)); } +#nullable disable internal static int InferTypeArityFromMetadataName(string emittedTypeName) { diff --git a/src/Compilers/Core/Portable/NativePdbWriter/SymWriterMetadataProvider.cs b/src/Compilers/Core/Portable/NativePdbWriter/SymWriterMetadataProvider.cs index bcd9b1cc00509..e306f811888c4 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/SymWriterMetadataProvider.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/SymWriterMetadataProvider.cs @@ -45,7 +45,7 @@ public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespa else { int generation = (t is INamedTypeDefinition namedType) ? _writer.Module.GetTypeDefinitionGeneration(namedType) : 0; - typeName = MetadataWriter.GetMangledName((INamedTypeReference)t, generation); + typeName = MetadataWriter.GetMetadataName((INamedTypeReference)t, generation); INamespaceTypeDefinition namespaceTypeDef; if ((namespaceTypeDef = t.AsNamespaceTypeDefinition(_writer.Context)) != null) diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 9b0743613eb5a..f952948663bbf 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -916,13 +916,13 @@ private static uint GetManagedResourceOffset(BlobBuilder resource, BlobBuilder r return (uint)result; } - public static string GetMangledName(INamedTypeReference namedType, int generation) + public static string GetMetadataName(INamedTypeReference namedType, int generation) { - string unmangledName = (generation == 0) ? namedType.Name : namedType.Name + "#" + generation; - - return namedType.MangleName - ? MetadataHelpers.ComposeAritySuffixedMetadataName(unmangledName, namedType.GenericParameterCount) - : unmangledName; + string nameWithGeneration = (generation == 0) ? namedType.Name : namedType.Name + "#" + generation; + string fileIdentifier = namedType.AssociatedFileIdentifier; + return namedType.MangleName || fileIdentifier != null + ? MetadataHelpers.ComposeAritySuffixedMetadataName(nameWithGeneration, namedType.GenericParameterCount, fileIdentifier) + : nameWithGeneration; } internal MemberReferenceHandle GetMemberReferenceHandle(ITypeMemberReference memberRef) @@ -2219,10 +2219,10 @@ private void PopulateExportedTypeTableRows() if ((namespaceTypeRef = exportedType.Type.AsNamespaceTypeReference) != null) { // exported types are not emitted in EnC deltas (hence generation 0): - string mangledTypeName = GetMangledName(namespaceTypeRef, generation: 0); + string metadataTypeName = GetMetadataName(namespaceTypeRef, generation: 0); - typeName = GetStringHandleForNameAndCheckLength(mangledTypeName, namespaceTypeRef); - typeNamespace = GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, mangledTypeName); + typeName = GetStringHandleForNameAndCheckLength(metadataTypeName, namespaceTypeRef); + typeNamespace = GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, metadataTypeName); implementation = GetExportedTypeImplementation(namespaceTypeRef); attributes = exportedType.IsForwarder ? TypeAttributes.NotPublic | Constants.TypeAttributes_TypeForwarder : TypeAttributes.Public; } @@ -2231,9 +2231,9 @@ private void PopulateExportedTypeTableRows() Debug.Assert(exportedType.ParentIndex != -1); // exported types are not emitted in EnC deltas (hence generation 0): - string mangledTypeName = GetMangledName(nestedRef, generation: 0); + string metadataTypeName = GetMetadataName(nestedRef, generation: 0); - typeName = GetStringHandleForNameAndCheckLength(mangledTypeName, nestedRef); + typeName = GetStringHandleForNameAndCheckLength(metadataTypeName, nestedRef); typeNamespace = default(StringHandle); implementation = MetadataTokens.ExportedTypeHandle(exportedType.ParentIndex + 1); attributes = exportedType.IsForwarder ? TypeAttributes.NotPublic : TypeAttributes.NestedPublic; @@ -2710,13 +2710,13 @@ private void PopulateTypeDefTableRows() var moduleBuilder = Context.Module; int generation = moduleBuilder.GetTypeDefinitionGeneration(typeDef); - string mangledTypeName = GetMangledName(typeDef, generation); + string metadataTypeName = GetMetadataName(typeDef, generation); ITypeReference baseType = typeDef.GetBaseClass(Context); metadata.AddTypeDefinition( attributes: GetTypeAttributes(typeDef), - @namespace: (namespaceType != null) ? GetStringHandleForNamespaceAndCheckLength(namespaceType, mangledTypeName) : default(StringHandle), - name: GetStringHandleForNameAndCheckLength(mangledTypeName, typeDef), + @namespace: (namespaceType != null) ? GetStringHandleForNamespaceAndCheckLength(namespaceType, metadataTypeName) : default(StringHandle), + name: GetStringHandleForNameAndCheckLength(metadataTypeName, typeDef), baseType: (baseType != null) ? GetTypeHandle(baseType) : default(EntityHandle), fieldList: GetFirstFieldDefinitionHandle(typeDef), methodList: GetFirstMethodDefinitionHandle(typeDef)); @@ -2785,9 +2785,9 @@ private void PopulateTypeRefTableRows() // It's not possible to reference newer versions of reloadable types from another assembly, hence generation 0: // TODO: https://github.com/dotnet/roslyn/issues/54981 - string mangledTypeName = GetMangledName(nestedTypeRef, generation: 0); + string metadataTypeName = GetMetadataName(nestedTypeRef, generation: 0); - name = this.GetStringHandleForNameAndCheckLength(mangledTypeName, nestedTypeRef); + name = this.GetStringHandleForNameAndCheckLength(metadataTypeName, nestedTypeRef); @namespace = default(StringHandle); } else @@ -2802,10 +2802,10 @@ private void PopulateTypeRefTableRows() // It's not possible to reference newer versions of reloadable types from another assembly, hence generation 0: // TODO: https://github.com/dotnet/roslyn/issues/54981 - string mangledTypeName = GetMangledName(namespaceTypeRef, generation: 0); + string metadataTypeName = GetMetadataName(namespaceTypeRef, generation: 0); - name = this.GetStringHandleForNameAndCheckLength(mangledTypeName, namespaceTypeRef); - @namespace = this.GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, mangledTypeName); + name = this.GetStringHandleForNameAndCheckLength(metadataTypeName, namespaceTypeRef); + @namespace = this.GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, metadataTypeName); } metadata.AddTypeReference( diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs index f90f9f573c9e6..dd6391debef0f 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs @@ -58,6 +58,11 @@ public bool MangleName get { return false; } } + public string? AssociatedFileIdentifier + { + get { return null; } + } + public string Name { get { return ""; } diff --git a/src/Compilers/Core/Portable/PEWriter/TypeNameSerializer.cs b/src/Compilers/Core/Portable/PEWriter/TypeNameSerializer.cs index 29115ffdff6a5..8a1639677e959 100644 --- a/src/Compilers/Core/Portable/PEWriter/TypeNameSerializer.cs +++ b/src/Compilers/Core/Portable/PEWriter/TypeNameSerializer.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using System.Text; using System.Diagnostics; +using System.Linq; namespace Microsoft.Cci { @@ -71,7 +72,7 @@ internal static string GetSerializedTypeName(this ITypeReference typeReference, sb.Append('.'); } - sb.Append(GetMangledAndEscapedName(namespaceType)); + sb.Append(GetEscapedMetadataName(namespaceType)); goto done; } @@ -113,7 +114,7 @@ internal static string GetSerializedTypeName(this ITypeReference typeReference, bool nestedTypeIsAssemblyQualified = false; sb.Append(GetSerializedTypeName(nestedType.GetContainingType(context), context, ref nestedTypeIsAssemblyQualified)); sb.Append('+'); - sb.Append(GetMangledAndEscapedName(nestedType)); + sb.Append(GetEscapedMetadataName(nestedType)); goto done; } @@ -193,12 +194,18 @@ private static void AppendAssemblyQualifierIfNecessary(StringBuilder sb, ITypeRe } } - private static string GetMangledAndEscapedName(INamedTypeReference namedType) + private static string GetEscapedMetadataName(INamedTypeReference namedType) { var pooled = PooledStringBuilder.GetInstance(); StringBuilder mangledName = pooled.Builder; const string needsEscaping = "\\[]*.+,& "; + if (namedType.AssociatedFileIdentifier is string fileIdentifier) + { + Debug.Assert(needsEscaping.All(c => !fileIdentifier.Contains(c))); + mangledName.Append(fileIdentifier); + } + foreach (var ch in namedType.Name) { if (needsEscaping.IndexOf(ch) >= 0) diff --git a/src/Compilers/Core/Portable/PEWriter/Types.cs b/src/Compilers/Core/Portable/PEWriter/Types.cs index 97e957df38f1c..2a4e50b50ac52 100644 --- a/src/Compilers/Core/Portable/PEWriter/Types.cs +++ b/src/Compilers/Core/Portable/PEWriter/Types.cs @@ -253,6 +253,9 @@ internal interface INamedTypeReference : ITypeReference, INamedEntity /// If true, the persisted type name is mangled by appending "`n" where n is the number of type parameters, if the number of type parameters is greater than 0. /// bool MangleName { get; } + + /// Indicates that the type is scoped to the file it is declared in. Used as a prefix for the metadata name. + string? AssociatedFileIdentifier { get; } } /// diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index bf7b9db988916..7018e80cb367a 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -111,4 +111,5 @@ virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPointerTyp virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitProperty(Microsoft.CodeAnalysis.IPropertySymbol! symbol, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitRangeVariable(Microsoft.CodeAnalysis.IRangeVariableSymbol! symbol, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitTypeParameter(Microsoft.CodeAnalysis.ITypeParameterSymbol! symbol, TArgument argument) -> TResult -Microsoft.CodeAnalysis.Operations.BinaryOperatorKind.UnsignedRightShift = 25 -> Microsoft.CodeAnalysis.Operations.BinaryOperatorKind \ No newline at end of file +Microsoft.CodeAnalysis.Operations.BinaryOperatorKind.UnsignedRightShift = 25 -> Microsoft.CodeAnalysis.Operations.BinaryOperatorKind +Microsoft.CodeAnalysis.INamedTypeSymbol.IsFile.get -> bool \ No newline at end of file diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs index 14346d9edbf25..d5b83d44d8fbc 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs @@ -72,5 +72,10 @@ internal enum SymbolDisplayCompilerInternalOptions /// Includes the scoped keyword. /// IncludeScoped = 1 << 9, + + /// + /// Display `MyType@File.cs` instead of `MyType`. + /// + IncludeContainingFileForFileTypes = 1 << 10, } } diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs index d8a5dc6aba65e..20f8c6d92d74c 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs @@ -202,7 +202,8 @@ public class SymbolDisplayFormat SymbolDisplayCompilerInternalOptions.IncludeScriptType | SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | SymbolDisplayCompilerInternalOptions.FlagMissingMetadataTypes | - SymbolDisplayCompilerInternalOptions.IncludeCustomModifiers); + SymbolDisplayCompilerInternalOptions.IncludeCustomModifiers | + SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes); /// /// A verbose format for displaying symbols (useful for testing). diff --git a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs index 3ba82acb88738..f8fab6b35decf 100644 --- a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs @@ -56,6 +56,11 @@ public interface INamedTypeSymbol : ITypeSymbol /// bool IsComImport { get; } + /// + /// Indicates the type is declared in source and is only visible in the file it is declared in. + /// + bool IsFile { get; } + /// /// Returns collection of names of members declared within this type. /// diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb index 22f8d32a16009..0ea8e1ba9b275 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb @@ -375,7 +375,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If rightPart.Kind = SyntaxKind.GenericName Then arity = DirectCast(rightPart, GenericNameSyntax).Arity - fullName = MetadataHelpers.ComposeAritySuffixedMetadataName(currDiagName, arity) + fullName = MetadataHelpers.ComposeAritySuffixedMetadataName(currDiagName, arity, associatedFileIdentifier:=Nothing) End If forwardedToAssembly = GetForwardedToAssembly(containingAssembly, fullName, arity, typeSyntax, diagBag) diff --git a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb index bafab6592ced3..30f4422a89728 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb @@ -30,6 +30,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit End Get End Property + Private ReadOnly Property INamedTypeReferenceAssociatedFileIdentifier As String Implements Cci.INamedTypeReference.AssociatedFileIdentifier + Get + Return Nothing + End Get + End Property + Private ReadOnly Property INamedEntityName As String Implements Cci.INamedEntity.Name Get ' CCI automatically handles type suffix, so use Name instead of MetadataName diff --git a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb index 7e5181c4d4a07..c857d34202f5a 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb @@ -758,6 +758,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property INamedTypeReferenceAssociatedFileIdentifier As String Implements INamedTypeReference.AssociatedFileIdentifier + Get + Return Nothing + End Get + End Property + Private ReadOnly Property INamedEntityName As String Implements INamedEntity.Name Get ' CCI automatically adds the arity suffix, so we return Name, not MetadataName here. diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index 449082250f976..a8b856e239863 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -443,7 +443,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit ' exported types are not emitted in EnC deltas (hence generation 0): Dim fullEmittedName As String = MetadataHelpers.BuildQualifiedName( DirectCast(typeReference, Cci.INamespaceTypeReference).NamespaceName, - Cci.MetadataWriter.GetMangledName(DirectCast(typeReference, Cci.INamedTypeReference), generation:=0)) + Cci.MetadataWriter.GetMetadataName(DirectCast(typeReference, Cci.INamedTypeReference), generation:=0)) ' First check against types declared in the primary module If ContainsTopLevelType(fullEmittedName) Then diff --git a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb index 77b23983ca0b2..7d30aafb6182c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb @@ -159,7 +159,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' Therefore it is a good practice to avoid type names with dots. Debug.Assert(Me.IsErrorType OrElse Not (TypeOf Me Is SourceNamedTypeSymbol) OrElse Not Name.Contains("."), "type name contains dots: " + Name) - Return If(MangleName, MetadataHelpers.ComposeAritySuffixedMetadataName(Name, Arity), Name) + Return If(MangleName, MetadataHelpers.ComposeAritySuffixedMetadataName(Name, Arity, associatedFileIdentifier:=Nothing), Name) End Get End Property @@ -1216,6 +1216,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property INamedTypeSymbol_IsFile As Boolean Implements INamedTypeSymbol.IsFile + Get + Return False + End Get + End Property + Private ReadOnly Property INamedTypeSymbol_NativeIntegerUnderlyingType As INamedTypeSymbol Implements INamedTypeSymbol.NativeIntegerUnderlyingType Get Return Nothing diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs index 5c3d6722ef93b..cfbc1cf8ddd02 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs @@ -684,5 +684,151 @@ class C await VerifyItemIsAbsentAsync(markup, "required"); } + + [Fact] + public async Task SuggestFileOnTypes() + { + var markup = $$""" + $$ class C { } + """; + + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task DoNotSuggestFileAfterFile() + { + var markup = $$""" + file $$ + """; + + await VerifyItemIsAbsentAsync(markup, "file"); + } + + [Fact] + public async Task SuggestFileAfterReadonly() + { + // e.g. 'readonly file struct X { }' + var markup = $$""" + readonly $$ + """; + + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task SuggestFileBeforeFileType() + { + var markup = $$""" + $$ + + file class C { } + """; + + // it might seem like we want to prevent 'file file class', + // but it's likely the user is declaring a file type above an existing file type here. + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task SuggestFileBeforeDelegate() + { + var markup = $$""" + $$ delegate + """; + + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task DoNotSuggestFileOnNestedTypes() + { + var markup = $$""" + class Outer + { + $$ class C { } + } + """; + + await VerifyItemIsAbsentAsync(markup, "file"); + } + + [Fact] + public async Task DoNotSuggestFileOnNonTypeMembers() + { + var markup = $$""" + class C + { + $$ + } + """; + + await VerifyItemIsAbsentAsync(markup, "file"); + } + + [Theory] + [InlineData("public")] + [InlineData("internal")] + [InlineData("protected")] + [InlineData("private")] + public async Task DoNotSuggestFileAfterFilteredKeywords(string keyword) + { + var markup = $$""" + {{keyword}} $$ + """; + + await VerifyItemIsAbsentAsync(markup, "file"); + } + + [Theory] + [InlineData("public")] + [InlineData("internal")] + [InlineData("protected")] + [InlineData("private")] + public async Task DoNotSuggestFilteredKeywordsAfterFile(string keyword) + { + var markup = $$""" + file $$ + """; + + await VerifyItemIsAbsentAsync(markup, keyword); + } + + [Fact] + public async Task SuggestFileInFileScopedNamespace() + { + var markup = $$""" + namespace NS; + + $$ + """; + + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task SuggestFileInNamespace() + { + var markup = $$""" + namespace NS + { + $$ + } + """; + + await VerifyItemExistsAsync(markup, "file"); + } + + [Fact] + public async Task SuggestFileAfterClass() + { + var markup = $$""" + file class C { } + + $$ + """; + + await VerifyItemExistsAsync(markup, "file"); + } } } diff --git a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs index 036aecc1b8656..11acb20fafeb1 100644 --- a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs +++ b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs @@ -239,6 +239,98 @@ public void M(List list) ResolveAndVerifySymbolList(members1, members2, comp1); } + [Fact] + public void FileType1() + { + var src1 = @"using System; + +namespace N1.N2 +{ + file class C { } +} +"; + var originalComp = CreateCompilation(src1, assemblyName: "Test"); + var newComp = CreateCompilation(src1, assemblyName: "Test"); + + var originalSymbols = GetSourceSymbols(originalComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + var newSymbols = GetSourceSymbols(newComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + + Assert.Equal(3, originalSymbols.Length); + ResolveAndVerifySymbolList(newSymbols, originalSymbols, originalComp); + } + + [Fact] + public void FileType2() + { + var src1 = @"using System; + +namespace N1.N2 +{ + file class C { } +} +"; + var originalComp = CreateCompilation(src1, assemblyName: "Test"); + var newComp = CreateCompilation(src1, assemblyName: "Test"); + + var originalSymbols = GetSourceSymbols(originalComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + var newSymbols = GetSourceSymbols(newComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + + Assert.Equal(3, originalSymbols.Length); + ResolveAndVerifySymbolList(newSymbols, originalSymbols, originalComp); + } + + [Fact] + public void FileType3() + { + var src1 = @"using System; + +namespace N1.N2 +{ + file class C { } +} +"; + // this should result in two entirely separate file symbols. + // note that the IDE can only distinguish file type symbols with the same name when they have distinct file paths. + // We are OK with this as we will require file types with identical names to have distinct file paths later in the preview. + // See https://github.com/dotnet/roslyn/issues/61999 + var originalComp = CreateCompilation(new[] { SyntaxFactory.ParseSyntaxTree(src1, path: "file1.cs"), SyntaxFactory.ParseSyntaxTree(src1, path: "file2.cs") }, assemblyName: "Test"); + var newComp = CreateCompilation(new[] { SyntaxFactory.ParseSyntaxTree(src1, path: "file1.cs"), SyntaxFactory.ParseSyntaxTree(src1, path: "file2.cs") }, assemblyName: "Test"); + + var originalSymbols = GetSourceSymbols(originalComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + var newSymbols = GetSourceSymbols(newComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + + Assert.Equal(4, originalSymbols.Length); + ResolveAndVerifySymbolList(newSymbols, originalSymbols, originalComp); + } + + [Fact] + public void FileType4() + { + // we should be able to distinguish a file type and non-file type when they have the same source name. + var src1 = SyntaxFactory.ParseSyntaxTree(@"using System; + +namespace N1.N2 +{ + file class C { } +} +", path: "File1.cs"); + + var src2 = SyntaxFactory.ParseSyntaxTree(@" +namespace N1.N2 +{ + class C { } +} +", path: "File2.cs"); + var originalComp = CreateCompilation(new[] { src1, src2 }, assemblyName: "Test"); + var newComp = CreateCompilation(new[] { src1, src2 }, assemblyName: "Test"); + + var originalSymbols = GetSourceSymbols(originalComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + var newSymbols = GetSourceSymbols(newComp, SymbolCategory.DeclaredType | SymbolCategory.DeclaredNamespace).OrderBy(s => s.Name).ToArray(); + + Assert.Equal(4, originalSymbols.Length); + ResolveAndVerifySymbolList(newSymbols, originalSymbols, originalComp); + } + #endregion #region "Change to symbol" diff --git a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyTests.cs b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyTests.cs index c14e7076089f5..0acc20355cab7 100644 --- a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyTests.cs +++ b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyTests.cs @@ -19,6 +19,111 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SymbolId [UseExportProvider] public class SymbolKeyTests { + [Fact] + public async Task FileType_01() + { + var typeSource = @" +file class C1 +{ + public static void M() { } +} +"; + + var workspaceXml = @$" + + + + +{typeSource} + + + +"; + using var workspace = TestWorkspace.Create(workspaceXml); + + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var compilation = await project.GetCompilationAsync(); + var type = compilation.GetTypeByMetadataName("F0__C1"); + Assert.NotNull(type); + var symbolKey = SymbolKey.Create(type); + var resolved = symbolKey.Resolve(compilation).Symbol; + Assert.Same(type, resolved); + } + + [Fact] + public async Task FileType_02() + { + var workspaceXml = $$""" + + + + +file class C +{ + public static void M() { } +} + + +file class C +{ + public static void M() { } +} + + + +"""; + using var workspace = TestWorkspace.Create(workspaceXml); + + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var compilation = await project.GetCompilationAsync(); + + var type = compilation.GetTypeByMetadataName("F1__C"); + Assert.NotNull(type); + var symbolKey = SymbolKey.Create(type); + var resolved = symbolKey.Resolve(compilation).Symbol; + Assert.Same(type, resolved); + + type = compilation.GetTypeByMetadataName("F0__C"); + Assert.NotNull(type); + symbolKey = SymbolKey.Create(type); + resolved = symbolKey.Resolve(compilation).Symbol; + Assert.Same(type, resolved); + } + + [Fact] + public async Task FileType_03() + { + var workspaceXml = $$""" + + + + +file class C +{ + public class Inner { } +} + + + +"""; + using var workspace = TestWorkspace.Create(workspaceXml); + + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var compilation = await project.GetCompilationAsync(); + + var type = compilation.GetTypeByMetadataName("F0__C+Inner"); + Assert.NotNull(type); + var symbolKey = SymbolKey.Create(type); + var resolved = symbolKey.Resolve(compilation).Symbol; + Assert.Same(type, resolved); + } + [Fact, WorkItem(45437, "https://github.com/dotnet/roslyn/issues/45437")] public async Task TestGenericsAndNullability() { diff --git a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs index e7706f9195b6b..529e64816b069 100644 --- a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs +++ b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs @@ -220,6 +220,59 @@ await TestAddNamedTypeAsync(input, expected, modifiers: new Editing.DeclarationModifiers(isStatic: true)); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem(544405, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")] + public async Task AddStaticAbstractClass() + { + var input = "namespace [|N|] { }"; + var expected = @"namespace N +{ + public static class C + { + } +}"; + // note that 'abstract' is dropped here + await TestAddNamedTypeAsync(input, expected, + modifiers: new Editing.DeclarationModifiers(isStatic: true, isAbstract: true)); + } + + [Theory, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem(544405, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")] + [InlineData(Accessibility.NotApplicable)] + [InlineData(Accessibility.Internal)] + [InlineData(Accessibility.Public)] + public async Task AddFileClass(Accessibility accessibility) + { + var input = "namespace [|N|] { }"; + var expected = @"namespace N +{ + file class C + { + } +}"; + // note: when invalid combinations of modifiers+accessibility are present here, + // we actually drop the accessibility. This is similar to what is done if someone declares a 'static abstract class C { }'. + await TestAddNamedTypeAsync(input, expected, + accessibility: accessibility, + modifiers: new Editing.DeclarationModifiers(isFile: true)); + } + + [Theory, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem(544405, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")] + [InlineData("struct", TypeKind.Struct)] + [InlineData("interface", TypeKind.Interface)] + [InlineData("enum", TypeKind.Enum)] + public async Task AddFileType(string kindString, TypeKind typeKind) + { + var input = "namespace [|N|] { }"; + var expected = @"namespace N +{ + file " + kindString + @" C + { + } +}"; + await TestAddNamedTypeAsync(input, expected, + typeKind: typeKind, + modifiers: new Editing.DeclarationModifiers(isFile: true)); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem(544405, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")] public async Task AddSealedClass() { diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index b3ca8a311e484..189654c326a60 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -29,6 +29,136 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense <[UseExportProvider]> Public Class CSharpCompletionCommandHandlerTests + + + Public Async Function CompletionOnFileType_SameFile_NonQualified(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + +namespace NS +{ + file class FC { } + + class C + { + public static void M() + { + var x = new $$ + } + } +} + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.Preview) + + state.SendTypeChars("F") + Await state.AssertSelectedCompletionItem(displayText:="FC", isHardSelected:=True) + + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("var x = new FC", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function CompletionOnFileType_SameFile_NamespaceQualified(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + +namespace NS +{ + file class FC { } + + class C + { + public static void M() + { + var x = new NS.$$ + } + } +} + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.Preview) + + state.SendTypeChars("F") + Await state.AssertSelectedCompletionItem(displayText:="FC", isHardSelected:=True) + + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("var x = new NS.FC", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function CompletionOnFileType_DifferentFile_NonQualified(showCompletionInArgumentLists As Boolean) As Task + Using State = New TestState( + > + +namespace NS +{ + file class FC { } +} + + +namespace NS +{ + class C + { + public static void M() + { + var x = new $$ + } + } +} + + + , + excludedTypes:=Nothing, extraExportedTypes:=Nothing, + includeFormatCommandHandler:=False, workspaceKind:=Nothing) + + State.Workspace.GlobalOptions.SetGlobalOption( + New OptionKey(CompletionOptionsStorage.TriggerInArgumentLists, LanguageNames.CSharp), showCompletionInArgumentLists) + + State.SendTypeChars("F") + Await State.AssertCompletionItemsDoNotContainAny("FC") + End Using + End Function + + + + Public Async Function CompletionOnFileType_DifferentFile_NamespaceQualified(showCompletionInArgumentLists As Boolean) As Task + Using State = New TestState( + > + +namespace NS +{ + file class FC { } +} + + +namespace NS +{ + class C + { + public static void M() + { + var x = new NS.$$ + } + } +} + + + , + excludedTypes:=Nothing, extraExportedTypes:=Nothing, + includeFormatCommandHandler:=False, workspaceKind:=Nothing) + + State.Workspace.GlobalOptions.SetGlobalOption( + New OptionKey(CompletionOptionsStorage.TriggerInArgumentLists, LanguageNames.CSharp), showCompletionInArgumentLists) + + State.SendTypeChars("F") + Await State.AssertCompletionItemsDoNotContainAny("FC") + End Using + End Function + Public Async Function CompletionOnExtendedPropertyPattern_FirstNested(showCompletionInArgumentLists As Boolean) As Task diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/EEMethodBinder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/EEMethodBinder.cs index 5b197c99e8a73..c7b5cc398148e 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/EEMethodBinder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/EEMethodBinder.cs @@ -44,7 +44,8 @@ internal EEMethodBinder(EEMethodSymbol method, MethodSymbol containingMethod, Bi var substitutedSourceMethod = method.SubstitutedSourceMethod; _parameterOffset = substitutedSourceMethod.IsStatic ? 0 : 1; _targetParameters = method.Parameters; - _sourceBinder = new InMethodBinder(substitutedSourceMethod, new BuckStopsHereBinder(next.Compilation)); + // https://github.com/dotnet/roslyn/issues/62334: add tests and adjust implementation to allow EE to access file types + _sourceBinder = new InMethodBinder(substitutedSourceMethod, new BuckStopsHereBinder(next.Compilation, associatedSyntaxTree: null)); } internal override void LookupSymbolsInSingleBinder(LookupResult result, string name, int arity, ConsList basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs index 7cd2b047799b1..c29de94be92eb 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs @@ -706,7 +706,8 @@ private static Binder CreateBinderChain( @namespace = @namespace.ContainingNamespace; } - Binder binder = new BuckStopsHereBinder(compilation); + // https://github.com/dotnet/roslyn/issues/62334: add tests and adjust implementation to allow EE to access file types + Binder binder = new BuckStopsHereBinder(compilation, associatedSyntaxTree: null); var hasImports = !importRecordGroups.IsDefaultOrEmpty; var numImportStringGroups = hasImports ? importRecordGroups.Length : 0; var currentStringGroup = numImportStringGroups - 1; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs index b6856a5f05ff1..2ee0b8d7df52f 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs @@ -159,6 +159,10 @@ internal override bool MangleName get { return false; } } + // https://github.com/dotnet/roslyn/issues/61999 + // Determine if 'null' is the right return value here + internal override SyntaxTree AssociatedSyntaxTree => null; + public override IEnumerable MemberNames { get { throw ExceptionUtilities.Unreachable; } diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs index d602dcf4c985e..b2cf692ad5453 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs @@ -77,6 +77,10 @@ internal NamespaceTypeDefinitionNoBase(INamespaceTypeDefinition underlyingType) bool INamedTypeReference.MangleName => UnderlyingType.MangleName; +#nullable enable + string? INamedTypeReference.AssociatedFileIdentifier => UnderlyingType.AssociatedFileIdentifier; +#nullable disable + string INamedEntity.Name => UnderlyingType.Name; string INamespaceTypeReference.NamespaceName => UnderlyingType.NamespaceName; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs index 2c74a4a4cc164..21c022e42f4eb 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs @@ -68,6 +68,7 @@ public KeywordCompletionProvider() new ExternKeywordRecommender(), new FalseKeywordRecommender(), new FieldKeywordRecommender(), + new FileKeywordRecommender(), new FinallyKeywordRecommender(), new FixedKeywordRecommender(), new FloatKeywordRecommender(), diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs new file mode 100644 index 0000000000000..73510f4b46dd1 --- /dev/null +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs @@ -0,0 +1,30 @@ +// 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. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; + +internal class FileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +{ + private static readonly ISet s_validModifiers = SyntaxKindSet.AllMemberModifiers + .Where(s => s != SyntaxKind.FileKeyword && !SyntaxFacts.IsAccessibilityModifier(s)) + .ToSet(); + + public FileKeywordRecommender() + : base(SyntaxKind.FileKeyword) + { + } + + protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) + { + return context.ContainingTypeDeclaration == null + && context.IsTypeDeclarationContext(s_validModifiers, SyntaxKindSet.AllTypeDeclarations, canBePartial: true, cancellationToken); + } +} diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs index 8439fc7c3bd27..cb8972561e3ef 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs @@ -144,6 +144,8 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public bool IsNativeIntegerType => _symbol.IsNativeIntegerType; + public bool IsFile => _symbol.IsFile; + public INamedTypeSymbol NativeIntegerUnderlyingType => _symbol.NativeIntegerUnderlyingType; NullableAnnotation ITypeSymbol.NullableAnnotation => throw new NotImplementedException(); diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 7e32e605a28e3..3ba8fffee3e30 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -1447,23 +1447,26 @@ public override SyntaxNode WithAccessibility(SyntaxNode declaration, Accessibili DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Static | - DeclarationModifiers.Unsafe; + DeclarationModifiers.Unsafe | + DeclarationModifiers.File; private static readonly DeclarationModifiers s_recordModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Sealed | - DeclarationModifiers.Unsafe; + DeclarationModifiers.Unsafe | + DeclarationModifiers.File; private static readonly DeclarationModifiers s_structModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Ref | - DeclarationModifiers.Unsafe; + DeclarationModifiers.Unsafe | + DeclarationModifiers.File; - private static readonly DeclarationModifiers s_interfaceModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; + private static readonly DeclarationModifiers s_interfaceModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Unsafe | DeclarationModifiers.File; private static readonly DeclarationModifiers s_accessorModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Virtual; private static readonly DeclarationModifiers s_localFunctionModifiers = @@ -1486,10 +1489,10 @@ private static DeclarationModifiers GetAllowedModifiers(SyntaxKind kind) return s_classModifiers; case SyntaxKind.EnumDeclaration: - return DeclarationModifiers.New; + return DeclarationModifiers.New | DeclarationModifiers.File; case SyntaxKind.DelegateDeclaration: - return DeclarationModifiers.New | DeclarationModifiers.Unsafe; + return DeclarationModifiers.New | DeclarationModifiers.Unsafe | DeclarationModifiers.File; case SyntaxKind.InterfaceDeclaration: return s_interfaceModifiers; diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs index 41249624471d0..941fef327976a 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs @@ -267,11 +267,17 @@ private static SyntaxTokenList GenerateModifiers( { var tokens = ArrayBuilder.GetInstance(); - var defaultAccessibility = destination is CodeGenerationDestination.CompilationUnit or CodeGenerationDestination.Namespace - ? Accessibility.Internal - : Accessibility.Private; - - AddAccessibilityModifiers(namedType.DeclaredAccessibility, tokens, info, defaultAccessibility); + if (!namedType.IsFile) + { + var defaultAccessibility = destination is CodeGenerationDestination.CompilationUnit or CodeGenerationDestination.Namespace + ? Accessibility.Internal + : Accessibility.Private; + AddAccessibilityModifiers(namedType.DeclaredAccessibility, tokens, info, defaultAccessibility); + } + else + { + tokens.Add(SyntaxFactory.Token(SyntaxKind.FileKeyword)); + } if (namedType.IsStatic) { diff --git a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs index ba6e428ebc76f..bc12ff16bfbe4 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs @@ -121,5 +121,7 @@ public override string MetadataName public INamedTypeSymbol TupleUnderlyingType => null; public bool IsSerializable => false; + + public bool IsFile => Modifiers.IsFile; } } diff --git a/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs b/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs index df4f81dd1d41e..b53d401c8b683 100644 --- a/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs +++ b/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs @@ -38,7 +38,8 @@ internal DeclarationModifiers( bool isRef = false, bool isVolatile = false, bool isExtern = false, - bool isRequired = false) + bool isRequired = false, + bool isFile = false) : this( (isStatic ? Modifiers.Static : Modifiers.None) | (isAbstract ? Modifiers.Abstract : Modifiers.None) | @@ -56,7 +57,8 @@ internal DeclarationModifiers( (isRef ? Modifiers.Ref : Modifiers.None) | (isVolatile ? Modifiers.Volatile : Modifiers.None) | (isExtern ? Modifiers.Extern : Modifiers.None) | - (isRequired ? Modifiers.Required : Modifiers.None)) + (isRequired ? Modifiers.Required : Modifiers.None) | + (isFile ? Modifiers.File : Modifiers.None)) { } @@ -84,7 +86,8 @@ IMethodSymbol or isVolatile: field?.IsVolatile == true, isExtern: symbol.IsExtern, isAsync: method?.IsAsync == true, - isRequired: symbol.IsRequired()); + isRequired: symbol.IsRequired(), + isFile: (symbol as INamedTypeSymbol)?.IsFile == true); } // Only named types, members of named types, and local functions have modifiers. @@ -126,6 +129,8 @@ IMethodSymbol or public bool IsRequired => (_modifiers & Modifiers.Required) != 0; + public bool IsFile => (_modifiers & Modifiers.File) != 0; + public DeclarationModifiers WithIsStatic(bool isStatic) => new(SetFlag(_modifiers, Modifiers.Static, isStatic)); @@ -178,6 +183,9 @@ public DeclarationModifiers WithIsExtern(bool isExtern) public DeclarationModifiers WithIsRequired(bool isRequired) => new(SetFlag(_modifiers, Modifiers.Required, isRequired)); + public DeclarationModifiers WithIsFile(bool isFile) + => new(SetFlag(_modifiers, Modifiers.File, isFile)); + private static Modifiers SetFlag(Modifiers existing, Modifiers modifier, bool isSet) => isSet ? (existing | modifier) : (existing & ~modifier); @@ -203,6 +211,7 @@ private enum Modifiers Volatile = 1 << 14, Extern = 1 << 15, Required = 1 << 16, + File = 1 << 17, #pragma warning restore format } @@ -225,6 +234,7 @@ private enum Modifiers public static DeclarationModifiers Volatile => new(Modifiers.Volatile); public static DeclarationModifiers Extern => new(Modifiers.Extern); public static DeclarationModifiers Required => new(Modifiers.Required); + public static DeclarationModifiers File => new(Modifiers.File); public static DeclarationModifiers operator |(DeclarationModifiers left, DeclarationModifiers right) => new(left._modifiers | right._modifiers); diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 33861f0f5b2f4..107531883f1ae 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -4,6 +4,8 @@ Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingMember = 4 -> Microsoft.C Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeFixes.FixAllScope Microsoft.CodeAnalysis.Editing.DeclarationModifiers.IsRequired.get -> bool Microsoft.CodeAnalysis.Editing.DeclarationModifiers.WithIsRequired(bool isRequired) -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers +Microsoft.CodeAnalysis.Editing.DeclarationModifiers.IsFile.get -> bool +Microsoft.CodeAnalysis.Editing.DeclarationModifiers.WithIsFile(bool isFile) -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers Microsoft.CodeAnalysis.Editing.SyntaxEditor.SyntaxEditor(Microsoft.CodeAnalysis.SyntaxNode root, Microsoft.CodeAnalysis.Host.HostWorkspaceServices services) -> void *REMOVED*static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.TextDocumentEventArgs @@ -16,6 +18,7 @@ Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler System.Collections.Immutable.ImmutableArray static Microsoft.CodeAnalysis.CodeFixes.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeFixes.FixAllProvider static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.Required.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers +static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers static Microsoft.CodeAnalysis.Recommendations.Recommender.GetRecommendedSymbolsAtPositionAsync(Microsoft.CodeAnalysis.Document document, int position, Microsoft.CodeAnalysis.Options.OptionSet options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task> static readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpAccessibilityFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpAccessibilityFacts.cs index 86cfbb6cfaa17..0f19e230090f4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpAccessibilityFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpAccessibilityFacts.cs @@ -34,6 +34,8 @@ public bool CanHaveAccessibility(SyntaxNode declaration) case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.DelegateDeclaration: + return !((MemberDeclarationSyntax)declaration).Modifiers.Any(SyntaxKind.FileKeyword); + case SyntaxKind.FieldDeclaration: case SyntaxKind.EventFieldDeclaration: case SyntaxKind.GetAccessorDeclaration: diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs index f45f66bdfe248..6b0fee736cd1e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs @@ -16,6 +16,9 @@ public static void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor) visitor.WriteSymbolKey(symbol.ContainingSymbol); visitor.WriteString(symbol.Name); visitor.WriteInteger(symbol.Arity); + visitor.WriteString(symbol.IsFile + ? symbol.DeclaringSyntaxReferences[0].SyntaxTree.FilePath + : null); visitor.WriteBoolean(symbol.IsUnboundGenericType); if (!symbol.Equals(symbol.ConstructedFrom) && !symbol.IsUnboundGenericType) @@ -33,7 +36,9 @@ public static SymbolKeyResolution Resolve(SymbolKeyReader reader, out string? fa var containingSymbolResolution = reader.ReadSymbolKey(out var containingSymbolFailureReason); var name = reader.ReadRequiredString(); var arity = reader.ReadInteger(); + var filePath = reader.ReadString(); var isUnboundGenericType = reader.ReadBoolean(); + using var typeArguments = reader.ReadSymbolKeyArray(out var typeArgumentsFailureReason); if (containingSymbolFailureReason != null) @@ -61,7 +66,7 @@ public static SymbolKeyResolution Resolve(SymbolKeyReader reader, out string? fa foreach (var nsOrType in containingSymbolResolution.OfType()) { Resolve( - result, nsOrType, name, arity, + result, nsOrType, name, arity, filePath, isUnboundGenericType, typeArgumentArray); } @@ -73,11 +78,28 @@ private static void Resolve( INamespaceOrTypeSymbol container, string name, int arity, + string? filePath, bool isUnboundGenericType, ITypeSymbol[] typeArguments) { foreach (var type in container.GetTypeMembers(name, arity)) { + // if this is a 'file' type, then only resolve to a file-type from this same file + if (filePath != null) + { + if (!type.IsFile || + // note: if we found 'IsFile' returned true, we can assume DeclaringSyntaxReferences is non-empty. + type.DeclaringSyntaxReferences[0].SyntaxTree.FilePath != filePath) + { + continue; + } + } + else if (type.IsFile) + { + // since this key lacks a file path it can't match against a 'file' type + continue; + } + var currentType = typeArguments.Length > 0 ? type.Construct(typeArguments) : type; currentType = isUnboundGenericType ? currentType.ConstructUnboundGenericType() : currentType; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs index e30a325b2c17b..ad6fe979a4fa9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs @@ -109,7 +109,7 @@ internal partial struct SymbolKey : IEquatable /// out a SymbolKey from a previous version of Roslyn and then attempt to use it in a /// newer version where the encoding has changed. /// - internal const int FormatVersion = 2; + internal const int FormatVersion = 3; [DataMember(Order = 0)] private readonly string _symbolKeyData; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs index 132a1de02a285..17444004886c8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs @@ -14,6 +14,7 @@ internal class SyntaxKindSet public static readonly ISet AllTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.AbstractKeyword, + SyntaxKind.FileKeyword, SyntaxKind.InternalKeyword, SyntaxKind.NewKeyword, SyntaxKind.PublicKeyword,