Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 10 additions & 57 deletions .github/scripts/generate-quality-report.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def parse_pmd() -> Optional[AnalysisReport]:
root = ET.parse(report_path).getroot()
except ET.ParseError:
# Unreadable is not clean. Reported as a finding of its own so the
# forbidden-rule gate below cannot mistake it for an empty report.
# PMD gate below cannot mistake it for an empty report.
findings.append(Finding(severity="P1", location=str(report_path),
message="PMD report could not be parsed",
rule="UnparseableReport"))
Expand Down Expand Up @@ -950,62 +950,15 @@ def main() -> None:

pmd = parse_pmd()
if pmd:
forbidden_pmd_rules = {
"UnparseableReport",
"ClassWithOnlyPrivateConstructorsShouldBeFinal",
"CompareObjectsWithEquals",
"FormalParameterNamingConventions",
"LiteralsFirstInComparisons",
"LocalVariableNamingConventions",
"MethodNamingConventions",
"OneDeclarationPerLine",
"MissingOverride",
"ControlStatementBraces",
"UnnecessaryFullyQualifiedName",
"UseCollectionIsEmpty",
"UnusedPrivateField",
"UnnecessaryLocalBeforeReturn",
"PreserveStackTrace",
"CloseResource",
"AssignmentInOperand",
"EmptyControlStatement",
"ForLoopCanBeForeach",
"UnusedFormalParameter",
"SwitchStmtsShouldHaveDefault",
"UnconditionalIfStatement",
"SimplifyConditional",
"UselessOverridingMethod",
"UselessQualifiedThis",
"AvoidBranchingStatementAsLastInLoop",
"AvoidUsingVolatile",
"DoNotCallGarbageCollectionExplicitly",
"SuspiciousEqualsMethodName",
"UseUtilityClass",
"EmptyCatchBlock",
"SimplifyBooleanReturns",
"UnusedLocalVariable",
"UnnecessaryConstructor",
"UnnecessaryImport",
"AvoidStringBufferField",
"LogicInversion",
"DefaultLabelNotLastInSwitchStmt",
"SingularField",
"InstantiationToGetClass",
"UnnecessaryModifier",
"FinalFieldCouldBeStatic",
"AvoidInstanceofChecksInCatchClause",
"AvoidUsingOctalValues",
"NonThreadSafeSingleton",
"ComparisonWithNaN",
"DontUseFloatTypeForLoopIndices",
"MissingStaticMethodInNonInstantiatableClass",
"OverrideBothEqualsAndHashcode",
"UnnecessarySemicolon"
}
violations = [f for f in pmd.findings if f.rule in forbidden_pmd_rules]
if violations:
print("\n❌ Build failed due to forbidden PMD violations:")
for v in violations:
# Zero findings, like SpotBugs: ANY rule fails the build. This used to
# be a list of forbidden rules, and a rule nobody had listed
# (AvoidUsingHardCodedIP) reached master while the report showed it
# in red. An intentional exception is a `//NOPMD <Rule> - <reason>`
# on the line, never a gap in this gate. UnparseableReport is a
# finding too, so a corrupt report also fails here.
if pmd.findings:
print("\n\u274c Build failed due to PMD findings (the gate is zero findings):")
for v in pmd.findings:
print(f" - {v.rule}: {v.location} - {v.message}")
sys.exit(1)

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ jobs:
# SpotBugs run that silently stops running is not mistaken for clean.
QUALITY_REPORT_REQUIRED_SPOTBUGS: core-unittests:android:ios:ByteCodeTranslator:codenameone-maven-plugin:build-engine:project-model:codenameone-gradle-plugin:build-hint-catalog:build-hint-tools:backend:backend-test:javac:android-res-compiler:android-compat:swing-compat:javafx-compat:fxml-compiler:desktop-compat-runtime
# PMD and Checkstyle run on the core and on the backend runtime, and each
# must produce its report: without one, the forbidden-rule gate below it
# must produce its report: without one, the PMD gate below it
# has nothing to read and passes.
QUALITY_REPORT_REQUIRED_PMD: core-unittests:backend:backend-test:backend-parparvm
QUALITY_REPORT_REQUIRED_CHECKSTYLE: core-unittests:backend:backend-test
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/unity-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ name: Unity compatibility
# built by the application's own Maven build and
# staged for every target, then converted to
# Gradle and staged again: the same entries
# static analysis SpotBugs (zero findings), PMD (the forbidden
# list) and Checkstyle (zero errors) over both
# static analysis SpotBugs (zero findings), PMD (zero
# findings) and Checkstyle (zero errors) over both
# modules, enforced by the script PR CI uses for
# the rest of the tree, and the ClassCastException
# gate over the runtime, which ParparVM translates
Expand Down Expand Up @@ -180,7 +180,7 @@ jobs:
rm -f cil-translator/target/spotbugsXml.xml cil-translator/target/pmd.xml cil-translator/target/checkstyle-result.xml
rm -f unity-compat/target/spotbugsXml.xml unity-compat/target/pmd.xml unity-compat/target/checkstyle-result.xml
xvfb-run -a mvn -B -Plocal-dev-javase -Dunity-compat -DskipTests -pl cil-translator,unity-compat verify
# The same script, with the same forbidden PMD list, that judges the rest
# The same script, with the same zero-findings PMD gate, that judges the rest
# of the tree in pr.yml. PMD only writes a report, so this step is what
# enforces it; every module named must have produced each report.
- name: Enforce the quality gates
Expand Down
17 changes: 11 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ any of them cannot skip it.
`maven/cil-translator/`, `maven/unity-compat/`), scoped to the
class or method it applies to and with a comment explaining why. Keep the
generated report at zero rather than tolerating known noise.
- PMD and Checkstyle still gate on their own lists in the same script.
- **PMD is a zero-findings gate too**, in the same script: any rule fails the
build. An intentional exception is a `//NOPMD <Rule> - <reason>` on the line.
It once gated a fixed list of rules, and an unlisted one reached master.
- Checkstyle gates on errors in the same script.

To reproduce the SpotBugs gate locally:

Expand Down Expand Up @@ -443,12 +446,11 @@ The backend is held to the client's gates:
- `///` markdown comments only, no `package.html`
(`validate-java25-markdown-docs.sh` covers `vm/backend/src` and `impl/`);
`package-info.java` per package; no since markers.
- SpotBugs (zero findings, `maven/backend/spotbugs-exclude.xml`), PMD (the core's
forbidden list) and Checkstyle (zero errors) run in `maven/backend`'s `verify`,
- SpotBugs (zero findings, `maven/backend/spotbugs-exclude.xml`), PMD (zero findings) and Checkstyle (zero errors) run in `maven/backend`'s `verify`,
using the core's own `pmd.xml` and `checkstyle.xml`. Only two of the three
fail a local build: SpotBugs, and Checkstyle through its `check` execution
(`checkstyle-zero-errors`). **PMD writes `maven/backend/target/pmd.xml` and
judges nothing** -- its forbidden list lives in
judges nothing** -- the zero-findings gate lives in
`.github/scripts/generate-quality-report.py`, which PR CI runs afterwards. A
green `mvn verify` therefore says nothing about PMD: read the report. Both
reports once sat at seven and fifteen findings behind green local builds.
Expand Down Expand Up @@ -908,8 +910,11 @@ exceptions, so that the next finding is fixed rather than added to them:
`get_position`, `GetComponent` and `Item1` are what translated bytecode
calls, so SpotBugs' `NM_*` patterns are excluded for
`com.codename1.unitycompat.{system,unityengine}` and each class with such
members carries `@SuppressWarnings("PMD.MethodNamingConventions")`. A new
runtime class needs that annotation; a Java-named helper never does.
members carries `@SuppressWarnings("PMD.MethodNamingConventions")` -- plus
`"PMD.ClassNamingConventions"` when the type itself has a C# name
(`Action_1`, `Int32_`, `TMP_Text`). A new runtime class needs that
annotation; a Java-named helper never does. An `extends Object` in
`unityengine` is `UnityEngine.Object`, so it carries `// NOPMD ExtendsObject`.
- **The translated classes are left out of SpotBugs by name**
(`UnityEngine.*`): they are generated bytecode in `target/classes` with no
Java source. PMD and Checkstyle read sources and never see them. A finding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
/// class, which `$sameType` cannot tell apart; the closed type a delegate
/// was made as travels in `Delegate.$typeKey`, and `Delegate.Combine`
/// compares that as well.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Action_1 extends MulticastDelegate {
public abstract void Invoke(Object a0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Action`2`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Action_2 extends MulticastDelegate {
public abstract void Invoke(Object a0, Object a1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Action`3`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Action_3 extends MulticastDelegate {
public abstract void Invoke(Object a0, Object a1, Object a2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// The members of `System.Array`, for the arrays that have no class of
/// their own to put them on: a JVM array for one dimension, an [MdArray]
/// for more. Which one an `Array` is, is only known when it gets here.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Array_ {
private Array_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Boolean`, which is the primitive `boolean` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Boolean_ {
private Boolean_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Byte`. A C# `byte` is unsigned and a JVM `byte`
/// is not, so the value is masked on the way in.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Byte_ {
private Byte_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Char`, which is the primitive `char` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Char_ {
private Char_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Double`, which is the primitive `double` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Double_ {
private Double_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
/// so is this: `IsDefined`, `HasFlag` and `CompareTo` refuse a value of
/// another enum, where `GetName` reads the number out of any enum or
/// integer.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Enum_ {
private Enum_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
package com.codename1.unitycompat.system;

/// ``System.EventHandler`1``. The event argument is erased.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class EventHandler_1 extends MulticastDelegate {
public abstract void Invoke(Object sender, Object e);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Func`1`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Func_1 extends MulticastDelegate {
public abstract Object Invoke();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Func`2`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Func_2 extends MulticastDelegate {
public abstract Object Invoke(Object a0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Func`3`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Func_3 extends MulticastDelegate {
public abstract Object Invoke(Object a0, Object a1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// `System.Func`4`. Type arguments are erased, so every parameter is an
/// `Object` and the generated subclass unboxes what the method it calls
/// takes as a primitive.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public abstract class Func_4 extends MulticastDelegate {
public abstract Object Invoke(Object a0, Object a1, Object a2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
package com.codename1.unitycompat.system;

/// `System.IComparable<T>`.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public interface IComparable_1 {
int CompareTo(Object other);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
package com.codename1.unitycompat.system;

/// `System.IEquatable<T>`.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public interface IEquatable_1 {
boolean Equals(Object other);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Int16`, which is the primitive `short` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Int16_ {
private Int16_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Int32`, which is the primitive `int` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Int32_ {
private Int32_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Int64`, which is the primitive `long` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Int64_ {
private Int64_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@
package com.codename1.unitycompat.system;

/// `System.MulticastDelegate`: the base of every delegate type.
@SuppressWarnings("PMD.AbstractClassWithoutAnyMethod") // abstract in C#, so never instantiated here either
public abstract class MulticastDelegate extends Delegate {
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/// class written in C# overrides `ToString`, `Equals` and `GetHashCode` as
/// the Java methods of the same meaning, so these mostly forward; what they
/// add is the .NET spelling of the values that are JDK boxes.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Object_ {
private Object_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.SByte`, which is the primitive `byte` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class SByte_ {
private SByte_() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/// The methods of `System.Single`, which is the primitive `float` here and so
/// cannot carry them itself. The value comes first.
@SuppressWarnings("PMD.MethodNamingConventions") // C# member names: translated code binds to them by name
@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.ClassNamingConventions"}) // C# member and type names: translated code binds to them by name
public final class Single_ {
private Single_() {
}
Expand Down
Loading
Loading