From c366edeffe6d51f2195742cdf795bb5460ba238b Mon Sep 17 00:00:00 2001 From: Shubham Ugare Date: Thu, 14 Mar 2019 13:29:50 +0530 Subject: [PATCH 1/5] Custom path to Optional class for Optional emptiness handler --- .../com/uber/nullaway/AbstractConfig.java | 7 +++ .../main/java/com/uber/nullaway/Config.java | 6 ++ .../com/uber/nullaway/DummyOptionsConfig.java | 5 ++ .../nullaway/ErrorProneCLIFlagsConfig.java | 2 + .../main/java/com/uber/nullaway/NullAway.java | 2 +- .../handlers/ApacheThriftIsSetHandler.java | 7 ++- .../nullaway/handlers/BaseNoOpHandler.java | 7 ++- .../nullaway/handlers/CompositeHandler.java | 9 ++- .../nullaway/handlers/ContractHandler.java | 7 ++- .../com/uber/nullaway/handlers/Handler.java | 8 ++- .../handlers/OptionalEmptinessHandler.java | 11 ++-- .../handlers/RxNullabilityPropagator.java | 9 ++- .../java/com/uber/nullaway/NullAwayTest.java | 59 +++++++++++++++++++ 13 files changed, 126 insertions(+), 13 deletions(-) diff --git a/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java b/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java index 3455252dd0..0fb4e38d66 100644 --- a/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java @@ -70,6 +70,8 @@ public abstract class AbstractConfig implements Config { protected boolean checkOptionalEmptiness; + protected String optionalClassPath; + protected boolean assertsEnabled; /** @@ -189,6 +191,11 @@ public boolean checkOptionalEmptiness() { return checkOptionalEmptiness; } + @Override + public String getOptionalClassPath() { + return optionalClassPath; + } + @Override public boolean assertsEnabled() { return assertsEnabled; diff --git a/nullaway/src/main/java/com/uber/nullaway/Config.java b/nullaway/src/main/java/com/uber/nullaway/Config.java index be7ae66621..c7fd7e8915 100644 --- a/nullaway/src/main/java/com/uber/nullaway/Config.java +++ b/nullaway/src/main/java/com/uber/nullaway/Config.java @@ -115,6 +115,12 @@ public interface Config { */ boolean checkOptionalEmptiness(); + /** + * @return the path for Optional class. In default case the path of {@link java.util.Optional} is + * used. + */ + String getOptionalClassPath(); + /** * @return the fully qualified name of a method which will take a @Nullable version of a value and * return an @NonNull copy (likely through an unsafe downcast, but performing runtime checking diff --git a/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java b/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java index ceeb09117e..312b0c086d 100644 --- a/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java @@ -117,6 +117,11 @@ public boolean checkOptionalEmptiness() { throw new IllegalStateException(error_msg); } + @Override + public String getOptionalClassPath() { + throw new IllegalStateException(error_msg); + } + @Override @Nullable public String getCastToNonNullMethod() { diff --git a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java index e782f9b129..a752801639 100644 --- a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java @@ -56,6 +56,7 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { static final String FL_ACKNOWLEDGE_RESTRICTIVE = EP_FL_NAMESPACE + ":AcknowledgeRestrictiveAnnotations"; static final String FL_CHECK_OPTIONAL_EMPTINESS = EP_FL_NAMESPACE + ":CheckOptionalEmptiness"; + static final String FL_OPTIONAL_CLASS_PACKAGE = EP_FL_NAMESPACE + ":OptionalClassPath"; static final String FL_SUPPRESS_COMMENT = EP_FL_NAMESPACE + ":AutoFixSuppressionComment"; /** --- JarInfer configs --- */ static final String FL_JI_ENABLED = EP_FL_NAMESPACE + ":JarInferEnabled"; @@ -141,6 +142,7 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { getFlagStringSet(flags, FL_EXCLUDED_FIELD_ANNOT, DEFAULT_EXCLUDED_FIELD_ANNOT)); castToNonNullMethod = flags.get(FL_CTNN_METHOD).orElse(null); autofixSuppressionComment = flags.get(FL_SUPPRESS_COMMENT).orElse(""); + optionalClassPath = flags.get(FL_OPTIONAL_CLASS_PACKAGE).orElse("java.util.Optional"); if (autofixSuppressionComment.contains("\n")) { throw new IllegalStateException( "Invalid -XepOpt" + FL_SUPPRESS_COMMENT + " value. Comment must be single line."); diff --git a/nullaway/src/main/java/com/uber/nullaway/NullAway.java b/nullaway/src/main/java/com/uber/nullaway/NullAway.java index 6019081dd2..2c0f7b671a 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullAway.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullAway.java @@ -1109,7 +1109,7 @@ public Description matchClass(ClassTree tree, VisitorState state) { matchWithinClass = !isExcludedClass(classSymbol, state); // since we are processing a new top-level class, invalidate any cached // results for previous classes - handler.onMatchTopLevelClass(this, tree, state, classSymbol); + handler.onMatchTopLevelClass(this, tree, state, classSymbol, config); getNullnessAnalysis(state).invalidateCaches(); initTree2PrevFieldInit.clear(); class2Entities.clear(); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java index ea44f219d4..3927866b26 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java @@ -29,6 +29,7 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; import com.uber.nullaway.dataflow.AccessPath; @@ -54,7 +55,11 @@ public class ApacheThriftIsSetHandler extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { if (tbaseType == null) { tbaseType = Optional.ofNullable(state.getTypeFromString(TBASE_NAME)).map(state.getTypes()::erasure); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java index 7844469198..c733e2bac0 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java @@ -34,6 +34,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.dataflow.AccessPath; @@ -60,7 +61,11 @@ protected BaseNoOpHandler() { @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { // NoOp } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java index 8112b4a4aa..21089ba0f7 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java @@ -35,6 +35,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.dataflow.AccessPath; @@ -62,9 +63,13 @@ class CompositeHandler implements Handler { @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { for (Handler h : handlers) { - h.onMatchTopLevelClass(analysis, tree, state, classSymbol); + h.onMatchTopLevelClass(analysis, tree, state, classSymbol, config); } } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java index 4a1c4aefcb..ada3b79b39 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java @@ -30,6 +30,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; @@ -83,7 +84,11 @@ public class ContractHandler extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { this.analysis = analysis; this.state = state; } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java index e86e6307d2..3cebb1da3b 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java @@ -34,6 +34,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; @@ -63,9 +64,14 @@ public interface Handler { * @param tree The AST node for the class being matched. * @param state The current visitor state. * @param classSymbol The class symbol for the class being matched. + * @param config The nullaway config. */ void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol); + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config); /** * Called when NullAway first matches a particular method node. diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java index 454c940e4a..6a2015796f 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java @@ -31,6 +31,7 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; +import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; @@ -49,8 +50,6 @@ */ public class OptionalEmptinessHandler extends BaseNoOpHandler { - private static String OPTIONAL_PATH = "java.util.Optional"; - @Nullable private Optional optionalType; @Override @@ -65,10 +64,14 @@ && optionalIsGetCall((Symbol.MethodSymbol) ASTHelpers.getSymbol(expr), state.get @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { if (optionalType == null) { optionalType = - Optional.ofNullable(state.getTypeFromString(OPTIONAL_PATH)) + Optional.ofNullable(state.getTypeFromString(config.getOptionalClassPath())) .map(state.getTypes()::erasure); } } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java b/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java index 8f4e0843fe..5bf69c9034 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java @@ -45,6 +45,7 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; +import com.uber.nullaway.Config; import com.uber.nullaway.NullAway; import com.uber.nullaway.NullabilityUtil; import com.uber.nullaway.Nullness; @@ -203,7 +204,7 @@ class RxNullabilityPropagator extends BaseNoOpHandler { private final Map returnToEnclosingMethodOrLambda = new LinkedHashMap(); - // Similar to above, but mapping espression-bodies to their enclosing lambdas + // Similar to above, but mapping expression-bodies to their enclosing lambdas private final Map expressionBodyToFilterLambda = new LinkedHashMap(); @@ -213,7 +214,11 @@ class RxNullabilityPropagator extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { + NullAway analysis, + ClassTree tree, + VisitorState state, + Symbol.ClassSymbol classSymbol, + Config config) { // Clear compilation unit specific state this.filterMethodOrLambdaSet.clear(); this.observableOuterCallInChain.clear(); diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java index dc41f53c78..d01f96de9e 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java @@ -1233,6 +1233,65 @@ public void OptionalEmptinessHandlerTest() { .doTest(); } + @Test + public void OptionalEmptinessHandlerWithCustomPathTest() { + compilationHelper + .setArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + "-XepOpt:NullAway:AnnotatedPackages=com.uber", + "-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", + "-XepOpt:NullAway:CheckOptionalEmptiness=true", + "-XepOpt:NullAway:OptionalClassPath=com.google.common.base.Optional")) + .addSourceLines( + "TestNegative.java", + "package com.uber;", + "import com.google.common.base.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestNegative {", + " void foo() {", + " Optional a = Optional.absent();", + " // no error since a.isPresent() is called", + " if(a.isPresent()){", + " a.get().toString();", + " }", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.absent();", + " if(b.isPresent()){", + " lambdaConsumer(v -> b.get().toString());", + " }", + " }", + "}") + .addSourceLines( + "TestPositive.java", + "package com.uber;", + "import com.google.common.base.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestPositive {", + " void foo() {", + " Optional a = Optional.absent();", + " // BUG: Diagnostic contains: Optional a can be empty", + " a.get().toString();", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.absent();", + " // BUG: Diagnostic contains: Optional b can be empty", + " lambdaConsumer(v -> b.get().toString());", + " }", + "}") + .doTest(); + } + @Test public void OptionalEmptinessUncheckedTest() { compilationHelper From ba417ed7568523d338d6affb26bf68c9259f77d4 Mon Sep 17 00:00:00 2001 From: Shubham Ugare Date: Mon, 18 Mar 2019 20:52:55 +0530 Subject: [PATCH 2/5] Addressed comments --- .../com/uber/nullaway/AbstractConfig.java | 6 +-- .../main/java/com/uber/nullaway/Config.java | 7 +-- .../com/uber/nullaway/DummyOptionsConfig.java | 3 +- .../nullaway/ErrorProneCLIFlagsConfig.java | 8 +++- .../handlers/OptionalEmptinessHandler.java | 45 +++++++++++-------- .../java/com/uber/nullaway/NullAwayTest.java | 27 +++++++++-- 6 files changed, 65 insertions(+), 31 deletions(-) diff --git a/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java b/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java index 0fb4e38d66..899973dca4 100644 --- a/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/AbstractConfig.java @@ -70,7 +70,7 @@ public abstract class AbstractConfig implements Config { protected boolean checkOptionalEmptiness; - protected String optionalClassPath; + protected Set optionalClassPaths; protected boolean assertsEnabled; @@ -192,8 +192,8 @@ public boolean checkOptionalEmptiness() { } @Override - public String getOptionalClassPath() { - return optionalClassPath; + public Set getOptionalClassPaths() { + return optionalClassPaths; } @Override diff --git a/nullaway/src/main/java/com/uber/nullaway/Config.java b/nullaway/src/main/java/com/uber/nullaway/Config.java index c7fd7e8915..93b4d11727 100644 --- a/nullaway/src/main/java/com/uber/nullaway/Config.java +++ b/nullaway/src/main/java/com/uber/nullaway/Config.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.sun.tools.javac.code.Symbol; +import java.util.Set; import javax.annotation.Nullable; /** Provides configuration parameters for the nullability checker. */ @@ -116,10 +117,10 @@ public interface Config { boolean checkOptionalEmptiness(); /** - * @return the path for Optional class. In default case the path of {@link java.util.Optional} is - * used. + * @return the paths for Optional class. The list always contains the path of {@link + * java.util.Optional}. */ - String getOptionalClassPath(); + Set getOptionalClassPaths(); /** * @return the fully qualified name of a method which will take a @Nullable version of a value and diff --git a/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java b/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java index 312b0c086d..0e1d81f44a 100644 --- a/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/DummyOptionsConfig.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableSet; import com.sun.tools.javac.code.Symbol; +import java.util.Set; import javax.annotation.Nullable; /** @@ -118,7 +119,7 @@ public boolean checkOptionalEmptiness() { } @Override - public String getOptionalClassPath() { + public Set getOptionalClassPaths() { throw new IllegalStateException(error_msg); } diff --git a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java index a752801639..7daeb78a8b 100644 --- a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java @@ -56,7 +56,7 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { static final String FL_ACKNOWLEDGE_RESTRICTIVE = EP_FL_NAMESPACE + ":AcknowledgeRestrictiveAnnotations"; static final String FL_CHECK_OPTIONAL_EMPTINESS = EP_FL_NAMESPACE + ":CheckOptionalEmptiness"; - static final String FL_OPTIONAL_CLASS_PACKAGE = EP_FL_NAMESPACE + ":OptionalClassPath"; + static final String FL_OPTIONAL_CLASS_PATHS = EP_FL_NAMESPACE + ":OptionalClassPaths"; static final String FL_SUPPRESS_COMMENT = EP_FL_NAMESPACE + ":AutoFixSuppressionComment"; /** --- JarInfer configs --- */ static final String FL_JI_ENABLED = EP_FL_NAMESPACE + ":JarInferEnabled"; @@ -142,7 +142,11 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { getFlagStringSet(flags, FL_EXCLUDED_FIELD_ANNOT, DEFAULT_EXCLUDED_FIELD_ANNOT)); castToNonNullMethod = flags.get(FL_CTNN_METHOD).orElse(null); autofixSuppressionComment = flags.get(FL_SUPPRESS_COMMENT).orElse(""); - optionalClassPath = flags.get(FL_OPTIONAL_CLASS_PACKAGE).orElse("java.util.Optional"); + optionalClassPaths = + new ImmutableSet.Builder() + .addAll(getFlagStringSet(flags, FL_OPTIONAL_CLASS_PATHS)) + .add("java.util.Optional") + .build(); if (autofixSuppressionComment.contains("\n")) { throw new IllegalStateException( "Invalid -XepOpt" + FL_SUPPRESS_COMMENT + " value. Comment must be single line."); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java index 6a2015796f..c624743fce 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java @@ -21,7 +21,6 @@ */ package com.uber.nullaway.handlers; -import com.google.common.base.Preconditions; import com.google.errorprone.VisitorState; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ClassTree; @@ -38,6 +37,8 @@ import com.uber.nullaway.dataflow.AccessPath; import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -50,7 +51,7 @@ */ public class OptionalEmptinessHandler extends BaseNoOpHandler { - @Nullable private Optional optionalType; + @Nullable private Set> optionalTypes; @Override public boolean onOverrideMayBeNullExpr( @@ -69,11 +70,15 @@ public void onMatchTopLevelClass( VisitorState state, Symbol.ClassSymbol classSymbol, Config config) { - if (optionalType == null) { - optionalType = - Optional.ofNullable(state.getTypeFromString(config.getOptionalClassPath())) - .map(state.getTypes()::erasure); - } + optionalTypes = + config + .getOptionalClassPaths() + .stream() + .map( + type -> + Optional.ofNullable(state.getTypeFromString(type)) + .map(state.getTypes()::erasure)) + .collect(Collectors.toSet()); } @Override @@ -144,20 +149,22 @@ private void updateNonNullAPsForElement( } private boolean optionalIsPresentCall(Symbol.MethodSymbol symbol, Types types) { - Preconditions.checkNotNull(optionalType); - // noinspection ConstantConditions - return optionalType.isPresent() - && symbol.getSimpleName().toString().equals("isPresent") - && symbol.getParameters().length() == 0 - && types.isSubtype(symbol.owner.type, optionalType.get()); + for (Optional optionalType : optionalTypes) { + if (optionalType.isPresent() + && symbol.getSimpleName().toString().equals("isPresent") + && symbol.getParameters().length() == 0 + && types.isSubtype(symbol.owner.type, optionalType.get())) return true; + } + return false; } private boolean optionalIsGetCall(Symbol.MethodSymbol symbol, Types types) { - Preconditions.checkNotNull(optionalType); - // noinspection ConstantConditions - return optionalType.isPresent() - && symbol.getSimpleName().toString().equals("get") - && symbol.getParameters().length() == 0 - && types.isSubtype(symbol.owner.type, optionalType.get()); + for (Optional optionalType : optionalTypes) { + if (optionalType.isPresent() + && symbol.getSimpleName().toString().equals("get") + && symbol.getParameters().length() == 0 + && types.isSubtype(symbol.owner.type, optionalType.get())) return true; + } + return false; } } diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java index d01f96de9e..a6ac31b0ec 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java @@ -1234,7 +1234,7 @@ public void OptionalEmptinessHandlerTest() { } @Test - public void OptionalEmptinessHandlerWithCustomPathTest() { + public void OptionalEmptinessHandlerWithSingleCustomPathTest() { compilationHelper .setArgs( Arrays.asList( @@ -1243,7 +1243,7 @@ public void OptionalEmptinessHandlerWithCustomPathTest() { "-XepOpt:NullAway:AnnotatedPackages=com.uber", "-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", "-XepOpt:NullAway:CheckOptionalEmptiness=true", - "-XepOpt:NullAway:OptionalClassPath=com.google.common.base.Optional")) + "-XepOpt:NullAway:OptionalClassPaths=com.google.common.base.Optional,does.not.matter.Optional")) .addSourceLines( "TestNegative.java", "package com.uber;", @@ -1271,11 +1271,32 @@ public void OptionalEmptinessHandlerWithCustomPathTest() { .addSourceLines( "TestPositive.java", "package com.uber;", - "import com.google.common.base.Optional;", + "import java.util.Optional;", "import javax.annotation.Nullable;", "import com.google.common.base.Function;", "public class TestPositive {", " void foo() {", + " Optional a = Optional.empty();", + " // BUG: Diagnostic contains: Optional a can be empty", + " a.get().toString();", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.empty();", + " // BUG: Diagnostic contains: Optional b can be empty", + " lambdaConsumer(v -> b.get().toString());", + " }", + "}") + .addSourceLines( + "TestPositive2.java", + "package com.uber;", + "import com.google.common.base.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestPositive2 {", + " void foo() {", " Optional a = Optional.absent();", " // BUG: Diagnostic contains: Optional a can be empty", " a.get().toString();", From 89a416bd5cd23ab2a1694c2e4da5ae6a5525689f Mon Sep 17 00:00:00 2001 From: Shubham Ugare Date: Fri, 22 Mar 2019 02:00:30 +0530 Subject: [PATCH 3/5] Addressed comments --- .../nullaway/ErrorProneCLIFlagsConfig.java | 3 +- .../main/java/com/uber/nullaway/NullAway.java | 2 +- .../handlers/ApacheThriftIsSetHandler.java | 7 +- .../nullaway/handlers/BaseNoOpHandler.java | 7 +- .../nullaway/handlers/CompositeHandler.java | 9 +- .../nullaway/handlers/ContractHandler.java | 7 +- .../com/uber/nullaway/handlers/Handler.java | 8 +- .../com/uber/nullaway/handlers/Handlers.java | 2 +- .../handlers/OptionalEmptinessHandler.java | 11 +-- .../handlers/RxNullabilityPropagator.java | 7 +- .../java/com/uber/nullaway/NullAwayTest.java | 82 ++++++++++++++++++- 11 files changed, 98 insertions(+), 47 deletions(-) diff --git a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java index 7daeb78a8b..57acc56746 100644 --- a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java @@ -56,7 +56,8 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { static final String FL_ACKNOWLEDGE_RESTRICTIVE = EP_FL_NAMESPACE + ":AcknowledgeRestrictiveAnnotations"; static final String FL_CHECK_OPTIONAL_EMPTINESS = EP_FL_NAMESPACE + ":CheckOptionalEmptiness"; - static final String FL_OPTIONAL_CLASS_PATHS = EP_FL_NAMESPACE + ":OptionalClassPaths"; + static final String FL_OPTIONAL_CLASS_PATHS = + EP_FL_NAMESPACE + ":CheckOptionalEmptinessCustomClasses"; static final String FL_SUPPRESS_COMMENT = EP_FL_NAMESPACE + ":AutoFixSuppressionComment"; /** --- JarInfer configs --- */ static final String FL_JI_ENABLED = EP_FL_NAMESPACE + ":JarInferEnabled"; diff --git a/nullaway/src/main/java/com/uber/nullaway/NullAway.java b/nullaway/src/main/java/com/uber/nullaway/NullAway.java index 2c0f7b671a..6019081dd2 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullAway.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullAway.java @@ -1109,7 +1109,7 @@ public Description matchClass(ClassTree tree, VisitorState state) { matchWithinClass = !isExcludedClass(classSymbol, state); // since we are processing a new top-level class, invalidate any cached // results for previous classes - handler.onMatchTopLevelClass(this, tree, state, classSymbol, config); + handler.onMatchTopLevelClass(this, tree, state, classSymbol); getNullnessAnalysis(state).invalidateCaches(); initTree2PrevFieldInit.clear(); class2Entities.clear(); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java index 3927866b26..ea44f219d4 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/ApacheThriftIsSetHandler.java @@ -29,7 +29,6 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; -import com.uber.nullaway.Config; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; import com.uber.nullaway.dataflow.AccessPath; @@ -55,11 +54,7 @@ public class ApacheThriftIsSetHandler extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { if (tbaseType == null) { tbaseType = Optional.ofNullable(state.getTypeFromString(TBASE_NAME)).map(state.getTypes()::erasure); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java index c733e2bac0..7844469198 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java @@ -34,7 +34,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; -import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.dataflow.AccessPath; @@ -61,11 +60,7 @@ protected BaseNoOpHandler() { @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { // NoOp } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java index 21089ba0f7..8112b4a4aa 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java @@ -35,7 +35,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; -import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.dataflow.AccessPath; @@ -63,13 +62,9 @@ class CompositeHandler implements Handler { @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { for (Handler h : handlers) { - h.onMatchTopLevelClass(analysis, tree, state, classSymbol, config); + h.onMatchTopLevelClass(analysis, tree, state, classSymbol); } } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java index ada3b79b39..4a1c4aefcb 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/ContractHandler.java @@ -30,7 +30,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; -import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; @@ -84,11 +83,7 @@ public class ContractHandler extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { this.analysis = analysis; this.state = state; } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java index 3cebb1da3b..e86e6307d2 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java @@ -34,7 +34,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.util.Context; -import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; @@ -64,14 +63,9 @@ public interface Handler { * @param tree The AST node for the class being matched. * @param state The current visitor state. * @param classSymbol The class symbol for the class being matched. - * @param config The nullaway config. */ void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config); + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol); /** * Called when NullAway first matches a particular method node. diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/Handlers.java b/nullaway/src/main/java/com/uber/nullaway/handlers/Handlers.java index 797c0a45a3..a2dad86698 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/Handlers.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/Handlers.java @@ -51,7 +51,7 @@ public static Handler buildDefault(Config config) { handlerListBuilder.add(new ContractHandler()); handlerListBuilder.add(new ApacheThriftIsSetHandler()); if (config.checkOptionalEmptiness()) { - handlerListBuilder.add(new OptionalEmptinessHandler()); + handlerListBuilder.add(new OptionalEmptinessHandler(config)); } return new CompositeHandler(handlerListBuilder.build()); } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java index c624743fce..2085f35911 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java @@ -52,6 +52,11 @@ public class OptionalEmptinessHandler extends BaseNoOpHandler { @Nullable private Set> optionalTypes; + private final Config config; + + OptionalEmptinessHandler(Config config) { + this.config = config; + } @Override public boolean onOverrideMayBeNullExpr( @@ -65,11 +70,7 @@ && optionalIsGetCall((Symbol.MethodSymbol) ASTHelpers.getSymbol(expr), state.get @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { optionalTypes = config .getOptionalClassPaths() diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java b/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java index 5bf69c9034..23292dc7a4 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/RxNullabilityPropagator.java @@ -45,7 +45,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; -import com.uber.nullaway.Config; import com.uber.nullaway.NullAway; import com.uber.nullaway.NullabilityUtil; import com.uber.nullaway.Nullness; @@ -214,11 +213,7 @@ class RxNullabilityPropagator extends BaseNoOpHandler { @Override public void onMatchTopLevelClass( - NullAway analysis, - ClassTree tree, - VisitorState state, - Symbol.ClassSymbol classSymbol, - Config config) { + NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { // Clear compilation unit specific state this.filterMethodOrLambdaSet.clear(); this.observableOuterCallInChain.clear(); diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java index a6ac31b0ec..eace3f58b4 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayTest.java @@ -1243,7 +1243,87 @@ public void OptionalEmptinessHandlerWithSingleCustomPathTest() { "-XepOpt:NullAway:AnnotatedPackages=com.uber", "-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", "-XepOpt:NullAway:CheckOptionalEmptiness=true", - "-XepOpt:NullAway:OptionalClassPaths=com.google.common.base.Optional,does.not.matter.Optional")) + "-XepOpt:NullAway:CheckOptionalEmptinessCustomClasses=com.google.common.base.Optional")) + .addSourceLines( + "TestNegative.java", + "package com.uber;", + "import com.google.common.base.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestNegative {", + " void foo() {", + " Optional a = Optional.absent();", + " // no error since a.isPresent() is called", + " if(a.isPresent()){", + " a.get().toString();", + " }", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.absent();", + " if(b.isPresent()){", + " lambdaConsumer(v -> b.get().toString());", + " }", + " }", + "}") + .addSourceLines( + "TestPositive.java", + "package com.uber;", + "import java.util.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestPositive {", + " void foo() {", + " Optional a = Optional.empty();", + " // BUG: Diagnostic contains: Optional a can be empty", + " a.get().toString();", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.empty();", + " // BUG: Diagnostic contains: Optional b can be empty", + " lambdaConsumer(v -> b.get().toString());", + " }", + "}") + .addSourceLines( + "TestPositive2.java", + "package com.uber;", + "import com.google.common.base.Optional;", + "import javax.annotation.Nullable;", + "import com.google.common.base.Function;", + "public class TestPositive2 {", + " void foo() {", + " Optional a = Optional.absent();", + " // BUG: Diagnostic contains: Optional a can be empty", + " a.get().toString();", + " }", + " public void lambdaConsumer(Function a){", + " return;", + " }", + " void bar() {", + " Optional b = Optional.absent();", + " // BUG: Diagnostic contains: Optional b can be empty", + " lambdaConsumer(v -> b.get().toString());", + " }", + "}") + .doTest(); + } + + @Test + public void OptionalEmptinessHandlerWithTwoCustomPathsTest() { + compilationHelper + .setArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + "-XepOpt:NullAway:AnnotatedPackages=com.uber", + "-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", + "-XepOpt:NullAway:CheckOptionalEmptiness=true", + "-XepOpt:NullAway:CheckOptionalEmptinessCustomClasses=does.not.matter.Optional,com.google.common.base.Optional")) .addSourceLines( "TestNegative.java", "package com.uber;", From ebb6870e295d62d1160cbb168b1d29dbb38c1a54 Mon Sep 17 00:00:00 2001 From: Shubham Ugare Date: Fri, 22 Mar 2019 03:26:21 +0530 Subject: [PATCH 4/5] Addressed comments --- .../handlers/OptionalEmptinessHandler.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java index 2085f35911..ed02e713e8 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java @@ -21,6 +21,7 @@ */ package com.uber.nullaway.handlers; +import com.google.common.collect.ImmutableSet; import com.google.errorprone.VisitorState; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ClassTree; @@ -36,8 +37,7 @@ import com.uber.nullaway.Nullness; import com.uber.nullaway.dataflow.AccessPath; import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; -import java.util.Optional; -import java.util.Set; +import java.util.Objects; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.lang.model.element.Element; @@ -51,7 +51,7 @@ */ public class OptionalEmptinessHandler extends BaseNoOpHandler { - @Nullable private Set> optionalTypes; + @Nullable private ImmutableSet optionalTypes; private final Config config; OptionalEmptinessHandler(Config config) { @@ -72,14 +72,14 @@ && optionalIsGetCall((Symbol.MethodSymbol) ASTHelpers.getSymbol(expr), state.get public void onMatchTopLevelClass( NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { optionalTypes = - config - .getOptionalClassPaths() - .stream() - .map( - type -> - Optional.ofNullable(state.getTypeFromString(type)) - .map(state.getTypes()::erasure)) - .collect(Collectors.toSet()); + ImmutableSet.copyOf( + config + .getOptionalClassPaths() + .stream() + .map(state::getTypeFromString) + .filter(Objects::nonNull) + .map(state.getTypes()::erasure) + .collect(Collectors.toSet())); } @Override @@ -150,21 +150,19 @@ private void updateNonNullAPsForElement( } private boolean optionalIsPresentCall(Symbol.MethodSymbol symbol, Types types) { - for (Optional optionalType : optionalTypes) { - if (optionalType.isPresent() - && symbol.getSimpleName().toString().equals("isPresent") + for (Type optionalType : optionalTypes) { + if (symbol.getSimpleName().toString().equals("isPresent") && symbol.getParameters().length() == 0 - && types.isSubtype(symbol.owner.type, optionalType.get())) return true; + && types.isSubtype(symbol.owner.type, optionalType)) return true; } return false; } private boolean optionalIsGetCall(Symbol.MethodSymbol symbol, Types types) { - for (Optional optionalType : optionalTypes) { - if (optionalType.isPresent() - && symbol.getSimpleName().toString().equals("get") + for (Type optionalType : optionalTypes) { + if (symbol.getSimpleName().toString().equals("get") && symbol.getParameters().length() == 0 - && types.isSubtype(symbol.owner.type, optionalType.get())) return true; + && types.isSubtype(symbol.owner.type, optionalType)) return true; } return false; } From eddf6ae8301944c529e73a527ce56d118a9058bb Mon Sep 17 00:00:00 2001 From: Shubham Ugare Date: Fri, 22 Mar 2019 11:46:38 +0530 Subject: [PATCH 5/5] minor nit --- .../handlers/OptionalEmptinessHandler.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java index ed02e713e8..2a3b5dcdc8 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/OptionalEmptinessHandler.java @@ -38,7 +38,6 @@ import com.uber.nullaway.dataflow.AccessPath; import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; import java.util.Objects; -import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -72,14 +71,13 @@ && optionalIsGetCall((Symbol.MethodSymbol) ASTHelpers.getSymbol(expr), state.get public void onMatchTopLevelClass( NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol) { optionalTypes = - ImmutableSet.copyOf( - config - .getOptionalClassPaths() - .stream() - .map(state::getTypeFromString) - .filter(Objects::nonNull) - .map(state.getTypes()::erasure) - .collect(Collectors.toSet())); + config + .getOptionalClassPaths() + .stream() + .map(state::getTypeFromString) + .filter(Objects::nonNull) + .map(state.getTypes()::erasure) + .collect(ImmutableSet.toImmutableSet()); } @Override