Skip to content

Commit b18fd72

Browse files
java-team-github-botError Prone Team
authored andcommitted
Automatic code cleanup.
PiperOrigin-RevId: 551640093
1 parent 25c361a commit b18fd72

File tree

4 files changed

+252
-0
lines changed

4 files changed

+252
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2023 The Error Prone Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.errorprone.bugpatterns;
18+
19+
import com.google.common.collect.ImmutableSet;
20+
import com.google.errorprone.BugPattern;
21+
import com.google.errorprone.BugPattern.SeverityLevel;
22+
import com.google.errorprone.VisitorState;
23+
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
24+
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
25+
import com.google.errorprone.matchers.Description;
26+
import com.google.errorprone.predicates.TypePredicate;
27+
import com.google.errorprone.predicates.TypePredicates;
28+
import com.google.errorprone.util.ASTHelpers;
29+
import com.sun.source.tree.MethodTree;
30+
import com.sun.source.tree.ModifiersTree;
31+
import com.sun.source.tree.VariableTree;
32+
import com.sun.tools.javac.code.Type;
33+
34+
/**
35+
* A Checker that catches {@link java.util.Optional}/{@link com.google.common.base.Optional} with
36+
* {@code Nullable} annotation.
37+
*/
38+
@BugPattern(
39+
summary =
40+
"Using an Optional variable which is expected to possibly be null is discouraged. It is"
41+
+ " best to indicate the absence of the value by assigning it an empty optional.",
42+
severity = SeverityLevel.WARNING)
43+
public final class NullableOptional extends BugChecker
44+
implements MethodTreeMatcher, VariableTreeMatcher {
45+
private static final TypePredicate IS_OPTIONAL_TYPE =
46+
TypePredicates.isExactTypeAny(
47+
ImmutableSet.of(
48+
java.util.Optional.class.getCanonicalName(),
49+
com.google.common.base.Optional.class.getCanonicalName()));
50+
51+
@Override
52+
public Description matchMethod(MethodTree tree, VisitorState state) {
53+
if (hasNullableAnnotation(tree.getModifiers())
54+
&& isOptional(ASTHelpers.getType(tree.getReturnType()), state)) {
55+
return describeMatch(tree);
56+
}
57+
return Description.NO_MATCH;
58+
}
59+
60+
@Override
61+
public Description matchVariable(VariableTree tree, VisitorState state) {
62+
if (hasNullableAnnotation(tree.getModifiers()) && isOptional(ASTHelpers.getType(tree), state)) {
63+
return describeMatch(tree);
64+
}
65+
return Description.NO_MATCH;
66+
}
67+
68+
/** Check if the input ModifiersTree has any kind of "Nullable" annotation. */
69+
private static boolean hasNullableAnnotation(ModifiersTree modifiersTree) {
70+
return ASTHelpers.getAnnotationWithSimpleName(modifiersTree.getAnnotations(), "Nullable")
71+
!= null;
72+
}
73+
74+
/**
75+
* Check if the input Type is either {@link java.util.Optional} or{@link
76+
* com.google.common.base.Optional}.
77+
*/
78+
private static boolean isOptional(Type type, VisitorState state) {
79+
return IS_OPTIONAL_TYPE.apply(type, state);
80+
}
81+
}

core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@
271271
import com.google.errorprone.bugpatterns.NullTernary;
272272
import com.google.errorprone.bugpatterns.NullableConstructor;
273273
import com.google.errorprone.bugpatterns.NullableOnContainingClass;
274+
import com.google.errorprone.bugpatterns.NullableOptional;
274275
import com.google.errorprone.bugpatterns.NullablePrimitive;
275276
import com.google.errorprone.bugpatterns.NullablePrimitiveArray;
276277
import com.google.errorprone.bugpatterns.NullableVoid;
@@ -979,6 +980,7 @@ public static ScannerSupplier warningChecks() {
979980
NotJavadoc.class,
980981
NullOptional.class,
981982
NullableConstructor.class,
983+
NullableOptional.class,
982984
NullablePrimitive.class,
983985
NullablePrimitiveArray.class,
984986
NullableVoid.class,
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2023 The Error Prone Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.errorprone.bugpatterns;
18+
19+
import com.google.errorprone.CompilationTestHelper;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
import org.junit.runners.JUnit4;
23+
24+
@RunWith(JUnit4.class)
25+
public final class NullableOptionalTest {
26+
27+
private final CompilationTestHelper compilationHelper =
28+
CompilationTestHelper.newInstance(NullableOptional.class, getClass());
29+
30+
@Test
31+
public void optionalFieldWithNullableAnnotation_showsError() {
32+
compilationHelper
33+
.addSourceLines(
34+
"Test.java",
35+
"import java.util.Optional;",
36+
"import javax.annotation.Nullable;",
37+
"final class Test {",
38+
" // BUG: Diagnostic contains:",
39+
" @Nullable private Optional<Object> foo;",
40+
"}")
41+
.doTest();
42+
}
43+
44+
@Test
45+
public void guavaOptionalFieldWithNullableAnnotation_showsError() {
46+
compilationHelper
47+
.addSourceLines(
48+
"Test.java",
49+
"import com.google.common.base.Optional;",
50+
"import javax.annotation.Nullable;",
51+
"final class Test {",
52+
" @Nullable",
53+
" // BUG: Diagnostic contains:",
54+
" private Optional<Object> foo;",
55+
"}")
56+
.doTest();
57+
}
58+
59+
@Test
60+
public void methodReturnsOptionalWithNullableAnnotation_showsError() {
61+
compilationHelper
62+
.addSourceLines(
63+
"Test.java",
64+
"import java.util.Optional;",
65+
"import javax.annotation.Nullable;",
66+
"final class Test {",
67+
" @Nullable",
68+
" // BUG: Diagnostic contains:",
69+
" private Optional<Object> foo() {",
70+
" return Optional.empty();",
71+
" }",
72+
"}")
73+
.doTest();
74+
}
75+
76+
@Test
77+
public void methodReturnsOptionalWithAnotherNullableAnnotation_showsError() {
78+
compilationHelper
79+
.addSourceLines(
80+
"Test.java",
81+
"import java.util.Optional;",
82+
"import org.jspecify.nullness.Nullable;",
83+
"final class Test {",
84+
" @Nullable",
85+
" // BUG: Diagnostic contains:",
86+
" private Optional<Object> foo() {",
87+
" return Optional.empty();",
88+
" }",
89+
"}")
90+
.doTest();
91+
}
92+
93+
@Test
94+
public void methodHasNullableOptionalAsParameter_showsError() {
95+
compilationHelper
96+
.addSourceLines(
97+
"Test.java",
98+
"import java.util.Optional;",
99+
"import javax.annotation.Nullable;",
100+
"final class Test {",
101+
" // BUG: Diagnostic contains:",
102+
" private void foo(@Nullable Optional<Object> optional) {}",
103+
"}")
104+
.doTest();
105+
}
106+
107+
@Test
108+
public void objectFieldWithNullableAnnotation_noError() {
109+
compilationHelper
110+
.addSourceLines(
111+
"Test.java",
112+
"import javax.annotation.Nullable;",
113+
"final class Test {",
114+
" @Nullable Object field;",
115+
"}")
116+
.doTest();
117+
}
118+
119+
@Test
120+
public void methodReturnsNonOptionalWithNullableAnnotation_noError() {
121+
compilationHelper
122+
.addSourceLines(
123+
"Test.java",
124+
"import javax.annotation.Nullable;",
125+
"final class Test {",
126+
" @Nullable",
127+
" private Object foo() {",
128+
" return null;",
129+
" }",
130+
"}")
131+
.doTest();
132+
}
133+
134+
@Test
135+
public void methodReturnsNonOptionalWithAnotherNullableAnnotation_noError() {
136+
compilationHelper
137+
.addSourceLines(
138+
"Test.java",
139+
"import org.jspecify.nullness.Nullable;",
140+
"final class Test {",
141+
" @Nullable",
142+
" private Object foo() {",
143+
" return null;",
144+
" }",
145+
"}")
146+
.doTest();
147+
}
148+
149+
@Test
150+
public void methodHasNullableNonOptionalAsParameter_noError() {
151+
compilationHelper
152+
.addSourceLines(
153+
"Test.java",
154+
"import org.jspecify.nullness.Nullable;",
155+
"final class Test {",
156+
" private void foo(@Nullable Object object) {}",
157+
"}")
158+
.doTest();
159+
}
160+
}

docs/bugpattern/NullableOptional.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
`Optional` is a container object which may or may not contain a value. The
2+
presence or absence of the contained value should be demonstrated by the
3+
`Optional` object itself.
4+
5+
Using an Optional variable which is expected to possibly be null is discouraged.
6+
An nullable Optional which uses `null` to indicate the absence of the value will
7+
lead to extra work for `null` checking when using the object and even cause
8+
exceptions such as `NullPointerException`. It is best to indicate the absence of
9+
the value by assigning it an empty optional.

0 commit comments

Comments
 (0)