Unnamed libraries

Fixes https://github.com/dart-lang/language/issues/1073

Spec: https://github.com/dart-lang/language/blob/master/accepted/future-releases/unnamed-libraries/feature-specification.md

This work allows library directives without a name. Every single one would look like this:

```
library;
```

:) it was a little anti-climactic implementing a non-feature like this, but there it is.

The affordance for a library directive without a name is guarded by an experiment flag, `--unnamed-libraries`.

Change-Id: I8612238359e88d6082f7e89d0d0fc624fdb45273
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/257490
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Kevin Moore <kevmoo@google.com>
This commit is contained in:
Sam Rawlins 2022-09-23 05:37:39 +00:00 committed by Commit Bot
parent 948eb70c02
commit 51114fca8a
40 changed files with 254 additions and 96 deletions

View file

@ -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

View file

@ -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");
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -432,12 +432,9 @@ class KytheDartVisitor extends GeneralizingAstVisitor<void> 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,

View file

@ -871,7 +871,7 @@ class CodeShapeDataCollector extends RecursiveAstVisitor<void> {
_visitChildren(node, {
'documentationComment': node.documentationComment,
'metadata': node.metadata,
'name': node.name,
'name': node.name2,
});
super.visitLibraryDirective(node);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -40,6 +40,7 @@ final _knownFeatures = <String, ExperimentalFeature>{
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);

View file

@ -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(

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -793,7 +793,7 @@ class ToSourceVisitor implements AstVisitor<void> {
void visitLibraryDirective(LibraryDirective node) {
_visitNodeList(node.metadata, separator: ' ', suffix: ' ');
sink.write('library ');
_visitNode(node.name);
_visitNode(node.name2);
sink.write(';');
}

View file

@ -915,7 +915,7 @@ class AstComparator implements AstVisitor<bool> {
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<bool> {
@override
bool visitLibraryDirective(covariant LibraryDirectiveImpl node) {
if (identical(node.name, _oldNode)) {
if (identical(node.name2, _oldNode)) {
node.name = _newNode as LibraryIdentifier;
return true;
}

View file

@ -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<SimpleIdentifier>;
var name = ast.libraryIdentifier(libraryName);
var libraryName = hasName ? pop() as List<SimpleIdentifier>? : null;
if (!hasName && !enableUnnamedLibraries) {
_reportFeatureNotEnabled(
feature: ExperimentalFeatures.unnamed_libraries,
startToken: libraryKeyword,
);
}
var name = libraryName == null ? null : ast.libraryIdentifier(libraryName);
var metadata = pop() as List<Annotation>?;
var comment = _findComment(metadata, libraryKeyword);
directives.add(

View file

@ -399,17 +399,20 @@ class AstTestFactory {
astFactory.labeledStatement(labels, statement);
static LibraryDirectiveImpl libraryDirective(
List<Annotation> metadata, LibraryIdentifier libraryName) =>
List<Annotation> 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(<Annotation>[], libraryIdentifier2([libraryName]));
static LibraryDirectiveImpl libraryDirective2(String? libraryName) =>
libraryDirective(
<Annotation>[],
libraryName == null ? null : libraryIdentifier2([libraryName]),
);
static LibraryIdentifierImpl libraryIdentifier(
List<SimpleIdentifier> components) =>

View file

@ -670,7 +670,7 @@ class AstTextPrinter extends ThrowingAstVisitor<void> {
void visitLibraryDirective(LibraryDirective node) {
_directive(node);
_token(node.libraryKeyword);
node.name.accept(this);
node.name2?.accept(this);
_token(node.semicolon);
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -2172,12 +2172,6 @@ class Wrong<T> {
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

View file

@ -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

View file

@ -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<int, int Function(String)>();}
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<int, int Function(String)>();}
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<int, int Function(String)>();}
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<int, int Function(String)>();}
void test_parseLibraryIdentifier_single() {
String name = "a";
LibraryIdentifier identifier = parseLibraryIdentifier(name);
LibraryIdentifier identifier = parseLibraryIdentifier(name)!;
expectNotNullIfNoErrors(identifier);
assertNoErrors();
expect(identifier.name, name);

View file

@ -1121,32 +1121,32 @@ Function(int, String) v;
expect(directive, TypeMatcher<LibraryDirective>());
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<LibraryDirective>());
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<LibraryDirective>());
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);
}

View file

@ -1238,7 +1238,7 @@ library foo;
destination: node,
source: node,
childAccessors: [
(node) => node.name,
(node) => node.name2!,
],
);
}

View file

@ -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"));

View file

@ -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 {}

View file

@ -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,
],
);

View file

@ -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
}

View file

@ -399,7 +399,7 @@ class _DartNavigationComputerVisitor extends RecursiveAstVisitor<void> {
@override
void visitLibraryDirective(LibraryDirective node) {
computer._addRegionForNode(node.name, node.element2);
computer._addRegionForNode(node.name2, node.element2);
}
@override

View file

@ -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<ExperimentalFlag, bool> 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,
};

View file

@ -1309,7 +1309,7 @@ class _MacroListener implements Listener {
}
@override
void endLibraryName(Token libraryKeyword, Token semicolon) {
void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) {
_unexpected();
}

View file

@ -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.
}

View file

@ -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<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
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;
}

View file

@ -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<String, Object?> get deprecatedArguments => {
"libraryKeyword": libraryKeyword,
"semicolon": semicolon,
"hasName": hasName,
};
}

View file

@ -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);
}

View file

@ -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)

View file

@ -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(;)

View file

@ -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

View file

@ -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: >-