mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #726 from Microsoft/sigHelp
Signature help in the new compiler
This commit is contained in:
+111
-62
@@ -91,6 +91,7 @@ module ts {
|
||||
getRootSymbol: getRootSymbol,
|
||||
getContextualType: getContextualType,
|
||||
getFullyQualifiedName: getFullyQualifiedName,
|
||||
getResolvedSignature: getResolvedSignature,
|
||||
getEnumMemberValue: getEnumMemberValue
|
||||
};
|
||||
|
||||
@@ -4330,55 +4331,26 @@ module ts {
|
||||
return unknownSignature;
|
||||
}
|
||||
|
||||
function isCandidateSignature(node: CallExpression, signature: Signature) {
|
||||
function signatureHasCorrectArity(node: CallExpression, signature: Signature): boolean {
|
||||
var args = node.arguments || emptyArray;
|
||||
return args.length >= signature.minArgumentCount &&
|
||||
var isCorrect = args.length >= signature.minArgumentCount &&
|
||||
(signature.hasRestParameter || args.length <= signature.parameters.length) &&
|
||||
(!node.typeArguments || signature.typeParameters && node.typeArguments.length === signature.typeParameters.length);
|
||||
}
|
||||
|
||||
// The candidate list orders groups in reverse, but within a group signatures are kept in declaration order
|
||||
// A nit here is that we reorder only signatures that belong to the same symbol,
|
||||
// so order how inherited signatures are processed is still preserved.
|
||||
// interface A { (x: string): void }
|
||||
// interface B extends A { (x: 'foo'): string }
|
||||
// var b: B;
|
||||
// b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void]
|
||||
function collectCandidates(node: CallExpression, signatures: Signature[]): Signature[]{
|
||||
var result: Signature[] = [];
|
||||
var lastParent: Node;
|
||||
var lastSymbol: Symbol;
|
||||
var cutoffPos: number = 0;
|
||||
var pos: number;
|
||||
for (var i = 0; i < signatures.length; i++) {
|
||||
var signature = signatures[i];
|
||||
if (isCandidateSignature(node, signature)) {
|
||||
var symbol = signature.declaration && getSymbolOfNode(signature.declaration);
|
||||
var parent = signature.declaration && signature.declaration.parent;
|
||||
if (!lastSymbol || symbol === lastSymbol) {
|
||||
if (lastParent && parent === lastParent) {
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
lastParent = parent;
|
||||
pos = cutoffPos;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// current declaration belongs to a different symbol
|
||||
// set cutoffPos so re-orderings in the future won't change result set from 0 to cutoffPos
|
||||
pos = cutoffPos = result.length;
|
||||
lastParent = parent;
|
||||
}
|
||||
lastSymbol = symbol;
|
||||
|
||||
for (var j = result.length; j > pos; j--) {
|
||||
result[j] = result[j - 1];
|
||||
}
|
||||
result[pos] = signature;
|
||||
// For error recovery, since we have parsed OmittedExpressions for any extra commas
|
||||
// in the argument list, if we see any OmittedExpressions, just return true.
|
||||
// The reason this is ok is because omitted expressions here are syntactically
|
||||
// illegal, and will cause a parse error.
|
||||
// Note: It may be worth keeping the upper bound check on arity, but removing
|
||||
// the lower bound check if there are omitted expressions.
|
||||
if (!isCorrect) {
|
||||
// Technically this type assertion is not safe because args could be initialized to emptyArray
|
||||
// above.
|
||||
if ((<NodeArray<Node>>args).hasTrailingComma || forEach(args, arg => arg.kind === SyntaxKind.OmittedExpression)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return isCorrect;
|
||||
}
|
||||
|
||||
// If type has a single call signature and no other members, return that signature. Otherwise, return undefined.
|
||||
@@ -4409,6 +4381,9 @@ module ts {
|
||||
var mapper = createInferenceMapper(context);
|
||||
// First infer from arguments that are not context sensitive
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i].kind === SyntaxKind.OmittedExpression) {
|
||||
continue;
|
||||
}
|
||||
if (!excludeArgument || excludeArgument[i] === undefined) {
|
||||
var parameterType = getTypeAtPosition(signature, i);
|
||||
inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, mapper), parameterType);
|
||||
@@ -4417,6 +4392,9 @@ module ts {
|
||||
// Next, infer from those context sensitive arguments that are no longer excluded
|
||||
if (excludeArgument) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i].kind === SyntaxKind.OmittedExpression) {
|
||||
continue;
|
||||
}
|
||||
if (excludeArgument[i] === false) {
|
||||
var parameterType = getTypeAtPosition(signature, i);
|
||||
inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, mapper), parameterType);
|
||||
@@ -4445,6 +4423,10 @@ module ts {
|
||||
if (node.arguments) {
|
||||
for (var i = 0; i < node.arguments.length; i++) {
|
||||
var arg = node.arguments[i];
|
||||
if (arg.kind === SyntaxKind.OmittedExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramType = getTypeAtPosition(signature, i);
|
||||
// String literals get string literal types unless we're reporting errors
|
||||
var argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors ?
|
||||
@@ -4462,9 +4444,11 @@ module ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveCall(node: CallExpression, signatures: Signature[]): Signature {
|
||||
function resolveCall(node: CallExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
|
||||
forEach(node.typeArguments, checkSourceElement);
|
||||
var candidates = collectCandidates(node, signatures);
|
||||
var candidates = candidatesOutArray || [];
|
||||
// collectCandidates fills up the candidates array directly
|
||||
collectCandidates();
|
||||
if (!candidates.length) {
|
||||
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
|
||||
return resolveErrorCall(node);
|
||||
@@ -4480,20 +4464,24 @@ module ts {
|
||||
var relation = candidates.length === 1 ? assignableRelation : subtypeRelation;
|
||||
while (true) {
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
if (!signatureHasCorrectArity(node, candidates[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var candidate = candidates[i];
|
||||
if (candidate.typeParameters) {
|
||||
var candidateWithCorrectArity = candidates[i];
|
||||
if (candidateWithCorrectArity.typeParameters) {
|
||||
var typeArguments = node.typeArguments ?
|
||||
checkTypeArguments(candidate, node.typeArguments) :
|
||||
inferTypeArguments(candidate, args, excludeArgument);
|
||||
candidate = getSignatureInstantiation(candidate, typeArguments);
|
||||
checkTypeArguments(candidateWithCorrectArity, node.typeArguments) :
|
||||
inferTypeArguments(candidateWithCorrectArity, args, excludeArgument);
|
||||
candidateWithCorrectArity = getSignatureInstantiation(candidateWithCorrectArity, typeArguments);
|
||||
}
|
||||
if (!checkApplicableSignature(node, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
|
||||
if (!checkApplicableSignature(node, candidateWithCorrectArity, relation, excludeArgument, /*reportErrors*/ false)) {
|
||||
break;
|
||||
}
|
||||
var index = excludeArgument ? indexOf(excludeArgument, true) : -1;
|
||||
if (index < 0) {
|
||||
return candidate;
|
||||
return candidateWithCorrectArity;
|
||||
}
|
||||
excludeArgument[index] = false;
|
||||
}
|
||||
@@ -4503,17 +4491,70 @@ module ts {
|
||||
}
|
||||
relation = assignableRelation;
|
||||
}
|
||||
|
||||
// No signatures were applicable. Now report errors based on the last applicable signature with
|
||||
// no arguments excluded from assignability checks.
|
||||
checkApplicableSignature(node, candidate, relation, undefined, /*reportErrors*/ true);
|
||||
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
|
||||
// skip the checkApplicableSignature check.
|
||||
if (candidateWithCorrectArity) {
|
||||
checkApplicableSignature(node, candidateWithCorrectArity, relation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
|
||||
}
|
||||
else {
|
||||
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
|
||||
// The candidate list orders groups in reverse, but within a group signatures are kept in declaration order
|
||||
// A nit here is that we reorder only signatures that belong to the same symbol,
|
||||
// so order how inherited signatures are processed is still preserved.
|
||||
// interface A { (x: string): void }
|
||||
// interface B extends A { (x: 'foo'): string }
|
||||
// var b: B;
|
||||
// b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void]
|
||||
function collectCandidates(): void {
|
||||
var result = candidates;
|
||||
var lastParent: Node;
|
||||
var lastSymbol: Symbol;
|
||||
var cutoffPos: number = 0;
|
||||
var pos: number;
|
||||
Debug.assert(!result.length);
|
||||
for (var i = 0; i < signatures.length; i++) {
|
||||
var signature = signatures[i];
|
||||
if (true) {
|
||||
var symbol = signature.declaration && getSymbolOfNode(signature.declaration);
|
||||
var parent = signature.declaration && signature.declaration.parent;
|
||||
if (!lastSymbol || symbol === lastSymbol) {
|
||||
if (lastParent && parent === lastParent) {
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
lastParent = parent;
|
||||
pos = cutoffPos;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// current declaration belongs to a different symbol
|
||||
// set cutoffPos so re-orderings in the future won't change result set from 0 to cutoffPos
|
||||
pos = cutoffPos = result.length;
|
||||
lastParent = parent;
|
||||
}
|
||||
lastSymbol = symbol;
|
||||
|
||||
for (var j = result.length; j > pos; j--) {
|
||||
result[j] = result[j - 1];
|
||||
}
|
||||
result[pos] = signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCallExpression(node: CallExpression): Signature {
|
||||
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
|
||||
if (node.func.kind === SyntaxKind.SuperKeyword) {
|
||||
var superType = checkSuperExpression(node.func);
|
||||
if (superType !== unknownType) {
|
||||
return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct));
|
||||
return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct), candidatesOutArray);
|
||||
}
|
||||
return resolveUntypedCall(node);
|
||||
}
|
||||
@@ -4561,10 +4602,10 @@ module ts {
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, callSignatures);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray);
|
||||
}
|
||||
|
||||
function resolveNewExpression(node: NewExpression): Signature {
|
||||
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature {
|
||||
var expressionType = checkExpression(node.func);
|
||||
if (expressionType === unknownType) {
|
||||
// Another error has already been reported
|
||||
@@ -4599,7 +4640,7 @@ module ts {
|
||||
// that the user will not add any.
|
||||
var constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
|
||||
if (constructSignatures.length) {
|
||||
return resolveCall(node, constructSignatures);
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray);
|
||||
}
|
||||
|
||||
// If ConstructExpr's apparent type is an object type with no construct signatures but
|
||||
@@ -4608,7 +4649,7 @@ module ts {
|
||||
// operation is Any.
|
||||
var callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
|
||||
if (callSignatures.length) {
|
||||
var signature = resolveCall(node, callSignatures);
|
||||
var signature = resolveCall(node, callSignatures, candidatesOutArray);
|
||||
if (getReturnTypeOfSignature(signature) !== voidType) {
|
||||
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
|
||||
}
|
||||
@@ -4619,11 +4660,19 @@ module ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
function getResolvedSignature(node: CallExpression): Signature {
|
||||
// candidatesOutArray is passed by signature help in the language service, and collectCandidates
|
||||
// must fill it up with the appropriate candidate signatures
|
||||
function getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature {
|
||||
var links = getNodeLinks(node);
|
||||
if (!links.resolvedSignature) {
|
||||
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
|
||||
// However, it is possible that either candidatesOutArray was not passed in the first time,
|
||||
// or that a different candidatesOutArray was passed in. Therefore, we need to redo the work
|
||||
// to correctly fill the candidatesOutArray.
|
||||
if (!links.resolvedSignature || candidatesOutArray) {
|
||||
links.resolvedSignature = anySignature;
|
||||
links.resolvedSignature = node.kind === SyntaxKind.CallExpression ? resolveCallExpression(node) : resolveNewExpression(node);
|
||||
links.resolvedSignature = node.kind === SyntaxKind.CallExpression
|
||||
? resolveCallExpression(node, candidatesOutArray)
|
||||
: resolveNewExpression(node, candidatesOutArray);
|
||||
}
|
||||
return links.resolvedSignature;
|
||||
}
|
||||
|
||||
+15
-1
@@ -11,7 +11,9 @@ module ts {
|
||||
var result: U;
|
||||
if (array) {
|
||||
for (var i = 0, len = array.length; i < len; i++) {
|
||||
if (result = callback(array[i])) break;
|
||||
if (result = callback(array[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -39,6 +41,18 @@ module ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function countWhere<T>(array: T[], predicate: (x: T) => boolean): number {
|
||||
var count = 0;
|
||||
if (array) {
|
||||
for (var i = 0, len = array.length; i < len; i++) {
|
||||
if (predicate(array[i])) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
|
||||
if (array) {
|
||||
var result: T[] = [];
|
||||
|
||||
+41
-18
@@ -748,23 +748,46 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitCommaList(nodes: Node[], count?: number) {
|
||||
if (!(count >= 0)) count = nodes.length;
|
||||
if (nodes) {
|
||||
for (var i = 0; i < count; i++) {
|
||||
if (i) write(", ");
|
||||
emit(nodes[i]);
|
||||
function emitTrailingCommaIfPresent(nodeList: NodeArray<Node>, isMultiline: boolean): void {
|
||||
if (nodeList.hasTrailingComma) {
|
||||
write(",");
|
||||
if (isMultiline) {
|
||||
writeLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitMultiLineList(nodes: Node[]) {
|
||||
function emitCommaList(nodes: NodeArray<Node>, includeTrailingComma: boolean, count?: number) {
|
||||
if (!(count >= 0)) {
|
||||
count = nodes.length;
|
||||
}
|
||||
if (nodes) {
|
||||
for (var i = 0; i < count; i++) {
|
||||
if (i) {
|
||||
write(", ");
|
||||
}
|
||||
emit(nodes[i]);
|
||||
}
|
||||
|
||||
if (includeTrailingComma) {
|
||||
emitTrailingCommaIfPresent(nodes, /*isMultiline*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitMultiLineList(nodes: NodeArray<Node>, includeTrailingComma: boolean) {
|
||||
if (nodes) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
if (i) write(",");
|
||||
if (i) {
|
||||
write(",");
|
||||
}
|
||||
writeLine();
|
||||
emit(nodes[i]);
|
||||
}
|
||||
|
||||
if (includeTrailingComma) {
|
||||
emitTrailingCommaIfPresent(nodes, /*isMultiline*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,14 +899,14 @@ module ts {
|
||||
if (node.flags & NodeFlags.MultiLine) {
|
||||
write("[");
|
||||
increaseIndent();
|
||||
emitMultiLineList(node.elements);
|
||||
emitMultiLineList(node.elements, /*includeTrailingComma*/ true);
|
||||
decreaseIndent();
|
||||
writeLine();
|
||||
write("]");
|
||||
}
|
||||
else {
|
||||
write("[");
|
||||
emitCommaList(node.elements);
|
||||
emitCommaList(node.elements, /*includeTrailingComma*/ true);
|
||||
write("]");
|
||||
}
|
||||
}
|
||||
@@ -895,14 +918,14 @@ module ts {
|
||||
else if (node.flags & NodeFlags.MultiLine) {
|
||||
write("{");
|
||||
increaseIndent();
|
||||
emitMultiLineList(node.properties);
|
||||
emitMultiLineList(node.properties, /*includeTrailingComma*/ compilerOptions.target >= ScriptTarget.ES5);
|
||||
decreaseIndent();
|
||||
writeLine();
|
||||
write("}");
|
||||
}
|
||||
else {
|
||||
write("{ ");
|
||||
emitCommaList(node.properties);
|
||||
emitCommaList(node.properties, /*includeTrailingComma*/ compilerOptions.target >= ScriptTarget.ES5);
|
||||
write(" }");
|
||||
}
|
||||
}
|
||||
@@ -949,13 +972,13 @@ module ts {
|
||||
emitThis(node.func);
|
||||
if (node.arguments.length) {
|
||||
write(", ");
|
||||
emitCommaList(node.arguments);
|
||||
emitCommaList(node.arguments, /*includeTrailingComma*/ false);
|
||||
}
|
||||
write(")");
|
||||
}
|
||||
else {
|
||||
write("(");
|
||||
emitCommaList(node.arguments);
|
||||
emitCommaList(node.arguments, /*includeTrailingComma*/ false);
|
||||
write(")");
|
||||
}
|
||||
}
|
||||
@@ -965,7 +988,7 @@ module ts {
|
||||
emit(node.func);
|
||||
if (node.arguments) {
|
||||
write("(");
|
||||
emitCommaList(node.arguments);
|
||||
emitCommaList(node.arguments, /*includeTrailingComma*/ false);
|
||||
write(")");
|
||||
}
|
||||
}
|
||||
@@ -1138,7 +1161,7 @@ module ts {
|
||||
if (node.declarations) {
|
||||
emitToken(SyntaxKind.VarKeyword, endPos);
|
||||
write(" ");
|
||||
emitCommaList(node.declarations);
|
||||
emitCommaList(node.declarations, /*includeTrailingComma*/ false);
|
||||
}
|
||||
if (node.initializer) {
|
||||
emit(node.initializer);
|
||||
@@ -1286,7 +1309,7 @@ module ts {
|
||||
function emitVariableStatement(node: VariableStatement) {
|
||||
emitLeadingComments(node);
|
||||
if (!(node.flags & NodeFlags.Export)) write("var ");
|
||||
emitCommaList(node.declarations);
|
||||
emitCommaList(node.declarations, /*includeTrailingComma*/ false);
|
||||
write(";");
|
||||
emitTrailingComments(node);
|
||||
}
|
||||
@@ -1395,7 +1418,7 @@ module ts {
|
||||
increaseIndent();
|
||||
write("(");
|
||||
if (node) {
|
||||
emitCommaList(node.parameters, node.parameters.length - (hasRestParameters(node) ? 1 : 0));
|
||||
emitCommaList(node.parameters, /*includeTrailingComma*/ false, node.parameters.length - (hasRestParameters(node) ? 1 : 0));
|
||||
}
|
||||
write(")");
|
||||
decreaseIndent();
|
||||
|
||||
+44
-24
@@ -628,12 +628,6 @@ module ts {
|
||||
Parameters, // Parameters in parameter list
|
||||
}
|
||||
|
||||
enum TrailingCommaBehavior {
|
||||
Disallow,
|
||||
Allow,
|
||||
Preserve
|
||||
}
|
||||
|
||||
// Tracks whether we nested (directly or indirectly) in a certain control block.
|
||||
// Used for validating break and continue statements.
|
||||
enum ControlBlockContext {
|
||||
@@ -1205,7 +1199,7 @@ module ts {
|
||||
}
|
||||
|
||||
// Parses a comma-delimited list of elements
|
||||
function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, trailingCommaBehavior: TrailingCommaBehavior): NodeArray<T> {
|
||||
function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, allowTrailingComma: boolean): NodeArray<T> {
|
||||
var saveParsingContext = parsingContext;
|
||||
parsingContext |= 1 << kind;
|
||||
var result = <NodeArray<T>>[];
|
||||
@@ -1230,15 +1224,14 @@ module ts {
|
||||
else if (isListTerminator(kind)) {
|
||||
// Check if the last token was a comma.
|
||||
if (commaStart >= 0) {
|
||||
if (trailingCommaBehavior === TrailingCommaBehavior.Disallow) {
|
||||
if (!allowTrailingComma) {
|
||||
if (file.syntacticErrors.length === errorCountBeforeParsingList) {
|
||||
// Report a grammar error so we don't affect lookahead
|
||||
grammarErrorAtPos(commaStart, scanner.getStartPos() - commaStart, Diagnostics.Trailing_comma_not_allowed);
|
||||
}
|
||||
}
|
||||
else if (trailingCommaBehavior === TrailingCommaBehavior.Preserve) {
|
||||
result.push(<T>createNode(SyntaxKind.OmittedExpression));
|
||||
}
|
||||
// Always preserve a trailing comma by marking it on the NodeArray
|
||||
result.hasTrailingComma = true;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1273,7 +1266,7 @@ module ts {
|
||||
|
||||
function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray<T> {
|
||||
if (parseExpected(startToken)) {
|
||||
var result = parseDelimitedList(kind, parseElement, TrailingCommaBehavior.Disallow);
|
||||
var result = parseDelimitedList(kind, parseElement, /*allowTrailingComma*/ false);
|
||||
parseExpected(endToken);
|
||||
return result;
|
||||
}
|
||||
@@ -2309,7 +2302,11 @@ module ts {
|
||||
else {
|
||||
parseExpected(SyntaxKind.OpenParenToken);
|
||||
}
|
||||
callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow);
|
||||
// It is an error to have a trailing comma in an argument list. However, the checker
|
||||
// needs evidence of a trailing comma in order to give good results for signature help.
|
||||
// That is why we do not allow a trailing comma, but we "preserve" a trailing comma.
|
||||
callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions,
|
||||
parseArgumentExpression, /*allowTrailingComma*/ false);
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
expr = finishNode(callExpr);
|
||||
continue;
|
||||
@@ -2378,15 +2375,33 @@ module ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseAssignmentExpressionOrOmittedExpression(omittedExpressionDiagnostic: DiagnosticMessage): Expression {
|
||||
if (token === SyntaxKind.CommaToken) {
|
||||
if (omittedExpressionDiagnostic) {
|
||||
var errorStart = scanner.getTokenPos();
|
||||
var errorLength = scanner.getTextPos() - errorStart;
|
||||
grammarErrorAtPos(errorStart, errorLength, omittedExpressionDiagnostic);
|
||||
}
|
||||
return createNode(SyntaxKind.OmittedExpression);
|
||||
}
|
||||
|
||||
return parseAssignmentExpression();
|
||||
}
|
||||
|
||||
function parseArrayLiteralElement(): Expression {
|
||||
return token === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : parseAssignmentExpression();
|
||||
return parseAssignmentExpressionOrOmittedExpression(/*omittedExpressionDiagnostic*/ undefined);
|
||||
}
|
||||
|
||||
function parseArgumentExpression(): Expression {
|
||||
return parseAssignmentExpressionOrOmittedExpression(Diagnostics.Argument_expression_expected);
|
||||
}
|
||||
|
||||
function parseArrayLiteral(): ArrayLiteral {
|
||||
var node = <ArrayLiteral>createNode(SyntaxKind.ArrayLiteral);
|
||||
parseExpected(SyntaxKind.OpenBracketToken);
|
||||
if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine;
|
||||
node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement, TrailingCommaBehavior.Preserve);
|
||||
node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers,
|
||||
parseArrayLiteralElement, /*allowTrailingComma*/ true);
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
return finishNode(node);
|
||||
}
|
||||
@@ -2428,10 +2443,7 @@ module ts {
|
||||
node.flags |= NodeFlags.MultiLine;
|
||||
}
|
||||
|
||||
// ES3 itself does not accept a trailing comma in an object literal, however, we'd like to preserve it in ES5.
|
||||
var trailingCommaBehavior = languageVersion === ScriptTarget.ES3 ? TrailingCommaBehavior.Allow : TrailingCommaBehavior.Preserve;
|
||||
|
||||
node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, trailingCommaBehavior);
|
||||
node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, /*allowTrailingComma*/ true);
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
|
||||
var seen: Map<SymbolFlags> = {};
|
||||
@@ -2520,7 +2532,11 @@ module ts {
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
node.func = parseCallAndAccess(parsePrimaryExpression(), /* inNewExpression */ true);
|
||||
if (parseOptional(SyntaxKind.OpenParenToken) || token === SyntaxKind.LessThanToken && (node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) {
|
||||
node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow);
|
||||
// It is an error to have a trailing comma in an argument list. However, the checker
|
||||
// needs evidence of a trailing comma in order to give good results for signature help.
|
||||
// That is why we do not allow a trailing comma, but we "preserve" a trailing comma.
|
||||
node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions,
|
||||
parseArgumentExpression, /*allowTrailingComma*/ false);
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
}
|
||||
return finishNode(node);
|
||||
@@ -3089,7 +3105,8 @@ module ts {
|
||||
}
|
||||
|
||||
function parseVariableDeclarationList(flags: NodeFlags, noIn?: boolean): NodeArray<VariableDeclaration> {
|
||||
return parseDelimitedList(ParsingContext.VariableDeclarations, () => parseVariableDeclaration(flags, noIn), TrailingCommaBehavior.Disallow);
|
||||
return parseDelimitedList(ParsingContext.VariableDeclarations,
|
||||
() => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false);
|
||||
}
|
||||
|
||||
function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement {
|
||||
@@ -3488,7 +3505,8 @@ module ts {
|
||||
var implementsKeywordLength: number;
|
||||
if (parseOptional(SyntaxKind.ImplementsKeyword)) {
|
||||
implementsKeywordLength = scanner.getStartPos() - implementsKeywordStart;
|
||||
node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow);
|
||||
node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences,
|
||||
parseTypeReference, /*allowTrailingComma*/ false);
|
||||
}
|
||||
var errorCountBeforeClassBody = file.syntacticErrors.length;
|
||||
if (parseExpected(SyntaxKind.OpenBraceToken)) {
|
||||
@@ -3516,7 +3534,8 @@ module ts {
|
||||
var extendsKeywordLength: number;
|
||||
if (parseOptional(SyntaxKind.ExtendsKeyword)) {
|
||||
extendsKeywordLength = scanner.getStartPos() - extendsKeywordStart;
|
||||
node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow);
|
||||
node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences,
|
||||
parseTypeReference, /*allowTrailingComma*/ false);
|
||||
}
|
||||
var errorCountBeforeInterfaceBody = file.syntacticErrors.length;
|
||||
node.members = parseTypeLiteral().members;
|
||||
@@ -3580,7 +3599,8 @@ module ts {
|
||||
parseExpected(SyntaxKind.EnumKeyword);
|
||||
node.name = parseIdentifier();
|
||||
if (parseExpected(SyntaxKind.OpenBraceToken)) {
|
||||
node.members = parseDelimitedList(ParsingContext.EnumMembers, parseAndCheckEnumMember, TrailingCommaBehavior.Allow);
|
||||
node.members = parseDelimitedList(ParsingContext.EnumMembers,
|
||||
parseAndCheckEnumMember, /*allowTrailingComma*/ true);
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -261,7 +261,9 @@ module ts {
|
||||
localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
|
||||
}
|
||||
|
||||
export interface NodeArray<T> extends Array<T>, TextRange { }
|
||||
export interface NodeArray<T> extends Array<T>, TextRange {
|
||||
hasTrailingComma?: boolean;
|
||||
}
|
||||
|
||||
export interface Identifier extends Node {
|
||||
text: string; // Text of identifier (with escapes converted to characters)
|
||||
@@ -648,6 +650,7 @@ module ts {
|
||||
getAugmentedPropertiesOfApparentType(type: Type): Symbol[];
|
||||
getRootSymbol(symbol: Symbol): Symbol;
|
||||
getContextualType(node: Node): Type;
|
||||
getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature;
|
||||
|
||||
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
|
||||
// computed value.
|
||||
@@ -935,7 +938,7 @@ module ts {
|
||||
resolvedReturnType: Type; // Resolved return type
|
||||
minArgumentCount: number; // Number of non-optional parameters
|
||||
hasRestParameter: boolean; // True if last parameter is rest parameter
|
||||
hasStringLiterals: boolean; // True if instantiated
|
||||
hasStringLiterals: boolean; // True if specialized
|
||||
target?: Signature; // Instantiation target
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
|
||||
|
||||
Reference in New Issue
Block a user