-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Background and Motivation
The file-local types feature (#60819) introduces public APIs.
[update:] API is named IsFileLocal
Proposed API
namespace Microsoft.CodeAnalysis
{
public interface INamedTypeSymbol
{
+ /// <summary>
+ /// Indicates that this type is only visible in the file it was declared in.
+ /// </summary>
+ bool IsFile { get; }
}
}namespace Microsoft.CodeAnalysis.CSharp
{
public enum SyntaxKind
{
// ...
/// <summary>Represents <see langword="required"/>.</summary>
RequiredKeyword = 8447,
+ /// <summary>Represents <see langword="file"/>.</summary>
+ FileKeyword = 8448,
}
}Usage Examples
INamedTypeSymbol.IsFile is needed to implement SymbolKey for file types.
Lines 12 to 22 in b547e91
| private static class NamedTypeSymbolKey | |
| { | |
| public static void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor) | |
| { | |
| visitor.WriteSymbolKey(symbol.ContainingSymbol); | |
| visitor.WriteString(symbol.Name); | |
| visitor.WriteInteger(symbol.Arity); | |
| visitor.WriteString(symbol.IsFile | |
| ? symbol.DeclaringSyntaxReferences[0].SyntaxTree.FilePath | |
| : null); | |
| visitor.WriteBoolean(symbol.IsUnboundGenericType); |
Lines 76 to 96 in b547e91
| private static void Resolve( | |
| PooledArrayBuilder<INamedTypeSymbol> result, | |
| INamespaceOrTypeSymbol container, | |
| string name, | |
| int arity, | |
| string? filePath, | |
| bool isUnboundGenericType, | |
| ITypeSymbol[] typeArguments) | |
| { | |
| foreach (var type in container.GetTypeMembers(name, arity)) | |
| { | |
| // if this is a 'file' type, then only resolve to a file-type from this same file | |
| if (filePath != null) | |
| { | |
| if (!type.IsFile || | |
| type.DeclaringSyntaxReferences.IsEmpty || | |
| type.DeclaringSyntaxReferences[0].SyntaxTree.FilePath != filePath) | |
| { | |
| continue; | |
| } | |
| } |
The IDE may also have other places where they want to handle a file type specially. For example, when it is displayed in the navbar or member list.
Whenever the IDE gets the members of a namespace symbol, they may need to filter out file types which aren't available in the current file, for example. LookupSymbols does this automatically when it is given a INamespaceOrTypeSymbol? container but INamespaceSymbol.GetMembers() won't be able to do this automatically.
Alternative Designs
We're not proposing any alternative designs here. We think it's not viable to proceed with IDE tooling for the feature without the INamedTypeSymbol.IsFile API. It might be possible to go to source for some cases, since file types is a source-only concept, but it is expensive and would break layering for symbol-oriented components to do so. This is something the IDE team strongly wants to avoid.
Earlier on we considered having an API SyntaxTree? INamedTypeSymbol.AssociatedSyntaxTree, where a non-null return value means the symbol is a file type, and a null return value means it is not a file type. However, it was felt that exposing just bool INamedTypeSymbol.IsFile is simpler here. Users who want to actually know which file the symbol is declared in can check DeclaringSyntaxReferences.
Risks
Not aware of any risks from the current design.
SymbolDisplay
We've added internal symbol display options to make it easier to distinguish file types declared in different files with the same name.
// FilePath: path/to/MyFile.cs
// displays as 'C@MyFile' with ToTestDisplayString()
file class C { }
// FilePath: path/to/OtherFile.cs
// displays as 'C@OtherFile' with ToTestDisplayString()
file class C { }This raises a few questions:
- Should the specific way this is displayed be changed? i.e. is there something better to denote the "file name" suffix than an
@symbol? - Should we make this symbol display option public?
namespace Microsoft.CodeAnalysis
{
internal enum SymbolDisplayCompilerInternalOptions
{
// ...
/// <summary>
/// Separate out nested types from containing types using <c>+</c> instead of <c>.</c> (dot).
/// </summary>
UsePlusForNestedTypes = 1 << 8,
+ /// <summary>
+ /// Display `MyType@File` instead of `MyType`.
+ /// </summary>
+ IncludeContainingFileForFileTypes = 1 << 9,
}