A tool to help eliminate NullPointerExceptions (NPEs) in your Java code with low build-time overhead

Overview

NullAway: Fast Annotation-Based Null Checking for Java Build Status Coverage Status

NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use NullAway, first add @Nullable annotations in your code wherever a field, method parameter, or return value may be null. Given these annotations, NullAway performs a series of type-based, local checks to ensure that any pointer that gets dereferenced in your code cannot be null. NullAway is similar to the type-based nullability checking in the Kotlin and Swift languages, and the Checker Framework and Eradicate null checkers for Java.

NullAway is fast. It is built as a plugin to Error Prone and can run on every single build of your code. In our measurements, the build-time overhead of running NullAway is usually less than 10%. NullAway is also practical: it does not prevent all possible NPEs in your code, but it catches most of the NPEs we have observed in production while imposing a reasonable annotation burden, giving a great "bang for your buck." At Uber, we combine NullAway with RAVE to obtain thorough protection against NPEs in our Android apps.

Installation

Overview

NullAway requires that you build your code with Error Prone, version 2.4.0 or higher. See the Error Prone documentation for instructions on getting started with Error Prone and integration with your build system. The instructions below assume you are using Gradle; see the docs for discussion of other build systems.

Gradle

Java (non-Android)

To integrate NullAway into your non-Android Java project, add the following to your build.gradle file:

plugins {
  // we assume you are already using the Java plugin
  id "net.ltgt.errorprone" version "0.6"
}

dependencies {
  annotationProcessor "com.uber.nullaway:nullaway:0.9.0"

  // Optional, some source of nullability annotations.
  // Not required on Android if you use the support 
  // library nullability annotations.
  compileOnly "com.google.code.findbugs:jsr305:3.0.2"

  errorprone "com.google.errorprone:error_prone_core:2.4.0"
  errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
}

import net.ltgt.gradle.errorprone.CheckSeverity

tasks.withType(JavaCompile) {
  // remove the if condition if you want to run NullAway on test code
  if (!name.toLowerCase().contains("test")) {
    options.errorprone {
      check("NullAway", CheckSeverity.ERROR)
      option("NullAway:AnnotatedPackages", "com.uber")
    }
  }
}

Let's walk through this script step by step. The plugins section pulls in the Gradle Error Prone plugin for Error Prone integration. If you are using the older apply plugin syntax instead of a plugins block, the following is equivalent:

buildscript {
  repositories {
    gradlePluginPortal()
  }
  dependencies {
    classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.6"
  }
}

apply plugin: 'net.ltgt.errorprone'

In dependencies, the annotationProcessor line loads NullAway, and the compileOnly line loads a JSR 305 library which provides a suitable @Nullable annotation (javax.annotation.Nullable). NullAway allows for any @Nullable annotation to be used, so, e.g., @Nullable from the Android Support Library or JetBrains annotations is also fine. The errorprone line ensures that a compatible version of Error Prone is used, and the errorproneJavac line is needed for JDK 8 compatibility.

Finally, in the tasks.withType(JavaCompile) section, we pass some configuration options to NullAway. First check("NullAway", CheckSeverity.ERROR) sets NullAway issues to the error level (it's equivalent to the -Xep:NullAway:ERROR standard Error Prone argument); by default NullAway emits warnings. Then, option("NullAway:AnnotatedPackages", "com.uber") (equivalent to the -XepOpt:NullAway:AnnotatedPackages=com.uber standard Error Prone argument), tells NullAway that source code in packages under the com.uber namespace should be checked for null dereferences and proper usage of @Nullable annotations, and that class files in these packages should be assumed to have correct usage of @Nullable (see the docs for more detail). NullAway requires at least the AnnotatedPackages configuration argument to run, in order to distinguish between annotated and unannotated code. See the configuration docs for other useful configuration options.

We recommend addressing all the issues that Error Prone reports, particularly those reported as errors (rather than warnings). But, if you'd like to try out NullAway without running other Error Prone checks, you can use options.errorprone.disableAllChecks (equivalent to passing "-XepDisableAllChecks" to the compiler, before the NullAway-specific arguments).

Snapshots of the development version are available in Sonatype's snapshots repository.

Android

The configuration for an Android project is very similar to the Java case, with one key difference: The com.google.code.findbugs:jsr305:3.0.2 dependency can be removed; you can use the android.support.annotation.Nullable annotation from the Android Support library.

dependencies {
  annotationProcessor "com.uber.nullaway:nullaway:0.9.0"
  errorprone "com.google.errorprone:error_prone_core:2.4.0"
  errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"  
}

A complete Android build.gradle example is here. Also see our sample app. (The sample app's build.gradle is not suitable for direct copy-pasting, as some configuration is inherited from the top-level build.gradle.)

Annotation Processors / Generated Code

Some annotation processors like Dagger and AutoValue generate code into the same package namespace as your own code. This can cause problems when setting NullAway to the ERROR level as suggested above, since errors in this generated code will block the build. Currently the best solution to this problem is to completely disable Error Prone on generated code, using the -XepExcludedPaths option added in Error Prone 2.13 (documented here, use options.errorprone.excludedPaths= in Gradle). To use, figure out which directory contains the generated code, and add that directory to the excluded path regex.

Note for Dagger users: Dagger versions older than 2.12 can have bad interactions with NullAway; see here. 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:

static void log(Object x) {
    System.out.println(x.toString());
}
static void foo() {
    log(null);
}

This code is buggy: when foo() is called, the subsequent call to log() will fail with an NPE. You can see this error in the NullAway sample app by running:

cp sample/src/main/java/com/uber/mylib/MyClass.java.buggy sample/src/main/java/com/uber/mylib/MyClass.java
./gradlew build

By default, NullAway assumes every method parameter, return value, and field is non-null, i.e., it can never be assigned a null value. In the above code, the x parameter of log() is assumed to be non-null. So, NullAway reports the following error:

warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
    log(null);
        ^

We can fix this error by allowing null to be passed to log(), with a @Nullable annotation:

static void log(@Nullable Object x) {
    System.out.println(x.toString());
}

With this annotation, NullAway points out the possible null dereference:

warning: [NullAway] dereferenced expression x is @Nullable
    System.out.println(x.toString());
                        ^

We can fix this warning by adding a null check:

static void log(@Nullable Object x) {
    if (x != null) {
        System.out.println(x.toString());
    }
}

With this change, all the NullAway warnings are fixed.

For more details on NullAway's checks, error messages, and limitations, see our detailed guide.

Support

Please feel free to open a GitHub issue if you have any questions on how to use NullAway. Or, you can join the NullAway Discord server and ask us a question there.

Contributors

We'd love for you to contribute to NullAway! Please note that once you create a pull request, you will be asked to sign our Uber Contributor License Agreement.

License

NullAway is licensed under the MIT license. See the LICENSE.txt file for more information.

Issues
  • CheckerFramework's @Nullable

    CheckerFramework's @Nullable

    Migrating from JSR-305 to the CheckerFramework and ErrorProne annotations resulted in false positives. Is org.checkerframework.checker.nullness.qual.Nullable supported? It doesn't appear to be.

    bug 
    opened by ben-manes 23
  • Stream support

    Stream support

    Thank you for contributing to NullAway!

    Please note that once you click "Create Pull Request" you will be asked to sign our Uber Contributor License Agreement via CLA assistant.

    Before pressing the "Create Pull Request" button, please provide the following:

    • [ ] A description about what and why you are contributing, even if it's trivial. Solve nullability check
    • [ ] The issue number(s) or PR number(s) in the description if you are contributing in response to those. Trying to solve #332
    • [ ] If applicable, unit tests.

    This is not ready by any means. Just wanted to let you know that copying the rxhandler and configuring it works fine.

    I just want to define the scope. This PR is just for regular Streams, not primitive streams. I will configure the stream function. After all cases are covered, do you want to unite the logic of rx streams and java streams (DRY school) or are you more of the copy school till the logic was implemented x times?

    opened by david-gang 23
  • Does not work with Java + Kotlin Android based projects

    Does not work with Java + Kotlin Android based projects

    Repro:

    buildscript {
      repositories {
        google()
        maven { url "https://plugins.gradle.org/m2/" }
      }
    
      dependencies {
        classpath "com.android.tools.build:gradle:2.3.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0"
        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.11"
      }
    }
    
    repositories {
      google()
      maven { url "https://plugins.gradle.org/m2/" }
    }
    
    apply plugin: "com.android.application"
    apply plugin: "com.kotlin-android"
    apply plugin: "com.kotlin-kapt"
    apply plugin: "net.ltgt.errorprone"
    
    android {
      ...
    }
    
    dependencies {
      kapt "com.uber.nullaway:nullaway:0.2.2"
    
      errorprone "com.google.errorprone:error_prone_core:2.1.1"
    }
    
    tasks.withType(JavaCompile) {
      // remove the if condition if you want to run NullAway on test code
      if (!name.toLowerCase().contains("test")) {
        options.compilerArgs += ["-Xep:NullAway:ERROR", "-XepOpt:NullAway:AnnotatedPackages=com.uber"]
      }
    }
    

    Error:

    > Task :app:compileDebugJavaWithJavac FAILED
    Note: Some input files use or override a deprecated API.
    Note: Recompile with -Xlint:deprecation for details.
    com.google.errorprone.InvalidCommandLineOptionException: NullAway is not a valid checker name
    
    FAILURE: Build failed with an exception.
    
    * What went wrong:
    Execution failed for task ':app:compileDebugJavaWithJavac'.
    > Compilation failed with exit code 2; see the compiler error output for details.
    
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
    
    * Get more help at https://help.gradle.org
    
    BUILD FAILED in 1m 25s
    77 actionable tasks: 77 executed
    
    opened by jaredsburrows 23
  • Spring bean properties

    Spring bean properties

    We have a similar issue as described in #94, but wanted to call out explicitly as it may not be quite the same.

    We have a very common pattern of something like:

    private MyObject myObject;

    in a class, and its value is set via spring bean property injection.

    These always yield initializer warnings.

    Is our best option here to use @SuppressWarnings("NullAway.Init") everywhere we have a field like this?

    or register @Inject in ExcludedFieldAnnotations -- but this seems to be effectively the same as @SuppressWarnings("NullAway.Init")

    opened by ctcpip 22
  • Optional get call on assignment gives a correct warning

    Optional get call on assignment gives a correct warning

    Assigning potentially empty optional get value to an object now gives a correct error

    Optional<Integer> optionalFoo = someFunction();
    Integer x = optionalFoo.get();
    x.toString();
    

    It will give error that optionalFoo can be empty.

    Fixes #351

    opened by shas19 17
  • NullAway not recognized as an EP option in Android Gradle Plugin 3.x

    NullAway not recognized as an EP option in Android Gradle Plugin 3.x

    Repro branch can be found here - https://github.com/uber/AutoDispose/tree/z/agp3testing1orchestrator

    Compiling the arch components artifact will give an error that NullAway is not a recognized argument. Not sure if something changed with how this is handled as far as compiler args go in 3.x, but we should provide some guidance on this.

    CC @kageiit

    help wanted 
    opened by ZacSweers 16
  • Support records

    Support records

    Fixes #374

    Add a useful level of support for records while avoiding any record-specific logic.


    Update Checker Framework to 3.1.0. This contains fixes for the dataflow and javacutil artifacts that allow them to analyze record classes without throwing exceptions.


    [copied from one of the commit messages]

    Avoid throwing exceptions when the source code contains records. Since records are a new kind of class, refactor the switch statements that make assertions about which elements and trees represent classes. Use general purpose isClassElement and isClassTree methods that are forward-compatible with records instead of enumerating ElementKind or Tree.Kind constants.

    Refactor checkFieldInitialization to ignore final fields. This check was wrongly flagging records because it couldn't analyze record constructors properly. Without the change to ignore final fields, this check runs into two kinds of trouble with records:

    1. It doesn't inspect record constructors at all because they are generated. NullAway.isConstructor(MethodTree) returns false and so those trees are skipped.

    2. The trees for the generated constructors don't contain explicit field assignments. That's just not how those were implemented. Something else is reponsible for assigning the fields at runtime, apparently.

    Rather than trying to teach NullAway about record constructors specifically, count on javac to verify that all final fields are initialized. Presumably, if NullAway were to complain that a constructor did not initialize a final field, that complaint would be redundant (with the error that javac would produce) at best and incorrect otherwise.

    opened by michaelhixson 15
  • When reporting error on an initializer, also give line numbers for uninitialized fields

    When reporting error on an initializer, also give line numbers for uninitialized fields

    In java.util.AbstractMap, the field "values" should be annotated with @MonotonicNonNull. However, there is no way to annotate it. The best way is to use a library model, I guess.

    https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/AbstractMap.java#L300

    good first issue help wanted 
    opened by chengniansun 15
  • Conflict when running Checker Framework and NullAway in the same build

    Conflict when running Checker Framework and NullAway in the same build

    /home/xeno/IdeaProjects/brix/config-loader/spi/src/main/java/com/xenoterracide/brix/configloader/spi/ConfigValueProcessor.java:23: error: An unhandled exception was thrown by the Error Prone static analysis plugin.
    public class ConfigValueProcessor {
           ^
         Please report this at https://github.com/google/error-prone/issues/new and include the following:
      
         error-prone version: 2.6.0
         BugPattern: NullAway
         Stack Trace:
         com.google.common.util.concurrent.ExecutionError: java.lang.NoSuchMethodError: 'java.lang.Object org.checkerframework.dataflow.cfg.node.NodeVisitor.visitImplicitThisLiteral(org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode, java.lang.Object)'
      	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2049)
      	at com.google.common.cache.LocalCache.get(LocalCache.java:3951)
      	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3974)
      	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4935)
      	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4941)
      	at com.uber.nullaway.dataflow.DataFlow.dataflow(DataFlow.java:149)
      	at com.uber.nullaway.dataflow.DataFlow.finalResult(DataFlow.java:222)
      	at com.uber.nullaway.dataflow.AccessPathNullnessAnalysis.getNonnullFieldsOfReceiverAtExit(AccessPathNullnessAnalysis.java:143)
      	at com.uber.nullaway.NullAway.guaranteedNonNullForConstructor(NullAway.java:1588)
      	at com.uber.nullaway.NullAway.checkConstructorInitialization(NullAway.java:1565)
      	at com.uber.nullaway.NullAway.checkFieldInitialization(NullAway.java:1424)
      	at com.uber.nullaway.NullAway.matchClass(NullAway.java:1154)
      	at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:450)
      	at com.google.errorprone.scanner.ErrorProneScanner.visitClass(ErrorProneScanner.java:548)
      	at com.google.errorprone.scanner.ErrorProneScanner.visitClass(ErrorProneScanner.java:151)
      	at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:808)
      	at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82)
      	at com.google.errorprone.scanner.Scanner.scan(Scanner.java:74)
      	at com.google.errorprone.scanner.Scanner.scan(Scanner.java:48)
      	at jdk.compiler/com.sun.source.util.TreeScanner.scan(TreeScanner.java:105)
      	at jdk.compiler/com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:113)
      	at jdk.compiler/com.sun.source.util.TreeScanner.visitCompilationUnit(TreeScanner.java:144)
      	at com.google.errorprone.scanner.ErrorProneScanner.visitCompilationUnit(ErrorProneScanner.java:561)
      	at com.google.errorprone.scanner.ErrorProneScanner.visitCompilationUnit(ErrorProneScanner.java:151)
      	at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCompilationUnit.accept(JCTree.java:591)
      	at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:56)
      	at com.google.errorprone.scanner.Scanner.scan(Scanner.java:58)
      	at com.google.errorprone.scanner.ErrorProneScannerTransformer.apply(ErrorProneScannerTransformer.java:43)
      	at com.google.errorprone.ErrorProneAnalyzer.finished(ErrorProneAnalyzer.java:152)
      	at jdk.compiler/com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:132)
      	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1418)
      	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1365)
      	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:960)
      	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104)
      	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:147)
      	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)
      	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)
      	at org.gradle.internal.compiler.java.IncrementalCompileTask.call(IncrementalCompileTask.java:77)
      	at org.gradle.api.internal.tasks.compile.AnnotationProcessingCompileTask.call(AnnotationProcessingCompileTask.java:94)
      	at org.gradle.api.internal.tasks.compile.ResourceCleaningCompilationTask.call(ResourceCleaningCompilationTask.java:57)
      	at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:55)
      	at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:40)
      	at org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler$CompilerWorkAction.execute(AbstractDaemonCompiler.java:135)
      	at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
      	at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:49)
      	at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:43)
      	at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:97)
      	at org.gradle.workers.internal.AbstractClassLoaderWorker.executeInClassLoader(AbstractClassLoaderWorker.java:43)
      	at org.gradle.workers.internal.FlatClassLoaderWorker.run(FlatClassLoaderWorker.java:32)
      	at org.gradle.workers.internal.FlatClassLoaderWorker.run(FlatClassLoaderWorker.java:22)
      	at org.gradle.workers.internal.WorkerDaemonServer.run(WorkerDaemonServer.java:85)
      	at org.gradle.workers.internal.WorkerDaemonServer.run(WorkerDaemonServer.java:55)
      	at org.gradle.process.internal.worker.request.WorkerAction$1.call(WorkerAction.java:138)
      	at org.gradle.process.internal.worker.child.WorkerLogEventListener.withWorkerLoggingProtocol(WorkerLogEventListener.java:41)
      	at org.gradle.process.internal.worker.request.WorkerAction.run(WorkerAction.java:135)
      	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
      	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
      	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
      	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
      	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
      	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
      	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
      	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
      	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
      	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
      	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
      	at java.base/java.lang.Thread.run(Thread.java:829)
      Caused by: java.lang.NoSuchMethodError: 'java.lang.Object org.checkerframework.dataflow.cfg.node.NodeVisitor.visitImplicitThisLiteral(org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode, java.lang.Object)'
      	at org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode.accept(ImplicitThisLiteralNode.java:21)
      	at org.checkerframework.dataflow.analysis.AbstractAnalysis.callTransferFunction(AbstractAnalysis.java:336)
      	at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.callTransferFunction(ForwardAnalysisImpl.java:373)
      	at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysisBlock(ForwardAnalysisImpl.java:128)
      	at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysis(ForwardAnalysisImpl.java:105)
      	at com.uber.nullaway.dataflow.DataFlow$1.load(DataFlow.java:88)
      	at com.uber.nullaway.dataflow.DataFlow$1.load(DataFlow.java:80)
      	at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3529)
      	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2278)
      	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2155)
      	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2045)
    

    this is the offending class

    package com.xenoterracide.brix.configloader.spi;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.mitchellbosecke.pebble.PebbleEngine;
    import com.xenoterracide.brix.cli.api.CliConfiguration;
    import com.xenoterracide.brix.configloader.api.ImmutableProcessedConfig;
    import com.xenoterracide.brix.configloader.api.ImmutableProcessedFileConfiguration;
    import com.xenoterracide.brix.configloader.api.ProcessedConfig;
    import com.xenoterracide.brix.configloader.api.ProcessedFileConfiguration;
    import io.vavr.control.Try;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.stereotype.Component;
    
    import java.io.StringWriter;
    import java.nio.file.Path;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    @Component
    public class ConfigValueProcessor {
      private final Logger log = LogManager.getLogger( this.getClass() );
    
      private final PebbleEngine engine;
    
      private final CliConfiguration cliConfiguration;
    
      private final ObjectMapper mapper;
    
      private final Path configDir;
    
      ConfigValueProcessor(
        CliConfiguration cliConfiguration,
        PebbleEngine stringEngine,
        ObjectMapper mapper,
        Path foundConfig
      ) {
        this.engine = stringEngine;
        this.cliConfiguration = cliConfiguration;
        this.mapper = mapper;
        this.configDir = foundConfig.getParent();
      }
    
      public ProcessedConfig from( RawConfig config ) {
        var fcs
          = config.getFileConfigurations()
          .stream()
          .map( this::from )
          .collect( Collectors.toList() );
    
        var processed = ImmutableProcessedConfig.builder().fileConfigurations( fcs ).build();
        log.debug( "processed: {}", processed );
        return processed;
      }
    
      ProcessedFileConfiguration from( RawFileConfiguration config ) {
        var context = this.getContext( config.getContext() );
        var bldr = ImmutableProcessedFileConfiguration.builder();
        bldr.overwrite( config.getOverwrite() );
        bldr.context( context );
    
        config.getSource().ifPresent( src -> {
          bldr.source( Path.of( this.processTemplate( src, context ) ) );
        } );
        return bldr.build();
      }
    
      @SuppressWarnings("unchecked")
      Map<String, Object> getContext( Map<String, String> context ) {
        var map = new HashMap<String, Object>();
        map.putAll( context );
        map.putAll( mapper.convertValue( cliConfiguration, Map.class ) );
        map.put( "configDir", configDir.getParent() );
        return Collections.unmodifiableMap( map );
      }
    
      String processTemplate( String template, Map<String, Object> context ) {
        var writer = new StringWriter();
        Try.run( () -> engine.getTemplate( template ).evaluate( writer, context ) ).get();
        return writer.toString();
      }
    }
    
    ------------------------------------------------------------
    Gradle 7.0
    ------------------------------------------------------------
    
    Build time:   2021-04-09 22:27:31 UTC
    Revision:     d5661e3f0e07a8caff705f1badf79fb5df8022c4
    
    Kotlin:       1.4.31
    Groovy:       3.0.7
    Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
    JVM:          11.0.10 (AdoptOpenJDK 11.0.10+9)
    OS:           Linux 5.11.10-200.fc33.x86_64 amd64
    
    \--- com.uber.nullaway:nullaway:0.+ -> 0.9.1
         +--- org.checkerframework:dataflow:3.6.0
         |    +--- org.checkerframework:checker-qual:3.6.0 -> 3.8.0
         |    \--- org.checkerframework:javacutil:3.6.0
         |         +--- org.checkerframework:checker-qual:3.6.0 -> 3.8.0
         |         \--- org.plumelib:plume-util:1.1.4
         |              \--- org.plumelib:reflection-util:0.2.2
    
    Fedora 33
    5.11.10-200.fc33.x86_64
     10:25:10 up 26 days, 11:31,  1 user,  load average: 1.36, 1.71, 1.68
                  total        used        free      shared  buff/cache   available
    Mem:            15G         12G        651M        1.2G        2.7G        1.9G
    Swap:           12G        2.8G        9.2G
    

    this issue does not exist if I switch my version to 0.8.+ instead of just 0.+

    opened by xenoterracide 12
  • Possible false positives using Objects.requireNonNull?

    Possible false positives using Objects.requireNonNull?

    Hi there,

    Consider the following code:

    @Nullable
    CustomValue value;    // member variable
    ...
    
    public void doSomething() {
        value = dao.getValueWithId(1);    // getValue can return null if no object with the specified id exists
        Objects.requireNonNull(value, "Value is null!");    // throw NPE if value is null.  Afterwards, value can be considered non-null
    
        final String name = value.getName();
        ...
    }
    

    I see the following reports when running nullaway:

    /Users/zach/Developer/Code/sampleapp/app/src/main/java/com/zao/sampleapp/SampleClass.java:20: warning: [NullAway] dereferenced expression value is @Nullable
            final String name = value.getName();
                                     ^
        (see http://t.uber.com/nullaway )
    

    Is this a false positive? Should this be safely ignored? Or am I using Objects.requireNonNull incorrectly? I know, for instance, that value = Objects.requireNonNull(dao.getValueWithId(1)) would avoid this, but I'd like to avoid "nesting" the calls.

    Edit: I am using nullaway 0.6.4 - setup via errorprone in an Android project, FWIW.

    Edit2: Updated Objects.requireNonNull call to include a message. This works perfectly when the message parameter is omitted.

    opened by ZOlbrys 12
  • NoSuchMethodError

    NoSuchMethodError

    I was following up the sample in below link to setup NullAway in my project, but I saw below NoSuchMethodError. Is there any suggestion for fixing this error?

    https://gist.github.com/msridhar/6cacd429567f1d1ad9a278e06809601c

    An exception has occurred in the compiler ((version info not available)). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you. java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.toImmutableSet()Ljava/util/stream/Collector; at com.google.errorprone.BugCheckerInfo.customSuppressionAnnotations(BugCheckerInfo.java:144) at com.google.errorprone.BugCheckerInfo.(BugCheckerInfo.java:122) at com.google.errorprone.BugCheckerInfo.create(BugCheckerInfo.java:110) at com.google.errorprone.scanner.BuiltInCheckerSuppliers.getSuppliers(BuiltInCheckerSuppliers.java:298) at com.google.errorprone.scanner.BuiltInCheckerSuppliers.getSuppliers(BuiltInCheckerSuppliers.java:291) at com.google.errorprone.scanner.BuiltInCheckerSuppliers.(BuiltInCheckerSuppliers.java:327) at com.google.errorprone.ErrorProneJavacPlugin.init(ErrorProneJavacPlugin.java:42) at com.sun.tools.javac.api.BasicJavacTask.initPlugins(BasicJavacTask.java:214) at com.sun.tools.javac.api.JavacTaskImpl.prepareCompiler(JavacTaskImpl.java:192) at com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:97) at com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142) at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:96) at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:90) at org.gradle.api.internal.tasks.compile.AnnotationProcessingCompileTask.call(AnnotationProcessingCompileTask.java:93) at org.gradle.api.internal.tasks.compile.ResourceCleaningCompilationTask.call(ResourceCleaningCompilationTask.java:57) at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:54) at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:39) at org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler$CompilerWorkAction.execute(AbstractDaemonCompiler.java:113) at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:47) at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:46) at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:36) at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98) at org.gradle.workers.internal.AbstractClassLoaderWorker.executeInClassLoader(AbstractClassLoaderWorker.java:36) at org.gradle.workers.internal.FlatClassLoaderWorker.execute(FlatClassLoaderWorker.java:31) at org.gradle.workers.internal.WorkerDaemonServer.execute(WorkerDaemonServer.java:56) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.process.internal.worker.request.WorkerAction.run(WorkerAction.java:118) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164) at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.lang.Thread.run(Thread.java:748)

    Task :Teams:compileDevDebugJavaWithJavac FAILED

    opened by wecheng 23
  • Better documentation and tests for initializer support

    Better documentation and tests for initializer support

    not sure how to fix this

    > Task :cli:config:compileJava FAILED
    /home/xeno/IdeaProjects/brix/modules/cli/config/src/main/java/com/xenoterracide/cli/config/CliCommand.java:32: error: [NullAway] initializer method does not guarantee @NonNull fields language (line 22), moduleType (line 24), project (line 26) are initialized along all control-flow paths (remember to check for exceptions or early returns).
      CliCommand( Dispatcher dispatcher ) {
      ^
        (see http://t.uber.com/nullaway )
    1 error
    

    I've tried adding this via the ep plugin

        option("NullAway:AnnotatedPackages", "com.xenoterracide")
        option("NullAway:ExternalInitAnnotations", "picocli.CommandLine.Option")
        option("NullAway:CustomInitializerAnnotations", "picocli.CommandLine.Option")
    
    package com.xenoterracide.cli.config;
    
    import com.xenoterracide.brix.cli.api.CliConfiguration;
    import com.xenoterracide.brix.dispatch.Dispatcher;
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
    import org.springframework.stereotype.Component;
    import picocli.CommandLine;
    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Optional;
    
    @Component
    public class CliCommand implements CliConfiguration, Runnable {
    
      private final Dispatcher dispatcher;
    
      private Path workdir = Paths.get( "" );
    
      private String language;
    
      private String moduleType;
    
      private String project;
    
      private @MonotonicNonNull String name;
    
      private @MonotonicNonNull Path repo;
    
      CliCommand( Dispatcher dispatcher ) {
        this.dispatcher = dispatcher;
      }
    
      @Override
      public String toString() {
        return ToStringBuilder.reflectionToString( this, ToStringStyle.MULTI_LINE_STYLE );
      }
    
      @Override
      public Optional<Path> getRepo() {
        return Optional.ofNullable( repo );
      }
    
      @CommandLine.Option(
        names = {"--repo"},
        description = "Repository path from the current working directory. " +
          "Templates and configs are looked up relative to here. If the config " +
          "isn't found here, then we will search ~/.config/brix"
      )
      public void setRepo( Path repo ) {
        this.repo = repo;
      }
    
      @Override
      public Path getWorkdir() {
        return workdir;
      }
    
      @CommandLine.Option(
        names = {"--workdir"},
        defaultValue = "",
        showDefaultValue = CommandLine.Help.Visibility.ALWAYS,
        description = "The working directory you want your destination paths to be relative to." +
          " Defaults to current working directory"
      )
      public void setWorkdir( Path workdir ) {
        this.workdir = workdir;
      }
    
      @Override
      public String getProject() {
        return project;
      }
    
      @CommandLine.Parameters(
        index = "2",
        description = "The name of the project you're generating code for."
      )
      public void setProject( String project ) {
        this.project = project;
      }
    
      @Override
      public String getLanguage() {
        return language;
      }
    
      @Override
      public String getModuleType() {
        return moduleType;
      }
    
      @CommandLine.Parameters(
        index = "1",
        description = "The type of code you're generating e.g controller, also the name of the config" +
          " file without the extension."
      )
      public void setModuleType( String moduleType ) {
        this.moduleType = moduleType;
      }
    
      @Override
      public @MonotonicNonNull String getName() {
        return name;
      }
    
      @CommandLine.Parameters(
        index = "3",
        description = "The name of the module to be created within the project.",
        arity = "0"
      )
      public void setName( String name ) {
        this.name = name;
      }
    
      @CommandLine.Parameters(
        index = "0",
        description = "The programming language you're generating code for. Directory under --dir"
      )
      public void setLanguage( String language ) {
        this.language = language;
      }
    
      @Override
      public void run() {
        dispatcher.run();
      }
    }
    
    opened by xenoterracide 8
  • fix non android gradle instructions

    fix non android gradle instructions

    resolves #466

    opened by xenoterracide 2
  • Instructions for plain gradle incorrect

    Instructions for plain gradle incorrect

    plain gradle instructions are incorrect, they lead to this

    given

    plugins {
      `java-library`
      `java-test-fixtures`
      id("brix.bom")
      id("net.ltgt.errorprone")
      id("org.checkerframework")
    }
    
    dependencies {
      errorprone("com.google.errorprone:error_prone_core:2.+")
      annotationProcessor("com.uber.nullaway:nullaway:0.8.+")
      checkerFramework(checker.processor)
      compileOnly(checker.annotations)
      testFixturesCompileOnly(checker.annotations)
    }
    
    tasks.withType<JavaCompile>().configureEach {
      options.compilerArgs.addAll(
        listOf(
          "-parameters",
          "-Xlint:deprecation",
          "-Xlint:unchecked"
        )
      )
      options.errorprone {
        disableWarningsInGeneratedCode.set(true)
        excludedPaths.set(".*/build/generated/sources/annotationProcessor/.*")
        option("NullAway:AnnotatedPackages", "com.xenoterracide")
        val errors = mutableListOf(
        errors.add("NullAway")
        error(*errors.toTypedArray())
        ...
    

    I get this error

    > Task :util:compileTestFixturesJava FAILED
    
    FAILURE: Build failed with an exception.
    
    * What went wrong:
    Execution failed for task ':util:compileTestFixturesJava'.
    > NullAway is not a valid checker name
    
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    
    * Get more help at https://help.gradle.org
    
    BUILD FAILED in 5s
    7 actionable tasks: 7 executed
    
    opened by xenoterracide 7
  • Is there any way to handle null-firendly DTOs?

    Is there any way to handle null-firendly DTOs?

    I have some typical DTO classes like

       public class User{
               private String firstName;
               private String lastName;
              // public getters and setters
       }
    

    Every field can be null, every setter can accept null, every getter can return null.

    Is there any way to marke the whole class as "Everything can be null" ? What is the recommended practice?

    opened by pablogrisafi1975 2
  • AnnotatedPackages w/ com.google.common not working due to Preconditions.checkNotNull

    AnnotatedPackages w/ com.google.common not working due to Preconditions.checkNotNull

    I'm using Nullaway 0.9.0 using the Bazel build system. We have a small library that is using google guava and are trying to treat the Guava library as if it is correctly annotated with Nullable annotations. Our problem is that when we add com.google.common to our list of annotatedPackages, the build starts failing because it says that Preconditions.checkNotNull rejects a Nullable field.

    The reason we're trying to do this in our real application is because we have some fields where a null value indicates a coding mistake and cannot easily restructure the code to avoid this pattern. Sample code:

    BUILD.bazel

    java_library(
        name = "mypackage",
        srcs = glob(
            ["*.java"],
        ),
        javacopts = [
            "-XepOpt:NullAway:AnnotatedPackages=com.mypackage,com.google.common",
        ],
        plugins = [
            "//third_party:nullaway",
        ],
        visibility = [
        ],
        deps = [
            "@third_party_jvm//3rdparty/jvm/com/google/code/findbugs:jsr305",
            "@third_party_jvm//3rdparty/jvm/com/google/guava",
        ],
    )
    

    com.mypackage.SomeClass

    package com.mypackage;
    
    import com.google.common.base.Preconditions;
    import com.google.common.collect.ImmutableList;
    import javax.annotation.Nullable;
    
    public class SomeClass {
    
      public ImmutableList<String> someMethod(@Nullable String nullable) {
        return ImmutableList.of(Preconditions.checkNotNull(nullable));
      }
    }
    

    Expected:

    My understanding is that this code should pass because Preconditions.checkNotNull is marked as a special method that accepts a nullable field, but ensures that the returned value is no-null because it fails if it is null. This is from reading the javadoc of the LibraryModels#failIfNullParameters interface (Note that description is actually on the nonNullParameters method). The code indicates that Preconditions is marked as FAIL_IF_NULL_PARAMETERS. This appears to be wired in as the default, and I believe that the documentation indicates that this should always take precedence:

    Also, specific method annotations can always be overridden by explicit Library Models, which take precedence over both the optimistic defaults and any annotations in the code, whether marked as annotated or unannotated.

    Actual behaviour:

    From Building src/main/java/com/mypackage/libmypackage.jar (1 source file):
    src/main/java/com/mypackage/SomeClass.java:10: warning: [NullAway] passing @Nullable parameter 'nullable' where @NonNull is required
        return ImmutableList.of(Preconditions.checkNotNull(nullable));
                                                           ^
        (see http://t.uber.com/nullaway )
    

    Our workaround right now would be to include guava in the AnnotatedPackages classpath but treat Preconditions as an unannotated class

        javacopts = [
            "-XepOpt:NullAway:AnnotatedPackages=com.mypackage,com.google.common",
            "-XepOpt:NullAway:UnannotatedClasses=com.google.common.base.Preconditions",
        ],
    

    This approach the build works as expected, however, from the documentation this doesn't appear to be the expected integration path. Is this a bug where the override behaviour is not working or am I misreading the documentation?


    We ran through some extra tests just to see what would happen.

    Test case 1: Do not include guava in the AnnotatedPackages classpath

    java_library(
        name = "mypackage",
        srcs = glob(
            ["*.java"],
        ),
        javacopts = [
            "-XepOpt:NullAway:AnnotatedPackages=com.mypackage",
        ],
        plugins = [
            "//third_party:nullaway",
        ],
        visibility = [
        ],
        deps = [
            "@third_party_jvm//3rdparty/jvm/com/google/code/findbugs:jsr305",
            "@third_party_jvm//3rdparty/jvm/com/google/guava",
        ],
    )
    
    package com.mypackage;
    
    import com.google.common.collect.ImmutableList;
    import javax.annotation.Nullable;
    
    public class SomeClass {
    
      public ImmutableList<String> someMethod(@Nullable String nullable) {
        return ImmutableList.of(nullable);
      }
    }
    

    As expected, errorprone did not error out and NullAway because it will not treat ImmutableList's argument as a nullable parameter.

    Test case 2: Include guava in the AnnotatedPackages classpath but don't use Preconditions

    java_library(
        name = "mypackage",
        srcs = glob(
            ["*.java"],
        ),
        javacopts = [
            "-XepOpt:NullAway:AnnotatedPackages=com.mypackage,com.google.base",
        ],
        plugins = [
            "//third_party:nullaway",
        ],
        visibility = [
        ],
        deps = [
            "@third_party_jvm//3rdparty/jvm/com/google/code/findbugs:jsr305",
            "@third_party_jvm//3rdparty/jvm/com/google/guava",
        ],
    )
    
    public class SomeClass {
    
      public ImmutableList<String> someMethod(@Nullable String nullable) {
        return ImmutableList.of(nullable);
      }
    }
    

    As expected, errorprone failed with the following message.

    INFO: From Building src/main/java/com/mypackage/libmypackage.jar (1 source file):
    src/main/java/com/mypackage/SomeClass.java:9: warning: [NullAway] passing @Nullable parameter 'nullable' where @NonNull is required
        return ImmutableList.of(nullable);
                                ^
        (see http://t.uber.com/nullaway )
    
    opened by rwinograd 6
  • IncompatibleClassChangeError with 0.9.0, JDK 8, and error-prone 2.5.1

    IncompatibleClassChangeError with 0.9.0, JDK 8, and error-prone 2.5.1

    When trying to update to 0.9.0, we see this runtime error from dataflow come up

    > Task :libraries:foundation:slack-commons:compileReleaseJavaWithJavac
    <path≥/slack/commons/logger/DebugLogger.java:27: error: An unhandled exception was thrown by the Error Prone static analysis plugin.
    public class DebugLogger implements Logger {
           ^
         Please report this at https://github.com/google/error-prone/issues/new and include the following:
      
         error-prone version: 2.5.1
         BugPattern: NullAway
         Stack Trace:
         java.lang.IncompatibleClassChangeError: Implementing class
            at java.lang.ClassLoader.defineClass1(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
            at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
            at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
            at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
            at java.lang.ClassLoader.defineClass1(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
            at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
            at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
            at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
            at com.uber.nullaway.dataflow.DataFlow.<init>(DataFlow.java:78)
            at com.uber.nullaway.dataflow.AccessPathNullnessAnalysis.<init>(AccessPathNullnessAnalysis.java:74)
            at com.uber.nullaway.dataflow.AccessPathNullnessAnalysis.instance(AccessPathNullnessAnalysis.java:104)
            at com.uber.nullaway.NullAway.getNullnessAnalysis(NullAway.java:1971)
            at com.uber.nullaway.NullAway.matchClass(NullAway.java:1139)
            at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:450)
            at com.google.errorprone.scanner.ErrorProneScanner.visitClass(ErrorProneScanner.java:548)
            at com.google.errorprone.scanner.ErrorProneScanner.visitClass(ErrorProneScanner.java:151)
            at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:808)
            at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82)
            at com.google.errorprone.scanner.Scanner.scan(Scanner.java:74)
            at com.google.errorprone.scanner.Scanner.scan(Scanner.java:48)
            at com.sun.source.util.TreeScanner.scan(TreeScanner.java:105)
            at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:113)
            at com.sun.source.util.TreeScanner.visitCompilationUnit(TreeScanner.java:144)
            at com.google.errorprone.scanner.ErrorProneScanner.visitCompilationUnit(ErrorProneScanner.java:561)
            at com.google.errorprone.scanner.ErrorProneScanner.visitCompilationUnit(ErrorProneScanner.java:151)
            at com.sun.tools.javac.tree.JCTree$JCCompilationUnit.accept(JCTree.java:591)
            at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:56)
            at com.google.errorprone.scanner.Scanner.scan(Scanner.java:58)
            at com.google.errorprone.scanner.ErrorProneScannerTransformer.apply(ErrorProneScannerTransformer.java:43)
            at com.google.errorprone.ErrorProneAnalyzer.finished(ErrorProneAnalyzer.java:152)
            at com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:120)
            at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1404)
            at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1353)
            at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:946)
            at com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:100)
            at com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142)
            at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:96)
            at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:90)
            at org.gradle.internal.compiler.java.IncrementalCompileTask.call(IncrementalCompileTask.java:74)
            at org.gradle.api.internal.tasks.compile.AnnotationProcessingCompileTask.call(AnnotationProcessingCompileTask.java:94)
            at org.gradle.api.internal.tasks.compile.ResourceCleaningCompilationTask.call(ResourceCleaningCompilationTask.java:57)
            at ...more gradle lines
    1 error
    

    Let me know what else I can help test. Possibly using a non-JDK8-compatible dataflow version?

    bug 
    opened by ZacSweers 30
  • Run GH Actions on pushes to branches?

    Run GH Actions on pushes to branches?

    I just found a negative consequence of our config to only run GH Actions on pushes to master:

    https://github.com/uber/NullAway/blob/af24db1e55daeb10029e25877a55e19f343344e4/.github/workflows/continuous-integration.yml#L2-L6

    Now, even on my fork of NullAway, I need to create a pull request in order to get CI jobs to run when I push to a branch. I just made a PR on my fork to work around this, but this is an annoying extra step.

    Maybe instead of the current config, we can just run GH Actions on pushes to branches, and have a convention of keeping one-off branches for PRs in our forks rather than upstream?

    opened by msridhar 1
  • Handling interactions between casts and our map handling in dataflow

    Handling interactions between casts and our map handling in dataflow

    The following null check is both correct and fully understood by NullAway:

    public void foo(HashMap valueMap) {
        String message =  valueMap.get("message") == null ? "" : (String) valueMap.get("message");
        expectsNonNull(message);  // NullAway knows message is @NonNull here
    }
    

    However, it's fairly natural - albeit unnecessary - for developers to also add a cast on the check side of the ternary operator:

    public void foo(HashMap valueMap) {
        String message =  (String) valueMap.get("message") == null ? "" : (String) valueMap.get("message");
       // warning: [NullAway] passing @Nullable parameter 'message' where @NonNull is required
        expectsNonNull(message);  
    }
    

    This breaks our handling of maps, resulting in the "passing @Nullable parameter" error above.

    The underlying reason likely has to be with the fact that (String) valueMap.get("message") is being compared to null, not valueMap.get("message"), and thus we never learn the nullability of that access path as far as our dataflow algorithm is concerned.

    Note: (String) valueMap.get("message") == null is parsed as ((String) valueMap.get("message")) == null.

    This is probably fairly low priority to handle, but might be worth fixing if it's easy enough.

    bug lowpriority 
    opened by lazaroclapp 0
  • Suggestion: Accept a `Nullable` annotation name and suggest it in cases where it's missing

    Suggestion: Accept a `Nullable` annotation name and suggest it in cases where it's missing

    Error-prone itself has some checks for this, but nullaway is obviously better at it.

    Given an error like this

    /Users/zsweers/dev/slack/restructure/slack-android-ng/libraries/foundation/slack-commons/src/main/java/slack/commons/logger/DebugLogger.java:62: error: [NullAway] parameter tag is @NonNull, but parameter in superclass method slack.commons.logger.Logger.log(java.lang.String,java.lang.String,java.lang.Throwable) is @Nullable
      public void log(final String tag, final String message, final Throwable throwable) {
                                   ^
        (see http://t.uber.com/nullaway )
    
    

    I'd like to be able to define my desired @Nullable annotation as an option and have nullaway suggest adding the annotation.

    enhancement longterm 
    opened by ZacSweers 1
Owner
Uber Open Source
Open Source Software at Uber
Uber Open Source
Your Software. Your Structures. Your Rules.

jQAssistant Master Repository We splitted jQAssistant in multiple single repositories to be able to build a better and more flexible build an release

null 159 May 30, 2021
Astra: a Java tool for analysing and refactoring Java source code

What is Astra? Astra is a Java tool for analysing and refactoring Java source code. For example: "References to type A should instead reference type B

Alfa 32 Jun 8, 2021
⚡️Lightning-fast linter for .env files. Written in Rust 🦀

⚡️ Lightning-fast linter for .env files. Written in Rust ?? Dotenv-linter can check / fix / compare .env files for problems that may cause the applica

null 933 Jun 9, 2021
Sourcetrail - free and open-source interactive source explorer

Sourcetrail Sourcetrail is a free and open-source cross-platform source explorer that helps you get productive on unfamiliar source code. Windows: Lin

Coati Software 11.5k Jun 18, 2021
:coffee: SonarSource Static Analyzer for Java Code Quality and Security

Code Quality and Security for Java This SonarSource project is a code analyzer for Java projects. Information about the analysis of Java features is a

SonarSource 787 Jun 16, 2021
Catch common Java mistakes as compile-time errors

Error Prone Error Prone is a static analysis tool for Java that catches common programming mistakes at compile-time. public class ShortSet { public

Google 5.6k Jun 14, 2021
SpotBugs is FindBugs' successor. A tool for static analysis to look for bugs in Java code.

SpotBugs is the spiritual successor of FindBugs, carrying on from the point where it left off with support of its community. SpotBugs is licensed unde

null 2.3k Jun 8, 2021
Continuous Inspection

SonarQube Continuous Inspection SonarQube provides the capability to not only show health of an application but also to highlight issues newly introdu

SonarSource 5.9k Jun 16, 2021
Tackle Data-intensive Validity Analyzer

Tackle-DiVA (Data-intensive Validity Analyzer) Tackle-DiVA is a command-line tool for data-centric application analysis. It imports a set of target ap

Konveyor 4 May 18, 2021
A static analyzer for Java, C, C++, and Objective-C

Infer Infer is a static analysis tool for Java, C++, Objective-C, and C. Infer is written in OCaml. Installation Read our Getting Started page for det

Facebook 12.4k Jun 13, 2021
Inria 1.1k Jun 16, 2021
OpenGrok is a fast and usable source code search and cross reference engine, written in Java

Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved. OpenGrok - a wicked fast source browser OpenGrok - a wicked fast source br

Oracle 3.3k Jun 14, 2021
An extensible multilanguage static code analyzer.

PMD About PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and

PMD 3.4k Jun 14, 2021