From 350f62ff292a581dc04f2397df4b2238eeb851a9 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 20 Apr 2016 16:17:29 -0700 Subject: [PATCH 1/2] treat TS only keywords as identifiers in jsdoc comments --- src/compiler/parser.ts | 4 +-- src/compiler/utilities.ts | 29 ++++++++++++++++--- .../server/jsdocParamTagSpecialKeywords.ts | 14 +++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/server/jsdocParamTagSpecialKeywords.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 38a341eaa5a..7ceb09730ff 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6160,7 +6160,7 @@ namespace ts { parseExpected(SyntaxKind.CloseBracketToken); } - else if (token === SyntaxKind.Identifier) { + else if (token === SyntaxKind.Identifier || isTSOnlyKeyword(token)) { name = parseJSDocIdentifier(); } @@ -6259,7 +6259,7 @@ namespace ts { } function parseJSDocIdentifier(): Identifier { - if (token !== SyntaxKind.Identifier) { + if (token !== SyntaxKind.Identifier && !isTSOnlyKeyword(token)) { parseErrorAtCurrentToken(Diagnostics.Identifier_expected); return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b97ad70a74a..eb5c62844c4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -435,7 +435,7 @@ namespace ts { const { line: startLine } = getLineAndCharacterOfPosition(sourceFile, node.body.pos); const { line: endLine } = getLineAndCharacterOfPosition(sourceFile, node.body.end); if (startLine < endLine) { - // The arrow function spans multiple lines, + // The arrow function spans multiple lines, // make the error span be the first line, inclusive. return createTextSpan(pos, getEndLinePosition(startLine, sourceFile) - pos + 1); } @@ -1301,10 +1301,10 @@ namespace ts { if (node.jsDocComment) { return node.jsDocComment; } - // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. - // /** + // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. + // /** // * @param {number} name - // * @returns {number} + // * @returns {number} // */ // var x = function(name) { return name.length; } if (checkParentVariableStatement) { @@ -1667,6 +1667,27 @@ namespace ts { return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword; } + // Some keywords are TypeScript only, so they should not be parsed as considered as + // keywords in JavaScript + export function isJSKeyword(token: SyntaxKind): boolean { + switch (token) { + case SyntaxKind.TypeKeyword: + case SyntaxKind.AsKeyword: + case SyntaxKind.AnyKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.IsKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.FromKeyword: + return false; + default: + return isKeyword(token); + } + } + + export function isTSOnlyKeyword(token: SyntaxKind): boolean { + return isKeyword(token) && !isJSKeyword(token); + } + export function isTrivia(token: SyntaxKind) { return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; } diff --git a/tests/cases/fourslash/server/jsdocParamTagSpecialKeywords.ts b/tests/cases/fourslash/server/jsdocParamTagSpecialKeywords.ts new file mode 100644 index 00000000000..be7ca5f0dff --- /dev/null +++ b/tests/cases/fourslash/server/jsdocParamTagSpecialKeywords.ts @@ -0,0 +1,14 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: test.js +//// /** +//// * @param {string} type +//// */ +//// function test(type) { +//// type./**/ +//// } + + +goTo.marker(); +verify.completionListContains("charAt"); \ No newline at end of file From 46f4bd4215412bb9f01e3baf09d960e2ed9c6f6a Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 21 Apr 2016 13:38:47 -0700 Subject: [PATCH 2/2] Allow keywords in jsdoc comments parsing --- src/compiler/parser.ts | 24 ++++++++++++++---------- src/compiler/utilities.ts | 21 --------------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7ceb09730ff..c6dd6a769eb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -886,7 +886,7 @@ namespace ts { /** Invokes the provided callback then unconditionally restores the parser to the state it * was in immediately prior to invoking the callback. The result of invoking the callback - * is returned from this function. + * is returned from this function. */ function lookAhead(callback: () => T): T { return speculationHelper(callback, /*isLookAhead*/ true); @@ -4988,7 +4988,7 @@ namespace ts { if (token === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { // We need to ensure that any subsequent modifiers appear on the same line - // so that when 'const' is a standalone declaration, we don't issue an error. + // so that when 'const' is a standalone declaration, we don't issue an error. if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { break; } @@ -5251,7 +5251,7 @@ namespace ts { node.decorators = decorators; setModifiers(node, modifiers); if (token === SyntaxKind.GlobalKeyword) { - // parse 'global' as name of global scope augmentation + // parse 'global' as name of global scope augmentation node.name = parseIdentifier(); node.flags |= NodeFlags.GlobalAugmentation; } @@ -6087,7 +6087,7 @@ namespace ts { atToken.end = scanner.getTextPos(); nextJSDocToken(); - const tagName = parseJSDocIdentifier(); + const tagName = parseJSDocIdentifierName(); if (!tagName) { return; } @@ -6150,7 +6150,7 @@ namespace ts { let isBracketed: boolean; // Looking for something like '[foo]' or 'foo' if (parseOptionalToken(SyntaxKind.OpenBracketToken)) { - name = parseJSDocIdentifier(); + name = parseJSDocIdentifierName(); isBracketed = true; // May have an optional default, e.g. '[foo = 42]' @@ -6160,8 +6160,8 @@ namespace ts { parseExpected(SyntaxKind.CloseBracketToken); } - else if (token === SyntaxKind.Identifier || isTSOnlyKeyword(token)) { - name = parseJSDocIdentifier(); + else if (tokenIsIdentifierOrKeyword(token)) { + name = parseJSDocIdentifierName(); } if (!name) { @@ -6225,7 +6225,7 @@ namespace ts { typeParameters.pos = scanner.getStartPos(); while (true) { - const name = parseJSDocIdentifier(); + const name = parseJSDocIdentifierName(); if (!name) { parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); return undefined; @@ -6258,8 +6258,12 @@ namespace ts { return token = scanner.scanJSDocToken(); } - function parseJSDocIdentifier(): Identifier { - if (token !== SyntaxKind.Identifier && !isTSOnlyKeyword(token)) { + function parseJSDocIdentifierName(): Identifier { + return createJSDocIdentifier(tokenIsIdentifierOrKeyword(token)); + } + + function createJSDocIdentifier(isIdentifier: boolean): Identifier { + if (!isIdentifier) { parseErrorAtCurrentToken(Diagnostics.Identifier_expected); return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index eb5c62844c4..db906c57067 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1667,27 +1667,6 @@ namespace ts { return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword; } - // Some keywords are TypeScript only, so they should not be parsed as considered as - // keywords in JavaScript - export function isJSKeyword(token: SyntaxKind): boolean { - switch (token) { - case SyntaxKind.TypeKeyword: - case SyntaxKind.AsKeyword: - case SyntaxKind.AnyKeyword: - case SyntaxKind.DeclareKeyword: - case SyntaxKind.IsKeyword: - case SyntaxKind.ReadonlyKeyword: - case SyntaxKind.FromKeyword: - return false; - default: - return isKeyword(token); - } - } - - export function isTSOnlyKeyword(token: SyntaxKind): boolean { - return isKeyword(token) && !isJSKeyword(token); - } - export function isTrivia(token: SyntaxKind) { return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; }