Skip to content

Commit bebe278

Browse files
committed
Add nullaway and jSpecify for nullness analysis
1 parent f6df219 commit bebe278

File tree

150 files changed

+716
-486
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+716
-486
lines changed

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ subprojects { subproject ->
3434
'cli',
3535
'standard-library',
3636
'system-test']) {
37-
apply plugin: 'java'
37+
apply plugin: 'java-library'
3838
java {
3939
toolchain {
4040
languageVersion = JavaLanguageVersion.of(25)
@@ -51,10 +51,18 @@ subprojects { subproject ->
5151
apply plugin: 'net.ltgt.errorprone'
5252
dependencies {
5353
errorprone "com.google.errorprone:error_prone_core:2.42.0"
54+
api libs.jspecify
55+
errorprone 'com.uber.nullaway:nullaway:0.12.9'
5456
}
5557
tasks.withType(JavaCompile).configureEach {
5658
options.errorprone {
5759
disableWarningsInGeneratedCode.set(true)
60+
option('NullAway:AnnotatedPackages', 'org.smoothbuild')
61+
// Avoid errors when field is injected by picocli.
62+
// In some cases, when picocli does not always inject some field
63+
// (for example user is not required to provide some option in commandline),
64+
// such field is annotated in code with @Nullable.
65+
option('NullAway:ExternalInitAnnotations', 'picocli.CommandLine.Command')
5866
}
5967
}
6068

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencyResolutionManagement {
2323
library('antlr4', 'org.antlr:antlr4:4.13.2')
2424
library('antlr4-runtime', 'org.antlr:antlr4-runtime:4.13.2')
2525
library('guava', 'com.google.guava:guava:33.5.0-jre')
26+
library('jspecify', 'org.jspecify:jspecify:1.0.0')
2627
library('dagger', 'com.google.dagger:dagger:2.57.2')
2728
library('dagger-compiler', 'com.google.dagger:dagger-compiler:2.57.2')
2829
library('okio', 'com.squareup.okio:okio:3.16.0')

src/cli/src/main/java/org/smoothbuild/cli/command/base/ProjectCommand.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.nio.file.Path;
1010
import java.util.concurrent.Callable;
1111
import org.smoothbuild.cli.layout.Layout;
12+
import org.smoothbuild.common.collect.Result;
1213

1314
public abstract class ProjectCommand extends LoggingCommand implements Callable<Integer> {
1415
@Override
@@ -21,15 +22,17 @@ public Integer call() {
2122
return EXIT_CODE_ERROR;
2223
}
2324
Path normalizedProjectDir = projectDir.normalize();
24-
FileLock fileLock = lockFile(out(), projectDir.resolve(Layout.SMOOTH_LOCK_PATH.toString()));
25-
if (fileLock == null) {
25+
Result<FileLock> lockFile =
26+
lockFile(out(), projectDir.resolve(Layout.SMOOTH_LOCK_PATH.toString()));
27+
if (lockFile.isErr()) {
28+
printError(lockFile.err());
2629
return EXIT_CODE_ERROR;
2730
}
2831
try {
2932
return executeCommand(normalizedProjectDir);
3033
} finally {
3134
try {
32-
fileLock.release();
35+
lockFile.ok().release();
3336
} catch (IOException e) {
3437
printError("Error closing file lock.");
3538
return EXIT_CODE_ERROR;

src/cli/src/main/java/org/smoothbuild/cli/command/build/DeleteArtifacts.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.smoothbuild.cli.command.build;
22

3+
import static org.smoothbuild.common.base.Throwables.messageFrom;
34
import static org.smoothbuild.common.log.base.Log.error;
45
import static org.smoothbuild.common.log.report.Report.report;
56
import static org.smoothbuild.common.schedule.Output.output;
@@ -31,7 +32,7 @@ public Output<Tuple0> execute() {
3132
fileSystem.deleteRecursively(artifactsPath);
3233
return output(tuple(), report(label));
3334
} catch (IOException e) {
34-
return output(report(label, error(e.getMessage())));
35+
return output(report(label, error(messageFrom(e))));
3536
}
3637
}
3738
}

src/cli/src/main/java/org/smoothbuild/cli/command/build/SaveArtifacts.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import static com.google.common.base.Throwables.getStackTraceAsString;
44
import static java.util.Comparator.comparing;
5+
import static java.util.Objects.requireNonNullElse;
56
import static java.util.stream.Collectors.joining;
7+
import static org.smoothbuild.common.base.Throwables.messageFrom;
68
import static org.smoothbuild.common.collect.List.list;
79
import static org.smoothbuild.common.filesystem.base.Path.path;
810
import static org.smoothbuild.common.log.base.Log.error;
@@ -65,7 +67,7 @@ public Output<Tuple0> execute(EvaluatedExprs evaluatedExprs) {
6567
try {
6668
fileSystem.createDir(artifactsPath);
6769
} catch (IOException e) {
68-
return output(report(label, error(e.getMessage())));
70+
return output(report(label, error(messageFrom(e))));
6971
}
7072
var sReferences = evaluatedExprs.sExprs().map(this::toReferenceS);
7173
var artifacts = sReferences.zip(evaluatedExprs.bValues(), Tuples::tuple);
@@ -77,7 +79,7 @@ public Output<Tuple0> execute(EvaluatedExprs evaluatedExprs) {
7779
}
7880

7981
private SPolyReference toReferenceS(SExpr expr) {
80-
return (SPolyReference) ((SInstantiate) expr).sPolyReference();
82+
return ((SInstantiate) expr).sPolyReference();
8183
}
8284

8385
private void save(SPolyReference valueS, BValue value, Logger logger) {
@@ -89,7 +91,7 @@ private void save(SPolyReference valueS, BValue value, Logger logger) {
8991
logger.fatal("Couldn't store artifact at " + artifactPath(name) + ". Caught exception:\n"
9092
+ getStackTraceAsString(e));
9193
} catch (DuplicatedPathsException e) {
92-
logger.error(e.getMessage());
94+
logger.error(requireNonNullElse(e.getMessage(), "DuplicatePathsException"));
9395
}
9496
}
9597

src/common-testing/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ plugins {
33
}
44

55
dependencies {
6+
api libs.jspecify
67
implementation libs.truth
78

89
testRuntimeOnly libs.junit.jupiter.engine

src/common-testing/src/main/java/org/smoothbuild/commontesting/AssertCall.java

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@
44
import static com.google.common.truth.Fact.simpleFact;
55
import static com.google.common.truth.Truth.assertAbout;
66
import static java.util.Arrays.asList;
7+
import static java.util.Objects.requireNonNull;
78

89
import com.google.common.truth.Fact;
910
import com.google.common.truth.FailureMetadata;
1011
import com.google.common.truth.Subject;
1112
import java.util.ArrayList;
1213
import java.util.List;
1314
import java.util.Objects;
15+
import org.jspecify.annotations.Nullable;
1416

1517
public class AssertCall {
1618
public static ThrownExceptionSubject assertCall(ThrowingRunnable runnable) {
1719
return assertAbout(thrownExceptions()).that(runRunnable(runnable));
1820
}
1921

22+
@Nullable
2023
private static Throwable runRunnable(ThrowingRunnable runnable) {
2124
try {
2225
runnable.run();
@@ -31,10 +34,12 @@ private static Subject.Factory<ThrownExceptionSubject, Throwable> thrownExceptio
3134
}
3235

3336
public static class ThrownExceptionSubject extends Subject {
34-
private final Object actual;
37+
private final FailureMetadata metadata;
38+
private final @Nullable Object actual;
3539

36-
public ThrownExceptionSubject(FailureMetadata metadata, Object actual) {
40+
public ThrownExceptionSubject(FailureMetadata metadata, @Nullable Object actual) {
3741
super(metadata, actual);
42+
this.metadata = metadata;
3843
this.actual = actual;
3944
}
4045

@@ -56,9 +61,13 @@ public ExceptionCauseSubject throwsException(Throwable expected) {
5661
fact("but was message", actualMessage));
5762
}
5863
}
59-
return new ExceptionCauseSubject(List.of(
60-
fact("expected call to throw", expectedClassName),
61-
fact("with message", ((Throwable) actual).getMessage())));
64+
var nonNullActual = requireNonNull(actual);
65+
return new ExceptionCauseSubject(
66+
metadata,
67+
nonNullActual,
68+
List.of(
69+
fact("expected call to throw", expectedClassName),
70+
fact("with message", expected.getMessage())));
6271
}
6372

6473
public ExceptionCauseSubject throwsException(Class<? extends Throwable> expected) {
@@ -69,62 +78,68 @@ public ExceptionCauseSubject throwsException(Class<? extends Throwable> expected
6978
} else if (actual.getClass() != expected) {
7079
failWithActual(fact("expected call to throw", expected.getCanonicalName()));
7180
}
81+
var nonNullActual = requireNonNull(actual);
7282
return new ExceptionCauseSubject(
83+
metadata,
84+
nonNullActual,
7385
List.of(fact("expected call to throw", expected.getCanonicalName())));
7486
}
87+
}
7588

76-
public class ExceptionCauseSubject {
77-
private final List<Fact> facts;
89+
public static class ExceptionCauseSubject extends Subject {
90+
private final Object actual;
91+
private final List<Fact> facts;
7892

79-
public ExceptionCauseSubject(List<Fact> facts) {
80-
this.facts = facts;
81-
}
93+
public ExceptionCauseSubject(FailureMetadata metadata, Object actual, List<Fact> facts) {
94+
super(metadata, actual);
95+
this.actual = actual;
96+
this.facts = facts;
97+
}
8298

83-
public void withCause(Throwable expectedCause) {
84-
Throwable actualCause = ((Throwable) actual).getCause();
85-
String expectedCauseName = expectedCause.getClass().getCanonicalName();
86-
if (actualCause == null) {
87-
callFailWithoutActual(
88-
facts,
89-
fact("with cause", expectedCauseName),
90-
simpleFact("but was exception without cause"));
91-
} else if (!Objects.equals(actualCause.getClass(), expectedCause.getClass())) {
92-
callFailWithoutActual(
93-
facts,
94-
fact("with cause", expectedCauseName),
95-
fact("but was cause", actualCause.getClass().getCanonicalName()));
96-
} else if (!Objects.equals(actualCause.getMessage(), expectedCause.getMessage())) {
97-
callFailWithoutActual(
98-
facts,
99-
fact("with cause", expectedCauseName),
100-
fact("with message", expectedCause.getMessage()),
101-
fact("but was message", actualCause.getMessage()));
102-
}
99+
public void withCause(Throwable expectedCause) {
100+
Throwable actualCause = ((Throwable) actual).getCause();
101+
String expectedCauseName = expectedCause.getClass().getCanonicalName();
102+
if (actualCause == null) {
103+
callFailWithoutActual(
104+
facts,
105+
fact("with cause", expectedCauseName),
106+
simpleFact("but was exception without cause"));
107+
} else if (!Objects.equals(actualCause.getClass(), expectedCause.getClass())) {
108+
callFailWithoutActual(
109+
facts,
110+
fact("with cause", expectedCauseName),
111+
fact("but was cause", actualCause.getClass().getCanonicalName()));
112+
} else if (!Objects.equals(actualCause.getMessage(), expectedCause.getMessage())) {
113+
callFailWithoutActual(
114+
facts,
115+
fact("with cause", expectedCauseName),
116+
fact("with message", expectedCause.getMessage()),
117+
fact("but was message", actualCause.getMessage()));
103118
}
119+
}
104120

105-
public void withCause(Class<? extends Throwable> expectedCause) {
106-
Throwable actualCause = ((Throwable) actual).getCause();
107-
String expectedCauseName = expectedCause.getCanonicalName();
108-
if (actualCause == null) {
109-
callFailWithoutActual(
110-
facts,
111-
fact("with cause", expectedCauseName),
112-
simpleFact("but was exception without cause"));
113-
} else if (!actualCause.getClass().equals(expectedCause)) {
114-
callFailWithoutActual(
115-
facts,
116-
fact("with cause", expectedCauseName),
117-
fact("but was cause", actualCause.getClass().getCanonicalName()));
118-
}
121+
public void withCause(Class<? extends Throwable> expectedCause) {
122+
Throwable actualCause = ((Throwable) actual).getCause();
123+
String expectedCauseName = expectedCause.getCanonicalName();
124+
if (actualCause == null) {
125+
callFailWithoutActual(
126+
facts,
127+
fact("with cause", expectedCauseName),
128+
simpleFact("but was exception without cause"));
129+
} else if (!actualCause.getClass().equals(expectedCause)) {
130+
callFailWithoutActual(
131+
facts,
132+
fact("with cause", expectedCauseName),
133+
fact("but was cause", actualCause.getClass().getCanonicalName()));
119134
}
135+
}
120136

121-
private void callFailWithoutActual(List<Fact> facts1, Fact... facts2) {
122-
Fact first = facts1.get(0);
123-
ArrayList<Fact> allWithoutFirst = new ArrayList<>();
124-
allWithoutFirst.addAll(facts1.subList(1, facts1.size()));
125-
allWithoutFirst.addAll(asList(facts2));
126-
failWithoutActual(first, allWithoutFirst.toArray(Fact[]::new));
127-
}
137+
private void callFailWithoutActual(List<Fact> facts1, Fact... facts2) {
138+
Fact first = facts1.get(0);
139+
ArrayList<Fact> allWithoutFirst = new ArrayList<>();
140+
allWithoutFirst.addAll(facts1.subList(1, facts1.size()));
141+
allWithoutFirst.addAll(asList(facts2));
142+
failWithoutActual(first, allWithoutFirst.toArray(Fact[]::new));
128143
}
129144
}
130145
}

src/common/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
}
55

66
dependencies {
7+
api libs.jspecify
78
implementation libs.guava
89
implementation libs.dagger
910
annotationProcessor libs.dagger.compiler

src/common/src/main/java/org/smoothbuild/common/base/Check.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import static java.util.Objects.requireNonNull;
44

5+
import org.jspecify.annotations.Nullable;
6+
57
public class Check {
6-
public static <T> T checkInitializedToNotNull(T value, String name) {
8+
public static <T> T checkInitializedToNotNull(@Nullable T value, String name) {
79
return requireNonNull(value, Strings.q(name) + " has not been initialized yet.");
810
}
911
}

0 commit comments

Comments
 (0)