diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
index 1331731c757b1..9c977c755319c 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
@@ -2493,7 +2493,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/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 3a545b4bd4048..139e9ce8ccbf4 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -7112,6 +7112,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
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.
+
unsigned right shift
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 a16378eed574f..fc2b6fc942183 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 4bf65a09750e1..255f4583dd594 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -2095,6 +2095,7 @@ internal enum ErrorCode
ERR_FileTypeDisallowedInSignature = 9300,
ERR_FileTypeNoExplicitAccessibility = 9301,
ERR_FileTypeBase = 9302,
+ ERR_FileTypeNested = 9303,
#endregion
diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs
index c913f82010b5e..6bad689cf0e76 100644
--- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs
+++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs
@@ -10,6 +10,7 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.SymbolDisplay;
using Roslyn.Utilities;
@@ -182,17 +183,28 @@ public override void VisitNamedType(INamedTypeSymbol symbol)
AddNullableAnnotations(symbol);
if ((format.CompilerInternalOptions & SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes) != 0
- // PROTOTYPE(ft): public API?
- && symbol.GetSymbol() is SourceMemberContainerTypeSymbol { IsFile: true } fileType)
+ && symbol is Symbols.PublicModel.Symbol { UnderlyingSymbol: NamedTypeSymbol { AssociatedSyntaxTree: SyntaxTree tree } internalSymbol })
{
- var tree = symbol.DeclaringSyntaxReferences[0].SyntaxTree;
- var fileDescription = tree.FilePath is { Length: not 0 } path
- ? Path.GetFileNameWithoutExtension(path)
- : $"";
+ 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 47d94ec32230c..9542e39a6f7d6 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 b0177ecb18c00..e1f1c52db543e 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/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
index cc59b10392b0f..44a6c46bbb5db 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
@@ -826,8 +826,7 @@ internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo HasFlag(DeclarationModifiers.File);
- /// If this symbol is only available within a single syntax tree, returns that syntax tree. Otherwise, returns null.
- internal SyntaxTree? AssociatedSyntaxTree => IsFile ? declaration.Declarations[0].Location.SourceTree : null;
+ internal sealed override SyntaxTree? AssociatedSyntaxTree => IsFile ? declaration.Declarations[0].Location.SourceTree : null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HasFlag(DeclarationModifiers flag) => (_declModifiers & flag) != 0;
@@ -1743,6 +1742,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/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 cf9acb4bcd6e0..74b6ef0e57a8e 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 a03039d83a6ca..6e36128fc5162 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/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
index 508a139a32598..2df49e75c19ab 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;
@@ -1361,6 +1362,16 @@ public static bool IsFileTypeOrUsesFileTypes(this TypeSymbol type)
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/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 54d37aa08912e..4edff182eda01 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 87b0902fd9c30..0d5dbb6ca6d5b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 83792b53903f8..c146398c4d563 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index 3a74a9c7bb492..52798620fbaf0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 3c47401ea0e07..177e269e704dc 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index 8c73c39d1d469..49343625d8876 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index f47b5109c0161..0199e0ab379a3 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index cd69d9f270d30..bfe3edd993e2e 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index 74a78e9c99d65..51ef2f7c6d194 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 895aa4d900286..49fe2ce705ee8 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index 40c6a24e526ce..9daaaba095219 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 7551f44f01217..211e53063cd85 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 7c52b486b9619..9ac1bddbf4c14 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -577,6 +577,11 @@
File type '{0}' cannot be used in a member signature in non-file type '{1}'.
+
+ 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.
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs
index 6b6ac34fcf5df..cb1148562ee86 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs
@@ -13159,6 +13159,334 @@ .locals init ([unchanged] V_0,
.maxstack 1
IL_0000: ldc.i4.s 10
IL_0002: ret
+}");
+ }
+
+ [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/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
index b5e473aee9b02..0fbb15b774fbb 100644
--- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs
+++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FileModifierTests.cs
@@ -3,9 +3,11 @@
// 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;
@@ -43,10 +45,16 @@ file class C { }
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));
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").WithLocation(3, 16),
+ // (3,16): error CS9303: 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();
+ comp.VerifyDiagnostics(
+ // (3,16): error CS9303: 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]
@@ -92,7 +100,6 @@ file class C { }
}
""";
- // PROTOTYPE(ft): determine whether an inner file class within a file class is an error or if it's just fine.
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.
@@ -100,10 +107,16 @@ file class C { }
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));
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("file types").WithLocation(3, 16),
+ // (3,16): error CS9303: 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();
+ comp.VerifyDiagnostics(
+ // (3,16): error CS9303: 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]
@@ -167,7 +180,7 @@ class C : B.A2 // ok: base type is bound as A1.A2
}
[Fact]
- public void SameFileUse()
+ public void SameFileUse_01()
{
var source = """
using System;
@@ -189,9 +202,244 @@ static void Main()
}
""";
- var verifier = CompileAndVerify(source, expectedOutput: "1");
+ 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 CS9300: 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();
- // PROTOTYPE(ft): check metadata names
+
+ 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]
@@ -226,11 +474,243 @@ static void Main()
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("file", "file")]
- [InlineData("file", "")]
- [InlineData("", "file")]
- public void Duplication_01(string firstFileModifier, string secondFileModifier)
+ [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".
@@ -269,24 +749,25 @@ static void Main()
}
""";
- // PROTOTYPE(ft): execute and check expectedOutput once name mangling is done
- // expectedOutput: "1"
- var comp = CreateCompilation(new[] { source1 + main, source2 });
+ 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();
- // expectedOutput: "2"
- comp = CreateCompilation(new[] { source1, source2 + main });
+ 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()
{
- comp.VerifyDiagnostics();
+ 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);
@@ -405,7 +886,8 @@ static void Main()
}
""";
- var comp = CreateCompilation(new[] { source1, source2, main }); // expectedOutput: 2
+ var verifier = CompileAndVerify(new[] { source1, source2, main }, expectedOutput: "2");
+ var comp = (CSharpCompilation)verifier.Compilation;
comp.VerifyDiagnostics();
var cs = comp.GetMembers("C");
@@ -470,7 +952,8 @@ static void Main()
}
""";
- var comp = CreateCompilation(new[] { source1, main }); // expectedOutput: 2
+ var verifier = CompileAndVerify(new[] { source1, main }, expectedOutput: "2");
+ var comp = (CSharpCompilation)verifier.Compilation;
comp.VerifyDiagnostics();
var cs = comp.GetMembers("C");
@@ -531,7 +1014,8 @@ static void Main()
}
""";
- var comp = CreateCompilation(new[] { source1, main }); // expectedOutput: 2
+ var verifier = CompileAndVerify(new[] { source1, main }, expectedOutput: "2");
+ var comp = (CSharpCompilation)verifier.Compilation;
comp.VerifyDiagnostics();
var cs = comp.GetMembers("C");
@@ -734,7 +1218,13 @@ public static void M() { }
""";
var compilation = CreateCompilation(new[] { source1, source2, source3 });
- compilation.VerifyDiagnostics();
+ compilation.VerifyDiagnostics(
+ // (3,16): error CS9303: 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 CS9303: 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");
@@ -834,15 +1324,12 @@ static void Main()
}
""";
- // PROTOTYPE(ft): execute and check expectedOutput once name mangling is done
- // expectedOutput: "1"
var comp = CreateCompilation(new[] { source1 + main, source2 });
var cs = comp.GetMembers("Program.C");
var tree = comp.SyntaxTrees[0];
var expectedSymbol = cs[0];
verify();
- // expectedOutput: "2"
comp = CreateCompilation(new[] { source1, source2 + main });
cs = comp.GetMembers("Program.C");
tree = comp.SyntaxTrees[1];
@@ -851,7 +1338,7 @@ static void Main()
void verify()
{
- comp.VerifyDiagnostics();
+ 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);
@@ -909,17 +1396,16 @@ static void Main()
}
""";
- // PROTOTYPE(ft): execute and check expectedOutput once name mangling is done
- // expectedOutput: "1"
- var comp = CreateCompilation(new[] { source1 + main, source2 });
+ 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();
- // expectedOutput: "2"
- comp = CreateCompilation(new[] { source1, source2 + main });
+ 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];
@@ -928,7 +1414,6 @@ static void Main()
void verify()
{
- comp.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);
@@ -1336,8 +1821,9 @@ static void Main()
}
""";
- var comp = CreateCompilation(new[] { source1, source2 }); // PROTOTYPE(ft): expectedOutput: 2
- comp.VerifyDiagnostics();
+ 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);
@@ -1375,8 +1861,9 @@ static void Main()
}
""";
- var comp = CreateCompilation(new[] { source1, source2 }); // PROTOTYPE(ft): expectedOutput: 1
- comp.VerifyDiagnostics();
+ 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);
@@ -1727,7 +2214,7 @@ public void AccessThroughType_01()
class Outer
{
- file class C
+ file class C // 1
{
public static void M() => Console.Write(1);
}
@@ -1737,17 +2224,18 @@ class Program
{
public static void Main()
{
- Outer.C.M(); // 1
+ Outer.C.M(); // 2
}
}
""";
- // note: there's no way to make 'file class C' internal here. it's forced to be private, at least for the initial release of the feature.
- // we access it within the same containing type in test 'Duplication_10'.
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
+ // (5,16): error CS9303: 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(); // 1
+ // Outer.C.M(); // 2
Diagnostic(ErrorCode.ERR_BadAccess, "C").WithArguments("Outer.C").WithLocation(15, 15));
}
@@ -1783,7 +2271,10 @@ static void Main()
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));
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "C").WithArguments("Outer", "C").WithLocation(5, 15),
+ // (5,16): error CS9303: 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]
@@ -1942,13 +2433,9 @@ public class D
}
""";
- // var verifier = CompileAndVerify(new[] { source1, source2 }, expectedOutput: "1");
- // verifier.VerifyDiagnostics();
- // var comp = (CSharpCompilation)verifier.Compilation;
-
- // PROTOTYPE(ft): replace the following with the above commented lines once name mangling is done
- var comp = CreateCompilation(new[] { source1, source2 });
- comp.VerifyDiagnostics();
+ 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);
@@ -1994,16 +2481,12 @@ class D
}
""";
- // var verifier = CompileAndVerify(new[] { source1, source2 }, expectedOutput: "2");
- // verifier.VerifyDiagnostics();
- // var comp = (CSharpCompilation)verifier.Compilation;
-
- // PROTOTYPE(ft): replace the following with the above commented lines once name mangling is done
- var comp = CreateCompilation(new[] { source1, source2 });
- comp.VerifyDiagnostics(
+ 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);
@@ -2052,9 +2535,20 @@ public static void Main()
}
""";
- // 'Derived.C' is not actually accessible from 'Program', so we just bind to 'Base.C' and things work.
- var compilation = CompileAndVerify(new[] { source, main }, expectedOutput: "1");
- compilation.VerifyDiagnostics();
+ // '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 CS9303: 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]
@@ -2443,12 +2937,11 @@ public static void M() { }
var c1 = comp.GetMember("C1");
var c2 = comp.GetMember("C2");
- var format = SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes);
- Assert.Equal("C1@", c1.ToDisplayString(format));
- Assert.Equal("C2@FileB", c2.ToDisplayString(format));
+ Assert.Equal("C1@", c1.ToTestDisplayString());
+ Assert.Equal("C2@FileB", c2.ToTestDisplayString());
- Assert.Equal("void C1@.M()", c1.GetMember("M").ToDisplayString(format));
- Assert.Equal("void C2@FileB.M()", c2.GetMember("M").ToDisplayString(format));
+ Assert.Equal("void C1@.M()", c1.GetMember("M").ToTestDisplayString());
+ Assert.Equal("void C2@FileB.M()", c2.GetMember("M").ToTestDisplayString());
}
[Fact]
@@ -2457,16 +2950,22 @@ public void Script_01()
var source1 = """
using System;
- C1.M();
+ C1.M("a");
- file class C1
+ static file class C1
{
- public static void M() { }
+ public static void M(this string s) { }
}
""";
var comp = CreateSubmission(source1, parseOptions: TestOptions.Script.WithLanguageVersion(LanguageVersion.Preview));
- comp.VerifyDiagnostics();
+ comp.VerifyDiagnostics(
+ // (5,19): error CS9303: 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]
@@ -2497,5 +2996,260 @@ file class Void { }
Assert.Equal("System.Void@", typeInfo.Type!.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes)));
}
- // PROTOTYPE(ft): public API (INamedTypeSymbol.IsFile?)
+ [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 CS9303: 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 = model.GetTypeInfo(node.Type!).Type;
+ Assert.Equal("C@", type.ToTestDisplayString());
+ Assert.Equal(tree, type.GetSymbol()!.AssociatedSyntaxTree);
+ }
+
+ [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 = model.GetTypeInfo(node.Type!).Type;
+ Assert.Equal("C", type.ToTestDisplayString());
+ Assert.Null(type.GetSymbol()!.AssociatedSyntaxTree);
+ }
+
+ [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 = model.GetTypeInfo(node.Type!).Type;
+ Assert.Equal("C@", type.ToTestDisplayString());
+ Assert.Equal(tree, type.GetSymbol()!.AssociatedSyntaxTree);
+ }
}
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs
index 95e79ed4401d2..0f7fbe85446b8 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs
@@ -664,7 +664,13 @@ class Outer
{
file class C { }
}
- """);
+ """,
+ expectedBindingDiagnostics: new[]
+ {
+ // (3,16): error CS9303: 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);
{
@@ -1998,7 +2004,13 @@ class C
{
file record X();
}
- """);
+ """,
+ expectedBindingDiagnostics: new[]
+ {
+ // (3,17): error CS9303: 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);
{
@@ -2090,7 +2102,13 @@ class C
{
file record X() { }
}
- """);
+ """,
+ expectedBindingDiagnostics: new[]
+ {
+ // (3,17): error CS9303: 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);
{
@@ -2179,7 +2197,13 @@ class C
{
file record X;
}
- """);
+ """,
+ expectedBindingDiagnostics: new[]
+ {
+ // (3,17): error CS9303: 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);
{
@@ -2202,6 +2226,32 @@ class C
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()
{
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 123739a331653..d16b004e69fd8 100644
--- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs
+++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs
@@ -592,6 +592,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 440db10838871..e15e537b25a7d 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 24b0def4eb4c9..30bb8fa8e6d43 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/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs
index a0a688a22b402..2bf7dd3ca6047 100644
--- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs
+++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs
@@ -62,6 +62,7 @@ internal enum SymbolDisplayCompilerInternalOptions
/// Display `System.[U]IntPtr` instead of `n[u]int`.
///
UseNativeIntegerUnderlyingType = 1 << 7,
+
///
/// Separate out nested types from containing types using + instead of . (dot).
///
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/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 619f9b8c1f77f..22b2eeed16b1e 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..c75ff30f818e3 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
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;