diff --git a/pkg/analysis_server/test/tool/lsp_spec/codegen_test.dart b/pkg/analysis_server/test/tool/lsp_spec/codegen_test.dart index fbaa2cb3af7..b6b5a490ca0 100644 --- a/pkg/analysis_server/test/tool/lsp_spec/codegen_test.dart +++ b/pkg/analysis_server/test/tool/lsp_spec/codegen_test.dart @@ -22,7 +22,7 @@ class CodegenTest { LspEnum( name: 'x', typeOfValues: TypeReference.int, - members: [ + constants: [ Constant(name: 'new', type: TypeReference.string, value: '1'), ], ), diff --git a/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart b/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart index 63bd35ed7c9..8c6336f1531 100644 --- a/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart +++ b/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart @@ -29,13 +29,13 @@ ast.LspEnum _enum({ String name = 'x', String type = 'int', bool flags = false, - required List members, + required List constants, }) { return ast.LspEnum( name: name, typeOfValues: ast.TypeReference(type), flags: flags, - members: members, + constants: constants, ); } @@ -48,22 +48,35 @@ ast.UnionType _union(List names) => class DartTest { void test_flags_requiresPowerOfTwo_double() { expect( - () => _enum(flags: true, members: [_constant(1.2)]), - throwsA(isA()), + () => _enum(flags: true, constants: [_constant(1.2)]), + throwsArgumentError, ); } void test_flags_requiresPowerOfTwo_nonPowerOfTwo() { expect( - () => _enum(flags: true, members: [_constant(3)]), - throwsA(isA()), + () => _enum(flags: true, constants: [_constant(3)]), + throwsArgumentError, ); } void test_flags_requiresPowerOfTwo_string() { expect( - () => _enum(flags: true, members: [_constant('test')]), - throwsA(isA()), + () => _enum(flags: true, constants: [_constant('test')]), + throwsArgumentError, + ); + } + + void test_flags_requiresUniqueValues() { + expect( + () => _enum( + flags: true, + constants: [ + _constant(1, name: 'a'), + _constant(1, name: 'b'), + ], + ), + throwsArgumentError, ); } diff --git a/pkg/analysis_server/test/tool/lsp_spec/meta_model_test.dart b/pkg/analysis_server/test/tool/lsp_spec/meta_model_test.dart index a161958ed86..9b7842928c9 100644 --- a/pkg/analysis_server/test/tool/lsp_spec/meta_model_test.dart +++ b/pkg/analysis_server/test/tool/lsp_spec/meta_model_test.dart @@ -385,13 +385,10 @@ Sometimes after a blank line we'll have a note. expect(output[0], const TypeMatcher()); var namespace = output[0] as LspEnum; - expect(namespace.members, hasLength(3)); - for (var m in namespace.members) { - expect(m, const TypeMatcher()); - } - var create = namespace.members[0] as Constant, - delete = namespace.members[1] as Constant, - rename = namespace.members[2] as Constant; + expect(namespace.constants, hasLength(3)); + var create = namespace.constants[0], + delete = namespace.constants[1], + rename = namespace.constants[2]; expect(create.name, equals('Create')); expect(create.type, isSimpleType('ResourceOperationKind')); expect(create.comment, equals('Supports creating new files and folders.')); diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart index c3ff4b3efa6..9c6183cfc23 100644 --- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart +++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart @@ -633,7 +633,7 @@ void _writeDocCommentsAndAnnotations( void _writeEnumClass(IndentableStringBuffer buffer, LspEnum namespace) { _writeDocCommentsAndAnnotations(buffer, namespace); - var consts = namespace.members.cast().toList(); + var consts = namespace.constants; var namespaceName = namespace.name; var typeOfValues = namespace.typeOfValues; var allowsAnyValue = enumClassAllowsAnyValue(namespaceName); @@ -674,10 +674,10 @@ void _writeEnumClass(IndentableStringBuffer buffer, LspEnum namespace) { ..outdent() ..writeIndentedln('}'); } - namespace.members.whereType().forEach((cons) { + for (var cons in consts) { // We don't use any deprecated enum values, so omit them entirely. if (cons.isDeprecated) { - return; + continue; } _writeDocCommentsAndAnnotations(buffer, cons); var memberName = cons.dartSafeName; @@ -685,7 +685,7 @@ void _writeEnumClass(IndentableStringBuffer buffer, LspEnum namespace) { buffer.writeIndentedln( 'static const $memberName = $namespaceName$constructorName($value);', ); - }); + } if (namespace.flags) { buffer ..writeln() diff --git a/pkg/analysis_server/tool/lsp_spec/custom/interactive_forms.dart b/pkg/analysis_server/tool/lsp_spec/custom/interactive_forms.dart index 6a2502f4b66..5292c563cdc 100644 --- a/pkg/analysis_server/tool/lsp_spec/custom/interactive_forms.dart +++ b/pkg/analysis_server/tool/lsp_spec/custom/interactive_forms.dart @@ -137,7 +137,7 @@ final interactiveFormClasses = [ name: 'FileExistence', typeOfValues: TypeReference.int, flags: true, - members: [ + constants: [ // Values should be powers of 2 to allow New|Existing. Constant( name: 'New', @@ -161,7 +161,7 @@ final interactiveFormClasses = [ name: 'FileType', typeOfValues: TypeReference.int, flags: true, - members: [ + constants: [ // Values should be powers of 2 to allow Regular|Directory. Constant( name: 'Regular', diff --git a/pkg/analysis_server/tool/lsp_spec/meta_model.dart b/pkg/analysis_server/tool/lsp_spec/meta_model.dart index dfca1dbb254..ffed2163dfc 100644 --- a/pkg/analysis_server/tool/lsp_spec/meta_model.dart +++ b/pkg/analysis_server/tool/lsp_spec/meta_model.dart @@ -185,23 +185,49 @@ abstract class LspEntity { class LspEnum extends LspEntity { final TypeBase typeOfValues; final bool flags; - final List members; + final List constants; LspEnum({ required super.name, super.comment, super.isProposed, required this.typeOfValues, this.flags = false, - required this.members, - }) : assert( - !flags || - members - .whereType() - .map((member) => int.tryParse(member.value)) - .every((value) => value != null && _isPowerOfTwo(value)), - 'flags enums require all enum values to be int powers of two.', - ) { - members.sortBy((member) => member.name.toLowerCase()); + required this.constants, + }) { + _validateFlagsEnum(); + constants.sortBy((constant) => constant.name.toLowerCase()); + } + + void _validateFlagsEnum() { + if (!flags) { + return; + } + + if (typeOfValues.dartTypeWithTypeArgs != 'int') { + throw ArgumentError( + 'Flags enum $name has value type ${typeOfValues.dartTypeWithTypeArgs}; ' + 'flags enum values must be ints.', + ); + } + + var usedValues = {}; + for (var constant in constants) { + var value = int.tryParse(constant.value); + if (value == null || !_isPowerOfTwo(value)) { + throw ArgumentError( + 'Flags enum $name constant ${constant.name} has value ' + '${constant.value}; ' + 'flags enum values must be int powers of two.', + ); + } + + if (!usedValues.add(value)) { + throw ArgumentError( + 'Flags enum $name has duplicate value $value; ' + 'flags enum values must be unique.', + ); + } + } } } diff --git a/pkg/analysis_server/tool/lsp_spec/meta_model_cleaner.dart b/pkg/analysis_server/tool/lsp_spec/meta_model_cleaner.dart index 84cbbdf8bf3..534337184ed 100644 --- a/pkg/analysis_server/tool/lsp_spec/meta_model_cleaner.dart +++ b/pkg/analysis_server/tool/lsp_spec/meta_model_cleaner.dart @@ -191,9 +191,9 @@ class LspMetaModelCleaner { isProposed: namespace.isProposed, typeOfValues: namespace.typeOfValues, flags: namespace.flags, - members: namespace.members + constants: namespace.constants .where(_includeEntityInOutput) - .map((member) => _cleanMember(namespace.name, member)) + .map(_cleanConst) .toList(), ); } @@ -387,7 +387,7 @@ class LspMetaModelCleaner { isProposed: dest.isProposed, typeOfValues: dest.typeOfValues, flags: dest.flags || source.flags, - members: [...dest.members, ...source.members], + constants: [...dest.constants, ...source.constants], ); } else if (source is Interface && dest is Interface) { return Interface( @@ -533,7 +533,7 @@ class LspMetaModelCleaner { isProposed: type.isProposed, typeOfValues: type.typeOfValues, flags: type.flags, - members: type.members, + constants: type.constants, ); } else { throw 'Renaming ${type.runtimeType} is not implemented'; diff --git a/pkg/analysis_server/tool/lsp_spec/meta_model_reader.dart b/pkg/analysis_server/tool/lsp_spec/meta_model_reader.dart index 36803ad17ea..94f8669aa93 100644 --- a/pkg/analysis_server/tool/lsp_spec/meta_model_reader.dart +++ b/pkg/analysis_server/tool/lsp_spec/meta_model_reader.dart @@ -60,7 +60,7 @@ class LspMetaModelReader { return LspMetaModel( types: types, - methods: methodsEnum?.members.cast().toList() ?? [], + methods: methodsEnum?.constants.toList() ?? [], ); } @@ -106,7 +106,7 @@ class LspMetaModelReader { name: 'Method', comment: comment, typeOfValues: TypeReference.string, - members: methodConstants, + constants: methodConstants, ); } @@ -299,7 +299,7 @@ class LspMetaModelReader { comment: documentation, isProposed: _isProposed(documentation), typeOfValues: baseType, - members: [ + constants: [ ...?(model['values'] as List?)?.map((p) => _extractEnumValue(type, p)), ], );