Report EXPECTED_NAMED_TYPE_X when not a NamedType is parsed.

Change-Id: Ibebc276777075da12e36642f08291fa9a9f9ac91
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260073
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-09-21 02:24:45 +00:00 committed by Commit Bot
parent 959854769c
commit 1aa398d4be
6 changed files with 188 additions and 61 deletions

View file

@ -2167,6 +2167,14 @@ ParserErrorCode.EXPECTED_INSTEAD:
status: needsEvaluation
ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL:
status: needsEvaluation
ParserErrorCode.EXPECTED_NAMED_TYPE_EXTENDS:
status: noFix
ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS:
status: noFix
ParserErrorCode.EXPECTED_NAMED_TYPE_ON:
status: noFix
ParserErrorCode.EXPECTED_NAMED_TYPE_WITH:
status: noFix
ParserErrorCode.EXPECTED_STRING_LITERAL:
status: needsEvaluation
ParserErrorCode.EXPECTED_TOKEN:

View file

@ -758,6 +758,10 @@ const List<ErrorCode> errorCodeValues = [
ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD,
ParserErrorCode.EXPECTED_INSTEAD,
ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL,
ParserErrorCode.EXPECTED_NAMED_TYPE_EXTENDS,
ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS,
ParserErrorCode.EXPECTED_NAMED_TYPE_ON,
ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
ParserErrorCode.EXPECTED_STRING_LITERAL,
ParserErrorCode.EXPECTED_TOKEN,
ParserErrorCode.EXPECTED_TYPE_NAME,

View file

@ -506,6 +506,36 @@ class ParserErrorCode extends ErrorCode {
"Try inserting a list or map literal, or remove the type arguments.",
);
static const ParserErrorCode EXPECTED_NAMED_TYPE_EXTENDS = ParserErrorCode(
'EXPECTED_NAMED_TYPE',
"Expected a class name.",
correctionMessage: "Try using a class name, possibly with type arguments.",
uniqueName: 'EXPECTED_NAMED_TYPE_EXTENDS',
);
static const ParserErrorCode EXPECTED_NAMED_TYPE_IMPLEMENTS = ParserErrorCode(
'EXPECTED_NAMED_TYPE',
"Expected the name of a class or mixin.",
correctionMessage:
"Try using a class or mixin name, possibly with type arguments.",
uniqueName: 'EXPECTED_NAMED_TYPE_IMPLEMENTS',
);
static const ParserErrorCode EXPECTED_NAMED_TYPE_ON = ParserErrorCode(
'EXPECTED_NAMED_TYPE',
"Expected the name of a class or mixin.",
correctionMessage:
"Try using a class or mixin name, possibly with type arguments.",
uniqueName: 'EXPECTED_NAMED_TYPE_ON',
);
static const ParserErrorCode EXPECTED_NAMED_TYPE_WITH = ParserErrorCode(
'EXPECTED_NAMED_TYPE',
"Expected a mixin name.",
correctionMessage: "Try using a mixin name, possibly with type arguments.",
uniqueName: 'EXPECTED_NAMED_TYPE_WITH',
);
static const ParserErrorCode EXPECTED_STRING_LITERAL = ParserErrorCode(
'EXPECTED_STRING_LITERAL',
"Expected a string literal.",

View file

@ -56,11 +56,13 @@ import 'package:_fe_analyzer_shared/src/scanner/token_constants.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart' show Token, TokenType;
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/fasta/error_converter.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
@ -2381,7 +2383,9 @@ class AstBuilder extends StackListener {
ImplementsClauseImpl? implementsClause;
if (implementsKeyword != null) {
var interfaces = popTypeList();
var interfaces = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS,
);
implementsClause = ImplementsClauseImpl(
implementsKeyword: implementsKeyword,
interfaces: interfaces,
@ -3221,12 +3225,16 @@ class AstBuilder extends StackListener {
),
);
} else {
// TODO(brianwilkerson) Produce a diagnostic indicating that the type
// annotation is either missing or an invalid kind. Also, consider
// (a) extending `ExtendsClause` to accept any type annotation for
// recovery purposes, and (b) extending the parser to parse a generic
// function type at this location.
push(NullValue.ExtendsClause);
// TODO(brianwilkerson) Consider (a) extending `ExtendsClause` to accept
// any type annotation for recovery purposes, and (b) extending the
// parser to parse a generic function type at this location.
if (supertype != null) {
errorReporter.errorReporter?.reportErrorForNode(
ParserErrorCode.EXPECTED_NAMED_TYPE_EXTENDS,
supertype,
);
}
}
}
@ -3281,7 +3289,9 @@ class AstBuilder extends StackListener {
@override
void handleClassWithClause(Token withKeyword) {
assert(optional('with', withKeyword));
var mixinTypes = popTypeList();
var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes));
}
@ -3524,7 +3534,9 @@ class AstBuilder extends StackListener {
@override
void handleEnumWithClause(Token withKeyword) {
assert(optional('with', withKeyword));
var mixinTypes = popTypeList();
var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes));
}
@ -3781,9 +3793,10 @@ class AstBuilder extends StackListener {
debugEvent("Implements");
if (implementsKeyword != null) {
var types = popTypedList2<TypeAnnotation>(interfacesCount);
// TODO(brianwilkerson) Report diagnostics for any type that's filtered out.
var interfaces = types.whereType<NamedType>().toList();
endTypeList(interfacesCount);
final interfaces = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS,
);
push(
ImplementsClauseImpl(
implementsKeyword: implementsKeyword,
@ -4086,9 +4099,10 @@ class AstBuilder extends StackListener {
debugEvent("MixinOn");
if (onKeyword != null) {
var types = popTypedList2<TypeAnnotation>(typeCount);
// TODO(brianwilkerson) Report diagnostics for any type that's filtered out.
var onTypes = types.whereType<NamedType>().toList();
endTypeList(typeCount);
final onTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_ON,
);
push(ast.onClause(onKeyword, onTypes));
} else {
push(NullValue.IdentifierList);
@ -4116,7 +4130,9 @@ class AstBuilder extends StackListener {
@override
void handleNamedMixinApplicationWithClause(Token withKeyword) {
assert(optionalOrNull('with', withKeyword));
var mixinTypes = popTypeList();
var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes));
}
@ -4676,21 +4692,6 @@ class AstBuilder extends StackListener {
return result.reversed.toList();
}
// List<T?>? popTypedList<T>(int count, [List<T>? list]) {
// if (count == 0) return null;
// assert(stack.length >= count);
//
// final tailList = list ?? List<T?>.filled(count, null, growable: true);
// stack.popList(count, tailList, null);
// return tailList;
// }
List<NamedType> popTypeList() {
var types = pop() as List<TypeAnnotation>;
// TODO(brianwilkerson) Report diagnostics for any type that's filtered out.
return types.whereType<NamedType>().toList();
}
void reportErrorIfNullableType(Token? questionMark) {
if (questionMark != null) {
assert(optional('?', questionMark));
@ -4761,6 +4762,21 @@ class AstBuilder extends StackListener {
typeArguments: typeArguments));
}
List<NamedType> _popNamedTypeList({
required ErrorCode errorCode,
}) {
final types = pop() as List<TypeAnnotation>;
final namedTypes = <NamedType>[];
for (final type in types) {
if (type is NamedType) {
namedTypes.add(type);
} else {
errorReporter.errorReporter?.reportErrorForNode(errorCode, type);
}
}
return namedTypes;
}
void _reportFeatureNotEnabled({
required ExperimentalFeature feature,
required Token startToken,

View file

@ -21892,6 +21892,22 @@ ParserErrorCode:
EXPECTED_LIST_OR_MAP_LITERAL:
problemMessage: Expected a list or map literal.
correctionMessage: Try inserting a list or map literal, or remove the type arguments.
EXPECTED_NAMED_TYPE_EXTENDS:
sharedName: EXPECTED_NAMED_TYPE
problemMessage: Expected a class name.
correctionMessage: Try using a class name, possibly with type arguments.
EXPECTED_NAMED_TYPE_IMPLEMENTS:
sharedName: EXPECTED_NAMED_TYPE
problemMessage: Expected the name of a class or mixin.
correctionMessage: Try using a class or mixin name, possibly with type arguments.
EXPECTED_NAMED_TYPE_ON:
sharedName: EXPECTED_NAMED_TYPE
problemMessage: Expected the name of a class or mixin.
correctionMessage: Try using a class or mixin name, possibly with type arguments.
EXPECTED_NAMED_TYPE_WITH:
sharedName: EXPECTED_NAMED_TYPE
problemMessage: Expected a mixin name.
correctionMessage: Try using a mixin name, possibly with type arguments.
EXPECTED_STRING_LITERAL:
problemMessage: Expected a string literal.
EXPECTED_TOKEN:

View file

@ -213,7 +213,9 @@ ConstructorDeclaration
var parseResult = parseStringWithErrors(r'''
class C extends (int, int) {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_EXTENDS, 16, 10),
]);
final node = parseResult.findNode.classDeclaration('class C');
assertParsedNodeText(
@ -230,9 +232,11 @@ ClassDeclaration
void test_class_implementsClause_recordType() {
var parseResult = parseStringWithErrors(r'''
class C implements (int, int) {}
class C implements A, (int, int), B {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS, 22, 10),
]);
final node = parseResult.findNode.classDeclaration('class C');
assertParsedNodeText(
@ -243,8 +247,15 @@ ClassDeclaration
name: C @6
implementsClause: ImplementsClause
implementsKeyword: implements @8
leftBracket: { @30
rightBracket: } @31
interfaces
NamedType
name: SimpleIdentifier
token: A @19
NamedType
name: SimpleIdentifier
token: B @34
leftBracket: { @36
rightBracket: } @37
''',
withOffsets: true);
}
@ -268,23 +279,29 @@ ClassDeclaration
void test_class_withClause_recordType() {
var parseResult = parseStringWithErrors(r'''
class C with (int, int) {}
class C with A, (int, int), B {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_WITH, 16, 10),
]);
final node = parseResult.findNode.classDeclaration('class C');
assertParsedNodeText(
node,
r'''
assertParsedNodeText(node, r'''
ClassDeclaration
classKeyword: class @0
name: C @6
classKeyword: class
name: C
withClause: WithClause
withKeyword: with @8
leftBracket: { @24
rightBracket: } @25
''',
withOffsets: true);
withKeyword: with
mixinTypes
NamedType
name: SimpleIdentifier
token: A
NamedType
name: SimpleIdentifier
token: B
leftBracket: {
rightBracket: }
''');
}
void test_classAlias_macro() {
@ -316,10 +333,12 @@ ClassTypeAlias
void test_classTypeAlias_implementsClause_recordType() {
var parseResult = parseStringWithErrors(r'''
class C = Object with M implements (int, int);
class C = Object with M implements A, (int, int), B;
mixin M {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS, 38, 10),
]);
final node = parseResult.findNode.classTypeAlias('class C');
assertParsedNodeText(
@ -340,16 +359,25 @@ ClassTypeAlias
token: M @22
implementsClause: ImplementsClause
implementsKeyword: implements @24
semicolon: ; @45
interfaces
NamedType
name: SimpleIdentifier
token: A @35
NamedType
name: SimpleIdentifier
token: B @50
semicolon: ; @51
''',
withOffsets: true);
}
void test_classTypeAlias_withClause_recordType() {
var parseResult = parseStringWithErrors(r'''
class C = Object with (int, int);
class C = Object with A, (int, int), B;
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_WITH, 25, 10),
]);
final node = parseResult.findNode.classTypeAlias('class C');
assertParsedNodeText(
@ -364,7 +392,14 @@ ClassTypeAlias
token: Object @10
withClause: WithClause
withKeyword: with @17
semicolon: ; @32
mixinTypes
NamedType
name: SimpleIdentifier
token: A @22
NamedType
name: SimpleIdentifier
token: B @37
semicolon: ; @38
''',
withOffsets: true);
}
@ -716,9 +751,11 @@ LibraryAugmentationDirective
void test_mixin_implementsClause_recordType() {
var parseResult = parseStringWithErrors(r'''
class C {}
mixin M on C implements (int, int) {}
mixin M on C implements A, (int, int), B {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS, 38, 10),
]);
final node = parseResult.findNode.mixinDeclaration('mixin M');
assertParsedNodeText(
@ -735,17 +772,26 @@ MixinDeclaration
token: C @22
implementsClause: ImplementsClause
implementsKeyword: implements @24
leftBracket: { @46
rightBracket: } @47
interfaces
NamedType
name: SimpleIdentifier
token: A @35
NamedType
name: SimpleIdentifier
token: B @50
leftBracket: { @52
rightBracket: } @53
''',
withOffsets: true);
}
void test_mixin_onClause_recordType() {
var parseResult = parseStringWithErrors(r'''
mixin M on (int, int) {}
mixin M on A, (int, int), B {}
''');
parseResult.assertNoErrors();
parseResult.assertErrors([
error(ParserErrorCode.EXPECTED_NAMED_TYPE_ON, 14, 10),
]);
final node = parseResult.findNode.mixinDeclaration('mixin M');
assertParsedNodeText(
@ -756,8 +802,15 @@ MixinDeclaration
name: M @6
onClause: OnClause
onKeyword: on @8
leftBracket: { @22
rightBracket: } @23
superclassConstraints
NamedType
name: SimpleIdentifier
token: A @11
NamedType
name: SimpleIdentifier
token: B @26
leftBracket: { @28
rightBracket: } @29
''',
withOffsets: true);
}