Skip to content

Commit

Permalink
Merge 26d4067 into abc1dcd
Browse files Browse the repository at this point in the history
  • Loading branch information
lazaroclapp committed Aug 6, 2020
2 parents abc1dcd + 26d4067 commit 97d9958
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 2 deletions.
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -88,6 +88,20 @@ Some annotation processors like [Dagger](https://google.github.io/dagger/) and [

**Note for Dagger users**: Dagger versions older than 2.12 can have bad interactions with NullAway; see [here](https://github.com/uber/NullAway/issues/48#issuecomment-340018409). Please update to Dagger 2.12 to fix the problem.

#### Lombok

Unlike other annotation processors above, Lombok modifies the in-memory AST of the code it processes, which is the source of numerous incompatibilities with Error Prone and, consequently, NullAway.

We do not particularly recommend using NullAway with Lombok. However, NullAway encodes some knowledge of common Lombok annotations and we do try for best-effort compatibility. In particular, common usages like `@lombok.Builder` and `@Data` classes should be supported.

In order for NullAway to successfully detect Lombok generated code within the in-memory Java AST, the following configuration option must be passed to Lombok as part of an applicable `lombok.config` file:

```
addLombokGeneratedAnnotation
```

This causes Lombok to add `@lombok.Generated` to the methods/classes it generates. NullAway will ignore (i.e. not check) the implementation of this generated code, treating it as unannotated.

## Code Example

Let's see how NullAway works on a simple code example:
Expand Down
1 change: 1 addition & 0 deletions gradle/dependencies.gradle
Expand Up @@ -73,6 +73,7 @@ def test = [
rxjava2 : "io.reactivex.rxjava2:rxjava:2.1.2",
commonsLang3 : "org.apache.commons:commons-lang3:3.8.1",
commonsLang : "commons-lang:commons-lang:2.6",
lombok : "org.projectlombok:lombok:1.18.12",
]

ext.deps = [
Expand Down
1 change: 1 addition & 0 deletions nullaway/build.gradle
Expand Up @@ -60,6 +60,7 @@ dependencies {
testCompile deps.test.commonsLang
testCompile deps.test.commonsLang3
testCompile project(":test-library-models")
testCompile deps.test.lombok
}

// We include and shade the checker framework jars into the NullAway jar, as we may have custom
Expand Down
Expand Up @@ -73,6 +73,9 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig {

private static final String DELIMITER = ",";

static final ImmutableSet<String> DEFAULT_CLASS_ANNOTATIONS_TO_EXCLUDE =
ImmutableSet.of("lombok.Generated");

static final ImmutableSet<String> DEFAULT_KNOWN_INITIALIZERS =
ImmutableSet.of(
"android.view.View.onFinishInflate",
Expand Down Expand Up @@ -107,6 +110,8 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig {
"org.junit.jupiter.api.BeforeAll",
"org.junit.jupiter.api.BeforeEach"); // + Anything with @Initializer as its "simple name"

static final ImmutableSet<String> DEFAULT_EXTERNAL_INIT_ANNOT = ImmutableSet.of("lombok.Builder");

static final ImmutableSet<String> DEFAULT_EXCLUDED_FIELD_ANNOT =
ImmutableSet.of(
"javax.inject.Inject", // no explicit initialization when there is dependency injection
Expand All @@ -133,10 +138,13 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig {
knownInitializers =
getKnownInitializers(
getFlagStringSet(flags, FL_KNOWN_INITIALIZERS, DEFAULT_KNOWN_INITIALIZERS));
excludedClassAnnotations = getFlagStringSet(flags, FL_CLASS_ANNOTATIONS_TO_EXCLUDE);
excludedClassAnnotations =
getFlagStringSet(
flags, FL_CLASS_ANNOTATIONS_TO_EXCLUDE, DEFAULT_CLASS_ANNOTATIONS_TO_EXCLUDE);
initializerAnnotations =
getFlagStringSet(flags, FL_INITIALIZER_ANNOT, DEFAULT_INITIALIZER_ANNOT);
externalInitAnnotations = getFlagStringSet(flags, FL_EXTERNAL_INIT_ANNOT);
externalInitAnnotations =
getFlagStringSet(flags, FL_EXTERNAL_INIT_ANNOT, DEFAULT_EXTERNAL_INIT_ANNOT);
isExhaustiveOverride = flags.getBoolean(FL_EXHAUSTIVE_OVERRIDE).orElse(false);
isSuggestSuppressions = flags.getBoolean(FL_SUGGEST_SUPPRESSIONS).orElse(false);
isAcknowledgeRestrictive = flags.getBoolean(FL_ACKNOWLEDGE_RESTRICTIVE).orElse(false);
Expand Down
5 changes: 5 additions & 0 deletions nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java
Expand Up @@ -95,6 +95,11 @@ public void coreNullabilitySkipClass() {
.doTest();
}

@Test
public void lombokSupportTesting() {
compilationHelper.addSourceFile("lombok/LombokBuilderInit.java").doTest();
}

@Test
public void skipClass() {
compilationHelper
Expand Down
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020 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.testdata.lombok;

import javax.annotation.Nullable;
import lombok.Builder;

@Builder
public class LombokBuilderInit {
private String field;
@Builder.Default private String fieldWithDefault = "Default";
@Nullable private String nullableField;
}
1 change: 1 addition & 0 deletions settings.gradle
Expand Up @@ -18,6 +18,7 @@ include ':sample-library-model'
include ':sample'
include ':sample-app'
include ':test-java-lib'
include ':test-java-lib-lombok'
include ':test-library-models'
include ':compile-bench'
include ':jar-infer:android-jarinfer-models-sdk28'
Expand Down
45 changes: 45 additions & 0 deletions test-java-lib-lombok/build.gradle
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2017. 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.
*/

import net.ltgt.gradle.errorprone.CheckSeverity

plugins {
id "java"
}

sourceCompatibility = "1.8"
targetCompatibility = "1.8"

dependencies {
annotationProcessor project(path: ":nullaway", configuration: "shadow")
annotationProcessor deps.test.lombok

compileOnly deps.build.jsr305Annotations
compileOnly deps.build.javaxValidation
compileOnly deps.test.cfQual
compileOnly deps.test.lombok
}

tasks.withType(JavaCompile) {
if (!name.toLowerCase().contains("test")) {
options.errorprone {
check("NullAway", CheckSeverity.ERROR)
check("UnusedVariable", CheckSeverity.OFF) // We are not the only checker that fails on Lombok
option("NullAway:AnnotatedPackages", "com.uber")
option("NullAway:UnannotatedSubPackages", "com.uber.lib.unannotated")
}
}
}
2 changes: 2 additions & 0 deletions test-java-lib-lombok/lombok.config
@@ -0,0 +1,2 @@
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 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.lombok;

import javax.annotation.Nullable;
import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class LombokBuilderInit {
private String field;
@Builder.Default private String fieldWithDefault = "Default";
@Nullable private String nullableField;
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 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.lombok;

class UsesBuilder {
public static String foo(LombokBuilderInit lbi) {
String s = "";
s += lbi.getField().toString();
s += " ";
// Removing this nullness check produces a NullAway error
s += (lbi.getNullableField() == null ? "" : lbi.getNullableField().toString());
return s;
}
}

0 comments on commit 97d9958

Please sign in to comment.