Skip to content

Commit 4163478

Browse files
danrubelcommit-bot@chromium.org
danrubel
authored andcommitted
Update AstBuilder to build AST with nullable types
... as part of adding NNBD as outlined in dart-lang/language#110 This CL updates the parser to recognize two specialized comments at the beginning of a library of the form '//@NNBD' and '//@NNBD*' for use by the analyzer to aid developers when converting their libraries to be non-nullable by default. In addition, the AstBuilder now populates the generated analyzer AST with nullable type information. Change-Id: I80d221dd138973aa32f05bde631245d9ac6ee10f Reviewed-on: https://dart-review.googlesource.com/c/87540 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Dan Rubel <[email protected]>
1 parent a9c1c05 commit 4163478

File tree

5 files changed

+92
-8
lines changed

5 files changed

+92
-8
lines changed

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class AstBuilder extends StackListener {
9696

9797
bool parseFunctionBodies = true;
9898

99+
bool showNullableTypeErrors = true;
100+
99101
AstBuilder(ErrorReporter errorReporter, this.fileUri, this.isFullAst,
100102
[Uri uri])
101103
: this.errorReporter = new FastaErrorReporter(errorReporter),
@@ -263,6 +265,11 @@ class AstBuilder extends StackListener {
263265
scriptTag = ast.scriptTag(token);
264266
}
265267

268+
@override
269+
void handleNNBD(bool isStar) {
270+
showNullableTypeErrors = !isStar;
271+
}
272+
266273
void handleStringJuxtaposition(int literalCount) {
267274
debugEvent("StringJuxtaposition");
268275

@@ -962,11 +969,13 @@ class AstBuilder extends StackListener {
962969
@override
963970
void handleType(Token beginToken, Token questionMark) {
964971
debugEvent("Type");
965-
reportErrorIfNullableType(questionMark);
972+
if (showNullableTypeErrors) {
973+
reportErrorIfNullableType(questionMark);
974+
}
966975

967976
TypeArgumentList arguments = pop();
968977
Identifier name = pop();
969-
push(ast.typeName(name, arguments));
978+
push(ast.typeName(name, arguments, question: questionMark));
970979
}
971980

972981
@override
@@ -1121,13 +1130,16 @@ class AstBuilder extends StackListener {
11211130
void endFunctionType(Token functionToken, Token questionMark) {
11221131
assert(optional('Function', functionToken));
11231132
debugEvent("FunctionType");
1124-
reportErrorIfNullableType(questionMark);
1133+
if (showNullableTypeErrors) {
1134+
reportErrorIfNullableType(questionMark);
1135+
}
11251136

11261137
FormalParameterList parameters = pop();
11271138
TypeAnnotation returnType = pop();
11281139
TypeParameterList typeParameters = pop();
11291140
push(ast.genericFunctionType(
1130-
returnType, functionToken, typeParameters, parameters));
1141+
returnType, functionToken, typeParameters, parameters,
1142+
question: questionMark));
11311143
}
11321144

11331145
void handleFormalParameterWithoutValue(Token token) {

pkg/analyzer/test/generated/parser_fasta_test.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:analyzer/src/generated/parser.dart' as analyzer;
1515
import 'package:analyzer/src/generated/utilities_dart.dart';
1616
import 'package:analyzer/src/string_source.dart';
1717
import 'package:front_end/src/fasta/parser/parser.dart' as fasta;
18+
import 'package:front_end/src/fasta/parser/forwarding_listener.dart' as fasta;
1819
import 'package:front_end/src/fasta/scanner.dart'
1920
show ScannerResult, scanString;
2021
import 'package:front_end/src/fasta/scanner/error_token.dart' show ErrorToken;
@@ -1419,4 +1420,41 @@ mixin A {
14191420
MixinDeclaration declaration = parseFullCompilationUnitMember();
14201421
expectCommentText(declaration.documentationComment, '/// Doc');
14211422
}
1423+
1424+
void test_parseNNDB() {
1425+
void testNNBD(String comments, {bool nnbd, bool star}) {
1426+
final listener = new NNBDTestListener();
1427+
String code = '''
1428+
$comments
1429+
main() {}''';
1430+
ScannerResult result = scanString(code, includeComments: true);
1431+
new fasta.Parser(listener).parseUnit(result.tokens);
1432+
1433+
expect(listener.isNNBD, nnbd ?? isNull);
1434+
expect(listener.isStar, star ?? isNull);
1435+
}
1436+
1437+
testNNBD('', nnbd: false);
1438+
testNNBD('#!/bin/dart', nnbd: false);
1439+
testNNBD('//@NNBDx', nnbd: false);
1440+
testNNBD('//@NNBD', nnbd: true, star: false);
1441+
testNNBD('// @NNBD', nnbd: true, star: false);
1442+
testNNBD('//@NNBD*', nnbd: true, star: true);
1443+
testNNBD('// @NNBD*', nnbd: true, star: true);
1444+
testNNBD('''#!/bin/dart
1445+
// @NNBD*
1446+
/* more comments */
1447+
/// and dartdoc''', nnbd: true, star: true);
1448+
}
1449+
}
1450+
1451+
class NNBDTestListener extends fasta.ForwardingListener {
1452+
bool isNNBD = false;
1453+
bool isStar;
1454+
1455+
@override
1456+
void handleNNBD(bool isStar) {
1457+
this.isNNBD = true;
1458+
this.isStar = isStar;
1459+
}
14221460
}

pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,11 @@ class ForwardingListener implements Listener {
12041204
listener?.handleForInitializerLocalVariableDeclaration(token);
12051205
}
12061206

1207+
@override
1208+
void handleNNBD(bool isStar) {
1209+
listener?.handleNNBD(isStar);
1210+
}
1211+
12071212
@override
12081213
void handleNoFieldInitializer(Token token) {
12091214
listener?.handleNoFieldInitializer(token);

pkg/front_end/lib/src/fasta/parser/listener.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,4 +1357,13 @@ class Listener implements UnescapeErrorListener {
13571357
/// This event is generated by the parser when the parser's
13581358
/// `parseOneCommentReference` method is called.
13591359
void handleNoCommentReference() {}
1360+
1361+
/// A comment of the form `//@NNBD` or `//@NNBD*` has been encountered
1362+
/// at the beginning of the file indicating that this particular library
1363+
/// is in the process of being converted to non-nullable by default.
1364+
///
1365+
/// If [isStar] is `true`, then the library should be considered
1366+
/// old style nullable but the `?` indicating that a type is nullable
1367+
/// should be ignored and no errors generated if they are present.
1368+
void handleNNBD(bool isStar) {}
13601369
}

pkg/front_end/lib/src/fasta/parser/parser.dart

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import '../../scanner/token.dart'
1515
ASSIGNMENT_PRECEDENCE,
1616
BeginToken,
1717
CASCADE_PRECEDENCE,
18+
CommentToken,
1819
EQUALITY_PRECEDENCE,
1920
Keyword,
2021
POSTFIX_PRECEDENCE,
@@ -343,6 +344,11 @@ class Parser {
343344
int count = 0;
344345
DirectiveContext directiveState = new DirectiveContext();
345346
token = syntheticPreviousToken(token);
347+
if (identical(token.next.type, TokenType.SCRIPT_TAG)) {
348+
directiveState?.checkScriptTag(this, token.next);
349+
token = parseScript(token);
350+
}
351+
parseNNBD(token);
346352
while (!token.next.isEof) {
347353
final Token start = token.next;
348354
token = parseTopLevelDeclarationImpl(token, directiveState);
@@ -369,6 +375,24 @@ class Parser {
369375
return token;
370376
}
371377

378+
/// Look for a comment of the form `//@NNBD` or `//@NNBD*`
379+
/// indicating that this file is in process of being converted
380+
/// to be non-nullable by default.
381+
void parseNNBD(Token token) {
382+
Token comment = token.next.precedingComments;
383+
if (comment is CommentToken) {
384+
String text = comment.lexeme;
385+
if (text.startsWith('//')) {
386+
text = text.substring(2).trim();
387+
if (text == '@NNBD') {
388+
listener.handleNNBD(false);
389+
} else if (text == '@NNBD*') {
390+
listener.handleNNBD(true);
391+
}
392+
}
393+
}
394+
}
395+
372396
/// This method exists for analyzer compatibility only
373397
/// and will be removed once analyzer/fasta integration is complete.
374398
///
@@ -459,10 +483,6 @@ class Parser {
459483
/// ```
460484
Token parseTopLevelDeclarationImpl(
461485
Token token, DirectiveContext directiveState) {
462-
if (identical(token.next.type, TokenType.SCRIPT_TAG)) {
463-
directiveState?.checkScriptTag(this, token.next);
464-
return parseScript(token);
465-
}
466486
token = parseMetadataStar(token);
467487
Token next = token.next;
468488
if (next.isTopLevelKeyword) {

0 commit comments

Comments
 (0)