diff --git a/nullaway/src/main/java/com/uber/nullaway/ErrorBuilder.java b/nullaway/src/main/java/com/uber/nullaway/ErrorBuilder.java index 4cdb31d4a5..889934a06a 100755 --- a/nullaway/src/main/java/com/uber/nullaway/ErrorBuilder.java +++ b/nullaway/src/main/java/com/uber/nullaway/ErrorBuilder.java @@ -36,7 +36,6 @@ import com.google.common.base.Joiner; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -141,9 +140,6 @@ public Description createErrorDescription( } if (config.serializationIsActive()) { - if (nonNullTarget != null) { - SerializationService.serializeFixSuggestion(config, state, nonNullTarget, errorMessage); - } // For the case of initializer errors, the leaf of state.getPath() may not be the field / // method on which the error is being reported (since we do a class-wide analysis to find such // errors). In such cases, the suggestTree is the appropriate field / method tree, so use @@ -403,15 +399,12 @@ private Description.Builder removeCastToNonNullFix( * @param message Error message. * @param state The VisitorState object. * @param descriptionBuilder the description builder for the error. - * @param nonNullFields list of @Nonnull fields that are not guaranteed to be initialized along - * all paths at exit points of the constructor. */ void reportInitializerError( Symbol.MethodSymbol methodSymbol, String message, VisitorState state, - Description.Builder descriptionBuilder, - ImmutableList nonNullFields) { + Description.Builder descriptionBuilder) { // Check needed here, despite check in hasPathSuppression because initialization // checking happens at the class-level (meaning state.getPath() might not include the // method itself). @@ -424,13 +417,6 @@ void reportInitializerError( ErrorMessage errorMessage = new ErrorMessage(METHOD_NO_INIT, message); state.reportMatch( createErrorDescription(errorMessage, methodTree, descriptionBuilder, state, null)); - if (config.serializationIsActive()) { - // For now, we serialize each fix suggestion separately and measure their effectiveness - // separately - nonNullFields.forEach( - symbol -> - SerializationService.serializeFixSuggestion(config, state, symbol, errorMessage)); - } } boolean symbolHasSuppressWarningsAnnotation(Symbol symbol, String suppression) { diff --git a/nullaway/src/main/java/com/uber/nullaway/NullAway.java b/nullaway/src/main/java/com/uber/nullaway/NullAway.java index d4b1c81eb2..e7c6af8f47 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullAway.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullAway.java @@ -2062,17 +2062,11 @@ private void checkFieldInitialization(ClassTree tree, VisitorState state) { } } for (Element constructorElement : errorFieldsForInitializer.keySet()) { - ImmutableList fieldSymbols = - errorFieldsForInitializer.get(constructorElement).stream() - .map(element -> ASTHelpers.getSymbol(getTreesInstance(state).getTree(element))) - .collect(ImmutableList.toImmutableList()); - errorBuilder.reportInitializerError( (Symbol.MethodSymbol) constructorElement, errMsgForInitializer(errorFieldsForInitializer.get(constructorElement), state), state, - buildDescription(getTreesInstance(state).getTree(constructorElement)), - fieldSymbols); + buildDescription(getTreesInstance(state).getTree(constructorElement))); } // For static fields Set notInitializedStaticFields = notInitializedStatic(entities, state); diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/FixSerializationConfig.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/FixSerializationConfig.java index cb9d71f919..3beb0cfaae 100644 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/FixSerializationConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/fixserialization/FixSerializationConfig.java @@ -24,7 +24,6 @@ import com.google.common.base.Preconditions; import com.uber.nullaway.fixserialization.adapters.SerializationAdapter; -import com.uber.nullaway.fixserialization.out.SuggestedNullableFixInfo; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -38,23 +37,6 @@ /** Config class for Fix Serialization package. */ public class FixSerializationConfig { - /** - * If enabled, the corresponding output file will be cleared and for all reported errors, NullAway - * will serialize information and suggest type changes to resolve them, in case these errors could - * be fixed by adding a {@code @Nullable} annotation. These type change suggestions are in form of - * {@link SuggestedNullableFixInfo} instances and will be serialized at output directory. If - * deactivated, no {@code SuggestedFixInfo} will be created and the output file will remain - * untouched. - */ - public final boolean suggestEnabled; - - /** - * If enabled, serialized information of a fix suggest will also include the enclosing method and - * class of the element involved in error. Finding enclosing elements is costly and will only be - * computed at request. - */ - public final boolean suggestEnclosing; - /** * If enabled, NullAway will serialize information about methods that initialize a field and leave * it {@code @NonNull} at exit point. @@ -68,20 +50,12 @@ public class FixSerializationConfig { /** Default Constructor, all features are disabled with this config. */ public FixSerializationConfig() { - suggestEnabled = false; - suggestEnclosing = false; fieldInitInfoEnabled = false; outputDirectory = null; serializer = null; } - public FixSerializationConfig( - boolean suggestEnabled, - boolean suggestEnclosing, - boolean fieldInitInfoEnabled, - @Nullable String outputDirectory) { - this.suggestEnabled = suggestEnabled; - this.suggestEnclosing = suggestEnclosing; + public FixSerializationConfig(boolean fieldInitInfoEnabled, @Nullable String outputDirectory) { this.fieldInitInfoEnabled = fieldInitInfoEnabled; this.outputDirectory = outputDirectory; serializer = @@ -111,17 +85,6 @@ public FixSerializationConfig(String configFilePath, int serializationVersion) { XMLUtil.getValueFromTag(document, "/serialization/path", String.class).orElse(null); Preconditions.checkNotNull( this.outputDirectory, "Error in FixSerialization Config: Output path cannot be null"); - suggestEnabled = - XMLUtil.getValueFromAttribute(document, "/serialization/suggest", "active", Boolean.class) - .orElse(false); - suggestEnclosing = - XMLUtil.getValueFromAttribute( - document, "/serialization/suggest", "enclosing", Boolean.class) - .orElse(false); - if (suggestEnclosing && !suggestEnabled) { - throw new IllegalStateException( - "Error in the fix serialization configuration, suggest flag must be enabled to activate enclosing method and class serialization."); - } fieldInitInfoEnabled = XMLUtil.getValueFromAttribute( document, "/serialization/fieldInitInfo", "active", Boolean.class) @@ -138,23 +101,13 @@ public FixSerializationConfig(String configFilePath, int serializationVersion) { /** Builder class for Serialization Config */ public static class Builder { - private boolean suggestEnabled; - private boolean suggestEnclosing; private boolean fieldInitInfo; private @Nullable String outputDir; public Builder() { - suggestEnabled = false; - suggestEnclosing = false; fieldInitInfo = false; } - public Builder setSuggest(boolean value, boolean withEnclosing) { - this.suggestEnabled = value; - this.suggestEnclosing = withEnclosing && suggestEnabled; - return this; - } - public Builder setFieldInitInfo(boolean enabled) { this.fieldInitInfo = enabled; return this; @@ -179,7 +132,7 @@ public FixSerializationConfig build() { if (outputDir == null) { throw new IllegalStateException("did not set mandatory output directory"); } - return new FixSerializationConfig(suggestEnabled, suggestEnclosing, fieldInitInfo, outputDir); + return new FixSerializationConfig(fieldInitInfo, outputDir); } } } diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/SerializationService.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/SerializationService.java index e85fa6799f..50d42126ae 100644 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/SerializationService.java +++ b/nullaway/src/main/java/com/uber/nullaway/fixserialization/SerializationService.java @@ -26,16 +26,10 @@ import com.google.common.collect.ImmutableMap; import com.google.errorprone.VisitorState; import com.sun.source.tree.Tree; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.uber.nullaway.Config; import com.uber.nullaway.ErrorMessage; -import com.uber.nullaway.Nullness; -import com.uber.nullaway.fixserialization.location.SymbolLocation; import com.uber.nullaway.fixserialization.out.ErrorInfo; -import com.uber.nullaway.fixserialization.out.SuggestedNullableFixInfo; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -77,42 +71,6 @@ public static String escapeSpecialCharacters(String str) { return str; } - /** - * Serializes the suggested type change of an element in the source code that can resolve the - * error. We do not want suggested fix changes to override explicit annotations in the code, - * therefore, if the target element has an explicit {@code @Nonnull} annotation, no type change is - * suggested. - * - * @param config NullAway config. - * @param state Visitor state. - * @param target Target element to alternate it's type. - * @param errorMessage Error caused by the target. - */ - public static void serializeFixSuggestion( - Config config, VisitorState state, Symbol target, ErrorMessage errorMessage) { - FixSerializationConfig serializationConfig = config.getSerializationConfig(); - if (!serializationConfig.suggestEnabled) { - return; - } - // Skip if the element has an explicit @Nonnull annotation. - if (Nullness.hasNonNullAnnotation(target, config)) { - return; - } - Trees trees = Trees.instance(JavacProcessingEnvironment.instance(state.context)); - // Skip if the element is received as bytecode. - if (trees.getPath(target) == null) { - return; - } - SymbolLocation location = SymbolLocation.createLocationFromSymbol(target); - SuggestedNullableFixInfo suggestedNullableFixInfo = - buildFixMetadata(state.getPath(), errorMessage, location); - Serializer serializer = serializationConfig.getSerializer(); - Preconditions.checkNotNull( - serializer, "Serializer shouldn't be null at this point, error in configuration setting!"); - serializer.serializeSuggestedFixInfo( - suggestedNullableFixInfo, serializationConfig.suggestEnclosing); - } - /** * Serializes the reporting error. * @@ -132,27 +90,4 @@ public static void serializeReportingError( serializer, "Serializer shouldn't be null at this point, error in configuration setting!"); serializer.serializeErrorInfo(new ErrorInfo(state.getPath(), errorTree, errorMessage, target)); } - - /** - * Builds the {@link SuggestedNullableFixInfo} instance based on the {@link ErrorMessage} type. - */ - private static SuggestedNullableFixInfo buildFixMetadata( - TreePath path, ErrorMessage errorMessage, SymbolLocation location) { - SuggestedNullableFixInfo suggestedNullableFixInfo; - switch (errorMessage.getMessageType()) { - case RETURN_NULLABLE: - case WRONG_OVERRIDE_RETURN: - case WRONG_OVERRIDE_PARAM: - case PASS_NULLABLE: - case FIELD_NO_INIT: - case ASSIGN_FIELD_NULLABLE: - case METHOD_NO_INIT: - suggestedNullableFixInfo = new SuggestedNullableFixInfo(path, location, errorMessage); - break; - default: - throw new IllegalStateException( - "Cannot suggest a type to resolve error of type: " + errorMessage.getMessageType()); - } - return suggestedNullableFixInfo; - } } diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/Serializer.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/Serializer.java index 055db9065a..fa996684b7 100644 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/Serializer.java +++ b/nullaway/src/main/java/com/uber/nullaway/fixserialization/Serializer.java @@ -27,7 +27,6 @@ import com.uber.nullaway.fixserialization.adapters.SerializationAdapter; import com.uber.nullaway.fixserialization.out.ErrorInfo; import com.uber.nullaway.fixserialization.out.FieldInitializationInfo; -import com.uber.nullaway.fixserialization.out.SuggestedNullableFixInfo; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -47,9 +46,6 @@ public class Serializer { /** Path to write errors. */ private final Path errorOutputPath; - /** Path to write suggested fix metadata. */ - private final Path suggestedFixesOutputPath; - /** Path to write suggested fix metadata. */ private final Path fieldInitializationOutputPath; @@ -63,29 +59,12 @@ public class Serializer { public Serializer(FixSerializationConfig config, SerializationAdapter serializationAdapter) { String outputDirectory = config.outputDirectory; this.errorOutputPath = Paths.get(outputDirectory, "errors.tsv"); - this.suggestedFixesOutputPath = Paths.get(outputDirectory, "fixes.tsv"); this.fieldInitializationOutputPath = Paths.get(outputDirectory, "field_init.tsv"); this.serializationAdapter = serializationAdapter; serializeVersion(outputDirectory); initializeOutputFiles(config); } - /** - * Appends the string representation of the {@link SuggestedNullableFixInfo}. - * - * @param suggestedNullableFixInfo SuggestedFixInfo object. - * @param enclosing Flag to control if enclosing method and class should be included. - */ - public void serializeSuggestedFixInfo( - SuggestedNullableFixInfo suggestedNullableFixInfo, boolean enclosing) { - if (enclosing) { - suggestedNullableFixInfo.initEnclosing(); - } - appendToFile( - suggestedNullableFixInfo.tabSeparatedToString(serializationAdapter), - suggestedFixesOutputPath); - } - /** * Appends the string representation of the {@link ErrorMessage}. * @@ -145,9 +124,6 @@ private void serializeVersion(@Nullable String outputDirectory) { private void initializeOutputFiles(FixSerializationConfig config) { try { Files.createDirectories(Paths.get(config.outputDirectory)); - if (config.suggestEnabled) { - initializeFile(suggestedFixesOutputPath, SuggestedNullableFixInfo.header()); - } if (config.fieldInitInfoEnabled) { initializeFile(fieldInitializationOutputPath, FieldInitializationInfo.header()); } diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/XMLUtil.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/XMLUtil.java index c37fa1e76f..4b8219f26c 100644 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/XMLUtil.java +++ b/nullaway/src/main/java/com/uber/nullaway/fixserialization/XMLUtil.java @@ -133,12 +133,6 @@ public static void writeInXMLFormat(FixSerializationConfig config, String path) Element rootElement = doc.createElement("serialization"); doc.appendChild(rootElement); - // Suggest - Element suggestElement = doc.createElement("suggest"); - suggestElement.setAttribute("active", String.valueOf(config.suggestEnabled)); - suggestElement.setAttribute("enclosing", String.valueOf(config.suggestEnclosing)); - rootElement.appendChild(suggestElement); - // Field Initialization Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); fieldInitInfoEnabled.setAttribute("active", String.valueOf(config.fieldInitInfoEnabled)); diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/out/SuggestedNullableFixInfo.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/out/SuggestedNullableFixInfo.java deleted file mode 100644 index 3c5c661239..0000000000 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/out/SuggestedNullableFixInfo.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2022 Uber Technologies, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.uber.nullaway.fixserialization.out; - -import com.sun.source.util.TreePath; -import com.uber.nullaway.ErrorMessage; -import com.uber.nullaway.fixserialization.Serializer; -import com.uber.nullaway.fixserialization.adapters.SerializationAdapter; -import com.uber.nullaway.fixserialization.location.SymbolLocation; -import java.util.Objects; - -/** Stores information suggesting adding @Nullable on an element in source code. */ -public class SuggestedNullableFixInfo { - - /** SymbolLocation of the target element in source code. */ - private final SymbolLocation symbolLocation; - - /** Error which will be resolved by this type change. */ - private final ErrorMessage errorMessage; - - private final ClassAndMemberInfo classAndMemberInfo; - - public SuggestedNullableFixInfo( - TreePath path, SymbolLocation symbolLocation, ErrorMessage errorMessage) { - this.symbolLocation = symbolLocation; - this.errorMessage = errorMessage; - this.classAndMemberInfo = new ClassAndMemberInfo(path); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SuggestedNullableFixInfo)) { - return false; - } - SuggestedNullableFixInfo suggestedNullableFixInfo = (SuggestedNullableFixInfo) o; - return Objects.equals(symbolLocation, suggestedNullableFixInfo.symbolLocation) - && Objects.equals( - errorMessage.getMessageType().toString(), - suggestedNullableFixInfo.errorMessage.getMessageType().toString()); - } - - @Override - public int hashCode() { - return Objects.hash(symbolLocation, errorMessage.getMessageType().toString()); - } - - /** - * returns string representation of content of an object. - * - * @param adapter adapter used to serialize symbols. - * @return string representation of contents of an object in a line separated by tabs. - */ - public String tabSeparatedToString(SerializationAdapter adapter) { - return String.join( - "\t", - symbolLocation.tabSeparatedToString(adapter), - errorMessage.getMessageType().toString(), - "nullable", - Serializer.serializeSymbol(classAndMemberInfo.getClazz(), adapter), - Serializer.serializeSymbol(classAndMemberInfo.getMember(), adapter)); - } - - /** Finds the class and member of program point where triggered this type change. */ - public void initEnclosing() { - classAndMemberInfo.findValues(); - } - - /** - * Creates header of an output file containing all {@link SuggestedNullableFixInfo} written in - * string which values are separated by tabs. - * - * @return string representation of the header separated by tabs. - */ - public static String header() { - return String.join( - "\t", SymbolLocation.header(), "reason", "annotation", "rootClass", "rootMethod"); - } -} diff --git a/nullaway/src/test/java/com/uber/nullaway/SerializationTest.java b/nullaway/src/test/java/com/uber/nullaway/SerializationTest.java index 1dc585a23a..a535ec4cbd 100644 --- a/nullaway/src/test/java/com/uber/nullaway/SerializationTest.java +++ b/nullaway/src/test/java/com/uber/nullaway/SerializationTest.java @@ -29,14 +29,13 @@ import com.google.errorprone.util.ASTHelpers; import com.sun.tools.javac.code.Symbol; import com.uber.nullaway.fixserialization.FixSerializationConfig; +import com.uber.nullaway.fixserialization.adapters.SerializationAdapter; import com.uber.nullaway.fixserialization.adapters.SerializationV1Adapter; import com.uber.nullaway.fixserialization.adapters.SerializationV3Adapter; import com.uber.nullaway.fixserialization.out.FieldInitializationInfo; -import com.uber.nullaway.fixserialization.out.SuggestedNullableFixInfo; import com.uber.nullaway.tools.DisplayFactory; import com.uber.nullaway.tools.ErrorDisplay; import com.uber.nullaway.tools.FieldInitDisplay; -import com.uber.nullaway.tools.FixDisplay; import com.uber.nullaway.tools.SerializationTestHelper; import com.uber.nullaway.tools.version1.ErrorDisplayV1; import java.io.IOException; @@ -59,12 +58,9 @@ public class SerializationTest extends NullAwayTestsBase { private String configPath; private Path root; - private final DisplayFactory fixDisplayFactory; private final DisplayFactory errorDisplayFactory; private final DisplayFactory fieldInitDisplayFactory; - private static final String SUGGEST_FIX_FILE_NAME = "fixes.tsv"; - private static final String SUGGEST_FIX_FILE_HEADER = SuggestedNullableFixInfo.header(); private static final String ERROR_FILE_NAME = "errors.tsv"; private static final String ERROR_FILE_HEADER = new SerializationV3Adapter().getErrorsOutputFileHeader(); @@ -72,21 +68,6 @@ public class SerializationTest extends NullAwayTestsBase { private static final String FIELD_INIT_HEADER = FieldInitializationInfo.header(); public SerializationTest() { - this.fixDisplayFactory = - values -> { - Preconditions.checkArgument( - values.length == 10, - "Needs exactly 10 values to create FixDisplay object but found: " + values.length); - // Fixes are written in Temp Directory and is not known at compile time, therefore, - // relative paths are getting compared. - return new FixDisplay( - values[7], - values[2], - values[3], - values[0], - values[1], - SerializationTestHelper.getRelativePathFromUnitTestTempDirectory(values[5])); - }; this.errorDisplayFactory = values -> { Preconditions.checkArgument( @@ -130,7 +111,7 @@ public void setup() { try { Files.createDirectories(root); FixSerializationConfig.Builder builder = - new FixSerializationConfig.Builder().setSuggest(true, false).setOutputDirectory(output); + new FixSerializationConfig.Builder().setOutputDirectory(output); Path config = root.resolve("serializer.xml"); Files.createFile(config); configPath = config.toString(); @@ -142,7 +123,7 @@ public void setup() { @Test public void suggestNullableReturnSimpleTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -164,21 +145,27 @@ public void suggestNullableReturnSimpleTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", + new ErrorDisplay( + "RETURN_NULLABLE", + "returning @Nullable expression from method with @NonNull return type", + "com.uber.SubClass", "test(boolean)", - "null", + 201, + "com/uber/SubClass.java", "METHOD", "com.uber.SubClass", + "test(boolean)", + "null", + "null", "com/uber/SubClass.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableReturnSuperClassTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -212,21 +199,27 @@ public void suggestNullableReturnSuperClassTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", + new ErrorDisplay( + "WRONG_OVERRIDE_RETURN", + "method returns @Nullable, but superclass method com.uber.Super.test(boolean) returns @NonNull", + "com.uber.SubClass", "test(boolean)", - "null", + 176, + "com/uber/test/SubClass.java", "METHOD", "com.uber.Super", + "test(boolean)", + "null", + "null", "com/uber/android/Super.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableParamSimpleTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -250,21 +243,27 @@ public void suggestNullableParamSimpleTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", - "run(int,java.lang.Object)", - "h", + new ErrorDisplay( + "PASS_NULLABLE", + "passing @Nullable parameter 'o' where @NonNull is required", + "com.uber.Test", + "test_param(java.lang.String)", + 265, + "com/uber/android/Test.java", "PARAMETER", "com.uber.Test", + "run(int,java.lang.Object)", + "h", + "1", "com/uber/android/Test.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableParamSubclassTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -298,21 +297,27 @@ public void suggestNullableParamSubclassTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", + new ErrorDisplay( + "WRONG_OVERRIDE_PARAM", + "parameter o is @NonNull, but parameter in superclass method com.uber.Super.test(java.lang.Object) is @Nullable", + "com.uber.SubClass", "test(java.lang.Object)", - "o", + 182, + "com/uber/test/SubClass.java", "PARAMETER", "com.uber.SubClass", + "test(java.lang.Object)", + "o", + "0", "com/uber/test/SubClass.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableParamThisConstructorTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -337,21 +342,27 @@ public void suggestNullableParamThisConstructorTest() { "", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", - "Test(java.lang.Object)", - "o", + new ErrorDisplay( + "PASS_NULLABLE", + "passing @Nullable parameter 'null' where @NonNull is required", + "com.uber.Test", + "Test()", + 196, + "com/uber/test/Test.java", "PARAMETER", "com.uber.Test", + "Test(java.lang.Object)", + "o", + "0", "com/uber/test/Test.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableParamGenericsTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -381,21 +392,27 @@ public void suggestNullableParamGenericsTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", - "newStatement(T,java.util.ArrayList,boolean,boolean)", - "lhs", + new ErrorDisplay( + "PASS_NULLABLE", + "passing @Nullable parameter 'null' where @NonNull is required", + "com.uber.Child", + "newSideEffect(java.util.ArrayList)", + 198, + "com/uber/Child.java", "PARAMETER", "com.uber.Super", + "newStatement(T,java.util.ArrayList,boolean,boolean)", + "lhs", + "0", "com/uber/Super.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableFieldSimpleTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -417,16 +434,27 @@ public void suggestNullableFieldSimpleTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", "null", "h", "FIELD", "com.uber.Super", "com/uber/android/Super.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + new ErrorDisplay( + "ASSIGN_FIELD_NULLABLE", + "assigning @Nullable expression to @NonNull field", + "com.uber.Super", + "test(java.lang.Object)", + 234, + "com/uber/android/Super.java", + "FIELD", + "com.uber.Super", + "null", + "h", + "null", + "com/uber/android/Super.java")) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableFieldInitializationTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -450,16 +478,27 @@ public void suggestNullableFieldInitializationTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", "null", "f", "FIELD", "com.uber.Super", "com/uber/android/Super.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + new ErrorDisplay( + "ASSIGN_FIELD_NULLABLE", + "assigning @Nullable expression to @NonNull field", + "com.uber.Super", + "f", + 128, + "com/uber/android/Super.java", + "FIELD", + "com.uber.Super", + "null", + "f", + "null", + "com/uber/android/Super.java")) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableFieldControlFlowTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -501,22 +540,40 @@ public void suggestNullableFieldControlFlowTest() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", "null", "h", "FIELD", "com.uber.Test", "com/uber/android/Test.java"), - new FixDisplay( - "nullable", "null", "f", "FIELD", "com.uber.Test", "com/uber/android/Test.java"), - new FixDisplay( - "nullable", "null", "g", "FIELD", "com.uber.Test", "com/uber/android/Test.java"), - new FixDisplay( - "nullable", "null", "i", "FIELD", "com.uber.Test", "com/uber/android/Test.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + new ErrorDisplay( + "METHOD_NO_INIT", + "initializer method does not guarantee @NonNull fields h (line 5), f (line 5) are initialized along all control-flow paths", + "com.uber.Test", + "Test(boolean)", + 184, + "com/uber/android/Test.java", + "null", + "null", + "null", + "null", + "null", + "null"), + new ErrorDisplay( + "METHOD_NO_INIT", + "initializer method does not guarantee @NonNull fields g (line 5), i (line 5) are initialized along all control-flow paths", + "com.uber.Test", + "Test(boolean,boolean)", + 425, + "com/uber/android/Test.java", + "null", + "null", + "null", + "null", + "null", + "null")) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @Test public void suggestNullableNoInitializationFieldTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -533,93 +590,21 @@ public void suggestNullableNoInitializationFieldTest() { " Object f;", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", "null", "f", "FIELD", "com.uber.Test", "com/uber/android/Test.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) - .doTest(); - } - - @Test - public void skipSuggestPassNullableParamExplicitNonnullTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); - tester - .setArgs( - Arrays.asList( - "-d", - temporaryFolder.getRoot().getAbsolutePath(), - "-XepOpt:NullAway:AnnotatedPackages=com.uber", - "-XepOpt:NullAway:SerializeFixMetadata=true", - "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) - .addSourceLines( - "com/uber/android/Test.java", - "package com.uber;", - "import javax.annotation.Nullable;", - "import javax.annotation.Nonnull;", - "public class Test {", - " Object test(int i, @Nonnull Object h) {", - " return h;", - " }", - " Object test_param(@Nullable String o) {", - " // BUG: Diagnostic contains: passing @Nullable", - " return test(0, o);", - " }", - "}") - .expectNoOutput() - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) - .doTest(); - } - - @Test - public void skipSuggestReturnNullableExplicitNonnullTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); - tester - .setArgs( - Arrays.asList( - "-d", - temporaryFolder.getRoot().getAbsolutePath(), - "-XepOpt:NullAway:AnnotatedPackages=com.uber", - "-XepOpt:NullAway:SerializeFixMetadata=true", - "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) - .addSourceLines( - "com/uber/Base.java", - "package com.uber;", - "import javax.annotation.Nonnull;", - "public class Base {", - " @Nonnull Object test() {", - " // BUG: Diagnostic contains: returning @Nullable", - " return null;", - " }", - "}") - .expectNoOutput() - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) - .doTest(); - } - - @Test - public void skipSuggestFieldNullableExplicitNonnullTest() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); - tester - .setArgs( - Arrays.asList( - "-d", - temporaryFolder.getRoot().getAbsolutePath(), - "-XepOpt:NullAway:AnnotatedPackages=com.uber", - "-XepOpt:NullAway:SerializeFixMetadata=true", - "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) - .addSourceLines( - "com/uber/Base.java", - "package com.uber;", - "import javax.annotation.Nonnull;", - "public class Base {", - " // BUG: Diagnostic contains: field f not initialized", - " @Nonnull Object f;", - "}") - .expectNoOutput() - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + new ErrorDisplay( + "FIELD_NO_INIT", + "field f not initialized", + "com.uber.Test", + "f", + 97, + "com/uber/android/Test.java", + "FIELD", + "com.uber.Test", + "null", + "f", + "null", + "com/uber/android/Test.java")) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @@ -811,10 +796,7 @@ public void fieldInitializationSerializationTest() { try { Files.createDirectories(tempRoot); FixSerializationConfig.Builder builder = - new FixSerializationConfig.Builder() - .setSuggest(true, false) - .setFieldInitInfo(true) - .setOutputDirectory(output); + new FixSerializationConfig.Builder().setFieldInitInfo(true).setOutputDirectory(output); Path config = tempRoot.resolve("serializer.xml"); Files.createFile(config); configPath = config.toString(); @@ -981,42 +963,6 @@ public void errorSerializationTestLocalTypes() { @Test public void errorSerializationTestIdenticalLocalTypes() { - String[] sourceLines = { - "package com.uber;", - "import javax.annotation.Nullable;", - "public class TestWithLocalTypes {", - " @Nullable String test(Object o) {", - " class LocalType {", - " public String returnsNullable() {", - " // BUG: Diagnostic contains: returning @Nullable expression", - " return null;", - " }", - " }", - " LocalType local = new LocalType();", - " return local.returnsNullable();", - " }", - " @Nullable String test2(Object o) {", - " class LocalType {", - " public String returnsNullable2() {", - " // BUG: Diagnostic contains: returning @Nullable expression", - " return null;", - " }", - " }", - " LocalType local = new LocalType();", - " return local.returnsNullable2();", - " }", - " @Nullable String test2() {", - " class LocalType {", - " public String returnsNullable2() {", - " // BUG: Diagnostic contains: returning @Nullable expression", - " return null;", - " }", - " }", - " LocalType local = new LocalType();", - " return local.returnsNullable2();", - " }", - "}" - }; SerializationTestHelper errorTester = new SerializationTestHelper<>(root); errorTester .setArgs( @@ -1026,7 +972,42 @@ public void errorSerializationTestIdenticalLocalTypes() { "-XepOpt:NullAway:AnnotatedPackages=com.uber", "-XepOpt:NullAway:SerializeFixMetadata=true", "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) - .addSourceLines("com/uber/TestWithLocalTypes.java", sourceLines) + .addSourceLines( + "com/uber/TestWithLocalTypes.java", + "package com.uber;", + "import javax.annotation.Nullable;", + "public class TestWithLocalTypes {", + " @Nullable String test(Object o) {", + " class LocalType {", + " public String returnsNullable() {", + " // BUG: Diagnostic contains: returning @Nullable expression", + " return null;", + " }", + " }", + " LocalType local = new LocalType();", + " return local.returnsNullable();", + " }", + " @Nullable String test2(Object o) {", + " class LocalType {", + " public String returnsNullable2() {", + " // BUG: Diagnostic contains: returning @Nullable expression", + " return null;", + " }", + " }", + " LocalType local = new LocalType();", + " return local.returnsNullable2();", + " }", + " @Nullable String test2() {", + " class LocalType {", + " public String returnsNullable2() {", + " // BUG: Diagnostic contains: returning @Nullable expression", + " return null;", + " }", + " }", + " LocalType local = new LocalType();", + " return local.returnsNullable2();", + " }", + "}") .setExpectedOutputs( new ErrorDisplay( "RETURN_NULLABLE", @@ -1070,41 +1051,6 @@ public void errorSerializationTestIdenticalLocalTypes() { .setFactory(errorDisplayFactory) .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); - SerializationTestHelper fixTester = new SerializationTestHelper<>(root); - fixTester - .setArgs( - Arrays.asList( - "-d", - temporaryFolder.getRoot().getAbsolutePath(), - "-XepOpt:NullAway:AnnotatedPackages=com.uber", - "-XepOpt:NullAway:SerializeFixMetadata=true", - "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) - .addSourceLines("com/uber/TestWithLocalTypes.java", sourceLines) - .setExpectedOutputs( - new FixDisplay( - "nullable", - "returnsNullable()", - "null", - "METHOD", - "com.uber.TestWithLocalTypes$1LocalType", - "com/uber/TestWithLocalTypes.java"), - new FixDisplay( - "nullable", - "returnsNullable2()", - "null", - "METHOD", - "com.uber.TestWithLocalTypes$2LocalType", - "com/uber/TestWithLocalTypes.java"), - new FixDisplay( - "nullable", - "returnsNullable2()", - "null", - "METHOD", - "com.uber.TestWithLocalTypes$3LocalType", - "com/uber/TestWithLocalTypes.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) - .doTest(); } @Test @@ -1547,7 +1493,7 @@ public void errorSerializationTestEnclosedByFieldDeclaration() { @Test public void suggestNullableArgumentOnBytecode() { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -1569,15 +1515,21 @@ public void suggestNullableArgumentOnBytecode() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", - "foo(java.lang.Object)", - "x", + new ErrorDisplay( + "PASS_NULLABLE", + "passing @Nullable parameter 'null' where @NonNull is required", + "com.uber.UsesUnannotated", + "test(boolean)", + 263, + "com/uber/UsesUnannotated.java", "PARAMETER", "com.uber.nullaway.testdata.unannotated.MinimalUnannotatedClass", + "foo(java.lang.Object)", + "x", + "0", "com/uber/nullaway/testdata/unannotated/MinimalUnannotatedClass.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } @@ -1603,7 +1555,7 @@ public void suggestNullableArgumentOnBytecodeNoFileInfo() { } return answer; }); - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); tester .setArgs( Arrays.asList( @@ -1625,15 +1577,21 @@ public void suggestNullableArgumentOnBytecodeNoFileInfo() { " }", "}") .setExpectedOutputs( - new FixDisplay( - "nullable", - "foo(java.lang.Object)", - "x", + new ErrorDisplay( + "PASS_NULLABLE", + "passing @Nullable parameter 'null' where @NonNull is required", + "com.uber.UsesUnannotated", + "test(boolean)", + 263, + "com/uber/UsesUnannotated.java", "PARAMETER", "com.uber.nullaway.testdata.unannotated.MinimalUnannotatedClass", + "foo(java.lang.Object)", + "x", + "0", "null")) // <- ! the important bit - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, ERROR_FILE_HEADER) .doTest(); } } @@ -2027,7 +1985,8 @@ ERROR_FILE_NAME, new SerializationV1Adapter().getErrorsOutputFileHeader()) * to be read from "serialization_version.txt". */ public void checkVersionSerialization(int version) { - SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationTestHelper tester = new SerializationTestHelper<>(root); + SerializationAdapter adapter = SerializationAdapter.getAdapterForVersion(version); tester .setArgs( Arrays.asList( @@ -2039,20 +1998,10 @@ public void checkVersionSerialization(int version) { "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) // Just to run serialization features, the serialized fixes are not point of interest in // this test. - .addSourceLines( - "com/uber/Test.java", - "package com.uber;", - "public class Test {", - " Object run() {", - " // BUG: Diagnostic contains: returning @Nullable expression", - " return null;", - " }", - "}") - .setExpectedOutputs( - new FixDisplay( - "nullable", "run()", "null", "METHOD", "com.uber.Test", "com/uber/Test.java")) - .setFactory(fixDisplayFactory) - .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .addSourceLines("com/uber/Test.java", "package com.uber;", "public class Test { }") + .expectNoOutput() + .setFactory(errorDisplayFactory) + .setOutputFileNameAndHeader(ERROR_FILE_NAME, adapter.getErrorsOutputFileHeader()) .doTest(); Path serializationVersionPath = root.resolve("serialization_version.txt"); diff --git a/nullaway/src/test/java/com/uber/nullaway/tools/FixDisplay.java b/nullaway/src/test/java/com/uber/nullaway/tools/FixDisplay.java deleted file mode 100644 index 213d8dff30..0000000000 --- a/nullaway/src/test/java/com/uber/nullaway/tools/FixDisplay.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2022 Uber Technologies, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.uber.nullaway.tools; - -import java.util.Objects; - -/** - * Helper class to represent a suggested fix contents in a test case's (expected or actual) output. - */ -public class FixDisplay implements Display { - public final String annotation; - public final String method; - public final String param; - public final String location; - public final String className; - public final String path; - - public FixDisplay( - String annotation, - String method, - String param, - String location, - String className, - String path) { - this.annotation = annotation; - this.method = method; - this.param = param; - this.location = location; - this.className = className; - this.path = path; - } - - @Override - public String toString() { - return "\n FixDisplay{" - + "\n\tannotation='" - + annotation - + '\'' - + ", \n\tmethod='" - + method - + '\'' - + ", \n\tparam='" - + param - + '\'' - + ", \n\tlocation='" - + location - + '\'' - + ", \n\tclassName='" - + className - + '\'' - + ", \n\tpath='" - + path - + '\'' - + "\n }\n"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof FixDisplay)) { - return false; - } - FixDisplay fix = (FixDisplay) o; - return Objects.equals(annotation, fix.annotation) - && Objects.equals(method, fix.method) - && Objects.equals(param, fix.param) - && Objects.equals(location, fix.location) - && Objects.equals(className, fix.className) - && SerializationTestHelper.pathsAreEqual(path, fix.path); - } - - @Override - public int hashCode() { - return Objects.hash(annotation, method, param, location, className, path); - } -}