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 status: needsEvaluation
ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL: ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL:
status: needsEvaluation 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: ParserErrorCode.EXPECTED_STRING_LITERAL:
status: needsEvaluation status: needsEvaluation
ParserErrorCode.EXPECTED_TOKEN: ParserErrorCode.EXPECTED_TOKEN:

View file

@ -758,6 +758,10 @@ const List<ErrorCode> errorCodeValues = [
ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD, ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD,
ParserErrorCode.EXPECTED_INSTEAD, ParserErrorCode.EXPECTED_INSTEAD,
ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL, 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_STRING_LITERAL,
ParserErrorCode.EXPECTED_TOKEN, ParserErrorCode.EXPECTED_TOKEN,
ParserErrorCode.EXPECTED_TYPE_NAME, 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.", "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( static const ParserErrorCode EXPECTED_STRING_LITERAL = ParserErrorCode(
'EXPECTED_STRING_LITERAL', 'EXPECTED_STRING_LITERAL',
"Expected a 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/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; 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/error/listener.dart';
import 'package:analyzer/source/line_info.dart'; import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart'; import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.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/fasta/error_converter.dart';
import 'package:analyzer/src/generated/utilities_dart.dart'; import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary2/ast_binary_tokens.dart'; import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
@ -2381,7 +2383,9 @@ class AstBuilder extends StackListener {
ImplementsClauseImpl? implementsClause; ImplementsClauseImpl? implementsClause;
if (implementsKeyword != null) { if (implementsKeyword != null) {
var interfaces = popTypeList(); var interfaces = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS,
);
implementsClause = ImplementsClauseImpl( implementsClause = ImplementsClauseImpl(
implementsKeyword: implementsKeyword, implementsKeyword: implementsKeyword,
interfaces: interfaces, interfaces: interfaces,
@ -3221,12 +3225,16 @@ class AstBuilder extends StackListener {
), ),
); );
} else { } 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); 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 @override
void handleClassWithClause(Token withKeyword) { void handleClassWithClause(Token withKeyword) {
assert(optional('with', withKeyword)); assert(optional('with', withKeyword));
var mixinTypes = popTypeList(); var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes)); push(ast.withClause(withKeyword, mixinTypes));
} }
@ -3524,7 +3534,9 @@ class AstBuilder extends StackListener {
@override @override
void handleEnumWithClause(Token withKeyword) { void handleEnumWithClause(Token withKeyword) {
assert(optional('with', withKeyword)); assert(optional('with', withKeyword));
var mixinTypes = popTypeList(); var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes)); push(ast.withClause(withKeyword, mixinTypes));
} }
@ -3781,9 +3793,10 @@ class AstBuilder extends StackListener {
debugEvent("Implements"); debugEvent("Implements");
if (implementsKeyword != null) { if (implementsKeyword != null) {
var types = popTypedList2<TypeAnnotation>(interfacesCount); endTypeList(interfacesCount);
// TODO(brianwilkerson) Report diagnostics for any type that's filtered out. final interfaces = _popNamedTypeList(
var interfaces = types.whereType<NamedType>().toList(); errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_IMPLEMENTS,
);
push( push(
ImplementsClauseImpl( ImplementsClauseImpl(
implementsKeyword: implementsKeyword, implementsKeyword: implementsKeyword,
@ -4086,9 +4099,10 @@ class AstBuilder extends StackListener {
debugEvent("MixinOn"); debugEvent("MixinOn");
if (onKeyword != null) { if (onKeyword != null) {
var types = popTypedList2<TypeAnnotation>(typeCount); endTypeList(typeCount);
// TODO(brianwilkerson) Report diagnostics for any type that's filtered out. final onTypes = _popNamedTypeList(
var onTypes = types.whereType<NamedType>().toList(); errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_ON,
);
push(ast.onClause(onKeyword, onTypes)); push(ast.onClause(onKeyword, onTypes));
} else { } else {
push(NullValue.IdentifierList); push(NullValue.IdentifierList);
@ -4116,7 +4130,9 @@ class AstBuilder extends StackListener {
@override @override
void handleNamedMixinApplicationWithClause(Token withKeyword) { void handleNamedMixinApplicationWithClause(Token withKeyword) {
assert(optionalOrNull('with', withKeyword)); assert(optionalOrNull('with', withKeyword));
var mixinTypes = popTypeList(); var mixinTypes = _popNamedTypeList(
errorCode: ParserErrorCode.EXPECTED_NAMED_TYPE_WITH,
);
push(ast.withClause(withKeyword, mixinTypes)); push(ast.withClause(withKeyword, mixinTypes));
} }
@ -4676,21 +4692,6 @@ class AstBuilder extends StackListener {
return result.reversed.toList(); 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) { void reportErrorIfNullableType(Token? questionMark) {
if (questionMark != null) { if (questionMark != null) {
assert(optional('?', questionMark)); assert(optional('?', questionMark));
@ -4761,6 +4762,21 @@ class AstBuilder extends StackListener {
typeArguments: typeArguments)); 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({ void _reportFeatureNotEnabled({
required ExperimentalFeature feature, required ExperimentalFeature feature,
required Token startToken, required Token startToken,

View file

@ -21892,6 +21892,22 @@ ParserErrorCode:
EXPECTED_LIST_OR_MAP_LITERAL: EXPECTED_LIST_OR_MAP_LITERAL:
problemMessage: Expected a list or map literal. problemMessage: Expected a list or map literal.
correctionMessage: Try inserting a list or map literal, or remove the type arguments. 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: EXPECTED_STRING_LITERAL:
problemMessage: Expected a string literal. problemMessage: Expected a string literal.
EXPECTED_TOKEN: EXPECTED_TOKEN:

View file

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