Augment. When moving enum 'values' constants from augmentation to the introductory declaration, skip 'values' fragment of the augmentation.

This `values` fragment is a purely synthetic construct.

Change-Id: I795e6b2a92d8d5809c8107b41522e0d41c7d03bd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506220
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-05-26 10:39:42 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent a527b4d9b0
commit 53c69a614e
4 changed files with 1206 additions and 147 deletions
@@ -120,9 +120,10 @@ class ElementBuilder {
? lastStaticFragments
: lastInstanceFragments;
var lastFragment = lastFragments[fragment.name];
var fragmentToTrack = fragment;
switch (fragment) {
case FieldFragmentImpl():
_handleInstanceFieldFragment(
fragmentToTrack = _handleInstanceFieldFragment(
instanceElement,
lastFragment,
fragment,
@@ -154,7 +155,7 @@ class ElementBuilder {
default:
throw UnimplementedError('${fragment.runtimeType}');
}
lastFragments[fragment.name] = fragment;
lastFragments[fragmentToTrack.name] = fragmentToTrack;
}
// Mark extension type members.
@@ -403,7 +404,15 @@ class ElementBuilder {
interfaceElement.addConstructor(element);
}
void _handleInstanceFieldFragment(
/// Adds [fieldFragment] into the element model for [instanceElement].
///
/// The returned fragment is the one that should be tracked as the last
/// fragment with this name in its namespace. Usually this is [fieldFragment].
/// For a synthetic enum `values` field from an augmentation, the synthetic
/// field itself is discarded after its initializer elements are moved into
/// the introductory `values` field, so the introductory `values` fragment is
/// returned instead.
FieldFragmentImpl _handleInstanceFieldFragment(
InstanceElementImpl instanceElement,
FragmentImpl? lastFragment,
FieldFragmentImpl fieldFragment,
@@ -411,7 +420,7 @@ class ElementBuilder {
var instanceFragment = fieldFragment.enclosingFragment;
// Move elements of `values` from augmentation to the first fragment.
if (fieldFragment.name == 'values' &&
if (fieldFragment.isOriginEnumValues &&
instanceFragment is EnumFragmentImpl &&
instanceFragment.previousFragment != null) {
var implicitsMap = libraryBuilder.implicitEnumNodes;
@@ -421,7 +430,7 @@ class ElementBuilder {
firstImplicit.valuesInitializer.addElements(
augmentationImplicit.valuesInitializer.elements,
);
return;
return firstImplicit.valuesFragment;
}
instanceFragment.addField(fieldFragment);
@@ -519,6 +528,8 @@ class ElementBuilder {
setterElement.variable = fieldElement;
}
}
return fieldFragment;
}
void _handleInstanceGetterFragment(
@@ -16,6 +16,24 @@ main() {
@reflectiveTest
class EnumDeclarationParserTest extends ParserDiagnosticsTest {
test_augment_blockBody_empty() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E {}
''');
var node = parseResult.findNode.singleEnumDeclaration;
assertParsedNodeText(node, r'''
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: E
body: BlockEnumBody
leftBracket: {
rightBracket: }
''');
}
test_augment_constant_add() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E {
@@ -95,6 +113,23 @@ EnumDeclaration
''');
}
test_augment_emptyBody() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E;
''');
var node = parseResult.findNode.singleEnumDeclaration;
assertParsedNodeText(node, r'''
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: E
body: EmptyEnumBody
semicolon: ;
''');
}
test_augment_implementsClause() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E implements B {}
@@ -116,6 +151,25 @@ EnumDeclaration
''');
}
test_augment_noConstants_semicolon() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E {;}
''');
var node = parseResult.findNode.singleEnumDeclaration;
assertParsedNodeText(node, r'''
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: E
body: BlockEnumBody
leftBracket: {
semicolon: ;
rightBracket: }
''');
}
test_augment_noConstants_semicolon_method() {
var parseResult = parseTestCodeWithDiagnostics(r'''
augment enum E {;
@@ -34,6 +34,208 @@ enum E {
''');
}
test_constant_augmentation_add() async {
var result = await resolveTestCodeWithDiagnostics(r'''
enum A {
v1
}
augment enum A {
v2
}
void f() {
A.v2;
}
''');
assertResolvedNodeText(result.unit, r'''
CompilationUnit
declarations
EnumDeclaration
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: A
body: BlockEnumBody
leftBracket: {
constants
EnumConstantDeclaration
name: v1
constructorElement: <testLibrary>::@enum::A::@constructor::new
declaredFragment: <testLibraryFragment> v1@11
rightBracket: }
declaredFragment: <testLibraryFragment> A@5
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: A
body: BlockEnumBody
leftBracket: {
constants
EnumConstantDeclaration
name: v2
constructorElement: <testLibrary>::@enum::A::@constructor::new
declaredFragment: <testLibraryFragment> v2@36
rightBracket: }
declaredFragment: <testLibraryFragment> A@30
FunctionDeclaration
returnType: NamedType
name: void
element: <null>
type: void
name: f
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
statements
ExpressionStatement
expression: PrefixedIdentifier
prefix: SimpleIdentifier
token: A
element: <testLibrary>::@enum::A
staticType: null
period: .
identifier: SimpleIdentifier
token: v2
element: <testLibrary>::@enum::A::@getter::v2
staticType: A
element: <testLibrary>::@enum::A::@getter::v2
staticType: A
semicolon: ;
rightBracket: }
declaredFragment: <testLibraryFragment> f@47
element: <testLibrary>::@function::f
type: void Function()
staticType: void Function()
declaredFragment: <testLibraryFragment> f@47
element: <testLibrary>::@function::f
type: void Function()
''');
}
test_constant_augmentation_valuesGetter_recovery() async {
var result = await resolveTestCodeWithDiagnostics(r'''
enum A {
v1
}
augment enum A {;
static int get values => 0;
// ^^^^^^
// [diag.valuesDeclarationInEnum] A member named 'values' can't be declared in an enum.
}
augment enum A {
v2
}
void f() {
A.values;
}
''');
assertResolvedNodeText(result.unit, r'''
CompilationUnit
declarations
EnumDeclaration
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: A
body: BlockEnumBody
leftBracket: {
constants
EnumConstantDeclaration
name: v1
constructorElement: <testLibrary>::@enum::A::@constructor::new
declaredFragment: <testLibraryFragment> v1@11
rightBracket: }
declaredFragment: <testLibraryFragment> A@5
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: A
body: BlockEnumBody
leftBracket: {
semicolon: ;
members
MethodDeclaration
modifierKeyword: static
returnType: NamedType
name: int
element: dart:core::@class::int
type: int
propertyKeyword: get
name: values
body: ExpressionFunctionBody
functionDefinition: =>
expression: IntegerLiteral
literal: 0
staticType: int
semicolon: ;
declaredFragment: <testLibraryFragment> values@52
element: <testLibrary>::@enum::A::@getter::values#1
type: int Function()
rightBracket: }
declaredFragment: <testLibraryFragment> A@30
EnumDeclaration
augmentKeyword: augment
enumKeyword: enum
namePart: NameWithTypeParameters
typeName: A
body: BlockEnumBody
leftBracket: {
constants
EnumConstantDeclaration
name: v2
constructorElement: <testLibrary>::@enum::A::@constructor::new
declaredFragment: <testLibraryFragment> v2@87
rightBracket: }
declaredFragment: <testLibraryFragment> A@81
FunctionDeclaration
returnType: NamedType
name: void
element: <null>
type: void
name: f
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
statements
ExpressionStatement
expression: PrefixedIdentifier
prefix: SimpleIdentifier
token: A
element: <testLibrary>::@enum::A
staticType: null
period: .
identifier: SimpleIdentifier
token: values
element: <testLibrary>::@enum::A::@getter::values
staticType: List<A>
element: <testLibrary>::@enum::A::@getter::values
staticType: List<A>
semicolon: ;
rightBracket: }
declaredFragment: <testLibraryFragment> f@98
element: <testLibrary>::@function::f
type: void Function()
staticType: void Function()
declaredFragment: <testLibraryFragment> f@98
element: <testLibrary>::@function::f
type: void Function()
''');
}
test_constructor_argumentList_contextType() async {
var result = await resolveTestCodeWithDiagnostics(r'''
enum E {
File diff suppressed because it is too large Load Diff