mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add support for Optional Chaining (#33294)
* Add support for Optional Chaining * Add grammar error for invalid tagged template, more tests * Prototype * PR feedback * Add errors for invalid assignments and a trailing '?.' * Add additional signature help test, fix lint warnings * Fix to insert text for completions * Add initial control-flow analysis for optional chains * PR Feedback and more tests * Update to control flow * Remove mangled smart quotes in comments * Fix lint, PR feedback * Updates to control flow * Switch to FlowCondition for CFA of optional chains * Fix ?. insertion for completions on type variables * Accept API baseline change * Clean up types * improve control-flow debug output * Revert Debug.formatControlFlowGraph helper
This commit is contained in:
+121
-24
@@ -797,6 +797,10 @@ namespace ts {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
bindVariableDeclarationFlow(<VariableDeclaration>node);
|
||||
break;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
bindAccessExpressionFlow(<AccessExpression>node);
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
bindCallExpressionFlow(<CallExpression>node);
|
||||
break;
|
||||
@@ -941,7 +945,9 @@ namespace ts {
|
||||
}
|
||||
if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
|
||||
expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
|
||||
return unreachableFlow;
|
||||
if (!isOptionalChainRoot(expression.parent)) {
|
||||
return unreachableFlow;
|
||||
}
|
||||
}
|
||||
if (!isNarrowingExpression(expression)) {
|
||||
return antecedent;
|
||||
@@ -1015,23 +1021,28 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isTopLevelLogicalExpression(node: Node): boolean {
|
||||
while (node.parent.kind === SyntaxKind.ParenthesizedExpression ||
|
||||
node.parent.kind === SyntaxKind.PrefixUnaryExpression &&
|
||||
(<PrefixUnaryExpression>node.parent).operator === SyntaxKind.ExclamationToken) {
|
||||
while (isParenthesizedExpression(node.parent) ||
|
||||
isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) {
|
||||
node = node.parent;
|
||||
}
|
||||
return !isStatementCondition(node) && !isLogicalExpression(node.parent);
|
||||
return !isStatementCondition(node) &&
|
||||
!isLogicalExpression(node.parent) &&
|
||||
!(isOptionalChain(node.parent) && node.parent.expression === node);
|
||||
}
|
||||
|
||||
function doWithConditionalBranches<T>(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||
const savedTrueTarget = currentTrueTarget;
|
||||
const savedFalseTarget = currentFalseTarget;
|
||||
currentTrueTarget = trueTarget;
|
||||
currentFalseTarget = falseTarget;
|
||||
action(value);
|
||||
currentTrueTarget = savedTrueTarget;
|
||||
currentFalseTarget = savedFalseTarget;
|
||||
}
|
||||
|
||||
function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||
const saveTrueTarget = currentTrueTarget;
|
||||
const saveFalseTarget = currentFalseTarget;
|
||||
currentTrueTarget = trueTarget;
|
||||
currentFalseTarget = falseTarget;
|
||||
bind(node);
|
||||
currentTrueTarget = saveTrueTarget;
|
||||
currentFalseTarget = saveFalseTarget;
|
||||
if (!node || !isLogicalExpression(node)) {
|
||||
doWithConditionalBranches(bind, node, trueTarget, falseTarget);
|
||||
if (!node || !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
|
||||
addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
|
||||
addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
|
||||
}
|
||||
@@ -1536,22 +1547,96 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function bindCallExpressionFlow(node: CallExpression) {
|
||||
// If the target of the call expression is a function expression or arrow function we have
|
||||
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
|
||||
// the current control flow (which includes evaluation of the IIFE arguments).
|
||||
let expr: Expression = node.expression;
|
||||
while (expr.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
expr = (<ParenthesizedExpression>expr).expression;
|
||||
function isOutermostOptionalChain(node: OptionalChain) {
|
||||
return !isOptionalChain(node.parent) || isOptionalChainRoot(node.parent) || node !== node.parent.expression;
|
||||
}
|
||||
|
||||
function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||
doWithConditionalBranches(bind, node, trueTarget, falseTarget);
|
||||
if (!isOptionalChain(node) || isOutermostOptionalChain(node)) {
|
||||
addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
|
||||
addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
|
||||
}
|
||||
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
|
||||
bindEach(node.typeArguments);
|
||||
bindEach(node.arguments);
|
||||
bind(node.expression);
|
||||
}
|
||||
|
||||
function bindOptionalChainRest(node: OptionalChain) {
|
||||
bind(node.questionDotToken);
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
bind(node.name);
|
||||
break;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
bind(node.argumentExpression);
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
bindEach(node.typeArguments);
|
||||
bindEach(node.arguments);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||
// For an optional chain, we emulate the behavior of a logical expression:
|
||||
//
|
||||
// a?.b -> a && a.b
|
||||
// a?.b.c -> a && a.b.c
|
||||
// a?.b?.c -> a && a.b && a.b.c
|
||||
// a?.[x = 1] -> a && a[x = 1]
|
||||
//
|
||||
// To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`)
|
||||
// and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest
|
||||
// of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost
|
||||
// chain node. We then treat the entire node as the right side of the expression.
|
||||
const preChainLabel = node.questionDotToken ? createBranchLabel() : undefined;
|
||||
bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget);
|
||||
if (preChainLabel) {
|
||||
currentFlow = finishFlowLabel(preChainLabel);
|
||||
}
|
||||
doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget);
|
||||
if (isOutermostOptionalChain(node)) {
|
||||
addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
|
||||
addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
|
||||
}
|
||||
}
|
||||
|
||||
function bindOptionalChainFlow(node: OptionalChain) {
|
||||
if (isTopLevelLogicalExpression(node)) {
|
||||
const postExpressionLabel = createBranchLabel();
|
||||
bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
|
||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
}
|
||||
else {
|
||||
bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
|
||||
}
|
||||
}
|
||||
|
||||
function bindAccessExpressionFlow(node: AccessExpression) {
|
||||
if (isOptionalChain(node)) {
|
||||
bindOptionalChainFlow(node);
|
||||
}
|
||||
else {
|
||||
bindEachChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
function bindCallExpressionFlow(node: CallExpression) {
|
||||
if (isOptionalChain(node)) {
|
||||
bindOptionalChainFlow(node);
|
||||
}
|
||||
else {
|
||||
// If the target of the call expression is a function expression or arrow function we have
|
||||
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
|
||||
// the current control flow (which includes evaluation of the IIFE arguments).
|
||||
const expr = skipParentheses(node.expression);
|
||||
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
|
||||
bindEach(node.typeArguments);
|
||||
bindEach(node.arguments);
|
||||
bind(node.expression);
|
||||
}
|
||||
else {
|
||||
bindEachChild(node);
|
||||
}
|
||||
}
|
||||
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const propertyAccess = <PropertyAccessExpression>node.expression;
|
||||
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
|
||||
@@ -3297,6 +3382,10 @@ namespace ts {
|
||||
const callee = skipOuterExpressions(node.expression);
|
||||
const expression = node.expression;
|
||||
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
transformFlags |= TransformFlags.ContainsESNext;
|
||||
}
|
||||
|
||||
if (node.typeArguments) {
|
||||
transformFlags |= TransformFlags.AssertTypeScript;
|
||||
}
|
||||
@@ -3692,6 +3781,10 @@ namespace ts {
|
||||
function computePropertyAccess(node: PropertyAccessExpression, subtreeFlags: TransformFlags) {
|
||||
let transformFlags = subtreeFlags;
|
||||
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
transformFlags |= TransformFlags.ContainsESNext;
|
||||
}
|
||||
|
||||
// If a PropertyAccessExpression starts with a super keyword, then it is
|
||||
// ES6 syntax, and requires a lexical `this` binding.
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
@@ -3707,6 +3800,10 @@ namespace ts {
|
||||
function computeElementAccess(node: ElementAccessExpression, subtreeFlags: TransformFlags) {
|
||||
let transformFlags = subtreeFlags;
|
||||
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
transformFlags |= TransformFlags.ContainsESNext;
|
||||
}
|
||||
|
||||
// If an ElementAccessExpression starts with a super keyword, then it is
|
||||
// ES6 syntax, and requires a lexical `this` binding.
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
|
||||
+159
-46
@@ -333,6 +333,10 @@ namespace ts {
|
||||
/** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
|
||||
let apparentArgumentCount: number | undefined;
|
||||
|
||||
// This object is reused for `checkOptionalExpression` return values to avoid frequent GC due to nursery object allocations.
|
||||
// This object represents a pool-size of 1.
|
||||
const pooledOptionalTypeResult: { isOptional: boolean, type: Type } = { isOptional: false, type: undefined! };
|
||||
|
||||
// for public members that accept a Node or one of its subtypes, we must guard against
|
||||
// synthetic nodes created during transformations by calling `getParseTreeNode`.
|
||||
// for most of these, we perform the guard only on `checker` to avoid any possible
|
||||
@@ -380,8 +384,10 @@ namespace ts {
|
||||
getParameterType: getTypeAtPosition,
|
||||
getPromisedTypeOfPromise,
|
||||
getReturnTypeOfSignature,
|
||||
isNullableType,
|
||||
getNullableType,
|
||||
getNonNullableType,
|
||||
getNonOptionalType: removeOptionalTypeMarker,
|
||||
getTypeArguments,
|
||||
typeToTypeNode: nodeBuilder.typeToTypeNode,
|
||||
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
|
||||
@@ -546,6 +552,7 @@ namespace ts {
|
||||
getNullType: () => nullType,
|
||||
getESSymbolType: () => esSymbolType,
|
||||
getNeverType: () => neverType,
|
||||
getOptionalType: () => optionalType,
|
||||
isSymbolAccessible,
|
||||
getObjectFlags,
|
||||
isArrayType,
|
||||
@@ -648,6 +655,7 @@ namespace ts {
|
||||
const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
|
||||
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
|
||||
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType);
|
||||
const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined");
|
||||
const nullType = createIntrinsicType(TypeFlags.Null, "null");
|
||||
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType);
|
||||
const stringType = createIntrinsicType(TypeFlags.String, "string");
|
||||
@@ -1171,7 +1179,7 @@ namespace ts {
|
||||
// obtain item referenced by 'export='
|
||||
mainModule = resolveExternalModuleSymbol(mainModule);
|
||||
if (mainModule.flags & SymbolFlags.Namespace) {
|
||||
// If we’re merging an augmentation to a pattern ambient module, we want to
|
||||
// If we're merging an augmentation to a pattern ambient module, we want to
|
||||
// perform the merge unidirectionally from the augmentation ('a.foo') to
|
||||
// the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you
|
||||
// all the exports both from the pattern and from the augmentation, but
|
||||
@@ -2657,7 +2665,7 @@ namespace ts {
|
||||
if (patternAmbientModules) {
|
||||
const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference);
|
||||
if (pattern) {
|
||||
// If the module reference matched a pattern ambient module ('*.foo') but there’s also a
|
||||
// If the module reference matched a pattern ambient module ('*.foo') but there's also a
|
||||
// module augmentation by the specific name requested ('a.foo'), we store the merged symbol
|
||||
// by the augmentation name ('a.foo'), because asking for *.foo should not give you exports
|
||||
// from a.foo.
|
||||
@@ -8599,6 +8607,12 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function createOptionalCallSignature(signature: Signature) {
|
||||
const result = cloneSignature(signature);
|
||||
result.isOptionalCall = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function getExpandedParameters(sig: Signature): readonly Symbol[] {
|
||||
if (sig.hasRestParameter) {
|
||||
const restIndex = sig.parameters.length - 1;
|
||||
@@ -10211,6 +10225,9 @@ namespace ts {
|
||||
signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
|
||||
getReturnTypeFromAnnotation(signature.declaration!) ||
|
||||
(nodeIsMissing((<FunctionLikeDeclaration>signature.declaration).body) ? anyType : getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration));
|
||||
if (signature.isOptionalCall) {
|
||||
type = propagateOptionalTypeMarker(type, /*wasOptional*/ true);
|
||||
}
|
||||
if (!popTypeResolution()) {
|
||||
if (signature.declaration) {
|
||||
const typeNode = getEffectiveReturnTypeNode(signature.declaration);
|
||||
@@ -15653,7 +15670,7 @@ namespace ts {
|
||||
for (const sourceProp of excludeProperties(getPropertiesOfType(source), excludedProperties)) {
|
||||
if (!getPropertyOfObjectType(target, sourceProp.escapedName)) {
|
||||
const sourceType = getTypeOfSymbol(sourceProp);
|
||||
if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) {
|
||||
if (!(sourceType === undefinedType || sourceType === undefinedWideningType || sourceType === optionalType)) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target));
|
||||
}
|
||||
@@ -16620,6 +16637,53 @@ namespace ts {
|
||||
return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type;
|
||||
}
|
||||
|
||||
function addOptionalTypeMarker(type: Type) {
|
||||
return strictNullChecks ? getUnionType([type, optionalType]) : type;
|
||||
}
|
||||
|
||||
function removeOptionalTypeMarker(type: Type): Type {
|
||||
return strictNullChecks ? filterType(type, t => t !== optionalType) : type;
|
||||
}
|
||||
|
||||
function propagateOptionalTypeMarker(type: Type, wasOptional: boolean) {
|
||||
return wasOptional ? addOptionalTypeMarker(type) : type;
|
||||
}
|
||||
|
||||
function createPooledOptionalTypeResult(isOptional: boolean, type: Type) {
|
||||
pooledOptionalTypeResult.isOptional = isOptional;
|
||||
pooledOptionalTypeResult.type = type;
|
||||
return pooledOptionalTypeResult;
|
||||
}
|
||||
|
||||
function checkOptionalExpression(
|
||||
parent: PropertyAccessExpression | QualifiedName | ElementAccessExpression | CallExpression,
|
||||
expression: Expression | QualifiedName,
|
||||
nullDiagnostic?: DiagnosticMessage,
|
||||
undefinedDiagnostic?: DiagnosticMessage,
|
||||
nullOrUndefinedDiagnostic?: DiagnosticMessage,
|
||||
) {
|
||||
let isOptional = false;
|
||||
let type = checkExpression(expression);
|
||||
if (isOptionalChain(parent)) {
|
||||
if (parent.questionDotToken) {
|
||||
// If we have a questionDotToken then we are an OptionalExpression and should remove `null` and
|
||||
// `undefined` from the type and add the optionalType to the result, if needed.
|
||||
isOptional = isNullableType(type);
|
||||
return createPooledOptionalTypeResult(isOptional, isOptional ? getNonNullableType(type) : type);
|
||||
}
|
||||
|
||||
// If we do not have a questionDotToken, then we are an OptionalChain and we remove the optionalType and
|
||||
// indicate whether we need to add optionalType back into the result.
|
||||
const nonOptionalType = removeOptionalTypeMarker(type);
|
||||
if (nonOptionalType !== type) {
|
||||
isOptional = true;
|
||||
type = nonOptionalType;
|
||||
}
|
||||
}
|
||||
|
||||
type = checkNonNullType(type, expression, nullDiagnostic, undefinedDiagnostic, nullOrUndefinedDiagnostic);
|
||||
return createPooledOptionalTypeResult(isOptional, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is source potentially coercible to target type under `==`.
|
||||
@@ -18105,7 +18169,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getFlowNodeId(flow: FlowNode): number {
|
||||
if (!flow.id) {
|
||||
if (!flow.id || flow.id < 0) {
|
||||
flow.id = nextFlowId;
|
||||
nextFlowId++;
|
||||
}
|
||||
@@ -18653,7 +18717,7 @@ namespace ts {
|
||||
// circularities in control flow analysis, we use getTypeOfDottedName when resolving the call
|
||||
// target expression of an assertion.
|
||||
const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression, /*diagnostic*/ undefined) :
|
||||
node.expression.kind !== SyntaxKind.SuperKeyword ? checkNonNullExpression(node.expression) :
|
||||
node.expression.kind !== SyntaxKind.SuperKeyword ? checkOptionalExpression(node, node.expression).type :
|
||||
undefined;
|
||||
const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call);
|
||||
const candidate = signatures.length === 1 && !signatures[0].typeParameters ? signatures[0] :
|
||||
@@ -19571,7 +19635,7 @@ namespace ts {
|
||||
|
||||
function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
|
||||
if (hasMatchingArgument(callExpression, reference)) {
|
||||
const signature = getEffectsSignature(callExpression);
|
||||
const signature = assumeTrue || !isCallChain(callExpression) ? getEffectsSignature(callExpression) : undefined;
|
||||
const predicate = signature && getTypePredicateOfSignature(signature);
|
||||
if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) {
|
||||
return narrowTypeByTypePredicate(type, predicate, callExpression, assumeTrue);
|
||||
@@ -19614,6 +19678,10 @@ namespace ts {
|
||||
// Narrow the given type based on the given expression having the assumed boolean value. The returned type
|
||||
// will be a subtype or the same type as the argument.
|
||||
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
|
||||
// for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a`
|
||||
if (isOptionalChainRoot(expr.parent)) {
|
||||
return narrowTypeByOptionality(type, expr, assumeTrue);
|
||||
}
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
@@ -19635,6 +19703,19 @@ namespace ts {
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function narrowTypeByOptionality(type: Type, expr: Expression, assumePresent: boolean): Type {
|
||||
if (isMatchingReference(reference, expr)) {
|
||||
return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull);
|
||||
}
|
||||
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
|
||||
return narrowTypeByDiscriminant(type, <AccessExpression>expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull));
|
||||
}
|
||||
if (containsMatchingReferenceDiscriminant(reference, expr)) {
|
||||
return declaredType;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) {
|
||||
@@ -22505,12 +22586,12 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
function isNullableType(type: Type) {
|
||||
return !!((strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable);
|
||||
}
|
||||
|
||||
function getNonNullableTypeIfNeeded(type: Type) {
|
||||
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
|
||||
if (kind) {
|
||||
return getNonNullableType(type);
|
||||
}
|
||||
return type;
|
||||
return isNullableType(type) ? getNonNullableType(type) : type;
|
||||
}
|
||||
|
||||
function checkNonNullType(
|
||||
@@ -22561,8 +22642,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
|
||||
let propType: Type;
|
||||
const leftType = checkNonNullExpression(left);
|
||||
const { isOptional, type: leftType } = checkOptionalExpression(node, left);
|
||||
const parentSymbol = getNodeLinks(left).resolvedSymbol;
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType);
|
||||
@@ -22576,6 +22656,8 @@ namespace ts {
|
||||
if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) {
|
||||
markAliasReferenced(parentSymbol, node);
|
||||
}
|
||||
|
||||
let propType: Type;
|
||||
if (!prop) {
|
||||
const indexInfo = assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined;
|
||||
if (!(indexInfo && indexInfo.type)) {
|
||||
@@ -22614,7 +22696,7 @@ namespace ts {
|
||||
}
|
||||
propType = getConstraintForLocation(getTypeOfSymbol(prop), node);
|
||||
}
|
||||
return getFlowTypeOfAccessExpression(node, prop, propType, right);
|
||||
return propagateOptionalTypeMarker(getFlowTypeOfAccessExpression(node, prop, propType, right), isOptional);
|
||||
}
|
||||
|
||||
function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) {
|
||||
@@ -22971,9 +23053,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkIndexedAccess(node: ElementAccessExpression): Type {
|
||||
const exprType = checkNonNullExpression(node.expression);
|
||||
const { isOptional, type: exprType } = checkOptionalExpression(node, node.expression);
|
||||
const objectType = getAssignmentTargetKind(node) !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(exprType) : exprType;
|
||||
|
||||
const indexExpression = node.argumentExpression;
|
||||
const indexType = checkExpression(indexExpression);
|
||||
|
||||
@@ -22991,7 +23072,7 @@ namespace ts {
|
||||
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
|
||||
AccessFlags.None;
|
||||
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType;
|
||||
return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node);
|
||||
return propagateOptionalTypeMarker(checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node), isOptional);
|
||||
}
|
||||
|
||||
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
|
||||
@@ -23074,7 +23155,7 @@ namespace ts {
|
||||
// interface B extends A { (x: 'foo'): string }
|
||||
// const b: B;
|
||||
// b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void]
|
||||
function reorderCandidates(signatures: readonly Signature[], result: Signature[]): void {
|
||||
function reorderCandidates(signatures: readonly Signature[], result: Signature[], isOptionalCall: boolean): void {
|
||||
let lastParent: Node | undefined;
|
||||
let lastSymbol: Symbol | undefined;
|
||||
let cutoffIndex = 0;
|
||||
@@ -23116,7 +23197,7 @@ namespace ts {
|
||||
spliceIndex = index;
|
||||
}
|
||||
|
||||
result.splice(spliceIndex, 0, signature);
|
||||
result.splice(spliceIndex, 0, isOptionalCall ? createOptionalCallSignature(signature) : signature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23769,7 +23850,7 @@ namespace ts {
|
||||
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
|
||||
}
|
||||
|
||||
function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage): Signature {
|
||||
function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, isOptionalCall: boolean, fallbackError?: DiagnosticMessage): Signature {
|
||||
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
|
||||
const isDecorator = node.kind === SyntaxKind.Decorator;
|
||||
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
|
||||
@@ -23788,7 +23869,7 @@ namespace ts {
|
||||
|
||||
const candidates = candidatesOutArray || [];
|
||||
// reorderCandidates fills up the candidates array directly
|
||||
reorderCandidates(signatures, candidates);
|
||||
reorderCandidates(signatures, candidates, isOptionalCall);
|
||||
if (!candidates.length) {
|
||||
if (reportErrors) {
|
||||
diagnostics.add(getDiagnosticForCallNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
|
||||
@@ -24172,13 +24253,14 @@ namespace ts {
|
||||
const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
|
||||
if (baseTypeNode) {
|
||||
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
|
||||
return resolveCall(node, baseConstructors, candidatesOutArray, checkMode);
|
||||
return resolveCall(node, baseConstructors, candidatesOutArray, checkMode, /*isOptional*/ false);
|
||||
}
|
||||
}
|
||||
return resolveUntypedCall(node);
|
||||
}
|
||||
|
||||
const funcType = checkNonNullExpression(
|
||||
const { isOptional, type: funcType } = checkOptionalExpression(
|
||||
node,
|
||||
node.expression,
|
||||
Diagnostics.Cannot_invoke_an_object_which_is_possibly_null,
|
||||
Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined,
|
||||
@@ -24188,8 +24270,8 @@ namespace ts {
|
||||
if (funcType === silentNeverType) {
|
||||
return silentNeverSignature;
|
||||
}
|
||||
const apparentType = getApparentType(funcType);
|
||||
|
||||
const apparentType = getApparentType(funcType);
|
||||
if (apparentType === errorType) {
|
||||
// Another error has already been reported
|
||||
return resolveErrorCall(node);
|
||||
@@ -24253,7 +24335,8 @@ namespace ts {
|
||||
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode);
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, isOptional);
|
||||
}
|
||||
|
||||
function isGenericFunctionReturningFunction(signature: Signature) {
|
||||
@@ -24324,7 +24407,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray, checkMode);
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray, checkMode, /*isOptional*/ false);
|
||||
}
|
||||
|
||||
// If expressionType's apparent type is an object type with no construct signatures but
|
||||
@@ -24333,7 +24416,7 @@ namespace ts {
|
||||
// operation is Any. It is an error to have a Void this type.
|
||||
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
|
||||
if (callSignatures.length) {
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode);
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false);
|
||||
if (!noImplicitAny) {
|
||||
if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
|
||||
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
|
||||
@@ -24548,7 +24631,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24611,7 +24694,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, headMessage);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false, headMessage);
|
||||
}
|
||||
|
||||
function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature {
|
||||
@@ -24664,7 +24747,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, signatures, candidatesOutArray, checkMode);
|
||||
return resolveCall(node, signatures, candidatesOutArray, checkMode, /*isOptional*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24851,18 +24934,20 @@ namespace ts {
|
||||
getTypeOfDottedName(node.expression, diagnostic);
|
||||
}
|
||||
}
|
||||
let jsAssignmentType: Type | undefined;
|
||||
|
||||
if (isInJSFile(node)) {
|
||||
const decl = getDeclarationOfExpando(node);
|
||||
if (decl) {
|
||||
const jsSymbol = getSymbolOfNode(decl);
|
||||
if (jsSymbol && hasEntries(jsSymbol.exports)) {
|
||||
jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined);
|
||||
(jsAssignmentType as ObjectType).objectFlags |= ObjectFlags.JSLiteral;
|
||||
const jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined);
|
||||
jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral;
|
||||
return getIntersectionType([returnType, jsAssignmentType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsAssignmentType ? getIntersectionType([returnType, jsAssignmentType]) : returnType;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
function isSymbolOrSymbolForCall(node: Node) {
|
||||
@@ -24969,7 +25054,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
|
||||
checkGrammarTypeArguments(node, node.typeArguments);
|
||||
if (!checkGrammarTaggedTemplateChain(node)) checkGrammarTypeArguments(node, node.typeArguments);
|
||||
if (languageVersion < ScriptTarget.ES2015) {
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject);
|
||||
}
|
||||
@@ -25886,13 +25971,17 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean {
|
||||
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
|
||||
// References are combinations of identifiers, parentheses, and property accesses.
|
||||
const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
|
||||
if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
|
||||
error(expr, invalidReferenceMessage);
|
||||
return false;
|
||||
}
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
error(expr, invalidOptionalChainMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26002,7 +26091,10 @@ namespace ts {
|
||||
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
|
||||
if (ok) {
|
||||
// run check only if former checks succeeded to avoid reporting cascading errors
|
||||
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
|
||||
checkReferenceExpression(
|
||||
node.operand,
|
||||
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
|
||||
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
|
||||
}
|
||||
return getUnaryResultType(operandType);
|
||||
}
|
||||
@@ -26020,7 +26112,10 @@ namespace ts {
|
||||
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
|
||||
if (ok) {
|
||||
// run check only if former checks succeeded to avoid reporting cascading errors
|
||||
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
|
||||
checkReferenceExpression(
|
||||
node.operand,
|
||||
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
|
||||
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
|
||||
}
|
||||
return getUnaryResultType(operandType);
|
||||
}
|
||||
@@ -26270,7 +26365,10 @@ namespace ts {
|
||||
const error = target.parent.kind === SyntaxKind.SpreadAssignment ?
|
||||
Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access :
|
||||
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access;
|
||||
if (checkReferenceExpression(target, error)) {
|
||||
const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ?
|
||||
Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access :
|
||||
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access;
|
||||
if (checkReferenceExpression(target, error, optionalError)) {
|
||||
checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target);
|
||||
}
|
||||
return sourceType;
|
||||
@@ -26476,7 +26574,7 @@ namespace ts {
|
||||
|
||||
if (!resultType) {
|
||||
// Types that have a reasonably good chance of being a valid operand type.
|
||||
// If both types have an awaited type of one of these, we’ll assume the user
|
||||
// If both types have an awaited type of one of these, we'll assume the user
|
||||
// might be missing an await without doing an exhaustive check that inserting
|
||||
// await(s) will actually be a completely valid binary expression.
|
||||
const closeEnoughKind = TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.AnyOrUnknown;
|
||||
@@ -26614,7 +26712,9 @@ namespace ts {
|
||||
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
|
||||
// and the type of the non-compound operation to be assignable to the type of VarExpr.
|
||||
|
||||
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
|
||||
if (checkReferenceExpression(left,
|
||||
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access,
|
||||
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
|
||||
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
|
||||
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
|
||||
checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right);
|
||||
@@ -27113,11 +27213,11 @@ namespace ts {
|
||||
const expr = skipParentheses(node);
|
||||
// Optimize for the common case of a call to a function with a single non-generic call
|
||||
// signature where we can just fetch the return type without checking the arguments.
|
||||
if (expr.kind === SyntaxKind.CallExpression && (<CallExpression>expr).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
|
||||
const funcType = checkNonNullExpression((<CallExpression>expr).expression);
|
||||
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
|
||||
const { isOptional, type: funcType } = checkOptionalExpression(expr, expr.expression);
|
||||
const signature = getSingleCallSignature(funcType);
|
||||
if (signature && !signature.typeParameters) {
|
||||
return getReturnTypeOfSignature(signature);
|
||||
return propagateOptionalTypeMarker(getReturnTypeOfSignature(signature), isOptional);
|
||||
}
|
||||
}
|
||||
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
|
||||
@@ -29958,7 +30058,10 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const leftType = checkExpression(varExpr);
|
||||
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access);
|
||||
checkReferenceExpression(
|
||||
varExpr,
|
||||
Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access,
|
||||
Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access);
|
||||
|
||||
// iteratedType will be undefined if the rightType was missing properties/signatures
|
||||
// required to get its iteratedType (like [Symbol.iterator] or next). This may be
|
||||
@@ -30008,7 +30111,10 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
// run check only former check succeeded to avoid cascading errors
|
||||
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access);
|
||||
checkReferenceExpression(
|
||||
varExpr,
|
||||
Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access,
|
||||
Diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34799,6 +34905,13 @@ namespace ts {
|
||||
checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
|
||||
}
|
||||
|
||||
function checkGrammarTaggedTemplateChain(node: TaggedTemplateExpression): boolean {
|
||||
if (node.questionDotToken || node.flags & NodeFlags.OptionalChain) {
|
||||
return grammarErrorOnNode(node.template, Diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkGrammarForOmittedArgument(args: NodeArray<Expression> | undefined): boolean {
|
||||
if (args) {
|
||||
for (const arg of args) {
|
||||
|
||||
@@ -187,6 +187,14 @@ namespace ts {
|
||||
assertNode)
|
||||
: noop;
|
||||
|
||||
export const assertNotNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
|
||||
test === undefined || !test(node),
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`,
|
||||
assertNode)
|
||||
: noop;
|
||||
|
||||
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
|
||||
test === undefined || node === undefined || test(node),
|
||||
@@ -260,5 +268,6 @@ namespace ts {
|
||||
|
||||
isDebugInfoEnabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1039,6 +1039,10 @@
|
||||
"category": "Error",
|
||||
"code": 1357
|
||||
},
|
||||
"Tagged template expressions are not permitted in an optional chain.": {
|
||||
"category": "Error",
|
||||
"code": 1358
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
@@ -2747,6 +2751,27 @@
|
||||
"category": "Error",
|
||||
"code": 2776
|
||||
},
|
||||
"The operand of an increment or decrement operator may not be an optional property access.": {
|
||||
"category": "Error",
|
||||
"code": 2777
|
||||
},
|
||||
"The target of an object rest assignment may not be an optional property access.": {
|
||||
"category": "Error",
|
||||
"code": 2778
|
||||
},
|
||||
"The left-hand side of an assignment expression may not be an optional property access.": {
|
||||
"category": "Error",
|
||||
"code": 2779
|
||||
},
|
||||
"The left-hand side of a 'for...in' statement may not be an optional property access.": {
|
||||
"category": "Error",
|
||||
"code": 2780
|
||||
},
|
||||
"The left-hand side of a 'for...of' statement may not be an optional property access.": {
|
||||
"category": "Error",
|
||||
"code": 2781
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4000
|
||||
|
||||
+69
-41
@@ -858,6 +858,8 @@ namespace ts {
|
||||
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined;
|
||||
let hasWrittenComment = false;
|
||||
let commentsDisabled = !!printerOptions.removeComments;
|
||||
let lastNode: Node | undefined;
|
||||
let lastSubstitution: Node | undefined;
|
||||
const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment");
|
||||
|
||||
reset();
|
||||
@@ -1080,8 +1082,7 @@ namespace ts {
|
||||
setSourceFile(sourceFile);
|
||||
}
|
||||
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
|
||||
pipelinePhase(hint, node);
|
||||
pipelineEmit(hint, node);
|
||||
}
|
||||
|
||||
function setSourceFile(sourceFile: SourceFile | undefined) {
|
||||
@@ -1113,6 +1114,8 @@ namespace ts {
|
||||
currentSourceFile = undefined!;
|
||||
currentLineMap = undefined!;
|
||||
detachedCommentsInfo = undefined;
|
||||
lastNode = undefined;
|
||||
lastSubstitution = undefined;
|
||||
setWriter(/*output*/ undefined, /*_sourceMapGenerator*/ undefined);
|
||||
}
|
||||
|
||||
@@ -1120,24 +1123,47 @@ namespace ts {
|
||||
return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!));
|
||||
}
|
||||
|
||||
function emit(node: Node): Node;
|
||||
function emit(node: Node | undefined): Node | undefined;
|
||||
function emit(node: Node | undefined) {
|
||||
if (node === undefined) return;
|
||||
|
||||
const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node);
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
|
||||
pipelinePhase(EmitHint.Unspecified, node);
|
||||
const substitute = pipelineEmit(EmitHint.Unspecified, node);
|
||||
recordBundleFileInternalSectionEnd(prevSourceFileTextKind);
|
||||
return substitute;
|
||||
}
|
||||
|
||||
function emitIdentifierName(node: Identifier | undefined) {
|
||||
function emitIdentifierName(node: Identifier): Node;
|
||||
function emitIdentifierName(node: Identifier | undefined): Node | undefined;
|
||||
function emitIdentifierName(node: Identifier | undefined): Node | undefined {
|
||||
if (node === undefined) return;
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
|
||||
pipelinePhase(EmitHint.IdentifierName, node);
|
||||
return pipelineEmit(EmitHint.IdentifierName, node);
|
||||
}
|
||||
|
||||
function emitExpression(node: Expression | undefined) {
|
||||
function emitExpression(node: Expression): Node;
|
||||
function emitExpression(node: Expression | undefined): Node | undefined;
|
||||
function emitExpression(node: Expression | undefined): Node | undefined {
|
||||
if (node === undefined) return;
|
||||
return pipelineEmit(EmitHint.Expression, node);
|
||||
}
|
||||
|
||||
function pipelineEmit(emitHint: EmitHint, node: Node) {
|
||||
const savedLastNode = lastNode;
|
||||
const savedLastSubstitution = lastSubstitution;
|
||||
lastNode = node;
|
||||
lastSubstitution = undefined;
|
||||
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
|
||||
pipelinePhase(EmitHint.Expression, node);
|
||||
pipelinePhase(emitHint, node);
|
||||
|
||||
Debug.assert(lastNode === node);
|
||||
|
||||
const substitute = lastSubstitution;
|
||||
lastNode = savedLastNode;
|
||||
lastSubstitution = savedLastSubstitution;
|
||||
|
||||
return substitute || node;
|
||||
}
|
||||
|
||||
function getPipelinePhase(phase: PipelinePhase, node: Node) {
|
||||
@@ -1179,11 +1205,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
|
||||
Debug.assert(lastNode === node);
|
||||
const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, node);
|
||||
onEmitNode(hint, node, pipelinePhase);
|
||||
Debug.assert(lastNode === node);
|
||||
}
|
||||
|
||||
function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile));
|
||||
if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier));
|
||||
if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
|
||||
@@ -1495,7 +1524,7 @@ namespace ts {
|
||||
if (isExpression(node)) {
|
||||
hint = EmitHint.Expression;
|
||||
if (substituteNode !== noEmitSubstitution) {
|
||||
node = substituteNode(hint, node);
|
||||
lastSubstitution = node = substituteNode(hint, node);
|
||||
}
|
||||
}
|
||||
else if (isToken(node)) {
|
||||
@@ -1611,8 +1640,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) {
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, node);
|
||||
pipelinePhase(hint, substituteNode(hint, node));
|
||||
lastSubstitution = substituteNode(hint, node);
|
||||
pipelinePhase(hint, lastSubstitution);
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
}
|
||||
|
||||
function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined {
|
||||
@@ -2116,8 +2148,7 @@ namespace ts {
|
||||
}
|
||||
writePunctuation("[");
|
||||
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node.typeParameter);
|
||||
pipelinePhase(EmitHint.MappedTypeParameter, node.typeParameter);
|
||||
pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter);
|
||||
|
||||
writePunctuation("]");
|
||||
if (node.questionToken) {
|
||||
@@ -2215,34 +2246,24 @@ namespace ts {
|
||||
}
|
||||
|
||||
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
|
||||
let indentBeforeDot = false;
|
||||
let indentAfterDot = false;
|
||||
const dotRangeFirstCommentStart = skipTrivia(
|
||||
currentSourceFile!.text,
|
||||
node.expression.end,
|
||||
/*stopAfterLineBreak*/ false,
|
||||
/*stopAtComments*/ true
|
||||
);
|
||||
const dotRangeStart = skipTrivia(currentSourceFile!.text, dotRangeFirstCommentStart);
|
||||
const dotRangeEnd = dotRangeStart + 1;
|
||||
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
|
||||
const dotToken = createToken(SyntaxKind.DotToken);
|
||||
dotToken.pos = node.expression.end;
|
||||
dotToken.end = dotRangeEnd;
|
||||
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
|
||||
indentAfterDot = needsIndentation(node, dotToken, node.name);
|
||||
}
|
||||
const expression = cast(emitExpression(node.expression), isExpression);
|
||||
const token = getDotOrQuestionDotToken(node);
|
||||
const indentBeforeDot = needsIndentation(node, node.expression, token);
|
||||
const indentAfterDot = needsIndentation(node, token, node.name);
|
||||
|
||||
emitExpression(node.expression);
|
||||
increaseIndentIf(indentBeforeDot, /*writeSpaceIfNotIndenting*/ false);
|
||||
|
||||
const dotHasCommentTrivia = dotRangeFirstCommentStart !== dotRangeStart;
|
||||
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression, dotHasCommentTrivia);
|
||||
const shouldEmitDotDot =
|
||||
token.kind !== SyntaxKind.QuestionDotToken &&
|
||||
mayNeedDotDotForPropertyAccess(expression) &&
|
||||
!writer.hasTrailingComment() &&
|
||||
!writer.hasTrailingWhitespace();
|
||||
|
||||
if (shouldEmitDotDot) {
|
||||
writePunctuation(".");
|
||||
}
|
||||
emitTokenWithComment(SyntaxKind.DotToken, node.expression.end, writePunctuation, node);
|
||||
|
||||
emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node);
|
||||
increaseIndentIf(indentAfterDot, /*writeSpaceIfNotIndenting*/ false);
|
||||
emit(node.name);
|
||||
decreaseIndentIf(indentBeforeDot, indentAfterDot);
|
||||
@@ -2250,28 +2271,27 @@ namespace ts {
|
||||
|
||||
// 1..toString is a valid property access, emit a dot after the literal
|
||||
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
|
||||
function needsDotDotForPropertyAccess(expression: Expression, dotHasTrivia: boolean) {
|
||||
function mayNeedDotDotForPropertyAccess(expression: Expression) {
|
||||
expression = skipPartiallyEmittedExpressions(expression);
|
||||
if (isNumericLiteral(expression)) {
|
||||
// check if numeric literal is a decimal literal that was originally written with a dot
|
||||
const text = getLiteralTextOfNode(<LiteralExpression>expression, /*neverAsciiEscape*/ true);
|
||||
// If he number will be printed verbatim and it doesn't already contain a dot, add one
|
||||
// if the expression doesn't have any comments that will be emitted.
|
||||
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!) &&
|
||||
(!dotHasTrivia || printerOptions.removeComments);
|
||||
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
|
||||
}
|
||||
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
|
||||
// check if constant enum value is integer
|
||||
const constantValue = getConstantValue(expression);
|
||||
// isFinite handles cases when constantValue is undefined
|
||||
return typeof constantValue === "number" && isFinite(constantValue)
|
||||
&& Math.floor(constantValue) === constantValue
|
||||
&& printerOptions.removeComments;
|
||||
&& Math.floor(constantValue) === constantValue;
|
||||
}
|
||||
}
|
||||
|
||||
function emitElementAccessExpression(node: ElementAccessExpression) {
|
||||
emitExpression(node.expression);
|
||||
emit(node.questionDotToken);
|
||||
emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node);
|
||||
emitExpression(node.argumentExpression);
|
||||
emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node);
|
||||
@@ -2279,6 +2299,7 @@ namespace ts {
|
||||
|
||||
function emitCallExpression(node: CallExpression) {
|
||||
emitExpression(node.expression);
|
||||
emit(node.questionDotToken);
|
||||
emitTypeArguments(node, node.typeArguments);
|
||||
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
|
||||
}
|
||||
@@ -3742,8 +3763,7 @@ namespace ts {
|
||||
writeLine();
|
||||
increaseIndent();
|
||||
if (isEmptyStatement(node)) {
|
||||
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
|
||||
pipelinePhase(EmitHint.EmbeddedStatement, node);
|
||||
pipelineEmit(EmitHint.EmbeddedStatement, node);
|
||||
}
|
||||
else {
|
||||
emit(node);
|
||||
@@ -4214,6 +4234,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
function needsIndentation(parent: Node, node1: Node, node2: Node): boolean {
|
||||
if (getEmitFlags(parent) & EmitFlags.NoIndentation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parent = skipSynthesizedParentheses(parent);
|
||||
node1 = skipSynthesizedParentheses(node1);
|
||||
node2 = skipSynthesizedParentheses(node2);
|
||||
@@ -4669,6 +4693,7 @@ namespace ts {
|
||||
// Comments
|
||||
|
||||
function pipelineEmitWithComments(hint: EmitHint, node: Node) {
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
enterComment();
|
||||
hasWrittenComment = false;
|
||||
const emitFlags = getEmitFlags(node);
|
||||
@@ -4735,6 +4760,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
exitComment();
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
}
|
||||
|
||||
function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
|
||||
@@ -4981,6 +5007,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) {
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
const pipelinePhase = getNextPipelinePhase(PipelinePhase.SourceMaps, node);
|
||||
if (isUnparsedSource(node) || isUnparsedPrepend(node)) {
|
||||
pipelinePhase(hint, node);
|
||||
@@ -5023,6 +5050,7 @@ namespace ts {
|
||||
emitSourcePos(source, end);
|
||||
}
|
||||
}
|
||||
Debug.assert(lastNode === node || lastSubstitution === node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1065,6 +1065,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a PropertyAccessChain using updatePropertyAccess. Use updatePropertyAccessChain instead.");
|
||||
// Because we are updating existed propertyAccess we want to inherit its emitFlags
|
||||
// instead of using the default from createPropertyAccess
|
||||
return node.expression !== expression
|
||||
@@ -1073,6 +1074,27 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier) {
|
||||
const node = <PropertyAccessChain>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
|
||||
node.flags |= NodeFlags.OptionalChain;
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.questionDotToken = questionDotToken;
|
||||
node.name = asName(name);
|
||||
setEmitFlags(node, EmitFlags.NoIndentation);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier) {
|
||||
Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update a PropertyAccessExpression using updatePropertyAccessChain. Use updatePropertyAccess instead.");
|
||||
// Because we are updating an existing PropertyAccessChain we want to inherit its emitFlags
|
||||
// instead of using the default from createPropertyAccess
|
||||
return node.expression !== expression
|
||||
|| node.questionDotToken !== questionDotToken
|
||||
|| node.name !== name
|
||||
? updateNode(setEmitFlags(createPropertyAccessChain(expression, questionDotToken, name), getEmitFlags(node)), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createElementAccess(expression: Expression, index: number | Expression) {
|
||||
const node = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
@@ -1081,12 +1103,31 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update an ElementAccessChain using updateElementAccess. Use updateElementAccessChain instead.");
|
||||
return node.expression !== expression
|
||||
|| node.argumentExpression !== argumentExpression
|
||||
? updateNode(createElementAccess(expression, argumentExpression), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression) {
|
||||
const node = <ElementAccessChain>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
|
||||
node.flags |= NodeFlags.OptionalChain;
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.questionDotToken = questionDotToken;
|
||||
node.argumentExpression = asExpression(index);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateElementAccessChain(node: ElementAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression) {
|
||||
Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update an ElementAccessExpression using updateElementAccessChain. Use updateElementAccess instead.");
|
||||
return node.expression !== expression
|
||||
|| node.questionDotToken !== questionDotToken
|
||||
|| node.argumentExpression !== argumentExpression
|
||||
? updateNode(createElementAccessChain(expression, questionDotToken, argumentExpression), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createCall(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) {
|
||||
const node = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
@@ -1096,6 +1137,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updateCall(node: CallExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a CallChain using updateCall. Use updateCallChain instead.");
|
||||
return node.expression !== expression
|
||||
|| node.typeArguments !== typeArguments
|
||||
|| node.arguments !== argumentsArray
|
||||
@@ -1103,6 +1145,26 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createCallChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) {
|
||||
const node = <CallChain>createSynthesizedNode(SyntaxKind.CallExpression);
|
||||
node.flags |= NodeFlags.OptionalChain;
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.questionDotToken = questionDotToken;
|
||||
node.typeArguments = asNodeArray(typeArguments);
|
||||
node.arguments = parenthesizeListElements(createNodeArray(argumentsArray));
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateCallChain(node: CallChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]) {
|
||||
Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update a CallExpression using updateCallChain. Use updateCall instead.");
|
||||
return node.expression !== expression
|
||||
|| node.questionDotToken !== questionDotToken
|
||||
|| node.typeArguments !== typeArguments
|
||||
|| node.arguments !== argumentsArray
|
||||
? updateNode(createCallChain(expression, questionDotToken, typeArguments, argumentsArray), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createNew(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) {
|
||||
const node = <NewExpression>createSynthesizedNode(SyntaxKind.NewExpression);
|
||||
node.expression = parenthesizeForNew(expression);
|
||||
@@ -2776,6 +2838,22 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSyntheticReferenceExpression(expression: Expression, thisArg: Expression) {
|
||||
const node = <SyntheticReferenceExpression>createSynthesizedNode(SyntaxKind.SyntheticReferenceExpression);
|
||||
node.expression = expression;
|
||||
node.thisArg = thisArg;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function updateSyntheticReferenceExpression(node: SyntheticReferenceExpression, expression: Expression, thisArg: Expression) {
|
||||
return node.expression !== expression
|
||||
|| node.thisArg !== thisArg
|
||||
? updateNode(createSyntheticReferenceExpression(expression, thisArg), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createBundle(sourceFiles: readonly SourceFile[], prepends: readonly (UnparsedSource | InputFiles)[] = emptyArray) {
|
||||
const node = <Bundle>createNode(SyntaxKind.Bundle);
|
||||
node.prepends = prepends;
|
||||
|
||||
+109
-46
@@ -212,17 +212,21 @@ namespace ts {
|
||||
return visitNodes(cbNode, cbNodes, (<ObjectLiteralExpression>node).properties);
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return visitNode(cbNode, (<PropertyAccessExpression>node).expression) ||
|
||||
visitNode(cbNode, (<PropertyAccessExpression>node).questionDotToken) ||
|
||||
visitNode(cbNode, (<PropertyAccessExpression>node).name);
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return visitNode(cbNode, (<ElementAccessExpression>node).expression) ||
|
||||
visitNode(cbNode, (<ElementAccessExpression>node).questionDotToken) ||
|
||||
visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression);
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
return visitNode(cbNode, (<CallExpression>node).expression) ||
|
||||
visitNode(cbNode, (<CallExpression>node).questionDotToken) ||
|
||||
visitNodes(cbNode, cbNodes, (<CallExpression>node).typeArguments) ||
|
||||
visitNodes(cbNode, cbNodes, (<CallExpression>node).arguments);
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return visitNode(cbNode, (<TaggedTemplateExpression>node).tag) ||
|
||||
visitNode(cbNode, (<TaggedTemplateExpression>node).questionDotToken) ||
|
||||
visitNodes(cbNode, cbNodes, (<TaggedTemplateExpression>node).typeArguments) ||
|
||||
visitNode(cbNode, (<TaggedTemplateExpression>node).template);
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
@@ -4243,7 +4247,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Now, we *may* be complete. However, we might have consumed the start of a
|
||||
// CallExpression. As such, we need to consume the rest of it here to be complete.
|
||||
// CallExpression or OptionalExpression. As such, we need to consume the rest
|
||||
// of it here to be complete.
|
||||
return parseCallExpressionRest(expression);
|
||||
}
|
||||
|
||||
@@ -4296,7 +4301,7 @@ namespace ts {
|
||||
// Because CallExpression and MemberExpression are left recursive, we need to bottom out
|
||||
// of the recursion immediately. So we parse out a primary expression to start with.
|
||||
const expression = parsePrimaryExpression();
|
||||
return parseMemberExpressionRest(expression);
|
||||
return parseMemberExpressionRest(expression, /*allowOptionalChain*/ true);
|
||||
}
|
||||
|
||||
function parseSuperExpression(): MemberExpression {
|
||||
@@ -4590,18 +4595,70 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression {
|
||||
function nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate() {
|
||||
nextToken();
|
||||
return tokenIsIdentifierOrKeyword(token())
|
||||
|| token() === SyntaxKind.OpenBracketToken
|
||||
|| isTemplateStartOfTaggedTemplate();
|
||||
}
|
||||
|
||||
function isStartOfOptionalPropertyOrElementAccessChain() {
|
||||
return token() === SyntaxKind.QuestionDotToken
|
||||
&& lookAhead(nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate);
|
||||
}
|
||||
|
||||
function parsePropertyAccessExpressionRest(expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) {
|
||||
const propertyAccess = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
|
||||
propertyAccess.expression = expression;
|
||||
propertyAccess.questionDotToken = questionDotToken;
|
||||
propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
|
||||
if (questionDotToken || expression.flags & NodeFlags.OptionalChain) {
|
||||
propertyAccess.flags |= NodeFlags.OptionalChain;
|
||||
}
|
||||
return finishNode(propertyAccess);
|
||||
}
|
||||
|
||||
function parseElementAccessExpressionRest(expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) {
|
||||
const indexedAccess = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, expression.pos);
|
||||
indexedAccess.expression = expression;
|
||||
indexedAccess.questionDotToken = questionDotToken;
|
||||
|
||||
if (token() === SyntaxKind.CloseBracketToken) {
|
||||
indexedAccess.argumentExpression = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.An_element_access_expression_should_take_an_argument);
|
||||
}
|
||||
else {
|
||||
const argument = allowInAnd(parseExpression);
|
||||
if (isStringOrNumericLiteralLike(argument)) {
|
||||
argument.text = internIdentifier(argument.text);
|
||||
}
|
||||
indexedAccess.argumentExpression = argument;
|
||||
}
|
||||
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
if (questionDotToken || expression.flags & NodeFlags.OptionalChain) {
|
||||
indexedAccess.flags |= NodeFlags.OptionalChain;
|
||||
}
|
||||
return finishNode(indexedAccess);
|
||||
}
|
||||
|
||||
function parseMemberExpressionRest(expression: LeftHandSideExpression, allowOptionalChain: boolean): MemberExpression {
|
||||
while (true) {
|
||||
const dotToken = parseOptionalToken(SyntaxKind.DotToken);
|
||||
if (dotToken) {
|
||||
const propertyAccess = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
|
||||
propertyAccess.expression = expression;
|
||||
propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
|
||||
expression = finishNode(propertyAccess);
|
||||
let questionDotToken: QuestionDotToken | undefined;
|
||||
let isPropertyAccess = false;
|
||||
if (allowOptionalChain && isStartOfOptionalPropertyOrElementAccessChain()) {
|
||||
questionDotToken = parseExpectedToken(SyntaxKind.QuestionDotToken);
|
||||
isPropertyAccess = tokenIsIdentifierOrKeyword(token());
|
||||
}
|
||||
else {
|
||||
isPropertyAccess = parseOptional(SyntaxKind.DotToken);
|
||||
}
|
||||
|
||||
if (isPropertyAccess) {
|
||||
expression = parsePropertyAccessExpressionRest(expression, questionDotToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
|
||||
if (!questionDotToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
|
||||
nextToken();
|
||||
const nonNullExpression = <NonNullExpression>createNode(SyntaxKind.NonNullExpression, expression.pos);
|
||||
nonNullExpression.expression = expression;
|
||||
@@ -4610,28 +4667,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
|
||||
if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) {
|
||||
const indexedAccess = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, expression.pos);
|
||||
indexedAccess.expression = expression;
|
||||
|
||||
if (token() === SyntaxKind.CloseBracketToken) {
|
||||
indexedAccess.argumentExpression = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.An_element_access_expression_should_take_an_argument);
|
||||
}
|
||||
else {
|
||||
const argument = allowInAnd(parseExpression);
|
||||
if (isStringOrNumericLiteralLike(argument)) {
|
||||
argument.text = internIdentifier(argument.text);
|
||||
}
|
||||
indexedAccess.argumentExpression = argument;
|
||||
}
|
||||
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
expression = finishNode(indexedAccess);
|
||||
if ((questionDotToken || !inDecoratorContext()) && parseOptional(SyntaxKind.OpenBracketToken)) {
|
||||
expression = parseElementAccessExpressionRest(expression, questionDotToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isTemplateStartOfTaggedTemplate()) {
|
||||
expression = parseTaggedTemplateRest(expression, /*typeArguments*/ undefined);
|
||||
expression = parseTaggedTemplateRest(expression, questionDotToken, /*typeArguments*/ undefined);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4643,19 +4685,25 @@ namespace ts {
|
||||
return token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead;
|
||||
}
|
||||
|
||||
function parseTaggedTemplateRest(tag: LeftHandSideExpression, typeArguments: NodeArray<TypeNode> | undefined) {
|
||||
function parseTaggedTemplateRest(tag: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined, typeArguments: NodeArray<TypeNode> | undefined) {
|
||||
const tagExpression = <TaggedTemplateExpression>createNode(SyntaxKind.TaggedTemplateExpression, tag.pos);
|
||||
tagExpression.tag = tag;
|
||||
tagExpression.questionDotToken = questionDotToken;
|
||||
tagExpression.typeArguments = typeArguments;
|
||||
tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral
|
||||
? <NoSubstitutionTemplateLiteral>parseLiteralNode()
|
||||
: parseTemplateExpression();
|
||||
if (questionDotToken || tag.flags & NodeFlags.OptionalChain) {
|
||||
tagExpression.flags |= NodeFlags.OptionalChain;
|
||||
}
|
||||
return finishNode(tagExpression);
|
||||
}
|
||||
|
||||
function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
|
||||
while (true) {
|
||||
expression = parseMemberExpressionRest(expression);
|
||||
expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ true);
|
||||
const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken);
|
||||
|
||||
// handle 'foo<<T>()'
|
||||
if (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken) {
|
||||
// See if this is the start of a generic invocation. If so, consume it and
|
||||
@@ -4663,32 +4711,47 @@ namespace ts {
|
||||
// part of an arithmetic expression. Break out so we consume it higher in the
|
||||
// stack.
|
||||
const typeArguments = tryParse(parseTypeArgumentsInExpression);
|
||||
if (!typeArguments) {
|
||||
return expression;
|
||||
}
|
||||
if (typeArguments) {
|
||||
if (isTemplateStartOfTaggedTemplate()) {
|
||||
expression = parseTaggedTemplateRest(expression, questionDotToken, typeArguments);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isTemplateStartOfTaggedTemplate()) {
|
||||
expression = parseTaggedTemplateRest(expression, typeArguments);
|
||||
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
|
||||
callExpr.expression = expression;
|
||||
callExpr.questionDotToken = questionDotToken;
|
||||
callExpr.typeArguments = typeArguments;
|
||||
callExpr.arguments = parseArgumentList();
|
||||
if (questionDotToken || expression.flags & NodeFlags.OptionalChain) {
|
||||
callExpr.flags |= NodeFlags.OptionalChain;
|
||||
}
|
||||
expression = finishNode(callExpr);
|
||||
continue;
|
||||
}
|
||||
|
||||
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
|
||||
callExpr.expression = expression;
|
||||
callExpr.typeArguments = typeArguments;
|
||||
callExpr.arguments = parseArgumentList();
|
||||
expression = finishNode(callExpr);
|
||||
continue;
|
||||
}
|
||||
else if (token() === SyntaxKind.OpenParenToken) {
|
||||
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
|
||||
callExpr.expression = expression;
|
||||
callExpr.questionDotToken = questionDotToken;
|
||||
callExpr.arguments = parseArgumentList();
|
||||
if (questionDotToken || expression.flags & NodeFlags.OptionalChain) {
|
||||
callExpr.flags |= NodeFlags.OptionalChain;
|
||||
}
|
||||
expression = finishNode(callExpr);
|
||||
continue;
|
||||
}
|
||||
|
||||
return expression;
|
||||
if (questionDotToken) {
|
||||
// We failed to parse anything, so report a missing identifier here.
|
||||
const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos) as PropertyAccessExpression;
|
||||
propertyAccess.expression = expression;
|
||||
propertyAccess.questionDotToken = questionDotToken;
|
||||
propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected);
|
||||
propertyAccess.flags |= NodeFlags.OptionalChain;
|
||||
expression = finishNode(propertyAccess);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
function parseArgumentList() {
|
||||
@@ -4957,12 +5020,12 @@ namespace ts {
|
||||
let expression: MemberExpression = parsePrimaryExpression();
|
||||
let typeArguments;
|
||||
while (true) {
|
||||
expression = parseMemberExpressionRest(expression);
|
||||
expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ false);
|
||||
typeArguments = tryParse(parseTypeArgumentsInExpression);
|
||||
if (isTemplateStartOfTaggedTemplate()) {
|
||||
Debug.assert(!!typeArguments,
|
||||
"Expected a type argument list; all plain tagged template starts should be consumed in 'parseMemberExpressionRest'");
|
||||
expression = parseTaggedTemplateRest(expression, typeArguments);
|
||||
expression = parseTaggedTemplateRest(expression, /*optionalChain*/ undefined, typeArguments);
|
||||
typeArguments = undefined;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -184,6 +184,7 @@ namespace ts {
|
||||
"&&": SyntaxKind.AmpersandAmpersandToken,
|
||||
"||": SyntaxKind.BarBarToken,
|
||||
"?": SyntaxKind.QuestionToken,
|
||||
"?.": SyntaxKind.QuestionDotToken,
|
||||
":": SyntaxKind.ColonToken,
|
||||
"=": SyntaxKind.EqualsToken,
|
||||
"+=": SyntaxKind.PlusEqualsToken,
|
||||
@@ -1829,6 +1830,10 @@ namespace ts {
|
||||
return token = SyntaxKind.GreaterThanToken;
|
||||
case CharacterCodes.question:
|
||||
pos++;
|
||||
if (text.charCodeAt(pos) === CharacterCodes.dot && !isDigit(text.charCodeAt(pos + 1))) {
|
||||
pos++;
|
||||
return token = SyntaxKind.QuestionDotToken;
|
||||
}
|
||||
return token = SyntaxKind.QuestionToken;
|
||||
case CharacterCodes.openBracket:
|
||||
pos++;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
/*@internal*/
|
||||
namespace ts {
|
||||
export function transformESNext(context: TransformationContext) {
|
||||
const {
|
||||
hoistVariableDeclaration
|
||||
} = context;
|
||||
|
||||
return chainBundle(transformSourceFile);
|
||||
|
||||
function transformSourceFile(node: SourceFile) {
|
||||
@@ -16,9 +20,157 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
case SyntaxKind.CallExpression:
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false);
|
||||
Debug.assertNotNode(updated, isSyntheticReference);
|
||||
return updated;
|
||||
}
|
||||
// falls through
|
||||
default:
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
}
|
||||
|
||||
function flattenChain(chain: OptionalChain) {
|
||||
const links: OptionalChain[] = [chain];
|
||||
while (!chain.questionDotToken && !isTaggedTemplateExpression(chain)) {
|
||||
chain = cast(chain.expression, isOptionalChain);
|
||||
links.unshift(chain);
|
||||
}
|
||||
return { expression: chain.expression, chain: links };
|
||||
}
|
||||
|
||||
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean): Expression {
|
||||
const expression = visitNonOptionalExpression(node.expression, captureThisArg);
|
||||
if (isSyntheticReference(expression)) {
|
||||
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
|
||||
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
|
||||
return createSyntheticReferenceExpression(updateParen(node, expression.expression), expression.thisArg);
|
||||
}
|
||||
return updateParen(node, expression);
|
||||
}
|
||||
|
||||
function visitNonOptionalPropertyAccessExpression(node: PropertyAccessExpression, captureThisArg: boolean): Expression {
|
||||
if (isOptionalChain(node)) {
|
||||
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
|
||||
return visitOptionalExpression(node, captureThisArg);
|
||||
}
|
||||
|
||||
let expression = visitNode(node.expression, visitor, isExpression);
|
||||
Debug.assertNotNode(expression, isSyntheticReference);
|
||||
|
||||
let thisArg: Expression | undefined;
|
||||
if (captureThisArg) {
|
||||
// `a.b` -> { expression: `(_a = a).b`, thisArg: `_a` }
|
||||
thisArg = createTempVariable(hoistVariableDeclaration);
|
||||
expression = createParen(createAssignment(thisArg, expression));
|
||||
}
|
||||
|
||||
expression = updatePropertyAccess(node, expression, visitNode(node.name, visitor, isIdentifier));
|
||||
return thisArg ? createSyntheticReferenceExpression(expression, thisArg) : expression;
|
||||
}
|
||||
|
||||
function visitNonOptionalElementAccessExpression(node: ElementAccessExpression, captureThisArg: boolean): Expression {
|
||||
if (isOptionalChain(node)) {
|
||||
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
|
||||
return visitOptionalExpression(node, captureThisArg);
|
||||
}
|
||||
|
||||
let expression = visitNode(node.expression, visitor, isExpression);
|
||||
Debug.assertNotNode(expression, isSyntheticReference);
|
||||
|
||||
let thisArg: Expression | undefined;
|
||||
if (captureThisArg) {
|
||||
// `a[b]` -> { expression: `(_a = a)[b]`, thisArg: `_a` }
|
||||
thisArg = createTempVariable(hoistVariableDeclaration);
|
||||
expression = createParen(createAssignment(thisArg, expression));
|
||||
}
|
||||
|
||||
expression = updateElementAccess(node, expression, visitNode(node.argumentExpression, visitor, isExpression));
|
||||
return thisArg ? createSyntheticReferenceExpression(expression, thisArg) : expression;
|
||||
}
|
||||
|
||||
function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression {
|
||||
if (isOptionalChain(node)) {
|
||||
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
|
||||
return visitOptionalExpression(node, captureThisArg);
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean): Expression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg);
|
||||
case SyntaxKind.PropertyAccessExpression: return visitNonOptionalPropertyAccessExpression(node as PropertyAccessExpression, captureThisArg);
|
||||
case SyntaxKind.ElementAccessExpression: return visitNonOptionalElementAccessExpression(node as ElementAccessExpression, captureThisArg);
|
||||
case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg);
|
||||
default: return visitNode(node, visitor, isExpression);
|
||||
}
|
||||
}
|
||||
|
||||
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean): Expression {
|
||||
const { expression, chain } = flattenChain(node);
|
||||
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]));
|
||||
const temp = createTempVariable(hoistVariableDeclaration);
|
||||
const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
|
||||
const leftExpression = isSyntheticReference(left) ? left.expression : left;
|
||||
let rightExpression: Expression = temp;
|
||||
let thisArg: Expression | undefined;
|
||||
for (let i = 0; i < chain.length; i++) {
|
||||
const segment = chain[i];
|
||||
switch (segment.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
if (i === chain.length - 1 && captureThisArg) {
|
||||
thisArg = createTempVariable(hoistVariableDeclaration);
|
||||
rightExpression = createParen(createAssignment(thisArg, rightExpression));
|
||||
}
|
||||
rightExpression = createPropertyAccess(
|
||||
rightExpression,
|
||||
visitNode(segment.name, visitor, isIdentifier)
|
||||
);
|
||||
break;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
if (i === chain.length - 1 && captureThisArg) {
|
||||
thisArg = createTempVariable(hoistVariableDeclaration);
|
||||
rightExpression = createParen(createAssignment(thisArg, rightExpression));
|
||||
}
|
||||
rightExpression = createElementAccess(
|
||||
rightExpression,
|
||||
visitNode(segment.argumentExpression, visitor, isExpression)
|
||||
);
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
if (i === 0 && leftThisArg) {
|
||||
rightExpression = createFunctionCall(
|
||||
rightExpression,
|
||||
leftThisArg,
|
||||
visitNodes(segment.arguments, visitor, isExpression)
|
||||
);
|
||||
}
|
||||
else {
|
||||
rightExpression = createCall(
|
||||
rightExpression,
|
||||
/*typeArguments*/ undefined,
|
||||
visitNodes(segment.arguments, visitor, isExpression)
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
setOriginalNode(rightExpression, segment);
|
||||
}
|
||||
|
||||
const target = createConditional(
|
||||
createLogicalOr(
|
||||
createStrictEquality(createAssignment(temp, leftExpression), createNull()),
|
||||
createStrictEquality(temp, createVoidZero())
|
||||
),
|
||||
createVoidZero(),
|
||||
rightExpression
|
||||
);
|
||||
return thisArg ? createSyntheticReferenceExpression(target, thisArg) : target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+85
-21
@@ -152,6 +152,7 @@ namespace ts {
|
||||
DotDotDotToken,
|
||||
SemicolonToken,
|
||||
CommaToken,
|
||||
QuestionDotToken,
|
||||
LessThanToken,
|
||||
LessThanSlashToken,
|
||||
GreaterThanToken,
|
||||
@@ -485,6 +486,7 @@ namespace ts {
|
||||
CommaListExpression,
|
||||
MergeDeclarationMarker,
|
||||
EndOfDeclarationMarker,
|
||||
SyntheticReferenceExpression,
|
||||
|
||||
// Enum value count
|
||||
Count,
|
||||
@@ -532,20 +534,21 @@ namespace ts {
|
||||
NestedNamespace = 1 << 2, // Namespace declaration
|
||||
Synthesized = 1 << 3, // Node was synthesized during transformation
|
||||
Namespace = 1 << 4, // Namespace declaration
|
||||
ExportContext = 1 << 5, // Export context (initialized by binding)
|
||||
ContainsThis = 1 << 6, // Interface contains references to "this"
|
||||
HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding)
|
||||
HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding)
|
||||
GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope
|
||||
HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding)
|
||||
DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed
|
||||
YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator
|
||||
DecoratorContext = 1 << 13, // If node was parsed as part of a decorator
|
||||
AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function
|
||||
ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node
|
||||
JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
|
||||
OptionalChain = 1 << 5, // Chained MemberExpression rooted to a pseudo-OptionalExpression
|
||||
ExportContext = 1 << 6, // Export context (initialized by binding)
|
||||
ContainsThis = 1 << 7, // Interface contains references to "this"
|
||||
HasImplicitReturn = 1 << 8, // If function implicitly returns on one of codepaths (initialized by binding)
|
||||
HasExplicitReturn = 1 << 9, // If function has explicit reachable return on one of codepaths (initialized by binding)
|
||||
GlobalAugmentation = 1 << 10, // Set if module declaration is an augmentation for the global scope
|
||||
HasAsyncFunctions = 1 << 11, // If the file has async functions (initialized by binding)
|
||||
DisallowInContext = 1 << 12, // If node was parsed in a context where 'in-expressions' are not allowed
|
||||
YieldContext = 1 << 13, // If node was parsed in the 'yield' context created when parsing a generator
|
||||
DecoratorContext = 1 << 14, // If node was parsed as part of a decorator
|
||||
AwaitContext = 1 << 15, // If node was parsed in the 'await' context created when parsing an async function
|
||||
ThisNodeHasError = 1 << 16, // If the parser encountered an error when parsing the code that created this node
|
||||
JavaScriptFile = 1 << 17, // If node was parsed in a JavaScript
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 18, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 19, // If we've computed data from children and cached it in this node
|
||||
|
||||
// These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid
|
||||
// walking the tree if the flags are not set. However, these flags are just a approximation
|
||||
@@ -556,13 +559,13 @@ namespace ts {
|
||||
// removal, it is likely that users will add the import anyway.
|
||||
// The advantage of this approach is its simplicity. For the case of batch compilation,
|
||||
// we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used.
|
||||
/* @internal */ PossiblyContainsDynamicImport = 1 << 19,
|
||||
/* @internal */ PossiblyContainsImportMeta = 1 << 20,
|
||||
/* @internal */ PossiblyContainsDynamicImport = 1 << 20,
|
||||
/* @internal */ PossiblyContainsImportMeta = 1 << 21,
|
||||
|
||||
JSDoc = 1 << 21, // If node was parsed inside jsdoc
|
||||
/* @internal */ Ambient = 1 << 22, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
|
||||
/* @internal */ InWithStatement = 1 << 23, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
|
||||
JsonFile = 1 << 24, // If node was parsed in a Json
|
||||
JSDoc = 1 << 22, // If node was parsed inside jsdoc
|
||||
/* @internal */ Ambient = 1 << 23, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
|
||||
/* @internal */ InWithStatement = 1 << 24, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
|
||||
JsonFile = 1 << 25, // If node was parsed in a Json
|
||||
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
@@ -732,8 +735,10 @@ namespace ts {
|
||||
kind: TKind;
|
||||
}
|
||||
|
||||
export type DotToken = Token<SyntaxKind.DotToken>;
|
||||
export type DotDotDotToken = Token<SyntaxKind.DotDotDotToken>;
|
||||
export type QuestionToken = Token<SyntaxKind.QuestionToken>;
|
||||
export type QuestionDotToken = Token<SyntaxKind.QuestionDotToken>;
|
||||
export type ExclamationToken = Token<SyntaxKind.ExclamationToken>;
|
||||
export type ColonToken = Token<SyntaxKind.ColonToken>;
|
||||
export type EqualsToken = Token<SyntaxKind.EqualsToken>;
|
||||
@@ -1803,9 +1808,19 @@ namespace ts {
|
||||
export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration {
|
||||
kind: SyntaxKind.PropertyAccessExpression;
|
||||
expression: LeftHandSideExpression;
|
||||
questionDotToken?: QuestionDotToken;
|
||||
name: Identifier;
|
||||
}
|
||||
|
||||
export interface PropertyAccessChain extends PropertyAccessExpression {
|
||||
_optionalChainBrand: any;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface PropertyAccessChainRoot extends PropertyAccessChain {
|
||||
questionDotToken: QuestionDotToken;
|
||||
}
|
||||
|
||||
export interface SuperPropertyAccessExpression extends PropertyAccessExpression {
|
||||
expression: SuperExpression;
|
||||
}
|
||||
@@ -1819,9 +1834,19 @@ namespace ts {
|
||||
export interface ElementAccessExpression extends MemberExpression {
|
||||
kind: SyntaxKind.ElementAccessExpression;
|
||||
expression: LeftHandSideExpression;
|
||||
questionDotToken?: QuestionDotToken;
|
||||
argumentExpression: Expression;
|
||||
}
|
||||
|
||||
export interface ElementAccessChain extends ElementAccessExpression {
|
||||
_optionalChainBrand: any;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface ElementAccessChainRoot extends ElementAccessChain {
|
||||
questionDotToken: QuestionDotToken;
|
||||
}
|
||||
|
||||
export interface SuperElementAccessExpression extends ElementAccessExpression {
|
||||
expression: SuperExpression;
|
||||
}
|
||||
@@ -1832,10 +1857,33 @@ namespace ts {
|
||||
export interface CallExpression extends LeftHandSideExpression, Declaration {
|
||||
kind: SyntaxKind.CallExpression;
|
||||
expression: LeftHandSideExpression;
|
||||
questionDotToken?: QuestionDotToken;
|
||||
typeArguments?: NodeArray<TypeNode>;
|
||||
arguments: NodeArray<Expression>;
|
||||
}
|
||||
|
||||
export interface CallChain extends CallExpression {
|
||||
_optionalChainBrand: any;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface CallChainRoot extends CallChain {
|
||||
questionDotToken: QuestionDotToken;
|
||||
}
|
||||
|
||||
export type OptionalChain =
|
||||
| PropertyAccessChain
|
||||
| ElementAccessChain
|
||||
| CallChain
|
||||
;
|
||||
|
||||
/* @internal */
|
||||
export type OptionalChainRoot =
|
||||
| PropertyAccessChainRoot
|
||||
| ElementAccessChainRoot
|
||||
| CallChainRoot
|
||||
;
|
||||
|
||||
/** @internal */
|
||||
export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } };
|
||||
|
||||
@@ -1866,6 +1914,7 @@ namespace ts {
|
||||
tag: LeftHandSideExpression;
|
||||
typeArguments?: NodeArray<TypeNode>;
|
||||
template: TemplateLiteral;
|
||||
/*@internal*/ questionDotToken?: QuestionDotToken; // NOTE: Invalid syntax, only used to report a grammar error.
|
||||
}
|
||||
|
||||
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement;
|
||||
@@ -2033,6 +2082,13 @@ namespace ts {
|
||||
kind: SyntaxKind.MergeDeclarationMarker;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface SyntheticReferenceExpression extends LeftHandSideExpression {
|
||||
kind: SyntaxKind.SyntheticReferenceExpression;
|
||||
expression: Expression;
|
||||
thisArg: Expression;
|
||||
}
|
||||
|
||||
export interface EmptyStatement extends Statement {
|
||||
kind: SyntaxKind.EmptyStatement;
|
||||
}
|
||||
@@ -2603,8 +2659,9 @@ namespace ts {
|
||||
AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph
|
||||
/** @internal */
|
||||
Cached = 1 << 14, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
|
||||
Label = BranchLabel | LoopLabel,
|
||||
Condition = TrueCondition | FalseCondition
|
||||
Condition = TrueCondition | FalseCondition,
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
@@ -3209,6 +3266,8 @@ namespace ts {
|
||||
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
|
||||
getNullableType(type: Type, flags: TypeFlags): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
/* @internal */ getNonOptionalType(type: Type): Type;
|
||||
/* @internal */ isNullableType(type: Type): boolean;
|
||||
getTypeArguments(type: TypeReference): readonly Type[];
|
||||
|
||||
// TODO: GH#18217 `xToDeclaration` calls are frequently asserted as defined.
|
||||
@@ -3329,6 +3388,7 @@ namespace ts {
|
||||
/* @internal */ getNullType(): Type;
|
||||
/* @internal */ getESSymbolType(): Type;
|
||||
/* @internal */ getNeverType(): Type;
|
||||
/* @internal */ getOptionalType(): Type;
|
||||
/* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type;
|
||||
/* @internal */ createArrayType(elementType: Type): Type;
|
||||
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
|
||||
@@ -4590,6 +4650,8 @@ namespace ts {
|
||||
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
|
||||
/* @internal */
|
||||
instantiations?: Map<Signature>; // Generic signature instantiation cache
|
||||
/* @internal */
|
||||
isOptionalCall?: boolean;
|
||||
}
|
||||
|
||||
export const enum IndexKind {
|
||||
@@ -6032,6 +6094,8 @@ namespace ts {
|
||||
getColumn(): number;
|
||||
getIndent(): number;
|
||||
isAtStartOfLine(): boolean;
|
||||
hasTrailingComment(): boolean;
|
||||
hasTrailingWhitespace(): boolean;
|
||||
getTextPosWithWriteLine?(): number;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ namespace ts {
|
||||
|
||||
function createSingleLineStringWriter(): EmitTextWriter {
|
||||
let str = "";
|
||||
|
||||
const writeText: (text: string) => void = text => str += text;
|
||||
return {
|
||||
getText: () => str,
|
||||
@@ -79,6 +78,8 @@ namespace ts {
|
||||
getColumn: () => 0,
|
||||
getIndent: () => 0,
|
||||
isAtStartOfLine: () => false,
|
||||
hasTrailingComment: () => false,
|
||||
hasTrailingWhitespace: () => !!str.length && isWhiteSpaceLike(str.charCodeAt(str.length - 1)),
|
||||
|
||||
// Completely ignore indentation for string writers. And map newlines to
|
||||
// a single space.
|
||||
@@ -3349,6 +3350,7 @@ namespace ts {
|
||||
let lineStart: boolean;
|
||||
let lineCount: number;
|
||||
let linePos: number;
|
||||
let hasTrailingComment = false;
|
||||
|
||||
function updateLineCountAndPosFor(s: string) {
|
||||
const lineStartsOfS = computeLineStarts(s);
|
||||
@@ -3362,7 +3364,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function write(s: string) {
|
||||
function writeText(s: string) {
|
||||
if (s && s.length) {
|
||||
if (lineStart) {
|
||||
s = getIndentString(indent) + s;
|
||||
@@ -3373,18 +3375,30 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function write(s: string) {
|
||||
if (s) hasTrailingComment = false;
|
||||
writeText(s);
|
||||
}
|
||||
|
||||
function writeComment(s: string) {
|
||||
if (s) hasTrailingComment = true;
|
||||
writeText(s);
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
output = "";
|
||||
indent = 0;
|
||||
lineStart = true;
|
||||
lineCount = 0;
|
||||
linePos = 0;
|
||||
hasTrailingComment = false;
|
||||
}
|
||||
|
||||
function rawWrite(s: string) {
|
||||
if (s !== undefined) {
|
||||
output += s;
|
||||
updateLineCountAndPosFor(s);
|
||||
hasTrailingComment = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3400,6 +3414,7 @@ namespace ts {
|
||||
lineCount++;
|
||||
linePos = output.length;
|
||||
lineStart = true;
|
||||
hasTrailingComment = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3422,6 +3437,8 @@ namespace ts {
|
||||
getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos,
|
||||
getText: () => output,
|
||||
isAtStartOfLine: () => lineStart,
|
||||
hasTrailingComment: () => hasTrailingComment,
|
||||
hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)),
|
||||
clear: reset,
|
||||
reportInaccessibleThisError: noop,
|
||||
reportPrivateInBaseOfClassExpression: noop,
|
||||
@@ -3436,7 +3453,7 @@ namespace ts {
|
||||
writeStringLiteral: write,
|
||||
writeSymbol: (s, _) => write(s),
|
||||
writeTrailingSemicolon: write,
|
||||
writeComment: write,
|
||||
writeComment,
|
||||
getTextPosWithWriteLine
|
||||
};
|
||||
}
|
||||
@@ -4792,6 +4809,10 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getDotOrQuestionDotToken(node: PropertyAccessExpression) {
|
||||
return node.questionDotToken || createNode(SyntaxKind.DotToken, node.expression.end, node.name.pos) as DotToken;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
@@ -5807,14 +5828,32 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.PropertyAccessExpression;
|
||||
}
|
||||
|
||||
export function isPropertyAccessChain(node: Node): node is PropertyAccessChain {
|
||||
return isPropertyAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
|
||||
}
|
||||
|
||||
export function isElementAccessExpression(node: Node): node is ElementAccessExpression {
|
||||
return node.kind === SyntaxKind.ElementAccessExpression;
|
||||
}
|
||||
|
||||
export function isElementAccessChain(node: Node): node is ElementAccessChain {
|
||||
return isElementAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
|
||||
}
|
||||
|
||||
export function isCallExpression(node: Node): node is CallExpression {
|
||||
return node.kind === SyntaxKind.CallExpression;
|
||||
}
|
||||
|
||||
export function isCallChain(node: Node): node is CallChain {
|
||||
return isCallExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
|
||||
}
|
||||
|
||||
export function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain {
|
||||
return isPropertyAccessChain(node)
|
||||
|| isElementAccessChain(node)
|
||||
|| isCallChain(node);
|
||||
}
|
||||
|
||||
export function isNewExpression(node: Node): node is NewExpression {
|
||||
return node.kind === SyntaxKind.NewExpression;
|
||||
}
|
||||
@@ -6823,6 +6862,11 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.NotEmittedStatement;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isSyntheticReference(node: Node): node is SyntheticReferenceExpression {
|
||||
return node.kind === SyntaxKind.SyntheticReferenceExpression;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isNotEmittedOrPartiallyEmittedNode(node: Node): node is NotEmittedStatement | PartiallyEmittedExpression {
|
||||
return isNotEmittedStatement(node)
|
||||
@@ -7127,6 +7171,11 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.GetAccessor;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isOptionalChainRoot(node: Node): node is OptionalChainRoot {
|
||||
return isOptionalChain(node) && !!node.questionDotToken;
|
||||
}
|
||||
|
||||
/** True if has jsdoc nodes attached to it. */
|
||||
/* @internal */
|
||||
// TODO: GH#19856 Would like to return `node is Node & { jsDoc: JSDoc[] }` but it causes long compile times
|
||||
|
||||
@@ -456,16 +456,35 @@ namespace ts {
|
||||
nodesVisitor((<ObjectLiteralExpression>node).properties, visitor, isObjectLiteralElementLike));
|
||||
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
return updatePropertyAccessChain(<PropertyAccessChain>node,
|
||||
visitNode((<PropertyAccessChain>node).expression, visitor, isExpression),
|
||||
visitNode((<PropertyAccessChain>node).questionDotToken, visitor, isToken),
|
||||
visitNode((<PropertyAccessChain>node).name, visitor, isIdentifier));
|
||||
}
|
||||
return updatePropertyAccess(<PropertyAccessExpression>node,
|
||||
visitNode((<PropertyAccessExpression>node).expression, visitor, isExpression),
|
||||
visitNode((<PropertyAccessExpression>node).name, visitor, isIdentifier));
|
||||
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
return updateElementAccessChain(<ElementAccessChain>node,
|
||||
visitNode((<ElementAccessChain>node).expression, visitor, isExpression),
|
||||
visitNode((<ElementAccessChain>node).questionDotToken, visitor, isToken),
|
||||
visitNode((<ElementAccessChain>node).argumentExpression, visitor, isExpression));
|
||||
}
|
||||
return updateElementAccess(<ElementAccessExpression>node,
|
||||
visitNode((<ElementAccessExpression>node).expression, visitor, isExpression),
|
||||
visitNode((<ElementAccessExpression>node).argumentExpression, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
if (node.flags & NodeFlags.OptionalChain) {
|
||||
return updateCallChain(<CallChain>node,
|
||||
visitNode((<CallChain>node).expression, visitor, isExpression),
|
||||
visitNode((<CallChain>node).questionDotToken, visitor, isToken),
|
||||
nodesVisitor((<CallChain>node).typeArguments, visitor, isTypeNode),
|
||||
nodesVisitor((<CallChain>node).arguments, visitor, isExpression));
|
||||
}
|
||||
return updateCall(<CallExpression>node,
|
||||
visitNode((<CallExpression>node).expression, visitor, isExpression),
|
||||
nodesVisitor((<CallExpression>node).typeArguments, visitor, isTypeNode),
|
||||
|
||||
+93
-29
@@ -11,28 +11,52 @@ namespace ts.Completions {
|
||||
}
|
||||
export type Log = (message: string) => void;
|
||||
|
||||
const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export, Promise }
|
||||
type SymbolOriginInfo = { kind: SymbolOriginInfoKind.ThisType } | { kind: SymbolOriginInfoKind.Promise } | { kind: SymbolOriginInfoKind.SymbolMemberNoExport } | SymbolOriginInfoExport;
|
||||
interface SymbolOriginInfoExport {
|
||||
kind: SymbolOriginInfoKind.SymbolMemberExport | SymbolOriginInfoKind.Export;
|
||||
const enum SymbolOriginInfoKind {
|
||||
ThisType = 1 << 0,
|
||||
SymbolMember = 1 << 1,
|
||||
Export = 1 << 2,
|
||||
Promise = 1 << 3,
|
||||
Nullable = 1 << 4,
|
||||
|
||||
SymbolMemberNoExport = SymbolMember,
|
||||
SymbolMemberExport = SymbolMember | Export,
|
||||
}
|
||||
|
||||
interface SymbolOriginInfo {
|
||||
kind: SymbolOriginInfoKind;
|
||||
}
|
||||
|
||||
interface SymbolOriginInfoExport extends SymbolOriginInfo {
|
||||
kind: SymbolOriginInfoKind;
|
||||
moduleSymbol: Symbol;
|
||||
isDefaultExport: boolean;
|
||||
}
|
||||
|
||||
function originIsThisType(origin: SymbolOriginInfo): boolean {
|
||||
return !!(origin.kind & SymbolOriginInfoKind.ThisType);
|
||||
}
|
||||
|
||||
function originIsSymbolMember(origin: SymbolOriginInfo): boolean {
|
||||
return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.SymbolMemberNoExport;
|
||||
return !!(origin.kind & SymbolOriginInfoKind.SymbolMember);
|
||||
}
|
||||
|
||||
function originIsExport(origin: SymbolOriginInfo): origin is SymbolOriginInfoExport {
|
||||
return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.Export;
|
||||
return !!(origin.kind & SymbolOriginInfoKind.Export);
|
||||
}
|
||||
|
||||
function originIsPromise(origin: SymbolOriginInfo): boolean {
|
||||
return origin.kind === SymbolOriginInfoKind.Promise;
|
||||
return !!(origin.kind & SymbolOriginInfoKind.Promise);
|
||||
}
|
||||
|
||||
function originIsNullableMember(origin: SymbolOriginInfo): boolean {
|
||||
return !!(origin.kind & SymbolOriginInfoKind.Nullable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map from symbol id -> SymbolOriginInfo.
|
||||
* Only populated for symbols that come from other modules.
|
||||
*/
|
||||
type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[];
|
||||
type SymbolOriginInfoMap = (SymbolOriginInfo | SymbolOriginInfoExport | undefined)[];
|
||||
|
||||
type SymbolSortTextMap = (SortText | undefined)[];
|
||||
|
||||
@@ -314,14 +338,26 @@ namespace ts.Completions {
|
||||
): CompletionEntry | undefined {
|
||||
let insertText: string | undefined;
|
||||
let replacementSpan: TextSpan | undefined;
|
||||
if (origin && origin.kind === SymbolOriginInfoKind.ThisType) {
|
||||
insertText = needsConvertPropertyAccess ? `this[${quote(name, preferences)}]` : `this.${name}`;
|
||||
const insertQuestionDot = origin && originIsNullableMember(origin);
|
||||
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
|
||||
if (origin && originIsThisType(origin)) {
|
||||
insertText = needsConvertPropertyAccess
|
||||
? `this${insertQuestionDot ? "?." : ""}[${quote(name, preferences)}]`
|
||||
: `this${insertQuestionDot ? "?." : "."}${name}`;
|
||||
}
|
||||
// We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
|
||||
// Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro.
|
||||
else if ((origin && originIsSymbolMember(origin) || needsConvertPropertyAccess) && propertyAccessToConvert) {
|
||||
insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`;
|
||||
const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!;
|
||||
else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) {
|
||||
insertText = useBraces ? needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]` : name;
|
||||
if (insertQuestionDot || propertyAccessToConvert.questionDotToken) {
|
||||
insertText = `?.${insertText}`;
|
||||
}
|
||||
|
||||
const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile) ||
|
||||
findChildOfKind(propertyAccessToConvert, SyntaxKind.QuestionDotToken, sourceFile);
|
||||
if (!dot) {
|
||||
return undefined;
|
||||
}
|
||||
// If the text after the '.' starts with this name, write over it. Else, add new text.
|
||||
const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end;
|
||||
replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end);
|
||||
@@ -337,7 +373,7 @@ namespace ts.Completions {
|
||||
if (origin && originIsPromise(origin) && propertyAccessToConvert) {
|
||||
if (insertText === undefined) insertText = name;
|
||||
const awaitText = `(await ${propertyAccessToConvert.expression.getText()})`;
|
||||
insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}.${insertText}`;
|
||||
insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}${insertQuestionDot ? "?." : "."}${insertText}`;
|
||||
replacementSpan = createTextSpanFromBounds(propertyAccessToConvert.getStart(sourceFile), propertyAccessToConvert.end);
|
||||
}
|
||||
|
||||
@@ -846,6 +882,7 @@ namespace ts.Completions {
|
||||
let node = currentToken;
|
||||
let propertyAccessToConvert: PropertyAccessExpression | undefined;
|
||||
let isRightOfDot = false;
|
||||
let isRightOfQuestionDot = false;
|
||||
let isRightOfOpenTag = false;
|
||||
let isStartingCloseTag = false;
|
||||
let isJsxInitializer: IsJsxInitializer = false;
|
||||
@@ -859,8 +896,9 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
let parent = contextToken.parent;
|
||||
if (contextToken.kind === SyntaxKind.DotToken) {
|
||||
isRightOfDot = true;
|
||||
if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
|
||||
isRightOfDot = contextToken.kind === SyntaxKind.DotToken;
|
||||
isRightOfQuestionDot = contextToken.kind === SyntaxKind.QuestionDotToken;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
propertyAccessToConvert = parent as PropertyAccessExpression;
|
||||
@@ -967,7 +1005,7 @@ namespace ts.Completions {
|
||||
const symbolToSortTextMap: SymbolSortTextMap = [];
|
||||
const importSuggestionsCache = host.getImportSuggestionsCache && host.getImportSuggestionsCache();
|
||||
|
||||
if (isRightOfDot) {
|
||||
if (isRightOfDot || isRightOfQuestionDot) {
|
||||
getTypeScriptMemberSymbols();
|
||||
}
|
||||
else if (isRightOfOpenTag) {
|
||||
@@ -1074,7 +1112,13 @@ namespace ts.Completions {
|
||||
if (!isTypeLocation &&
|
||||
symbol.declarations &&
|
||||
symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
|
||||
addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node), !!(node.flags & NodeFlags.AwaitContext));
|
||||
let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType();
|
||||
let insertQuestionDot = false;
|
||||
if (type.isNullableType()) {
|
||||
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
|
||||
type = type.getNonNullableType();
|
||||
}
|
||||
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1089,12 +1133,21 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
if (!isTypeLocation) {
|
||||
addTypeProperties(typeChecker.getTypeAtLocation(node), !!(node.flags & NodeFlags.AwaitContext));
|
||||
let type = typeChecker.getTypeAtLocation(node).getNonOptionalType();
|
||||
let insertQuestionDot = false;
|
||||
if (type.isNullableType()) {
|
||||
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
|
||||
type = type.getNonNullableType();
|
||||
}
|
||||
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
|
||||
}
|
||||
}
|
||||
|
||||
function addTypeProperties(type: Type, insertAwait?: boolean): void {
|
||||
function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean): void {
|
||||
isNewIdentifierLocation = !!type.getStringIndexType();
|
||||
if (isRightOfQuestionDot && some(type.getCallSignatures())) {
|
||||
isNewIdentifierLocation = true;
|
||||
}
|
||||
|
||||
const propertyAccess = node.kind === SyntaxKind.ImportType ? <ImportTypeNode>node : <PropertyAccessExpression | QualifiedName>node.parent;
|
||||
if (isUncheckedFile) {
|
||||
@@ -1108,7 +1161,7 @@ namespace ts.Completions {
|
||||
else {
|
||||
for (const symbol of type.getApparentProperties()) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) {
|
||||
addPropertySymbol(symbol);
|
||||
addPropertySymbol(symbol, /*insertAwait*/ false, insertQuestionDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1118,14 +1171,14 @@ namespace ts.Completions {
|
||||
if (promiseType) {
|
||||
for (const symbol of promiseType.getApparentProperties()) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, promiseType, symbol)) {
|
||||
addPropertySymbol(symbol, /* insertAwait */ true);
|
||||
addPropertySymbol(symbol, /* insertAwait */ true, insertQuestionDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPropertySymbol(symbol: Symbol, insertAwait?: boolean) {
|
||||
function addPropertySymbol(symbol: Symbol, insertAwait: boolean, insertQuestionDot: boolean) {
|
||||
// For a computed property with an accessible name like `Symbol.iterator`,
|
||||
// we'll add a completion for the *name* `Symbol` instead of for the property.
|
||||
// If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
|
||||
@@ -1139,23 +1192,34 @@ namespace ts.Completions {
|
||||
symbols.push(firstAccessibleSymbol);
|
||||
const moduleSymbol = firstAccessibleSymbol.parent;
|
||||
symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] =
|
||||
!moduleSymbol || !isExternalModuleSymbol(moduleSymbol) ? { kind: SymbolOriginInfoKind.SymbolMemberNoExport } : { kind: SymbolOriginInfoKind.SymbolMemberExport, moduleSymbol, isDefaultExport: false };
|
||||
!moduleSymbol || !isExternalModuleSymbol(moduleSymbol)
|
||||
? { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) }
|
||||
: { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport), moduleSymbol, isDefaultExport: false };
|
||||
}
|
||||
else if (preferences.includeCompletionsWithInsertText) {
|
||||
addPromiseSymbolOriginInfo(symbol);
|
||||
addSymbolOriginInfo(symbol);
|
||||
symbols.push(symbol);
|
||||
}
|
||||
}
|
||||
else {
|
||||
addPromiseSymbolOriginInfo(symbol);
|
||||
addSymbolOriginInfo(symbol);
|
||||
symbols.push(symbol);
|
||||
}
|
||||
|
||||
function addPromiseSymbolOriginInfo (symbol: Symbol) {
|
||||
if (insertAwait && preferences.includeCompletionsWithInsertText && !symbolToOriginInfoMap[getSymbolId(symbol)]) {
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.Promise };
|
||||
function addSymbolOriginInfo(symbol: Symbol) {
|
||||
if (preferences.includeCompletionsWithInsertText) {
|
||||
if (insertAwait && !symbolToOriginInfoMap[getSymbolId(symbol)]) {
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.Promise) };
|
||||
}
|
||||
else if (insertQuestionDot) {
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.Nullable };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNullableSymbolOriginInfoKind(kind: SymbolOriginInfoKind) {
|
||||
return insertQuestionDot ? kind | SymbolOriginInfoKind.Nullable : kind;
|
||||
}
|
||||
}
|
||||
|
||||
/** Given 'a.b.c', returns 'a'. */
|
||||
|
||||
@@ -409,9 +409,15 @@ namespace ts {
|
||||
getBaseTypes(): BaseType[] | undefined {
|
||||
return this.isClassOrInterface() ? this.checker.getBaseTypes(this) : undefined;
|
||||
}
|
||||
isNullableType(): boolean {
|
||||
return this.checker.isNullableType(this);
|
||||
}
|
||||
getNonNullableType(): Type {
|
||||
return this.checker.getNonNullableType(this);
|
||||
}
|
||||
getNonOptionalType(): Type {
|
||||
return this.checker.getNonOptionalType(this);
|
||||
}
|
||||
getConstraint(): Type | undefined {
|
||||
return this.checker.getBaseConstraintOfType(this);
|
||||
}
|
||||
|
||||
@@ -1061,6 +1061,8 @@ namespace ts.textChanges {
|
||||
getColumn,
|
||||
getIndent,
|
||||
isAtStartOfLine,
|
||||
hasTrailingComment: () => writer.hasTrailingComment(),
|
||||
hasTrailingWhitespace: () => writer.hasTrailingWhitespace(),
|
||||
clear
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace ts {
|
||||
getNumberIndexType(): Type | undefined;
|
||||
getBaseTypes(): BaseType[] | undefined;
|
||||
getNonNullableType(): Type;
|
||||
/*@internal*/ getNonOptionalType(): Type;
|
||||
/*@internal*/ isNullableType(): boolean;
|
||||
getConstraint(): Type | undefined;
|
||||
getDefault(): Type | undefined;
|
||||
|
||||
|
||||
@@ -956,6 +956,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function removeOptionality(type: Type, isOptionalExpression: boolean, isOptionalChain: boolean) {
|
||||
return isOptionalExpression ? type.getNonNullableType() :
|
||||
isOptionalChain ? type.getNonOptionalType() :
|
||||
type;
|
||||
}
|
||||
|
||||
export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean {
|
||||
const info = getPossibleTypeArgumentsInfo(token, sourceFile);
|
||||
return info !== undefined && (isPartOfTypeNode(info.called) ||
|
||||
@@ -964,7 +970,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getPossibleGenericSignatures(called: Expression, typeArgumentCount: number, checker: TypeChecker): readonly Signature[] {
|
||||
const type = checker.getTypeAtLocation(called);
|
||||
let type = checker.getTypeAtLocation(called);
|
||||
if (isOptionalChain(called.parent)) {
|
||||
type = removeOptionality(type, !!called.parent.questionDotToken, /*isOptionalChain*/ true);
|
||||
}
|
||||
|
||||
const signatures = isNewExpression(called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
return signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= typeArgumentCount);
|
||||
}
|
||||
@@ -993,6 +1003,9 @@ namespace ts {
|
||||
case SyntaxKind.LessThanToken:
|
||||
// Found the beginning of the generic argument expression
|
||||
token = findPrecedingToken(token.getFullStart(), sourceFile);
|
||||
if (token && token.kind === SyntaxKind.QuestionDotToken) {
|
||||
token = findPrecedingToken(token.getFullStart(), sourceFile);
|
||||
}
|
||||
if (!token || !isIdentifier(token)) return undefined;
|
||||
if (!remainingLessThanTokens) {
|
||||
return isDeclarationName(token) ? undefined : { called: token, nTypeArguments };
|
||||
@@ -1493,6 +1506,8 @@ namespace ts {
|
||||
getColumn: () => 0,
|
||||
getLine: () => 0,
|
||||
isAtStartOfLine: () => false,
|
||||
hasTrailingWhitespace: () => false,
|
||||
hasTrailingComment: () => false,
|
||||
rawWrite: notImplemented,
|
||||
getIndent: () => indent,
|
||||
increaseIndent: () => { indent++; },
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
"unittests/evaluation/awaiter.ts",
|
||||
"unittests/evaluation/forAwaitOf.ts",
|
||||
"unittests/evaluation/forOf.ts",
|
||||
"unittests/evaluation/optionalCall.ts",
|
||||
"unittests/evaluation/objectRest.ts",
|
||||
"unittests/services/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/services/colorization.ts",
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
describe("unittests:: evaluation:: optionalCall", () => {
|
||||
it("f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
function f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
export const output: any[] = [];
|
||||
f?.(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.isUndefined(result.output[1]);
|
||||
});
|
||||
it("o.f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o.f?.(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o);
|
||||
});
|
||||
it("o.x.f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
x: {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o.x.f?.(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o.x);
|
||||
});
|
||||
it("o?.f()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o?.f(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o);
|
||||
});
|
||||
it("o?.f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o?.f?.(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o);
|
||||
});
|
||||
it("o.x?.f()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
x: {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o.x?.f(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o.x);
|
||||
});
|
||||
it("o?.x.f()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
x: {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o?.x.f(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o.x);
|
||||
});
|
||||
it("o?.x?.f()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
x: {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o?.x?.f(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o.x);
|
||||
});
|
||||
it("o?.x?.f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
x: {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const output: any[] = [];
|
||||
o?.x?.f?.(1);
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], result.o.x);
|
||||
});
|
||||
it("f?.()?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
function g(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
function f(a) {
|
||||
output.push(a);
|
||||
return g;
|
||||
}
|
||||
export const output: any[] = [];
|
||||
f?.(1)?.(2)
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], 2);
|
||||
assert.isUndefined(result.output[2]);
|
||||
});
|
||||
it("f?.().f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
};
|
||||
function f(a) {
|
||||
output.push(a);
|
||||
return o;
|
||||
}
|
||||
export const output: any[] = [];
|
||||
f?.(1).f?.(2)
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], 2);
|
||||
assert.strictEqual(result.output[2], result.o);
|
||||
});
|
||||
it("f?.()?.f?.()", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const o = {
|
||||
f(a) {
|
||||
output.push(a);
|
||||
output.push(this);
|
||||
}
|
||||
};
|
||||
function f(a) {
|
||||
output.push(a);
|
||||
return o;
|
||||
}
|
||||
export const output: any[] = [];
|
||||
f?.(1)?.f?.(2)
|
||||
`);
|
||||
assert.strictEqual(result.output[0], 1);
|
||||
assert.strictEqual(result.output[1], 2);
|
||||
assert.strictEqual(result.output[2], result.o);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user