Merge pull request #14920 from Microsoft/jsInferRestArgs
Infer a rest parameter for javascript function that uses 'arguments'
This commit is contained in:
commit
22b4e4d65d
|
@ -493,6 +493,10 @@ namespace ts {
|
|||
return symbol;
|
||||
}
|
||||
|
||||
function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
|
||||
return (symbol.flags & SymbolFlags.Transient) !== 0;
|
||||
}
|
||||
|
||||
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
|
||||
let result: SymbolFlags = 0;
|
||||
if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes;
|
||||
|
@ -3385,23 +3389,23 @@ namespace ts {
|
|||
|
||||
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
|
||||
const parameterNode = <ParameterDeclaration>p.valueDeclaration;
|
||||
if (isRestParameter(parameterNode)) {
|
||||
if (parameterNode ? isRestParameter(parameterNode) : isTransientSymbol(p) && p.isRestParameter) {
|
||||
writePunctuation(writer, SyntaxKind.DotDotDotToken);
|
||||
}
|
||||
if (isBindingPattern(parameterNode.name)) {
|
||||
if (parameterNode && isBindingPattern(parameterNode.name)) {
|
||||
buildBindingPatternDisplay(<BindingPattern>parameterNode.name, writer, enclosingDeclaration, flags, symbolStack);
|
||||
}
|
||||
else {
|
||||
appendSymbolNameOnly(p, writer);
|
||||
}
|
||||
if (isOptionalParameter(parameterNode)) {
|
||||
if (parameterNode && isOptionalParameter(parameterNode)) {
|
||||
writePunctuation(writer, SyntaxKind.QuestionToken);
|
||||
}
|
||||
writePunctuation(writer, SyntaxKind.ColonToken);
|
||||
writeSpace(writer);
|
||||
|
||||
let type = getTypeOfSymbol(p);
|
||||
if (isRequiredInitializedParameter(parameterNode)) {
|
||||
if (parameterNode && isRequiredInitializedParameter(parameterNode)) {
|
||||
type = includeFalsyTypes(type, TypeFlags.Undefined);
|
||||
}
|
||||
buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack);
|
||||
|
@ -6170,6 +6174,37 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function containsArgumentsReference(declaration: FunctionLikeDeclaration): boolean {
|
||||
const links = getNodeLinks(declaration);
|
||||
if (links.containsArgumentsReference === undefined) {
|
||||
if (links.flags & NodeCheckFlags.CaptureArguments) {
|
||||
links.containsArgumentsReference = true;
|
||||
}
|
||||
else {
|
||||
links.containsArgumentsReference = traverse(declaration.body);
|
||||
}
|
||||
}
|
||||
return links.containsArgumentsReference;
|
||||
|
||||
function traverse(node: Node): boolean {
|
||||
if (!node) return false;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return (<Identifier>node).text === "arguments" && isPartOfExpression(node);
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return (<Declaration>node).name.kind === SyntaxKind.ComputedPropertyName
|
||||
&& traverse((<Declaration>node).name);
|
||||
|
||||
default:
|
||||
return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && forEachChild(node, traverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
|
||||
if (!symbol) return emptyArray;
|
||||
const result: Signature[] = [];
|
||||
|
@ -11613,9 +11648,7 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
if (node.flags & NodeFlags.AwaitContext) {
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments;
|
||||
}
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments;
|
||||
return getTypeOfSymbol(symbol);
|
||||
}
|
||||
|
||||
|
@ -14854,6 +14887,21 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
if (signatures.length === 1) {
|
||||
const declaration = signatures[0].declaration;
|
||||
if (declaration && isInJavaScriptFile(declaration) && !hasJSDocParameterTags(declaration)) {
|
||||
if (containsArgumentsReference(<FunctionLikeDeclaration>declaration)) {
|
||||
const signatureWithRest = cloneSignature(signatures[0]);
|
||||
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args");
|
||||
syntheticArgsSymbol.type = anyArrayType;
|
||||
syntheticArgsSymbol.isRestParameter = true;
|
||||
signatureWithRest.parameters = concatenate(signatureWithRest.parameters, [syntheticArgsSymbol]);
|
||||
signatureWithRest.hasRestParameter = true;
|
||||
signatures = [signatureWithRest];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const candidates = candidatesOutArray || [];
|
||||
// reorderCandidates fills up the candidates array directly
|
||||
reorderCandidates(signatures, candidates);
|
||||
|
|
|
@ -2870,6 +2870,7 @@ namespace ts {
|
|||
/* @internal */
|
||||
export interface TransientSymbol extends Symbol, SymbolLinks {
|
||||
checkFlags: CheckFlags;
|
||||
isRestParameter?: boolean;
|
||||
}
|
||||
|
||||
export type SymbolTable = Map<Symbol>;
|
||||
|
@ -2899,7 +2900,7 @@ namespace ts {
|
|||
ContextChecked = 0x00000400, // Contextual types have been assigned
|
||||
AsyncMethodWithSuper = 0x00000800, // An async method that reads a value from a member of 'super'.
|
||||
AsyncMethodWithSuperBinding = 0x00001000, // An async method that assigns a value to a member of 'super'.
|
||||
CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions)
|
||||
CaptureArguments = 0x00002000, // Lexical 'arguments' used in body
|
||||
EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them.
|
||||
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.
|
||||
LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure
|
||||
|
@ -2923,6 +2924,7 @@ namespace ts {
|
|||
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
|
||||
enumMemberValue?: number; // Constant value of enum member
|
||||
isVisible?: boolean; // Is this node visible
|
||||
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference
|
||||
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
|
||||
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
|
||||
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
|
||||
|
|
17
tests/cases/fourslash/signatureHelpCallExpressionJs.ts
Normal file
17
tests/cases/fourslash/signatureHelpCallExpressionJs.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @checkJs: true
|
||||
// @allowJs: true
|
||||
|
||||
// @Filename: main.js
|
||||
////function fnTest() { arguments; }
|
||||
////fnTest(/*1*/);
|
||||
////fnTest(1, 2, 3);
|
||||
|
||||
goTo.marker('1');
|
||||
verify.signatureHelpCountIs(1);
|
||||
verify.currentSignatureParameterCountIs(1);
|
||||
verify.currentSignatureHelpIs('fnTest(...args: any[]): void');
|
||||
verify.currentParameterHelpArgumentNameIs('args');
|
||||
verify.currentParameterSpanIs("...args: any[]");
|
||||
verify.numberOfErrorsInCurrentFile(0);
|
Loading…
Reference in a new issue