diff --git a/src/com/google/javascript/jscomp/ConvertToTypedInterface.java b/src/com/google/javascript/jscomp/ConvertToTypedInterface.java index 09d900a9b6f..c72905dc5fc 100644 --- a/src/com/google/javascript/jscomp/ConvertToTypedInterface.java +++ b/src/com/google/javascript/jscomp/ConvertToTypedInterface.java @@ -108,18 +108,18 @@ private void propagateJsdocAtName(NodeTraversal t, Node nameNode) { private static JSDocInfo getJSDocForRhs(NodeTraversal t, Node rhs, JSDocInfo oldJSDoc) { switch (NodeUtil.getKnownValueType(rhs)) { case BOOLEAN: - return getTypeJSDoc(oldJSDoc, "boolean"); + return getConstJSDoc(oldJSDoc, "boolean"); case NUMBER: - return getTypeJSDoc(oldJSDoc, "number"); + return getConstJSDoc(oldJSDoc, "number"); case STRING: - return getTypeJSDoc(oldJSDoc, "string"); + return getConstJSDoc(oldJSDoc, "string"); case NULL: - return getTypeJSDoc(oldJSDoc, "null"); + return getConstJSDoc(oldJSDoc, "null"); case VOID: - return getTypeJSDoc(oldJSDoc, "void"); + return getConstJSDoc(oldJSDoc, "void"); case OBJECT: if (rhs.isRegExp()) { - return getTypeJSDoc(oldJSDoc, new Node(Token.BANG, IR.string("RegExp"))); + return getConstJSDoc(oldJSDoc, new Node(Token.BANG, IR.string("RegExp"))); } break; case UNDETERMINED: @@ -161,7 +161,7 @@ private static JSDocInfo getJSDocForName(Var decl, JSDocInfo oldJSDoc) { default: break; } - return getTypeJSDoc(oldJSDoc, expr); + return getConstJSDoc(oldJSDoc, expr); } } @@ -474,13 +474,17 @@ private void removeEnumValues(Node objLit) { } private static boolean isInferrableConst(JSDocInfo jsdoc, Node nameNode) { - return jsdoc != null - && jsdoc.hasConstAnnotation() + boolean isConst = + nameNode.getParent().isConst() || (jsdoc != null && jsdoc.hasConstAnnotation()); + return isConst && !hasAnnotatedType(jsdoc) && !NodeUtil.isNamespaceDecl(nameNode); } private static boolean hasAnnotatedType(JSDocInfo jsdoc) { + if (jsdoc == null) { + return false; + } return jsdoc.hasType() || jsdoc.hasReturnType() || jsdoc.getParameterCount() > 0 @@ -518,33 +522,32 @@ private static JSDocInfo pullJsdocTypeFromAst( JSType type = nameNode.getJSType(); if (type == null) { compiler.report(JSError.make(nameNode, CONSTANT_WITHOUT_EXPLICIT_TYPE)); - return getTypeJSDoc(oldJSDoc, new Node(Token.STAR)); + return getConstJSDoc(oldJSDoc, new Node(Token.STAR)); } else { - return getTypeJSDoc(oldJSDoc, type.toNonNullAnnotationString()); + return getConstJSDoc(oldJSDoc, type.toNonNullAnnotationString()); } } private static JSDocInfo getAllTypeJSDoc() { - JSDocInfoBuilder builder = new JSDocInfoBuilder(false); - builder.recordType(asTypeExpression(new Node(Token.STAR))); - return builder.build(); + return getConstJSDoc(null, new Node(Token.STAR)); } private static JSTypeExpression asTypeExpression(Node typeAst) { return new JSTypeExpression(typeAst, ""); } - private static JSDocInfo getTypeJSDoc(JSDocInfo oldJSDoc, String contents) { - return getTypeJSDoc(oldJSDoc, Node.newString(contents)); + private static JSDocInfo getConstJSDoc(JSDocInfo oldJSDoc, String contents) { + return getConstJSDoc(oldJSDoc, Node.newString(contents)); } - private static JSDocInfo getTypeJSDoc(JSDocInfo oldJSDoc, Node typeAst) { - return getTypeJSDoc(oldJSDoc, asTypeExpression(typeAst)); + private static JSDocInfo getConstJSDoc(JSDocInfo oldJSDoc, Node typeAst) { + return getConstJSDoc(oldJSDoc, asTypeExpression(typeAst)); } - private static JSDocInfo getTypeJSDoc(JSDocInfo oldJSDoc, JSTypeExpression newType) { - JSDocInfoBuilder builder = JSDocInfoBuilder.copyFrom(oldJSDoc); + private static JSDocInfo getConstJSDoc(JSDocInfo oldJSDoc, JSTypeExpression newType) { + JSDocInfoBuilder builder = JSDocInfoBuilder.maybeCopyFrom(oldJSDoc); builder.recordType(newType); + builder.recordConstancy(); return builder.build(); } } diff --git a/test/com/google/javascript/jscomp/ConvertToTypedInterfaceTest.java b/test/com/google/javascript/jscomp/ConvertToTypedInterfaceTest.java index d1c1787a250..9b43272a31c 100644 --- a/test/com/google/javascript/jscomp/ConvertToTypedInterfaceTest.java +++ b/test/com/google/javascript/jscomp/ConvertToTypedInterfaceTest.java @@ -61,6 +61,10 @@ public void testSimpleConstJsdocPropagation() { ConvertToTypedInterface.CONSTANT_WITHOUT_EXPLICIT_TYPE); } + public void testConstKeywordJsdocPropagation() { + testEs6("const x = 5;", "/** @const {number} */ var x;"); + } + public void testThisPropertiesInConstructors() { test( "/** @constructor */ function Foo() { /** @const {number} */ this.x; }", @@ -68,7 +72,7 @@ public void testThisPropertiesInConstructors() { test( "/** @constructor */ function Foo() { this.x; }", - "/** @constructor */ function Foo() {} \n /** @type {*} */ Foo.prototype.x;"); + "/** @constructor */ function Foo() {} \n /** @const {*} */ Foo.prototype.x;"); test( "/** @constructor */ function Foo() { /** @type {?number} */ this.x = null; this.x = 5; }", @@ -314,7 +318,7 @@ public void testEnums() { test( "var x = 7; /** @enum {number} */ var E = { A: x };", - "/** @type {*} */ var x; /** @enum {number} */ var E = { A: 0 };"); + "/** @const {*} */ var x; /** @enum {number} */ var E = { A: 0 };"); } public void testTryCatch() { @@ -382,7 +386,7 @@ public void testLoops() { test("while (true) { foo(); break; }", "{}"); test("for (var i = 0; i < 10; i++) { var field = 88; }", - "/** @type {*} */ var i; {/** @type {*} */ var field;}"); + "/** @const {*} */ var i; {/** @const {*} */ var field;}"); test( "while (i++ < 10) { var /** number */ field = i; }", @@ -402,7 +406,7 @@ public void testLoops() { test( "for (var i = 0; i < 10; i++) { var /** number */ field = i; }", - "/** @type {*} */ var i; { /** @type {number } */ var field; }"); + "/** @const {*} */ var i; { /** @type {number } */ var field; }"); } public void testNamespaces() { @@ -428,7 +432,7 @@ public void testRemoveRepeatedProperties() { test( "/** @const */ var ns = {}; ns.x = 5; ns.x = 7;", - "/** @const */ var ns = {}; /** @type {*} */ ns.x;"); + "/** @const */ var ns = {}; /** @const {*} */ ns.x;"); testEs6( "const ns = {}; /** @type {number} */ ns.x = 5; ns.x = 7;", @@ -440,9 +444,9 @@ public void testRemoveRepeatedDeclarations() { test("/** @type {number} */ var x = 4; x = 7;", "/** @type {number} */ var x;"); - test("var x = 4; var x = 7;", "/** @type {*} */ var x;"); + test("var x = 4; var x = 7;", "/** @const {*} */ var x;"); - test("var x = 4; x = 7;", "/** @type {*} */ var x;"); + test("var x = 4; x = 7;", "/** @const {*} */ var x;"); } public void testDontRemoveGoogModuleContents() {