User: joe Date: 14 Nov 26 01:21 Revision: cf5ea915cc6073030781b3a37e698e8b65302537 Summary: Include much more detail in inspection descriptions TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10766&personal=false Index: src/main/resources/inspectionDescriptions/AddedEnumConstantsNamePattern.html =================================================================== --- src/main/resources/inspectionDescriptions/AddedEnumConstantsNamePattern.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/AddedEnumConstantsNamePattern.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ +

-Reports enum constants that are not properly prefixed by the mod id + Reports enum constants that are not properly prefixed by the mod id +

+ +

+ Enum constants added by mixins share the target enum with constants added by every other mod. Prefixing their names with the + mod id avoids conflicts between mods that add constants with the same name. The required pattern can be configured. +

+

+@Mixin(Color.class)
+enum ColorMixin {
+    PURPLE,       // bad
+    MY_MOD_PURPLE // good
+}
+
Index: src/main/resources/inspectionDescriptions/AddedMixinMembersNamePattern.html =================================================================== --- src/main/resources/inspectionDescriptions/AddedMixinMembersNamePattern.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/AddedMixinMembersNamePattern.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ +

-Reports added members not matching the correct name format + Reports added members not matching the correct name format +

+ +

+ Members added by mixins share the target class with members added by every other mod. Prefixing their names with the mod id + avoids conflicts between mods that add members with the same name. The required pattern and which members are reported can + be configured. This should be done when @Unique can't be used. +

+

+@Override public void doSomething() { ... }       // bad
+@Override public void myMod$doSomething() { ... } // good
+
Index: src/main/resources/inspectionDescriptions/AmbiguousMixinReference.html =================================================================== --- src/main/resources/inspectionDescriptions/AmbiguousMixinReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/AmbiguousMixinReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,19 @@ +

-Reports ambiguous references in Mixin annotations + Reports ambiguous references in Mixin annotations +

+ +

+ When a method name matches several overloads in the target class, Mixin targets all the overloads, which is often + not intended. Specify the descriptor to target a specific overload, or add the * wildcard to explicitly + target all of them. +

+

+@Inject(method = "add", ...)                     // bad
+@Inject(method = "add(Ljava/lang/String;)V", ...) // good: one overload
+@Inject(method = "add*", ...)                    // if all overloads are intended
+
Index: src/main/resources/inspectionDescriptions/AnnotationOutsideOfMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/AnnotationOutsideOfMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/AnnotationOutsideOfMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ -Reports Mixin annotations outside of @Mixin classes +

+ Reports Mixin annotations outside of @Mixin classes +

+ +

+ Mixin only processes these annotations in mixin classes. Anywhere else, they are ignored. +

Index: src/main/resources/inspectionDescriptions/AtUsage.html =================================================================== --- src/main/resources/inspectionDescriptions/AtUsage.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/AtUsage.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-The declared access transformer is never used + The declared access transformer is never used +

+ +

+ An unused entry changes the access of a Minecraft member for no reason. Unused entries are often left behind when the code that + needed them is removed, or the member is renamed in a new version of Minecraft. +

Index: src/main/resources/inspectionDescriptions/BukkitListenerImplemented.html =================================================================== --- src/main/resources/inspectionDescriptions/BukkitListenerImplemented.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/BukkitListenerImplemented.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ -All Bukkit @EventHandler methods must reside in a class that implements Listener. +

+ All Bukkit @EventHandler methods must reside in a class that implements Listener. +

+ +

+ Bukkit only registers event handlers of objects passed to PluginManager.registerEvents, which requires a + Listener. +

Index: src/main/resources/inspectionDescriptions/BungeeCordListenerImplemented.html =================================================================== --- src/main/resources/inspectionDescriptions/BungeeCordListenerImplemented.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/BungeeCordListenerImplemented.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ -All BungeeCord @EventHandler methods must reside in a class that implements Listener. +

+ All BungeeCord @EventHandler methods must reside in a class that implements Listener. +

+ +

+ BungeeCord only registers event handlers of objects passed to PluginManager.registerListener, which requires a + Listener. +

Index: src/main/resources/inspectionDescriptions/CancellableInjectionUsage.html =================================================================== --- src/main/resources/inspectionDescriptions/CancellableInjectionUsage.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CancellableInjectionUsage.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,27 @@ -Reports missing or unused cancellable @Inject usages +

+ Reports missing or unused cancellable @Inject usages +

+ +

+ Calling cancel() or setReturnValue() on the callback info of an @Inject that is not + marked cancellable = true throws an exception at runtime. Conversely, an @Inject marked + cancellable = true that is never canceled makes Mixin generate an unnecessary check in the target method. +

+

+// bad
+@Inject(method = "foo", at = @At("HEAD"))
+private void onFoo(CallbackInfo ci) {
+    ci.cancel();
+}
+
+// good
+@Inject(method = "foo", at = @At("HEAD"), cancellable = true)
+private void onFoo(CallbackInfo ci) {
+    ci.cancel();
+}
+
Index: src/main/resources/inspectionDescriptions/CleanupJoinDeclarationAndAssignment.html =================================================================== --- src/main/resources/inspectionDescriptions/CleanupJoinDeclarationAndAssignment.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CleanupJoinDeclarationAndAssignment.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,21 +20,6 @@ -

- Reports local variable declarations that are followed by an assignment and can be combined into a single statement. -

-

- Joining declarations and assignments helps simplify the code and clarifies the scope of variables. -

-

Example:

-

-String s;
-s = "foo";
-
-

After the fix:

-

-String s = "foo";
-
- +Copy of the built-in join declaration and assignment inspection, so that it can be used internally for cleanup Index: src/main/resources/inspectionDescriptions/CleanupUnnecessaryLocalVariable.html =================================================================== --- src/main/resources/inspectionDescriptions/CleanupUnnecessaryLocalVariable.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CleanupUnnecessaryLocalVariable.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -1,40 +1,25 @@ + --> -

- Reports local variable declarations that can be removed and their initializers inlined. -

-

- Removing unnecessary local variables helps simplify the code and clarifies its intent. -

-

Example:

-

-String s = "foo";
-return s;
-
-

After the fix:

-

-return "foo";
-
- +Copy of the built-in unnecessary local variable inspection, so that it can be used internally for cleanup Index: src/main/resources/inspectionDescriptions/ConflictingShareType.html =================================================================== --- src/main/resources/inspectionDescriptions/ConflictingShareType.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ConflictingShareType.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,21 @@ -Reports when two @Shares of the same variable have different types +

+ Reports when two @Shares of the same variable have different types +

+ +

+ All @Share parameters with the same name in the same target method refer to the same variable, so they must have + the same type. +

+

+// bad
+@Inject(method = "foo", at = @At("HEAD"))
+private void before(CallbackInfo ci, @Share("count") LocalIntRef count) { ... }
+
+@Inject(method = "foo", at = @At("TAIL"))
+private void after(CallbackInfo ci, @Share("count") LocalLongRef count) { ... }
+
Index: src/main/resources/inspectionDescriptions/CtorHeadNoUnsafe.html =================================================================== --- src/main/resources/inspectionDescriptions/CtorHeadNoUnsafe.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CtorHeadNoUnsafe.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Reports when @At("CTOR_HEAD") is missing unsafe +

+ Reports when @At("CTOR_HEAD") is missing unsafe +

+ +

+ Sponge Mixin restricts where injectors can inject into constructors, and CTOR_HEAD fails without + unsafe = true. Fabric's version of Mixin doesn't have this restriction, so by default this inspection is + disabled when Fabric Mixin is present. +

+

+@Inject(method = "<init>", at = @At("CTOR_HEAD"))          // bad
+@Inject(method = "<init>", at = @At(value = "CTOR_HEAD", unsafe = true)) // good
+
Index: src/main/resources/inspectionDescriptions/CtorHeadPostInit.html =================================================================== --- src/main/resources/inspectionDescriptions/CtorHeadPostInit.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CtorHeadPostInit.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Reports when CTOR_HEAD with enforce=POST_INIT doesn't target a field +

+ Reports when CTOR_HEAD with enforce=POST_INIT doesn't target a field +

+ +

+ With enforce=POST_INIT, CTOR_HEAD injects after the field initializers of a constructor. If + Mixin's heuristic to detect field initializers fails, it will default to injecting after the delegate constructor call. + This inspection reports when this happens. +

Index: src/main/resources/inspectionDescriptions/CtorHeadUsedForNonConstructor.html =================================================================== --- src/main/resources/inspectionDescriptions/CtorHeadUsedForNonConstructor.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/CtorHeadUsedForNonConstructor.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ -Reports when CTOR_HEAD is used without targeting a constructor method +

+ Reports when CTOR_HEAD is used without targeting a constructor method +

+ +

+ In methods that aren't constructors, CTOR_HEAD behaves the same as HEAD. Use HEAD + instead to make the intent clear. +

Index: src/main/resources/inspectionDescriptions/DeprecatedTranslation.html =================================================================== --- src/main/resources/inspectionDescriptions/DeprecatedTranslation.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DeprecatedTranslation.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Detects usage of translations that are removed or renamed in deprecated.json +

+ Detects usage of translations that are removed or renamed in deprecated.json +

+ +

+ Minecraft's deprecated.json lists translation keys that are no longer used. A removed key no longer has + a translation, and a renamed key should be referred to by its new name. Using such a key will cause your translation + key not to be translated. +

Index: src/main/resources/inspectionDescriptions/DisallowedTargetInsn.html =================================================================== --- src/main/resources/inspectionDescriptions/DisallowedTargetInsn.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DisallowedTargetInsn.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,16 @@ -Reports disallowed injector targets +

+ Reports targets not supported by an injector +

+ +

+ Each injector can only operate on certain kinds of instructions, and Mixin fails when an injector targets any other kind. +

+

+// bad: @ModifyArg can only target method invocations
+@ModifyArg(method = "foo", at = @At(value = "FIELD", target = "..."), index = 0)
+
Index: src/main/resources/inspectionDescriptions/DiscouragedInjectionPoint.html =================================================================== --- src/main/resources/inspectionDescriptions/DiscouragedInjectionPoint.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DiscouragedInjectionPoint.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Reports when a discouraged injection point is used + Reports when a discouraged injection point is used +

+ +

+ For example, JUMP selects jump instructions by their opcode and position in the method, which can change when the + target method is modified even slightly or is recompiled. Consider targeting a more specific instruction. +

Index: src/main/resources/inspectionDescriptions/DiscouragedShift.html =================================================================== --- src/main/resources/inspectionDescriptions/DiscouragedShift.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DiscouragedShift.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,25 @@ +

-Reports discouraged usages of shifting in injection points + Reports discouraged usages of shifting in injection points +

+ +

+ Shifting moves the injection point by a number of bytecode instructions, which depends on exactly how the target method was + compiled and breaks easily when it changes. Shifting is allowed where it is commonly needed, for example shifting + INVOKE and FIELD injection points to after the instruction. +

+

+ A common mistake is to use `Shift.BEFORE` on injection points that already target before, such as `INVOKE`. This results in + the injection being shifted one instruction too early. +

+

+// bad
+@At(value = "INVOKE", target = "...", shift = At.Shift.BY, by = 3)
+
+// good
+@At(value = "INVOKE", target = "...", shift = At.Shift.AFTER)
+
Index: src/main/resources/inspectionDescriptions/DuplicateInterface.html =================================================================== --- src/main/resources/inspectionDescriptions/DuplicateInterface.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DuplicateInterface.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,6 @@ -Reports duplicate @Interface classes in an @Implements annotation. +Reports duplicate @Interface classes in an @Implements annotation. Index: src/main/resources/inspectionDescriptions/DuplicateInterfacePrefix.html =================================================================== --- src/main/resources/inspectionDescriptions/DuplicateInterfacePrefix.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/DuplicateInterfacePrefix.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,19 @@ -Reports duplicate @Interface prefixes in an @Implements annotation. +

+ Reports duplicate @Interface prefixes in an @Implements annotation. +

+ +

+ Methods whose name starts with an @Interface prefix are renamed when the mixin is applied, to soft-implement + a method of that interface. If two interfaces share a prefix, it is ambiguous which interface a prefixed method belongs to. +

+

+@Implements({
+    @Interface(iface = Foo.class, prefix = "foo$"),
+    @Interface(iface = Bar.class, prefix = "foo$") // Duplicate prefix 'foo$'
+})
+
Index: src/main/resources/inspectionDescriptions/EmptyImplements.html =================================================================== --- src/main/resources/inspectionDescriptions/EmptyImplements.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/EmptyImplements.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,6 @@ -Reports empty @Implements annotations (without an @Interface) +Reports empty @Implements annotations (without an @Interface) Index: src/main/resources/inspectionDescriptions/FabricModJsonEntrypointsValidation.html =================================================================== --- src/main/resources/inspectionDescriptions/FabricModJsonEntrypointsValidation.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/FabricModJsonEntrypointsValidation.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Validates entrypoints declared in Fabric mod JSON files. +

+ Validates entrypoints declared in fabric.mod.json files. +

+ +

+ Fabric Loader creates entrypoints from the classes, methods and fields listed in fabric.mod.json, and fails to + load the mod if it can't. For example, an entrypoint class must have an empty constructor, and an entrypoint method must be + public and have no parameters. +

Index: src/main/resources/inspectionDescriptions/FieldDeclarationSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/FieldDeclarationSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/FieldDeclarationSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,9 @@ -A field in a class annotated for one side cannot be declared as being in the other side. For example, a class which is annotated as @SideOnly(Side.SERVER) cannot contain a field which is annotated as @SideOnly(Side.CLIENT). Since a class that is annotated with @SideOnly brings everything with it, @SideOnly annotated fields are usually useless +A field in a class annotated for one side cannot be declared as being in the other side. For example, a class which is annotated +as @SideOnly(Side.SERVER) cannot contain a field which is annotated as @SideOnly(Side.CLIENT). Since +a class that is annotated with @SideOnly brings everything with it, @SideOnly annotated fields are +usually useless Index: src/main/resources/inspectionDescriptions/ImplicitConstructorInvoker.html =================================================================== --- src/main/resources/inspectionDescriptions/ImplicitConstructorInvoker.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ImplicitConstructorInvoker.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ -Implicit constructor invoker (fails outside of dev) +

+ Reports implicit constructor invokers (fails outside of dev) +

+ +

+ An @Invoker without a target, whose name starts with create or new, invokes a + constructor. This works in the development environment, but fails in production. Specify the constructor explicitly instead. +

+

+@Invoker              // bad
+static Foo createFoo(int x) { throw new AssertionError(); }
+
+@Invoker("<init>")    // good
+static Foo createFoo(int x) { throw new AssertionError(); }
+
Index: src/main/resources/inspectionDescriptions/InjectCancellableBeforeSuperConstructorCall.html =================================================================== --- src/main/resources/inspectionDescriptions/InjectCancellableBeforeSuperConstructorCall.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InjectCancellableBeforeSuperConstructorCall.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ -Reports when cancellable @Injects are used before a superconstructor call +

+ Reports when cancellable @Injects are used before a delegate constructor call +

+ +

+ A constructor must call the delegate constructor before it returns. Cancelling an injector before that call would return + without it, which is not valid bytecode. +

Index: src/main/resources/inspectionDescriptions/InjectCouldBeOverwrite.html =================================================================== --- src/main/resources/inspectionDescriptions/InjectCouldBeOverwrite.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InjectCouldBeOverwrite.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,30 @@ -Reports when an @Inject is better written as an @Overwrite, because the @Inject always cancels and could cause silent mod incompatibilities +

+ Reports when an @Inject can be written as an @Overwrite +

+ +

+ Such an @Inject effectively replaces the target method, so any other mod's injectors into the target method + silently stop working. An @Overwrite makes this explicit, and Mixin reports conflicts between overwrites. +

+

+// bad
+@Inject(method = "getSpeed", at = @At("HEAD"), cancellable = true)
+private void getSpeed(CallbackInfoReturnable<Float> cir) {
+    cir.setReturnValue(2.0f);
+}
+
+// good
+/**
+ * @author MyMod
+ * @reason Double the speed
+ */
+@Overwrite
+public float getSpeed() {
+    return 2.0f;
+}
+
Index: src/main/resources/inspectionDescriptions/InjectIntoConstructor.html =================================================================== --- src/main/resources/inspectionDescriptions/InjectIntoConstructor.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InjectIntoConstructor.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,23 @@ -@Inject into Constructor +

+ @Inject into Constructor +

+ +

+ Reports @Inject injectors that inject into a constructor at an instruction other than RETURN, + without unsafe = true. +

+

+ Sponge Mixin only allows @Inject into constructors at RETURN by default, because injecting earlier + risks running code before the object is initialized. Fabric's version of Mixin doesn't have this restriction, so by default + it is allowed when Fabric Mixin is present. +

+

+@Inject(method = "<init>", at = @At(value = "INVOKE", target = "..."))                // bad
+@Inject(method = "<init>", at = @At(value = "INVOKE", target = "...", unsafe = true)) // good
+@Inject(method = "<init>", at = @At("RETURN"))                                         // good
+
Index: src/main/resources/inspectionDescriptions/InjectLocalCaptureCanBeReplacedWithLocal.html =================================================================== --- src/main/resources/inspectionDescriptions/InjectLocalCaptureCanBeReplacedWithLocal.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InjectLocalCaptureCanBeReplacedWithLocal.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,26 @@ -Reports when @Inject local capture can be replaced with @Local, which is less brittle +

+ Reports when @Inject local capture can be replaced with @Local +

+ +

+ Local capture requires the injector to declare every local variable up to the ones it needs, in order, so it breaks whenever + the local variables of the target method change. @Local captures only the locals that are needed. +

+

+// bad
+@Inject(method = "foo", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILHARD)
+private void onFoo(CallbackInfo ci, int i, String name, List<String> list) {
+    System.out.println(name);
+}
+
+// good
+@Inject(method = "foo", at = @At("TAIL"))
+private void onFoo(CallbackInfo ci, @Local String name) {
+    System.out.println(name);
+}
+
Index: src/main/resources/inspectionDescriptions/InterfaceIsInterface.html =================================================================== --- src/main/resources/inspectionDescriptions/InterfaceIsInterface.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InterfaceIsInterface.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ -Reports usages of @Interface with a regular class instead of an interface. +

+ Reports usages of @Interface with a regular class instead of an interface. +

+ +

+ @Implements makes the target class implement interfaces, so it can't be used with a class. +

Index: src/main/resources/inspectionDescriptions/InvalidConfigValue.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidConfigValue.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidConfigValue.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ +

-Reports invalid values in Mixin configuration files. + Reports invalid values in Mixin configuration files. +

+ +

+ Mixin fails to load a configuration file whose values don't have the expected types. +

Index: src/main/resources/inspectionDescriptions/InvalidInjectorMethodSignature.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidInjectorMethodSignature.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidInjectorMethodSignature.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,24 @@ +

-Reports problems related to the method signature of Mixin injectors + Reports problems related to the method signature of Mixin injectors +

+ +

+ Mixin expects specific parameters and return type, depending on the injector and its target. Mixin fails to apply an + injector whose signature doesn't match. +

+

+// target: public int getCount(String name)
+
+// bad
+@Inject(method = "getCount", at = @At("HEAD"))
+private void onGetCount(CallbackInfo ci) { ... }
+
+// good
+@Inject(method = "getCount", at = @At("HEAD"))
+private void onGetCount(String name, CallbackInfoReturnable<Integer> cir) { ... }
+
Index: src/main/resources/inspectionDescriptions/InvalidInterfacePrefix.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidInterfacePrefix.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidInterfacePrefix.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,16 @@ -Reports invalid prefixes in @Interface annotations. The prefixes must end with a dollar sign ($). +

+ Reports invalid prefixes in @Interface annotations. The prefixes must end with a dollar sign ($). +

+ +

+ Mixin rejects such prefixes when the mixin is applied. +

+

+@Interface(iface = Foo.class, prefix = "foo")  // bad
+@Interface(iface = Foo.class, prefix = "foo$") // good
+
Index: src/main/resources/inspectionDescriptions/InvalidMemberReference.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidMemberReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidMemberReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,10 +20,25 @@ -Reports invalid usages of member references in Mixin annotations. Two different formats are supported by Mixin: +

+ Reports member references in Mixin annotations that Mixin can't parse. +

+ +

+ Mixin fails to apply an annotation whose method or target it can't parse. A member reference + consists of an optional owner class, the member name and an optional descriptor, in one of these forms: +

+

+ The name can be followed by a quantifier such as *. Regex selectors, such as /^execute$/, and dynamic + selectors, such as @Desc(id), are also supported. +

+

+@At(value = "INVOKE", target = "com.example.ExampleClass#execute")      // bad
+@At(value = "INVOKE", target = "Lcom/example/ExampleClass;execute(II)V") // good
+
Index: src/main/resources/inspectionDescriptions/InvalidMixinPluginRegistration.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidMixinPluginRegistration.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidMixinPluginRegistration.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Reports invalid Mixin plugins in Mixin configuration files. + Reports invalid Mixin plugins in Mixin configuration files. +

+ +

+ Mixin creates an instance of the plugin class, so it must implement IMixinConfigPlugin, and be a non-abstract + class with a public no-argument constructor. +

Index: src/main/resources/inspectionDescriptions/InvalidMixinRegistration.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidMixinRegistration.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidMixinRegistration.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ +

-Reports invalid Mixin classes in Mixin configuration files. + Reports invalid Mixin classes in Mixin configuration files. +

+ +

+ Only @Mixin classes can be listed as mixins. +

Index: src/main/resources/inspectionDescriptions/InvalidMixinTarget.html =================================================================== --- src/main/resources/inspectionDescriptions/InvalidMixinTarget.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvalidMixinTarget.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ +

-A Mixin class must target either one or more classes or provide one or more string targets + A Mixin class must target either one or more classes or provide one or more string targets +

+ +

+ Mixin needs to know which classes to apply the mixin to. +

+

+@Mixin                       // bad
+@Mixin(Target.class)         // good
+@Mixin(targets = "a.b.Target") // good, for classes that can't be referenced
+
Index: src/main/resources/inspectionDescriptions/InvokeAssignCanReplacedWithExpression.html =================================================================== --- src/main/resources/inspectionDescriptions/InvokeAssignCanReplacedWithExpression.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/InvokeAssignCanReplacedWithExpression.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,21 @@ -Reports when INVOKE_ASSIGN could be replaced with a MixinExtras expression. Expressions are preferred over INVOKE_ASSIGN because they fail when an assignment doesn't exist. +

+ Reports when INVOKE_ASSIGN could be replaced with a MixinExtras expression. +

+ +

+ Expressions are preferred over INVOKE_ASSIGN because they fail when an assignment doesn't exist. +

+

+// bad
+@Inject(method = "foo", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/example/Foo;getName()Ljava/lang/String;"))
+
+// good
+@Definition(id = "getName", method = "Lcom/example/Foo;getName()Ljava/lang/String;")
+@Expression("? = ?.getName()")
+@Inject(method = "foo", at = @At(value = "MIXINEXTRAS:EXPRESSION", shift = At.Shift.AFTER))
+
Index: src/main/resources/inspectionDescriptions/IsCancelled.html =================================================================== --- src/main/resources/inspectionDescriptions/IsCancelled.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/IsCancelled.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,23 @@ +

-Reports useless event cancellation checks + Reports useless event cancellation checks +

+ +

+ Some event listeners only receive events that are not canceled, so checking whether the event is canceled always gives the + same result. On Bukkit, this applies to handlers with ignoreCancelled = true. On Sponge, listeners only receive + events that are not canceled, unless annotated with @IsCancelled. +

+

+@EventHandler(ignoreCancelled = true)
+public void onBreak(BlockBreakEvent event) {
+    if (event.isCancelled()) { // always false
+        return;
+    }
+    ...
+}
+
Index: src/main/resources/inspectionDescriptions/IsCancelled_zh.html =================================================================== --- src/main/resources/inspectionDescriptions/IsCancelled_zh.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/IsCancelled_zh.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ +

-报告无用的 event 取消检查 + 报告无用的 event 取消检查 +

+ +

+ 某些事件监听器只会收到未被取消的事件,因此检查事件是否已取消总是得到相同的结果。在 Bukkit 上,这适用于带有 + ignoreCancelled = true 的处理器。在 Sponge 上,除非标注了 @IsCancelled,监听器只会收到未被取消的事件。 +

+

+@EventHandler(ignoreCancelled = true)
+public void onBreak(BlockBreakEvent event) {
+    if (event.isCancelled()) { // 总是 false
+        return;
+    }
+    ...
+}
+
Index: src/main/resources/inspectionDescriptions/LocalCaptureFailException.html =================================================================== --- src/main/resources/inspectionDescriptions/LocalCaptureFailException.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/LocalCaptureFailException.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,9 +20,18 @@ -Usage of LocalCapture.CAPTURE_FAILEXCEPTION is usually a mistake and should be replaced with -LocalCapture.CAPTURE_FAILHARD. CAPTURE_FAILEXCEPTION generates code which throws an -exception when the callback is reached, if the locals do not match. If this is really what you want, you can -suppress this warning. +

+ Reports usages of LocalCapture.CAPTURE_FAILEXCEPTION. +

+ +

+ If the captured locals don't match the target method, CAPTURE_FAILEXCEPTION generates code which throws an + exception when the callback is reached, instead of failing when the mixin is applied. This hides the problem until that code + runs, so it is usually a mistake. +

+

+@Inject(method = "foo", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) // bad
+@Inject(method = "foo", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILHARD)      // good
+
Index: src/main/resources/inspectionDescriptions/LocalMayBeArgsOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/LocalMayBeArgsOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/LocalMayBeArgsOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ -Checks that @Local has argsOnly if it targets arguments, which improves performance of the mixin +

+ Checks that @Local has argsOnly if it targets arguments, which improves performance of the mixin +

+ +

+ With argsOnly = true, MixinExtras only searches the method's parameters, so it doesn't have to analyze the local + variables of the target method. +

+

+@Local String name                  // bad
+@Local(argsOnly = true) String name // good
+
Index: src/main/resources/inspectionDescriptions/LocalMayUseName.html =================================================================== --- src/main/resources/inspectionDescriptions/LocalMayUseName.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/LocalMayUseName.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Reports @Local relying on index or ordinal that may use a name instead +

+ Reports @Local relying on index or ordinal that may use a name instead +

+ +

+ The index and ordinal of a local variable change whenever local variables are added to or removed from the target method, + while its name usually stays the same. Names are also easier to understand. This is only reported where local variable names + are available at runtime. +

+

+@Local(ordinal = 1) int count        // bad
+@Local(name = "count") int count     // good
+
Index: src/main/resources/inspectionDescriptions/LocalVariableDeclarationSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/LocalVariableDeclarationSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/LocalVariableDeclarationSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,25 @@ -A variable whose class declaration is annotated with @SideOnly for one side cannot be declared in a class or method that does not match the same side. +

+ A variable whose class declaration is annotated with @SideOnly for one side cannot be declared in a class or + method that does not match the same side. +

+ +

+ Code annotated with @SideOnly is removed when running on the other side, so code that uses it crashes there. +

+

+// bad: runs on both sides
+public void update() {
+    Minecraft mc = Minecraft.getMinecraft(); // Minecraft is @SideOnly(Side.CLIENT)
+}
+
+// good
+@SideOnly(Side.CLIENT)
+public void update() {
+    Minecraft mc = Minecraft.getMinecraft();
+}
+
Index: src/main/resources/inspectionDescriptions/MethodCallSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/MethodCallSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MethodCallSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,24 @@ -Methods which are declared with a @SideOnly annotation can only be used in matching @SideOnly classes and methods. +

+ Methods which are declared with a @SideOnly annotation can only be used in matching @SideOnly classes and methods. +

+ +

+ Code annotated with @SideOnly is removed when running on the other side, so code that uses it crashes there. +

+

+// bad: runs on both sides
+public void update() {
+    getRenderer().render(); // render() is @SideOnly(Side.CLIENT)
+}
+
+// good
+@SideOnly(Side.CLIENT)
+public void update() {
+    getRenderer().render();
+}
+
Index: src/main/resources/inspectionDescriptions/MethodSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/MethodSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MethodSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ -A method in a class annotated for one side cannot be declared as being in the other side. For example, a class which is annotated as @SideOnly(Side.SERVER) cannot contain a method which is annotated as @SideOnly(Side.CLIENT). Since a class that is annotated with @SideOnly brings everything with it, @SideOnly annotated methods are usually useless +

+ Reports methods annotated with a different @SideOnly side than their class, and methods returning a type + annotated with a different @SideOnly side than the method or its class. +

+ +

+ A class annotated with @SideOnly is removed together with everything in it on the other side, so a method in it + can't be for a different side, and @SideOnly annotations on its methods are usually useless. Likewise, a method + can't return a type that doesn't exist on its side. +

+

+@SideOnly(Side.SERVER)
+public class ServerData {
+    @SideOnly(Side.CLIENT) // bad
+    public void render() { ... }
+}
+
Index: src/main/resources/inspectionDescriptions/MissingAbstractShadowsInEnumMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/MissingAbstractShadowsInEnumMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MissingAbstractShadowsInEnumMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,29 @@ +

-Reports missing abstract shadows in enum mixins + Reports missing abstract shadows in enum mixins +

+ +

+ If the target enum has abstract methods, every enum constant must implement them. Declaring them as abstract + @Shadow methods in the mixin makes the compiler check that the constants added by the mixin implement them. + Otherwise, the missing implementations cause errors at runtime. In obfuscated environments, the override will not be + remapped if the shadow isn't present. +

+

+enum Shape {
+    CIRCLE { @Override double area(double r) { ... } };
+    abstract double area(double r);
+}
+
+@Mixin(Shape.class)
+enum ShapeMixin {
+    MYMOD_SQUARE { @Override double area(double r) { ... } };
+
+    @Shadow
+    abstract double area(double r);
+}
+
Index: src/main/resources/inspectionDescriptions/MissingFormat.html =================================================================== --- src/main/resources/inspectionDescriptions/MissingFormat.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MissingFormat.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ +

-Detects missing format arguments for translations + Detects missing format arguments for translations +

+ +

+ If a translation contains more placeholders such as %s than there are arguments, it can't be formatted and isn't + displayed correctly. +

+

+// "my_mod.greeting": "Hello, %s!"
+Component.translatable("my_mod.greeting")       // bad
+Component.translatable("my_mod.greeting", name) // good
+
Index: src/main/resources/inspectionDescriptions/MissingMessageConstructor.html =================================================================== --- src/main/resources/inspectionDescriptions/MissingMessageConstructor.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MissingMessageConstructor.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,25 @@ -All implementations of IMessage and IMessageHandler must have an empty constructor +

+ All implementations of IMessage and IMessageHandler must have an empty constructor +

+ +

+ When a message is received, Forge creates the message and its handler using their empty constructors, before reading the + message's contents. Without an empty constructor, this fails. +

+

+public class MyMessage implements IMessage {
+    private int value;
+
+    public MyMessage() {} // required
+
+    public MyMessage(int value) {
+        this.value = value;
+    }
+    ...
+}
+
Index: src/main/resources/inspectionDescriptions/MissingOrInvalidOpcode.html =================================================================== --- src/main/resources/inspectionDescriptions/MissingOrInvalidOpcode.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MissingOrInvalidOpcode.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,24 @@ +

-Reports missing or invalid usages of opcode + Reports missing or invalid usages of opcode +

+ +

+ Some injection points, such as FIELD, match several kinds of instructions, and it's good to specify an opcode + to say which kind to target. For example, without an opcode, FIELD matches both reads and writes of the field, + which is rarely intended. +

+

+ Other injection points ignore the opcode, so specifying one there is misleading. +

+

+// bad
+@At(value = "FIELD", target = "Lcom/example/Foo;bar:I")
+
+// good
+@At(value = "FIELD", target = "Lcom/example/Foo;bar:I", opcode = Opcodes.PUTFIELD)
+
Index: src/main/resources/inspectionDescriptions/MissingUnique.html =================================================================== --- src/main/resources/inspectionDescriptions/MissingUnique.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MissingUnique.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Reports missing @Unique annotations +

+ Reports missing @Unique annotations +

+ +

+ Many mixins can add members to the same target class. Without @Unique, if two mixins add a member with the + same name and descriptor, one silently replaces the other. With @Unique, Mixin renames a conflicting private + member, and reports an error for a conflicting public member. +

+

+private int count; // bad
+@Unique private int count; // good
+
Index: src/main/resources/inspectionDescriptions/MixinAnnotationTarget.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinAnnotationTarget.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinAnnotationTarget.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Verifies targets of mixin annotations + Verifies targets of mixin annotations +

+ +

+ When Mixin can't find the method or instruction an annotation targets, the injector fails when the mixin is applied. It either + crashes the game, or, if the injector is not required, silently does nothing. +

Index: src/main/resources/inspectionDescriptions/MixinConstructorDoesntExist.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinConstructorDoesntExist.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinConstructorDoesntExist.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,33 @@ +

-Reports when a mixin class is instantiated within that same class using a constructor which doesn't exist in the target class + Reports when a mixin class is instantiated within that same class using a constructor which doesn't exist in the target class +

+ +

+ A mixin class is merged into its target class, so calls to its constructors become calls to constructors of the target class. + If the target class has no constructor with matching parameters, this fails at runtime. For example, this applies to enum + constants added by an enum mixin. Add a @Shadow constructor matching a constructor in the target class. +

+

+enum Color {
+    RED(0xFF0000);
+    Color(int rgb) { ... }
+}
+
+@Mixin(Color.class)
+enum ColorMixin {
+    MYMOD_PURPLE; // bad (calls implicit shadow constructor, which doesn't exist in target class)
+}
+
+@Mixin(Color.class)
+enum ColorMixin {
+    MYMOD_PURPLE(0x800080); // good
+
+    @Shadow
+    ColorMixin(int rgb) {}
+}
+
Index: src/main/resources/inspectionDescriptions/MixinExtrasOperationParameters.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinExtrasOperationParameters.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinExtrasOperationParameters.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,23 @@ -Operation.call called with the wrong parameter types +

+ Operation.call called with the wrong parameter types +

+ +

+ Operation.call accepts any arguments at compile time, but at runtime they must match the parameters of the + wrapped operation: the receiver, if any, followed by the arguments of the original call. Otherwise the call fails with an + exception. +

+

+// target: this.setHealth(health);
+
+@WrapOperation(method = "foo", at = @At(value = "INVOKE", target = "..."))
+private void wrapSetHealth(Entity entity, float health, Operation<Void> original) {
+    original.call(health);         // bad
+    original.call(entity, health); // good
+}
+
Index: src/main/resources/inspectionDescriptions/MixinExtrasUnnecessaryMutableLocal.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinExtrasUnnecessaryMutableLocal.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinExtrasUnnecessaryMutableLocal.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,27 @@ +

-Unnecessary mutable reference to captured local + Unnecessary mutable reference to captured local +

+ +

+ Reports @Local parameters of a LocalRef type whose value is only read, never set. +

+

+ A mutable reference makes MixinExtras wrap the local in a reference object and write its value back afterwards. If the value + is never set, capture the local directly. +

+

+// bad
+private void onFoo(CallbackInfo ci, @Local LocalIntRef count) {
+    System.out.println(count.get());
+}
+
+// good
+private void onFoo(CallbackInfo ci, @Local int count) {
+    System.out.println(count);
+}
+
Index: src/main/resources/inspectionDescriptions/MixinInnerClass.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinInnerClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinInnerClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Reports invalid usages of inner classes in Mixins. + Reports invalid usages of inner classes in Mixins. +

+ +

+ A nested mixin is loaded independently, without an instance of the outer class, so it must be static. Mixin copies anonymous + classes declared in a mixin into the target, but doesn't support classes nested inside those anonymous classes. +

Index: src/main/resources/inspectionDescriptions/MixinSuperClass.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinSuperClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinSuperClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,30 @@ -Reports invalid @Mixin super classes. +

+ Reports invalid @Mixin super classes. +

+ +

+ The superclass of a mixin gives the mixin access to the members of a superclass of its target. It must therefore be a class in + the hierarchy of every target class, and can't be the target class itself. If the superclass is another mixin, it must target a + class in the hierarchy of every target class. +

+

+class Animal {}
+class Dog extends Animal {}
+
+@Mixin(Dog.class)
+class DogMixin extends Animal {}   // good
+
+@Mixin(Dog.class)
+class DogMixin extends AnimalMixin {} // good
+
+@Mixin(Dog.class)
+class DogMixin extends Dog {}      // bad: extends the target class
+
+@Mixin(Dog.class)
+class DogMixin extends Vehicle {}  // bad: not in Dog's hierarchy
+
Index: src/main/resources/inspectionDescriptions/MixinWrongClassType.html =================================================================== --- src/main/resources/inspectionDescriptions/MixinWrongClassType.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/MixinWrongClassType.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,23 @@ +

-Reports when a Mixin uses the wrong class type + Reports when a Mixin uses the wrong class type +

+ +

+ A mixin is merged into its target class, so it must be the same kind of type. Use an interface mixin to target interfaces, and + a class mixin to target classes. Enum mixins are only supported for enum targets. Classes can also be used to target enums and + records. +

+

+interface Target {}
+
+@Mixin(Target.class)
+abstract class TargetMixin {} // bad
+
+@Mixin(Target.class)
+interface TargetMixin {}      // good
+
Index: src/main/resources/inspectionDescriptions/ModifyVariableMayBeArgsOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/ModifyVariableMayBeArgsOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ModifyVariableMayBeArgsOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ -Checks that @ModifyVariable has argsOnly if it targets arguments, which improves performance of the mixin +

+ Checks that @ModifyVariable has argsOnly if it targets arguments, which improves performance of the mixin +

+ +

+ With argsOnly = true, Mixin only searches the method's parameters, so it doesn't have to analyze the local + variables of the target method. +

+

+// bad
+@ModifyVariable(method = "setName", at = @At("HEAD"))
+private String modifyName(String name) { ... }
+
+// good
+@ModifyVariable(method = "setName", at = @At("HEAD"), argsOnly = true)
+private String modifyName(String name) { ... }
+
Index: src/main/resources/inspectionDescriptions/ModifyVariableMayUseName.html =================================================================== --- src/main/resources/inspectionDescriptions/ModifyVariableMayUseName.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ModifyVariableMayUseName.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Reports @ModifyVariable injectors relying on index or ordinal that may use a name instead +

+ Reports @ModifyVariable injectors relying on index or ordinal that may use a name instead +

+ +

+ The index and ordinal of a local variable change whenever local variables are added to or removed from the target method, + while its name usually stays the same. Names are also easier to understand. This is only reported where local variable names + are available at runtime. +

+

+@ModifyVariable(method = "foo", at = @At("STORE"), ordinal = 1)       // bad
+@ModifyVariable(method = "foo", at = @At("STORE"), name = "count")    // good
+
Index: src/main/resources/inspectionDescriptions/ModsTomlValidation.html =================================================================== --- src/main/resources/inspectionDescriptions/ModsTomlValidation.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ModsTomlValidation.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ -Checks mods.toml files for errors +

+ Checks mods.toml files for errors +

+ +

+ Forge fails to load a mod whose mods.toml contains invalid values, such as an invalid mod ID or a value of the + wrong type. +

Index: src/main/resources/inspectionDescriptions/NameDoesntMatchTargetClass.html =================================================================== --- src/main/resources/inspectionDescriptions/NameDoesntMatchTargetClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/NameDoesntMatchTargetClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,24 @@ +

-Reports when a parameter name in a Mixin does not match the target class + Reports when a parameter name in a Mixin does not match the target class +

+ +

+ Naming parameters the same as the corresponding parameters or local variables in the target class makes the injector easier + to read. A mismatched name can also indicate that the parameter doesn't refer to the variable it was meant to. +

+

+// target: public void setHealth(float health)
+
+// bad
+@Inject(method = "setHealth", at = @At("HEAD"))
+private void onSetHealth(float value, CallbackInfo ci) { ... }
+
+// good
+@Inject(method = "setHealth", at = @At("HEAD"))
+private void onSetHealth(float health, CallbackInfo ci) { ... }
+
Index: src/main/resources/inspectionDescriptions/NestedClassSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/NestedClassSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/NestedClassSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ -Classes which are annotated with @SideOnly cannot contain any nested classes which are annotated with a different @SideOnly annotation. Since a class that is annotated with @SideOnly brings everything with it, @SideOnly annotated nested classes are usually useless. +

+ Reports nested classes annotated with a different @SideOnly side than their containing class. +

+ +

+ A class annotated with @SideOnly is removed together with everything in it on the other side, so a nested class + can't be for a different side. @SideOnly annotations on nested classes are therefore usually useless. +

+

+@SideOnly(Side.CLIENT)
+public class Renderer {
+    @SideOnly(Side.SERVER) // bad
+    public static class Data { ... }
+}
+
Index: src/main/resources/inspectionDescriptions/NewExpressionSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/NewExpressionSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/NewExpressionSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,26 @@ -A class that is annotated as @SideOnly(Side.CLIENT) or @SideOnly(Side.SERVER) cannot be used in classes or methods which are annotated differently, or not at all. Since the irrelevant code is removed when operating as a server or a client, common code cannot use @SideOnly annotated classes either. +

+ Reports instantiations of classes annotated with @SideOnly in classes or methods that are annotated differently, + or not at all. +

+ +

+ Code annotated with @SideOnly is removed when running on the other side, so code that runs on both sides can't + use it either. +

+

+// bad: runs on both sides
+public void update() {
+    new GuiMyScreen(); // GuiMyScreen is @SideOnly(Side.CLIENT)
+}
+
+// good
+@SideOnly(Side.CLIENT)
+public void update() {
+    new GuiMyScreen();
+}
+
Index: src/main/resources/inspectionDescriptions/NoTranslation.html =================================================================== --- src/main/resources/inspectionDescriptions/NoTranslation.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/NoTranslation.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Checks whether a translation key used in calls to StatCollector.translateToLocal(), StatCollector.translateToLocalFormatted() or I18n.format() exists. +

+ Reports translation keys that don't have a translation. +

+ +

+ If a translation key doesn't exist, Minecraft displays the key itself instead of a translated text. This is usually caused by + a typo in the key, or a translation missing from the language file. +

+

+// en_us.json: { "my_mod.greeting": "Hello!" }
+Component.translatable("my_mod.greting") // bad
+Component.translatable("my_mod.greeting") // good
+
Index: src/main/resources/inspectionDescriptions/NonJavaMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/NonJavaMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/NonJavaMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ +

-Mixins should be written in Java. See this Mixin issue + Mixins should be written in Java. See this Mixin issue +

+ +

+ Mixin works directly on the bytecode of mixin classes, and only supports the bytecode produced by the Java compiler. + Compilers for other languages, such as Kotlin, generate extra classes, synthetic members and calls to runtime helpers that + Mixin can't handle, which causes errors when the mixin is applied. +

Index: src/main/resources/inspectionDescriptions/OverwriteAuthorRequired.html =================================================================== --- src/main/resources/inspectionDescriptions/OverwriteAuthorRequired.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/OverwriteAuthorRequired.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,21 @@ +

-For maintainability reasons, the Sponge project requires @Overwrite methods to declare an @author JavaDoc tag. + For maintainability reasons, the Sponge project requires @Overwrite methods to declare an @author JavaDoc tag. +

+ +

+ An @Overwrite replaces the target method entirely and can conflict with other mods. The @author tag + says who to contact about the overwrite, and the @reason tag explains why it was needed. +

+

+/**
+ * @author MyMod
+ * @reason Fix the speed calculation for slow entities
+ */
+@Overwrite
+public float getSpeed() { ... }
+
Index: src/main/resources/inspectionDescriptions/OverwriteModifiers.html =================================================================== --- src/main/resources/inspectionDescriptions/OverwriteModifiers.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/OverwriteModifiers.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ +

-Validates the modifiers of @Overwrite methods + Validates the modifiers of @Overwrite methods +

+ +

+ An @Overwrite replaces the target method, so it must have the same modifiers, and can't have lower visibility + than the target method. Annotations of the target method, such as nullability annotations, should also be present on the + overwrite so it keeps the same contract. +

+

+// target: public static int getMaxCount()
+
+@Overwrite private int getMaxCount() { ... }       // bad
+@Overwrite public static int getMaxCount() { ... } // good
+
Index: src/main/resources/inspectionDescriptions/PluginYml.html =================================================================== --- src/main/resources/inspectionDescriptions/PluginYml.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/PluginYml.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Reports issues in Bukkit-like plugin.yml files + Reports issues in Bukkit-like plugin.yml files +

+ +

+ The server fails to load a plugin whose plugin.yml refers to a main class that doesn't exist or doesn't + implement the plugin interface. +

Index: src/main/resources/inspectionDescriptions/PublicStaticMixinMember.html =================================================================== --- src/main/resources/inspectionDescriptions/PublicStaticMixinMember.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/PublicStaticMixinMember.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,16 @@ +

-A mixin class does not exist at runtime, and thus having them not private does not make sense. Make the field/method private instead. + A mixin class does not exist at runtime, and thus having them not private does not make sense. Make the field/method private instead. +

+ +

+ Mixin rejects non-private static fields and methods when the mixin is applied. @Shadow, @Overwrite, + @Accessor and @Invoker members are allowed. +

+

+ To share static members, move them to a separate class that is not a mixin. +

Index: src/main/resources/inspectionDescriptions/ReferenceToMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/ReferenceToMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ReferenceToMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ +

-A Mixin class doesn't exist at runtime, and thus cannot be referenced directly. + A Mixin class doesn't exist at runtime, and thus cannot be referenced directly. +

+ +

+ Instead of the mixin class, use the target class, or an interface implemented by the mixin. +

+

+// bad
+((EntityMixin) (Object) entity).myMod$getData();
+
+// good: EntityMixin implements MyModEntity
+((MyModEntity) entity).myMod$getData();
+
+

+ Accessor mixins, and references from a mixin to its own super mixins, are allowed. +

Index: src/main/resources/inspectionDescriptions/ShadowFieldPrefix.html =================================================================== --- src/main/resources/inspectionDescriptions/ShadowFieldPrefix.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ShadowFieldPrefix.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ -Reports @Shadow fields with prefixes +

+ Reports @Shadow fields with prefixes +

+ +

+ The @Shadow prefix allows a shadow method to avoid a name clash with another method in the mixin. Mixin doesn't + support prefixes for fields, and fails when applying the mixin. +

+

+@Shadow private int shadow$count; // bad
+@Shadow(prefix = "myMod$") private int myMod$count; // bad
+@Shadow private int count;        // good
+
Index: src/main/resources/inspectionDescriptions/ShadowFinalModification.html =================================================================== --- src/main/resources/inspectionDescriptions/ShadowFinalModification.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ShadowFinalModification.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,6 @@ -@Final annotated fields cannot be modified, as the field it is targeting is final. This can be overridden with @Mutable. +@Final annotated fields cannot be modified, as the field it is targeting is final. This can be overridden with @Mutable. Index: src/main/resources/inspectionDescriptions/ShadowModifiers.html =================================================================== --- src/main/resources/inspectionDescriptions/ShadowModifiers.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ShadowModifiers.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ -Validates access modifiers of @Shadow members +

+ Validates access modifiers of @Shadow members +

+ +

+ A @Shadow member stands for a member of the target class, so its static modifier and access level + must match the target member. Shadows of final fields must be annotated with @Final, so that + assignments to them are reported. +

+

+// target: private static final Logger LOGGER
+
+@Shadow public static Logger LOGGER;               // bad
+@Shadow @Final private static Logger LOGGER;       // good
+
Index: src/main/resources/inspectionDescriptions/ShadowNameDoesntMatchTargetClass.html =================================================================== --- src/main/resources/inspectionDescriptions/ShadowNameDoesntMatchTargetClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ShadowNameDoesntMatchTargetClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ +

-Reports when a parameter name in a shadow method does not match the target method + Reports when a parameter name in a shadow method does not match the target method +

+ +

+ Naming the parameters the same as in the target method makes the shadow easier to read and compare with the target method. +

+

+// target: public void setHealth(float health)
+
+@Shadow public abstract void setHealth(float value);  // bad
+@Shadow public abstract void setHealth(float health); // good
+
Index: src/main/resources/inspectionDescriptions/ShareNonLocalRef.html =================================================================== --- src/main/resources/inspectionDescriptions/ShareNonLocalRef.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/ShareNonLocalRef.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ +

-Reports when a @Share parameter is of a non-LocalRef type + Reports when a @Share parameter is of a non-LocalRef type +

+ +

+ A @Share variable is shared by reference between injectors, so the parameter must be a LocalRef + type, such as LocalIntRef or LocalRef<String>. +

+

+@Share("count") int count         // bad
+@Share("count") LocalIntRef count // good
+
Index: src/main/resources/inspectionDescriptions/SoftImplementOverrides.html =================================================================== --- src/main/resources/inspectionDescriptions/SoftImplementOverrides.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SoftImplementOverrides.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ +

-Reports soft-implemented methods in Mixins that do not override a method in the target classes. + Reports soft-implemented methods in Mixins that do not override a method in the target classes. +

+ +

+ A method with an @Interface prefix is meant to implement a method of that interface once the prefix is removed. + If no such interface method exists, the method doesn't implement anything, which usually means there is a typo in its name or + parameter types. +

+

+@Mixin(Target.class)
+@Implements(@Interface(iface = Foo.class, prefix = "foo$"))
+public class TargetMixin {
+    // Foo has no method getNmae()
+    public String foo$getNmae() { ... }
+}
+
Index: src/main/resources/inspectionDescriptions/SpongeInjection.html =================================================================== --- src/main/resources/inspectionDescriptions/SpongeInjection.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SpongeInjection.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Invalid @Inject usage in Sponge plugin class. +

+ Invalid @Inject usage in Sponge plugin class. +

+ +

+ Sponge injects these values when it creates the plugin, and fails when an injection can't be satisfied. For example, + primitive types can't be injected, constructor injections can't be optional, and an injected Asset must be + annotated with an @AssetId that refers to an existing file. +

Index: src/main/resources/inspectionDescriptions/SpongeInvalidGetterTarget.html =================================================================== --- src/main/resources/inspectionDescriptions/SpongeInvalidGetterTarget.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SpongeInvalidGetterTarget.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ -@Getter must target a method accessible from the event class of this listener +

+ @Getter must target a method accessible from the event class of this listener +

+ +

+ Sponge fills in the parameter by calling the method named by @Getter on the event, so the method must exist on + the event class. +

+

+@Listener
+public void onJoin(ServerSideConnectionEvent.Join event, @Getter("getPlayer") ServerPlayer player) { ... } // good
+
Index: src/main/resources/inspectionDescriptions/SpongeLogging.html =================================================================== --- src/main/resources/inspectionDescriptions/SpongeLogging.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SpongeLogging.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Sponge provides a org.slf4j.Logger logger implementation using @Inject in your plugin class. You should not use java.util.logging.Logger in your plugin. +

+ Reports usages of java.util.logging.Logger in Sponge plugins. +

+ +

+ Sponge provides an org.slf4j.Logger for each plugin, which can be injected using @Inject in the plugin + class. It is set up by Sponge, so its messages are identified as coming from your plugin and are logged consistently with the + rest of the server. +

Index: src/main/resources/inspectionDescriptions/SpongePluginClass.html =================================================================== --- src/main/resources/inspectionDescriptions/SpongePluginClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SpongePluginClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Checks the plugin class is valid. +

+ Checks the Sponge plugin class is valid. +

+ +

+ Sponge creates an instance of the plugin class, so it must have either a non-private constructor without parameters, or a + single constructor annotated with @Inject. The plugin ID must also only contain lowercase letters, numbers, + dashes and underscores. +

Index: src/main/resources/inspectionDescriptions/SpongeWrongGetterType.html =================================================================== --- src/main/resources/inspectionDescriptions/SpongeWrongGetterType.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SpongeWrongGetterType.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ -@Getter requires the parameter's type to be assignable from the annotation's target method return type +

+ @Getter requires the parameter's type to be assignable from the annotation's target method return type +

+ +

+ Sponge passes the value returned by the method to the parameter, so the parameter's type must be able to hold it. +

+

+// event.getPlayer() returns ServerPlayer
+@Getter("getPlayer") String player      // bad
+@Getter("getPlayer") ServerPlayer player // good
+
Index: src/main/resources/inspectionDescriptions/StackEmpty.html =================================================================== --- src/main/resources/inspectionDescriptions/StackEmpty.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/StackEmpty.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ -Comparing an ItemStack to ItemStack.EMPTY to query stack emptiness can cause unwanted issues.When a stack in an inventory is shrunk, the instance is not replaced with ItemStack.EMPTY, but the stack should still be considered empty. Instead, isEmpty() should be called. +

+ Reports comparisons of an ItemStack with ItemStack.EMPTY. +

+ +

+ An empty stack isn't always the ItemStack.EMPTY instance. When a stack in an inventory is shrunk, the instance is + not replaced with ItemStack.EMPTY, but the stack should still be considered empty. +

+

+if (stack == ItemStack.EMPTY) { ... } // bad
+if (stack.isEmpty()) { ... }          // good
+
Index: src/main/resources/inspectionDescriptions/SuperfluousFormat.html =================================================================== --- src/main/resources/inspectionDescriptions/SuperfluousFormat.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SuperfluousFormat.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,17 @@ +

-Detect superfluous format arguments for translations + Detect superfluous format arguments for translations +

+ +

+ Arguments that the translation has no placeholders for are ignored, which usually means the wrong translation key was used or + the translation is missing a placeholder. +

+

+// "my_mod.greeting": "Hello!"
+Component.translatable("my_mod.greeting", name) // bad
+
Index: src/main/resources/inspectionDescriptions/SwitchOnEnumMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/SwitchOnEnumMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/SwitchOnEnumMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,26 @@ +

-Reports when a switch statement or expression is used on an enum mixin + Reports when a switch statement or expression is used on an enum mixin +

+ +

+ The Java compiler compiles a switch on an enum into code that uses the ordinals in the enum mixin directly. The ordinals + in the enum mixins don't match the ordinals as they will be once merged into the target class. Cast the value to the + target class instead. +

+

+@Mixin(Color.class)
+enum ColorMixin {
+    ;
+    int bad() {
+        switch (this) { ... }
+    }
+    int good() {
+        switch ((Color) (Object) this) { ... }
+    }
+}
+
Index: src/main/resources/inspectionDescriptions/UnknownCtNamespace.html =================================================================== --- src/main/resources/inspectionDescriptions/UnknownCtNamespace.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnknownCtNamespace.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,13 @@ +

-Reports an unknown namespace in a ClassTweaker header + Reports an unknown namespace in a ClassTweaker header +

+ +

+ The namespace says which mappings the entries are written in, so that they can be remapped. It must be one of the mapping + namespaces of the project, such as official or named. +

Index: src/main/resources/inspectionDescriptions/UnnecessaryQualifiedMemberReference.html =================================================================== --- src/main/resources/inspectionDescriptions/UnnecessaryQualifiedMemberReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnnecessaryQualifiedMemberReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,16 @@ -Reports unnecessary qualified member references in @Inject annotations +

+ Reports unnecessary qualified member references in method = "..." +

+ +

+ An injector always targets methods of the mixin's target classes, so the owner class in the reference is redundant. +

+

+@Inject(method = "Lcom/example/Foo;bar()V", ...) // bad
+@Inject(method = "bar()V", ...)                  // good
+
Index: src/main/resources/inspectionDescriptions/UnnecessaryUnsafe.html =================================================================== --- src/main/resources/inspectionDescriptions/UnnecessaryUnsafe.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnnecessaryUnsafe.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Reports unnecessary unsafe = true +

+ Reports unnecessary unsafe = true +

+ +

+ unsafe = true only relaxes restrictions on injecting into constructors, so it has no effect on injectors that + don't target a constructor. Fabric's version of Mixin doesn't have these restrictions at all, so by default + unsafe = true is always reported as unnecessary when Fabric Mixin is present. +

Index: src/main/resources/inspectionDescriptions/UnqualifiedMemberReference.html =================================================================== --- src/main/resources/inspectionDescriptions/UnqualifiedMemberReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnqualifiedMemberReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,16 @@ -Reports unqualified member references in @At targets +

+ Reports unqualified member references in @At targets +

+ +

+ @At requires member references to be qualified. +

+

+@At(value = "INVOKE", target = "bar")                     // bad
+@At(value = "INVOKE", target = "Lcom/example/Foo;bar()V") // good
+
Index: src/main/resources/inspectionDescriptions/UnresolvedConfigReference.html =================================================================== --- src/main/resources/inspectionDescriptions/UnresolvedConfigReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnresolvedConfigReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ +

-Reports unresolved references in Mixin configuration files. + Reports unresolved references in Mixin configuration files. +

+ +

+ Mixin fails to load mixins, packages or other classes listed in a configuration file that don't exist. +

Index: src/main/resources/inspectionDescriptions/UnresolvedFabricModJsonReference.html =================================================================== --- src/main/resources/inspectionDescriptions/UnresolvedFabricModJsonReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnresolvedFabricModJsonReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ -Reports unresolved references in Fabric mod JSON files. +

+ Reports unresolved references in fabric.mod.json files. +

+ +

+ Fabric Loader fails to load the mod if fabric.mod.json refers to classes or members that don't exist. +

Index: src/main/resources/inspectionDescriptions/UnresolvedLocalCapture.html =================================================================== --- src/main/resources/inspectionDescriptions/UnresolvedLocalCapture.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnresolvedLocalCapture.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,14 @@ -Verifies targets of @Local annotations +

+ Verifies targets of @Local annotations +

+ +

+ A @Local must match exactly one local variable at the injection point, otherwise MixinExtras fails to apply the + injector. When there are several locals of the same type, specify which one with name, ordinal or + index. +

Index: src/main/resources/inspectionDescriptions/UnresolvedMixinReference.html =================================================================== --- src/main/resources/inspectionDescriptions/UnresolvedMixinReference.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnresolvedMixinReference.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ +

-Reports unresolved references in Mixin annotations + Reports unresolved references in Mixin annotations +

+ +

+ Mixin fails to apply an annotation whose target doesn't exist. +

Index: src/main/resources/inspectionDescriptions/UnusedInjectLocalCapture.html =================================================================== --- src/main/resources/inspectionDescriptions/UnusedInjectLocalCapture.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnusedInjectLocalCapture.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,26 @@ -Reports when an @Inject local capture is unused +

+ Reports when an @Inject local capture is unused +

+ +

+ Local capture depends on the exact local variables of the target method, so it breaks easily when the target method changes. + If none of the captured locals are used, remove the capture. +

+

+// bad
+@Inject(method = "foo", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILHARD)
+private void onFoo(CallbackInfo ci, int count, String name) {
+    System.out.println("foo");
+}
+
+// good
+@Inject(method = "foo", at = @At("TAIL"))
+private void onFoo(CallbackInfo ci) {
+    System.out.println("foo");
+}
+
Index: src/main/resources/inspectionDescriptions/UnusedMixin.html =================================================================== --- src/main/resources/inspectionDescriptions/UnusedMixin.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnusedMixin.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,12 @@ +

-Ensures that all Mixin classes are referenced from a Mixin configuration + Ensures that all Mixin classes are referenced from a Mixin configuration +

+ +

+ Mixin only applies the mixins listed in a Mixin configuration file, so a mixin that isn't listed has no effect. +

Index: src/main/resources/inspectionDescriptions/UnusedShadowMethodPrefix.html =================================================================== --- src/main/resources/inspectionDescriptions/UnusedShadowMethodPrefix.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/UnusedShadowMethodPrefix.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,20 @@ -Reports unused prefixes of @Shadow methods +

+ Reports unused prefixes of @Shadow methods +

+ +

+ The prefix is only removed from method names that start with it, so a prefix that doesn't match the method's name has no + effect. +

+

+@Shadow(prefix = "shadow$")
+public abstract int getCount();        // bad
+
+@Shadow(prefix = "shadow$")
+public abstract int shadow$getCount(); // good
+
Index: src/main/resources/inspectionDescriptions/VariableUseSideOnly.html =================================================================== --- src/main/resources/inspectionDescriptions/VariableUseSideOnly.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/VariableUseSideOnly.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,28 @@ -Variables which are declared with a @SideOnly annotation can only be used in matching @SideOnly classes and methods. +

+ Reports uses of fields annotated with @SideOnly, or declared in a class annotated with @SideOnly, in + classes or methods that are annotated differently, or not at all. +

+ +

+ Code annotated with @SideOnly is removed when running on the other side, so code that uses it crashes there. +

+

+@SideOnly(Side.CLIENT)
+private int renderColor;
+
+// bad: runs on both sides
+public int getColor() {
+    return renderColor;
+}
+
+// good
+@SideOnly(Side.CLIENT)
+public int getColor() {
+    return renderColor;
+}
+
Index: src/main/resources/inspectionDescriptions/WrapWithConditionTargetsNonVoid.html =================================================================== --- src/main/resources/inspectionDescriptions/WrapWithConditionTargetsNonVoid.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/WrapWithConditionTargetsNonVoid.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,29 @@ -Reports when @WrapWithCondition targets a non-void instruction followed by a pop +

+ Reports when @WrapWithCondition targets a non-void instruction followed by a pop +

+ +

+ When the condition is false, @WrapWithCondition skips the call, so the call has no return value. This only works + because the return value is discarded, and breaks if the target method is changed to use it, including by another Mixin. + Use @WrapOperation instead, which can provide a return value. +

+

+// target: list.add(item);
+
+// bad
+@WrapWithCondition(method = "foo", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"))
+private boolean shouldAdd(List<Item> list, Item item) {
+    return item != null;
+}
+
+// good
+@WrapOperation(method = "foo", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"))
+private boolean add(List<Item> list, Item item, Operation<Boolean> original) {
+    return item != null && original.call(list, item);
+}
+
Index: src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass.html =================================================================== --- src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,23 @@ -Reports when the class passed to an entity data parameter definition is not the same as the containing entity class. +

+ Reports when the class passed to a synched entity data definition is not the same as the containing entity class. +

+ +

+ Synched entity data is numbered per entity class, based on how many parameters that class and its superclasses define. + Defining a data accessor with a different class can give it an ID that conflicts with another accessor, which breaks entity data + syncing. +

+

+public class MyEntity extends Entity {
+    // bad
+    private static final EntityDataAccessor<Integer> DATA = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.INT);
+
+    // good
+    private static final EntityDataAccessor<Integer> DATA = SynchedEntityData.defineId(MyEntity.class, EntityDataSerializers.INT);
+}
+
Index: src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass_zh.html =================================================================== --- src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass_zh.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/WrongEntityDataParameterClass_zh.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,22 @@ -当传递给实体数据参数定义的类与包含的实体类不一致时,会发出报告。 +

+ 当传递给 synched entity data 定义的类与包含的实体类不一致时,会发出报告。 +

+ +

+ Synched entity data 按实体类编号,编号取决于该类及其父类定义了多少个参数。使用其他类定义 data accessor 可能会使其 ID 与另一个 + data accessor 冲突,从而破坏 entity data 的同步。 +

+

+public class MyEntity extends Entity {
+    // 错误
+    private static final EntityDataAccessor<Integer> DATA = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.INT);
+
+    // 正确
+    private static final EntityDataAccessor<Integer> DATA = SynchedEntityData.defineId(MyEntity.class, EntityDataSerializers.INT);
+}
+
Index: src/main/resources/inspectionDescriptions/WrongTypeInTranslationArgs.html =================================================================== --- src/main/resources/inspectionDescriptions/WrongTypeInTranslationArgs.html (revision ea0444f7c6e18e3b6038b83b6030405830f0b7b9) +++ src/main/resources/inspectionDescriptions/WrongTypeInTranslationArgs.html (revision cf5ea915cc6073030781b3a37e698e8b65302537) @@ -20,6 +20,18 @@ +

-Detect wrong argument types in translation arguments + Detect wrong argument types in translation arguments +

+ +

+ Translation arguments must be a String, Number, Boolean or Component. + Minecraft can't serialize arguments of other types, which causes errors, for example when the component is sent over the + network. Convert such arguments to strings, or use Component.translatableEscape. +

+

+Component.translatable("my_mod.position", pos)                 // bad
+Component.translatable("my_mod.position", String.valueOf(pos)) // good
+