Skip to content

Commit 2ab72c4

Browse files
committed
JS: Support line breaks in types
1 parent 625cdb8 commit 2ab72c4

File tree

3 files changed

+48
-21
lines changed

3 files changed

+48
-21
lines changed

javascript/extractor/src/com/semmle/js/parser/JSDocParser.java

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ private static boolean isWhiteSpace(char ch) {
109109
return Character.isWhitespace(ch) && !isLineTerminator(ch) || ch == '\u00a0';
110110
}
111111

112+
private static boolean isWhiteSpaceOrLineTerminator(char ch) {
113+
return Character.isWhitespace(ch) || ch == '\u00a0';
114+
}
115+
112116
private static boolean isDecimalDigit(char ch) {
113117
return "0123456789".indexOf(ch) >= 0;
114118
}
@@ -300,7 +304,14 @@ private String inputSubstring(int start, int end) {
300304

301305
private int advance() {
302306
if (index >= source.length()) return -1;
303-
return source.charAt(index++);
307+
int ch = source.charAt(index);
308+
++index;
309+
if (isLineTerminator(ch) && !(ch == '\r' && index < endIndex && source.charAt(index) == '\n')) {
310+
lineNumber += 1;
311+
lineStart = index;
312+
index = skipStars(index, endIndex);
313+
}
314+
return ch;
304315
}
305316

306317
private String scanHexEscape(char prefix) {
@@ -571,7 +582,7 @@ private Token next() throws ParseError {
571582

572583
endOfPrevToken = index;
573584

574-
while (index < endIndex && isWhiteSpace(source.charAt(index))) {
585+
while (index < endIndex && isWhiteSpaceOrLineTerminator(source.charAt(index))) {
575586
advance();
576587
}
577588
if (index >= endIndex) {
@@ -1211,36 +1222,37 @@ private JSDocTypeExpression parseParamType(int startIndex, int endIndex, int lin
12111222
}
12121223
}
12131224

1225+
/** Skips the leading indentation and '*' at the beginning of a line. */
1226+
private int skipStars(int index, int end) {
1227+
while (index < end
1228+
&& isWhiteSpace(source.charAt(index))
1229+
&& !isLineTerminator(source.charAt(index))) {
1230+
index += 1;
1231+
}
1232+
while (index < end && source.charAt(index) == '*') {
1233+
index += 1;
1234+
}
1235+
while (index < end
1236+
&& isWhiteSpace(source.charAt(index))
1237+
&& !isLineTerminator(source.charAt(index))) {
1238+
index += 1;
1239+
}
1240+
return index;
1241+
}
1242+
12141243
private TypeExpressionParser typed = new TypeExpressionParser();
12151244

12161245
private class JSDocTagParser {
12171246
int index, lineNumber, lineStart, length;
12181247
boolean recoverable = true, sloppy = false;
12191248

1220-
private int skipStars(int index) {
1221-
while (index < length
1222-
&& isWhiteSpace(source.charAt(index))
1223-
&& !isLineTerminator(source.charAt(index))) {
1224-
index += 1;
1225-
}
1226-
while (index < length && source.charAt(index) == '*') {
1227-
index += 1;
1228-
}
1229-
while (index < length
1230-
&& isWhiteSpace(source.charAt(index))
1231-
&& !isLineTerminator(source.charAt(index))) {
1232-
index += 1;
1233-
}
1234-
return index;
1235-
}
1236-
12371249
private char advance() {
12381250
char ch = source.charAt(index);
12391251
index += 1;
12401252
if (isLineTerminator(ch) && !(ch == '\r' && index < length && source.charAt(index) == '\n')) {
12411253
lineNumber += 1;
12421254
lineStart = index;
1243-
index = skipStars(index);
1255+
index = skipStars(index, length);
12441256
}
12451257
return ch;
12461258
}
@@ -1268,7 +1280,7 @@ private int seekContent() {
12681280
&& !(ch == '\r' && last + 1 < length && source.charAt(last + 1) == '\n')) {
12691281
lineNumber += 1;
12701282
lineStart = last + 1;
1271-
last = skipStars(last + 1) - 1;
1283+
last = skipStars(last + 1, length) - 1;
12721284
waiting = true;
12731285
} else if (waiting) {
12741286
if (ch == '@') {

javascript/ql/test/library-tests/JSDoc/tests.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ test_JSDoc
121121
| tst.js:368:3:370:5 | /**\\n ... p\\n */ | | tst.js:368:3:370:5 | /**\\n ... p\\n */ |
122122
| tst.js:375:3:377:5 | /**\\n ... p\\n */ | | tst.js:375:3:377:5 | /**\\n ... p\\n */ |
123123
| tst.js:380:3:382:5 | /**\\n ... p\\n */ | | tst.js:380:3:382:5 | /**\\n ... p\\n */ |
124+
| tst.js:386:1:389:3 | /**\\n * ... } x\\n */ | | tst.js:386:1:389:3 | /**\\n * ... } x\\n */ |
124125
test_JSDocTag
125126
| tst.js:5:5:5:10 | @const | const | tst.js:5:1:5:13 | /** @const */ | 0 | (none) | (none) | (none) |
126127
| tst.js:9:4:9:9 | @const | const | tst.js:7:1:11:3 | /**\\n * ... ng}\\n */ | 0 | (none) | (none) | (none) |
@@ -228,6 +229,7 @@ test_JSDocTag
228229
| tst.js:369:6:369:11 | @param | param | tst.js:368:3:370:5 | /**\\n ... p\\n */ | 0 | (none) | p | T2 |
229230
| tst.js:376:6:376:11 | @param | param | tst.js:375:3:377:5 | /**\\n ... p\\n */ | 0 | (none) | p | T3 |
230231
| tst.js:381:6:381:11 | @param | param | tst.js:380:3:382:5 | /**\\n ... p\\n */ | 0 | (none) | p | T4 |
232+
| tst.js:387:4:387:9 | @param | param | tst.js:386:1:389:3 | /**\\n * ... } x\\n */ | 0 | (none) | x | Array.<number> |
231233
test_ObjectExpr_getDocumentation
232234
| tst.js:48:25:48:36 | { 'x': 321 } | tst.js:48:12:48:23 | /** @dict */ |
233235
| tst.js:55:20:59:1 | {\\n TRU ... BE: 0\\n} | tst.js:51:1:54:3 | /**\\n * ... er}\\n */ |
@@ -339,6 +341,7 @@ test_next_token
339341
| tst.js:368:3:370:5 | /**\\n ... p\\n */ | tst.js:371:3:371:13 | fancyMethod |
340342
| tst.js:375:3:377:5 | /**\\n ... p\\n */ | tst.js:378:3:378:13 | constructor |
341343
| tst.js:380:3:382:5 | /**\\n ... p\\n */ | tst.js:383:3:383:13 | classMethod |
344+
| tst.js:386:1:389:3 | /**\\n * ... } x\\n */ | tst.js:390:1:390:8 | function |
342345
test_JSDocTypeExpr
343346
| tst.js:10:11:10:16 | string | tst.js:10:4:10:8 | @type | 0 |
344347
| tst.js:23:14:23:20 | boolean | tst.js:23:5:23:11 | @define | 0 |
@@ -461,6 +464,9 @@ test_JSDocTypeExpr
461464
| tst.js:369:14:369:15 | T2 | tst.js:369:6:369:11 | @param | 0 |
462465
| tst.js:376:14:376:15 | T3 | tst.js:376:6:376:11 | @param | 0 |
463466
| tst.js:381:14:381:15 | T4 | tst.js:381:6:381:11 | @param | 0 |
467+
| tst.js:387:12:387:16 | Array | tst.js:387:12:388:13 | Array.<number> | -1 |
468+
| tst.js:387:12:388:13 | Array.<number> | tst.js:387:4:387:9 | @param | 0 |
469+
| tst.js:388:7:388:12 | number | tst.js:387:12:388:13 | Array.<number> | 0 |
464470
test_Function_getDocumentation
465471
| tst.js:20:1:21:1 | functio ... t() {\\n} | tst.js:16:1:19:3 | /**\\n * ... tor\\n */ |
466472
| tst.js:36:34:37:1 | function(node) {\\n} | tst.js:29:1:35:3 | /**\\n * ... ().\\n */ |
@@ -505,6 +511,7 @@ test_Function_getDocumentation
505511
| tst.js:371:14:371:19 | (p) {} | tst.js:368:3:370:5 | /**\\n ... p\\n */ |
506512
| tst.js:378:14:378:19 | (p) {} | tst.js:375:3:377:5 | /**\\n ... p\\n */ |
507513
| tst.js:383:14:383:19 | (p) {} | tst.js:380:3:382:5 | /**\\n ... p\\n */ |
514+
| tst.js:390:1:390:24 | functio ... e(x) {} | tst.js:386:1:389:3 | /**\\n * ... } x\\n */ |
508515
test_JSDocOptionalParameterTypeExpr
509516
| tst.js:239:12:239:18 | number= | tst.js:239:12:239:17 | number |
510517
| tst.js:240:21:240:28 | ?string= | tst.js:240:21:240:27 | ?string |
@@ -525,6 +532,7 @@ test_getParameterTag
525532
| tst.js:371:15:371:15 | p | p | tst.js:369:6:369:11 | @param | p | tst.js:369:14:369:15 | T2 |
526533
| tst.js:378:15:378:15 | p | p | tst.js:376:6:376:11 | @param | p | tst.js:376:14:376:15 | T3 |
527534
| tst.js:383:15:383:15 | p | p | tst.js:381:6:381:11 | @param | p | tst.js:381:14:381:15 | T4 |
535+
| tst.js:390:20:390:20 | x | x | tst.js:387:4:387:9 | @param | x | tst.js:387:12:388:13 | Array.<number> |
528536
test_VarDeclStmt_getDocumentation
529537
| tst.js:5:15:5:36 | var MY_ ... stout'; | tst.js:5:1:5:13 | /** @const */ |
530538
| tst.js:24:1:24:24 | var ENA ... = true; | tst.js:23:1:23:24 | /** @de ... ean} */ |
@@ -557,6 +565,7 @@ test_JSDocAppliedTypeExpr
557565
| tst.js:333:17:333:28 | Foo.<string> | tst.js:333:17:333:19 | Foo | 0 | tst.js:333:22:333:27 | string |
558566
| tst.js:334:17:334:28 | Foo.<number> | tst.js:334:17:334:19 | Foo | 0 | tst.js:334:22:334:27 | number |
559567
| tst.js:357:12:357:25 | Array.<number> | tst.js:357:12:357:16 | Array | 0 | tst.js:357:19:357:24 | number |
568+
| tst.js:387:12:388:13 | Array.<number> | tst.js:387:12:387:16 | Array | 0 | tst.js:388:7:388:12 | number |
560569
test_JSDocNonNullableTypeExpr
561570
| tst.js:229:12:229:18 | !Object | tst.js:229:13:229:18 | Object | prefix |
562571
| tst.js:258:12:258:24 | !Foo.<string> | tst.js:258:13:258:24 | Foo.<string> | prefix |

javascript/ql/test/library-tests/JSDoc/tst.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,9 @@ class C {
382382
*/
383383
classMethod(p) {}
384384
}
385+
386+
/**
387+
* @param {Array.<
388+
* number>} x
389+
*/
390+
function multiline(x) {}

0 commit comments

Comments
 (0)