Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(264)

Unified Diff: runtime/vm/parser.cc

Issue 2260693002: Implement parsing support for generic functions (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Cleanup Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/generic_functions_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/parser.cc
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index e9e6e93b215b35c03ca941902529e795dc244fa2..6ec2886643f90657b49167f26d6ffce2168e3d5e 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -48,6 +48,7 @@ DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
// committed to the current version.
DEFINE_FLAG(bool, conditional_directives, true,
"Enable conditional directives");
+DEFINE_FLAG(bool, generic_method_syntax, false, "Enbable generic functions.");
DEFINE_FLAG(bool, initializing_formal_access, false,
"Make initializing formal parameters visible in initializer list.");
DEFINE_FLAG(bool, warn_super, false,
@@ -125,6 +126,24 @@ class BoolScope : public ValueObject {
};
+// Helper class to save and restore token position.
+class Parser::TokenPosScope : public ValueObject {
+ public:
+ explicit TokenPosScope(Parser *p) : _p(p) {
+ _saved_pos = p->TokenPos();
+ }
+ TokenPosScope(Parser *p, TokenPosition pos) : _p(p), _saved_pos(pos) {
+ }
+ ~TokenPosScope() {
+ _p->SetPosition(_saved_pos);
+ }
+
+ private:
+ Parser* _p;
+ TokenPosition _saved_pos;
siva 2016/08/19 20:30:58 Normally we have been using _ as a suffix not pref
hausner 2016/08/19 21:40:04 Done. Accedentally mixed dart and c++ convention.
+};
+
+
class RecursionChecker : public ValueObject {
public:
explicit RecursionChecker(Parser* p) : parser_(p) {
@@ -2065,7 +2084,7 @@ void Parser::ParseFormalParameter(bool allow_explicit_default_value,
}
}
- if (CurrentToken() == Token::kLPAREN) {
+ if (IsParameterPart()) {
// This parameter is probably a closure. If we saw the keyword 'var'
// or 'final', a closure is not legal here and we ignore the
// opening parens.
@@ -2079,6 +2098,18 @@ void Parser::ParseFormalParameter(bool allow_explicit_default_value,
AbstractType::Handle(Z, parameter.type->raw());
// Finish parsing the function type parameter.
+ if (CurrentToken() == Token::kLT) {
+ // TODO(hausner): handle generic function types.
+ TokenPosition type_param_pos = TokenPos();
+ if (!TryParseTypeParameters()) {
+ ReportError(type_param_pos, "error in type parameters");
siva 2016/08/19 20:30:58 This error seems to be printed unconditionally eve
hausner 2016/08/19 21:40:04 The old code would have printed some other syntax
+ }
+ if (!FLAG_generic_method_syntax) {
+ ReportError(type_param_pos, "generic function types not supported");
+ }
+ }
+
+ ASSERT(CurrentToken() == Token::kLPAREN);
ParamList func_params;
// Add implicit closure object parameter.
@@ -3716,7 +3747,10 @@ RawLibraryPrefix* Parser::ParsePrefix() {
void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
TRACE_PARSER("ParseMethodOrConstructor");
- ASSERT(CurrentToken() == Token::kLPAREN || method->IsGetter());
+ // We are at the beginning of the formal parameters list.
+ ASSERT(CurrentToken() == Token::kLPAREN ||
+ CurrentToken() == Token::kLT ||
+ method->IsGetter());
ASSERT(method->type != NULL);
ASSERT(current_member_ == method);
@@ -3741,6 +3775,22 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
current_class().set_is_const();
}
+ if (CurrentToken() == Token::kLT) {
+ // Parse type parameters, but ignore them.
+ // TODO(hausner): handle type parameters.
+ TokenPosition type_param_pos = TokenPos();
+ if (method->IsFactoryOrConstructor()) {
+ ReportError(method->name_pos, "constructor cannot be generic");
+ }
+ if (method->IsGetter() || method->IsSetter()) {
+ ReportError(type_param_pos, "%s cannot be generic",
+ method->IsGetter() ? "getter" : "setter");
+ }
+ if (!TryParseTypeParameters()) {
+ ReportError(type_param_pos, "error in type parameters");
+ }
siva 2016/08/19 20:30:58 Ditto comment about error being unconditional and
hausner 2016/08/19 21:40:04 The error will be printed when the function is act
+ }
+
// Parse the formal parameters.
const bool are_implicitly_final = method->has_const;
const bool allow_explicit_default_values = true;
@@ -4341,6 +4391,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
member.has_static = true;
// The result type depends on the name of the factory method.
}
+
// Optionally parse a type.
if (CurrentToken() == Token::kVOID) {
if (member.has_var || member.has_factory) {
@@ -4349,29 +4400,24 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
ConsumeToken();
ASSERT(member.type == NULL);
member.type = &Object::void_type();
- } else if (CurrentToken() == Token::kIDENT) {
- // This is either a type name or the name of a method/constructor/field.
- if ((member.type == NULL) && !member.has_factory) {
- // We have not seen a member type yet, so we check if the next
- // identifier could represent a type before parsing it.
- Token::Kind follower = LookaheadToken(1);
- // We have an identifier followed by a 'follower' token.
- // We either parse a type or assume that no type is specified.
- if ((follower == Token::kLT) || // Parameterized type.
- (follower == Token::kGET) || // Getter following a type.
- (follower == Token::kSET) || // Setter following a type.
- (follower == Token::kOPERATOR) || // Operator following a type.
- (Token::IsIdentifier(follower)) || // Member name following a type.
- ((follower == Token::kPERIOD) && // Qualified class name of type,
- (LookaheadToken(3) != Token::kLPAREN))) { // but not a named constr.
- ASSERT(is_top_level_);
- // The declared type of fields is never ignored, even in unchecked mode,
- // because getters and setters could be closurized at some time (not
- // supported yet).
- member.type = &AbstractType::ZoneHandle(Z,
- ParseType(ClassFinalizer::kResolveTypeParameters));
+ } else {
+ bool found_type = false;
+ {
+ // Lookahead to determine whether the next tokens are a return type.
+ TokenPosScope saved_pos(this);
+ if (TryParseReturnType()) {
+ if (IsIdentifier() ||
+ (CurrentToken() == Token::kGET) ||
+ (CurrentToken() == Token::kSET) ||
+ (CurrentToken() == Token::kOPERATOR)) {
+ found_type = true;
+ }
}
}
+ if (found_type) {
+ member.type = &AbstractType::ZoneHandle(Z,
+ ParseType(ClassFinalizer::kResolveTypeParameters));
+ }
}
// Optionally parse a (possibly named) constructor name or factory.
@@ -4422,6 +4468,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
CheckToken(Token::kLPAREN);
} else if ((CurrentToken() == Token::kGET) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
+ (LookaheadToken(1) != Token::kLT) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
@@ -4432,6 +4479,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
// If the result type was not specified, it will be set to DynamicType.
} else if ((CurrentToken() == Token::kSET) && !member.has_var &&
(LookaheadToken(1) != Token::kLPAREN) &&
+ (LookaheadToken(1) != Token::kLT) &&
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
@@ -4450,6 +4498,8 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
(LookaheadToken(1) != Token::kASSIGN) &&
(LookaheadToken(1) != Token::kCOMMA) &&
(LookaheadToken(1) != Token::kSEMICOLON)) {
+ // TODO(hausner): handle the case of a generic function named 'operator':
+ // eg: T operator<T>(a, b) => ...
ConsumeToken();
if (!Token::CanBeOverloaded(CurrentToken())) {
ReportError("invalid operator overloading");
@@ -4473,7 +4523,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members,
}
ASSERT(member.name != NULL);
- if (CurrentToken() == Token::kLPAREN || member.IsGetter()) {
+ if (IsParameterPart() || member.IsGetter()) {
// Constructor or method.
if (member.type == NULL) {
member.type = &Object::dynamic_type();
@@ -5128,16 +5178,14 @@ bool Parser::IsFunctionTypeAliasName() {
if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) {
return true;
}
- const TokenPosition saved_pos = TokenPos();
- bool is_alias_name = false;
+ const TokenPosScope saved_pos(this);
if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (TryParseTypeParameters() && (CurrentToken() == Token::kLPAREN)) {
- is_alias_name = true;
+ return true;
}
}
- SetPosition(saved_pos);
- return is_alias_name;
+ return false;
}
@@ -5147,16 +5195,14 @@ bool Parser::IsMixinAppAlias() {
if (IsIdentifier() && (LookaheadToken(1) == Token::kASSIGN)) {
return true;
}
- const TokenPosition saved_pos = TokenPos();
- bool is_mixin_def = false;
+ const TokenPosScope saved_pos(this);
if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) {
ConsumeToken();
if (TryParseTypeParameters() && (CurrentToken() == Token::kASSIGN)) {
- is_mixin_def = true;
+ return true;
}
}
- SetPosition(saved_pos);
- return is_mixin_def;
+ return false;
}
@@ -5263,9 +5309,9 @@ void Parser::ParseTypedef(const GrowableObjectArray& pending_classes,
}
-// Consumes exactly one right angle bracket. If the current token is a single
-// bracket token, it is consumed normally. However, if it is a double or triple
-// bracket, it is replaced by a single or double bracket token without
+// Consumes exactly one right angle bracket. If the current token is
+// a single bracket token, it is consumed normally. However, if it is
+// a double bracket, it is replaced by a single bracket token without
// incrementing the token index.
void Parser::ConsumeRightAngleBracket() {
if (token_kind_ == Token::kGT) {
@@ -5282,12 +5328,10 @@ bool Parser::IsPatchAnnotation(TokenPosition pos) {
if (pos == TokenPosition::kNoSource) {
return false;
}
- TokenPosition saved_pos = TokenPos();
+ TokenPosScope saved_pos(this);
SetPosition(pos);
ExpectToken(Token::kAT);
- bool is_patch = IsSymbol(Symbols::Patch());
- SetPosition(saved_pos);
- return is_patch;
+ return IsSymbol(Symbols::Patch());
}
@@ -5645,8 +5689,7 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
result_type = Type::VoidType();
} else {
// Parse optional type.
- if ((CurrentToken() == Token::kIDENT) &&
- (LookaheadToken(1) != Token::kLPAREN)) {
+ if (IsFunctionReturnType()) {
result_type = ParseType(ClassFinalizer::kResolveTypeParameters);
}
}
@@ -5668,6 +5711,18 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
// A setter named x= may co-exist with a function named x, thus we do
// not need to check setters.
+ if (CurrentToken() == Token::kLT) {
+ // Type parameters of generic function.
+ // TODO(hausner): handle type parameters.
+ TokenPosition type_arg_pos = TokenPos();
+ if (!TryParseTypeParameters()) {
+ ReportError(type_arg_pos, "error in type parameters");
+ }
siva 2016/08/19 20:30:58 Ditto comment about the error being unconditional
hausner 2016/08/19 21:40:04 Done.
+ if (!FLAG_generic_method_syntax) {
+ ReportError(type_arg_pos, "generic functions not supported");
+ }
+ }
+
CheckToken(Token::kLPAREN);
const TokenPosition function_pos = TokenPos();
ParamList params;
@@ -7760,15 +7815,14 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
const TokenPosition function_pos = TokenPos();
TokenPosition metadata_pos = TokenPosition::kNoSource;
if (is_literal) {
- ASSERT(CurrentToken() == Token::kLPAREN);
+ ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT);
function_name = &Symbols::AnonymousClosure();
} else {
metadata_pos = SkipMetadata();
if (CurrentToken() == Token::kVOID) {
ConsumeToken();
result_type = Type::VoidType();
- } else if ((CurrentToken() == Token::kIDENT) &&
- (LookaheadToken(1) != Token::kLPAREN)) {
+ } else if (IsFunctionReturnType()) {
result_type = ParseType(ClassFinalizer::kCanonicalize);
}
const TokenPosition name_pos = TokenPos();
@@ -7790,6 +7844,18 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
line_number);
}
}
+
+ if (CurrentToken() == Token::kLT) {
+ TokenPosition type_arg_pos = TokenPos();
+ // TODO(hausner): handle type parameters of generic function.
+ if (!TryParseTypeParameters()) {
+ ReportError(type_arg_pos, "error in type parameters");
+ }
siva 2016/08/19 20:30:58 Ditto comment about the error being unconditional
hausner 2016/08/19 21:40:04 Done.
+ if (!FLAG_generic_method_syntax) {
+ ReportError(type_arg_pos, "generic functions not supported");
+ }
+ }
+
CheckToken(Token::kLPAREN);
// Check whether we have parsed this closure function before, in a previous
@@ -7967,20 +8033,22 @@ bool Parser::TryParseTypeParameters() {
// We are possibly looking at type parameters. Find closing ">".
int nesting_level = 0;
do {
- if (CurrentToken() == Token::kLT) {
+ Token::Kind ct = CurrentToken();
+ if (ct == Token::kLT) {
nesting_level++;
- } else if (CurrentToken() == Token::kGT) {
+ } else if (ct == Token::kGT) {
nesting_level--;
- } else if (CurrentToken() == Token::kSHR) {
+ } else if (ct == Token::kSHR) {
nesting_level -= 2;
- } else if (CurrentToken() == Token::kIDENT) {
+ } else if (ct == Token::kIDENT) {
// Check to see if it is a qualified identifier.
if (LookaheadToken(1) == Token::kPERIOD) {
// Consume the identifier, the period will be consumed below.
ConsumeToken();
}
- } else if (CurrentToken() != Token::kCOMMA &&
- CurrentToken() != Token::kEXTENDS) {
+ } else if ((ct != Token::kCOMMA) &&
+ (ct != Token::kEXTENDS) &&
+ (!FLAG_generic_method_syntax || (ct != Token::kSUPER))) {
// We are looking at something other than type parameters.
return false;
}
@@ -7994,6 +8062,84 @@ bool Parser::TryParseTypeParameters() {
}
+// Returns true if the next tokens can be parsed as type parameters.
+bool Parser::IsTypeParameters() {
+ if (CurrentToken() == Token::kLT) {
+ TokenPosScope param_pos(this);
+ if (!TryParseTypeParameters()) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+// Returns true if the next tokens are [ typeParameters ] '('.
+bool Parser::IsParameterPart() {
+ if (CurrentToken() == Token::kLPAREN) {
+ return true;
+ }
+ if (CurrentToken() == Token::kLT) {
+ TokenPosScope type_arg_pos(this);
+ if (!TryParseTypeParameters()) {
+ return false;
+ }
+ return CurrentToken() == Token::kLPAREN;
+ }
+ return false;
+}
+
+
+// Returns true if the current and next tokens can be parsed as type
+// arguments. Current token position is not saved and restored.
+bool Parser::TryParseTypeArguments() {
+ if (CurrentToken() == Token::kLT) {
siva 2016/08/19 20:30:58 Ditto comment about turning this 'if' into an ASSE
hausner 2016/08/19 21:40:04 Done.
+ // We are possibly looking at type arguments. Find closing ">".
+ int nesting_level = 0;
+ do {
+ Token::Kind ct = CurrentToken();
+ if (ct == Token::kLT) {
+ nesting_level++;
+ } else if (ct == Token::kGT) {
+ nesting_level--;
+ } else if (ct == Token::kSHR) {
+ nesting_level -= 2;
+ } else if (ct == Token::kIDENT) {
+ // Check to see if it is a qualified identifier.
+ if (ct == Token::kPERIOD) {
siva 2016/08/19 20:30:58 ct was kIDENT based on the check above, how will (
hausner 2016/08/19 21:40:04 Nice catch, thank you.
+ // Consume the identifier, the period will be consumed below.
+ ConsumeToken();
+ }
+ } else if (ct != Token::kCOMMA) {
+ return false;
+ }
+ ConsumeToken();
+ } while (nesting_level > 0);
+ if (nesting_level < 0) {
+ return false;
+ }
+ }
+ return true;
siva 2016/08/19 20:30:58 This will return true for something like <<>,<>> o
hausner 2016/08/19 21:40:04 Yes, for the same reason as explained above. Parse
+}
+
+
+// Returns true if the next tokens are [ typeArguments ] '('.
+bool Parser::IsArgumentPart() {
+ if (CurrentToken() == Token::kLPAREN) {
+ return true;
+ }
+ if (CurrentToken() == Token::kLT) {
+ TokenPosScope type_arg_pos(this);
+ if (!TryParseTypeArguments()) {
+ return false;
+ }
+ return CurrentToken() == Token::kLPAREN;
+ }
+ return false;
+}
+
+
bool Parser::IsSimpleLiteral(const AbstractType& type, Instance* value) {
// Assigning null never causes a type error.
if (CurrentToken() == Token::kNULL) {
@@ -8157,84 +8303,114 @@ bool Parser::IsVariableDeclaration() {
}
+// Look ahead to see if the following tokens are a return type followed
+// by an identifier.
+bool Parser::IsFunctionReturnType() {
+ TokenPosScope decl_pos(this);
+ if (TryParseReturnType()) {
+ if (IsIdentifier()) {
+ // Return type followed by function name.
+ return true;
+ }
+ }
+ return false;
+}
+
+
// Look ahead to detect whether the next tokens should be parsed as
// a function declaration. Token position remains unchanged.
bool Parser::IsFunctionDeclaration() {
- const TokenPosition saved_pos = TokenPos();
bool is_external = false;
+ TokenPosScope decl_pos(this);
SkipMetadata();
- if (is_top_level_ && (CurrentToken() == Token::kEXTERNAL)) {
- // Skip over 'external' for top-level function declarations.
- is_external = true;
- ConsumeToken();
+ if (is_top_level_) {
+ if (is_patch_source() &&
+ (CurrentToken() == Token::kIDENT) &&
+ CurrentLiteral()->Equals("patch") &&
+ (LookaheadToken(1) != Token::kLPAREN)) {
+ // Skip over 'patch' for top-level function declarations in patch sources.
siva 2016/08/19 20:30:58 I thought we removed support for 'patch' going for
hausner 2016/08/19 21:40:04 Yes. This probably reappeared because I copied thi
+ ConsumeToken();
+ } else if (CurrentToken() == Token::kEXTERNAL) {
+ // Skip over 'external' for top-level function declarations.
+ is_external = true;
+ ConsumeToken();
+ }
}
- if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) {
- // Possibly a function without explicit return type.
- ConsumeToken(); // Consume function identifier.
- } else if (TryParseReturnType()) {
+ const TokenPosition type_or_name_pos = TokenPos();
+ if (TryParseReturnType()) {
if (!IsIdentifier()) {
- SetPosition(saved_pos);
- return false;
+ SetPosition(type_or_name_pos);
}
- ConsumeToken(); // Consume function identifier.
} else {
- SetPosition(saved_pos);
+ SetPosition(type_or_name_pos);
+ }
+ // Check for function name followed by optional type parameters.
+ if (!IsIdentifier()) {
return false;
}
+ ConsumeToken();
+ if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
+ return false;
+ }
+
+ // Optional type, function name and optinal type parameters are parsed.
+ if (CurrentToken() != Token::kLPAREN) {
+ return false;
+ }
+
// Check parameter list and the following token.
- if (CurrentToken() == Token::kLPAREN) {
- SkipToMatchingParenthesis();
- if ((CurrentToken() == Token::kLBRACE) ||
- (CurrentToken() == Token::kARROW) ||
- (is_top_level_ && IsSymbol(Symbols::Native())) ||
- is_external ||
- IsSymbol(Symbols::Async()) ||
- IsSymbol(Symbols::Sync())) {
- SetPosition(saved_pos);
- return true;
- }
+ SkipToMatchingParenthesis();
+ if ((CurrentToken() == Token::kLBRACE) ||
+ (CurrentToken() == Token::kARROW) ||
+ (is_top_level_ && IsSymbol(Symbols::Native())) ||
+ is_external ||
+ IsSymbol(Symbols::Async()) ||
+ IsSymbol(Symbols::Sync())) {
+ return true;
}
- SetPosition(saved_pos);
return false;
}
bool Parser::IsTopLevelAccessor() {
- const TokenPosition saved_pos = TokenPos();
+ const TokenPosScope saved_pos(this);
if (CurrentToken() == Token::kEXTERNAL) {
ConsumeToken();
}
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
- SetPosition(saved_pos);
return true;
}
if (TryParseReturnType()) {
if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) {
if (Token::IsIdentifier(LookaheadToken(1))) { // Accessor name.
- SetPosition(saved_pos);
return true;
}
}
}
- SetPosition(saved_pos);
return false;
}
bool Parser::IsFunctionLiteral() {
- if (CurrentToken() != Token::kLPAREN || !allow_function_literals_) {
+ if (!allow_function_literals_) {
return false;
}
- const TokenPosition saved_pos = TokenPos();
- bool is_function_literal = false;
- SkipToMatchingParenthesis();
- ParseFunctionModifier();
- if ((CurrentToken() == Token::kLBRACE) ||
- (CurrentToken() == Token::kARROW)) {
- is_function_literal = true;
+ if ((CurrentToken() == Token::kLPAREN) || (CurrentToken() == Token::kLT)) {
+ TokenPosScope saved_pos(this);
+ if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) {
+ return false;
+ }
+ if (CurrentToken() != Token::kLPAREN) {
+ return false;
+ }
+ SkipToMatchingParenthesis();
+ ParseFunctionModifier();
+ if ((CurrentToken() == Token::kLBRACE) ||
+ (CurrentToken() == Token::kARROW)) {
+ return true;
+ }
}
- SetPosition(saved_pos);
- return is_function_literal;
+ return false;
}
@@ -8242,8 +8418,7 @@ bool Parser::IsFunctionLiteral() {
// statement. Returns true if we recognize a for ( .. in expr)
// statement.
bool Parser::IsForInStatement() {
- const TokenPosition saved_pos = TokenPos();
- bool result = false;
+ const TokenPosScope saved_pos(this);
// Allow const modifier as well when recognizing a for-in statement
// pattern. We will get an error later if the loop variable is
// declared with const.
@@ -8254,16 +8429,15 @@ bool Parser::IsForInStatement() {
}
if (IsIdentifier()) {
if (LookaheadToken(1) == Token::kIN) {
- result = true;
+ return true;
} else if (TryParseOptionalType()) {
if (IsIdentifier()) {
ConsumeToken();
}
- result = (CurrentToken() == Token::kIN);
+ return CurrentToken() == Token::kIN;
}
}
- SetPosition(saved_pos);
- return result;
+ return false;
}
@@ -11663,8 +11837,17 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) {
}
const TokenPosition ident_pos = TokenPos();
String* ident = ExpectIdentifier("identifier expected");
- if (CurrentToken() == Token::kLPAREN) {
- // Identifier followed by a opening paren: method call.
+ if (IsArgumentPart()) {
+ // Identifier followed by optional type arguments and opening paren:
+ // method call.
+ if (CurrentToken() == Token::kLT) {
+ // Type arguments.
+ if (!FLAG_generic_method_syntax) {
+ ReportError("generic type arguments not supported.");
+ }
+ // TODO(hausner): handle type arguments.
+ ParseTypeArguments(ClassFinalizer::kIgnore);
+ }
if (left->IsPrimaryNode() &&
left->AsPrimaryNode()->primary().IsClass()) {
// Static method call prefixed with class name.
@@ -11756,7 +11939,15 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) {
}
selector = new(Z) LoadIndexedNode(
bracket_pos, array, index, Class::ZoneHandle(Z));
- } else if (CurrentToken() == Token::kLPAREN) {
+ } else if (IsArgumentPart()) {
+ if (CurrentToken() == Token::kLT) {
+ // Type arguments.
+ if (!FLAG_generic_method_syntax) {
+ ReportError("generic type arguments not supported.");
+ }
+ // TODO(hausner): handle type arguments.
+ ParseTypeArguments(ClassFinalizer::kIgnore);
+ }
if (left->IsPrimaryNode()) {
PrimaryNode* primary_node = left->AsPrimaryNode();
const TokenPosition primary_pos = primary_node->token_pos();
@@ -14377,6 +14568,9 @@ void Parser::SkipMapLiteral() {
void Parser::SkipActualParameters() {
+ if (CurrentToken() == Token::kLT) {
+ SkipTypeArguments();
+ }
ExpectToken(Token::kLPAREN);
while (CurrentToken() != Token::kRPAREN) {
if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) {
@@ -14544,7 +14738,7 @@ void Parser::SkipSelectors() {
ConsumeToken();
SkipNestedExpr();
ExpectToken(Token::kRBRACK);
- } else if (current_token == Token::kLPAREN) {
+ } else if (IsArgumentPart()) {
SkipActualParameters();
} else {
break;
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/generic_functions_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698