Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Library Models Integration #922

Merged
merged 61 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
1d48e6e
fixes
akulk022 Jan 31, 2024
b0631cb
stubx file writer logic
akulk022 Feb 7, 2024
e5353d1
changing method returns to nullable
akulk022 Feb 9, 2024
ea7d117
Adding safe cast for EnumDeclaration as parent for method
akulk022 Feb 14, 2024
e9a06dc
updating logic for method declaration
akulk022 Feb 18, 2024
19b020b
moving lib model to separate module
akulk022 Feb 21, 2024
31f569c
moving code
akulk022 Feb 21, 2024
a26f856
infrastructure code for lib model
akulk022 Feb 24, 2024
a863363
Merge branch 'uber:master' into library_model_stub_abhijitk
akulk022 Feb 24, 2024
ee56362
adding sample_jdk source files in resources
akulk022 Feb 26, 2024
3806c80
Merge remote-tracking branch 'origin/library_model_stub_abhijitk' int…
akulk022 Feb 26, 2024
32f0990
adding logic to create directories if they don't exist to avoid NoSuc…
akulk022 Feb 26, 2024
3dedc77
changing logic for ArrayTypes and adding Scanner findInLine test case…
akulk022 Feb 27, 2024
07586ae
replacing with synthetic test case files
akulk022 Feb 27, 2024
4b7891e
unnecessary formatting change
akulk022 Feb 27, 2024
10c36e3
minor cleanup
msridhar Feb 29, 2024
36b5a21
Update lib-model/lib-model-consume/src/main/java/com/uber/nullaway/li…
akulk022 Feb 29, 2024
a6c8204
fixes
akulk022 Feb 29, 2024
0c2cd44
Generating MethodAnnotationsRecord using AutoValue
akulk022 Feb 29, 2024
ac14c99
Removing Redundant files for StubxWriter and MethodAnnotationsRecord
akulk022 Mar 1, 2024
2dbc283
Merge branch 'master' into library_model_stub_abhijitk
msridhar Mar 5, 2024
32e80d5
making methodRecords local variable and passing as a param to make it…
akulk022 Mar 5, 2024
2b637f1
Merge remote-tracking branch 'origin/library_model_stub_abhijitk' int…
akulk022 Mar 5, 2024
888c1b2
separating lib model cli module
akulk022 Mar 5, 2024
fb81869
removing unnecessary dependencies
akulk022 Mar 6, 2024
e889dc3
refactoring module names and build files
akulk022 Mar 7, 2024
26efa96
using shadowJar plugin
akulk022 Mar 7, 2024
cbe7bf5
formatting
akulk022 Mar 7, 2024
600164c
refactoring, adding javadocs and clean-up
akulk022 Mar 7, 2024
d1fed93
create directory fix
akulk022 Mar 7, 2024
db993b8
Handling ArrayType @Nullable return Scenario and some refactoring
akulk022 Mar 7, 2024
09c0f4c
javadoc changes
akulk022 Mar 7, 2024
161c6ac
Logic to only consider jspecify Nullable annotation
akulk022 Mar 8, 2024
27ebcc9
tweak comment
msridhar Mar 8, 2024
5ee2428
Refactoring and documentation
akulk022 Mar 8, 2024
8c442f7
removing comment
akulk022 Mar 9, 2024
609f2e5
cleanup
akulk022 Mar 9, 2024
fb83aff
Adding NullMarked flag and some clean up
akulk022 Mar 10, 2024
629ec5b
adding doc
akulk022 Mar 10, 2024
5190597
refactoring
akulk022 Mar 13, 2024
fd7d546
Removing unused test case
akulk022 Mar 13, 2024
f213d82
Refactoring test setup for library-model-generator
akulk022 Mar 13, 2024
1137aa9
Refactoring test setup
akulk022 Mar 13, 2024
6e0ef33
Merge branch 'master' into library_model_stub_abhijitk
akulk022 Mar 13, 2024
41bc90f
adding comment for NullMarked functionality
akulk022 Mar 14, 2024
26c4907
adding simple usage message
akulk022 Mar 22, 2024
dc0618a
review suggestion
msridhar Mar 23, 2024
5fa73da
adding condition to consider jspecify annotation in InferredJARModels…
akulk022 Mar 23, 2024
14e39b6
Merge branch 'master' into library_model_stub_abhijitk
akulk022 Mar 24, 2024
f390cd1
try to incorporate more code coverage info
msridhar Mar 24, 2024
ad44788
Adding a test to check if the functionality is off without the JarInf…
akulk022 Mar 24, 2024
2a1c2da
Merge remote-tracking branch 'origin/library_model_stub_abhijitk' int…
akulk022 Mar 24, 2024
8f86b36
Handling the scenario for inner class: https://github.com/uber/NullAw…
akulk022 Mar 24, 2024
35b988c
minor comment modifications.
akulk022 Mar 24, 2024
c1fe36b
Removing redundant main class specification in javaexec; it's already…
akulk022 Mar 24, 2024
d301e28
Merge branch 'master' into library_model_stub_abhijitk
akulk022 Mar 24, 2024
439ca14
changing parentName from StringBuilder to String
akulk022 Mar 28, 2024
e2a9c60
Merge branch 'master' into library_model_stub_abhijitk
akulk022 Mar 28, 2024
a072bbd
updating year in copyright header for newly created files
akulk022 Mar 29, 2024
75785fa
Merge remote-tracking branch 'origin/library_model_stub_abhijitk' int…
akulk022 Mar 29, 2024
80be0d4
Merge branch 'master' into library_model_stub_abhijitk
akulk022 Mar 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ subprojects { project ->
google()
}

// For some reason, spotless complains when applied to the jar-infer folder itself, even
// though there is no top-level :jar-infer project
if (project.name != "jar-infer") {
// Spotless complains when applied to the folders containing projects
// when they do not have a build.gradle file
if (project.name != "jar-infer" && project.name != "library-model") {
project.apply plugin: "com.diffplug.spotless"
spotless {
java {
Expand Down
2 changes: 2 additions & 0 deletions code-coverage-report/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ dependencies {
implementation project(':jar-infer:nullaway-integration-test')
implementation project(':guava-recent-unit-tests')
implementation project(':jdk-recent-unit-tests')
implementation project(':library-model:library-model-generator')
implementation project(':library-model:library-model-generator-integration-test')
}
1 change: 1 addition & 0 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def build = [
errorProneTestHelpersOld: "com.google.errorprone:error_prone_test_helpers:${oldestErrorProneVersion}",
checkerDataflow : "org.checkerframework:dataflow-nullaway:${versions.checkerFramework}",
guava : "com.google.guava:guava:30.1-jre",
javaparser : "com.github.javaparser:javaparser-core:3.25.8",
javaxValidation : "javax.validation:validation-api:2.0.1.Final",
jspecify : "org.jspecify:jspecify:0.3.0",
jsr305Annotations : "com.google.code.findbugs:jsr305:3.0.2",
Expand Down
1 change: 1 addition & 0 deletions jar-infer/jar-infer-cli/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
implementation deps.build.commonscli
implementation deps.build.guava
implementation project(":jar-infer:jar-infer-lib")
implementation project(":library-model:library-model-generator")

testImplementation deps.test.junit4
testImplementation(deps.build.errorProneTestHelpers) {
Expand Down
1 change: 1 addition & 0 deletions jar-infer/jar-infer-lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
api deps.build.guava
api deps.build.commonsIO
compileOnly deps.build.errorProneCheckApi
implementation project(":library-model:library-model-generator")

testImplementation deps.test.junit4
testImplementation(deps.build.errorProneTestHelpers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.config.FileOfClasses;
import com.uber.nullaway.libmodel.MethodAnnotationsRecord;
import com.uber.nullaway.libmodel.StubxWriter;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
Expand Down Expand Up @@ -437,15 +439,15 @@ private void writeModel(DataOutputStream out) throws IOException {
}
methodRecords.put(
sign,
new MethodAnnotationsRecord(
MethodAnnotationsRecord.create(
nullableReturns.contains(sign) ? ImmutableSet.of("Nullable") : ImmutableSet.of(),
ImmutableMap.copyOf(argAnnotation)));
nullableReturns.remove(sign);
}
for (String nullableReturnMethodSign : Iterator2Iterable.make(nullableReturns.iterator())) {
methodRecords.put(
nullableReturnMethodSign,
new MethodAnnotationsRecord(ImmutableSet.of("Nullable"), ImmutableMap.of()));
MethodAnnotationsRecord.create(ImmutableSet.of("Nullable"), ImmutableMap.of()));
}
StubxWriter.write(out, importedAnnotations, packageAnnotations, typeAnnotations, methodRecords);
}
Expand Down

This file was deleted.

43 changes: 43 additions & 0 deletions library-model/library-model-generator-cli/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2024. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id "java-library"
id "com.github.johnrengelman.shadow"
}

jar{
manifest {
attributes('Main-Class':'com.uber.nullaway.libmodel.LibraryModelGeneratorCLI')
}
// add this classifier so that the output file for the jar task differs from
// the output file for the shadowJar task (otherwise they overwrite each other's
// outputs, forcing the tasks to always re-run)
archiveClassifier = "nonshadow"
}

shadowJar {
mergeServiceFiles()
configurations = [
project.configurations.runtimeClasspath
]
archiveClassifier = ""
}
shadowJar.dependsOn jar
assemble.dependsOn shadowJar

dependencies {
implementation project(":library-model:library-model-generator")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Uber Technologies, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.uber.nullaway.libmodel;

/**
* A CLI tool for invoking the process for {@link LibraryModelGenerator} which generates astubx
* file(s) from a directory containing annotated source code to be used as external library models.
*/
public class LibraryModelGeneratorCLI {
/**
* This is the main method of the cli tool. It parses the source files within a specified
* directory, obtains meaningful Nullability annotation information and writes it into an astubx
* file.
*
* @param args Command line arguments for the directory containing source files and the output
* directory.
*/
public static void main(String[] args) {
if (args.length != 2) {
System.out.println(
"Incorrect number of command line arguments. Required arguments: <inputSourceDirectory> <outputDirectory>");
return;
}
LibraryModelGenerator libraryModelGenerator = new LibraryModelGenerator();
libraryModelGenerator.generateAstubxForLibraryModels(args[0], args[1]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the number of args and print a basic usage message? (Not sure if we do that for JarInfer, and I don't think we need a full argument parser here yet, a check on the length of args would suffice for now)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id "java-library"
id "nullaway.java-test-conventions"
}

dependencies {
testImplementation project(":nullaway")
testImplementation project(":library-model:test-library-model-generator")
testImplementation deps.test.junit4
testImplementation(deps.build.errorProneTestHelpers) {
exclude group: "junit", module: "junit"
}
implementation deps.build.guava
implementation deps.build.javaparser
compileOnly deps.apt.autoValueAnnot
annotationProcessor deps.apt.autoValue
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.uber.nullaway.libmodel;

import com.google.errorprone.CompilationTestHelper;
import com.uber.nullaway.NullAway;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class LibraryModelIntegrationTest {

@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();

private CompilationTestHelper compilationHelper;

@Before
public void setup() {
compilationHelper = CompilationTestHelper.newInstance(NullAway.class, getClass());
}

@Test
public void libraryModelNullableReturnsTest() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JarInferUseReturnAnnotations=true"))
Comment on lines +30 to +31
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @yuxincs that overloading the meaning of the JarInfer[...] CLI options for NullAway for this is bad practice, specially because other than the Android SDK, JarInfer no longer uses .astubx but actually modifies the bytecode of jars to add annotations (so this options aren't used with JarInfer except for that Android SDK jar).

It can certainly be a follow up PR, but I'd be in favor of rethinking these CLI flags. Maybe -XepOpt:NullAway:JarInferEnabled is actually something like -XepOpt:NullAway:ScanClassPathForAstubXModels? Is there a better name? A nicer way to specify which .astubx files we want to load without breaking things in our build internally? (I think there is! We only really care about that Android SDK astubx file right now, so it's not like this changes for each target like it used to, cc: @yuxincs )

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am in favor of changing these, but maybe in a follow-up PR. I don't think we need to go as far as keeping the old flag but deprecating it, as long as we document properly in release notes. But we should probably check as to what this breaks (if anything) internally at Uber.

@akulk022 can you open an issue on renaming these flags?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created the issue #940

.addSourceLines(
"Test.java",
"package com.uber;",
"import com.uber.nullaway.libmodel.AnnotationExample;",
"class Test {",
" static AnnotationExample annotationExample = new AnnotationExample();",
" static void test(String value){",
" }",
" static void testPositive() {",
" // BUG: Diagnostic contains: passing @Nullable parameter 'annotationExample.makeUpperCase(\"nullaway\")'",
" test(annotationExample.makeUpperCase(\"nullaway\"));",
" }",
" static void testNegative() {",
" test(annotationExample.nullReturn());",
" }",
"}")
.doTest();
}

@Test
public void libraryModelNullableReturnsArrayTest() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JarInferUseReturnAnnotations=true"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import com.uber.nullaway.libmodel.AnnotationExample;",
"class Test {",
" static AnnotationExample annotationExample = new AnnotationExample();",
" static void test(Integer[] value){",
" }",
" static void testPositive() {",
" // BUG: Diagnostic contains: passing @Nullable parameter 'annotationExample.generateIntArray(7)'",
" test(annotationExample.generateIntArray(7));",
Comment on lines +70 to +71
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fun! This wasn't possible in my version of the astubx format, correct? 🙂

Edit: Ah, never mind, it uses the same position of annotation as the case above, we haven't introduced yet a way to annotate both the elements of an array and the array itself, correct? But this parses the annotation in the correct/JSpecify location.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are correct. jspecify/jdk puts the annotation in the right spot, so if you parse it the incorrect way you get incorrect specs :-) You're right that we don't yet support annotations in both positions. Do you think this needs a code comment @lazaroclapp?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't hurt, but I also think that behavior will get less and less surprising with time as the "JSpecify way" becomes the default overall for NullAway and arrays (at least for type-use annotations).

" }",
"}")
.doTest();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test case without the -XepOpt:NullAway:JarInferEnabled flag or whatever we end up calling it, to check that this functionality is off without it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, may I suggest a case with a model adding @NonNull to a method argument, just for completeness here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lazaroclapp We have not added the functionality for processing the annotations on method parameters in this PR so a test case with @NonNull on a method parameter will not be supported on these changes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also @lazaroclapp I don't think supporting @NonNull on parameters will be a priority. Instead we are focusing on supporting treatment of a modeled class as @NullMarked, so everything is @NonNull by default, and then pulling in any explicit @Nullable annotations on parameters into the model

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a chance that specific classes will be @NullUnmarked with a few @NonNull locations? Either way, understood, not needed as part of this PR. I do feel in the medium term we might get surprised by the lack of symmetry here when using this for other kinds of library models.


@Test
public void libraryModelWithoutJarInferEnabledTest() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import com.uber.nullaway.libmodel.AnnotationExample;",
"class Test {",
" static AnnotationExample annotationExample = new AnnotationExample();",
" static void test(String value){",
" }",
" static void testNegative() {",
" // Since the JarInferEnabled and JarInferUseReturnAnnotations flags are not set, we don't get an error here",
" test(annotationExample.makeUpperCase(\"nullaway\"));",
" }",
"}")
.doTest();
}

@Test
public void libraryModelInnerClassNullableReturnsTest() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JarInferUseReturnAnnotations=true"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import com.uber.nullaway.libmodel.AnnotationExample;",
"class Test {",
" static AnnotationExample.InnerExample innerExample = new AnnotationExample.InnerExample();",
" static void test(String value){",
" }",
" static void testPositive() {",
" // BUG: Diagnostic contains: passing @Nullable parameter 'innerExample.returnNull()'",
" test(innerExample.returnNull());",
" }",
"}")
.doTest();
}
}
26 changes: 26 additions & 0 deletions library-model/library-model-generator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id 'java-library'
id 'nullaway.java-test-conventions'
}

dependencies {
implementation deps.build.guava
implementation deps.build.javaparser
compileOnly deps.apt.autoValueAnnot
annotationProcessor deps.apt.autoValue
}