From 514bff0c7df60ea1547b8f57f8637e2ee0e52daa Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 1 Feb 2022 15:38:48 -0800 Subject: [PATCH] Add undefined to parameter's name type Only parameters to JSDocFunctionTypes may not have names, but these parameters cause crashes from time to time, most recently #47606. This PR is NOT in a mergeable state -- having fixed all the compile errors, I don't actually think it's worthwhile. A lot of the difficulty comes from deciding whether to handle an undefined name instead of just asserting that it's defined. Any piece of code that only handles function *values* doesn't need to handle undefined parameters. However, it's difficult to know when this is the case. --- src/compiler/checker.ts | 38 +++++++++---------- src/compiler/emitter.ts | 2 +- src/compiler/factory/nodeFactory.ts | 4 +- src/compiler/parser.ts | 2 +- src/compiler/transformers/declarations.ts | 7 ++-- src/compiler/transformers/es2015.ts | 2 + src/compiler/transformers/es2017.ts | 1 + src/compiler/transformers/ts.ts | 14 ++++--- src/compiler/types.ts | 6 +-- src/compiler/utilities.ts | 4 +- src/compiler/visitorPublic.ts | 4 +- .../codefixes/addOptionalPropertyUndefined.ts | 2 +- .../codefixes/convertToAsyncFunction.ts | 1 + src/services/codefixes/fixAddMissingMember.ts | 2 +- src/services/codefixes/fixUnusedIdentifier.ts | 4 +- src/services/codefixes/helpers.ts | 2 +- src/services/codefixes/inferFromUsage.ts | 9 +++-- src/services/inlayHints.ts | 2 +- src/services/jsDoc.ts | 2 + .../convertOverloadListToSingleSignature.ts | 4 +- .../convertParamsToDestructuredObject.ts | 8 ++-- src/services/refactors/extractSymbol.ts | 2 +- src/services/textChanges.ts | 2 + 23 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 82b2880ba14..534e85a1fd5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1739,7 +1739,7 @@ namespace ts { return false; function requiresScopeChange(node: ParameterDeclaration): boolean { - return requiresScopeChangeWorker(node.name) + return !!node.name && requiresScopeChangeWorker(node.name) || !!node.initializer && requiresScopeChangeWorker(node.initializer); } @@ -21293,7 +21293,7 @@ namespace ts { break; case SyntaxKind.Parameter: const param = declaration as ParameterDeclaration; - if (isIdentifier(param.name) && + if (param.name && isIdentifier(param.name) && (isCallSignatureDeclaration(param.parent) || isMethodSignature(param.parent) || isFunctionTypeNode(param.parent)) && param.parent.parameters.indexOf(param) > -1 && (resolveName(param, param.name.escapedText, SymbolFlags.Type, undefined, param.name.escapedText, /*isUse*/ true) || @@ -25990,7 +25990,7 @@ namespace ts { const parentType = getContextualTypeForVariableLikeDeclaration(parent) || parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent); if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined; - if (parent.name.kind === SyntaxKind.ArrayBindingPattern) { + if (parent.name?.kind === SyntaxKind.ArrayBindingPattern) { const index = indexOfNode(declaration.parent.elements, declaration); if (index < 0) return undefined; return getContextualTypeForElementExpression(parentType, index); @@ -31488,7 +31488,7 @@ namespace ts { } function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember) { - Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names + Debug.assert(d.name && isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names return d.name.escapedText; } @@ -31541,10 +31541,10 @@ namespace ts { } function isParameterDeclarationWithIdentifierName(symbol: Symbol) { - return symbol.valueDeclaration && isParameter(symbol.valueDeclaration) && isIdentifier(symbol.valueDeclaration.name); + return symbol.valueDeclaration && isParameter(symbol.valueDeclaration) && symbol.valueDeclaration.name && isIdentifier(symbol.valueDeclaration.name); } function isValidDeclarationForTupleLabel(d: Declaration): d is NamedTupleMember | (ParameterDeclaration & { name: Identifier }) { - return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && d.name && isIdentifier(d.name)); + return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && !!d.name && isIdentifier(d.name)); } function getNameableDeclarationAtPosition(signature: Signature, pos: number) { @@ -31771,7 +31771,7 @@ namespace ts { if (!links.type) { const declaration = parameter.valueDeclaration as ParameterDeclaration; links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true); - if (declaration.name.kind !== SyntaxKind.Identifier) { + if (declaration.name && declaration.name.kind !== SyntaxKind.Identifier) { // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. if (links.type === unknownType) { links.type = getTypeFromBindingPattern(declaration.name); @@ -33717,7 +33717,7 @@ namespace ts { const initializer = getEffectiveInitializer(declaration)!; const type = getQuickTypeOfExpression(initializer) || (contextualType ? checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, CheckMode.Normal) : checkExpressionCached(initializer)); - return isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern && + return isParameter(declaration) && declaration.name?.kind === SyntaxKind.ArrayBindingPattern && isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ? padTupleType(type, declaration.name) : type; } @@ -34254,7 +34254,7 @@ namespace ts { if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) { error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); } - if (func.kind === SyntaxKind.Constructor && isIdentifier(node.name) && node.name.escapedText === "constructor") { + if (func.kind === SyntaxKind.Constructor && node.name && isIdentifier(node.name) && node.name.escapedText === "constructor") { error(node.name, Diagnostics.constructor_cannot_be_used_as_a_parameter_property_name); } } @@ -36436,7 +36436,7 @@ namespace ts { case SyntaxKind.Constructor: for (const parameter of (member as ConstructorDeclaration).parameters) { if (!parameter.symbol.isReferenced && hasSyntacticModifier(parameter, ModifierFlags.Private)) { - addDiagnostic(parameter, UnusedKind.Local, createDiagnosticForNode(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol))); + addDiagnostic(parameter, UnusedKind.Local, createDiagnosticForNode(parameter.name!, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol))); } } break; @@ -42937,17 +42937,17 @@ namespace ts { } if (parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.A_rest_parameter_cannot_have_an_initializer); } } else if (isOptionalParameter(parameter)) { seenOptionalParameter = true; if (parameter.questionToken && parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); } } else if (seenOptionalParameter && !parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); } } } @@ -43017,7 +43017,7 @@ namespace ts { const parameter = node.parameters[0]; if (node.parameters.length !== 1) { if (parameter) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_must_have_exactly_one_parameter); } else { return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter); @@ -43028,23 +43028,23 @@ namespace ts { return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); } if (hasEffectiveModifiers(parameter)) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); } if (parameter.questionToken) { return grammarErrorOnNode(parameter.questionToken, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark); } if (parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer); } if (!parameter.type) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation); } const type = getTypeFromTypeNode(parameter.type); if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericType(type)) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead); } if (!everyType(type, isValidIndexKeyType)) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_number_symbol_or_a_template_literal_type); + return grammarErrorOnNode(parameter.name || parameter, Diagnostics.An_index_signature_parameter_type_must_be_string_number_symbol_or_a_template_literal_type); } if (!node.type) { return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index cbc1d218dc6..7d28eb26ef6 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4264,7 +4264,7 @@ namespace ts { && !parameter.questionToken // parameter may not be optional && !parameter.type // parameter may not have a type annotation && !parameter.initializer // parameter may not have an initializer - && isIdentifier(parameter.name); // parameter name must be identifier + && isIdentifier(parameter.name!); // parameter name must be identifier } function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index bde236a32b0..d6daa7060aa 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -1151,7 +1151,7 @@ namespace ts { decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, - name: string | BindingName, + name: string | BindingName | undefined, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression @@ -1186,7 +1186,7 @@ namespace ts { decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, - name: string | BindingName, + name: string | BindingName | undefined, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c796e78d451..7a067778319 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3031,7 +3031,7 @@ namespace ts { /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, // TODO(rbuckton): JSDoc parameters don't have names (except `this`/`new`), should we manufacture an empty identifier? - name!, + name, /*questionToken*/ undefined, parseJSDocType(), /*initializer*/ undefined diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index b17d861cc51..324631629e5 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -447,8 +447,8 @@ namespace ts { return ret; } - function filterBindingPatternInitializers(name: BindingName) { - if (name.kind === SyntaxKind.Identifier) { + function filterBindingPatternInitializers(name: BindingName | undefined) { + if (!name || name.kind === SyntaxKind.Identifier) { return name; } else { @@ -465,7 +465,7 @@ namespace ts { if (elem.kind === SyntaxKind.OmittedExpression) { return elem; } - return factory.updateBindingElement(elem, elem.dotDotDotToken, elem.propertyName, filterBindingPatternInitializers(elem.name), shouldPrintWithInitializer(elem) ? elem.initializer : undefined); + return factory.updateBindingElement(elem, elem.dotDotDotToken, elem.propertyName, filterBindingPatternInitializers(elem.name)!, shouldPrintWithInitializer(elem) ? elem.initializer : undefined); } } @@ -1371,6 +1371,7 @@ namespace ts { const oldDiag = getSymbolAccessibilityDiagnostic; parameterProperties = compact(flatMap(ctor.parameters, (param) => { if (!hasSyntacticModifier(param, ModifierFlags.ParameterPropertyModifier) || shouldStripInternal(param)) return; + Debug.type(param.name); getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param); if (param.name.kind === SyntaxKind.Identifier) { return preserveJsDoc(factory.createPropertyDeclaration( diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 36a0f29d109..8bf9cce7079 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -1310,6 +1310,7 @@ namespace ts { added = insertDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer) || added; } else if (initializer) { + Debug.type(name); insertDefaultValueAssignmentForInitializer(statements, parameter, name, initializer); added = true; } @@ -1439,6 +1440,7 @@ namespace ts { // `declarationName` is the name of the local declaration for the parameter. // TODO(rbuckton): Does this need to be parented? + Debug.type(parameter.name); const declarationName = parameter.name.kind === SyntaxKind.Identifier ? setParent(setTextRange(factory.cloneNode(parameter.name), parameter.name), parameter.name.parent) : factory.createTempVariable(/*recordTempVariable*/ undefined); setEmitFlags(declarationName, EmitFlags.NoSourceMap); diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 50c4fa62c0d..a8cc41efc71 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -373,6 +373,7 @@ namespace ts { } function recordDeclarationName({ name }: ParameterDeclaration | VariableDeclaration | BindingElement, names: Set<__String>) { + Debug.type(name); if (isIdentifier(name)) { names.add(name.escapedText); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index f8cec499a8f..c55e2cc93c3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -891,6 +891,7 @@ namespace ts { if (parametersWithPropertyAssignments) { for (const parameter of parametersWithPropertyAssignments) { + Debug.type(parameter.name); if (isIdentifier(parameter.name)) { members.push(setOriginalNode(factory.createPropertyDeclaration( /*decorators*/ undefined, @@ -1414,11 +1415,9 @@ namespace ts { */ function serializeParameterTypesOfNode(node: Node, container: ClassLikeDeclaration): ArrayLiteralExpression { const valueDeclaration = - isClassLike(node) - ? getFirstConstructorWithBody(node) - : isFunctionLike(node) && nodeIsPresent((node as FunctionLikeDeclaration).body) - ? node - : undefined; + isClassLike(node) ? getFirstConstructorWithBody(node) + : isFunctionLike(node) && nodeIsPresent((node as FunctionLikeDeclaration).body) ? node + : undefined; const expressions: SerializedTypeNode[] = []; if (valueDeclaration) { @@ -1426,6 +1425,7 @@ namespace ts { const numParameters = parameters.length; for (let i = 0; i < numParameters; i++) { const parameter = parameters[i]; + Debug.type(parameter.name); if (i === 0 && isIdentifier(parameter.name) && parameter.name.escapedText === "this") { continue; } @@ -2176,7 +2176,9 @@ namespace ts { setCommentRange(updated, node); setTextRange(updated, moveRangePastModifiers(node)); setSourceMapRange(updated, moveRangePastModifiers(node)); - setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap); + if (updated.name) { + setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap); + } } return updated; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8a675112088..56b7382ac1a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1304,7 +1304,7 @@ namespace ts { readonly kind: SyntaxKind.Parameter; readonly parent: SignatureDeclaration; readonly dotDotDotToken?: DotDotDotToken; // Present on rest parameter - readonly name: BindingName; // Declared parameter name. + readonly name?: BindingName; // Declared parameter name. readonly questionToken?: QuestionToken; // Present on optional parameter readonly type?: TypeNode; // Optional type annotation readonly initializer?: Expression; // Optional initializer @@ -7188,8 +7188,8 @@ namespace ts { createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode): TypeParameterDeclaration; updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined): TypeParameterDeclaration; - createParameterDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration; - updateParameterDeclaration(node: ParameterDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration; + createParameterDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName | undefined, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration; + updateParameterDeclaration(node: ParameterDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName | undefined, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration; createDecorator(expression: Expression): Decorator; updateDecorator(node: Decorator, expression: Expression): Decorator; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ab7d0e6375e..42b8ae7dd3e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2206,7 +2206,7 @@ namespace ts { /** Get the declaration initializer when it is container-like (See getExpandoInitializer). */ export function getDeclaredExpandoInitializer(node: HasExpressionInitializer) { const init = getEffectiveInitializer(node); - return init && getExpandoInitializer(init, isPrototypeAccess(node.name)); + return init && node.name && getExpandoInitializer(init, isPrototypeAccess(node.name)); } function hasExpandoValueProperty(node: ObjectLiteralExpression, isPrototypeAssignment: boolean) { @@ -2780,7 +2780,7 @@ namespace ts { if (!decl) { return undefined; } - const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); + const parameter = find(decl.parameters, p => !!p.name && p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); return parameter && parameter.symbol; } diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 6ea70250317..7b29d62ceca 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -201,12 +201,13 @@ namespace ts { // so let's just ignore it. return parameter.dotDotDotToken ? parameter : isBindingPattern(parameter.name) ? addDefaultValueAssignmentForBindingPattern(parameter, context) : - parameter.initializer ? addDefaultValueAssignmentForInitializer(parameter, parameter.name, parameter.initializer, context) : + parameter.initializer && parameter.name ? addDefaultValueAssignmentForInitializer(parameter, parameter.name, parameter.initializer, context) : parameter; } function addDefaultValueAssignmentForBindingPattern(parameter: ParameterDeclaration, context: TransformationContext) { const { factory } = context; + Debug.type(parameter.name); context.addInitializationStatement( factory.createVariableStatement( /*modifiers*/ undefined, @@ -391,6 +392,7 @@ namespace ts { case SyntaxKind.Parameter: Debug.type(node); + Debug.type(node.name); return factory.updateParameterDeclaration(node, nodesVisitor(node.decorators, visitor, isDecorator), nodesVisitor(node.modifiers, visitor, isModifier), diff --git a/src/services/codefixes/addOptionalPropertyUndefined.ts b/src/services/codefixes/addOptionalPropertyUndefined.ts index 13b80ed8a11..7b2e879ae79 100644 --- a/src/services/codefixes/addOptionalPropertyUndefined.ts +++ b/src/services/codefixes/addOptionalPropertyUndefined.ts @@ -64,7 +64,7 @@ namespace ts.codefix { const i = errorNode.parent.arguments.indexOf(errorNode); if (i === -1) return undefined; const name = (n.valueDeclaration as any as SignatureDeclaration).parameters[i].name; - if (isIdentifier(name)) return { source: errorNode, target: name }; + if (name && isIdentifier(name)) return { source: errorNode, target: name }; } else if (isPropertyAssignment(errorNode.parent) && isIdentifier(errorNode.parent.name) || isShorthandPropertyAssignment(errorNode.parent)) { diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index 3c6045fdb6c..f4c8fe875c9 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -746,6 +746,7 @@ namespace ts.codefix { if (isFunctionLikeDeclaration(funcNode)) { if (funcNode.parameters.length > 0) { const param = funcNode.parameters[0].name; + Debug.type(param); name = getMappedBindingNameOrDefault(param); } } diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 0eb72741b43..f0f9742ca82 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -167,7 +167,7 @@ namespace ts.codefix { if (!(signature && signature.declaration && signature.parameters[argIndex])) return undefined; const param = signature.parameters[argIndex].valueDeclaration; - if (!(param && isParameter(param) && isIdentifier(param.name))) return undefined; + if (!(param && isParameter(param) && param.name && isIdentifier(param.name))) return undefined; const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent), checker.getTypeAtLocation(param), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false)); if (!length(properties)) return undefined; diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index f11c6bce72f..299b79f3282 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -255,7 +255,7 @@ namespace ts.codefix { isFixAll = false): void { if (mayDeleteParameter(checker, sourceFile, parameter, sourceFiles, program, cancellationToken, isFixAll)) { if (parameter.modifiers && parameter.modifiers.length > 0 && - (!isIdentifier(parameter.name) || FindAllReferences.Core.isSymbolReferencedInFile(parameter.name, checker, sourceFile))) { + (!parameter.name || !isIdentifier(parameter.name) || FindAllReferences.Core.isSymbolReferencedInFile(parameter.name, checker, sourceFile))) { parameter.modifiers.forEach(modifier => changes.deleteModifier(sourceFile, modifier)); } else if (!parameter.initializer && isNotProvidedArguments(parameter, checker, sourceFiles)) { @@ -331,7 +331,7 @@ namespace ts.codefix { const index = parameters.indexOf(parameter); Debug.assert(index !== -1, "The parameter should already be in the list"); return isFixAll ? - parameters.slice(index + 1).every(p => isIdentifier(p.name) && !p.symbol.isReferenced) : + parameters.slice(index + 1).every(p => p.name && isIdentifier(p.name) && !p.symbol.isReferenced) : index === parameters.length - 1; } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 21981c8df5f..b88edaf0dc7 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -121,7 +121,7 @@ namespace ts.codefix { else { Debug.assertNode(accessor, isSetAccessorDeclaration, "The counterpart to a getter should be a setter"); const parameter = getSetAccessorValueParameter(accessor); - const parameterName = parameter && isIdentifier(parameter.name) ? idText(parameter.name) : undefined; + const parameterName = parameter?.name && isIdentifier(parameter.name) ? idText(parameter.name) : undefined; addClassElement(factory.createSetAccessorDeclaration( /*decorators*/ undefined, modifiers, diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 643d64f2c21..b34c89decfa 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -229,6 +229,7 @@ namespace ts.codefix { host: LanguageServiceHost, cancellationToken: CancellationToken, ): void { + Debug.type(parameterDeclaration.name); if (!isIdentifier(parameterDeclaration.name)) { return; } @@ -287,7 +288,7 @@ namespace ts.codefix { ): void { const param = firstOrUndefined(setAccessorDeclaration.parameters); - if (param && isIdentifier(setAccessorDeclaration.name) && isIdentifier(param.name)) { + if (param && isIdentifier(setAccessorDeclaration.name) && param.name && isIdentifier(param.name)) { let type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken); if (type === program.getTypeChecker().getAnyType()) { type = inferTypeForVariableFromUsage(param.name, program, cancellationToken); @@ -344,7 +345,7 @@ namespace ts.codefix { const inferences = mapDefined(parameterInferences, inference => { const param = inference.declaration; // only infer parameters that have (1) no type and (2) an accessible inferred type - if (param.initializer || getJSDocType(param) || !isIdentifier(param.name)) { + if (param.initializer || getJSDocType(param) || !param.name || !isIdentifier(param.name)) { return; } const typeNode = inference.type && getTypeNodeIfAccessible(inference.type, param, program, host); @@ -398,7 +399,7 @@ namespace ts.codefix { return references && inferTypeFromReferences(program, references, cancellationToken).parameters(func) || func.parameters.map(p => ({ declaration: p, - type: isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : program.getTypeChecker().getAnyType() + type: p.name && isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : program.getTypeChecker().getAnyType() })); } @@ -557,7 +558,7 @@ namespace ts.codefix { types.push(checker.getBaseTypeOfLiteralType(call.argumentTypes[parameterIndex])); } } - if (isIdentifier(parameter.name)) { + if (parameter.name && isIdentifier(parameter.name)) { const inferred = inferTypesFromReferencesSingle(getReferences(parameter.name, program, cancellationToken)); types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred)); } diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 59add59b0ae..b938169ea40 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -270,7 +270,7 @@ namespace ts.InlayHints { const param = node.parameters[i]; const effectiveTypeAnnotation = getEffectiveTypeAnnotationNode(param); - if (effectiveTypeAnnotation) { + if (effectiveTypeAnnotation || !param.name) { continue; } diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 2e2bc61136a..a1e81a3d2df 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -266,6 +266,7 @@ namespace ts.JsDoc { if (!isFunctionLike(fn)) return []; return mapDefined(fn.parameters, param => { + Debug.type(param.name); if (!isIdentifier(param.name)) return undefined; const name = param.name.text; @@ -372,6 +373,7 @@ namespace ts.JsDoc { function parameterDocComments(parameters: readonly ParameterDeclaration[], isJavaScriptFile: boolean, indentationStr: string, newLine: string): string { return parameters.map(({ name, dotDotDotToken }, i) => { + Debug.type(name); const paramName = name.kind === SyntaxKind.Identifier ? name.text : "param" + i; const type = isJavaScriptFile ? (dotDotDotToken ? "{...any} " : "{any} ") : ""; return `${indentationStr} * @param ${type}${paramName}${newLine}`; diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index 83c071c70ac..d9bb3de90bf 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -142,7 +142,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { } function convertParameterToNamedTupleMember(p: ParameterDeclaration): NamedTupleMember { - Debug.assert(isIdentifier(p.name)); // This is checked during refactoring applicability checking + Debug.assert(p.name && isIdentifier(p.name)); // This is checked during refactoring applicability checking const result = setTextRange(factory.createNamedTupleMember( p.dotDotDotToken, p.name, @@ -209,7 +209,7 @@ ${newComment.split("\n").map(c => ` * ${c}`).join("\n")} return; } const signatureDecls = decls as (MethodSignature | MethodDeclaration | CallSignatureDeclaration | ConstructorDeclaration | ConstructSignatureDeclaration | FunctionDeclaration)[]; - if (some(signatureDecls, d => !!d.typeParameters || some(d.parameters, p => !!p.decorators || !!p.modifiers || !isIdentifier(p.name)))) { + if (some(signatureDecls, d => !!d.typeParameters || some(d.parameters, p => !!p.decorators || !!p.modifiers || !p.name || !isIdentifier(p.name)))) { return; } const signatures = mapDefined(signatureDecls, d => checker.getSignatureFromDeclaration(d)); diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 784cefda23b..6a4c705ddcc 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -404,7 +404,7 @@ namespace ts.refactor.convertParamsToDestructuredObject { const type = checker.getTypeAtLocation(parameterDeclaration); if (!checker.isArrayType(type) && !checker.isTupleType(type)) return false; } - return !parameterDeclaration.modifiers && !parameterDeclaration.decorators && isIdentifier(parameterDeclaration.name); + return !parameterDeclaration.modifiers && !parameterDeclaration.decorators && !!parameterDeclaration.name && isIdentifier(parameterDeclaration.name); } function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { @@ -412,7 +412,7 @@ namespace ts.refactor.convertParamsToDestructuredObject { } function hasThisParameter(parameters: NodeArray): boolean { - return parameters.length > 0 && isThis(parameters[0].name); + return parameters.length > 0 && !!parameters[0].name && isThis(parameters[0].name); } function getRefactorableParametersLength(parameters: NodeArray): number { @@ -492,8 +492,8 @@ namespace ts.refactor.convertParamsToDestructuredObject { /*questionToken*/ undefined, thisParameter.type); - suppressLeadingAndTrailingTrivia(newThisParameter.name); - copyComments(thisParameter.name, newThisParameter.name); + suppressLeadingAndTrailingTrivia(newThisParameter.name!); + copyComments(thisParameter.name, newThisParameter.name!); if (thisParameter.type) { suppressLeadingAndTrailingTrivia(newThisParameter.type!); copyComments(thisParameter.type, newThisParameter.type!); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 5b0032e6f01..733844e0475 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1291,7 +1291,7 @@ namespace ts.refactor.extractSymbol { const firstParameter = firstOrUndefined(parameters); // If the function signature has a this parameter and if the first defined parameter is not the this parameter, we must add it // Note: If this parameter was already there, it would have been previously updated with the type if not type was present - if ((!firstParameter || (isIdentifier(firstParameter.name) && firstParameter.name.escapedText !== "this"))) { + if (!firstParameter || !firstParameter.name || (isIdentifier(firstParameter.name) && firstParameter.name.escapedText !== "this")) { const thisType = checker.getTypeOfSymbolAtLocation(functionSignature.thisParameter, node); parameters.splice(0, 0, factory.createParameterDeclaration( /* decorators */ undefined, diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 83d5acb9d72..db8170c5b95 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -540,6 +540,8 @@ namespace ts.textChanges { } } else { + Debug.assert(node.parent.kind !== SyntaxKind.JSDocFunctionType); + Debug.type(node.name); endNode = (node.kind === SyntaxKind.VariableDeclaration ? node.exclamationToken : node.questionToken) ?? node.name; }