Skip to content

Commit 23f1482

Browse files
authored
Merge branch 'master' into method-type-param
2 parents a814475 + 6165b8e commit 23f1482

File tree

20 files changed

+362
-100
lines changed

20 files changed

+362
-100
lines changed

AGENTS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Testing
2+
3+
To run only the tests for the main NullAway module, run `./gradlew :nullaway:test`. Run _only_ these tests unless you
4+
are specifically asked to run a test in a different module. If you want to run a single
5+
test class or method within that module, you can use the `--tests` flag. For example, to run all tests in the
6+
`com.uber.nullaway.NullAwayTest` class, you would run:
7+
8+
```bash
9+
./gradlew :nullaway:test --tests "com.uber.nullaway.NullAwayTest"
10+
```

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,52 @@
11
Changelog
22
=========
33

4+
Version 0.12.14
5+
---------------
6+
7+
This release fixes a bug where the new `RequireExplicitNullMarking` check was not fully disabled by default. It also includes a couple of performance optimizations to reduce NullAway overhead.
8+
9+
* Various optimizations (#1358)
10+
* Don't report matches from RequireExplicitNullMarking when run at SUGGESTION level (#1365)
11+
* Optimize methods that check for annotations on a Symbol (#1362)
12+
* Tune warning message for RequireExplicitNullMarking (#1366)
13+
* Maintenance
14+
- Fix arg concatenation bug in JMH (#1357)
15+
- Move wildcard-related tests to a separate test class (#1361)
16+
17+
Version 0.12.13
18+
---------------
19+
20+
NullAway now includes a new Error Prone checker, `RequireExplicitNullMarking`,
21+
that checks that every class is explicitly `@NullMarked` or `@NullUnmarked` (at the class or package level), so code
22+
is not left `@NullUnmarked` unintentionally. The check is disabled by default. See [the docs](https://github.com/uber/NullAway/wiki/JSpecify-Support#requireexplicitnullmarking-checker)
23+
for further details.
24+
25+
NullAway also includes support for a new `@PureExceptLambda` annotation, contributed by @FxMorin, to tell NullAway
26+
to preserve nullability information from the enclosing method when analyzing the body of certain lambdas.
27+
See [the docs](https://github.com/uber/NullAway/wiki/Supported-Annotations#pureexceptlambda) for details.
28+
29+
* Use passed-in `TreePath` in one more place when available (#1329)
30+
* Checker to ensure explicit null marking of every class (#1323)
31+
* Bound size of alreadyRunAnalyses cache to be consistent (#1334)
32+
* Improve inference for generic methods with lambda argument containing return statements by @dhruv-agr (#1337)
33+
* Add `PureExceptLambda` annotation by @FxMorin (#1325)
34+
* Support marking method type variable upper bounds as `@Nullable` in library models (#1345)
35+
* Method name parsing in `ExternalStubxLibraryModels` class is missing a corner case by @haewiful (#1344)
36+
* Better fix for dataflow analysis caching (#1353)
37+
* Maintenance
38+
- Add package-info files with @NullMarked (#1331)
39+
- Speed up buildWithNullAway task (#1330)
40+
- Enable the VoidMissingNullable checker and autofix all extant warnings (#1332)
41+
- Bump JDK version to 25 for integration tests (#1336)
42+
- Switch Coderabbit to assertive mode (#1338)
43+
- Enable EqualsMissingNullable check and fix all extant warnings (#1339)
44+
- Bump to AutoValue 1.11.1 (#1340)
45+
- Update Caffeine benchmark (#1342)
46+
- Add a test for a useless @Contract (#1346)
47+
- Test case for wildcards in Generic Method with Lambda Arguments by @dhruv-agr (#1349)
48+
- Add initial AGENTS.md file (#1352)
49+
450
Version 0.12.12
551
---------------
652

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ org.gradle.caching=true
1212
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
1313

1414
GROUP=com.uber.nullaway
15-
VERSION_NAME=0.12.13-SNAPSHOT
15+
VERSION_NAME=0.12.15-SNAPSHOT
1616

1717
POM_DESCRIPTION=A fast annotation-based null checker for Java
1818

gradle/dependencies.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def build = [
8383
errorProneTestHelpersOld: "com.google.errorprone:error_prone_test_helpers:${oldestErrorProneVersion}",
8484
checkerDataflow : "org.checkerframework:dataflow-nullaway:${versions.checkerFramework}",
8585
gson : "com.google.code.gson:gson:2.13.1",
86-
guava : "com.google.guava:guava:30.1-jre",
86+
guava : "com.google.guava:guava:31.1-jre",
8787
javaparser : "com.github.javaparser:javaparser-core:${versions.javaparser}",
8888
javaparserSymbolSolver : "com.github.javaparser:javaparser-symbol-solver-core:${versions.javaparser}",
8989
javaxValidation : "javax.validation:validation-api:2.0.1.Final",

guava-recent-unit-tests/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
errorProneOldest(deps.build.errorProneTestHelpersOld) {
4040
exclude group: "junit", module: "junit"
4141
}
42+
errorProneOldest deps.build.guava
4243
}
4344

4445
// Create a task to test with the oldest supported version of Error Prone

jmh/src/main/java/com/uber/nullaway/jmh/NullawayJavac.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.nio.file.Path;
3131
import java.nio.file.Paths;
3232
import java.util.ArrayList;
33-
import java.util.Arrays;
3433
import java.util.Collections;
3534
import java.util.List;
3635
import javax.tools.DiagnosticListener;
@@ -175,25 +174,33 @@ private NullawayJavac(
175174
outputDir.toFile().deleteOnExit();
176175
this.options = new ArrayList<>();
177176
if (classpath != null) {
178-
options.addAll(Arrays.asList("-classpath", classpath));
177+
options.addAll(List.of("-classpath", classpath));
179178
}
180179
String processorPath =
181180
System.getProperty("java.class.path") + File.pathSeparator + extraProcessorPath;
181+
// Error Prone arguments must be passed space separated with -Xplugin:ErrorProne
182+
String allErrorProneArgs =
183+
String.join(
184+
" ",
185+
List.of(
186+
"-Xplugin:ErrorProne",
187+
"-XepDisableAllChecks",
188+
"-Xep:NullAway:ERROR",
189+
"-XepOpt:NullAway:AnnotatedPackages=" + annotatedPackages,
190+
String.join(" ", extraErrorProneArgs)));
182191
options.addAll(
183-
Arrays.asList(
192+
List.of(
184193
"-processorpath",
185194
processorPath,
186195
"-d",
187196
outputDir.toAbsolutePath().toString(),
188197
"-XDcompilePolicy=simple",
189198
"--should-stop=ifError=FLOW",
190-
"-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:AnnotatedPackages="
191-
+ annotatedPackages
192-
+ String.join(" ", extraErrorProneArgs)));
199+
allErrorProneArgs));
193200
// add these options since we have at least one benchmark that only compiles with access to
194201
// javac-internal APIs
195202
options.addAll(
196-
Arrays.asList(
203+
List.of(
197204
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
198205
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
199206
"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
@@ -205,6 +212,8 @@ private NullawayJavac(
205212
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
206213
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
207214
"--add-exports=jdk.compiler/com.sun.source.tree=ALL-UNNAMED"));
215+
// for JSpecify mode (benign outside JSpecify mode)
216+
options.add("-XDaddTypeAnnotationsToSymbol=true");
208217
}
209218

210219
/**

nullaway/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dependencies {
8181
errorProneOldest(deps.build.errorProneTestHelpersOld) {
8282
exclude group: "junit", module: "junit"
8383
}
84+
errorProneOldest deps.build.guava
8485
}
8586

8687
javadoc {

nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.Map;
5454
import java.util.Set;
5555
import java.util.function.BiPredicate;
56+
import java.util.function.Predicate;
5657
import java.util.stream.Collectors;
5758
import java.util.stream.Stream;
5859
import javax.lang.model.element.AnnotationMirror;
@@ -191,6 +192,37 @@ public static Stream<? extends AnnotationMirror> getAllAnnotations(Symbol symbol
191192
return Stream.concat(symbol.getAnnotationMirrors().stream(), typeUseAnnotations);
192193
}
193194

195+
/**
196+
* Check if any direct annotation a symbol matches a given predicate. Works for both source and
197+
* bytecode.
198+
*
199+
* @param symbol the symbol
200+
* @param config NullAway configuration
201+
* @param predicate the predicate to match annotation names against
202+
* @return true if any annotation on the symbol matches the predicate, false otherwise
203+
*/
204+
public static boolean hasAnyAnnotationMatching(
205+
Symbol symbol, Config config, Predicate<String> predicate) {
206+
for (AnnotationMirror annotationMirror : symbol.getAnnotationMirrors()) {
207+
if (predicate.test(annotationMirror.getAnnotationType().toString())) {
208+
return true;
209+
}
210+
}
211+
// to handle bytecodes, also check direct type-use annotations stored in attributes
212+
Symbol typeAnnotationOwner =
213+
symbol.getKind().equals(ElementKind.PARAMETER) ? symbol.owner : symbol;
214+
for (Attribute.TypeCompound typeCompound : typeAnnotationOwner.getRawTypeAttributes()) {
215+
if (!targetTypeMatches(symbol, typeCompound.position)
216+
|| !isDirectTypeUseAnnotation(typeCompound, symbol, config)) {
217+
continue;
218+
}
219+
if (predicate.test(typeCompound.getAnnotationType().toString())) {
220+
return true;
221+
}
222+
}
223+
return false;
224+
}
225+
194226
/**
195227
* Retrieve the {@code value} attribute of a method annotation of some type.
196228
*

nullaway/src/main/java/com/uber/nullaway/Nullness.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,10 @@ public static boolean isMonotonicNonNullAnnotation(String annotName) {
6868
* {@code symbol}. Used to reason whether a field may be null.
6969
*/
7070
public static boolean hasNullableOrMonotonicNonNullAnnotation(Symbol symbol, Config config) {
71-
return hasNullableOrMonotonicNonNullAnnotation(
72-
NullabilityUtil.getAllAnnotations(symbol, config), config);
73-
}
74-
75-
private static boolean hasNullableOrMonotonicNonNullAnnotation(
76-
Stream<? extends AnnotationMirror> annotations, Config config) {
77-
return annotations
78-
.map(anno -> anno.getAnnotationType().toString())
79-
.anyMatch(anno -> isNullableAnnotation(anno, config) || isMonotonicNonNullAnnotation(anno));
71+
return NullabilityUtil.hasAnyAnnotationMatching(
72+
symbol,
73+
config,
74+
annot -> isNullableAnnotation(annot, config) || isMonotonicNonNullAnnotation(annot));
8075
}
8176

8277
// The following leastUpperBound and greatestLowerBound methods were created by handwriting a
@@ -219,7 +214,8 @@ public static boolean isNonNullAnnotation(String annotName, Config config) {
219214
* Config)}
220215
*/
221216
public static boolean hasNonNullAnnotation(Symbol symbol, Config config) {
222-
return hasNonNullAnnotation(NullabilityUtil.getAllAnnotations(symbol, config), config);
217+
return NullabilityUtil.hasAnyAnnotationMatching(
218+
symbol, config, annot -> isNonNullAnnotation(annot, config));
223219
}
224220

225221
/**
@@ -230,7 +226,8 @@ public static boolean hasNonNullAnnotation(Symbol symbol, Config config) {
230226
* Config)}
231227
*/
232228
public static boolean hasNullableAnnotation(Symbol symbol, Config config) {
233-
return hasNullableAnnotation(NullabilityUtil.getAllAnnotations(symbol, config), config);
229+
return NullabilityUtil.hasAnyAnnotationMatching(
230+
symbol, config, annot -> isNullableAnnotation(annot, config));
234231
}
235232

236233
private static boolean hasNullableTypeUseAnnotation(Symbol symbol, Config config) {
@@ -316,11 +313,21 @@ public static boolean varargsArrayIsNullable(Symbol paramSymbol, Config config)
316313

317314
/** Checks if the symbol has a {@code @Nullable} declaration annotation */
318315
public static boolean hasNullableDeclarationAnnotation(Symbol symbol, Config config) {
319-
return hasNullableAnnotation(symbol.getRawAttributes().stream(), config);
316+
for (AnnotationMirror annotationMirror : symbol.getRawAttributes()) {
317+
if (isNullableAnnotation(annotationMirror.getAnnotationType().toString(), config)) {
318+
return true;
319+
}
320+
}
321+
return false;
320322
}
321323

322324
/** Checks if the symbol has a {@code @NonNull} declaration annotation */
323325
public static boolean hasNonNullDeclarationAnnotation(Symbol symbol, Config config) {
324-
return hasNonNullAnnotation(symbol.getRawAttributes().stream(), config);
326+
for (AnnotationMirror annotationMirror : symbol.getRawAttributes()) {
327+
if (isNonNullAnnotation(annotationMirror.getAnnotationType().toString(), config)) {
328+
return true;
329+
}
330+
}
331+
return false;
325332
}
326333
}

0 commit comments

Comments
 (0)