Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1298)

Unified Diff: Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java

Issue 137553005: DevTools: [JsDocValidator] Refactor JsDoc annotation checkers (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Limit the thread count by the number of validated files Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
diff --git a/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java b/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
new file mode 100644
index 0000000000000000000000000000000000000000..7401b98063444aaca65958f020a47123b0a32bf7
--- /dev/null
+++ b/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
@@ -0,0 +1,243 @@
+package org.chromium.devtools.jsdoc.checks;
+
+import com.google.javascript.rhino.head.Token;
+import com.google.javascript.rhino.head.ast.Assignment;
+import com.google.javascript.rhino.head.ast.AstNode;
+import com.google.javascript.rhino.head.ast.Comment;
+import com.google.javascript.rhino.head.ast.FunctionNode;
+import com.google.javascript.rhino.head.ast.ObjectProperty;
+
+import org.chromium.devtools.jsdoc.ValidationCheck;
+import org.chromium.devtools.jsdoc.ValidatorContext;
+import org.chromium.devtools.jsdoc.checks.TypeRecord.InheritanceEntry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ContextTrackingValidationCheck extends ValidationCheck {
+
+ private static final Pattern EXTENDS_PATTERN =
+ Pattern.compile("@extends\\s+\\{\\s*([^\\s}]+)\\s*\\}");
+ private static final Pattern IMPLEMENTS_PATTERN =
+ Pattern.compile("@implements\\s+\\{\\s*([^\\s}]+)\\s*\\}");
+ private static final Pattern RETURN_PATTERN =
+ Pattern.compile("@return\\s+\\{\\s*(.+)\\s*\\}");
+ 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 RequiredThisAnnotationChecker());
+ registerClient(new ReturnAnnotationChecker());
+ }
+
+ @Override
+ public void doVisit(AstNode node) {
+ switch (node.getType()) {
+ case Token.ASSIGN:
+ enterAssignNode((Assignment) node);
+ break;
+ case Token.FUNCTION:
+ enterFunctionNode((FunctionNode) node);
+ break;
+ }
+
+ enterNode(node);
+ }
+
+ @Override
+ public void didVisit(AstNode node) {
+ leaveNode(node);
+
+ switch (node.getType()) {
+ case Token.ASSIGN:
+ leaveAssignNode((Assignment) node);
+ break;
+ case Token.FUNCTION:
+ leaveFunctionNode((FunctionNode) node);
+ break;
+ }
+ }
+
+ public void registerClient(ContextTrackingChecker client) {
+ this.clients.add(client);
+ client.setState(state);
+ }
+
+ private void enterNode(AstNode node) {
+ for (ContextTrackingChecker client : clients) {
+ client.enterNode(node);
+ }
+ }
+
+ private void leaveNode(AstNode node) {
+ for (ContextTrackingChecker client : clients) {
+ client.leaveNode(node);
+ }
+ }
+
+ private void enterFunctionNode(FunctionNode node) {
+ Comment jsDocNode = getJsDocNode(node);
+ AstNode nameNode = AstUtil.getFunctionNameNode(node);
+
+ // It can be a type declaration: /** @constructor */ function MyType() {...}.
+ boolean isConstructor = nameNode == null
eustas 2014/01/28 08:47:21 nodeName != null && ...
apavlov 2014/01/28 10:40:17 Done.
+ ? false
+ : rememberTypeRecordIfNeeded(getNodeText(nameNode), jsDocNode);
+ state.addFunctionRecord(new FunctionRecord(
+ node,
+ isConstructor,
+ getReturnType(jsDocNode),
+ state.getCurrentTypeRecord(),
+ state.getCurrentFunctionRecord()));
+ }
+
+ @SuppressWarnings("unused")
+ private void leaveFunctionNode(FunctionNode node) {
+ state.functionRecords.removeLast();
+ }
+
+ private String getReturnType(Comment jsDocNode) {
+ if (jsDocNode == null) {
+ return null;
+ }
+ String jsDoc = getNodeText(jsDocNode);
+ Matcher m = RETURN_PATTERN.matcher(jsDoc);
+ if (!m.find()) {
+ return null;
+ }
+ return m.group(1);
+ }
+
+ private void enterAssignNode(Assignment assignment) {
+ String assignedTypeName = getAssignedTypeName(assignment);
+ if (assignedTypeName == null) {
+ return;
+ }
+ if (AstUtil.isPrototypeName(assignedTypeName)) {
+ // MyType.prototype = ...
+ String typeName = AstUtil.getTypeNameFromPrototype(assignedTypeName);
+ TypeRecord typeRecord = state.typeRecordsByTypeName.get(typeName);
+ if (typeRecord != null) {
+ typeRecord.setPrototypeNode(assignment.getRight());
+ }
+ // We should push anything here to maintain a valid current type record.
+ state.addTypeRecord(typeRecord);
+ return;
+ }
+
+ if (assignment.getRight().getType() == Token.FUNCTION) {
+ // MyType = function() {...}
+ rememberTypeRecordIfNeeded(assignedTypeName, getJsDocNode(assignment));
+ }
+
+ }
+
+ private void leaveAssignNode(Assignment assignment) {
+ String assignedTypeName = getAssignedTypeName(assignment);
+ if (assignedTypeName == null) {
+ return;
+ }
+ if (AstUtil.isPrototypeName(assignedTypeName)
+ && assignment.getRight().getType() == Token.OBJECTLIT) {
+ // Remove the current type record when leaving prototype object.
+ state.typeRecords.removeLast();
+ return;
+ }
+ }
+
+ private String getAssignedTypeName(Assignment assignment) {
+ AstNode node = AstUtil.getAssignedTypeNameNode(assignment);
+ return getNodeText(node);
+ }
+
+ private boolean rememberTypeRecordIfNeeded(String typeName, Comment jsDocNode) {
+ String jsDoc = getNodeText(jsDocNode);
+ if (!isConstructor(jsDoc) && !isInterface(jsDoc)) {
+ return false;
+ }
+ TypeRecord record = new TypeRecord(
+ typeName,
+ isInterface(jsDoc),
+ getExtendsEntries(jsDocNode),
+ getImplementsEntries(jsDocNode));
+ state.typeRecordsByTypeName.put(typeName, record);
+ return true;
+ }
+
+ private static boolean isInterface(String jsDoc) {
+ return jsDoc != null && jsDoc.contains("@interface");
+ }
+
+ private static boolean isConstructor(String jsDoc) {
+ return jsDoc != null && jsDoc.contains("@constructor");
+ }
+
+ private static Comment getJsDocNode(AstNode node) {
+ if (node.getType() == Token.FUNCTION) {
+ return getJsDocNode((FunctionNode) node);
+ }
+ return node.getJsDocNode();
+ }
+
+ private static Comment getJsDocNode(FunctionNode functionNode) {
+ Comment jsDocNode = functionNode.getJsDocNode();
+ if (jsDocNode != null) {
+ return jsDocNode;
+ }
+
+ // reader.onloadend = function() {...}
+ if (AstUtil.hasParentOfType(functionNode, Token.ASSIGN)) {
+ Assignment assignment = (Assignment) functionNode.getParent();
+ if (assignment.getRight() == functionNode) {
+ jsDocNode = assignment.getJsDocNode();
+ if (jsDocNode != null) {
+ return jsDocNode;
+ }
+ }
+ }
+
+ if (AstUtil.hasParentOfType(functionNode, Token.COLON)) {
+ jsDocNode = ((ObjectProperty) functionNode.getParent()).getLeft().getJsDocNode();
+ if (jsDocNode != null) {
+ return jsDocNode;
+ }
+ }
+ return null;
+ }
+
+ private List<InheritanceEntry> getImplementsEntries(Comment jsDocNode) {
+ if (jsDocNode == null) {
+ return Collections.emptyList();
+ }
+ String jsDoc = getNodeText(jsDocNode);
+ if (!isConstructor(jsDoc)) {
+ return null;
+ }
+ return collectInheritanceEntries(IMPLEMENTS_PATTERN, jsDocNode, jsDoc);
+ }
+
+ private List<InheritanceEntry> getExtendsEntries(Comment jsDocNode) {
+ if (jsDocNode == null) {
+ return Collections.emptyList();
+ }
+ return collectInheritanceEntries(EXTENDS_PATTERN, jsDocNode, getNodeText(jsDocNode));
+ }
+
+ private List<InheritanceEntry> collectInheritanceEntries(
+ Pattern pattern, Comment jsDocNode, String jsDoc) {
+ List<InheritanceEntry> result = new ArrayList<>(2);
+ Matcher matcher = pattern.matcher(jsDoc);
+ while (matcher.find()) {
+ result.add(new InheritanceEntry(matcher.group(1), jsDocNode, matcher.start(1)));
+ }
+
+ return result;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698