diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Report.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Report.java index 38b535a0..90d4ebf1 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Report.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Report.java @@ -34,9 +34,7 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Container class to store information regarding effectiveness of a fix, its associated fix tree @@ -183,28 +181,22 @@ public boolean testEquals(Config config, Report found) { this.tree.add(this.root); found.tree.add(found.root); Set thisTree = - this.tree.stream() - .flatMap((Function>) fix -> fix.toLocations().stream()) - .collect(Collectors.toSet()); + this.tree.stream().flatMap(fix -> fix.toLocations().stream()).collect(Collectors.toSet()); Set otherTree = - found.tree.stream() - .flatMap((Function>) fix -> fix.toLocations().stream()) - .collect(Collectors.toSet()); + found.tree.stream().flatMap(fix -> fix.toLocations().stream()).collect(Collectors.toSet()); if (!thisTree.equals(otherTree)) { return false; } Set thisTriggered = this.triggeredErrors.stream() - .map(Error::getFix) - .filter(Objects::nonNull) - .flatMap((Function>) fix -> fix.toLocations().stream()) + .flatMap(error -> error.getFixes().stream()) + .flatMap(fix -> fix.toLocations().stream()) .collect(Collectors.toSet()); Set otherTriggered = found.triggeredErrors.stream() - .map(Error::getFix) - .filter(Objects::nonNull) - .flatMap((Function>) fix -> fix.toLocations().stream()) + .flatMap(error -> error.getFixes().stream()) + .flatMap(fix -> fix.toLocations().stream()) .collect(Collectors.toSet()); return otherTriggered.equals(thisTriggered); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index bfdfbc18..0c26c1a4 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -392,11 +392,7 @@ private NullAwayError createError( annot -> !module.getNonnullStore().hasExplicitNonnullAnnotation(annot.getLocation())) .collect(ImmutableSet.toImmutableSet()); - Fix fix = - cleanedAnnotations.isEmpty() - ? null - : new Fix(cleanedAnnotations, ImmutableSet.of(errorType), true); - return new NullAwayError(errorType, errorMessage, region, offset, fix); + return new NullAwayError(errorType, errorMessage, region, offset, cleanedAnnotations); } @Override diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java index 37f2405c..b343011a 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java @@ -27,7 +27,10 @@ import edu.ucr.cs.riple.core.registries.index.Error; import edu.ucr.cs.riple.core.registries.index.Fix; import edu.ucr.cs.riple.core.registries.region.Region; +import edu.ucr.cs.riple.injector.changes.AddAnnotation; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; /** Represents an error reported by {@link NullAway}. */ public class NullAwayError extends Error { @@ -37,8 +40,20 @@ public class NullAwayError extends Error { /** Error type for field initialization errors from NullAway in {@code String}. */ public static final String FIELD_INITIALIZER_ERROR = "FIELD_NO_INIT"; - public NullAwayError(String messageType, String message, Region region, int offset, Fix fix) { - super(messageType, message, region, offset, fix); + public NullAwayError( + String messageType, + String message, + Region region, + int offset, + Set annotations) { + super(messageType, message, region, offset, annotations); + } + + @Override + protected Set computeFixesFromAnnotations(Set annotations) { + return annotations.stream() + .map(annot -> new Fix(annot, messageType, true)) + .collect(Collectors.toSet()); } @Override @@ -61,7 +76,7 @@ public boolean equals(Object o) { // message and should not be treated as a separate error. return true; } - return message.equals(error.message) && fix.equals(error.fix) && offset == error.offset; + return message.equals(error.message) && fixes.equals(error.fixes) && offset == error.offset; } @Override @@ -71,7 +86,7 @@ public int hashCode() { // to make sure equal objects will produce the same hashcode. messageType.equals(METHOD_INITIALIZER_ERROR) ? METHOD_INITIALIZER_ERROR : message, region, - fix, + fixes, offset); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/evaluators/graph/Node.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/evaluators/graph/Node.java index 880fba4f..dd45df13 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/evaluators/graph/Node.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/evaluators/graph/Node.java @@ -88,7 +88,7 @@ public void setOrigins(ErrorStore errorStore) { this.origins = ImmutableSet.copyOf( errorStore.getRegionsForElements( - error -> error.getFix() != null && error.getFix().equals(root))); + error -> error.getFixes() != null && error.getFixes().equals(root))); } /** diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/registries/index/Error.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/registries/index/Error.java index 3ed53417..b0994a81 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/registries/index/Error.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/registries/index/Error.java @@ -27,11 +27,12 @@ import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.registries.region.Region; +import edu.ucr.cs.riple.injector.changes.AddAnnotation; import edu.ucr.cs.riple.injector.location.Location; import edu.ucr.cs.riple.injector.location.OnParameter; import java.util.Collection; import java.util.Objects; -import javax.annotation.Nullable; +import java.util.Set; /** Represents an error reported by NullAway. */ @SuppressWarnings("JavaLangClash") @@ -41,31 +42,43 @@ public abstract class Error { public final String messageType; /** Error message. */ public final String message; + /** Annotations */ + public final Set annotations; /** The fixes which can resolve this error (possibly null). */ - @Nullable protected final Fix fix; + protected final Set fixes; /** Offset of program point in original version where error is reported. */ protected final int offset; /** Containing region. */ protected final Region region; + /** Error type for method initialization errors from NullAway in {@code String}. */ - public Error(String messageType, String message, Region region, int offset, @Nullable Fix fix) { + public Error( + String messageType, + String message, + Region region, + int offset, + Set annotations) { this.region = region; this.messageType = messageType; this.message = message; this.offset = offset; - this.fix = fix; + this.annotations = annotations; + this.fixes = computeFixesFromAnnotations(annotations); } + + protected abstract Set computeFixesFromAnnotations(Set annotations); + /** * Checks if error is resolvable. * * @return true if error is resolvable and false otherwise. */ public boolean hasFix() { - return this.fix != null; + return this.fixes != null; } - public Fix getFix() { - return this.fix; + public Set getFixes() { + return this.fixes; } /** @@ -74,7 +87,7 @@ public Fix getFix() { * @return true if error is resolvable with only one annotation and false otherwise. */ public boolean isSingleAnnotationFix() { - return this.fix != null && fix.changes.size() == 1; + return !fixes.isEmpty() && fixes.iterator().next().changes.size() == 1; } /** @@ -83,10 +96,9 @@ public boolean isSingleAnnotationFix() { * @return Location of the fix resolving this error. */ public Location toResolvingLocation() { - Preconditions.checkArgument(fix != null && fix.changes.size() == 1); - Preconditions.checkArgument(fix.toLocations().size() == 1); + Preconditions.checkArgument(!fixes.isEmpty() && fixes.iterator().next().changes.size() == 1); // no get() method, have to use iterator. - return fix.toLocations().iterator().next(); + return fixes.iterator().next().changes.iterator().next().getLocation(); } /** @@ -139,7 +151,7 @@ public boolean equals(Object o) { return messageType.equals(other.messageType) && region.equals(other.region) && message.equals(other.message) - && fix.equals(other.fix) + && fixes.equals(other.fixes) && offset == other.offset; } @@ -151,14 +163,14 @@ public boolean equals(Object o) { * @return true, if error is resolvable via fixes on target module. */ public boolean isFixableOnTarget(Context context) { - return fix != null - && this.fix.changes.stream() - .allMatch(change -> context.targetModuleInfo.declaredInModule(change.getLocation())); + return this.fixes.stream() + .flatMap(fix -> fix.changes.stream()) + .allMatch(change -> context.targetModuleInfo.declaredInModule(change.getLocation())); } @Override public int hashCode() { - return Objects.hash(messageType, message, region, fix, offset); + return Objects.hash(messageType, message, region, fixes, offset); } @Override @@ -182,10 +194,7 @@ public String toString() { */ public static ImmutableSet getResolvingFixesOfErrors( Collection errors) { - return errors.stream() - .filter(Error::hasFix) - .map(Error::getFix) - .collect(ImmutableSet.toImmutableSet()); + return errors.stream().flatMap(t -> t.fixes.stream()).collect(ImmutableSet.toImmutableSet()); } /** @@ -195,9 +204,9 @@ public static ImmutableSet getResolvingFixesOfErrors( * @return true, if this error is resolvable. */ public boolean isResolvableWith(Collection fixes) { - if (fix == null) { + if (this.fixes.isEmpty()) { return false; } - return fixes.contains(this.fix); + return fixes.containsAll(this.fixes); } } diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/TError.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/TError.java index 1f944401..7ac7b88d 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/TError.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/TError.java @@ -24,9 +24,14 @@ package edu.ucr.cs.riple.core.tools; +import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.registries.index.Error; +import edu.ucr.cs.riple.core.registries.index.Fix; import edu.ucr.cs.riple.core.registries.region.Region; +import edu.ucr.cs.riple.injector.changes.AddAnnotation; import edu.ucr.cs.riple.injector.location.Location; +import java.util.Set; +import java.util.stream.Collectors; /** * Wrapper class for {@link Error} used to create dummy errors (with default values) as part of @@ -36,6 +41,13 @@ public class TError extends Error { public TError(Location location) { - super("null", "null", new Region("null", "null"), 0, new TFix(location)); + super("null", "null", new Region("null", "null"), 0, Set.of(new DefaultAnnotation(location))); + } + + @Override + protected Set computeFixesFromAnnotations(Set annotations) { + return annotations.stream() + .map(addAnnotation -> new Fix(addAnnotation, ImmutableSet.of(messageType), true)) + .collect(Collectors.toSet()); } }