Skip to content

Commit 0825436

Browse files
authored
Support @MonotonicNonNull on static fields (#1166)
Fixes #1160 Add more tests for other cases also
1 parent 0161a80 commit 0825436

File tree

2 files changed

+109
-7
lines changed

2 files changed

+109
-7
lines changed

nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessAnalysis.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,8 @@ public NullnessStore getNullnessInfoBeforeNestedMethodNode(
220220
} else { // last element
221221
// must be a field that is final or annotated with @MonotonicNonNull
222222
if (!e.getKind().equals(ElementKind.FIELD)
223-
|| (!e.getModifiers().contains(Modifier.FINAL)
224-
&& !e.getAnnotationMirrors().stream()
225-
.anyMatch(
226-
am ->
227-
Nullness.isMonotonicNonNullAnnotation(
228-
am.getAnnotationType().toString())))) {
223+
|| !(e.getModifiers().contains(Modifier.FINAL)
224+
|| hasMonotonicNonNullAnnotation(e))) {
229225
allAPNonRootElementsAreFinalFields = false;
230226
}
231227
}
@@ -235,14 +231,21 @@ public NullnessStore getNullnessInfoBeforeNestedMethodNode(
235231
return e == null // This is the case for: this(.f)* where each f is a final field.
236232
|| e.getKind().equals(ElementKind.PARAMETER)
237233
|| e.getKind().equals(ElementKind.LOCAL_VARIABLE)
234+
// rooted at a static field that is either final or annotated with @MonotonicNonNull
238235
|| (e.getKind().equals(ElementKind.FIELD)
239-
&& e.getModifiers().contains(Modifier.FINAL));
236+
&& (e.getModifiers().contains(Modifier.FINAL)
237+
|| hasMonotonicNonNullAnnotation(e)));
240238
}
241239

242240
return handlerPredicate.test(ap);
243241
});
244242
}
245243

244+
private static boolean hasMonotonicNonNullAnnotation(Element e) {
245+
return e.getAnnotationMirrors().stream()
246+
.anyMatch(am -> Nullness.isMonotonicNonNullAnnotation(am.getAnnotationType().toString()));
247+
}
248+
246249
/**
247250
* Get the {@link Nullness} value of an access path ending in a field at some program point.
248251
*

nullaway/src/test/java/com/uber/nullaway/MonotonicNonNullTests.java

+99
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,105 @@ public void lambdas() {
6767
.doTest();
6868
}
6969

70+
@Test
71+
public void rootedAtParameterOrLocal() {
72+
defaultCompilationHelper
73+
.addSourceLines(
74+
"Test.java",
75+
"package com.uber;",
76+
"import com.uber.nullaway.annotations.MonotonicNonNull;",
77+
"class Test {",
78+
" @MonotonicNonNull Object f1;",
79+
" void testPositiveParam(Test t) {",
80+
" Runnable r = () -> {",
81+
" // BUG: Diagnostic contains: dereferenced expression t.f1",
82+
" t.f1.toString();",
83+
" };",
84+
" }",
85+
" void testNegativeParam(Test t) {",
86+
" t.f1 = new Object();",
87+
" Runnable r = () -> {",
88+
" t.f1.toString();",
89+
" };",
90+
" }",
91+
" void testPositiveLocal() {",
92+
" Test t = new Test();",
93+
" Runnable r = () -> {",
94+
" // BUG: Diagnostic contains: dereferenced expression t.f1",
95+
" t.f1.toString();",
96+
" };",
97+
" }",
98+
" void testNegativeLocal() {",
99+
" Test t = new Test();",
100+
" t.f1 = new Object();",
101+
" Runnable r = () -> {",
102+
" t.f1.toString();",
103+
" };",
104+
" }",
105+
"}")
106+
.doTest();
107+
}
108+
109+
@Test
110+
public void rootedAtStaticFinal() {
111+
defaultCompilationHelper
112+
.addSourceLines(
113+
"Test.java",
114+
"package com.uber;",
115+
"import com.uber.nullaway.annotations.MonotonicNonNull;",
116+
"class Test {",
117+
" @MonotonicNonNull Object f1;",
118+
" static final Test t = new Test();",
119+
" void testPositive() {",
120+
" Runnable r = () -> {",
121+
" // BUG: Diagnostic contains: dereferenced expression t.f1",
122+
" t.f1.toString();",
123+
" };",
124+
" }",
125+
" void testNegative() {",
126+
" t.f1 = new Object();",
127+
" Runnable r = () -> {",
128+
" t.f1.toString();",
129+
" };",
130+
" }",
131+
"}")
132+
.doTest();
133+
}
134+
135+
@Test
136+
public void monotonicNonNullStatic() {
137+
defaultCompilationHelper
138+
.addSourceLines(
139+
"Test.java",
140+
"package com.uber;",
141+
"import com.uber.nullaway.annotations.MonotonicNonNull;",
142+
"import org.jspecify.annotations.Nullable;",
143+
"class Test {",
144+
" @MonotonicNonNull static Object f1;",
145+
" @Nullable static Object f2;",
146+
" void testPositive() {",
147+
" Runnable r = () -> {",
148+
" // BUG: Diagnostic contains: dereferenced expression f1",
149+
" f1.toString();",
150+
" };",
151+
" }",
152+
" void testNegative() {",
153+
" f1 = new Object();",
154+
" Runnable r = () -> {",
155+
" f1.toString();",
156+
" };",
157+
" }",
158+
" void testPositive2() {",
159+
" f2 = new Object();",
160+
" Runnable r = () -> {",
161+
" // BUG: Diagnostic contains: dereferenced expression f2",
162+
" f2.toString();",
163+
" };",
164+
" }",
165+
"}")
166+
.doTest();
167+
}
168+
70169
@Test
71170
public void anonymousClasses() {
72171
defaultCompilationHelper

0 commit comments

Comments
 (0)