Skip to content

Commit bd85412

Browse files
committed
Ensure that JSDoc parsing happens within a ParsingContext
1 parent a84f0b9 commit bd85412

File tree

5 files changed

+34
-47
lines changed

5 files changed

+34
-47
lines changed

src/compiler/parser.ts

+26-3
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,8 @@ namespace Parser {
14461446
let identifiers: Map<string, string>;
14471447
let identifierCount: number;
14481448

1449+
// TODO(jakebailey): This type is a lie; this value actually contains the result
1450+
// of ORing a bunch of `1 << ParsingContext.XYZ`.
14491451
let parsingContext: ParsingContext;
14501452

14511453
let notParenthesizedArrow: Set<number> | undefined;
@@ -2824,9 +2826,13 @@ namespace Parser {
28242826
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken;
28252827
case ParsingContext.JsxChildren:
28262828
return true;
2829+
case ParsingContext.StandaloneJSDoc:
2830+
return true;
2831+
case ParsingContext.Count:
2832+
return Debug.fail("ParsingContext.Count used as a context"); // Not a real context, only a marker.
2833+
default:
2834+
Debug.assertNever(parsingContext, "Non-exhaustive case in 'isListElement'.");
28272835
}
2828-
2829-
return Debug.fail("Non-exhaustive case in 'isListElement'.");
28302836
}
28312837

28322838
function isValidHeritageClauseObjectLiteral() {
@@ -2962,6 +2968,9 @@ namespace Parser {
29622968

29632969
// True if positioned at element or terminator of the current list or any enclosing list
29642970
function isInSomeParsingContext(): boolean {
2971+
// We should be in at least one parsing context, be it SourceElements while parsing
2972+
// a SourceFile, or StandaloneJSDoc when lazily parsing JSDoc.
2973+
Debug.assert(parsingContext, "Missing parsing context");
29652974
for (let kind = 0; kind < ParsingContext.Count; kind++) {
29662975
if (parsingContext & (1 << kind)) {
29672976
if (isListElement(kind, /*inErrorRecovery*/ true) || isListTerminator(kind)) {
@@ -3078,6 +3087,7 @@ namespace Parser {
30783087
case ParsingContext.VariableDeclarations:
30793088
case ParsingContext.JSDocParameters:
30803089
case ParsingContext.Parameters:
3090+
case ParsingContext.StandaloneJSDoc: // TODO(jakebailey): is it?
30813091
return true;
30823092
}
30833093
return false;
@@ -3337,6 +3347,7 @@ namespace Parser {
33373347
case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
33383348
case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
33393349
case ParsingContext.AssertEntries: return parseErrorAtCurrentToken(Diagnostics.Identifier_or_string_literal_expected); // AssertionKey.
3350+
case ParsingContext.StandaloneJSDoc: return; // TODO(jakebailey): any error here?
33403351
case ParsingContext.Count: return Debug.fail("ParsingContext.Count used as a context"); // Not a real context, only a marker.
33413352
default: Debug.assertNever(context);
33423353
}
@@ -7210,6 +7221,8 @@ namespace Parser {
72107221

72117222
function tryReuseAmbientDeclaration(pos: number): Statement | undefined {
72127223
return doInsideOfContext(NodeFlags.Ambient, () => {
7224+
// TODO(jakebailey): this is totally wrong; `parsingContext` is the result of ORing a bunch of `1 << ParsingContext.XYZ`.
7225+
// The enum should really be a bunch of flags.
72137226
const node = currentNode(parsingContext, pos);
72147227
if (node) {
72157228
return consumeNode(node) as Statement;
@@ -8397,7 +8410,8 @@ namespace Parser {
83978410
TupleElementTypes, // Element types in tuple element type list
83988411
HeritageClauses, // Heritage clauses for a class or interface declaration.
83998412
ImportOrExportSpecifiers, // Named import clause's import specifier list,
8400-
AssertEntries, // Import entries list.
8413+
AssertEntries, // Import entries list.
8414+
StandaloneJSDoc, // TODO(jakebailey): describe
84018415
Count // Number of parsing contexts
84028416
}
84038417

@@ -8503,6 +8517,15 @@ namespace Parser {
85038517
}
85048518

85058519
function parseJSDocCommentWorker(start = 0, length: number | undefined): JSDoc | undefined {
8520+
const saveParsingContext = parsingContext;
8521+
parsingContext |= 1 << ParsingContext.StandaloneJSDoc;
8522+
const jsdoc = parseJSDocCommentWorkerWorker(start, length);
8523+
parsingContext = saveParsingContext;
8524+
return jsdoc;
8525+
}
8526+
8527+
// TODO(jakebailey): name
8528+
function parseJSDocCommentWorkerWorker(start: number, length: number | undefined): JSDoc | undefined {
85068529
const content = sourceText;
85078530
const end = length === undefined ? content.length : start + length;
85088531
length = end - start;

tests/baselines/reference/jsDocDontBreakWithNamespaces.baseline

+1-25
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
// zee('');
2626
// ^
2727
// | ----------------------------------------------------------------------
28-
// | zee(**arg0: any**, arg1: any, arg2: any): any
28+
// | zee(**arg0: any**, arg1: any): any
2929
// | ----------------------------------------------------------------------
3030

3131
[
@@ -259,30 +259,6 @@
259259
],
260260
"isOptional": false,
261261
"isRest": false
262-
},
263-
{
264-
"name": "arg2",
265-
"documentation": [],
266-
"displayParts": [
267-
{
268-
"text": "arg2",
269-
"kind": "parameterName"
270-
},
271-
{
272-
"text": ":",
273-
"kind": "punctuation"
274-
},
275-
{
276-
"text": " ",
277-
"kind": "space"
278-
},
279-
{
280-
"text": "any",
281-
"kind": "keyword"
282-
}
283-
],
284-
"isOptional": false,
285-
"isRest": false
286262
}
287263
],
288264
"documentation": [],
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
1+
/a.js(1,13): error TS1098: Type parameter list cannot be empty.
12
/a.js(1,14): error TS1139: Type parameter declaration expected.
2-
/a.js(1,17): error TS1003: Identifier expected.
3-
/a.js(1,17): error TS1110: Type expected.
4-
/a.js(1,17): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
5-
/a.js(1,18): error TS1005: '>' expected.
6-
/a.js(1,18): error TS1005: '}' expected.
73

84

9-
==== /a.js (6 errors) ====
5+
==== /a.js (2 errors) ====
106
/** @param {<} x */
7+
~~
8+
!!! error TS1098: Type parameter list cannot be empty.
119
~
1210
!!! error TS1139: Type parameter declaration expected.
13-
14-
!!! error TS1003: Identifier expected.
15-
16-
!!! error TS1110: Type expected.
17-
18-
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
19-
20-
!!! error TS1005: '>' expected.
21-
22-
!!! error TS1005: '}' expected.
2311
function f(x) {}
2412

Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== /a.js ===
22
/** @param {<} x */
33
function f(x) {}
4-
>f : (x: any) => void
5-
>x : any
4+
>f : (x: () => any) => void
5+
>x : () => any
66

tests/cases/fourslash/badJSDocNoCrash.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
//// constructor(options = {}) {}
99
//// }
1010

11-
verify.encodedSyntacticClassificationsLength(84);
11+
verify.encodedSyntacticClassificationsLength(69);

0 commit comments

Comments
 (0)