diff --git a/src/com/google/javascript/refactoring/Matchers.java b/src/com/google/javascript/refactoring/Matchers.java index f1d0ff6cf49..85992d31db1 100644 --- a/src/com/google/javascript/refactoring/Matchers.java +++ b/src/com/google/javascript/refactoring/Matchers.java @@ -340,7 +340,7 @@ public static Matcher jsDocType(final String type) { JSDocInfo jsDoc = node.getParent().isVar() ? node.getParent().getJSDocInfo() : node.getJSDocInfo(); JSType jsType = node.getJSType(); - return jsDoc != null && jsType != null + return jsDoc != null && jsDoc.hasType() && jsType != null && providedJsType.isEquivalentTo(jsType.restrictByNotNullOrUndefined()); } }; diff --git a/src/com/google/javascript/refactoring/SuggestedFix.java b/src/com/google/javascript/refactoring/SuggestedFix.java index 3432be163cb..247162ea943 100644 --- a/src/com/google/javascript/refactoring/SuggestedFix.java +++ b/src/com/google/javascript/refactoring/SuggestedFix.java @@ -465,17 +465,20 @@ public Builder changeJsDocType(Node n, AbstractCompiler compiler, String type) { String originalComment = info.getOriginalCommentString(); int originalPosition = info.getOriginalCommentPosition(); - // TODO(mknichel): Support multiline @type annotations. - Pattern typeDocPattern = Pattern.compile( - "@(type|private|protected|public|const|return) *\\{?[^\\s}]+\\}?"); - Matcher m = typeDocPattern.matcher(originalComment); - while (m.find()) { - replacements.put( - n.getSourceFileName(), - new CodeReplacement( - originalPosition + m.start(), - m.end() - m.start(), - "@" + m.group(1) + " {" + type + "}")); + // If there isn't an original comment, then it is generated and we can't make a change. + if (originalComment != null) { + // TODO(mknichel): Support multiline @type annotations. + Pattern typeDocPattern = Pattern.compile( + "@(type|private|protected|public|const|return) *\\{?[^\\s}@]+\\}?"); + Matcher m = typeDocPattern.matcher(originalComment); + while (m.find()) { + replacements.put( + n.getSourceFileName(), + new CodeReplacement( + originalPosition + m.start(), + m.end() - m.start(), + "@" + m.group(1) + " {" + type + "}")); + } } return this; diff --git a/test/com/google/javascript/refactoring/MatchersTest.java b/test/com/google/javascript/refactoring/MatchersTest.java index 141fdf65f23..8dcc8d11847 100644 --- a/test/com/google/javascript/refactoring/MatchersTest.java +++ b/test/com/google/javascript/refactoring/MatchersTest.java @@ -271,7 +271,7 @@ public void testPrototypeDeclarations() { } @Test - public void testJsDocType() { + public void testJsDocType1() { String input = "/** @type {number} */ var foo = 1;"; Compiler compiler = getCompiler(input); Node root = compileToScriptRoot(compiler); @@ -280,6 +280,46 @@ public void testJsDocType() { assertFalse(Matchers.jsDocType("string").matches(node, new NodeMetadata(compiler))); } + @Test + public void testJsDocType2() { + String input = "/** @type {number} */ let foo = 1;"; + Compiler compiler = getCompiler(input); + Node root = compileToScriptRoot(compiler); + Node node = root.getFirstFirstChild(); + assertTrue(Matchers.jsDocType("number").matches(node, new NodeMetadata(compiler))); + assertFalse(Matchers.jsDocType("string").matches(node, new NodeMetadata(compiler))); + } + + @Test + public void testJsDocType3() { + String input = "/** @type {number} */ const foo = 1;"; + Compiler compiler = getCompiler(input); + Node root = compileToScriptRoot(compiler); + Node node = root.getFirstFirstChild(); + assertTrue(Matchers.jsDocType("number").matches(node, new NodeMetadata(compiler))); + assertFalse(Matchers.jsDocType("string").matches(node, new NodeMetadata(compiler))); + } + + @Test + public void testJsDocTypeNoMatch1() { + String input = "/** @const */ var foo = 1;"; + Compiler compiler = getCompiler(input); + Node root = compileToScriptRoot(compiler); + Node node = root.getFirstFirstChild(); + assertFalse(Matchers.jsDocType("number").matches(node, new NodeMetadata(compiler))); + assertFalse(Matchers.jsDocType("string").matches(node, new NodeMetadata(compiler))); + } + + @Test + public void testJsDocTypeNoMatch2() { + String input = "const foo = 1;"; + Compiler compiler = getCompiler(input); + Node root = compileToScriptRoot(compiler); + Node node = root.getFirstFirstChild(); + assertFalse(Matchers.jsDocType("number").matches(node, new NodeMetadata(compiler))); + assertFalse(Matchers.jsDocType("string").matches(node, new NodeMetadata(compiler))); + } + @Test public void testPropertyAccess() { String input = "foo.bar.method();";