diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index acd6d0a30becc..2b26f05ca857c 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -640,19 +640,6 @@ void visitFunctionPointerSignature(IMethodSymbol symbol) foreach (var param in symbol.Parameters) { - // https://github.com/dotnet/roslyn/issues/61647: Use public API. -#if DEBUG - if ((param as Symbols.PublicModel.ParameterSymbol)?.GetSymbol() is { } p) - { - Debug.Assert((p.EffectiveScope, ParameterHelpers.IsRefScopedByDefault(p)) switch - { - (DeclarationScope.Unscoped, false) => true, - (DeclarationScope.RefScoped, true) => true, - _ => false, - }); - } -#endif - AddParameterRefKind(param.RefKind); AddCustomModifiersIfNeeded(param.RefCustomModifiers); @@ -828,8 +815,7 @@ public override void VisitParameter(IParameterSymbol symbol) AddParameterRefKindIfNeeded(symbol); AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().EffectiveScope == DeclarationScope.ValueScoped && + if (symbol.ScopedKind == ScopedKind.ScopedValue && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { AddKeyword(SyntaxKind.ScopedKeyword); @@ -1129,15 +1115,16 @@ private void AddParameterRefKindIfNeeded(IParameterSymbol symbol) { if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeParamsRefOut)) { - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol() is { } parameter && - parameter.EffectiveScope == DeclarationScope.RefScoped && - !ParameterHelpers.IsRefScopedByDefault(parameter) && - !parameter.IsThis && + if (symbol.ScopedKind == ScopedKind.ScopedRef && + !symbol.IsThis && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); + var parameter = (symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol(); + if (parameter is null || !ParameterHelpers.IsRefScopedByDefault(parameter)) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } } AddParameterRefKind(symbol.RefKind); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index 62f5d4d96fa4c..4895b9e96e2e2 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -190,8 +190,7 @@ public override void VisitLocal(ILocalSymbol symbol) if (symbol.IsRef && format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeRef)) { - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.LocalSymbol)?.GetSymbol().Scope == DeclarationScope.RefScoped && + if (symbol.ScopedKind == ScopedKind.ScopedRef && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { AddKeyword(SyntaxKind.ScopedKeyword); @@ -208,8 +207,7 @@ public override void VisitLocal(ILocalSymbol symbol) } } - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.LocalSymbol)?.GetSymbol().Scope == DeclarationScope.ValueScoped && + if (symbol.ScopedKind == ScopedKind.ScopedValue && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { AddKeyword(SyntaxKind.ScopedKeyword); diff --git a/src/Compilers/CSharp/Portable/Symbols/DeclarationScope.cs b/src/Compilers/CSharp/Portable/Symbols/DeclarationScope.cs index 4f810253cade2..dd3b8db748a81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/DeclarationScope.cs +++ b/src/Compilers/CSharp/Portable/Symbols/DeclarationScope.cs @@ -2,16 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis.CSharp.Symbols { - // https://github.com/dotnet/roslyn/issues/61647: Internally, scope is represented with this enum, - // but the public API uses a pair of IsRefScoped and IsValueScoped bools (see ILocalSymbol, - // IParameterSymbol, and ScopedRefAttribute). We should have a common representation. - // And we should use common terms for the attribute and enum names. internal enum DeclarationScope : byte { Unscoped = 0, RefScoped = 1, ValueScoped = 2, } + + internal static class DeclarationScopeExtensions + { + internal static ScopedKind AsScopedKind(this DeclarationScope scope) + { + return scope switch + { + DeclarationScope.Unscoped => ScopedKind.None, + DeclarationScope.RefScoped => ScopedKind.ScopedRef, + DeclarationScope.ValueScoped => ScopedKind.ScopedValue, + _ => throw ExceptionUtilities.UnexpectedValue(scope), + }; + } + } } diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/LocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/LocalSymbol.cs index fc4afb2708ebf..efd106f4fb90f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/LocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/LocalSymbol.cs @@ -51,6 +51,8 @@ bool ILocalSymbol.IsFunctionValue RefKind ILocalSymbol.RefKind => _underlying.RefKind; + ScopedKind ILocalSymbol.ScopedKind => _underlying.Scope.AsScopedKind(); + bool ILocalSymbol.HasConstantValue => _underlying.HasConstantValue; object ILocalSymbol.ConstantValue => _underlying.ConstantValue; diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs index 4248d36d27694..0e2adc20699bb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs @@ -58,6 +58,8 @@ IParameterSymbol IParameterSymbol.OriginalDefinition RefKind IParameterSymbol.RefKind => _underlying.RefKind; + ScopedKind IParameterSymbol.ScopedKind => _underlying.EffectiveScope.AsScopedKind(); + bool IParameterSymbol.IsDiscard => _underlying.IsDiscard; bool IParameterSymbol.IsParams => _underlying.IsParams; diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs index 76cae51fc8c0b..4ed6d2a213243 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs @@ -321,8 +321,6 @@ static void Main() } }"; var comp = CreateCompilation(source1, references: new[] { ref0 }); - // https://github.com/dotnet/roslyn/issues/61647: If the [ScopedRef] scoped value is an int - // rather than a pair of bools, the compiler should reject attribute values that it does not recognize. comp.VerifyDiagnostics(); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 48cc150d4ba03..c11a5fab0837b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -9873,9 +9873,7 @@ private static void VerifyParameterSymbol(ParameterSymbol parameter, string expe private static void VerifyParameterSymbol(IParameterSymbol parameter, string expectedDisplayString, RefKind expectedRefKind, DeclarationScope expectedScope) { Assert.Equal(expectedRefKind, parameter.RefKind); - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - //Assert.Equal(expectedScope == DeclarationScope.RefScoped, parameter.IsRefScoped); - //Assert.Equal(expectedScope == DeclarationScope.ValueScoped, parameter.IsValueScoped); + Assert.Equal(expectedScope.AsScopedKind(), parameter.ScopedKind); Assert.Equal(expectedDisplayString, parameter.ToDisplayString(displayFormatWithScoped)); } @@ -10369,9 +10367,7 @@ private static void VerifyLocalSymbol(LocalSymbol local, string expectedDisplayS private static void VerifyLocalSymbol(ILocalSymbol local, string expectedDisplayString, RefKind expectedRefKind, DeclarationScope expectedScope) { Assert.Equal(expectedRefKind, local.RefKind); - // https://github.com/dotnet/roslyn/issues/61647: Use public API. - //Assert.Equal(expectedScope == DeclarationScope.RefScoped, local.IsRefScoped); - //Assert.Equal(expectedScope == DeclarationScope.ValueScoped, local.IsValueScoped); + Assert.Equal(expectedScope.AsScopedKind(), local.ScopedKind); Assert.Equal(expectedDisplayString, local.ToDisplayString(displayFormatWithScoped)); } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index dcbc44f41fc46..f13aa08394547 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -15,6 +15,12 @@ Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.IsGeneratedCode.get Microsoft.CodeAnalysis.GeneratorDriver.ReplaceGenerators(System.Collections.Immutable.ImmutableArray generators) -> Microsoft.CodeAnalysis.GeneratorDriver! Microsoft.CodeAnalysis.ILocalSymbol.IsForEach.get -> bool Microsoft.CodeAnalysis.ILocalSymbol.IsUsing.get -> bool +Microsoft.CodeAnalysis.ILocalSymbol.ScopedKind.get -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.IParameterSymbol.ScopedKind.get -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.ScopedKind.None = 0 -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.ScopedKind.ScopedRef = 1 -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.ScopedKind.ScopedValue = 2 -> Microsoft.CodeAnalysis.ScopedKind override sealed Microsoft.CodeAnalysis.CompilationOptions.GetHashCode() -> int static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.Action! onDispose) -> Microsoft.CodeAnalysis.ModuleMetadata! Microsoft.CodeAnalysis.INamedTypeSymbol.IsFileLocal.get -> bool diff --git a/src/Compilers/Core/Portable/Symbols/ILocalSymbol.cs b/src/Compilers/Core/Portable/Symbols/ILocalSymbol.cs index 9c16dc72409fa..17054b36fd744 100644 --- a/src/Compilers/Core/Portable/Symbols/ILocalSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/ILocalSymbol.cs @@ -40,6 +40,11 @@ public interface ILocalSymbol : ISymbol /// RefKind RefKind { get; } + /// + /// Returns the scoped kind of the local. + /// + ScopedKind ScopedKind { get; } + /// /// Returns false if the local variable wasn't declared as "const", or constant value was omitted or erroneous. /// True otherwise. diff --git a/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs b/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs index 7c77dbe8962c6..635b36bbf1f0c 100644 --- a/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs @@ -22,6 +22,11 @@ public interface IParameterSymbol : ISymbol /// RefKind RefKind { get; } + /// + /// Returns the scoped kind of the parameter. + /// + ScopedKind ScopedKind { get; } + /// /// Returns true if the parameter was declared as a parameter array. /// diff --git a/src/Compilers/Core/Portable/Symbols/ScopedKind.cs b/src/Compilers/Core/Portable/Symbols/ScopedKind.cs new file mode 100644 index 0000000000000..cf48ffe3cf3fe --- /dev/null +++ b/src/Compilers/Core/Portable/Symbols/ScopedKind.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Microsoft.CodeAnalysis +{ + /// + /// Enumeration for kinds of scoped modifiers. + /// + public enum ScopedKind : byte + { + /// + /// Not scoped. + /// + None = 0, + + /// + /// A ref scoped to the enclosing block or method. + /// + ScopedRef = 1, + + /// + /// A value scoped to the enclosing block or method. + /// + ScopedValue = 2, + } +} diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb index b446c8cd44acc..4eaa90c458499 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb @@ -303,6 +303,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property IParameterSymbol_ScopedKind As ScopedKind Implements IParameterSymbol.ScopedKind + Get + Return ScopedKind.None + End Get + End Property + Private ReadOnly Property IParameterSymbol_Type As ITypeSymbol Implements IParameterSymbol.Type Get Return Me.Type diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/LocalSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/LocalSymbol.vb index 7cc9d5b00ee12..1cb85ee18e920 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/LocalSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/LocalSymbol.vb @@ -301,6 +301,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property ILocalSymbol_ScopedKind As ScopedKind Implements ILocalSymbol.ScopedKind + Get + Return ScopedKind.None + End Get + End Property + Public MustOverride ReadOnly Property IsFunctionValue As Boolean Implements ILocalSymbol.IsFunctionValue Friend ReadOnly Property IsCompilerGenerated As Boolean diff --git a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs index 36caad767784c..ded0775e915b9 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs @@ -68,6 +68,8 @@ public override TResult Accept(SymbolVisitor CustomModifiers => ImmutableArray.Create(); + public ScopedKind ScopedKind => ScopedKind.None; + public bool IsDiscard => false; } }