Skip to content

Commit

Permalink
Merge branch 'master' into jdk21_cloneMetaData_updates
Browse files Browse the repository at this point in the history
  • Loading branch information
msridhar authored Dec 28, 2023
2 parents 8c22d6c + 6da0b8d commit 579b60c
Show file tree
Hide file tree
Showing 27 changed files with 359 additions and 75 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
Changelog
=========
Version 0.10.19
---------------
* Update to Checker Framework 3.41.0 (#873)
* Extend library models to mark fields as nullable (#878)
- Main use case is NullAwayAnnotator
* Fix jarinfer cli output determinism (#884)
* Add support for AssertJ as() and describedAs() in AssertionHandler (#885)
* Support for JSpecify's 0.3.0 annotation [experimental]
- JSpecify: In generics code, get rid of checks for ClassType (#863)
* Update some dependencies (#883)

Version 0.10.18
---------------
* Fix assertion check for structure of enhanced-for loop over a Map keySet (#868)
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ buildscript {
}
}
plugins {
id "com.diffplug.spotless" version "6.21.0"
id "com.diffplug.spotless" version "6.23.3"
id "net.ltgt.errorprone" version "3.1.0" apply false
id "com.github.johnrengelman.shadow" version "8.1.1" apply false
id "me.champeau.jmh" version "0.7.1" apply false
id "com.github.ben-manes.versions" version "0.48.0"
id "com.github.ben-manes.versions" version "0.50.0"
id "com.felipefzdz.gradle.shellcheck" version "1.4.6"
}

Expand Down Expand Up @@ -118,7 +118,7 @@ spotless {
}
}
spotlessPredeclare {
java { googleJavaFormat('1.17.0') }
java { googleJavaFormat('1.18.1') }
groovyGradle {
greclipse()
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ org.gradle.caching=true
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m

GROUP=com.uber.nullaway
VERSION_NAME=0.10.19-SNAPSHOT
VERSION_NAME=0.10.20-SNAPSHOT

POM_DESCRIPTION=A fast annotation-based null checker for Java

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@ public static void annotateBytecodeInClass(
annotateBytecode(is, os, nonnullParams, nullableReturns, javaxNullableDesc, javaxNonnullDesc);
}

/**
* Create a zip entry with creation time of 0 to ensure that jars always have the same checksum.
*
* @param name of the zip entry.
* @return the zip entry.
*/
private static ZipEntry createZipEntry(String name) {
ZipEntry entry = new ZipEntry(name);
entry.setTime(0);
return entry;
}

private static void copyAndAnnotateJarEntry(
JarEntry jarEntry,
InputStream is,
Expand All @@ -224,7 +236,7 @@ private static void copyAndAnnotateJarEntry(
throws IOException {
String entryName = jarEntry.getName();
if (entryName.endsWith(".class")) {
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
annotateBytecode(is, jarOS, nonnullParams, nullableReturns, nullableDesc, nonnullDesc);
} else if (entryName.equals("META-INF/MANIFEST.MF")) {
// Read full file
Expand All @@ -241,7 +253,7 @@ private static void copyAndAnnotateJarEntry(
if (!manifestText.equals(manifestMinusDigests) && !stripJarSignatures) {
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
}
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(manifestMinusDigests.getBytes(UTF_8));
} else if (entryName.startsWith("META-INF/")
&& (entryName.endsWith(".DSA")
Expand All @@ -251,7 +263,7 @@ private static void copyAndAnnotateJarEntry(
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
} // the case where stripJarSignatures==true is handled by default by skipping these files
} else {
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(IOUtils.toByteArray(is));
}
jarOS.closeEntry();
Expand Down Expand Up @@ -329,7 +341,7 @@ public static void annotateBytecodeInAar(
while (zipIterator.hasNext()) {
ZipEntry zipEntry = zipIterator.next();
InputStream is = inputZip.getInputStream(zipEntry);
zipOS.putNextEntry(new ZipEntry(zipEntry.getName()));
zipOS.putNextEntry(createZipEntry(zipEntry.getName()));
if (zipEntry.getName().equals("classes.jar")) {
JarInputStream jarIS = new JarInputStream(is);
JarEntry inputJarEntry = jarIS.getNextJarEntry();
Expand Down
21 changes: 21 additions & 0 deletions nullaway/src/main/java/com/uber/nullaway/LibraryModels.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

package com.uber.nullaway;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
Expand Down Expand Up @@ -131,6 +132,13 @@ public interface LibraryModels {
*/
ImmutableSetMultimap<MethodRef, Integer> castToNonNullMethods();

/**
* Get the set of library fields that may be {@code null}.
*
* @return set of library fields that may be {@code null}.
*/
ImmutableSet<FieldRef> nullableFields();

/**
* Get a list of custom stream library specifications.
*
Expand Down Expand Up @@ -244,4 +252,17 @@ public String toString() {
+ '}';
}
}

/** Representation of a field as a qualified class name + a field name */
@AutoValue
abstract class FieldRef {

public abstract String getEnclosingClassName();

public abstract String getFieldName();

public static FieldRef fieldRef(String enclosingClass, String fieldName) {
return new AutoValue_LibraryModels_FieldRef(enclosingClass, fieldName);
}
}
}
10 changes: 7 additions & 3 deletions nullaway/src/main/java/com/uber/nullaway/NullAway.java
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ public Description matchAssignment(AssignmentTree tree, VisitorState state) {
return Description.NO_MATCH;
}

if (Nullness.hasNullableAnnotation(assigned, config)) {
if (Nullness.hasNullableAnnotation(assigned, config)
|| handler.onOverrideFieldNullability(assigned)) {
// field already annotated
return Description.NO_MATCH;
}
Expand Down Expand Up @@ -762,7 +763,10 @@ private Description checkParamOverriding(
// Check handlers for any further/overriding nullness information
overriddenMethodArgNullnessMap =
handler.onOverrideMethodInvocationParametersNullability(
state, overriddenMethod, isOverriddenMethodAnnotated, overriddenMethodArgNullnessMap);
state.context,
overriddenMethod,
isOverriddenMethodAnnotated,
overriddenMethodArgNullnessMap);

// If we have an unbound method reference, the first parameter of the overridden method must be
// @NonNull, as this parameter will be used as a method receiver inside the generated lambda.
Expand Down Expand Up @@ -1717,7 +1721,7 @@ private Description handleInvocation(
// Allow handlers to override the list of non-null argument positions
argumentPositionNullness =
handler.onOverrideMethodInvocationParametersNullability(
state, methodSymbol, isMethodAnnotated, argumentPositionNullness);
state.context, methodSymbol, isMethodAnnotated, argumentPositionNullness);

// now actually check the arguments
// NOTE: the case of an invocation on a possibly-null reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ private static Node unwrapAssignExpr(Node node) {
public NullnessStore initialStore(
UnderlyingAST underlyingAST, List<LocalVariableNode> parameters) {
return nullnessStoreInitializer.getInitialStore(
underlyingAST, parameters, handler, state, config);
underlyingAST, parameters, handler, state.context, state.getTypes(), config);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static com.uber.nullaway.Nullness.NONNULL;
import static com.uber.nullaway.Nullness.NULLABLE;

import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.LambdaExpressionTree;
Expand Down Expand Up @@ -31,9 +30,9 @@ public NullnessStore getInitialStore(
UnderlyingAST underlyingAST,
List<LocalVariableNode> parameters,
Handler handler,
VisitorState state,
Context context,
Types types,
Config config) {
Context context = state.context;
if (underlyingAST.getKind().equals(UnderlyingAST.Kind.ARBITRARY_CODE)) {
// not a method or a lambda; an initializer expression or block
UnderlyingAST.CFGStatement ast = (UnderlyingAST.CFGStatement) underlyingAST;
Expand All @@ -45,7 +44,8 @@ public NullnessStore getInitialStore(
(UnderlyingAST.CFGLambda) underlyingAST,
parameters,
handler,
state,
context,
types,
config,
getCodeAnnotationInfo(context));
} else {
Expand Down Expand Up @@ -77,20 +77,20 @@ private static NullnessStore lambdaInitialStore(
UnderlyingAST.CFGLambda underlyingAST,
List<LocalVariableNode> parameters,
Handler handler,
VisitorState state,
Context context,
Types types,
Config config,
CodeAnnotationInfo codeAnnotationInfo) {
// include nullness info for locals from enclosing environment
EnclosingEnvironmentNullness environmentNullness =
EnclosingEnvironmentNullness.instance(state.context);
EnclosingEnvironmentNullness.instance(context);
NullnessStore environmentMapping =
Objects.requireNonNull(
environmentNullness.getEnvironmentMapping(underlyingAST.getLambdaTree()),
"no environment stored for lambda");
NullnessStore.Builder result = environmentMapping.toBuilder();
LambdaExpressionTree code = underlyingAST.getLambdaTree();
// need to check annotation for i'th parameter of functional interface declaration
Types types = state.getTypes();
Symbol.MethodSymbol fiMethodSymbol = NullabilityUtil.getFunctionalInterfaceMethod(code, types);
com.sun.tools.javac.util.List<Symbol.VarSymbol> fiMethodParameters =
fiMethodSymbol.getParameters();
Expand Down Expand Up @@ -119,7 +119,7 @@ private static NullnessStore lambdaInitialStore(
}
fiArgumentPositionNullness =
handler.onOverrideMethodInvocationParametersNullability(
state, fiMethodSymbol, isFIAnnotated, fiArgumentPositionNullness);
context, fiMethodSymbol, isFIAnnotated, fiArgumentPositionNullness);

for (int i = 0; i < parameters.size(); i++) {
LocalVariableNode param = parameters.get(i);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.uber.nullaway.dataflow;

import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.util.Trees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.uber.nullaway.Config;
Expand All @@ -30,15 +30,17 @@ public abstract class NullnessStoreInitializer {
* @param underlyingAST The AST node being matched.
* @param parameters list of local variable nodes.
* @param handler reference to the handler invoked.
* @param state the visitor state.
* @param context context.
* @param types types.
* @param config config for analysis.
* @return Initial Nullness store.
*/
public abstract NullnessStore getInitialStore(
UnderlyingAST underlyingAST,
List<LocalVariableNode> parameters,
Handler handler,
VisitorState state,
Context context,
Types types,
Config config);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.uber.nullaway.dataflow.AccessPath;
import com.uber.nullaway.dataflow.AccessPathNullnessPropagation;
import java.util.List;
import javax.annotation.Nullable;
import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.nullaway.dataflow.cfg.node.Node;

Expand Down Expand Up @@ -60,17 +61,9 @@ public NullnessHint onDataflowVisitMethodInvocation(
// assertThat(A).isInstanceOf(Foo.class)
// A will not be NULL after this statement.
if (methodNameUtil.isMethodIsNotNull(callee) || methodNameUtil.isMethodIsInstanceOf(callee)) {
Node receiver = node.getTarget().getReceiver();
if (receiver instanceof MethodInvocationNode) {
MethodInvocationNode receiver_method = (MethodInvocationNode) receiver;
Symbol.MethodSymbol receiver_symbol = ASTHelpers.getSymbol(receiver_method.getTree());
if (methodNameUtil.isMethodAssertThat(receiver_symbol)) {
Node arg = receiver_method.getArgument(0);
AccessPath ap = AccessPath.getAccessPathForNode(arg, state, apContext);
if (ap != null) {
bothUpdates.set(ap, NONNULL);
}
}
AccessPath ap = getAccessPathForNotNullAssertThatExpr(node, state, apContext);
if (ap != null) {
bothUpdates.set(ap, NONNULL);
}
}

Expand All @@ -94,4 +87,31 @@ public NullnessHint onDataflowVisitMethodInvocation(

return NullnessHint.UNKNOWN;
}

/**
* Returns the AccessPath for the argument of an assertThat() call, if present as a valid nested
* receiver expression of a method invocation
*
* @param node the method invocation node
* @param state the visitor state
* @param apContext the access path context
* @return the AccessPath for the argument of the assertThat() call, if present, otherwise {@code
* null}
*/
private @Nullable AccessPath getAccessPathForNotNullAssertThatExpr(
MethodInvocationNode node, VisitorState state, AccessPath.AccessPathContext apContext) {
Node receiver = node.getTarget().getReceiver();
if (receiver instanceof MethodInvocationNode) {
MethodInvocationNode receiver_method = (MethodInvocationNode) receiver;
Symbol.MethodSymbol receiver_symbol = ASTHelpers.getSymbol(receiver_method.getTree());
if (methodNameUtil.isMethodAssertThat(receiver_symbol)) {
Node arg = receiver_method.getArgument(0);
return AccessPath.getAccessPathForNode(arg, state, apContext);
} else if (methodNameUtil.isMethodAssertJDescribedAs(receiver_symbol)) {
// For calls to as() or describedAs(), we recursively search for the assertThat() call
return getAccessPathForNotNullAssertThatExpr(receiver_method, state, apContext);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,15 @@ public Nullness onOverrideMethodReturnNullability(
return returnNullness;
}

@Override
public boolean onOverrideFieldNullability(Symbol field) {
// NoOp
return false;
}

@Override
public Nullness[] onOverrideMethodInvocationParametersNullability(
VisitorState state,
Context context,
Symbol.MethodSymbol methodSymbol,
boolean isAnnotated,
Nullness[] argumentPositionNullness) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,28 @@ public Nullness onOverrideMethodReturnNullability(
return returnNullness;
}

@Override
public boolean onOverrideFieldNullability(Symbol field) {
for (Handler h : handlers) {
if (h.onOverrideFieldNullability(field)) {
// If any handler determines that the field is @Nullable, we should acknowledge that and
// treat it as such.
return true;
}
}
return false;
}

@Override
public Nullness[] onOverrideMethodInvocationParametersNullability(
VisitorState state,
Context context,
Symbol.MethodSymbol methodSymbol,
boolean isAnnotated,
Nullness[] argumentPositionNullness) {
for (Handler h : handlers) {
argumentPositionNullness =
h.onOverrideMethodInvocationParametersNullability(
state, methodSymbol, isAnnotated, argumentPositionNullness);
context, methodSymbol, isAnnotated, argumentPositionNullness);
}
return argumentPositionNullness;
}
Expand Down
Loading

0 comments on commit 579b60c

Please sign in to comment.