mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
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:
parent
959854769c
commit
1aa398d4be
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue