Node assign = IR.assign(qualifiedMemberName, method);
assign.useSourceInfoIfMissingFromForTree(member);
JSDocInfo info = member.getJSDocInfo();
if (member.isStaticMember() && NodeUtil.referencesThis(assign.getLastChild())) {
JSDocInfoBuilder memberDoc;
if (info == null) {
memberDoc = new JSDocInfoBuilder(true);
} else {
memberDoc = JSDocInfoBuilder.copyFrom(info);
}
memberDoc.recordThisType(
new JSTypeExpression(new Node(Token.BANG, new Node(Token.QMARK)),
member.getSourceFileName()));
info = memberDoc.build(assign);
}
if (info != null) {
info.setAssociatedNode(assign);
assign.setJSDocInfo(info);
}
Node newNode = NodeUtil.newExpr(assign);
insertionPoint.getParent().addChildAfter(newNode, insertionPoint);
insertionPoint = newNode;
}
}
// Rewrite constructor
if (constructor == null) {
Node body = IR.block();
if (!superClassName.isEmpty()) {
Node superCall = baseCall(classNode, "constructor", null);
body.addChildToBack(IR.exprResult(superCall));
}
Node name = anonymous
? IR.name("").srcref(className) : className.detachFromParent();
constructor = IR.function(
name,
IR.paramList(),
body).useSourceInfoIfMissingFromForTree(classNode);
}
JSDocInfo classJSDoc = classNode.getJSDocInfo();
JSDocInfoBuilder newInfo = (classJSDoc != null) ?
JSDocInfoBuilder.copyFrom(classJSDoc) :
new JSDocInfoBuilder(true);
newInfo.recordConstructor();
if (!superClassName.isEmpty()) {
if (newInfo.isInterfaceRecorded()) {
newInfo.recordExtendedInterface(new JSTypeExpression(new Node(Token.BANG,
IR.string(superClassString)),
superClassName.getSourceFileName()));
} else {
Node inherits = IR.call(
NodeUtil.newQName(compiler, INHERITS),
NodeUtil.newQName(compiler, fullClassName),
NodeUtil.newQName(compiler, superClassString));
Node inheritsCall = IR.exprResult(inherits);
inheritsCall.useSourceInfoIfMissingFromForTree(classNode);
Node enclosingStatement = NodeUtil.getEnclosingStatement(classNode);
enclosingStatement.getParent().addChildAfter(inheritsCall, enclosingStatement);
newInfo.recordBaseType(new JSTypeExpression(new Node(Token.BANG,
IR.string(superClassString)),
superClassName.getSourceFileName()));
Node copyProps = IR.call(
NodeUtil.newQName(compiler, COPY_PROP),
NodeUtil.newQName(compiler, fullClassName),
NodeUtil.newQName(compiler, superClassString));
copyProps.useSourceInfoIfMissingFromForTree(classNode);
enclosingStatement.getParent().addChildAfter(
IR.exprResult(copyProps).srcref(classNode), enclosingStatement);
}
}
// Classes are @struct by default.
if (!newInfo.isUnrestrictedRecorded() && !newInfo.isDictRecorded() &&
!newInfo.isStructRecorded()) {
newInfo.recordStruct();
}
if (ctorJSDocInfo != null) {
newInfo.recordSuppressions(ctorJSDocInfo.getSuppressions());
for (String param : ctorJSDocInfo.getParameterNames()) {
newInfo.recordParameter(param, ctorJSDocInfo.getParameterType(param));
}
}
insertionPoint = constructor;
if (NodeUtil.isStatement(classNode)) {
constructor.getFirstChild().setString("");
Node ctorVar = IR.var(IR.name(fullClassName), constructor);
ctorVar.useSourceInfoIfMissingFromForTree(classNode);
parent.replaceChild(classNode, ctorVar);
} else {
parent.replaceChild(classNode, constructor);
}
if (NodeUtil.isStatement(constructor)) {
insertionPoint.setJSDocInfo(newInfo.build(insertionPoint));
} else if (parent.isName()) {
// The constructor function is the RHS of a var statement.
// Add the JSDoc to the VAR node.
Node var = parent.getParent();
var.setJSDocInfo(newInfo.build(var));
} else if (constructor.getParent().isName()) {
// Is a newly created VAR node.
Node var = constructor.getParent().getParent();
var.setJSDocInfo(newInfo.build(var));
} else if (parent.isAssign()) {
// The constructor function is the RHS of an assignment.
// Add the JSDoc to the ASSIGN node.
parent.setJSDocInfo(newInfo.build(parent));
} else {
throw new IllegalStateException("Unexpected parent node " + parent);
}
compiler.reportCodeChange();