diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d74f0b1df391c..b305458900de6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10510,7 +10510,7 @@ namespace ts { const signature = getSignatureFromDeclaration(node.parent); const parameterIndex = node.parent.parameters.indexOf(node); Debug.assert(parameterIndex >= 0); - return parameterIndex >= getMinArgumentCount(signature); + return parameterIndex >= getMinArgumentCount(signature, /*strongArityForUntypedJS*/ true); } const iife = getImmediatelyInvokedFunctionExpression(node.parent); if (iife) { @@ -10601,6 +10601,9 @@ namespace ts { isValueSignatureDeclaration(declaration) && !hasJSDocParameterTags(declaration) && !getJSDocType(declaration); + if (isUntypedSignatureInJSFile) { + flags |= SignatureFlags.IsUntypedSignatureInJSFile; + } // If this is a JSDoc construct signature, then skip the first parameter in the // parameter list. The first parameter represents the return type of the construct @@ -10631,7 +10634,6 @@ namespace ts { const isOptionalParameter = isOptionalJSDocParameterTag(param) || param.initializer || param.questionToken || param.dotDotDotToken || iife && parameters.length > iife.arguments.length && !type || - isUntypedSignatureInJSFile || isJSDocOptionalParameter(param); if (!isOptionalParameter) { minArgumentCount = parameters.length; @@ -21107,8 +21109,10 @@ namespace ts { if (isInJS && className) { const classSymbol = checkExpression(className).symbol; if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) { - const classType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType!; - return getFlowTypeOfReference(node, classType); + const classType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType; + if (classType) { + return getFlowTypeOfReference(node, classType); + } } } // Check if it's a constructor definition, can be either a variable decl or function decl @@ -26377,7 +26381,7 @@ namespace ts { return length; } - function getMinArgumentCount(signature: Signature) { + function getMinArgumentCount(signature: Signature, strongArityForUntypedJS?: boolean) { if (signatureHasRestParameter(signature)) { const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); if (isTupleType(restType)) { @@ -26387,6 +26391,9 @@ namespace ts { } } } + if (!strongArityForUntypedJS && signature.flags & SignatureFlags.IsUntypedSignatureInJSFile) { + return 0; + } return signature.minArgumentCount; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index adbe97212e2b0..356eb54165e2b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2273,7 +2273,7 @@ namespace ts { function emitPropertyAccessExpression(node: PropertyAccessExpression) { const expression = cast(emitExpression(node.expression), isExpression); - const token = getDotOrQuestionDotToken(node); + const token = node.questionDotToken || createNode(SyntaxKind.DotToken, node.expression.end, node.name.pos) as DotToken; const indentBeforeDot = needsIndentation(node, node.expression, token); const indentAfterDot = needsIndentation(node, token, node.name); @@ -2289,7 +2289,12 @@ namespace ts { writePunctuation("."); } - emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node); + if (node.questionDotToken) { + emit(token); + } + else { + emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node); + } increaseIndentIf(indentAfterDot, /*writeSpaceIfNotIndenting*/ false); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6202e5bb7eeac..557021b0e429b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4817,6 +4817,7 @@ namespace ts { HasLiteralTypes = 1 << 1, // Indicates signature is specialized IsInnerCallChain = 1 << 2, // Indicates signature comes from a CallChain nested in an outer OptionalChain IsOuterCallChain = 1 << 3, // Indicates signature comes from a CallChain that is the outermost chain of an optional expression + IsUntypedSignatureInJSFile = 1 << 4, // Indicates signature is from a js file and has no types // We do not propagate `IsInnerCallChain` to instantiated signatures, as that would result in us // attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c0b623da1f32c..648bed6f7f9db 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5052,10 +5052,6 @@ namespace ts { } } - export function getDotOrQuestionDotToken(node: PropertyAccessExpression) { - return node.questionDotToken || createNode(SyntaxKind.DotToken, node.expression.end, node.name.pos) as DotToken; - } - export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports { return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports; } diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 42e3e82540092..fa611bc9bde3e 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -459,7 +459,7 @@ namespace ts { if (node.flags & NodeFlags.OptionalChain) { return updatePropertyAccessChain(node, visitNode((node).expression, visitor, isExpression), - visitNode((node).questionDotToken, visitor, isToken), + visitNode((node).questionDotToken, tokenVisitor, isToken), visitNode((node).name, visitor, isIdentifier)); } return updatePropertyAccess(node, @@ -470,7 +470,7 @@ namespace ts { if (node.flags & NodeFlags.OptionalChain) { return updateElementAccessChain(node, visitNode((node).expression, visitor, isExpression), - visitNode((node).questionDotToken, visitor, isToken), + visitNode((node).questionDotToken, tokenVisitor, isToken), visitNode((node).argumentExpression, visitor, isExpression)); } return updateElementAccess(node, @@ -481,7 +481,7 @@ namespace ts { if (node.flags & NodeFlags.OptionalChain) { return updateCallChain(node, visitNode((node).expression, visitor, isExpression), - visitNode((node).questionDotToken, visitor, isToken), + visitNode((node).questionDotToken, tokenVisitor, isToken), nodesVisitor((node).typeArguments, visitor, isTypeNode), nodesVisitor((node).arguments, visitor, isExpression)); } @@ -527,7 +527,7 @@ namespace ts { nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), visitParameterList((node).parameters, visitor, context, nodesVisitor), visitNode((node).type, visitor, isTypeNode), - visitNode((node).equalsGreaterThanToken, visitor, isToken), + visitNode((node).equalsGreaterThanToken, tokenVisitor, isToken), visitFunctionBody((node).body, visitor, context)); case SyntaxKind.DeleteExpression: @@ -558,14 +558,14 @@ namespace ts { return updateBinary(node, visitNode((node).left, visitor, isExpression), visitNode((node).right, visitor, isExpression), - visitNode((node).operatorToken, visitor, isToken)); + visitNode((node).operatorToken, tokenVisitor, isToken)); case SyntaxKind.ConditionalExpression: return updateConditional(node, visitNode((node).condition, visitor, isExpression), - visitNode((node).questionToken, visitor, isToken), + visitNode((node).questionToken, tokenVisitor, isToken), visitNode((node).whenTrue, visitor, isExpression), - visitNode((node).colonToken, visitor, isToken), + visitNode((node).colonToken, tokenVisitor, isToken), visitNode((node).whenFalse, visitor, isExpression)); case SyntaxKind.TemplateExpression: @@ -659,7 +659,7 @@ namespace ts { case SyntaxKind.ForOfStatement: return updateForOf(node, - visitNode((node).awaitModifier, visitor, isToken), + visitNode((node).awaitModifier, tokenVisitor, isToken), visitNode((node).initializer, visitor, isForInitializer), visitNode((node).expression, visitor, isExpression), visitNode((node).statement, visitor, isStatement, liftToBlock)); diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 9331a87553d92..369f11bae282c 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -7020,6 +7020,12 @@ + + + + + + @@ -7047,11 +7053,20 @@ - + + + + + + + + + + - + - + @@ -9543,6 +9558,9 @@ + + + @@ -10869,6 +10887,12 @@ + + + + + + diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index dd6b2eddde18d..acd525bcb6be8 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -7008,6 +7008,12 @@ + + + + + + @@ -7035,11 +7041,20 @@ - + + + + + + + + + + - + - + @@ -9531,6 +9546,9 @@ + + + @@ -10857,6 +10875,12 @@ + + + + + + diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 49f75cf41ecb4..7d424506dc39e 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -966,7 +966,6 @@ namespace ts.textChanges { function createWriter(newLine: string): TextChangesWriter { let lastNonTriviaPosition = 0; - const writer = createTextWriter(newLine); const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => { if (node) { diff --git a/tests/baselines/reference/allowJsClassThisTypeCrash.symbols b/tests/baselines/reference/allowJsClassThisTypeCrash.symbols new file mode 100644 index 0000000000000..2f72ed43e2266 --- /dev/null +++ b/tests/baselines/reference/allowJsClassThisTypeCrash.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/app.js === +const f = function() {}; +>f : Symbol(f, Decl(app.js, 0, 5)) + +var g = f; +>g : Symbol(g, Decl(app.js, 1, 3)) +>f : Symbol(f, Decl(app.js, 0, 5)) + +g.prototype.m = function () { +>g.prototype : Symbol(g.m, Decl(app.js, 1, 10)) +>g : Symbol(g, Decl(app.js, 1, 3)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>m : Symbol(g.m, Decl(app.js, 1, 10)) + + this; +}; diff --git a/tests/baselines/reference/allowJsClassThisTypeCrash.types b/tests/baselines/reference/allowJsClassThisTypeCrash.types new file mode 100644 index 0000000000000..7c136950f3d69 --- /dev/null +++ b/tests/baselines/reference/allowJsClassThisTypeCrash.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/app.js === +const f = function() {}; +>f : () => void +>function() {} : () => void + +var g = f; +>g : () => void +>f : () => void + +g.prototype.m = function () { +>g.prototype.m = function () { this;} : () => void +>g.prototype.m : any +>g.prototype : any +>g : () => void +>prototype : any +>m : any +>function () { this;} : () => void + + this; +>this : any + +}; diff --git a/tests/baselines/reference/jsDeclarationsClassLeadingOptional.js b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.js new file mode 100644 index 0000000000000..a53737c1f0d29 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.js @@ -0,0 +1,27 @@ +//// [bar.js] +export class Z { + f(x = 1, y) { + return [x, y]; + } +} + +//// [bar.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Z = void 0; +var Z = /** @class */ (function () { + function Z() { + } + Z.prototype.f = function (x, y) { + if (x === void 0) { x = 1; } + return [x, y]; + }; + return Z; +}()); +exports.Z = Z; + + +//// [bar.d.ts] +export class Z { + f(x: number, y: any): any[]; +} diff --git a/tests/baselines/reference/jsDeclarationsClassLeadingOptional.symbols b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.symbols new file mode 100644 index 0000000000000..15087e370eb5c --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/jsdoc/declarations/bar.js === +export class Z { +>Z : Symbol(Z, Decl(bar.js, 0, 0)) + + f(x = 1, y) { +>f : Symbol(Z.f, Decl(bar.js, 0, 16)) +>x : Symbol(x, Decl(bar.js, 1, 6)) +>y : Symbol(y, Decl(bar.js, 1, 12)) + + return [x, y]; +>x : Symbol(x, Decl(bar.js, 1, 6)) +>y : Symbol(y, Decl(bar.js, 1, 12)) + } +} diff --git a/tests/baselines/reference/jsDeclarationsClassLeadingOptional.types b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.types new file mode 100644 index 0000000000000..2d4599e383648 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsClassLeadingOptional.types @@ -0,0 +1,16 @@ +=== tests/cases/conformance/jsdoc/declarations/bar.js === +export class Z { +>Z : Z + + f(x = 1, y) { +>f : (x: number, y: any) => any[] +>x : number +>1 : 1 +>y : any + + return [x, y]; +>[x, y] : any[] +>x : number +>y : any + } +} diff --git a/tests/baselines/reference/propertyAccessExpressionInnerComments.js b/tests/baselines/reference/propertyAccessExpressionInnerComments.js index 3348c0432194d..f8a57d1c0ac55 100644 --- a/tests/baselines/reference/propertyAccessExpressionInnerComments.js +++ b/tests/baselines/reference/propertyAccessExpressionInnerComments.js @@ -13,6 +13,23 @@ /*1*/Array // Single-line comment /*2*/./*3*/toString/*4*/ + +/* Existing issue: the "2" comments below are duplicated and "3"s are missing */ + +/*1*/Array/*2*/?./*3*/toString/*4*/ + +/*1*/Array +/*2*/?./*3*/ + // Single-line comment + toString/*4*/ + +/*1*/Array/*2*/?./*3*/ + // Single-line comment + toString/*4*/ + +/*1*/Array + // Single-line comment + /*2*/?./*3*/toString/*4*/ //// [propertyAccessExpressionInnerComments.js] @@ -27,3 +44,15 @@ /*1*/ Array // Single-line comment /*2*/ . /*3*/toString; /*4*/ +/* Existing issue: the "2" comments below are duplicated and "3"s are missing */ +/*1*/ Array /*2*/ === null || Array /*2*/ === void 0 ? void 0 : Array /*2*/.toString; /*4*/ +/*1*/ Array === null || Array === void 0 ? void 0 : Array +/*2*/ . +// Single-line comment +toString; /*4*/ +/*1*/ Array /*2*/ === null || Array /*2*/ === void 0 ? void 0 : Array /*2*/. +// Single-line comment +toString; /*4*/ +/*1*/ Array === null || Array === void 0 ? void 0 : Array +// Single-line comment +/*2*/ .toString; /*4*/ diff --git a/tests/baselines/reference/propertyAccessExpressionInnerComments.symbols b/tests/baselines/reference/propertyAccessExpressionInnerComments.symbols index 439195f985b72..6a0ae25059eac 100644 --- a/tests/baselines/reference/propertyAccessExpressionInnerComments.symbols +++ b/tests/baselines/reference/propertyAccessExpressionInnerComments.symbols @@ -29,3 +29,35 @@ /*2*/./*3*/toString/*4*/ >toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) +/* Existing issue: the "2" comments below are duplicated and "3"s are missing */ + +/*1*/Array/*2*/?./*3*/toString/*4*/ +>Array/*2*/?./*3*/toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) + +/*1*/Array +>Array/*2*/?./*3*/ // Single-line comment toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +/*2*/?./*3*/ + // Single-line comment + toString/*4*/ +>toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) + +/*1*/Array/*2*/?./*3*/ +>Array/*2*/?./*3*/ // Single-line comment toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + // Single-line comment + toString/*4*/ +>toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) + +/*1*/Array +>Array // Single-line comment /*2*/?./*3*/toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + // Single-line comment + /*2*/?./*3*/toString/*4*/ +>toString : Symbol(Function.toString, Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/propertyAccessExpressionInnerComments.types b/tests/baselines/reference/propertyAccessExpressionInnerComments.types index 63bd06c68d4a8..d9bc3c2fa498d 100644 --- a/tests/baselines/reference/propertyAccessExpressionInnerComments.types +++ b/tests/baselines/reference/propertyAccessExpressionInnerComments.types @@ -29,3 +29,35 @@ /*2*/./*3*/toString/*4*/ >toString : () => string +/* Existing issue: the "2" comments below are duplicated and "3"s are missing */ + +/*1*/Array/*2*/?./*3*/toString/*4*/ +>Array/*2*/?./*3*/toString : () => string +>Array : ArrayConstructor +>toString : () => string + +/*1*/Array +>Array/*2*/?./*3*/ // Single-line comment toString : () => string +>Array : ArrayConstructor + +/*2*/?./*3*/ + // Single-line comment + toString/*4*/ +>toString : () => string + +/*1*/Array/*2*/?./*3*/ +>Array/*2*/?./*3*/ // Single-line comment toString : () => string +>Array : ArrayConstructor + + // Single-line comment + toString/*4*/ +>toString : () => string + +/*1*/Array +>Array // Single-line comment /*2*/?./*3*/toString : () => string +>Array : ArrayConstructor + + // Single-line comment + /*2*/?./*3*/toString/*4*/ +>toString : () => string + diff --git a/tests/cases/compiler/allowJsClassThisTypeCrash.ts b/tests/cases/compiler/allowJsClassThisTypeCrash.ts new file mode 100644 index 0000000000000..bec30d8960168 --- /dev/null +++ b/tests/cases/compiler/allowJsClassThisTypeCrash.ts @@ -0,0 +1,10 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: app.js +const f = function() {}; +var g = f; +g.prototype.m = function () { + this; +}; \ No newline at end of file diff --git a/tests/cases/compiler/propertyAccessExpressionInnerComments.ts b/tests/cases/compiler/propertyAccessExpressionInnerComments.ts index f4efc986b3803..fff9401468a5b 100644 --- a/tests/cases/compiler/propertyAccessExpressionInnerComments.ts +++ b/tests/cases/compiler/propertyAccessExpressionInnerComments.ts @@ -12,3 +12,20 @@ /*1*/Array // Single-line comment /*2*/./*3*/toString/*4*/ + +/* Existing issue: the "2" comments below are duplicated and "3"s are missing */ + +/*1*/Array/*2*/?./*3*/toString/*4*/ + +/*1*/Array +/*2*/?./*3*/ + // Single-line comment + toString/*4*/ + +/*1*/Array/*2*/?./*3*/ + // Single-line comment + toString/*4*/ + +/*1*/Array + // Single-line comment + /*2*/?./*3*/toString/*4*/ diff --git a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassLeadingOptional.ts b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassLeadingOptional.ts new file mode 100644 index 0000000000000..b3b27f096e9a9 --- /dev/null +++ b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsClassLeadingOptional.ts @@ -0,0 +1,11 @@ +// @allowJs: true +// @checkJs: true +// @target: es5 +// @outDir: ./out +// @declaration: true +// @filename: bar.js +export class Z { + f(x = 1, y) { + return [x, y]; + } +} \ No newline at end of file diff --git a/tests/cases/fourslash/extract-const2.ts b/tests/cases/fourslash/extract-const2.ts new file mode 100644 index 0000000000000..00a9ca6ca52ba --- /dev/null +++ b/tests/cases/fourslash/extract-const2.ts @@ -0,0 +1,16 @@ +/// + +////function foo(bar: number) { +//// /*a*/bar.toString();/*b*/ +////} + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "constant_scope_0", + actionDescription: "Extract to constant in enclosing scope", + newContent: +`function foo(bar: number) { + const /*RENAME*/newLocal = bar.toString(); +}` +}); diff --git a/tests/cases/fourslash/extract-const3.ts b/tests/cases/fourslash/extract-const3.ts new file mode 100644 index 0000000000000..c4e79ccb746ca --- /dev/null +++ b/tests/cases/fourslash/extract-const3.ts @@ -0,0 +1,18 @@ +/// + +// GH#35372 + +////function foo(bar?: number) { +//// /*a*/bar?.toString();/*b*/ +////} + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "constant_scope_0", + actionDescription: "Extract to constant in enclosing scope", + newContent: +`function foo(bar?: number) { + const /*RENAME*/newLocal = bar?.toString(); +}` +});