/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs
493 строки
21 KB
AlekseyTs
Initial support for union declarations (#82500)
25 фев 2026, 22:37
Не верифицирован
25 фев 2026, 22:37
49a4215
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed partial class SourceNamespaceSymbol : NamespaceSymbol { private static readonly ImmutableDictionary<SingleNamespaceDeclaration, AliasesAndUsings> s_emptyMap = ImmutableDictionary<SingleNamespaceDeclaration, AliasesAndUsings>.Empty.WithComparers(ReferenceEqualityComparer.Instance); private readonly SourceModuleSymbol _module; private readonly Symbol _container; private readonly MergedNamespaceDeclaration _mergedDeclaration; private SymbolCompletionState _state; private ImmutableArray<Location> _locations; private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamespaceOrTypeSymbol>> _nameToMembersMap; private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> _nameToTypeMembersMap; private ImmutableArray<Symbol> _lazyAllMembers; private ImmutableArray<NamedTypeSymbol> _lazyTypeMembersUnordered; /// <summary> /// Should only be read using <see cref="GetAliasesAndUsings(SingleNamespaceDeclaration)"/>. /// </summary> private ImmutableDictionary<SingleNamespaceDeclaration, AliasesAndUsings> _aliasesAndUsings_doNotAccessDirectly = s_emptyMap; #if DEBUG /// <summary> /// Should only be read using <see cref="GetAliasesAndUsingsForAsserts"/>. /// </summary> private ImmutableDictionary<SingleNamespaceDeclaration, AliasesAndUsings> _aliasesAndUsingsForAsserts_doNotAccessDirectly = s_emptyMap; #endif private MergedGlobalAliasesAndUsings _lazyMergedGlobalAliasesAndUsings; private const int LazyAllMembersIsSorted = 0x1; // Set if "lazyAllMembers" is sorted. private int _flags; private LexicalSortKey _lazyLexicalSortKey = LexicalSortKey.NotInitialized; internal SourceNamespaceSymbol( SourceModuleSymbol module, Symbol container, MergedNamespaceDeclaration mergedDeclaration, BindingDiagnosticBag diagnostics) { Debug.Assert(mergedDeclaration != null); _module = module; _container = container; _mergedDeclaration = mergedDeclaration; foreach (var singleDeclaration in mergedDeclaration.Declarations) diagnostics.AddRange(singleDeclaration.Diagnostics); } internal MergedNamespaceDeclaration MergedDeclaration => _mergedDeclaration; public override Symbol ContainingSymbol => _container; public override AssemblySymbol ContainingAssembly => _module.ContainingAssembly; public override string Name => _mergedDeclaration.Name; internal override LexicalSortKey GetLexicalSortKey() { if (!_lazyLexicalSortKey.IsInitialized) { _lazyLexicalSortKey.SetFrom(_mergedDeclaration.GetLexicalSortKey(this.DeclaringCompilation)); } return _lazyLexicalSortKey; } public override ImmutableArray<Location> Locations { get { if (_locations.IsDefault) { ImmutableInterlocked.InterlockedCompareExchange(ref _locations, _mergedDeclaration.NameLocations, default); } return _locations; } } #nullable enable public override Location? TryGetFirstLocation() => _mergedDeclaration.Declarations is [var declaration, ..] ? declaration.NameLocation : null; #nullable disable public override bool HasLocationContainedWithin(SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch) { // Avoid the allocation of .Locations in the base method. foreach (var decl in _mergedDeclaration.Declarations) { if (IsLocationContainedWithin(decl.NameLocation, tree, declarationSpan, out wasZeroWidthMatch)) return true; } wasZeroWidthMatch = false; return false; } private static readonly Func<SingleNamespaceDeclaration, SyntaxReference> s_declaringSyntaxReferencesSelector = d => new NamespaceDeclarationSyntaxReference(d.SyntaxReference); public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ComputeDeclaringReferencesCore(); private ImmutableArray<SyntaxReference> ComputeDeclaringReferencesCore() { // SyntaxReference in the namespace declaration points to the name node of the namespace decl node not // namespace decl node we want to return. here we will wrap the original syntax reference in // the translation syntax reference so that we can lazily manipulate a node return to the caller return _mergedDeclaration.Declarations.SelectAsArray(s_declaringSyntaxReferencesSelector); } internal override ImmutableArray<Symbol> GetMembersUnordered() { var result = _lazyAllMembers; if (result.IsDefault) { var members = StaticCast<Symbol>.From(this.GetNameToMembersMap().Flatten(null)); // don't sort. ImmutableInterlocked.InterlockedInitialize(ref _lazyAllMembers, members); result = _lazyAllMembers; } return result.ConditionallyDeOrder(); } public override ImmutableArray<Symbol> GetMembers() { if ((_flags & LazyAllMembersIsSorted) != 0) { return _lazyAllMembers; } else { var allMembers = this.GetMembersUnordered(); if (allMembers.Length >= 2) { // The array isn't sorted. Sort it and remember that we sorted it. allMembers = allMembers.Sort(LexicalOrderSymbolComparer.Instance); ImmutableInterlocked.InterlockedExchange(ref _lazyAllMembers, allMembers); } ThreadSafeFlagOperations.Set(ref _flags, LazyAllMembersIsSorted); return allMembers; } } public override ImmutableArray<Symbol> GetMembers(ReadOnlyMemory<char> name) { ImmutableArray<NamespaceOrTypeSymbol> members; return this.GetNameToMembersMap().TryGetValue(name, out members) ? members.Cast<NamespaceOrTypeSymbol, Symbol>() : ImmutableArray<Symbol>.Empty; } internal override ImmutableArray<NamedTypeSymbol> GetTypeMembersUnordered() { if (_lazyTypeMembersUnordered.IsDefault) { var members = this.GetNameToTypeMembersMap().Flatten(); ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeMembersUnordered, members); } return _lazyTypeMembersUnordered; } public override ImmutableArray<NamedTypeSymbol> GetTypeMembers() { return this.GetNameToTypeMembersMap().Flatten(LexicalOrderSymbolComparer.Instance); } public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name) { ImmutableArray<NamedTypeSymbol> members; return this.GetNameToTypeMembersMap().TryGetValue(name, out members) ? members : ImmutableArray<NamedTypeSymbol>.Empty; } public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name, int arity) { return GetTypeMembers(name).WhereAsArray((s, arity) => s.Arity == arity, arity); } internal override ModuleSymbol ContainingModule { get { return _module; } } internal override NamespaceExtent Extent { get { return new NamespaceExtent(_module); } } private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamespaceOrTypeSymbol>> GetNameToMembersMap() { if (_nameToMembersMap == null) { var diagnostics = BindingDiagnosticBag.GetInstance(); if (Interlocked.CompareExchange(ref _nameToMembersMap, MakeNameToMembersMap(diagnostics), null) == null) { // NOTE: the following is not cancellable. Once we've set the // members, we *must* do the following to make sure we're in a consistent state. this.AddDeclarationDiagnostics(diagnostics); RegisterDeclaredCorTypes(); // We may produce a SymbolDeclaredEvent for the enclosing namespace before events for its contained members DeclaringCompilation.SymbolDeclaredEvent(this); var wasSetThisThread = _state.NotePartComplete(CompletionPart.NameToMembersMap); Debug.Assert(wasSetThisThread); } diagnostics.Free(); } return _nameToMembersMap; } private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> GetNameToTypeMembersMap() { if (_nameToTypeMembersMap == null) { // NOTE: This method depends on MakeNameToMembersMap() on creating a proper // NOTE: type of the array, see comments in MakeNameToMembersMap() for details Interlocked.CompareExchange( ref _nameToTypeMembersMap, ImmutableArrayExtensions.GetTypesFromMemberMap<ReadOnlyMemory<char>, NamespaceOrTypeSymbol, NamedTypeSymbol>( GetNameToMembersMap(), ReadOnlyMemoryOfCharComparer.Instance), comparand: null); } return _nameToTypeMembersMap; } private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamespaceOrTypeSymbol>> MakeNameToMembersMap(BindingDiagnosticBag diagnostics) { // NOTE: Even though the resulting map stores ImmutableArray<NamespaceOrTypeSymbol> as // NOTE: values if the name is mapped into an array of named types, which is frequently // NOTE: the case, we actually create an array of NamedTypeSymbol[] and wrap it in // NOTE: ImmutableArray<NamespaceOrTypeSymbol> // NOTE: // NOTE: This way we can save time and memory in GetNameToTypeMembersMap() -- when we see that // NOTE: a name maps into values collection containing types only instead of allocating another // NOTE: array of NamedTypeSymbol[] we downcast the array to ImmutableArray<NamedTypeSymbol> var builder = s_nameToObjectPool.Allocate(); foreach (var declaration in _mergedDeclaration.Children) { NamespaceOrTypeSymbol symbol = BuildSymbol(declaration, diagnostics); ImmutableArrayExtensions.AddToMultiValueDictionaryBuilder(builder, symbol.Name.AsMemory(), symbol); } var result = new Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamespaceOrTypeSymbol>>(builder.Count, ReadOnlyMemoryOfCharComparer.Instance); ImmutableArrayExtensions.CreateNameToMembersMap<ReadOnlyMemory<char>, NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol>(builder, result); builder.Free(); CheckMembers(this, result, diagnostics); return result; } private static void CheckMembers(NamespaceSymbol @namespace, Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamespaceOrTypeSymbol>> result, BindingDiagnosticBag diagnostics) { var memberOfArity = new Symbol[10]; MergedNamespaceSymbol mergedAssemblyNamespace = null; if (@namespace.ContainingAssembly.Modules.Length > 1) { mergedAssemblyNamespace = @namespace.ContainingAssembly.GetAssemblyNamespace(@namespace) as MergedNamespaceSymbol; } foreach (var name in result.Keys) { Array.Clear(memberOfArity, 0, memberOfArity.Length); foreach (var symbol in result[name]) { var nts = symbol as SourceMemberContainerTypeSymbol; // It should be impossible to have a type member of a source namespace symbol which is not a SourceMemberContainerTypeSymbol Debug.Assert((object)nts != null || symbol is not TypeSymbol); var arity = ((object)nts != null) ? nts.Arity : 0; if (arity >= memberOfArity.Length) { Array.Resize(ref memberOfArity, arity + 1); } if (nts?.IsExtension == true) continue; var other = memberOfArity[arity]; if ((object)other == null && (object)mergedAssemblyNamespace != null) { // Check for collision with declarations from added modules. foreach (NamespaceSymbol constituent in mergedAssemblyNamespace.ConstituentNamespaces) { if ((object)constituent != (object)@namespace) { // For whatever reason native compiler only detects conflicts against types. // It doesn't complain when source declares a type with the same name as // a namespace in added module, but complains when source declares a namespace // with the same name as a type in added module. var types = constituent.GetTypeMembers(symbol.Name, arity); if (types.Length > 0) { other = types[0]; // Since the error doesn't specify what added module this type belongs to, we can stop searching // at the first match. break; } } } } if ((object)other != null) { // To decide whether type declarations are duplicates, we need to access members which are only meaningful on source original definition symbols. Debug.Assert((object)nts?.OriginalDefinition == nts && (object)other.OriginalDefinition == other); switch (nts, other) { case ({ } left, SourceMemberContainerTypeSymbol right) when isFileLocalTypeInSeparateFileFrom(right, left): case ({ } left1, NamespaceOrTypeSymbol right1) when isFileLocalTypeInSeparateFileFrom(left1, right1): // no error break; case ({ IsFileLocal: true }, _) or (_, SourceMemberContainerTypeSymbol { IsFileLocal: true }): diagnostics.Add(ErrorCode.ERR_FileLocalDuplicateNameInNS, symbol.GetFirstLocationOrNone(), symbol.Name, @namespace); break; case ({ IsPartial: true }, SourceMemberContainerTypeSymbol { IsPartial: true }): diagnostics.Add(ErrorCode.ERR_PartialTypeKindConflict, symbol.GetFirstLocationOrNone(), symbol); break; default: diagnostics.Add(ErrorCode.ERR_DuplicateNameInNS, symbol.GetFirstLocationOrNone(), symbol.Name, @namespace); break; } } memberOfArity[arity] = symbol; if ((object)nts != null) { //types declared at the namespace level may only have declared accessibility of public or internal (Section 3.5.1) Accessibility declaredAccessibility = nts.DeclaredAccessibility; if (declaredAccessibility != Accessibility.Public && declaredAccessibility != Accessibility.Internal) { diagnostics.Add(ErrorCode.ERR_NoNamespacePrivate, symbol.GetFirstLocationOrNone()); } } } } static bool isFileLocalTypeInSeparateFileFrom(SourceMemberContainerTypeSymbol possibleFileLocalType, NamespaceOrTypeSymbol otherSymbol) { if (!possibleFileLocalType.IsFileLocal) { return false; } var leftTree = possibleFileLocalType.MergedDeclaration.Declarations[0].Location.SourceTree; if (otherSymbol is SourceNamedTypeSymbol { MergedDeclaration.NameLocations: var typeNameLocations }) { return !typeNameLocations.Any(static (loc, leftTree) => (object)loc.SourceTree == leftTree, leftTree); } else if (otherSymbol is SourceNamespaceSymbol { MergedDeclaration.NameLocations: var namespaceNameLocations }) { return !namespaceNameLocations.Any(static (loc, leftTree) => (object)loc.SourceTree == leftTree, leftTree); } throw ExceptionUtilities.UnexpectedValue(otherSymbol); } } private NamespaceOrTypeSymbol BuildSymbol(MergedNamespaceOrTypeDeclaration declaration, BindingDiagnosticBag diagnostics) { switch (declaration.Kind) { case DeclarationKind.Namespace: return new SourceNamespaceSymbol(_module, this, (MergedNamespaceDeclaration)declaration, diagnostics); case DeclarationKind.Struct: case DeclarationKind.Union: case DeclarationKind.Interface: case DeclarationKind.Enum: case DeclarationKind.Delegate: case DeclarationKind.Class: case DeclarationKind.Record: case DeclarationKind.RecordStruct: case DeclarationKind.Extension: return new SourceNamedTypeSymbol(this, (MergedTypeDeclaration)declaration, diagnostics); case DeclarationKind.Script: case DeclarationKind.Submission: case DeclarationKind.ImplicitClass: return new ImplicitNamedTypeSymbol(this, (MergedTypeDeclaration)declaration, diagnostics); default: throw ExceptionUtilities.UnexpectedValue(declaration.Kind); } } /// <summary> /// Register COR types declared in this namespace, if any, in the COR types cache. /// </summary> private void RegisterDeclaredCorTypes() { AssemblySymbol containingAssembly = ContainingAssembly; if (containingAssembly.KeepLookingForDeclaredSpecialTypes) { // Register newly declared COR types foreach (var array in _nameToMembersMap.Values) { foreach (var member in array) { var type = member as NamedTypeSymbol; if ((object)type != null && type.SpecialType != SpecialType.None) { containingAssembly.RegisterDeclaredSpecialType(type); if (!containingAssembly.KeepLookingForDeclaredSpecialTypes) { return; } } } } } } public override bool IsDefinedInSourceTree(SyntaxTree tree, TextSpan? definedWithinSpan, CancellationToken cancellationToken = default(CancellationToken)) { if (this.IsGlobalNamespace) { return true; } // Check if any namespace declaration block intersects with the given tree/span. foreach (var declaration in _mergedDeclaration.Declarations) { cancellationToken.ThrowIfCancellationRequested(); var declarationSyntaxRef = declaration.SyntaxReference; if (declarationSyntaxRef.SyntaxTree != tree) { continue; } if (!definedWithinSpan.HasValue) { return true; } var syntax = NamespaceDeclarationSyntaxReference.GetSyntax(declarationSyntaxRef, cancellationToken); if (syntax.FullSpan.IntersectsWith(definedWithinSpan.Value)) { return true; } } return false; } } }