Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f828f00

Browse files
jensjohacommit-bot@chromium.org
authored andcommitted
[parser] Error recovery of type parameters on void
Fixes #39958. Change-Id: I851bef762776f4b68f0fcb645d634019b8b32e81 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/137925 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 067c1e0 commit f828f00

File tree

46 files changed

+522
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+522
-3
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8681,6 +8681,16 @@ const MessageCode messageVoidExpression = const MessageCode("VoidExpression",
86818681
analyzerCodes: <String>["USE_OF_VOID_RESULT"],
86828682
message: r"""This expression has type 'void' and can't be used.""");
86838683

8684+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
8685+
const Code<Null> codeVoidWithTypeParameters = messageVoidWithTypeParameters;
8686+
8687+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
8688+
const MessageCode messageVoidWithTypeParameters = const MessageCode(
8689+
"VoidWithTypeParameters",
8690+
index: 100,
8691+
message: r"""Type 'void' can't have type parameters.""",
8692+
tip: r"""Try removing the type parameters.""");
8693+
86848694
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
86858695
const Template<
86868696
Message Function(

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,11 @@ class ForwardingListener implements Listener {
15691569
listener?.handleVoidKeyword(token);
15701570
}
15711571

1572+
@override
1573+
void handleVoidKeywordWithTypeArguments(Token token) {
1574+
listener?.handleVoidKeywordWithTypeArguments(token);
1575+
}
1576+
15721577
@override
15731578
void logEvent(String name) {
15741579
listener?.logEvent(name);

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,12 @@ class Listener implements UnescapeErrorListener {
15511551
logEvent("VoidKeyword");
15521552
}
15531553

1554+
/// The parser saw a void with type arguments (e.g. void<int>).
1555+
/// This is not valid - an error has already been emitted.
1556+
void handleVoidKeywordWithTypeArguments(Token token) {
1557+
logEvent("handleVoidKeywordWithTypeArguments");
1558+
}
1559+
15541560
void beginYieldStatement(Token token) {}
15551561

15561562
void endYieldStatement(Token yieldToken, Token starToken, Token endToken) {

pkg/_fe_analyzer_shared/lib/src/parser/type_info_impl.dart

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,39 @@ class VoidType implements TypeInfo {
355355

356356
@override
357357
Token parseType(Token token, Parser parser) {
358-
token = token.next;
359-
parser.listener.handleVoidKeyword(token);
358+
Token voidKeyword = token = token.next;
359+
bool hasTypeArguments = false;
360+
361+
// Recovery: Skip past, but issue problem, if followed by type arguments.
362+
if (optional('<', token.next)) {
363+
TypeParamOrArgInfo typeParam = computeTypeParamOrArg(token);
364+
if (typeParam != noTypeParamOrArg) {
365+
hasTypeArguments = true;
366+
parser.reportRecoverableError(
367+
token.next, codes.messageVoidWithTypeParameters);
368+
token = typeParam.parseArguments(token, parser);
369+
}
370+
}
371+
if (hasTypeArguments) {
372+
parser.listener.handleVoidKeywordWithTypeArguments(voidKeyword);
373+
} else {
374+
// Normal case.
375+
parser.listener.handleVoidKeyword(voidKeyword);
376+
}
360377
return token;
361378
}
362379

363380
@override
364381
Token skipType(Token token) {
365-
return token.next;
382+
token = token.next;
383+
// Recovery: Skip past if followed by type arguments.
384+
if (optional('<', token.next)) {
385+
TypeParamOrArgInfo typeParam = computeTypeParamOrArg(token);
386+
if (typeParam != noTypeParamOrArg) {
387+
token = typeParam.skip(token);
388+
}
389+
}
390+
return token;
366391
}
367392
}
368393

pkg/analyzer/lib/error/error.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ const List<ErrorCode> errorCodeValues = [
656656
ParserErrorCode.VAR_ENUM,
657657
ParserErrorCode.VAR_RETURN_TYPE,
658658
ParserErrorCode.VAR_TYPEDEF,
659+
ParserErrorCode.VOID_WITH_TYPE_PARAMETERS,
659660
ParserErrorCode.WITH_BEFORE_EXTENDS,
660661
ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER,
661662
ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP,

pkg/analyzer/lib/src/dart/error/syntactic_errors.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,9 @@ class ParserErrorCode extends ErrorCode {
881881
correction: "Try removing the keyword 'var', or "
882882
"replacing it with the name of the return type.");
883883

884+
static const ParserErrorCode VOID_WITH_TYPE_PARAMETERS =
885+
_VOID_WITH_TYPE_PARAMETERS;
886+
884887
/**
885888
* Initialize a newly created error code to have the given [name]. The message
886889
* associated with the error will be created from the given [message]

pkg/analyzer/lib/src/dart/error/syntactic_errors.g.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ final fastaAnalyzerErrorCodes = <ErrorCode>[
107107
_MULTIPLE_VARIANCE_MODIFIERS,
108108
_INVALID_USE_OF_COVARIANT_IN_EXTENSION,
109109
_TYPE_PARAMETER_ON_CONSTRUCTOR,
110+
_VOID_WITH_TYPE_PARAMETERS,
110111
];
111112

112113
const ParserErrorCode _ABSTRACT_CLASS_MEMBER = ParserErrorCode(
@@ -574,6 +575,10 @@ const ParserErrorCode _VAR_RETURN_TYPE = ParserErrorCode(
574575
correction:
575576
"Try removing the keyword 'var', or replacing it with the name of the return type.");
576577

578+
const ParserErrorCode _VOID_WITH_TYPE_PARAMETERS = ParserErrorCode(
579+
'VOID_WITH_TYPE_PARAMETERS', r"Type 'void' can't have type parameters.",
580+
correction: "Try removing the type parameters.");
581+
577582
const ParserErrorCode _WITH_BEFORE_EXTENDS = ParserErrorCode(
578583
'WITH_BEFORE_EXTENDS',
579584
r"The extends clause must be before the with clause.",

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3443,6 +3443,19 @@ class AstBuilder extends StackListener {
34433443
handleType(voidKeyword, null);
34443444
}
34453445

3446+
@override
3447+
void handleVoidKeywordWithTypeArguments(Token voidKeyword) {
3448+
assert(optional('void', voidKeyword));
3449+
debugEvent("VoidKeywordWithTypeArguments");
3450+
TypeArgumentList arguments = pop();
3451+
3452+
// TODO(paulberry): is this sufficient, or do we need to hook the "void"
3453+
// keyword up to an element?
3454+
handleIdentifier(voidKeyword, IdentifierContext.typeReference);
3455+
push(arguments);
3456+
handleType(voidKeyword, null);
3457+
}
3458+
34463459
@override
34473460
dynamic internalProblem(Message message, int charOffset, Uri uri) {
34483461
throw UnsupportedError(message.message);

pkg/analyzer/test/src/diagnostics/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ import 'uri_does_not_exist_test.dart' as uri_does_not_exist;
487487
import 'uri_with_interpolation_test.dart' as uri_with_interpolation;
488488
import 'use_of_void_result_test.dart' as use_of_void_result;
489489
import 'variable_type_mismatch_test.dart' as variable_type_mismatch;
490+
import 'void_test.dart' as void_test;
490491
import 'wrong_number_of_parameters_for_operator_test.dart'
491492
as wrong_number_of_parameters_for_operator;
492493
import 'wrong_number_of_parameters_for_setter_test.dart'
@@ -834,6 +835,7 @@ main() {
834835
uri_with_interpolation.main();
835836
use_of_void_result.main();
836837
variable_type_mismatch.main();
838+
void_test.main();
837839
wrong_number_of_parameters_for_operator.main();
838840
wrong_number_of_parameters_for_setter.main();
839841
wrong_number_of_type_arguments.main();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/driver_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(VoidTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class VoidTest extends DriverResolutionTest {
18+
test_void_with_type_parameters() async {
19+
await assertErrorsInCode('''
20+
void<int> f() {}
21+
''', [
22+
error(ParserErrorCode.VOID_WITH_TYPE_PARAMETERS, 4, 1),
23+
]);
24+
}
25+
26+
test_void_with_no_type_parameters() async {
27+
await assertErrorsInCode('''
28+
void f() {}
29+
''', []);
30+
}
31+
}

0 commit comments

Comments
 (0)