mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-09-06 12:27:56 +00:00
Switch to JSpecify annotations
Now, hear me out, what if instead of having three @Nullable annotations, we had *four*? I've been wanting to switch away from javax.annoations for a while. The library has been deprecated for ever and, unlike other @Nullable annotations, the annotation is attached to the parameter/function itself, rather than the type. We use JSpecify rather than one of the alternatives (JetBrains, CheckerFramework) mostly because it's what NullAway recommends. We keep CheckerFramework around for @DefaultQualifier, and JB's for @Contract. There are some ugly changes here — for instance, `@Nullable byte[]` is replace by `byte @Nullable`, and `@Nullable ILuaMachine.Factory` is `ILuaMachine.@Nullable Factory`. Ughr, I understand why, but it does not spark joy :).
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
|
||||
|
||||
package cc.tweaked.linter
|
||||
|
||||
import com.google.errorprone.BugPattern
|
||||
import com.google.errorprone.VisitorState
|
||||
import com.google.errorprone.bugpatterns.BugChecker
|
||||
import com.google.errorprone.fixes.SuggestedFix
|
||||
import com.google.errorprone.matchers.Description
|
||||
import com.google.errorprone.util.ASTHelpers
|
||||
import com.sun.source.tree.ImportTree
|
||||
import com.sun.source.tree.Tree
|
||||
|
||||
@BugPattern(
|
||||
summary = "Checks for forbidden imports.",
|
||||
severity = BugPattern.SeverityLevel.ERROR,
|
||||
tags = [BugPattern.StandardTags.LIKELY_ERROR],
|
||||
)
|
||||
class ForbiddenImport : BugChecker(), BugChecker.ImportTreeMatcher {
|
||||
override fun matchImport(tree: ImportTree, state: VisitorState): Description {
|
||||
if (tree.isStatic || tree.qualifiedIdentifier.kind != Tree.Kind.MEMBER_SELECT) return Description.NO_MATCH
|
||||
|
||||
val sym = ASTHelpers.getSymbol(tree.qualifiedIdentifier) ?: return Description.NO_MATCH
|
||||
val importedName = sym.qualifiedName.toString()
|
||||
if (!IMPORTS.contains(importedName)) return Description.NO_MATCH
|
||||
|
||||
val message = buildDescription(tree.qualifiedIdentifier).setMessage("Cannot import this symbol")
|
||||
val replacement = ALTERNATIVE_IMPORTS[importedName]
|
||||
if (replacement != null) message.addFix(SuggestedFix.replace(tree.qualifiedIdentifier, replacement))
|
||||
|
||||
return message.build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ALTERNATIVE_IMPORTS = mapOf(
|
||||
// Ban JSR 305 and JetBrains @Nullable, and prefer the JSpecify one.
|
||||
"org.javax.annotation.Nullable" to "org.jspecify.annotations.Nullable",
|
||||
"org.jetbrains.annotations.Nullable" to "org.jspecify.annotations.Nullable",
|
||||
// Prefer ErrorProne annotations over JSR ones.
|
||||
"javax.annotation.CheckReturnValue" to "com.google.errorprone.annotations.CheckReturnValue",
|
||||
"javax.annotation.OverridingMethodsMustInvokeSuper" to "com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper",
|
||||
"javax.annotation.concurrent.GuardedBy" to "com.google.errorprone.annotations.concurrent.GuardedBy",
|
||||
)
|
||||
|
||||
private val IMPORTS: Set<String> = setOf(
|
||||
// We ban all @Nonnull annotations, because that should be default already.
|
||||
"javax.annotation.Nonnull",
|
||||
"org.jetbrains.annotations.NotNull",
|
||||
"org.jspecify.annotations.NonNull",
|
||||
) + ALTERNATIVE_IMPORTS.keys
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
cc.tweaked.linter.ExtraMustCallSuper
|
||||
cc.tweaked.linter.ForbiddenImport
|
||||
cc.tweaked.linter.LoaderOverride
|
||||
cc.tweaked.linter.MissingLoaderOverride
|
||||
cc.tweaked.linter.SideChecker
|
||||
|
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.linter;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TestForbiddenImport {
|
||||
private final CompilationTestHelper compilationHelper = CompilationTestHelper.newInstance(ForbiddenImport.class, getClass());
|
||||
|
||||
@Test
|
||||
public void testForbiddenImport() {
|
||||
compilationHelper
|
||||
.addSourceLines("Import.java", """
|
||||
// BUG: Diagnostic matches: X
|
||||
import org.jspecify.annotations.NonNull;
|
||||
class X {
|
||||
}
|
||||
""")
|
||||
.expectErrorMessage("X", Predicates.containsPattern("Cannot import this symbol"))
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForbiddenImportSuggestion() {
|
||||
compilationHelper
|
||||
.addSourceLines("Import.java", """
|
||||
// BUG: Diagnostic matches: X
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
class X {
|
||||
}
|
||||
""")
|
||||
.expectErrorMessage("X", Predicates.containsPattern("Did you mean 'import com.google.errorprone.annotations.concurrent.GuardedBy;'"))
|
||||
.doTest();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user