1 package org.chromium.devtools.jsdoc.checks;
3 import com.google.javascript.rhino.JSDocInfo;
4 import com.google.javascript.rhino.Node;
5 import com.google.javascript.rhino.Token;
7 import org.chromium.devtools.jsdoc.ValidationCheck;
8 import org.chromium.devtools.jsdoc.ValidatorContext;
10 import java.util.ArrayList;
11 import java.util.List;
13 public class ContextTrackingValidationCheck extends ValidationCheck {
15 private ContextTrackingState state;
16 private final List<ContextTrackingChecker> clients = new ArrayList<>(5);
19 protected void setContext(ValidatorContext context) {
20 super.setContext(context);
21 state = new ContextTrackingState(context);
22 registerClient(new ProtoFollowsExtendsChecker());
23 registerClient(new MethodAnnotationChecker());
24 registerClient(new FunctionReceiverChecker());
28 public void doVisit(Node node) {
29 switch (node.getType()) {
32 enterAssignOrVarNode(node);
35 enterFunctionNode(node);
45 public void didVisit(Node node) {
48 switch (node.getType()) {
50 leaveAssignNode(node);
53 leaveFunctionNode(node);
60 public void registerClient(ContextTrackingChecker client) {
61 this.clients.add(client);
62 client.setState(state);
65 private void enterNode(Node node) {
66 for (ContextTrackingChecker client : clients) {
67 client.enterNode(node);
71 private void leaveNode(Node node) {
72 for (ContextTrackingChecker client : clients) {
73 client.leaveNode(node);
77 private void enterFunctionNode(Node node) {
78 TypeRecord parentType = state.getCurrentFunctionRecord() == null
79 ? state.getCurrentTypeRecord()
81 Node nameNode = AstUtil.getFunctionNameNode(node);
82 String functionName = nameNode == null ? null : state.getNodeText(nameNode);
83 FunctionRecord functionRecord = new FunctionRecord(
87 state.getCurrentFunctionRecord());
88 state.pushFunctionRecord(functionRecord);
89 rememberTypeRecordIfNeeded(functionName, functionRecord.info);
92 @SuppressWarnings("unused")
93 private void leaveFunctionNode(Node node) {
94 state.functionRecords.removeLast();
97 private void enterAssignOrVarNode(Node node) {
98 String assignedTypeName = getAssignedTypeName(node);
99 if (assignedTypeName == null) {
102 if (AstUtil.isPrototypeName(assignedTypeName)) {
103 // MyType.prototype = ...
104 String typeName = AstUtil.getTypeNameFromPrototype(assignedTypeName);
105 TypeRecord typeRecord = state.typeRecordsByTypeName.get(typeName);
106 // We should push anything here to maintain a valid current type record.
107 state.pushTypeRecord(typeRecord);
108 state.pushFunctionRecord(null);
113 private void leaveAssignNode(Node assignment) {
114 String assignedTypeName = getAssignedTypeName(assignment);
115 if (assignedTypeName == null) {
118 if (AstUtil.isPrototypeName(assignedTypeName)) {
119 // Remove the current type record when leaving prototype object.
120 state.typeRecords.removeLast();
121 state.functionRecords.removeLast();
126 private String getAssignedTypeName(Node assignment) {
127 Node node = AstUtil.getAssignedTypeNameNode(assignment);
128 return getNodeText(node);
131 private boolean rememberTypeRecordIfNeeded(String typeName, JSDocInfo info) {
135 if (typeName == null) {
136 return info.isConstructor() || info.isInterface();
138 if (!info.isConstructor() && !info.isInterface()) {
141 TypeRecord record = new TypeRecord(typeName, info);
142 state.typeRecordsByTypeName.put(typeName, record);