Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModeName;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption.CombinedValue;
import com.palantir.gradle.suppressibleerrorprone.modes.interferences.ApplyingAndSuppressingOrRemoveUnusedInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.interferences.DisableModeInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.interferences.RemovingAndSuppressingInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.interferences.SuppressingAndApplyingInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.interferences.RemoveRolloutAndSuppressingInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.ApplyMode;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.DisableMode;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.RemoveRolloutMode;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.RemoveUnusedMode;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.SuppressMode;
import com.palantir.gradle.suppressibleerrorprone.modes.modes.TimingsMode;
import java.util.List;
Expand Down Expand Up @@ -60,13 +61,14 @@ public abstract class Modes {
ModeName.APPLY, new ApplyMode(),
ModeName.DISABLE, new DisableMode(),
ModeName.REMOVE_ROLLOUT, new RemoveRolloutMode(),
ModeName.REMOVE_UNUSED, new RemoveUnusedMode(),
ModeName.SUPPRESS, new SuppressMode(),
ModeName.TIMINGS, new TimingsMode());

private final Set<ModeInterference> interferences = Set.of(
new DisableModeInterference(),
new RemovingAndSuppressingInterference(),
new SuppressingAndApplyingInterference());
new RemoveRolloutAndSuppressingInterference(),
new ApplyingAndSuppressingOrRemoveUnusedInterference());

public final CombinedValue modifyCheckApi() {
return ModifyCheckApiOption.combine(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import one.util.streamex.EntryStream;

/**
Expand Down Expand Up @@ -56,7 +57,8 @@ public PatchChecksOption patchChecks() {
public Map<String, String> extraErrorProneCheckOptions() {
return EntryStream.of(CommonOptions.this.extraErrorProneCheckOptions())
.append(other.extraErrorProneCheckOptions())
.toMap();
.collect(Collectors.toMap(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to specify how we collect duplicate keys here, or else we get an error

Map.Entry::getKey, Map.Entry::getValue, (val1, val2) -> val1 + "," + val2));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
* (see {@link CommonOptions}). Modes can interfere with each other, see {@link ModeInterference}.
*/
public interface Mode {
String SUPPRESSIBLE_ERROR_PRONE_MODE = "SuppressibleErrorProne:Mode";

default ModifyCheckApiOption modifyCheckApi() {
return ModifyCheckApiOption.noEffect();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public enum ModeName {
APPLY("errorProneApply"),
SUPPRESS("errorProneSuppress"),
REMOVE_ROLLOUT("errorProneRemoveRollout"),
REMOVE_UNUSED("errorProneRemoveUnused"),
TIMINGS("errorProneTimings"),
// Historically, the logic of this plugin lived in baseline, so we need to support the old disable flag.
DISABLE("errorProneDisable", "com.palantir.baseline-error-prone.disable"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,42 @@

package com.palantir.gradle.suppressibleerrorprone.modes.interferences;

import com.google.common.collect.Sets;
import com.palantir.gradle.suppressibleerrorprone.modes.common.CommonOptions;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModeInterference;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModeInterferenceResult;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModeName;
import com.palantir.gradle.suppressibleerrorprone.modes.common.SpecificModeInterference;
import java.util.Map;
import java.util.Set;
import one.util.streamex.EntryStream;

/**
* Interference between Apply && (Suppress || RemoveUnused).
*/
public final class ApplyingAndSuppressingOrRemoveUnusedInterference implements ModeInterference {

public final class SuppressingAndApplyingInterference extends SpecificModeInterference {
@Override
protected Set<ModeName> interferingModes() {
return Set.of(ModeName.SUPPRESS, ModeName.APPLY);
public ModeInterferenceResult interferesWith(Set<ModeName> modeNames) {
if (!modeNames.contains(ModeName.APPLY)) {
return ModeInterferenceResult.noInterference();
}

Set<ModeName> interferingWithApply =
Sets.intersection(modeNames, Set.of(ModeName.SUPPRESS, ModeName.REMOVE_UNUSED));

if (interferingWithApply.isEmpty()) {
return ModeInterferenceResult.noInterference();
}

return ModeInterferenceResult.interferenceBetween(Sets.union(Set.of(ModeName.APPLY), interferingWithApply));
}

@Override
public CommonOptions interfere(Map<ModeName, CommonOptions> modeCommonOptions) {
CommonOptions suppress = modeCommonOptions.get(ModeName.SUPPRESS);
CommonOptions apply = modeCommonOptions.get(ModeName.APPLY);
CommonOptions naivelyCombined = EntryStream.of(modeCommonOptions)
.values()
.reduce(CommonOptions.empty(), CommonOptions::naivelyCombinedWith);

// If we're applying suggested patches at the same time as suppressing, we still need to tell
// errorprone to patch all checks, so we can make suggested fixes for suppressions in any check.
Expand All @@ -40,9 +60,8 @@ public CommonOptions interfere(Map<ModeName, CommonOptions> modeCommonOptions) {
// fixes for and which to suppress. So we add the PreferPatchChecks argument here, which we can
// use inside error-prone/the compiler.

return suppress.naivelyCombinedWith(apply)
.withExtraErrorProneCheckFlag(
"SuppressibleErrorProne:PreferPatchChecks",
() -> apply.patchChecks().asCommaSeparated().orElse(""));
return naivelyCombined.withExtraErrorProneCheckFlag(
"SuppressibleErrorProne:PreferPatchChecks",
() -> apply.patchChecks().asCommaSeparated().orElse(""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* Removing and suppressing cannot run at the same time, as removing just runs an errorprone to remove the for-rollout:
* suppressions - if suppressing happened at the same time, this errorprone would just get suppressed.
*/
public final class RemovingAndSuppressingInterference implements ModeInterference {
public final class RemoveRolloutAndSuppressingInterference implements ModeInterference {
@Override
public ModeInterferenceResult interferesWith(Set<ModeName> modeNames) {
if (modeNames.containsAll(Set.of(ModeName.REMOVE_ROLLOUT, ModeName.SUPPRESS))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.palantir.gradle.suppressibleerrorprone.modes.common.CommonOptions;
import com.palantir.gradle.suppressibleerrorprone.modes.common.Mode;
import com.palantir.gradle.suppressibleerrorprone.modes.common.PatchChecksOption;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -31,6 +32,11 @@ public CommonOptions configureAndReturnCommonOptions(ModeOptionContext context)
public PatchChecksOption patchChecks() {
return PatchChecksOption.someChecks(() -> checksToApplySuggestedPatchesFor(context));
}

@Override
public Map<String, String> extraErrorProneCheckOptions() {
return Map.of(SUPPRESSIBLE_ERROR_PRONE_MODE, "Apply");
}
Comment on lines +36 to +39
Copy link
Copy Markdown
Contributor Author

@kelvinou01 kelvinou01 Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To maintain parity with RemoveUnusedMode. Also, potentially a way to move mode interference logic into VisitorStateModifications in the future (see #180)

};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public Map<String, String> extraErrorProneCheckOptions() {

return Map.of(
"SuppressibleErrorProne:RemoveRolloutSuppressions",
context.flagValue().orElse(ALL_CHECKS));
context.flagValue().orElse(ALL_CHECKS),
SUPPRESSIBLE_ERROR_PRONE_MODE,
"RemoveRollout");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* (c) Copyright 2025 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.gradle.suppressibleerrorprone.modes.modes;

import com.palantir.gradle.suppressibleerrorprone.modes.common.CommonOptions;
import com.palantir.gradle.suppressibleerrorprone.modes.common.Mode;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption.ModifiedFile;
import com.palantir.gradle.suppressibleerrorprone.modes.common.PatchChecksOption;
import java.util.Map;

public final class RemoveUnusedMode implements Mode {
private static final String ALL_CHECKS = "";

@Override
public ModifyCheckApiOption modifyCheckApi() {
return ModifyCheckApiOption.mustModify(
ModifiedFile.BUG_CHECKER_INFO, ModifiedFile.SUPPRESSIBLE_TREE_PATH_SCANNER, ModifiedFile.VISITOR_STATE);
}

@Override
public CommonOptions configureAndReturnCommonOptions(ModeOptionContext context) {
return new CommonOptions() {
@Override
public PatchChecksOption patchChecks() {
return PatchChecksOption.allChecks();
}

@Override
public Map<String, String> extraErrorProneCheckOptions() {
return Map.of(SUPPRESSIBLE_ERROR_PRONE_MODE, "RemoveUnused");
}
Comment on lines +43 to +46
Copy link
Copy Markdown
Contributor Author

@kelvinou01 kelvinou01 Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be used in VisitorStateModifications to branch to the removeUnused procedure.


@Override
public boolean ignoreSuppressionAnnotations() {
return true;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption;
import com.palantir.gradle.suppressibleerrorprone.modes.common.ModifyCheckApiOption.ModifiedFile;
import com.palantir.gradle.suppressibleerrorprone.modes.common.PatchChecksOption;
import java.util.Map;

public final class SuppressMode implements Mode {
@Override
Expand All @@ -35,6 +36,11 @@ public CommonOptions configureAndReturnCommonOptions(ModeOptionContext context)
public PatchChecksOption patchChecks() {
return PatchChecksOption.allChecks();
}

@Override
public Map<String, String> extraErrorProneCheckOptions() {
return Map.of(SUPPRESSIBLE_ERROR_PRONE_MODE, "Suppress");
}
Comment on lines +40 to +43
Copy link
Copy Markdown
Contributor Author

@kelvinou01 kelvinou01 Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To maintain parity with RemoveUnusedMode. Also, potentially a way to move mode interference logic into VisitorStateModifications in the future (see #180)

};
}
}
4 changes: 3 additions & 1 deletion suppressible-error-prone/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ apply plugin: 'com.palantir.external-publish-jar'
dependencies {
implementation 'com.google.errorprone:error_prone_annotation'
implementation 'com.google.errorprone:error_prone_check_api'
implementation 'com.google.errorprone:error_prone_core'
implementation 'one.util:streamex'

annotationProcessor 'com.google.auto.service:auto-service'
compileOnly 'com.google.auto.service:auto-service'
Expand Down Expand Up @@ -45,6 +47,6 @@ publishing {

tasks.withType(JavaCompile) {
options.errorprone {
disable('StrictUnusedVariable')
disable('StrictUnusedVariable', 'PreferSafeLoggableExceptions')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* (c) Copyright 2025 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.suppressibleerrorprone;

import com.google.common.base.Suppliers;
import com.google.errorprone.BugCheckerInfo;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.scanner.BuiltInCheckerSuppliers;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import one.util.streamex.StreamEx;

public final class AllErrorprones {
private static Supplier<Set<String>> cachedAllBugcheckerNames = Suppliers.memoize(() -> {
Stream<BugChecker> pluginChecks =
ServiceLoader.load(BugChecker.class).stream().map(ServiceLoader.Provider::get);
Stream<String> pluginCheckNames =
StreamEx.of(pluginChecks).flatMap(bugchecker -> bugchecker.allNames().stream());

Stream<BugCheckerInfo> builtInChecks = BuiltInCheckerSuppliers.allChecks().getAllChecks().values().stream();
Stream<String> builtInCheckNames =
StreamEx.of(builtInChecks).flatMap(bugchecker -> bugchecker.allNames().stream());

return Stream.concat(pluginCheckNames, builtInCheckNames).collect(Collectors.toSet());
});

public static Set<String> allBugcheckerNames() {
return cachedAllBugcheckerNames.get();
}

private AllErrorprones() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import java.util.stream.Stream;
import javax.lang.model.element.Name;

Expand Down Expand Up @@ -67,5 +71,21 @@ static Name annotationName(Tree annotationType) {
"Unsupported annotation type: " + annotationType.getClass().getCanonicalName());
}

static boolean isSuppressWarningsAnnotation(AnnotationTree annotation) {
return AnnotationUtils.annotationName(annotation.getAnnotationType()).contentEquals("SuppressWarnings");
}

static ModifiersTree getModifiers(Tree suppressible) {
if (suppressible instanceof ClassTree classTree) {
return classTree.getModifiers();
} else if (suppressible instanceof MethodTree methodTree) {
return methodTree.getModifiers();
} else if (suppressible instanceof VariableTree variableTree) {
return variableTree.getModifiers();
} else {
throw new IllegalArgumentException("Unsupported tree type: " + suppressible.getClass());
}
}

private AnnotationUtils() {}
}
Loading