Skip to content

Commit 5d66743

Browse files
committed
feat(errorprone): enforce no-java.lang.Math rule at compile time
Replace the regex-based .github/workflows/math-check.yml scan with a custom ErrorProne BugChecker (ForbidJavaLangMath) in the :errorprone module. It resolves symbols on the type-attributed AST, so it catches every usage form (direct, fully-qualified, statically-imported, method references, field access) with no string/comment false positives. java.lang.StrictMath remains allowed. - add ForbidJavaLangMath BugChecker (auto-registered via @autoservice) - enable -Xep:ForbidJavaLangMath:ERROR in build.gradle - exempt the canonical x86 MathWrapper via @SuppressWarnings - delete .github/workflows/math-check.yml Note: ErrorProne runs only on JDK 11+, so the rule is no longer enforced on the JDK 8 (x86_64) build path.
1 parent 4b5d37d commit 5d66743

4 files changed

Lines changed: 108 additions & 93 deletions

File tree

.github/workflows/math-check.yml

Lines changed: 0 additions & 93 deletions
This file was deleted.

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ subprojects {
130130
errorproneArgs.addAll([
131131
'-Xep:StringCaseLocaleUsage:ERROR',
132132
'-Xep:StringCaseLocaleUsageMethodRef:ERROR',
133+
'-Xep:ForbidJavaLangMath:ERROR',
133134
])
134135
}
135136
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package errorprone;
2+
3+
import com.google.auto.service.AutoService;
4+
import com.google.errorprone.BugPattern;
5+
import com.google.errorprone.VisitorState;
6+
import com.google.errorprone.bugpatterns.BugChecker;
7+
import com.google.errorprone.matchers.Description;
8+
import com.google.errorprone.util.ASTHelpers;
9+
import com.sun.source.tree.IdentifierTree;
10+
import com.sun.source.tree.MemberReferenceTree;
11+
import com.sun.source.tree.MemberSelectTree;
12+
import com.sun.source.tree.MethodInvocationTree;
13+
import com.sun.source.tree.Tree;
14+
import com.sun.tools.javac.code.Symbol;
15+
16+
/**
17+
* Forbids any direct use of {@code java.lang.Math}.
18+
*
19+
* <p>{@code java.lang.Math} permits the JIT to use platform-specific intrinsics for some
20+
* methods (notably the transcendental and floating-point ones), so results are not
21+
* guaranteed to be bit-for-bit identical across CPUs / JVMs. In a consensus system that
22+
* non-determinism can fork the chain. All math must therefore go through
23+
* {@code org.tron.common.math.StrictMathWrapper}, which is backed by {@code java.lang.StrictMath}
24+
* and produces reproducible results.
25+
*
26+
* <p>This checker replaces the previous regex-based {@code .github/workflows/math-check.yml}
27+
* scan. It resolves symbols on the type-attributed AST, so it has no false positives from
28+
* strings/comments or unrelated classes named {@code Math}, and it catches every usage form:
29+
* <ul>
30+
* <li>direct calls: {@code Math.max(a, b)}</li>
31+
* <li>fully-qualified calls: {@code java.lang.Math.max(a, b)}</li>
32+
* <li>statically-imported calls: {@code import static java.lang.Math.max; ... max(a, b)}</li>
33+
* <li>method references: {@code Math::max}</li>
34+
* <li>field access: {@code Math.PI}, {@code Math.E}</li>
35+
* <li>statically-imported fields used bare: {@code import static java.lang.Math.PI; ... PI}</li>
36+
* </ul>
37+
*
38+
* <p>{@code java.lang.StrictMath} itself is intentionally allowed — it is the deterministic
39+
* primitive that {@code StrictMathWrapper} and the deprecated {@code MathWrapper} are built on.
40+
* Those wrappers legitimately call {@code java.lang.Math}/{@code StrictMath} and are exempted via
41+
* {@code @SuppressWarnings("ForbidJavaLangMath")}.
42+
*/
43+
@AutoService(BugChecker.class)
44+
@BugPattern(
45+
name = "ForbidJavaLangMath",
46+
summary = "Direct use of java.lang.Math is forbidden: its results are not guaranteed to be "
47+
+ "bit-for-bit identical across platforms, which can break consensus. Use "
48+
+ "org.tron.common.math.StrictMathWrapper instead.",
49+
severity = BugPattern.SeverityLevel.ERROR
50+
)
51+
public class ForbidJavaLangMath extends BugChecker
52+
implements BugChecker.MethodInvocationTreeMatcher,
53+
BugChecker.MemberReferenceTreeMatcher,
54+
BugChecker.MemberSelectTreeMatcher,
55+
BugChecker.IdentifierTreeMatcher {
56+
57+
private static final String JAVA_LANG_MATH = "java.lang.Math";
58+
59+
@Override
60+
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
61+
return flagIfMathMember(ASTHelpers.getSymbol(tree), tree);
62+
}
63+
64+
@Override
65+
public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
66+
return flagIfMathMember(ASTHelpers.getSymbol(tree), tree);
67+
}
68+
69+
@Override
70+
public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
71+
// Method selects (Math.max) are already reported via matchMethodInvocation /
72+
// matchMemberReference; only flag field selects (Math.PI, Math.E) here to avoid
73+
// double-reporting the same usage.
74+
Symbol sym = ASTHelpers.getSymbol(tree);
75+
if (!(sym instanceof Symbol.VarSymbol)) {
76+
return Description.NO_MATCH;
77+
}
78+
return flagIfMathMember(sym, tree);
79+
}
80+
81+
@Override
82+
public Description matchIdentifier(IdentifierTree tree, VisitorState state) {
83+
// Catches statically-imported constants referenced bare (e.g. `import static
84+
// java.lang.Math.PI; ... double c = PI;`). Statically-imported *methods* are caught by
85+
// matchMethodInvocation, so restrict this to fields to avoid double-reporting.
86+
Symbol sym = ASTHelpers.getSymbol(tree);
87+
if (!(sym instanceof Symbol.VarSymbol)) {
88+
return Description.NO_MATCH;
89+
}
90+
return flagIfMathMember(sym, tree);
91+
}
92+
93+
private Description flagIfMathMember(Symbol sym, Tree tree) {
94+
if (sym == null) {
95+
return Description.NO_MATCH;
96+
}
97+
Symbol.ClassSymbol enclosingClass = ASTHelpers.enclosingClass(sym);
98+
if (enclosingClass == null) {
99+
return Description.NO_MATCH;
100+
}
101+
if (enclosingClass.getQualifiedName().contentEquals(JAVA_LANG_MATH)) {
102+
return describeMatch(tree);
103+
}
104+
return Description.NO_MATCH;
105+
}
106+
}

platform/src/main/java/x86/org/tron/common/math/MathWrapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* especially for floating-point calculations.
77
*/
88
@Deprecated
9+
@SuppressWarnings("ForbidJavaLangMath") // canonical wrapper: deliberately delegates to java.lang.Math
910
public class MathWrapper {
1011

1112
public static double pow(double a, double b) {

0 commit comments

Comments
 (0)