diff --git a/pom.xml b/pom.xml
index 8e8be60..bbe0e50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
For example: Optional Provided by nb-additional-hints plugin Provided by nb-additional-hints plugin For example
Provided by nb-additional-hints plugin
"}) -public class ReturnNullForOptional { +public class ReturnForOptional { - @Hint(displayName = "#DN_ReturnNullForOptional", description = "#DESC_ReturnNullForOptional", category = "bugs", hintKind = Hint.Kind.INSPECTION, severity = Severity.ERROR) - @TriggerTreeKind(Tree.Kind.NULL_LITERAL) - public static ErrorDescription nullForOpt(HintContext ctx) { + @Hint(displayName = "#DN_ReturnForOptional", description = "#DESC_ReturnForOptional", category = "bugs", hintKind = Hint.Kind.INSPECTION, severity = Severity.ERROR) + @TriggerPattern("return $a") + public static ErrorDescription toFix(HintContext ctx) { - final TreePath nullTP = ctx.getPath(); - final TreePath returnTP = nullTP.getParentPath(); - if (null == returnTP || Tree.Kind.RETURN != returnTP.getLeaf().getKind()) { - return null; - } + final TreePath returnTP = ctx.getPath(); TreePath methodTP = getSurroundingMethod(returnTP); if (null == methodTP) { return null; } - ExecutableElement method = (ExecutableElement) ctx.getInfo().getTrees().getElement(methodTP); - final String returnTyp = method.getReturnType().toString(); - if (returnTyp.startsWith("java.util.Optional<") || returnTyp.equals("java.util.Optional")) { - Fix fix = rewriteFix(ctx, Bundle.DN_ReturnNullForOptional(), ctx.getPath(), "java.util.Optional.empty()"); - return forName(ctx, ctx.getPath(), Bundle.ERR_ReturnNullForOptional(), fix); + final CompilationInfo ci = ctx.getInfo(); + ExecutableElement method = (ExecutableElement) ci.getTrees().getElement(methodTP); + TypeMirror methodReturnType = method.getReturnType(); + final String returnType = ci.getTypes().erasure(methodReturnType).toString(); + if ("java.util.Optional".equals(returnType)) { + + TreePath path = ctx.getVariables().get("$a"); + //ignore existing "return Optional..."/"return o" + if (path.getLeaf().getKind()== Kind.IDENTIFIER || path.getLeaf().getKind()== Kind.METHOD_INVOCATION){ + TypeMirror typeMirror = ci.getTypes().erasure(ci.getTrees().getTypeMirror(path)); + if ("java.util.Optional".equals(typeMirror.toString())){ + return null; + } + } + + if (path.getLeaf().getKind() == Kind.NULL_LITERAL) { + Fix fix = rewriteFix(ctx, Bundle.DN_ReturnForOptionalEmpty(), returnTP, "return java.util.Optional.empty()"); + return forName(ctx, returnTP, Bundle.ERR_ReturnForOptionalEmpty(), fix); + } else { + Fix fixA = rewriteFix(ctx, Bundle.DN_ReturnForOptionalOfNullable(), returnTP, "return java.util.Optional.ofNullable($a)"); + Fix fixB = rewriteFix(ctx, Bundle.DN_ReturnForOptionalOf(), returnTP, "return java.util.Optional.of($a)"); + return forName(ctx, returnTP, Bundle.ERR_ReturnForOptionalNullable(), fixA, fixB); + } } return null; diff --git a/src/test/java/de/markiewb/netbeans/plugins/hints/optional/AccessOptionalTest.java b/src/test/java/de/markiewb/netbeans/plugins/hints/optional/AccessOptionalTest.java index 4afec71..9c04325 100644 --- a/src/test/java/de/markiewb/netbeans/plugins/hints/optional/AccessOptionalTest.java +++ b/src/test/java/de/markiewb/netbeans/plugins/hints/optional/AccessOptionalTest.java @@ -26,7 +26,7 @@ public void testCaseA() throws Exception { + " }\n" + "}\n") .sourceLevel("1.8") - .run(AccessOptional.class) + .run(CompareOptional.class) .findWarning("4:12-4:19:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_AccessOptional()) .applyFix() .assertCompilable() @@ -51,7 +51,7 @@ public void testCaseB() throws Exception { + " }\n" + "}\n") .sourceLevel("1.8") - .run(AccessOptional.class) + .run(CompareOptional.class) .findWarning("4:12-4:19:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_AccessOptional()) .applyFix() .assertCompilable() @@ -76,7 +76,7 @@ public void testCaseC() throws Exception { + " }\n" + "}\n") .sourceLevel("1.8") - .run(AccessOptional.class) + .run(CompareOptional.class) .findWarning("4:12-4:19:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_AccessOptional()) .applyFix() .assertCompilable() @@ -101,7 +101,7 @@ public void testCaseD() throws Exception { + " }\n" + "}\n") .sourceLevel("1.8") - .run(AccessOptional.class) + .run(CompareOptional.class) .findWarning("4:12-4:19:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_AccessOptional()) .applyFix() .assertCompilable() diff --git a/src/test/java/de/markiewb/netbeans/plugins/hints/optional/ReturnForOptionalTest.java b/src/test/java/de/markiewb/netbeans/plugins/hints/optional/ReturnForOptionalTest.java new file mode 100644 index 0000000..678f646 --- /dev/null +++ b/src/test/java/de/markiewb/netbeans/plugins/hints/optional/ReturnForOptionalTest.java @@ -0,0 +1,225 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package de.markiewb.netbeans.plugins.hints.optional; + +import java.util.Optional; +import org.junit.Test; +import org.netbeans.modules.java.hints.test.api.HintTest; + +/** + * + * @author markiewb + */ +public class ReturnForOptionalTest { + + @Test + public void testCaseSimpleCase_FullQualifiedName_null() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " public java.util.Optional method() {\n" + + " return null;\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("3:8-3:14:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalEmpty()) + .applyFix() + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public java.util.Optional method() {\n" + + " return Optional.empty();\n" + + " }\n" + + "}"); + + } + + @Test + public void testCaseSimpleCase_Nested_null() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " {{return null;}}\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("4:10-4:16:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalEmpty()) + .applyFix() + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " {{return Optional.empty();}}\n" + + " }\n" + + "}"); + + } + @Test + public void testCaseSimpleCase_ignoreExistingFQNOptional() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " public java.util.Optional method() {\n" + + " return java.util.Optional.empty();\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .assertWarnings(); + + } + @Test + public void testCaseSimpleCase_ignoreExistingOptional() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return Optional.empty();\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .assertWarnings(); + + } + + @Test + public void testCaseSimpleCase_ignoreExistingOptionalVariable() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " public java.util.Optional method(java.util.Optional o) {\n" + + " return o;\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .assertWarnings(); + + } + @Test + public void testCaseSimpleCase_notNull_2_of() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return \"ABC\";\n" + + " }\n" + + "}", false) + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("4:8-4:14:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalNullable()) + .applyFix(Bundle.DN_ReturnForOptionalOf()) + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return Optional.of(\"ABC\");\n" + + " }\n" + + "}"); + } + @Test + public void testCaseSimpleCase_notNull_2_ofNullable() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return \"ABC\";\n" + + " }\n" + + "}", false) + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("4:8-4:14:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalNullable()) + .applyFix(Bundle.DN_ReturnForOptionalOfNullable()) + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return Optional.ofNullable(\"ABC\");\n" + + " }\n" + + "}"); + } + + @Test + public void testCaseSimpleCase_null() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return null;\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("4:8-4:14:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalEmpty()) + .applyFix() + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return Optional.empty();\n" + + " }\n" + + "}"); + + } + + @Test + public void testCaseSimpleCase_null_nontypedMethod() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return null;\n" + + " }\n" + + "}") + .sourceLevel("1.8") + .run(ReturnForOptional.class) + .findWarning("4:8-4:14:error:" + de.markiewb.netbeans.plugins.hints.optional.Bundle.ERR_ReturnForOptionalEmpty()) + .applyFix() + .assertOutput("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional method() {\n" + + " return Optional.empty();\n" + + " }\n" + + "}"); + + } + + @Test + public void testCaseSimpleCase_null_typedMethod() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Optional;\n" + + "public class Test {\n" + + " public Optional