// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.LanguageService; internal sealed class CSharpAccessibilityFacts : IAccessibilityFacts { public static readonly IAccessibilityFacts Instance = new CSharpAccessibilityFacts(); private CSharpAccessibilityFacts() { } public bool CanHaveAccessibility(SyntaxNode declaration, bool ignoreDeclarationModifiers = false) { switch (declaration.Kind()) { case SyntaxKind.ClassDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.RecordStructDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.DelegateDeclaration: return ignoreDeclarationModifiers || !((MemberDeclarationSyntax)declaration).Modifiers.Any(SyntaxKind.FileKeyword); case SyntaxKind.FieldDeclaration: case SyntaxKind.EventFieldDeclaration: case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: return true; case SyntaxKind.VariableDeclaration: return declaration.Parent is BaseFieldDeclarationSyntax; case SyntaxKind.VariableDeclarator: return declaration.Parent is VariableDeclarationSyntax { Parent: BaseFieldDeclarationSyntax }; case SyntaxKind.ConstructorDeclaration: // Static constructor can't have accessibility return ignoreDeclarationModifiers || !((ConstructorDeclarationSyntax)declaration).Modifiers.Any(SyntaxKind.StaticKeyword); case SyntaxKind.PropertyDeclaration: return ((PropertyDeclarationSyntax)declaration).ExplicitInterfaceSpecifier == null; case SyntaxKind.IndexerDeclaration: return ((IndexerDeclarationSyntax)declaration).ExplicitInterfaceSpecifier == null; case SyntaxKind.OperatorDeclaration: return ((OperatorDeclarationSyntax)declaration).ExplicitInterfaceSpecifier == null; case SyntaxKind.ConversionOperatorDeclaration: return ((ConversionOperatorDeclarationSyntax)declaration).ExplicitInterfaceSpecifier == null; case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)declaration; if (method.ExplicitInterfaceSpecifier != null) { // explicit interface methods can't have accessibility. return false; } if (method.Modifiers.Any(SyntaxKind.PartialKeyword)) { // partial methods can't have accessibility modifiers. return false; } return true; case SyntaxKind.EventDeclaration: return ((EventDeclarationSyntax)declaration).ExplicitInterfaceSpecifier == null; default: return false; } } public Accessibility GetAccessibility(SyntaxNode declaration) { if (!CanHaveAccessibility(declaration)) return Accessibility.NotApplicable; var modifierTokens = GetModifierTokens(declaration); GetAccessibilityAndModifiers(modifierTokens, out var accessibility, out _, out _); return accessibility; } public static void GetAccessibilityAndModifiers(SyntaxTokenList modifierList, out Accessibility accessibility, out Modifiers modifiers, out bool isDefault) { accessibility = Accessibility.NotApplicable; modifiers = Modifiers.None; isDefault = false; foreach (var token in modifierList) { accessibility = (token.Kind(), accessibility) switch { (SyntaxKind.PublicKeyword, _) => Accessibility.Public, (SyntaxKind.PrivateKeyword, Accessibility.Protected) => Accessibility.ProtectedAndInternal, (SyntaxKind.PrivateKeyword, _) => Accessibility.Private, (SyntaxKind.InternalKeyword, Accessibility.Protected) => Accessibility.ProtectedOrInternal, (SyntaxKind.InternalKeyword, _) => Accessibility.Internal, (SyntaxKind.ProtectedKeyword, Accessibility.Private) => Accessibility.ProtectedAndInternal, (SyntaxKind.ProtectedKeyword, Accessibility.Internal) => Accessibility.ProtectedOrInternal, (SyntaxKind.ProtectedKeyword, _) => Accessibility.Protected, _ => accessibility, }; modifiers |= token.Kind() switch { SyntaxKind.AbstractKeyword => Modifiers.Abstract, SyntaxKind.NewKeyword => Modifiers.New, SyntaxKind.OverrideKeyword => Modifiers.Override, SyntaxKind.VirtualKeyword => Modifiers.Virtual, SyntaxKind.StaticKeyword => Modifiers.Static, SyntaxKind.AsyncKeyword => Modifiers.Async, SyntaxKind.ConstKeyword => Modifiers.Const, SyntaxKind.ReadOnlyKeyword => Modifiers.ReadOnly, SyntaxKind.SealedKeyword => Modifiers.Sealed, SyntaxKind.UnsafeKeyword => Modifiers.Unsafe, SyntaxKind.PartialKeyword => Modifiers.Partial, SyntaxKind.RefKeyword => Modifiers.Ref, SyntaxKind.VolatileKeyword => Modifiers.Volatile, SyntaxKind.ExternKeyword => Modifiers.Extern, SyntaxKind.FileKeyword => Modifiers.File, SyntaxKind.RequiredKeyword => Modifiers.Required, SyntaxKind.FixedKeyword => Modifiers.Fixed, _ => Modifiers.None, }; isDefault |= token.Kind() == SyntaxKind.DefaultKeyword; } } public static SyntaxTokenList GetModifierTokens(SyntaxNode declaration) => declaration switch { MemberDeclarationSyntax memberDecl => memberDecl.Modifiers, ParameterSyntax parameter => parameter.Modifiers, LocalDeclarationStatementSyntax localDecl => localDecl.Modifiers, LocalFunctionStatementSyntax localFunc => localFunc.Modifiers, AccessorDeclarationSyntax accessor => accessor.Modifiers, VariableDeclarationSyntax varDecl => GetModifierTokens(varDecl.GetRequiredParent()), VariableDeclaratorSyntax varDecl => GetModifierTokens(varDecl.GetRequiredParent()), AnonymousFunctionExpressionSyntax anonymous => anonymous.Modifiers, _ => default, }; } |