| Index: third_party/WebKit/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
|
| diff --git a/third_party/WebKit/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java b/third_party/WebKit/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1273cb2c20ae8be6f6a7cbf775a2bcac5984f022
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
|
| @@ -0,0 +1,159 @@
|
| +package org.chromium.devtools.jsdoc.checks;
|
| +
|
| +import com.google.javascript.jscomp.NodeUtil;
|
| +import com.google.javascript.rhino.JSDocInfo;
|
| +import com.google.javascript.rhino.Node;
|
| +import com.google.javascript.rhino.Token;
|
| +
|
| +import org.chromium.devtools.jsdoc.ValidationCheck;
|
| +import org.chromium.devtools.jsdoc.ValidatorContext;
|
| +
|
| +import java.util.ArrayList;
|
| +import java.util.List;
|
| +
|
| +public class ContextTrackingValidationCheck extends ValidationCheck {
|
| +
|
| + private ContextTrackingState state;
|
| + private final List<ContextTrackingChecker> clients = new ArrayList<>(5);
|
| +
|
| + @Override
|
| + protected void setContext(ValidatorContext context) {
|
| + super.setContext(context);
|
| + state = new ContextTrackingState(context);
|
| + registerClient(new ProtoFollowsExtendsChecker());
|
| + registerClient(new MethodAnnotationChecker());
|
| + registerClient(new FunctionReceiverChecker());
|
| + registerClient(new DisallowedGlobalPropertiesChecker());
|
| + }
|
| +
|
| + @Override
|
| + public void doVisit(Node node) {
|
| + switch (node.getType()) {
|
| + case Token.ASSIGN:
|
| + case Token.VAR:
|
| + enterAssignOrVarNode(node);
|
| + break;
|
| + case Token.FUNCTION:
|
| + enterFunctionNode(node);
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| +
|
| + enterNode(node);
|
| + }
|
| +
|
| + @Override
|
| + public void didVisit(Node node) {
|
| + leaveNode(node);
|
| +
|
| + switch (node.getType()) {
|
| + case Token.ASSIGN:
|
| + leaveAssignNode(node);
|
| + break;
|
| + case Token.FUNCTION:
|
| + leaveFunctionNode(node);
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| +
|
| + public void registerClient(ContextTrackingChecker client) {
|
| + this.clients.add(client);
|
| + client.setState(state);
|
| + }
|
| +
|
| + private void enterNode(Node node) {
|
| + for (ContextTrackingChecker client : clients) {
|
| + client.enterNode(node);
|
| + }
|
| + }
|
| +
|
| + private void leaveNode(Node node) {
|
| + for (ContextTrackingChecker client : clients) {
|
| + client.leaveNode(node);
|
| + }
|
| + }
|
| +
|
| + private void enterFunctionNode(Node node) {
|
| + TypeRecord parentType = state.getCurrentFunctionRecord() == null
|
| + ? state.getCurrentTypeRecord()
|
| + : null;
|
| + Node nameNode = AstUtil.getFunctionNameNode(node);
|
| + String functionName = nameNode == null ? null : state.getNodeText(nameNode);
|
| + FunctionRecord functionRecord = new FunctionRecord(
|
| + node,
|
| + functionName,
|
| + getFunctionParameterNames(node),
|
| + parentType,
|
| + state.getCurrentFunctionRecord());
|
| + state.pushFunctionRecord(functionRecord);
|
| + rememberTypeRecordIfNeeded(functionName, functionRecord.info);
|
| + }
|
| +
|
| + @SuppressWarnings("unused")
|
| + private void leaveFunctionNode(Node node) {
|
| + state.functionRecords.removeLast();
|
| + }
|
| +
|
| + private void enterAssignOrVarNode(Node node) {
|
| + String assignedTypeName = getAssignedTypeName(node);
|
| + if (assignedTypeName == null) {
|
| + return;
|
| + }
|
| + if (AstUtil.isPrototypeName(assignedTypeName)) {
|
| + // MyType.prototype = ...
|
| + String typeName = AstUtil.getTypeNameFromPrototype(assignedTypeName);
|
| + TypeRecord typeRecord = state.typeRecordsByTypeName.get(typeName);
|
| + // We should push anything here to maintain a valid current type record.
|
| + state.pushTypeRecord(typeRecord);
|
| + state.pushFunctionRecord(null);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + private void leaveAssignNode(Node assignment) {
|
| + String assignedTypeName = getAssignedTypeName(assignment);
|
| + if (assignedTypeName == null) {
|
| + return;
|
| + }
|
| + if (AstUtil.isPrototypeName(assignedTypeName)) {
|
| + // Remove the current type record when leaving prototype object.
|
| + state.typeRecords.removeLast();
|
| + state.functionRecords.removeLast();
|
| + return;
|
| + }
|
| + }
|
| +
|
| + private String getAssignedTypeName(Node assignment) {
|
| + Node node = AstUtil.getAssignedTypeNameNode(assignment);
|
| + return getNodeText(node);
|
| + }
|
| +
|
| + private List<String> getFunctionParameterNames(Node functionNode) {
|
| + List<String> parameterNames = new ArrayList<String>();
|
| + Node parametersNode = NodeUtil.getFunctionParameters(functionNode);
|
| + for (int i = 0, childCount = parametersNode.getChildCount(); i < childCount; ++i) {
|
| + Node paramNode = parametersNode.getChildAtIndex(i);
|
| + String paramName = state.getContext().getNodeText(paramNode);
|
| + parameterNames.add(paramName);
|
| + }
|
| + return parameterNames;
|
| + }
|
| +
|
| + private boolean rememberTypeRecordIfNeeded(String typeName, JSDocInfo info) {
|
| + if (info == null) {
|
| + return false;
|
| + }
|
| + if (typeName == null) {
|
| + return info.isConstructor() || info.isInterface();
|
| + }
|
| + if (!info.isConstructor() && !info.isInterface()) {
|
| + return false;
|
| + }
|
| + TypeRecord record = new TypeRecord(typeName, info);
|
| + state.typeRecordsByTypeName.put(typeName, record);
|
| + return true;
|
| + }
|
| +}
|
|
|