Add LibraryAugmentationDirective and parsing for it.

Change-Id: I64e19bcd8f1f57c558237562deabc024fad84163
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244301
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-05-11 16:26:34 +00:00 committed by Commit Bot
parent e59c04ff55
commit f3ac80c753
17 changed files with 346 additions and 3 deletions

View file

@ -281,6 +281,17 @@ class ForwardingListener implements Listener {
listener?.beginLabeledStatement(token, labelCount);
}
@override
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
listener?.beginLibraryAugmentation(libraryKeyword, augmentKeyword);
}
@override
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
listener?.endLibraryAugmentation(libraryKeyword, augmentKeyword, semicolon);
}
@override
void beginLibraryName(Token token) {
listener?.beginLibraryName(token);

View file

@ -953,6 +953,16 @@ class Listener implements UnescapeErrorListener {
logEvent("LabeledStatement");
}
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {}
/// Handle the end of a library augmentation directive. Substructures:
/// - metadata
/// - uri
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
logEvent("LibraryAugmentation");
}
void beginLibraryName(Token token) {}
/// Handle the end of a library directive. Substructures:

View file

@ -617,7 +617,13 @@ class Parser {
} else if (identical(value, 'library')) {
context.parseTopLevelKeywordModifiers(start, keyword);
directiveState?.checkLibrary(this, keyword);
return parseLibraryName(keyword);
final Token tokenAfterKeyword = keyword.next!;
if (tokenAfterKeyword.isIdentifier &&
tokenAfterKeyword.lexeme == 'augment') {
return parseLibraryAugmentation(keyword, tokenAfterKeyword);
} else {
return parseLibraryName(keyword);
}
}
}
}
@ -625,6 +631,23 @@ class Parser {
throw "Internal error: Unhandled top level keyword '$value'.";
}
/// ```
/// libraryAugmentationDirective:
/// 'library' 'augment' uri ';'
/// ;
/// ```
Token parseLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
assert(optional('library', libraryKeyword));
assert(optional('augment', augmentKeyword));
listener.beginUncategorizedTopLevelDeclaration(libraryKeyword);
listener.beginLibraryAugmentation(libraryKeyword, augmentKeyword);
Token start = augmentKeyword;
Token token = ensureLiteralString(start);
Token semicolon = ensureSemicolon(token);
listener.endLibraryAugmentation(libraryKeyword, augmentKeyword, semicolon);
return semicolon;
}
/// ```
/// libraryDirective:
/// 'library' qualified ';'

View file

@ -39,6 +39,7 @@ import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
import 'package:meta/meta.dart';
/// Two or more string literals that are implicitly concatenated because of
/// being adjacent (separated only by whitespace).
@ -504,6 +505,8 @@ abstract class AstVisitor<R> {
R? visitLabeledStatement(LabeledStatement node);
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node);
R? visitLibraryDirective(LibraryDirective node);
R? visitLibraryIdentifier(LibraryIdentifier node);
@ -3111,6 +3114,24 @@ abstract class LabeledStatement implements Statement {
Statement get statement;
}
/// A library augmentation directive.
///
/// libraryAugmentationDirective ::=
/// [metadata] 'library' 'augment' [StringLiteral] ';'
///
/// Clients may not extend, implement or mix-in this class.
@experimental
abstract class LibraryAugmentationDirective implements UriBasedDirective {
/// Return the token representing the 'augment' keyword.
Token get augmentKeyword;
/// Return the token representing the 'library' keyword.
Token get libraryKeyword;
/// Return the semicolon terminating the directive.
Token get semicolon;
}
/// A library directive.
///
/// libraryDirective ::=
@ -4473,6 +4494,7 @@ abstract class TypeParameterList implements AstNode {
/// A directive that references a URI.
///
/// uriBasedDirective ::=
/// [LibraryAugmentationDirective]
/// [ExportDirective]
/// | [ImportDirective]
/// | [PartDirective]

View file

@ -425,6 +425,11 @@ class GeneralizingAstVisitor<R> implements AstVisitor<R> {
@override
R? visitLabeledStatement(LabeledStatement node) => visitStatement(node);
@override
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
return visitUriBasedDirective(node);
}
@override
R? visitLibraryDirective(LibraryDirective node) => visitDirective(node);
@ -1112,6 +1117,12 @@ class RecursiveAstVisitor<R> implements AstVisitor<R> {
return null;
}
@override
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
node.visitChildren(this);
return null;
}
@override
R? visitLibraryDirective(LibraryDirective node) {
node.visitChildren(this);
@ -1682,6 +1693,11 @@ class SimpleAstVisitor<R> implements AstVisitor<R> {
@override
R? visitLabeledStatement(LabeledStatement node) => null;
@override
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
return null;
}
@override
R? visitLibraryDirective(LibraryDirective node) => null;
@ -2098,6 +2114,11 @@ class ThrowingAstVisitor<R> implements AstVisitor<R> {
@override
R? visitLabeledStatement(LabeledStatement node) => _throw(node);
@override
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
_throw(node);
}
@override
R? visitLibraryDirective(LibraryDirective node) => _throw(node);
@ -2265,7 +2286,7 @@ class ThrowingAstVisitor<R> implements AstVisitor<R> {
@override
R? visitYieldStatement(YieldStatement node) => _throw(node);
R _throw(AstNode node) {
Never _throw(AstNode node) {
throw Exception('Missing implementation of visit${node.runtimeType}');
}
}
@ -2901,6 +2922,14 @@ class TimedAstVisitor<T> implements AstVisitor<T> {
return result;
}
@override
T? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
stopwatch.start();
T? result = _baseVisitor.visitLibraryAugmentationDirective(node);
stopwatch.stop();
return result;
}
@override
T? visitLibraryDirective(LibraryDirective node) {
stopwatch.start();
@ -3595,6 +3624,11 @@ class UnifyingAstVisitor<R> implements AstVisitor<R> {
@override
R? visitLabeledStatement(LabeledStatement node) => visitNode(node);
@override
R? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
return visitNode(node);
}
@override
R? visitLibraryDirective(LibraryDirective node) => visitNode(node);

View file

@ -20,6 +20,7 @@ import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:meta/meta.dart';
/// Two or more string literals that are implicitly concatenated because of
/// being adjacent (separated only by whitespace).
@ -7136,6 +7137,60 @@ class LabelImpl extends AstNodeImpl implements Label {
}
}
/// A library directive.
///
/// libraryAugmentationDirective ::=
/// [metadata] 'library' 'augment' [StringLiteral] ';'
@experimental
class LibraryAugmentationDirectiveImpl extends UriBasedDirectiveImpl
implements LibraryAugmentationDirective {
@override
Token libraryKeyword;
@override
Token augmentKeyword;
@override
Token semicolon;
LibraryAugmentationDirectiveImpl({
required CommentImpl? comment,
required List<Annotation>? metadata,
required this.libraryKeyword,
required this.augmentKeyword,
required StringLiteralImpl uri,
required this.semicolon,
}) : super(comment, metadata, uri);
@override
Token get endToken => semicolon;
@override
Token get firstTokenAfterCommentAndMetadata => libraryKeyword;
@override
Token get keyword => libraryKeyword;
@override
LibraryElement? get uriElement {
// TODO(scheglov) Implement it.
throw UnimplementedError();
// return element?.importedLibrary;
}
@override
ChildEntities get _childEntities => super._childEntities
..addToken('libraryKeyword', libraryKeyword)
..addToken('augmentKeyword', augmentKeyword)
..addNode('uri', uri)
..addToken('semicolon', semicolon);
@override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitLibraryAugmentationDirective(this);
}
}
/// A library directive.
///
/// libraryDirective ::=

View file

@ -712,6 +712,15 @@ class ToSourceVisitor implements AstVisitor<void> {
_visitNode(node.statement);
}
@override
void visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
_visitNodeList(node.metadata, separator: ' ', suffix: ' ');
sink.write('library ');
sink.write('augment ');
_visitNode(node.uri);
sink.write(';');
}
@override
void visitLibraryDirective(LibraryDirective node) {
_visitNodeList(node.metadata, separator: ' ', suffix: ' ');

View file

@ -821,6 +821,18 @@ class AstComparator implements AstVisitor<bool> {
isEqualNodes(node.statement, other.statement);
}
@override
bool? visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
final other = _other as LibraryAugmentationDirective;
return isEqualNodes(
node.documentationComment, other.documentationComment) &&
_isEqualNodeLists(node.metadata, other.metadata) &&
isEqualTokens(node.libraryKeyword, other.libraryKeyword) &&
isEqualTokens(node.augmentKeyword, other.augmentKeyword) &&
isEqualNodes(node.uri, other.uri) &&
isEqualTokens(node.semicolon, other.semicolon);
}
@override
bool visitLibraryDirective(LibraryDirective node) {
LibraryDirective other = _other as LibraryDirective;
@ -2502,6 +2514,13 @@ class NodeReplacer implements AstVisitor<bool> {
return visitNode(node);
}
@override
bool? visitLibraryAugmentationDirective(
covariant LibraryAugmentationDirectiveImpl node,
) {
return visitUriBasedDirective(node);
}
@override
bool visitLibraryDirective(covariant LibraryDirectiveImpl node) {
if (identical(node.name, _oldNode)) {

View file

@ -330,6 +330,9 @@ class AstBuilder extends StackListener {
@override
void beginIsOperatorType(Token asOperator) {}
@override
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {}
@override
void beginLiteralString(Token literalString) {
assert(identical(literalString.kind, STRING_TOKEN));
@ -1924,6 +1927,24 @@ class AstBuilder extends StackListener {
push(ast.labeledStatement(labels, statement));
}
@override
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
final uri = pop() as StringLiteralImpl;
final metadata = pop() as List<Annotation>?;
final comment = _findComment(metadata, libraryKeyword);
directives.add(
LibraryAugmentationDirectiveImpl(
comment: comment,
metadata: metadata,
libraryKeyword: libraryKeyword,
augmentKeyword: augmentKeyword,
uri: uri,
semicolon: semicolon,
),
);
}
@override
void endLibraryName(Token libraryKeyword, Token semicolon) {
assert(optional('library', libraryKeyword));
@ -4200,7 +4221,8 @@ class AstBuilder extends StackListener {
}
}
Comment? _findComment(List<Annotation>? metadata, Token tokenAfterMetadata) {
CommentImpl? _findComment(
List<Annotation>? metadata, Token tokenAfterMetadata) {
// Find the dartdoc tokens
var dartdoc = parser.findDartDoc(tokenAfterMetadata);
if (dartdoc == null) {

View file

@ -315,6 +315,10 @@ class FindNode {
return _node(search, (n) => n is LibraryDirective);
}
LibraryAugmentationDirective libraryAugmentation(String search) {
return _node(search, (n) => n is LibraryAugmentationDirective);
}
LibraryIdentifier libraryIdentifier(String search) {
return _node(search, (n) => n is LibraryIdentifier);
}

View file

@ -2353,6 +2353,16 @@ import 'foo.dart'
[AstTestFactory.label2("a")], AstTestFactory.returnStatement()));
}
void test_visitLibraryAugmentationDirective() {
var findNode = _parseStringToFindNode(r'''
library augment 'a.dart';
''');
_assertSource(
"library augment 'a.dart';",
findNode.libraryAugmentation('library'),
);
}
void test_visitLibraryDirective() {
_assertSource("library l;", AstTestFactory.libraryDirective2("l"));
}

View file

@ -385,6 +385,23 @@ MethodDeclaration
''');
}
void test_library_augment() {
var parseResult = parseStringWithErrors(r'''
library augment 'a.dart';
''');
parseResult.assertNoErrors();
var node = parseResult.findNode.libraryAugmentation('library');
assertParsedNodeText(node, r'''
LibraryAugmentationDirective
libraryKeyword: library
augmentKeyword: augment
uri: SimpleStringLiteral
literal: 'a.dart'
semicolon: ;
''');
}
void test_superFormalParameter() {
var parseResult = parseStringWithErrors(r'''
class A {

View file

@ -719,6 +719,19 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
});
}
@override
void visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
_writeln('LibraryAugmentationDirective');
_withIndent(() {
_writeNamedChildEntities(node);
// TODO(scheglov) Implement.
// _writeElement('element', node.element);
// _writeRaw('uriContent', node.uriContent);
// _writeElement('uriElement', node.uriElement);
// _writeSource('uriSource', node.uriSource);
});
}
@override
void visitLibraryDirective(LibraryDirective node) {
_writeln('LibraryDirective');

View file

@ -691,6 +691,17 @@ class _MacroListener implements Listener {
_unsupported();
}
@override
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
_unexpected();
}
@override
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
_unexpected();
}
@override
void beginLibraryName(Token token) {
_unexpected();

View file

@ -1248,6 +1248,25 @@ abstract class AbstractParserAstListener implements Listener {
seen(data);
}
@override
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
LibraryAugmentationBegin data = new LibraryAugmentationBegin(
ParserAstType.BEGIN,
libraryKeyword: libraryKeyword,
augmentKeyword: augmentKeyword);
seen(data);
}
@override
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
LibraryAugmentationEnd data = new LibraryAugmentationEnd(ParserAstType.END,
libraryKeyword: libraryKeyword,
augmentKeyword: augmentKeyword,
semicolon: semicolon);
seen(data);
}
@override
void beginLibraryName(Token token) {
LibraryNameBegin data =
@ -4812,6 +4831,40 @@ class LabeledStatementEnd extends ParserAstNode {
};
}
class LibraryAugmentationBegin extends ParserAstNode {
final Token libraryKeyword;
final Token augmentKeyword;
LibraryAugmentationBegin(ParserAstType type,
{required this.libraryKeyword, required this.augmentKeyword})
: super("LibraryAugmentation", type);
@override
Map<String, Object?> get deprecatedArguments => {
"libraryKeyword": libraryKeyword,
"augmentKeyword": augmentKeyword,
};
}
class LibraryAugmentationEnd extends ParserAstNode {
final Token libraryKeyword;
final Token augmentKeyword;
final Token semicolon;
LibraryAugmentationEnd(ParserAstType type,
{required this.libraryKeyword,
required this.augmentKeyword,
required this.semicolon})
: super("LibraryAugmentation", type);
@override
Map<String, Object?> get deprecatedArguments => {
"libraryKeyword": libraryKeyword,
"augmentKeyword": augmentKeyword,
"semicolon": semicolon,
};
}
class LibraryNameBegin extends ParserAstNode {
final Token token;

View file

@ -1358,6 +1358,27 @@ class ParserTestListener implements Listener {
doPrint('endLabeledStatement(' '$labelCount)');
}
@override
void beginLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
seen(libraryKeyword);
seen(augmentKeyword);
doPrint('beginLibraryAugmentation(' '$libraryKeyword, ' '$augmentKeyword)');
indent++;
}
@override
void endLibraryAugmentation(
Token libraryKeyword, Token augmentKeyword, Token semicolon) {
indent--;
seen(libraryKeyword);
seen(augmentKeyword);
seen(semicolon);
doPrint('endLibraryAugmentation('
'$libraryKeyword, '
'$augmentKeyword, '
'$semicolon)');
}
@override
void beginLibraryName(Token token) {
seen(token);

View file

@ -176,6 +176,15 @@ class TestParser extends Parser {
return result;
}
@override
Token parseLibraryAugmentation(Token libraryKeyword, Token augmentKeyword) {
doPrint('parseLibraryAugmentation(' '$libraryKeyword, ' '$augmentKeyword)');
indent++;
var result = super.parseLibraryAugmentation(libraryKeyword, augmentKeyword);
indent--;
return result;
}
@override
Token parseLibraryName(Token libraryKeyword) {
doPrint('parseLibraryName(' '$libraryKeyword)');