Skip to content

Commit ba4ff83

Browse files
rmacqueenmsridhar
andauthored
Add library model for Apache Commons CollectionUtils.isNotEmpty, Amazon CollectionUtils.IsNullOrEmpty, and a couple Amazon StringUtils methods (#1242)
## Summary This PR extends DefaultLibraryModels to recognize additional null-check helper methods from common libraries, ensuring NullAway treats them as guarding against null values. Added support for: * org.apache.commons.collections.CollectionUtils.isEmpty(Collection) * org.apache.commons.collections4.CollectionUtils.isEmpty(Collection<?>) * software.amazon.awssdk.utils.CollectionUtils.isNullOrEmpty(Collection<?>) Each method is now mapped so that NullAway understands they perform null checks on their first argument. Added corresponding unit tests in FrameworkTests to validate behavior for Apache Commons Collections, Apache Commons Collections4, and AWS CollectionUtils. Impact With these changes, NullAway will correctly recognize these helper methods as null guards, reducing false positives when developers use them to validate collections or objects before dereferencing. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Broadened null/empty-check recognition to cover additional common collection and string utilities (Apache Commons, Commons Lang/3, AWS SDK utils, Spark utils, Android TextUtils), improving null-safety analysis. * **Tests** * Added tests validating handling of null/empty guards using Apache Commons Collections, Commons Collections4 and AWS SDK utils. * **Chores** * Added AWS SDK utils as a test dependency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Manu Sridharan <[email protected]>
1 parent 7b64688 commit ba4ff83

4 files changed

Lines changed: 111 additions & 0 deletions

File tree

gradle/dependencies.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def test = [
123123
mockito : "org.mockito:mockito-core:5.16.1",
124124
javaxAnnotationApi : "javax.annotation:javax.annotation-api:1.3.2",
125125
assertJ : "org.assertj:assertj-core:3.23.1",
126+
amazonUtils : "software.amazon.awssdk:utils:2.32.19",
126127
]
127128

128129
ext.deps = [

nullaway/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ dependencies {
7272
testImplementation deps.test.mockito
7373
testImplementation deps.test.javaxAnnotationApi
7474
testImplementation deps.test.assertJ
75+
testImplementation deps.test.amazonUtils
7576
// This is for a test exposing a CFG construction failure in the Checker Framework. We can probably remove it once
7677
// the issue is fixed upstream and we update. See https://github.com/typetools/checker-framework/issues/6396.
7778
testImplementation 'org.apache.spark:spark-sql_2.12:3.3.2'

nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,29 @@ private static class DefaultLibraryModels implements LibraryModels {
713713
methodRef(
714714
"org.springframework.util.CollectionUtils", "isEmpty(java.util.Collection<?>)"),
715715
0)
716+
.put(
717+
methodRef(
718+
"org.apache.commons.collections.CollectionUtils",
719+
"isEmpty(java.util.Collection)"),
720+
0)
721+
.put(
722+
methodRef(
723+
"org.apache.commons.collections4.CollectionUtils",
724+
"isEmpty(java.util.Collection<?>)"),
725+
0)
726+
.put(
727+
methodRef(
728+
"software.amazon.awssdk.utils.CollectionUtils",
729+
"isNullOrEmpty(java.util.Collection<?>)"),
730+
0)
731+
.put(
732+
methodRef(
733+
"software.amazon.awssdk.utils.StringUtils", "isEmpty(java.lang.CharSequence)"),
734+
0)
735+
.put(
736+
methodRef(
737+
"software.amazon.awssdk.utils.StringUtils", "isBlank(java.lang.CharSequence)"),
738+
0)
716739
.put(methodRef("org.springframework.util.StringUtils", "isEmpty(java.lang.Object)"), 0)
717740
.put(methodRef("org.springframework.util.ObjectUtils", "isEmpty(java.lang.Object)"), 0)
718741
.put(methodRef("spark.utils.ObjectUtils", "isEmpty(java.lang.Object[])"), 0)

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

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,92 @@ public void apacheCollections4CollectionUtilsIsNotEmpty() {
10681068
.doTest();
10691069
}
10701070

1071+
@Test
1072+
public void apacheCollectionsCollectionUtilsIsEmpty() {
1073+
defaultCompilationHelper
1074+
.addSourceLines(
1075+
"Foo.java",
1076+
"package com.uber;",
1077+
"import org.apache.commons.collections.CollectionUtils;",
1078+
"import org.jetbrains.annotations.Nullable;",
1079+
"import java.util.List;",
1080+
"public class Foo {",
1081+
" public void bar(@Nullable List<String> s) {",
1082+
" if(CollectionUtils.isEmpty(s)) {",
1083+
" return;",
1084+
" }",
1085+
" s.get(0);",
1086+
" }",
1087+
"}")
1088+
.doTest();
1089+
}
1090+
1091+
@Test
1092+
public void apacheCollections4CollectionUtilsIsEmpty() {
1093+
defaultCompilationHelper
1094+
.addSourceLines(
1095+
"Foo.java",
1096+
"package com.uber;",
1097+
"import org.apache.commons.collections4.CollectionUtils;",
1098+
"import org.jetbrains.annotations.Nullable;",
1099+
"import java.util.List;",
1100+
"public class Foo {",
1101+
" public void bar(@Nullable List<String> s) {",
1102+
" if(CollectionUtils.isEmpty(s)) {",
1103+
" return;",
1104+
" }",
1105+
" s.get(0);",
1106+
" }",
1107+
"}")
1108+
.doTest();
1109+
}
1110+
1111+
@Test
1112+
public void amazonAwsUtilCollectionUtilsIsNullOrEmpty() {
1113+
defaultCompilationHelper
1114+
.addSourceLines(
1115+
"Foo.java",
1116+
"package com.uber;",
1117+
"import software.amazon.awssdk.utils.CollectionUtils;",
1118+
"import org.jetbrains.annotations.Nullable;",
1119+
"import java.util.List;",
1120+
"public class Foo {",
1121+
" public void bar(@Nullable List<String> s) {",
1122+
" if(CollectionUtils.isNullOrEmpty(s)) {",
1123+
" return;",
1124+
" }",
1125+
" s.get(0);",
1126+
" }",
1127+
"}")
1128+
.doTest();
1129+
}
1130+
1131+
@Test
1132+
public void amazonAwsStringUtilsIsEmptyOrIsBlank() {
1133+
defaultCompilationHelper
1134+
.addSourceLines(
1135+
"Foo.java",
1136+
"package com.uber;",
1137+
"import software.amazon.awssdk.utils.StringUtils;",
1138+
"import org.jetbrains.annotations.Nullable;",
1139+
"import java.util.List;",
1140+
"public class Foo {",
1141+
" public void bar(@Nullable String s) {",
1142+
" if(StringUtils.isEmpty(s)) {",
1143+
" return;",
1144+
" }",
1145+
" s.hashCode();",
1146+
" }",
1147+
" public void baz(@Nullable String s) {",
1148+
" if(StringUtils.isBlank(s)) {",
1149+
" return;",
1150+
" }",
1151+
" s.hashCode();",
1152+
" }",
1153+
"}")
1154+
.doTest();
1155+
}
1156+
10711157
@Test
10721158
public void filesIsDirectory() {
10731159
defaultCompilationHelper

0 commit comments

Comments
 (0)