Skip to content

Commit f51895d

Browse files
committed
Merge branch 'master' into nnbd-mastermerge-0907
2 parents 2f11e3e + a6b2eb9 commit f51895d

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

lib/src/comment_references/parser.dart

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class CommentReferenceParser {
9696
/// ```text
9797
/// <rawCommentReference> ::= <prefix>?<commentReference><suffix>?
9898
///
99-
/// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <dartdocIdentifier> ('.' <identifier>)*
99+
/// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <dartdocIdentifier> <typeArguments> ('.' <identifier> <typeArguments>)*
100100
/// ```
101101
List<CommentReferenceNode> _parseRawCommentReference() {
102102
var children = <CommentReferenceNode>[];
@@ -124,6 +124,17 @@ class CommentReferenceParser {
124124
} else if (identifierResult.type ==
125125
_IdentifierResultType.parsedIdentifier) {
126126
children.add(identifierResult.node);
127+
var typeVariablesResult = _parseTypeVariables();
128+
if (typeVariablesResult.type == _TypeVariablesResultType.endOfFile) {
129+
break;
130+
} else if (typeVariablesResult.type ==
131+
_TypeVariablesResultType.notTypeVariables) {
132+
// Do nothing, _index has not moved.
133+
;
134+
} else if (typeVariablesResult.type ==
135+
_TypeVariablesResultType.parsedTypeVariables) {
136+
children.add(typeVariablesResult.node);
137+
}
127138
}
128139
if (_atEnd || _thisChar != $dot) {
129140
break;
@@ -239,6 +250,22 @@ class CommentReferenceParser {
239250
IdentifierNode(codeRef.substring(startIndex, _index)));
240251
}
241252

253+
/// Parse a list of type variables (arguments or parameters).
254+
///
255+
/// Dartdoc isolates these where present and potentially valid, but we don't
256+
/// break them down.
257+
_TypeVariablesParseResult _parseTypeVariables() {
258+
if (_atEnd) {
259+
return _TypeVariablesParseResult.endOfFile;
260+
}
261+
var startIndex = _index;
262+
if (_matchBraces($lt, $gt)) {
263+
return _TypeVariablesParseResult.ok(
264+
TypeVariablesNode(codeRef.substring(startIndex + 1, _index - 1)));
265+
}
266+
return _TypeVariablesParseResult.notIdentifier;
267+
}
268+
242269
static const _callableHintSuffix = '()';
243270

244271
/// ```text
@@ -270,7 +297,7 @@ class CommentReferenceParser {
270297
if ((_thisChar == $exclamation || _thisChar == $question) && _nextAtEnd) {
271298
return _SuffixParseResult.junk;
272299
}
273-
if (_matchBraces($lparen, $rparen) || _matchBraces($lt, $gt)) {
300+
if (_matchBraces($lparen, $rparen)) {
274301
return _SuffixParseResult.junk;
275302
}
276303

@@ -334,8 +361,10 @@ class CommentReferenceParser {
334361
while (!_atEnd) {
335362
if (_thisChar == startChar) braceCount++;
336363
if (_thisChar == endChar) braceCount--;
337-
++_index;
338-
if (braceCount == 0) return true;
364+
_index++;
365+
if (braceCount == 0) {
366+
return true;
367+
}
339368
}
340369
_index = startIndex;
341370
return false;
@@ -395,6 +424,32 @@ class _IdentifierParseResult {
395424
_IdentifierParseResult._(_IdentifierResultType.notIdentifier, null);
396425
}
397426

427+
enum _TypeVariablesResultType {
428+
endOfFile, // Found end of file instead of the beginning of a list of type
429+
// variables.
430+
notTypeVariables, // Found something, but it isn't type variables.
431+
parsedTypeVariables, // Found type variables.
432+
}
433+
434+
class _TypeVariablesParseResult {
435+
final _TypeVariablesResultType type;
436+
437+
final TypeVariablesNode node;
438+
439+
const _TypeVariablesParseResult._(this.type, this.node);
440+
441+
factory _TypeVariablesParseResult.ok(TypeVariablesNode node) =>
442+
_TypeVariablesParseResult._(
443+
_TypeVariablesResultType.parsedTypeVariables, node);
444+
445+
static const _TypeVariablesParseResult endOfFile =
446+
_TypeVariablesParseResult._(_TypeVariablesResultType.endOfFile, null);
447+
448+
static const _TypeVariablesParseResult notIdentifier =
449+
_TypeVariablesParseResult._(
450+
_TypeVariablesResultType.notTypeVariables, null);
451+
}
452+
398453
enum _SuffixResultType {
399454
junk, // Found known types of junk it is OK to ignore.
400455
missing, // There is no suffix here. Same as EOF as this is a suffix.
@@ -459,3 +514,19 @@ class IdentifierNode extends CommentReferenceNode {
459514
@override
460515
String toString() => 'Identifier["$text"]';
461516
}
517+
518+
/// Represents one or more type variables, may be
519+
/// comma separated.
520+
class TypeVariablesNode extends CommentReferenceNode {
521+
@override
522+
523+
/// Note that this will contain commas, spaces, and other text, as
524+
/// generally type variables are a form of junk that comment references
525+
/// should ignore.
526+
final String text;
527+
528+
TypeVariablesNode(this.text);
529+
530+
@override
531+
String toString() => 'TypeVariablesNode["$text"]';
532+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ environment:
77
sdk: '>=2.12.0 <3.0.0'
88

99
dependencies:
10-
analyzer: ^2.1.0
10+
analyzer: ^2.2.0
1111
args: ^2.0.0
1212
charcode: ^1.2.0
1313
collection: ^1.15.0

test/comment_referable/parser_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ void main() {
8383
['ThisThingy', '[]', 'parameter']);
8484
});
8585

86+
test('Check that embedded types within tearoff-like constructs parse', () {
87+
expectParseEquivalent('this<stuff>.isValid', ['this', 'isValid']);
88+
expectParseEquivalent(
89+
'this<stuff, is, also>.isValid', ['this', 'isValid']);
90+
expectParseEquivalent('this<stuff<that<is, real>, complicated>>.isValid',
91+
['this', 'isValid']);
92+
expectParseError('this<stuff.isntValid');
93+
expectParseError('this<stuff>, notBeingValid>.isntValid');
94+
expectParseError('(AndThisMayBeValidDart.butIt).isntValidInReferences');
95+
expectParseError('<NotActually>.valid');
96+
});
97+
8698
test('Basic negative tests', () {
8799
expectParseError(r'.');
88100
expectParseError(r'');

test/end2end/model_special_cases_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,31 @@ void main() {
205205
expect(referenceLookup(constructorTearoffs, 'Ft.new'),
206206
equals(MatchingLinkResult(Fnew)));
207207
});
208+
209+
test('we can use (ignored) type parameters in references', () {
210+
expect(referenceLookup(E, 'D<String>.new'),
211+
equals(MatchingLinkResult(Dnew)));
212+
expect(referenceLookup(constructorTearoffs, 'F<T>.new'),
213+
equals(MatchingLinkResult(Fnew)));
214+
expect(
215+
referenceLookup(
216+
constructorTearoffs, 'F<InvalidThings, DoNotMatterHere>.new'),
217+
equals(MatchingLinkResult(Fnew)));
218+
});
219+
220+
test('negative tests', () {
221+
// Mixins do not have constructors.
222+
expect(referenceLookup(constructorTearoffs, 'M.new'),
223+
equals(MatchingLinkResult(null)));
224+
// These things aren't expressions, parentheses are still illegal.
225+
expect(referenceLookup(constructorTearoffs, '(C).new'),
226+
equals(MatchingLinkResult(null)));
227+
228+
// A bare new will still not work to reference constructors.
229+
// TODO(jcollins-g): reconsider this if we remove "new" as a hint.
230+
expect(referenceLookup(A, 'new'), equals(MatchingLinkResult(null)));
231+
expect(referenceLookup(At, 'new'), equals(MatchingLinkResult(null)));
232+
});
208233
}, skip: !_constructorTearoffsAllowed.allows(utils.platformVersion));
209234
});
210235

0 commit comments

Comments
 (0)