From 3500c92a788bcb9775486814b890686c013a5a8c Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 2 Dec 2023 01:48:36 +0200 Subject: [PATCH] feat(56600): JSDoc @callback doesn't support this parameters via @this (#56610) --- src/compiler/checker.ts | 13 ++++++---- src/compiler/parser.ts | 8 +++--- .../baselines/reference/callbackTag4.symbols | 23 +++++++++++++++++ tests/baselines/reference/callbackTag4.types | 25 +++++++++++++++++++ tests/cases/conformance/jsdoc/callbackTag4.ts | 19 ++++++++++++++ 5 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/callbackTag4.symbols create mode 100644 tests/baselines/reference/callbackTag4.types create mode 100644 tests/cases/conformance/jsdoc/callbackTag4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d1de194a79c..9ee8fe65728 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -597,6 +597,7 @@ import { isJSDocSatisfiesTag, isJSDocSignature, isJSDocTemplateTag, + isJSDocThisTag, isJSDocTypeAlias, isJSDocTypeAssertion, isJSDocTypedefTag, @@ -15079,6 +15080,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let flags = SignatureFlags.None; let minArgumentCount = 0; let thisParameter: Symbol | undefined; + let thisTag: JSDocThisTag | undefined = isInJSFile(declaration) ? getJSDocThisTag(declaration) : undefined; let hasThisParameter = false; const iife = getImmediatelyInvokedFunctionExpression(declaration); const isJSConstructSignature = isJSDocConstructSignature(declaration); @@ -15096,6 +15098,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // signature. for (let i = isJSConstructSignature ? 1 : 0; i < declaration.parameters.length; i++) { const param = declaration.parameters[i]; + if (isInJSFile(param) && isJSDocThisTag(param)) { + thisTag = param; + continue; + } let paramSymbol = param.symbol; const type = isJSDocParameterTag(param) ? (param.typeExpression && param.typeExpression.type) : param.type; @@ -15139,11 +15145,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - if (isInJSFile(declaration)) { - const thisTag = getJSDocThisTag(declaration); - if (thisTag && thisTag.typeExpression) { - thisParameter = createSymbolWithType(createSymbol(SymbolFlags.FunctionScopedVariable, InternalSymbolName.This), getTypeFromTypeNode(thisTag.typeExpression)); - } + if (thisTag && thisTag.typeExpression) { + thisParameter = createSymbolWithType(createSymbol(SymbolFlags.FunctionScopedVariable, InternalSymbolName.This), getTypeFromTypeNode(thisTag.typeExpression)); } const hostDeclaration = isJSDocSignature(declaration) ? getEffectiveJSDocHost(declaration) : declaration; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index fcf95696eb9..9da128e705a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -9343,7 +9343,7 @@ namespace Parser { function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) { if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) { const pos = getNodePos(); - let child: JSDocPropertyLikeTag | JSDocTypeTag | JSDocTemplateTag | false; + let child: JSDocPropertyLikeTag | JSDocTypeTag | JSDocTemplateTag | JSDocThisTag | false; let children: JSDocPropertyLikeTag[] | undefined; while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) { if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) { @@ -9635,7 +9635,7 @@ namespace Parser { return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | JSDocTemplateTag | false; } - function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false { + function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | JSDocThisTag | false { let canParseTag = true; let seenAsterisk = false; while (true) { @@ -9672,7 +9672,7 @@ namespace Parser { } } - function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false { + function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | JSDocThisTag | false { Debug.assert(token() === SyntaxKind.AtToken); const start = scanner.getTokenFullStart(); nextTokenJSDoc(); @@ -9694,6 +9694,8 @@ namespace Parser { break; case "template": return parseTemplateTag(start, tagName, indent, indentText); + case "this": + return parseThisTag(start, tagName, indent, indentText); default: return false; } diff --git a/tests/baselines/reference/callbackTag4.symbols b/tests/baselines/reference/callbackTag4.symbols new file mode 100644 index 00000000000..17c0d29004c --- /dev/null +++ b/tests/baselines/reference/callbackTag4.symbols @@ -0,0 +1,23 @@ +//// [tests/cases/conformance/jsdoc/callbackTag4.ts] //// + +=== ./a.js === +/** + * @callback C + * @this {{ a: string, b: number }} + * @param {string} a + * @param {number} b + * @returns {boolean} + */ + +/** @type {C} */ +const cb = function (a, b) { +>cb : Symbol(cb, Decl(a.js, 9, 5)) +>a : Symbol(a, Decl(a.js, 9, 21)) +>b : Symbol(b, Decl(a.js, 9, 23)) + + this +>this : Symbol(this) + + return true +} + diff --git a/tests/baselines/reference/callbackTag4.types b/tests/baselines/reference/callbackTag4.types new file mode 100644 index 00000000000..1afbc752a07 --- /dev/null +++ b/tests/baselines/reference/callbackTag4.types @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/jsdoc/callbackTag4.ts] //// + +=== ./a.js === +/** + * @callback C + * @this {{ a: string, b: number }} + * @param {string} a + * @param {number} b + * @returns {boolean} + */ + +/** @type {C} */ +const cb = function (a, b) { +>cb : C +>function (a, b) { this return true} : (this: { a: string; b: number; }, a: string, b: number) => boolean +>a : string +>b : number + + this +>this : { a: string; b: number; } + + return true +>true : true +} + diff --git a/tests/cases/conformance/jsdoc/callbackTag4.ts b/tests/cases/conformance/jsdoc/callbackTag4.ts new file mode 100644 index 00000000000..a5ac356aedd --- /dev/null +++ b/tests/cases/conformance/jsdoc/callbackTag4.ts @@ -0,0 +1,19 @@ +// @allowJs: true +// @checkJs: true +// @strict: true +// @noEmit: true +// @filename: ./a.js + +/** + * @callback C + * @this {{ a: string, b: number }} + * @param {string} a + * @param {number} b + * @returns {boolean} + */ + +/** @type {C} */ +const cb = function (a, b) { + this + return true +}