Skip to content

Commit

Permalink
Make pos/end/parent and JSDoc 'comment' read-only
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Dec 11, 2019
1 parent 602efc5 commit 322515c
Show file tree
Hide file tree
Showing 19 changed files with 460 additions and 404 deletions.
7 changes: 3 additions & 4 deletions src/compat/deprecations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1933,13 +1933,12 @@ namespace ts {
* NOTE: It is unsafe to change any properties of a `Node` that relate to its AST children, as those changes won't be
* captured with respect to transformations.
*
* @deprecated Use `factory.cloneNode` instead and set `pos`, `end`, and `parent` as needed.
* @deprecated Use `factory.cloneNode` instead and use `setCommentRange` or `setSourceMapRange` and avoid setting `parent`.
*/
export function getMutableClone<T extends Node>(node: T): T {
const clone = factory.cloneNode(node);
clone.pos = node.pos;
clone.end = node.end;
clone.parent = node.parent;
setTextRange(clone, node);
setParent(clone, node.parent);
return clone;
}

Expand Down
45 changes: 20 additions & 25 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ namespace ts {
export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map<ModuleInstanceState | undefined>): ModuleInstanceState {
if (node.body && !node.body.parent) {
// getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already
setParentPointers(node, node.body);
setParent(node.body, node);
setParentRecursive(node.body, /*incremental*/ false);
}
return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated;
}
Expand Down Expand Up @@ -114,7 +115,8 @@ namespace ts {
for (const statement of statements) {
if (nodeHasName(statement, name)) {
if (!statement.parent) {
setParentPointers(p, statement);
setParent(statement, p);
setParentRecursive(statement, /*incremental*/ false);
}
const state = getModuleInstanceStateCached(statement, visited);
if (found === undefined || state > found) {
Expand Down Expand Up @@ -467,7 +469,7 @@ namespace ts {
else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) {
// Assignment declarations are allowed to merge with variables, no matter what other flags they have.
if (isNamedDeclaration(node)) {
node.name.parent = node;
setParent(node.name, node);
}

// Report errors every position with duplicate declaration
Expand Down Expand Up @@ -1484,9 +1486,10 @@ namespace ts {
}

function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
node.tagName.parent = node;
setParent(node.tagName, node);
if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {
setParentPointers(node, node.fullName);
setParent(node.fullName, node);
setParentRecursive(node.fullName, /*incremental*/ false);
}
}

Expand Down Expand Up @@ -2174,7 +2177,7 @@ namespace ts {
if (!node) {
return;
}
node.parent = parent;
setParent(node, parent);
const saveInStrictMode = inStrictMode;

// Even though in the AST the jsdoc @typedef node belongs to the current node,
Expand Down Expand Up @@ -2233,7 +2236,8 @@ namespace ts {
}
else {
for (const j of node.jsDoc!) {
setParentPointers(node, j);
setParent(j, node);
setParentRecursive(j, /*incremental*/ false);
}
}
}
Expand Down Expand Up @@ -2726,8 +2730,8 @@ namespace ts {

/** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */
function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) {
node.left.parent = node;
node.right.parent = node;
setParent(node.left, node);
setParent(node.right, node);
bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true);
}

Expand All @@ -2751,9 +2755,9 @@ namespace ts {
const constructorFunction = classPrototype.expression;

// Fix up parent pointers since we're going to use these nodes before we bind into them
lhs.parent = parent;
constructorFunction.parent = classPrototype;
classPrototype.parent = lhs;
setParent(constructorFunction, classPrototype);
setParent(classPrototype, lhs);
setParent(lhs, parent);

bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true);
}
Expand All @@ -2772,8 +2776,8 @@ namespace ts {
return;
}
// Fix up parent pointers since we're going to use these nodes before we bind into them
node.left.parent = node;
node.right.parent = node;
setParent(node.left, node);
setParent(node.right, node);
if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) {
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
// var util = module.exports;
Expand All @@ -2797,7 +2801,7 @@ namespace ts {
* Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y;
*/
function bindStaticPropertyAssignment(node: BindableStaticAccessExpression) {
node.expression.parent = node;
setParent(node.expression, node);
bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
}

Expand Down Expand Up @@ -2979,7 +2983,7 @@ namespace ts {
const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName);
if (symbolExport) {
if (node.name) {
node.name.parent = node;
setParent(node.name, node);
}
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)));
}
Expand Down Expand Up @@ -3241,13 +3245,4 @@ namespace ts {
}
return container.symbol && container.symbol.exports && container.symbol.exports.get(name);
}

/**
* "Binds" JSDoc nodes in TypeScript code.
* Since we will never create symbols for JSDoc, we just set parent pointers instead.
*/
function setParentPointers(parent: Node, child: Node): void {
child.parent = parent;
forEachChild(child, grandchild => setParentPointers(child, grandchild));
}
}
28 changes: 15 additions & 13 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5616,7 +5616,7 @@ namespace ts {
// Add a namespace
// Create namespace as non-synthetic so it is usable as an enclosing declaration
let fakespace = parseNodeFactory.createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createIdentifier(localName), factory.createModuleBlock([]), NodeFlags.Namespace);
fakespace.parent = enclosingDeclaration as SourceFile | NamespaceDeclaration;
setParent(fakespace, enclosingDeclaration as SourceFile | NamespaceDeclaration);
fakespace.locals = createSymbolTable(props);
fakespace.symbol = props[0].parent!;

Expand Down Expand Up @@ -6775,8 +6775,8 @@ namespace ts {
if (propName) {
const literal = setTextRange(parseNodeFactory.createStringLiteral(propName), node);
const result = setTextRange(parseNodeFactory.createElementAccess(parentAccess, literal), node);
literal.parent = result;
result.parent = node;
setParent(literal, result);
setParent(result, node);
result.flowNode = parentAccess.flowNode;
return result;
}
Expand Down Expand Up @@ -20313,9 +20313,10 @@ namespace ts {
const unionTypes = (<UnionTypeNode>funcTypeNode.type).types;
if (unionTypes && unionTypes[unionTypes.length - 1].kind === SyntaxKind.UndefinedKeyword) {
// TODO(rbuckton): Does this need to be parented?
const parenedFuncType = setParent(setTextRange(factory.cloneNode(funcTypeNode), funcTypeNode), funcTypeNode.parent);
// Highlight to the end of the second to last constituent of the union
parenedFuncType.end = unionTypes[unionTypes.length - 2].end;
const parenedFuncType = setParent(
setTextRangePosEnd(factory.cloneNode(funcTypeNode), funcTypeNode.pos, unionTypes[unionTypes.length - 2].end),
funcTypeNode.parent);
addRelatedInfo(diag, createDiagnosticForNode(parenedFuncType, Diagnostics.Did_you_mean_to_parenthesize_this_function_type));
}
}
Expand Down Expand Up @@ -22371,7 +22372,7 @@ namespace ts {
(getArrayLiteralTupleTypeIfApplicable(childrenTypes, childrenContextualType, /*hasRestElement*/ false) || createArrayType(getUnionType(childrenTypes)));
// Fake up a property declaration for the children
childrenPropSymbol.valueDeclaration = factory.createPropertySignature(/*modifiers*/ undefined, unescapeLeadingUnderscores(jsxChildrenPropertyName), /*questionToken*/ undefined, /*type*/ undefined);
childrenPropSymbol.valueDeclaration.parent = attributes;
setParent(childrenPropSymbol.valueDeclaration, attributes);
childrenPropSymbol.valueDeclaration.symbol = childrenPropSymbol;
const childPropMap = createSymbolTable();
childPropMap.set(jsxChildrenPropertyName, childrenPropSymbol);
Expand Down Expand Up @@ -23985,7 +23986,7 @@ namespace ts {
function createSyntheticExpression(parent: Node, type: Type, isSpread?: boolean) {
const result = parseNodeFactory.createSyntheticExpression(type, isSpread);
setTextRange(result, parent);
result.parent = parent;
setParent(result, parent);
return result;
}

Expand Down Expand Up @@ -24181,11 +24182,12 @@ namespace ts {
spanArray = factory.createNodeArray(args.slice(max));
}

spanArray.pos = first(spanArray).pos;
spanArray.end = last(spanArray).end;
if (spanArray.end === spanArray.pos) {
spanArray.end++;
const pos = first(spanArray).pos;
let end = last(spanArray).end;
if (end === pos) {
end++;
}
setTextRangePosEnd(spanArray, pos, end);
const diagnostic = createDiagnosticForNodeArray(
getSourceFileOfNode(node), spanArray, error, paramRange, argCount);
return related ? addRelatedInfo(diagnostic, related) : diagnostic;
Expand Down Expand Up @@ -32044,8 +32046,8 @@ namespace ts {

function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
const reference = factory.createPropertyAccess(factory.createThis(), propName);
reference.expression.parent = reference;
reference.parent = constructor;
setParent(reference.expression, reference);
setParent(reference, constructor);
reference.flowNode = constructor.returnFlowNode;
const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
return !(getFalsyFlags(flowType) & TypeFlags.Undefined);
Expand Down
14 changes: 5 additions & 9 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ namespace ts {
const statements = prologueInfo?.directives.map(directive => {
const literal = setTextRange(factory.createStringLiteral(directive.expression.text), directive.expression);
const statement = setTextRange(factory.createExpressionStatement(literal), directive);
literal.parent = statement;
setParent(literal, statement);
return statement;
});
const eofToken = factory.createToken(SyntaxKind.EndOfFileToken);
Expand All @@ -692,14 +692,10 @@ namespace ts {
!host.useCaseSensitiveFileNames()
);
sourceFile.text = prologueInfo?.text ?? "";
sourceFile.pos = 0;
sourceFile.end = prologueInfo?.text.length ?? 0;
for (const statement of sourceFile.statements) {
statement.parent = sourceFile;
}
eofToken.pos = sourceFile.end;
eofToken.end = sourceFile.end;
eofToken.parent = sourceFile;
setTextRangePosWidth(sourceFile, 0, prologueInfo?.text.length ?? 0);
setEachParent(sourceFile.statements, sourceFile);
setTextRangePosWidth(eofToken, sourceFile.end, 0);
setParent(eofToken, sourceFile);
return sourceFile;
});
}
Expand Down
30 changes: 14 additions & 16 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,7 @@ namespace ts {
// small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation.
const length = elements.length;
const array = <NodeArray<T>>(length >= 1 && length <= 4 ? elements.slice() : elements);
array.pos = -1;
array.end = -1;
setTextRangePosEnd(array, -1, -1);
if (hasTrailingComma) {
array.hasTrailingComma = hasTrailingComma;
}
Expand Down Expand Up @@ -3942,8 +3941,8 @@ namespace ts {
}

// @api
function createJSDocTemplateTag(tagName: Identifier | undefined, constraint: JSDocTypeExpression | undefined, typeParameters: readonly TypeParameterDeclaration[]): JSDocTemplateTag {
const node = createBaseJSDocTag<JSDocTemplateTag>(SyntaxKind.JSDocTemplateTag, tagName || createIdentifier("template"), /*comment*/ undefined);
function createJSDocTemplateTag(tagName: Identifier | undefined, constraint: JSDocTypeExpression | undefined, typeParameters: readonly TypeParameterDeclaration[], comment?: string): JSDocTemplateTag {
const node = createBaseJSDocTag<JSDocTemplateTag>(SyntaxKind.JSDocTemplateTag, tagName || createIdentifier("template"), comment);
setChild(node, node.constraint = constraint);
setChildren(node, node.typeParameters = createNodeArray(typeParameters));
return finishJSDoc(node);
Expand Down Expand Up @@ -3973,8 +3972,8 @@ namespace ts {
}

// @api
function createJSDocThisTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression): JSDocThisTag {
const node = createBaseJSDocTag<JSDocThisTag>(SyntaxKind.JSDocThisTag, tagName || createIdentifier("this"), /*comment*/ undefined);
function createJSDocThisTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression, comment?: string): JSDocThisTag {
const node = createBaseJSDocTag<JSDocThisTag>(SyntaxKind.JSDocThisTag, tagName || createIdentifier("this"), comment);
setChild(node, node.typeExpression = typeExpression);
return finishJSDoc(node);
}
Expand Down Expand Up @@ -4006,8 +4005,8 @@ namespace ts {
}

// @api
function createJSDocAugmentsTag(tagName: Identifier | undefined, className: JSDocAugmentsTag["class"]): JSDocAugmentsTag {
const node = createBaseJSDocTag<JSDocAugmentsTag>(SyntaxKind.JSDocAugmentsTag, tagName || createIdentifier("augments"), /*comment*/ undefined);
function createJSDocAugmentsTag(tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment?: string): JSDocAugmentsTag {
const node = createBaseJSDocTag<JSDocAugmentsTag>(SyntaxKind.JSDocAugmentsTag, tagName || createIdentifier("augments"), comment);
setChild(node, node.class = className);
return finishJSDoc(node);
}
Expand All @@ -4022,21 +4021,21 @@ namespace ts {
}

// @api
function createJSDocClassTag(tagName: Identifier | undefined): JSDocClassTag {
const node = createBaseJSDocTag<JSDocClassTag>(SyntaxKind.JSDocClassTag, tagName || createIdentifier("class"), /*comment*/ undefined);
function createJSDocClassTag(tagName: Identifier | undefined, comment?: string): JSDocClassTag {
const node = createBaseJSDocTag<JSDocClassTag>(SyntaxKind.JSDocClassTag, tagName || createIdentifier("class"), comment);
return finishJSDoc(node);
}

// @api
function createJSDocEnumTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression): JSDocEnumTag {
const node = createBaseJSDocTag<JSDocEnumTag>(SyntaxKind.JSDocEnumTag, tagName || createIdentifier("enum"), /*comment*/ undefined);
function createJSDocEnumTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression, comment?: string): JSDocEnumTag {
const node = createBaseJSDocTag<JSDocEnumTag>(SyntaxKind.JSDocEnumTag, tagName || createIdentifier("enum"), comment);
setChild(node, node.typeExpression = typeExpression);
return finishJSDoc(node);
}

// @api
function createJSDocUnknownTag(tagName: Identifier): JSDocUnknownTag {
const node = createBaseJSDocTag<JSDocUnknownTag>(SyntaxKind.JSDocTag, tagName, /*comment*/ undefined);
function createJSDocUnknownTag(tagName: Identifier, comment?: string): JSDocUnknownTag {
const node = createBaseJSDocTag<JSDocUnknownTag>(SyntaxKind.JSDocTag, tagName, comment);
return finishJSDoc(node);
}

Expand Down Expand Up @@ -5803,8 +5802,7 @@ namespace ts {

if (!texts) {
const textNode = factory.createUnparsedTextLike(/*data*/ undefined, /*internal*/ false);
textNode.pos = 0;
textNode.end = typeof length === "function" ? length() : length;
setTextRangePosWidth(textNode, 0, typeof length === "function" ? length() : length);
texts = [textNode];
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace ts {
const react = parseNodeFactory.createIdentifier(reactNamespace || "React");
// Set the parent that is in parse tree
// this makes sure that parent chain is intact for checker to traverse complete scope tree
react.parent = getParseTreeNode(parent)!;
setParent(react, getParseTreeNode(parent));
return react;
}

Expand Down
Loading

0 comments on commit 322515c

Please sign in to comment.