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

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

Issue 137553005: DevTools: [JsDocValidator] Refactor JsDoc annotation checkers (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Address comments from sergeyv 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/ProtoFollowsExtendsChecker.java
diff --git a/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ProtoFollowsExtendsChecker.java b/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ProtoFollowsExtendsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a968c5b9c5b4f1bdbf387fc1d70eeff30bdbf17
--- /dev/null
+++ b/Source/devtools/scripts/jsdoc-validator/src/org/chromium/devtools/jsdoc/checks/ProtoFollowsExtendsChecker.java
@@ -0,0 +1,126 @@
+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.ObjectProperty;
+
+import org.chromium.devtools.jsdoc.checks.TypeRecord.InheritanceEntry;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public final class ProtoFollowsExtendsChecker extends ContextTrackingChecker {
+
+ private static final String PROTO_PROPERTY_NAME = "__proto__";
+ private final Set<TypeRecord> typesWithAssignedProto = new HashSet<>();
+
+ @Override
+ protected void enterNode(AstNode node) {
+ if (node.getType() == Token.COLON) {
+ handleColonNode((ObjectProperty) node);
+ return;
+ }
+ if (node.getType() == Token.ASSIGN) {
+ handleAssignment((Assignment) node);
+ return;
+ }
+ }
+
+ @Override
+ protected void leaveNode(AstNode node) {
+ if (node.getType() == Token.SCRIPT) {
+ checkFinished();
+ }
+ }
+
+ private void checkFinished() {
+ for (TypeRecord record : getState().getTypeRecordsByTypeName().values()) {
+ if (record.isInterface || typesWithAssignedProto.contains(record)) {
+ continue;
+ }
+ InheritanceEntry entry = record.getFirstExtendedType();
+ if (entry != null) {
+ getContext().reportErrorInNode(
+ entry.jsDocNode, entry.offsetInJsDocText,
+ String.format("No __proto__ assigned for type %s having @extends",
+ record.typeName));
+ }
+ }
+ }
+
+ private void handleColonNode(ObjectProperty node) {
+ ContextTrackingState state = getState();
+ TypeRecord type = state.getCurrentTypeRecord();
+ if (type == null) {
+ return;
+ }
+ String propertyName = state.getNodeText(node.getLeft());
+ if (!PROTO_PROPERTY_NAME.equals(propertyName)) {
+ return;
+ }
+ TypeRecord currentType = state.getCurrentTypeRecord();
+ if (currentType == null) {
+ // FIXME: __proto__: Foo.prototype not in an object literal for Bar.prototype.
+ return;
+ }
+ typesWithAssignedProto.add(currentType);
+ String value = state.getNodeText(node.getRight());
+ if (!AstUtil.isPrototypeName(value)) {
+ state.getContext().reportErrorInNode(
+ node.getRight(), 0, "__proto__ value is not a prototype");
+ return;
+ }
+ String superType = AstUtil.getTypeNameFromPrototype(value);
+ if (type.isInterface) {
+ state.getContext().reportErrorInNode(node.getLeft(), 0, String.format(
+ "__proto__ defined for interface %s", type.typeName));
+ return;
+ } else {
+ if (type.extendedTypes.isEmpty()) {
+ state.getContext().reportErrorInNode(node.getRight(), 0, String.format(
+ "No @extends annotation for %s extending %s", type.typeName, superType));
+ return;
+ }
+ }
+ // FIXME: Should we check that there is only one @extend-ed type
+ // for the non-interface |type|? Closure is supposed to do this anyway...
+ InheritanceEntry entry = type.getFirstExtendedType();
+ String extendedTypeName = entry.superTypeName;
+ if (!superType.equals(extendedTypeName)) {
+ state.getContext().reportErrorInNode(node.getRight(), 0, String.format(
+ "Supertype does not match %s declared in @extends for %s (line %d)",
+ extendedTypeName, type.typeName,
+ state.getContext().getPosition(entry.jsDocNode, entry.offsetInJsDocText).line));
+ }
+ }
+
+ private void handleAssignment(Assignment assignment) {
+ String assignedTypeName =
+ getState().getNodeText(AstUtil.getAssignedTypeNameNode(assignment));
+ if (assignedTypeName == null) {
+ return;
+ }
+ if (!AstUtil.isPrototypeName(assignedTypeName)) {
+ return;
+ }
+ AstNode prototypeValueNode = assignment.getRight();
+
+ if (prototypeValueNode.getType() == Token.OBJECTLIT) {
+ return;
+ }
+
+ // Foo.prototype = notObjectLiteral
+ ContextTrackingState state = getState();
+ TypeRecord type = state.getCurrentTypeRecord();
+ if (type == null) {
+ // Assigning a prototype for unknown type. Leave it to the closure compiler.
+ return;
+ }
+ if (!type.extendedTypes.isEmpty()) {
+ state.getContext().reportErrorInNode(prototypeValueNode, 0, String.format(
+ "@extends found for type %s but its prototype is not an object "
+ + "containing __proto__", AstUtil.getTypeNameFromPrototype(assignedTypeName)));
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698