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

Unified Diff: pkg/analyzer/lib/src/generated/parser.dart

Issue 1434863003: initial generic method comment parsing (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: re-sort members Created 5 years, 1 month 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 | « no previous file | pkg/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/lib/src/generated/parser.dart
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 76f9dc7e76e32af10ddc3113de95be5c22ab1a81..0d9729b68e03f80565d399a5d6fc18996ca92669 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -2123,6 +2123,12 @@ class Parser {
bool parseGenericMethods = false;
/**
+ * A flag indicating whether to parse generic method comments, of the form
+ * `/*=T*/` and `/*<T>*/`.
+ */
+ bool parseGenericMethodComments = false;
+
+ /**
* Initialize a newly created parser to parse the content of the given
* [_source] and to report any errors that are found to the given
* [_errorListener].
@@ -2514,7 +2520,9 @@ class Parser {
parseSimpleIdentifier(),
parseFormalParameterList());
} else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ TypeName returnType = _parseOptionalTypeNameComment();
SimpleIdentifier methodName = parseSimpleIdentifier();
+ TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
FormalParameterList parameters = parseFormalParameterList();
if (_matches(TokenType.COLON) ||
modifiers.factoryKeyword != null ||
@@ -2535,9 +2543,9 @@ class Parser {
commentAndMetadata,
modifiers.externalKeyword,
modifiers.staticKeyword,
- null,
+ returnType,
methodName,
- null,
+ typeParameters,
parameters);
} else if (_peek()
.matchesAny([TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
@@ -2617,6 +2625,7 @@ class Parser {
}
} else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
SimpleIdentifier methodName = parseSimpleIdentifier();
+ TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
FormalParameterList parameters = parseFormalParameterList();
if (methodName.name == className) {
_reportErrorForNode(ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, type);
@@ -2638,7 +2647,7 @@ class Parser {
modifiers.staticKeyword,
type,
methodName,
- null,
+ typeParameters,
parameters);
} else if (parseGenericMethods && _tokenMatches(_peek(), TokenType.LT)) {
return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
@@ -3135,10 +3144,7 @@ class Parser {
* typeParameters? formalParameterList functionExpressionBody
*/
FunctionExpression parseFunctionExpression() {
- TypeParameterList typeParameters = null;
- if (parseGenericMethods && _matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
- }
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
FormalParameterList parameters = parseFormalParameterList();
_validateFormalParameterList(parameters);
FunctionBody body =
@@ -3265,10 +3271,7 @@ class Parser {
period = _expect(TokenType.PERIOD);
}
SimpleIdentifier identifier = parseSimpleIdentifier();
- TypeParameterList typeParameters = null;
- if (parseGenericMethods && _matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
- }
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
if (_matches(TokenType.OPEN_PAREN)) {
FormalParameterList parameters = parseFormalParameterList();
if (thisKeyword == null) {
@@ -3476,21 +3479,13 @@ class Parser {
* qualified typeArguments?
*/
TypeName parseTypeName() {
- Identifier typeName;
- if (_matchesKeyword(Keyword.VAR)) {
- _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
- typeName = new SimpleIdentifier(getAndAdvance());
- } else if (_matchesIdentifier()) {
- typeName = parsePrefixedIdentifier();
- } else {
- typeName = _createSyntheticIdentifier();
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
- }
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
- return new TypeName(typeName, typeArguments);
+ TypeName realType = _parseTypeName();
+ // If this is followed by a generic method type comment, allow the comment
+ // type to replace the real type name.
+ // TODO(jmesserly): this feels like a big hammer. Can we restrict it to
+ // only work inside generic methods?
+ TypeName typeComment = _parseOptionalTypeNameComment();
+ return typeComment ?? realType;
}
/**
@@ -3909,6 +3904,46 @@ class Parser {
return null;
}
+ bool _injectGenericComment(TokenType type, int prefixLen) {
+ if (parseGenericMethodComments) {
+ CommentToken t = _currentToken.precedingComments;
+ for (; t != null; t = t.next) {
+ if (t.type == type) {
+ String comment = t.lexeme.substring(prefixLen, t.lexeme.length - 2);
+ Token list = _scanGenericMethodComment(comment, t.offset + prefixLen);
+ if (list != null) {
+ // Insert the tokens into the stream.
+ _injectTokenList(list);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Matches a generic comment type substitution and injects it into the token
+ * stream. Returns true if a match was injected, otherwise false.
+ *
+ * These comments are of the form `/*=T*/`, in other words, a [TypeName]
+ * inside a slash-star comment, preceded by equals sign.
+ */
+ bool _injectGenericCommentTypeAssign() {
+ return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3);
+ }
+
+ /**
+ * Matches a generic comment type parameters and injects them into the token
+ * stream. Returns true if a match was injected, otherwise false.
+ *
+ * These comments are of the form `/*<K, V>*/`, in other words, a
+ * [TypeParameterList] or [TypeArgumentList] inside a slash-star comment.
+ */
+ bool _injectGenericCommentTypeList() {
+ return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_LIST, 2);
+ }
+
/**
* Inject the given [token] into the token stream immediately before the
* current token.
@@ -3920,6 +3955,19 @@ class Parser {
return token;
}
+ void _injectTokenList(Token firstToken) {
+ // Scanner creates a cyclic EOF token.
+ Token lastToken = firstToken;
+ while (lastToken.next.type != TokenType.EOF) {
+ lastToken = lastToken.next;
+ }
+ // Inject these new tokens into the stream.
+ Token previous = _currentToken.previous;
+ lastToken.setNext(_currentToken);
+ previous.setNext(firstToken);
+ _currentToken = firstToken;
+ }
+
/**
* Return `true` if the current token appears to be the beginning of a
* function declaration.
@@ -4361,10 +4409,7 @@ class Parser {
bool isOptional = primaryAllowed || expression is SimpleIdentifier;
while (true) {
while (_isLikelyParameterList()) {
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
ArgumentList argumentList = parseArgumentList();
if (expression is SimpleIdentifier) {
expression = new MethodInvocation(null, null,
@@ -4573,10 +4618,7 @@ class Parser {
(expression != null && functionName == null));
if (_isLikelyParameterList()) {
while (_isLikelyParameterList()) {
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
if (functionName != null) {
expression = new MethodInvocation(expression, period, functionName,
typeArguments, parseArgumentList());
@@ -4604,10 +4646,7 @@ class Parser {
expression = selector;
progress = true;
while (_isLikelyParameterList()) {
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
if (expression is PropertyAccess) {
PropertyAccess propertyAccess = expression as PropertyAccess;
expression = new MethodInvocation(
@@ -5691,16 +5730,27 @@ class Parser {
keyword = getAndAdvance();
if (_isTypedIdentifier(_currentToken)) {
type = parseTypeName();
+ } else {
+ // Support `final/*=T*/ x;`
+ type = _parseOptionalTypeNameComment();
}
} else if (_matchesKeyword(Keyword.VAR)) {
keyword = getAndAdvance();
+ // Support `var/*=T*/ x;`
+ type = _parseOptionalTypeNameComment();
+ if (type != null) {
+ // Clear the keyword to prevent an error.
+ keyword = null;
+ }
+ } else if (_isTypedIdentifier(_currentToken)) {
+ type = parseReturnType();
+ } else if (!optional) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
} else {
- if (_isTypedIdentifier(_currentToken)) {
- type = parseReturnType();
- } else if (!optional) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
- }
+ // Support parameters such as `(/*=K*/ key, /*=V*/ value)`
+ // This is not supported if the type is required.
+ type = _parseOptionalTypeNameComment();
}
return new FinalConstVarOrType(keyword, type);
}
@@ -6025,10 +6075,7 @@ class Parser {
keyword = getAndAdvance();
}
SimpleIdentifier name = parseSimpleIdentifier();
- TypeParameterList typeParameters = null;
- if (parseGenericMethods && _matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
- }
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
FormalParameterList parameters = null;
if (!isGetter) {
if (_matches(TokenType.OPEN_PAREN)) {
@@ -6182,6 +6229,36 @@ class Parser {
}
/**
+ * Parses generic type parameters from a comment.
+ *
+ * Normally this is handled by [_parseGenericMethodTypeParameters], but if the
+ * code already handles the normal generic type parameters, the comment
+ * matcher can be called directly. For example, we may have already tried
+ * matching `<` (less than sign) in a method declaration, and be currently
+ * on the `(` (open paren) because we didn't find it. In that case, this
+ * function will parse the preceding comment such as `/*<T, R>*/`.
+ */
+ TypeParameterList _parseGenericCommentTypeParameters() {
+ if (_injectGenericCommentTypeList()) {
+ return parseTypeParameterList();
+ }
+ return null;
+ }
+
+ /**
+ * Parse the generic method or function's type parameters.
+ *
+ * For backwards compatibility this can optionally use comments.
+ * See [parseGenericMethodComments].
+ */
+ TypeParameterList _parseGenericMethodTypeParameters() {
+ if (parseGenericMethods && _matches(TokenType.LT) ||
+ _injectGenericCommentTypeList()) {
+ return parseTypeParameterList();
+ }
+ }
+
+ /**
* Parse a getter. The [commentAndMetadata] is the documentation comment and
* metadata to be associated with the declaration. The externalKeyword] is the
* 'external' token. The staticKeyword] is the static keyword, or `null` if
@@ -6473,10 +6550,7 @@ class Parser {
* | mapLiteral
*/
TypedLiteral _parseListOrMapLiteral(Token modifier) {
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
return _parseMapLiteral(modifier, typeArguments);
} else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
@@ -6610,10 +6684,7 @@ class Parser {
Token staticKeyword,
TypeName returnType) {
SimpleIdentifier methodName = parseSimpleIdentifier();
- TypeParameterList typeParameters = null;
- if (parseGenericMethods && _matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
- }
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
FormalParameterList parameters;
if (!_matches(TokenType.OPEN_PAREN) &&
(_matches(TokenType.OPEN_CURLY_BRACKET) ||
@@ -7010,7 +7081,10 @@ class Parser {
* advancing. Return the return type that was parsed.
*/
TypeName _parseOptionalReturnType() {
- if (_matchesKeyword(Keyword.VOID)) {
+ TypeName typeComment = _parseOptionalTypeNameComment();
+ if (typeComment != null) {
+ return typeComment;
+ } else if (_matchesKeyword(Keyword.VOID)) {
return parseReturnType();
} else if (_matchesIdentifier() &&
!_matchesKeyword(Keyword.GET) &&
@@ -7030,6 +7104,24 @@ class Parser {
}
/**
+ * Parse a [TypeArgumentList] if present, otherwise return null.
+ * This also supports the comment form, if enabled: `/*<T>*/`
+ */
+ TypeArgumentList _parseOptionalTypeArguments() {
+ if (_matches(TokenType.LT) || _injectGenericCommentTypeList()) {
+ return parseTypeArgumentList();
+ }
+ return null;
+ }
+
+ TypeName _parseOptionalTypeNameComment() {
+ if (_injectGenericCommentTypeAssign()) {
+ return _parseTypeName();
+ }
+ return null;
+ }
+
+ /**
* Parse a part or part-of directive. The [commentAndMetadata] is the metadata
* to be associated with the directive. Return the part or part-of directive
* that was parsed.
@@ -7081,10 +7173,7 @@ class Parser {
(parseGenericMethods && _matches(TokenType.LT))) {
do {
if (_isLikelyParameterList()) {
- TypeArgumentList typeArguments = null;
- if (_matches(TokenType.LT)) {
- typeArguments = parseTypeArgumentList();
- }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
ArgumentList argumentList = parseArgumentList();
if (operand is PropertyAccess) {
PropertyAccess access = operand as PropertyAccess;
@@ -7741,6 +7830,21 @@ class Parser {
return _parseFunctionTypeAlias(commentAndMetadata, keyword);
}
+ TypeName _parseTypeName() {
+ Identifier typeName;
+ if (_matchesKeyword(Keyword.VAR)) {
+ _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
+ typeName = new SimpleIdentifier(getAndAdvance());
+ } else if (_matchesIdentifier()) {
+ typeName = parsePrefixedIdentifier();
+ } else {
+ typeName = _createSyntheticIdentifier();
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
+ }
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ return new TypeName(typeName, typeArguments);
+ }
+
/**
* Parse a unary expression. Return the unary expression that was parsed.
*
@@ -8084,6 +8188,22 @@ class Parser {
}
/**
+ * Scans the generic method comment, and returns the tokens, otherwise
+ * returns null.
+ */
+ Token _scanGenericMethodComment(String code, int offset) {
+ BooleanErrorListener listener = new BooleanErrorListener();
+ Scanner scanner =
+ new Scanner(null, new SubSequenceReader(code, offset), listener);
+ scanner.setSourceStart(1, 1);
+ Token firstToken = scanner.tokenize();
+ if (listener.errorReported) {
+ return null;
+ }
+ return firstToken;
+ }
+
+ /**
* Skips a block with all containing blocks.
*/
void _skipBlock() {
@@ -8420,7 +8540,8 @@ class Parser {
*/
Token _skipTypeArgumentList(Token startToken) {
Token token = startToken;
- if (!_tokenMatches(token, TokenType.LT)) {
+ if (!_tokenMatches(token, TokenType.LT) &&
+ !_injectGenericCommentTypeList()) {
return null;
}
token = _skipTypeName(token.next);
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698