diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart index 4c4175aa868..00010039dfa 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart @@ -973,8 +973,8 @@ class ForwardingListener implements Listener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { - listener?.endLibraryName(libraryKeyword, semicolon); + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { + listener?.endLibraryName(libraryKeyword, semicolon, hasName); } @override diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart index 2ace0c831fb..42b13f4400d 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart @@ -968,7 +968,7 @@ class Listener implements UnescapeErrorListener { /// Handle the end of a library directive. Substructures: /// - Metadata /// - Library name (a qualified identifier) - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { logEvent("LibraryName"); } diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart index 26082722661..aa768c94108 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart @@ -654,17 +654,23 @@ class Parser { /// ``` /// libraryDirective: - /// 'library' qualified ';' + /// 'library' qualified? ';' /// ; /// ``` Token parseLibraryName(Token libraryKeyword) { assert(optional('library', libraryKeyword)); listener.beginUncategorizedTopLevelDeclaration(libraryKeyword); listener.beginLibraryName(libraryKeyword); - Token token = parseQualified(libraryKeyword, IdentifierContext.libraryName, - IdentifierContext.libraryNameContinuation); - token = ensureSemicolon(token); - listener.endLibraryName(libraryKeyword, token); + Token token = libraryKeyword.next!; + bool hasName = !optional(';', token); + if (hasName) { + token = parseQualified(libraryKeyword, IdentifierContext.libraryName, + IdentifierContext.libraryNameContinuation); + token = ensureSemicolon(token); + } else { + token = ensureSemicolon(libraryKeyword); + } + listener.endLibraryName(libraryKeyword, token, hasName); return token; } diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart index 04ab6247351..9f94cc7e185 100644 --- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart +++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart @@ -130,7 +130,7 @@ void sendAnalysisNotificationOverrides( String? _computeLibraryName(CompilationUnit unit) { for (var directive in unit.directives) { if (directive is LibraryDirective) { - return directive.name.name; + return directive.name2?.name; } } for (var directive in unit.directives) { diff --git a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart index 63cf6843313..dc7542c9a37 100644 --- a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart +++ b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart @@ -432,12 +432,9 @@ class KytheDartVisitor extends GeneralizingAstVisitor with OutputUtils { } } - var start = 0; - var end = 0; - if (libraryDirective != null) { - start = libraryDirective.name.offset; - end = libraryDirective.name.end; - } + final libraryName = libraryDirective?.name2; + final start = libraryName?.offset ?? 0; + final end = libraryName?.end ?? 0; // package node var packageVName = addNodeAndFacts(schema.PACKAGE_KIND, diff --git a/pkg/analysis_server/tool/code_completion/code_metrics.dart b/pkg/analysis_server/tool/code_completion/code_metrics.dart index 5d4ea8fbb1c..5aeddc0d526 100644 --- a/pkg/analysis_server/tool/code_completion/code_metrics.dart +++ b/pkg/analysis_server/tool/code_completion/code_metrics.dart @@ -871,7 +871,7 @@ class CodeShapeDataCollector extends RecursiveAstVisitor { _visitChildren(node, { 'documentationComment': node.documentationComment, 'metadata': node.metadata, - 'name': node.name, + 'name': node.name2, }); super.visitLibraryDirective(node); } diff --git a/pkg/analyzer/lib/dart/analysis/features.dart b/pkg/analyzer/lib/dart/analysis/features.dart index 3fea176f8da..395c6ef322f 100644 --- a/pkg/analyzer/lib/dart/analysis/features.dart +++ b/pkg/analyzer/lib/dart/analysis/features.dart @@ -66,6 +66,9 @@ abstract class Feature { static final nonfunction_type_aliases = ExperimentalFeatures.nonfunction_type_aliases; + /// Feature information for unnamed libraries. + static final unnamedLibraries = ExperimentalFeatures.unnamed_libraries; + /// Feature information for variance. static final variance = ExperimentalFeatures.variance; diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart index 40e4945ee74..bba3243fbff 100644 --- a/pkg/analyzer/lib/dart/ast/ast.dart +++ b/pkg/analyzer/lib/dart/ast/ast.dart @@ -3334,7 +3334,7 @@ abstract class LibraryAugmentationDirective implements UriBasedDirective { /// A library directive. /// /// libraryDirective ::= -/// [Annotation] 'library' [Identifier] ';' +/// [Annotation] 'library' [LibraryIdentifier]? ';' /// /// Clients may not extend, implement or mix-in this class. abstract class LibraryDirective implements Directive { @@ -3342,8 +3342,12 @@ abstract class LibraryDirective implements Directive { Token get libraryKeyword; /// Return the name of the library being defined. + @Deprecated('Use name2') LibraryIdentifier get name; + /// Return the name of the library being defined. + LibraryIdentifier? get name2; + /// Return the semicolon terminating the directive. Token get semicolon; } diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart index 48e6a50ae17..18513d574c1 100644 --- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart +++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart @@ -40,6 +40,7 @@ final _knownFeatures = { EnableString.super_parameters: ExperimentalFeatures.super_parameters, EnableString.test_experiment: ExperimentalFeatures.test_experiment, EnableString.triple_shift: ExperimentalFeatures.triple_shift, + EnableString.unnamed_libraries: ExperimentalFeatures.unnamed_libraries, EnableString.value_class: ExperimentalFeatures.value_class, EnableString.variance: ExperimentalFeatures.variance, }; @@ -110,6 +111,9 @@ class EnableString { /// String to enable the experiment "triple-shift" static const String triple_shift = 'triple-shift'; + /// String to enable the experiment "unnamed-libraries" + static const String unnamed_libraries = 'unnamed-libraries'; + /// String to enable the experiment "value-class" static const String value_class = 'value-class'; @@ -333,8 +337,18 @@ class ExperimentalFeatures { releaseVersion: Version.parse('2.14.0'), ); - static final value_class = ExperimentalFeature( + static final unnamed_libraries = ExperimentalFeature( index: 21, + enableString: EnableString.unnamed_libraries, + isEnabledByDefault: IsEnabledByDefault.unnamed_libraries, + isExpired: IsExpired.unnamed_libraries, + documentation: 'Unnamed libraries', + experimentalReleaseVersion: null, + releaseVersion: null, + ); + + static final value_class = ExperimentalFeature( + index: 22, enableString: EnableString.value_class, isEnabledByDefault: IsEnabledByDefault.value_class, isExpired: IsExpired.value_class, @@ -344,7 +358,7 @@ class ExperimentalFeatures { ); static final variance = ExperimentalFeature( - index: 22, + index: 23, enableString: EnableString.variance, isEnabledByDefault: IsEnabledByDefault.variance, isExpired: IsExpired.variance, @@ -420,6 +434,9 @@ class IsEnabledByDefault { /// Default state of the experiment "triple-shift" static const bool triple_shift = true; + /// Default state of the experiment "unnamed-libraries" + static const bool unnamed_libraries = false; + /// Default state of the experiment "value-class" static const bool value_class = false; @@ -494,6 +511,9 @@ class IsExpired { /// Expiration status of the experiment "triple-shift" static const bool triple_shift = true; + /// Expiration status of the experiment "unnamed-libraries" + static const bool unnamed_libraries = false; + /// Expiration status of the experiment "value-class" static const bool value_class = false; @@ -574,6 +594,10 @@ mixin _CurrentState { /// Current state for the flag "triple-shift" bool get triple_shift => isEnabled(ExperimentalFeatures.triple_shift); + /// Current state for the flag "unnamed-libraries" + bool get unnamed_libraries => + isEnabled(ExperimentalFeatures.unnamed_libraries); + /// Current state for the flag "value-class" bool get value_class => isEnabled(ExperimentalFeatures.value_class); diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart index f97b9a1a8cc..346739d2340 100644 --- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart +++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart @@ -872,7 +872,7 @@ class FileState { } } else if (directive is LibraryDirective) { libraryDirective = UnlinkedLibraryDirective( - name: directive.name.name, + name: directive.name2?.name, ); } else if (directive is PartDirective) { parts.add( diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart index 6e537eb63d2..fdf249dde4e 100644 --- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart +++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart @@ -704,7 +704,7 @@ class LibraryAnalyzer { directive.element = containerElement; } else if (directive is LibraryDirectiveImpl) { directive.element = containerElement; - libraryNameNode = directive.name; + libraryNameNode = directive.name2; } else if (directive is PartDirectiveImpl) { if (containerKind is LibraryFileKind && containerElement is LibraryElementImpl) { diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart index 8ce40c0fead..f5c307271b4 100644 --- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart +++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart @@ -181,7 +181,7 @@ class UnlinkedLibraryAugmentationDirective { } class UnlinkedLibraryDirective { - final String name; + final String? name; UnlinkedLibraryDirective({ required this.name, @@ -191,12 +191,12 @@ class UnlinkedLibraryDirective { SummaryDataReader reader, ) { return UnlinkedLibraryDirective( - name: reader.readStringUtf8(), + name: reader.readOptionalStringUtf8(), ); } void write(BufferedSink sink) { - sink.writeStringUtf8(name); + sink.writeOptionalStringUtf8(name); } } diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart index 6aa163ef8d0..8c5ee6523bb 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast.dart @@ -7804,7 +7804,7 @@ class LibraryDirectiveImpl extends DirectiveImpl implements LibraryDirective { Token libraryKeyword; /// The name of the library being defined. - LibraryIdentifierImpl _name; + LibraryIdentifierImpl? _name; /// The semicolon terminating the directive. @override @@ -7817,7 +7817,7 @@ class LibraryDirectiveImpl extends DirectiveImpl implements LibraryDirective { required super.comment, required super.metadata, required this.libraryKeyword, - required LibraryIdentifierImpl name, + required LibraryIdentifierImpl? name, required this.semicolon, }) : _name = name { _becomeParentOf(_name); @@ -7830,16 +7830,20 @@ class LibraryDirectiveImpl extends DirectiveImpl implements LibraryDirective { Token get firstTokenAfterCommentAndMetadata => libraryKeyword; @override - LibraryIdentifierImpl get name => _name; + @Deprecated('Use name2') + LibraryIdentifierImpl get name => _name!; - set name(LibraryIdentifier name) { - _name = _becomeParentOf(name as LibraryIdentifierImpl); + set name(LibraryIdentifier? name) { + _name = _becomeParentOf(name as LibraryIdentifierImpl?); } + @override + LibraryIdentifierImpl? get name2 => _name; + @override ChildEntities get _childEntities => super._childEntities ..addToken('libraryKeyword', libraryKeyword) - ..addNode('name', name) + ..addNode('name', name2) ..addToken('semicolon', semicolon); @override @@ -7848,7 +7852,7 @@ class LibraryDirectiveImpl extends DirectiveImpl implements LibraryDirective { @override void visitChildren(AstVisitor visitor) { super.visitChildren(visitor); - _name.accept(visitor); + _name?.accept(visitor); } } diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart index 5ac9ea62c07..a674efa1ba8 100644 --- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart +++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart @@ -793,7 +793,7 @@ class ToSourceVisitor implements AstVisitor { void visitLibraryDirective(LibraryDirective node) { _visitNodeList(node.metadata, separator: ' ', suffix: ' '); sink.write('library '); - _visitNode(node.name); + _visitNode(node.name2); sink.write(';'); } diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart index 270915fdd2a..328579034db 100644 --- a/pkg/analyzer/lib/src/dart/ast/utilities.dart +++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart @@ -915,7 +915,7 @@ class AstComparator implements AstVisitor { node.documentationComment, other.documentationComment) && _isEqualNodeLists(node.metadata, other.metadata) && isEqualTokens(node.libraryKeyword, other.libraryKeyword) && - isEqualNodes(node.name, other.name) && + isEqualNodes(node.name2, other.name2) && isEqualTokens(node.semicolon, other.semicolon); } @@ -2780,7 +2780,7 @@ class NodeReplacer extends ThrowingAstVisitor { @override bool visitLibraryDirective(covariant LibraryDirectiveImpl node) { - if (identical(node.name, _oldNode)) { + if (identical(node.name2, _oldNode)) { node.name = _newNode as LibraryIdentifier; return true; } diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart index 5bb878a62e0..6f06439b546 100644 --- a/pkg/analyzer/lib/src/fasta/ast_builder.dart +++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart @@ -141,6 +141,9 @@ class AstBuilder extends StackListener { /// `true` if records are enabled final bool enableRecords; + /// `true` if unnamed-library behavior is enabled + final bool enableUnnamedLibraries; + final FeatureSet _featureSet; final LineInfo _lineInfo; @@ -163,6 +166,8 @@ class AstBuilder extends StackListener { enableEnhancedEnums = _featureSet.isEnabled(Feature.enhanced_enums), enableMacros = _featureSet.isEnabled(Feature.macros), enableRecords = _featureSet.isEnabled(Feature.records), + enableUnnamedLibraries = + _featureSet.isEnabled(Feature.unnamedLibraries), uri = uri ?? fileUri; @override @@ -2131,13 +2136,20 @@ class AstBuilder extends StackListener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { assert(optional('library', libraryKeyword)); assert(optional(';', semicolon)); debugEvent("LibraryName"); - var libraryName = pop() as List; - var name = ast.libraryIdentifier(libraryName); + var libraryName = hasName ? pop() as List? : null; + + if (!hasName && !enableUnnamedLibraries) { + _reportFeatureNotEnabled( + feature: ExperimentalFeatures.unnamed_libraries, + startToken: libraryKeyword, + ); + } + var name = libraryName == null ? null : ast.libraryIdentifier(libraryName); var metadata = pop() as List?; var comment = _findComment(metadata, libraryKeyword); directives.add( diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart index 94964b06b9a..54a9b3f2aba 100644 --- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart +++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart @@ -399,17 +399,20 @@ class AstTestFactory { astFactory.labeledStatement(labels, statement); static LibraryDirectiveImpl libraryDirective( - List metadata, LibraryIdentifier libraryName) => + List metadata, LibraryIdentifier? libraryName) => LibraryDirectiveImpl( comment: null, metadata: metadata, libraryKeyword: TokenFactory.tokenFromKeyword(Keyword.LIBRARY), - name: libraryName as LibraryIdentifierImpl, + name: libraryName as LibraryIdentifierImpl?, semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON), ); - static LibraryDirectiveImpl libraryDirective2(String libraryName) => - libraryDirective([], libraryIdentifier2([libraryName])); + static LibraryDirectiveImpl libraryDirective2(String? libraryName) => + libraryDirective( + [], + libraryName == null ? null : libraryIdentifier2([libraryName]), + ); static LibraryIdentifierImpl libraryIdentifier( List components) => diff --git a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart index 0f1feb55d1e..a1b0394e883 100644 --- a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart +++ b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart @@ -670,7 +670,7 @@ class AstTextPrinter extends ThrowingAstVisitor { void visitLibraryDirective(LibraryDirective node) { _directive(node); _token(node.libraryKeyword); - node.name.accept(this); + node.name2?.accept(this); _token(node.semicolon); } diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart index 365c3398baf..fc0b1e5224d 100644 --- a/pkg/analyzer/lib/src/summary2/informative_data.dart +++ b/pkg/analyzer/lib/src/summary2/informative_data.dart @@ -1423,8 +1423,11 @@ class _InformativeDataWriter { for (var directive in unit.directives) { firstDirective ??= directive; if (directive is LibraryDirective) { - nameOffset = directive.name.offset; - nameLength = directive.name.length; + final libraryName = directive.name2; + if (libraryName != null) { + nameOffset = libraryName.offset; + nameLength = libraryName.length; + } break; } } diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart index 242be884475..5e9a1b5ea3e 100644 --- a/pkg/analyzer/lib/src/summary2/library_builder.dart +++ b/pkg/analyzer/lib/src/summary2/library_builder.dart @@ -760,9 +760,12 @@ class LibraryBuilder { var nameLength = 0; for (final directive in libraryUnitNode.directives) { if (directive is ast.LibraryDirective) { - name = directive.name.components.map((e) => e.name).join('.'); - nameOffset = directive.name.offset; - nameLength = directive.name.length; + final nameIdentifier = directive.name2; + if (nameIdentifier != null) { + name = nameIdentifier.components.map((e) => e.name).join('.'); + nameOffset = nameIdentifier.offset; + nameLength = nameIdentifier.length; + } break; } } diff --git a/pkg/analyzer/test/generated/error_parser_test.dart b/pkg/analyzer/test/generated/error_parser_test.dart index 8428cced583..f30512fde7e 100644 --- a/pkg/analyzer/test/generated/error_parser_test.dart +++ b/pkg/analyzer/test/generated/error_parser_test.dart @@ -2172,12 +2172,6 @@ class Wrong { expect(parameter.name, isNotNull); } - void test_missingNameInLibraryDirective() { - CompilationUnit unit = parseCompilationUnit("library;", - errors: [expectedError(ParserErrorCode.MISSING_IDENTIFIER, 7, 1)]); - expect(unit, isNotNull); - } - void test_missingNameInPartOfDirective() { CompilationUnit unit = parseCompilationUnit("part of;", errors: [expectedError(ParserErrorCode.EXPECTED_STRING_LITERAL, 7, 1)]); @@ -2837,6 +2831,18 @@ main() { errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 7, 1)]); } + void test_unnamedLibraryDirective() { + CompilationUnit unit = parseCompilationUnit("library;", + errors: [expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 0, 7)]); + expect(unit, isNotNull); + } + + void test_unnamedLibraryDirective_enabled() { + CompilationUnit unit = parseCompilationUnit("library;", + featureSet: FeatureSets.latestWithExperiments); + expect(unit, isNotNull); + } + void test_unterminatedString_at_eof() { // Although the "unterminated string" error message is produced by the // scanner, we need to verify that the parser can handle the tokens diff --git a/pkg/analyzer/test/generated/parser_fasta_listener.dart b/pkg/analyzer/test/generated/parser_fasta_listener.dart index c5ebdacd85d..bbe9421f4b5 100644 --- a/pkg/analyzer/test/generated/parser_fasta_listener.dart +++ b/pkg/analyzer/test/generated/parser_fasta_listener.dart @@ -1021,9 +1021,9 @@ class ForwardingTestListener extends ForwardingListener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { end('LibraryName'); - super.endLibraryName(libraryKeyword, semicolon); + super.endLibraryName(libraryKeyword, semicolon, hasName); } @override diff --git a/pkg/analyzer/test/generated/simple_parser_test.dart b/pkg/analyzer/test/generated/simple_parser_test.dart index 0f1b1ea7639..53c2c61c52d 100644 --- a/pkg/analyzer/test/generated/simple_parser_test.dart +++ b/pkg/analyzer/test/generated/simple_parser_test.dart @@ -63,13 +63,13 @@ class SimpleParserTest extends FastaParserTestCase { return classDecl.implementsClause!; } - LibraryIdentifier parseLibraryIdentifier(String name) { + LibraryIdentifier? parseLibraryIdentifier(String name) { createParser('library $name;'); CompilationUnit unit = parser.parseCompilationUnit2(); expect(unit, isNotNull); expect(unit.directives, hasLength(1)); var directive = unit.directives[0] as LibraryDirective; - return directive.name; + return directive.name2; } /// Parse the given [content] as a sequence of statements by enclosing it in a @@ -1468,7 +1468,7 @@ void main() {final c = C();} void test_parseLibraryIdentifier_builtin() { String name = "deferred"; - LibraryIdentifier identifier = parseLibraryIdentifier(name); + LibraryIdentifier identifier = parseLibraryIdentifier(name)!; expectNotNullIfNoErrors(identifier); assertNoErrors(); expect(identifier.name, name); @@ -1484,7 +1484,7 @@ void main() {final c = C();} void test_parseLibraryIdentifier_multiple() { String name = "a.b.c"; - LibraryIdentifier identifier = parseLibraryIdentifier(name); + LibraryIdentifier identifier = parseLibraryIdentifier(name)!; expectNotNullIfNoErrors(identifier); assertNoErrors(); expect(identifier.name, name); @@ -1492,7 +1492,7 @@ void main() {final c = C();} void test_parseLibraryIdentifier_pseudo() { String name = "await"; - LibraryIdentifier identifier = parseLibraryIdentifier(name); + LibraryIdentifier identifier = parseLibraryIdentifier(name)!; expectNotNullIfNoErrors(identifier); assertNoErrors(); expect(identifier.name, name); @@ -1501,7 +1501,7 @@ void main() {final c = C();} void test_parseLibraryIdentifier_single() { String name = "a"; - LibraryIdentifier identifier = parseLibraryIdentifier(name); + LibraryIdentifier identifier = parseLibraryIdentifier(name)!; expectNotNullIfNoErrors(identifier); assertNoErrors(); expect(identifier.name, name); diff --git a/pkg/analyzer/test/generated/top_level_parser_test.dart b/pkg/analyzer/test/generated/top_level_parser_test.dart index 5cf20d317e8..74fabcfff18 100644 --- a/pkg/analyzer/test/generated/top_level_parser_test.dart +++ b/pkg/analyzer/test/generated/top_level_parser_test.dart @@ -1121,32 +1121,32 @@ Function(int, String) v; expect(directive, TypeMatcher()); var libraryDirective = directive as LibraryDirective; expect(libraryDirective.libraryKeyword, isNotNull); - expect(libraryDirective.name, isNotNull); + expect(libraryDirective.name2, isNotNull); expect(libraryDirective.semicolon, isNotNull); } void test_parseDirective_library_1_component() { createParser("library a;"); var lib = parseFullDirective() as LibraryDirective; - expect(lib.name.components, hasLength(1)); - expect(lib.name.components[0].name, 'a'); + expect(lib.name2!.components, hasLength(1)); + expect(lib.name2!.components[0].name, 'a'); } void test_parseDirective_library_2_components() { createParser("library a.b;"); var lib = parseFullDirective() as LibraryDirective; - expect(lib.name.components, hasLength(2)); - expect(lib.name.components[0].name, 'a'); - expect(lib.name.components[1].name, 'b'); + expect(lib.name2!.components, hasLength(2)); + expect(lib.name2!.components[0].name, 'a'); + expect(lib.name2!.components[1].name, 'b'); } void test_parseDirective_library_3_components() { createParser("library a.b.c;"); var lib = parseFullDirective() as LibraryDirective; - expect(lib.name.components, hasLength(3)); - expect(lib.name.components[0].name, 'a'); - expect(lib.name.components[1].name, 'b'); - expect(lib.name.components[2].name, 'c'); + expect(lib.name2!.components, hasLength(3)); + expect(lib.name2!.components[0].name, 'a'); + expect(lib.name2!.components[1].name, 'b'); + expect(lib.name2!.components[2].name, 'c'); } void test_parseDirective_library_annotation() { @@ -1157,7 +1157,7 @@ Function(int, String) v; expect(directive, TypeMatcher()); var libraryDirective = directive as LibraryDirective; expect(libraryDirective.libraryKeyword, isNotNull); - expect(libraryDirective.name, isNotNull); + expect(libraryDirective.name2, isNotNull); expect(libraryDirective.semicolon, isNotNull); expect(libraryDirective.metadata, hasLength(1)); expect(libraryDirective.metadata[0].name.name, 'A'); @@ -1172,12 +1172,18 @@ Function(int, String) v; expect(directive, TypeMatcher()); var libraryDirective = directive as LibraryDirective; expect(libraryDirective.libraryKeyword, isNotNull); - expect(libraryDirective.name, isNotNull); + expect(libraryDirective.name2, isNotNull); expect(libraryDirective.semicolon, isNotNull); expect(libraryDirective.metadata, hasLength(1)); expect(libraryDirective.metadata[0].name.name, 'A'); } + void test_parseDirective_library_unnamed() { + createParser("library;"); + var lib = parseFullDirective() as LibraryDirective; + expect(lib.name2, isNull); + } + void test_parseDirective_library_withDocumentationComment() { createParser('/// Doc\nlibrary l;'); var directive = parseFullDirective() as LibraryDirective; @@ -1904,7 +1910,7 @@ enum E { expect(directive, isNotNull); assertNoErrors(); expect(directive.libraryKeyword, isNotNull); - expect(directive.name, isNotNull); + expect(directive.name2, isNotNull); expect(directive.semicolon, isNotNull); } diff --git a/pkg/analyzer/test/generated/utilities_test.dart b/pkg/analyzer/test/generated/utilities_test.dart index 9ce328ab5d1..e7be7a0d1db 100644 --- a/pkg/analyzer/test/generated/utilities_test.dart +++ b/pkg/analyzer/test/generated/utilities_test.dart @@ -1238,7 +1238,7 @@ library foo; destination: node, source: node, childAccessors: [ - (node) => node.name, + (node) => node.name2!, ], ); } diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart index b5d732c399a..029835a58e2 100644 --- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart +++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart @@ -638,6 +638,13 @@ void f() {} _assertSource("", AstTestFactory.compilationUnit()); } + void test_visitCompilationUnit_libraryWithoutName() { + _assertSource( + "library ;", + AstTestFactory.compilationUnit3([AstTestFactory.libraryDirective2(null)]), + ); + } + void test_visitCompilationUnit_script() { _assertSource( "!#/bin/dartvm", AstTestFactory.compilationUnit5("!#/bin/dartvm")); diff --git a/pkg/analyzer/test/src/fasta/ast_builder_test.dart b/pkg/analyzer/test/src/fasta/ast_builder_test.dart index c3eb403709d..9390d338d8d 100644 --- a/pkg/analyzer/test/src/fasta/ast_builder_test.dart +++ b/pkg/analyzer/test/src/fasta/ast_builder_test.dart @@ -748,6 +748,42 @@ LibraryAugmentationDirective '''); } + void test_library_with_name() { + var parseResult = parseStringWithErrors(r''' +library name.and.dots; +'''); + parseResult.assertNoErrors(); + + var node = parseResult.findNode.library('library'); + assertParsedNodeText(node, r''' +LibraryDirective + libraryKeyword: library + name: LibraryIdentifier + components + SimpleIdentifier + token: name + SimpleIdentifier + token: and + SimpleIdentifier + token: dots + semicolon: ; +'''); + } + + void test_library_without_name() { + var parseResult = parseStringWithErrors(r''' +library; +'''); + parseResult.assertNoErrors(); + + var node = parseResult.findNode.library('library'); + assertParsedNodeText(node, r''' +LibraryDirective + libraryKeyword: library + semicolon: ; +'''); + } + void test_mixin_implementsClause_recordType() { var parseResult = parseStringWithErrors(r''' class C {} diff --git a/pkg/analyzer/test/util/feature_sets.dart b/pkg/analyzer/test/util/feature_sets.dart index 253a5bf7ecf..dd844760b29 100644 --- a/pkg/analyzer/test/util/feature_sets.dart +++ b/pkg/analyzer/test/util/feature_sets.dart @@ -32,6 +32,11 @@ class FeatureSets { flags: [], ); + static final FeatureSet language_2_19 = FeatureSet.fromEnableFlags2( + sdkLanguageVersion: Version.parse('2.19.0'), + flags: [], + ); + static final FeatureSet latest = FeatureSet.latestLanguageVersion(); static final FeatureSet latestWithExperiments = FeatureSet.fromEnableFlags2( @@ -43,6 +48,7 @@ class FeatureSets { EnableString.named_arguments_anywhere, EnableString.records, EnableString.super_parameters, + EnableString.unnamed_libraries, ], ); diff --git a/pkg/analyzer/tool/summary/mini_ast.dart b/pkg/analyzer/tool/summary/mini_ast.dart index d07bb772483..8d9079d9c30 100644 --- a/pkg/analyzer/tool/summary/mini_ast.dart +++ b/pkg/analyzer/tool/summary/mini_ast.dart @@ -328,9 +328,11 @@ class MiniAstBuilder extends StackListener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { debugEvent("LibraryName"); - pop(); // Library name + if (hasName) { + pop(); // Library name + } pop(); // Metadata pop(); // Comment } diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart index 78540c45826..54ac5154b14 100644 --- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart +++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart @@ -399,7 +399,7 @@ class _DartNavigationComputerVisitor extends RecursiveAstVisitor { @override void visitLibraryDirective(LibraryDirective node) { - computer._addRegionForNode(node.name, node.element2); + computer._addRegionForNode(node.name2, node.element2); } @override diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart index 8624c8bb22e..7d6aea0b620 100644 --- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart +++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart @@ -227,6 +227,14 @@ class ExperimentalFlag { experimentEnabledVersion: const Version(2, 14), experimentReleasedVersion: const Version(2, 14)); + static const ExperimentalFlag unnamedLibraries = const ExperimentalFlag( + name: 'unnamed-libraries', + isEnabledByDefault: false, + isExpired: false, + enabledVersion: const Version(2, 19), + experimentEnabledVersion: const Version(2, 19), + experimentReleasedVersion: const Version(2, 19)); + static const ExperimentalFlag valueClass = const ExperimentalFlag( name: 'value-class', isEnabledByDefault: false, @@ -378,6 +386,10 @@ class GlobalFeatures { GlobalFeature get tripleShift => _tripleShift ??= _computeGlobalFeature(ExperimentalFlag.tripleShift); + GlobalFeature? _unnamedLibraries; + GlobalFeature get unnamedLibraries => _unnamedLibraries ??= + _computeGlobalFeature(ExperimentalFlag.unnamedLibraries); + GlobalFeature? _valueClass; GlobalFeature get valueClass => _valueClass ??= _computeGlobalFeature(ExperimentalFlag.valueClass); @@ -515,6 +527,11 @@ class LibraryFeatures { _tripleShift ??= globalFeatures._computeLibraryFeature( ExperimentalFlag.tripleShift, canonicalUri, libraryVersion); + LibraryFeature? _unnamedLibraries; + LibraryFeature get unnamedLibraries => + _unnamedLibraries ??= globalFeatures._computeLibraryFeature( + ExperimentalFlag.unnamedLibraries, canonicalUri, libraryVersion); + LibraryFeature? _valueClass; LibraryFeature get valueClass => _valueClass ??= globalFeatures._computeLibraryFeature( @@ -572,6 +589,8 @@ ExperimentalFlag? parseExperimentalFlag(String flag) { return ExperimentalFlag.testExperiment; case "triple-shift": return ExperimentalFlag.tripleShift; + case "unnamed-libraries": + return ExperimentalFlag.unnamedLibraries; case "value-class": return ExperimentalFlag.valueClass; case "variance": @@ -619,6 +638,8 @@ final Map defaultExperimentalFlags = { ExperimentalFlag.testExperiment: ExperimentalFlag.testExperiment.isEnabledByDefault, ExperimentalFlag.tripleShift: ExperimentalFlag.tripleShift.isEnabledByDefault, + ExperimentalFlag.unnamedLibraries: + ExperimentalFlag.unnamedLibraries.isEnabledByDefault, ExperimentalFlag.valueClass: ExperimentalFlag.valueClass.isEnabledByDefault, ExperimentalFlag.variance: ExperimentalFlag.variance.isEnabledByDefault, }; diff --git a/pkg/front_end/lib/src/fasta/kernel/macro/annotation_parser.dart b/pkg/front_end/lib/src/fasta/kernel/macro/annotation_parser.dart index 707727abac0..1651b0b620a 100644 --- a/pkg/front_end/lib/src/fasta/kernel/macro/annotation_parser.dart +++ b/pkg/front_end/lib/src/fasta/kernel/macro/annotation_parser.dart @@ -1309,7 +1309,7 @@ class _MacroListener implements Listener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { _unexpected(); } diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart index f3f576b8cb8..38813c10589 100644 --- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart +++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart @@ -438,9 +438,11 @@ class DietListener extends StackListenerImpl { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { debugEvent("endLibraryName"); - pop(); // Name. + if (hasName) { + pop(); // Name. + } pop(); // Annotations. } diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart index b87d3aaff42..97ef60cf7c2 100644 --- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart @@ -832,14 +832,20 @@ class OutlineBuilder extends StackListenerImpl { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { debugEvent("endLibraryName"); - popCharOffset(); - Object? name = pop(); + Object? name = null; + if (hasName) { + popCharOffset(); + name = pop(); + } List? metadata = pop() as List?; - if (name is! ParserRecovery) { + if (name != null && name is! ParserRecovery) { libraryBuilder.name = - flattenName(name!, offsetForToken(libraryKeyword), uri); + flattenName(name, offsetForToken(libraryKeyword), uri); + } else { + reportIfNotEnabled( + libraryFeatures.unnamedLibraries, semicolon.charOffset, noLength); } libraryBuilder.metadata = metadata; } diff --git a/pkg/front_end/lib/src/fasta/util/parser_ast_helper.dart b/pkg/front_end/lib/src/fasta/util/parser_ast_helper.dart index 2959856ec2d..ec32dfb00fc 100644 --- a/pkg/front_end/lib/src/fasta/util/parser_ast_helper.dart +++ b/pkg/front_end/lib/src/fasta/util/parser_ast_helper.dart @@ -1275,9 +1275,9 @@ abstract class AbstractParserAstListener implements Listener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { LibraryNameEnd data = new LibraryNameEnd(ParserAstType.END, - libraryKeyword: libraryKeyword, semicolon: semicolon); + libraryKeyword: libraryKeyword, semicolon: semicolon, hasName: hasName); seen(data); } @@ -4960,15 +4960,19 @@ class LibraryNameBegin extends ParserAstNode { class LibraryNameEnd extends ParserAstNode { final Token libraryKeyword; final Token semicolon; + final bool hasName; LibraryNameEnd(ParserAstType type, - {required this.libraryKeyword, required this.semicolon}) + {required this.libraryKeyword, + required this.semicolon, + required this.hasName}) : super("LibraryName", type); @override Map get deprecatedArguments => { "libraryKeyword": libraryKeyword, "semicolon": semicolon, + "hasName": hasName, }; } diff --git a/pkg/front_end/lib/src/fasta/util/textual_outline.dart b/pkg/front_end/lib/src/fasta/util/textual_outline.dart index 50d2d77b4c1..bac2bc092fd 100644 --- a/pkg/front_end/lib/src/fasta/util/textual_outline.dart +++ b/pkg/front_end/lib/src/fasta/util/textual_outline.dart @@ -768,7 +768,7 @@ class TextualOutlineListener extends Listener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { unsortableElementStartToChunk[libraryKeyword] = new _LibraryNameChunk(libraryKeyword, semicolon); } diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.expect b/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.expect index 998ca285248..341f6c5fc30 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.expect @@ -11,7 +11,7 @@ beginCompilationUnit(library) beginLibraryName(library) handleRecoverableError(Message[ExpectedIdentifierButGotKeyword, 'enum' can't be used as an identifier because it's a keyword., Try renaming this to be an identifier that isn't a keyword., {lexeme: enum}], enum, enum) handleIdentifier(enum, libraryName) - endLibraryName(library, ;) + endLibraryName(library, ;, true) endTopLevelDeclaration(main) beginMetadataStar(main) endMetadataStar(0) diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.intertwined.expect index 7fa7ad9fc03..4520f30bf54 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_48371_prime1.dart.intertwined.expect @@ -16,7 +16,7 @@ parseUnit(library) listener: handleRecoverableError(Message[ExpectedIdentifierButGotKeyword, 'enum' can't be used as an identifier because it's a keyword., Try renaming this to be an identifier that isn't a keyword., {lexeme: enum}], enum, enum) listener: handleIdentifier(enum, libraryName) ensureSemicolon(enum) - listener: endLibraryName(library, ;) + listener: endLibraryName(library, ;, true) listener: endTopLevelDeclaration(main) parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext') parseMetadataStar(;) diff --git a/pkg/front_end/test/parser_test_listener.dart b/pkg/front_end/test/parser_test_listener.dart index 425d19993ba..ca6f088d776 100644 --- a/pkg/front_end/test/parser_test_listener.dart +++ b/pkg/front_end/test/parser_test_listener.dart @@ -1387,11 +1387,11 @@ class ParserTestListener implements Listener { } @override - void endLibraryName(Token libraryKeyword, Token semicolon) { + void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { indent--; seen(libraryKeyword); seen(semicolon); - doPrint('endLibraryName(' '$libraryKeyword, ' '$semicolon)'); + doPrint('endLibraryName(' '$libraryKeyword, ' '$semicolon, ' '$hasName)'); } @override diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml index c624c60e6c0..d27ee9e061d 100644 --- a/tools/experimental_features.yaml +++ b/tools/experimental_features.yaml @@ -17,8 +17,8 @@ # dart pkg/analyzer/tool/experiments/generate.dart # # Also, pkg/analyzer/lib/src/dart/analysis/driver.dart will need a bump in -# DATA_VERSION if making changes that changes previous flags' "index", e.g. -# if adding a new flag that doesn't happen to be lexicographically last. +# DATA_VERSION if making changes that change the "index" of any previous flags, +# e.g. if adding a new flag that doesn't happen to be lexicographically last. # # kernel: # pkg/front_end/tool/fasta generate-experimental-flags @@ -134,6 +134,9 @@ features: patterns: help: "Patterns" + unnamed-libraries: + help: "Unnamed libraries" + # Experiment flag only used for testing. test-experiment: help: >-