PMD自定义LongMethodName规则正则报错,求解决方案
自定义PMD LongMethodName规则正则报错问题解决
问题背景
为PMD编写了自定义LongMethodName规则,意图检查Java方法名的单词数量(按驼峰拆分)是否超过5个,但运行规则时出现正则解析报错,将正则替换为简单的"a"则可正常执行。
自定义规则XML代码
<?xml version="1.0"?> <ruleset name="Custom Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <rule name="LongMethodName" language="java" message="Method name too long" class="net.sourceforge.pmd.lang.rule.XPathRule"> <description> Method name should be composed by less that five words </description> <priority>4</priority> <properties> <property name="version" value="2.0" /> <property name="xpath"> <value> <![CDATA[ //MethodDeclaration[count(tokenize(@Name, '(?<=[a-z])(?=[A-Z])')) + 1 > 5] ]]> </value> </property> </properties> </rule> </ruleset>
报错信息
Nov 04, 2022 10:45:42 PM net.sourceforge.pmd.RuleSet apply WARNING: Exception applying rule LongMethodName on file /Users/francescobresciani/MSDE/1sem/software-design-modeling/sdem-ass2/fastjson-master/src/main/java/com/alibaba/fastjson/parser/SymbolTable.java, continuing with next rule java.lang.RuntimeException: net.sf.saxon.trans.XPathException: Error at character 1 in regular expression "(?<=[a-z])(?=[A-Z])": expected ()) at net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery.initializeXPathExpression(SaxonXPathRuleQuery.java:272) at net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery.evaluate(SaxonXPathRuleQuery.java:113) at net.sourceforge.pmd.lang.rule.XPathRule.evaluate(XPathRule.java:176) at net.sourceforge.pmd.lang.rule.XPathRule.apply(XPathRule.java:158) at net.sourceforge.pmd.RuleSet.apply(RuleSet.java:670) at net.sourceforge.pmd.RuleSets.apply(RuleSets.java:163) at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:209) at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:118) at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:100) at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:62) at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:89) at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:30) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: net.sf.saxon.trans.XPathException: Error at character 1 in regular expression "(?<=[a-z])(?=[A-Z])": expected ()) at net.sf.saxon.java.JRegularExpression.<init>(JRegularExpression.java:70) at net.sf.saxon.java.JavaPlatform.compileRegularExpression(JavaPlatform.java:198) at net.sf.saxon.functions.Matches.tryToCompile(Matches.java:218) at net.sf.saxon.functions.Tokenize.maybePrecompile(Tokenize.java:45) at net.sf.saxon.functions.Tokenize.simplify(Tokenize.java:36) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.FunctionCall.simplifyArguments(FunctionCall.java:100) at net.sf.saxon.expr.FunctionCall.simplify(FunctionCall.java:88) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.BinaryExpression.simplify(BinaryExpression.java:45) at net.sf.saxon.expr.ArithmeticExpression.simplify(ArithmeticExpression.java:42) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.BinaryExpression.simplify(BinaryExpression.java:45) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.FilterExpression.simplify(FilterExpression.java:130) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.SlashExpression.simplify(SlashExpression.java:122) at net.sf.saxon.expr.ExpressionVisitor.simplify(ExpressionVisitor.java:159) at net.sf.saxon.expr.ExpressionTool.make(ExpressionTool.java:74) at net.sf.saxon.sxpath.XPathEvaluator.createExpression(XPathEvaluator.java:167) at net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery.initializeXPathExpression(SaxonXPathRuleQuery.java:269)
测试情况
- 在regex101测试正则
(?<=[a-z])(?=[A-Z])有效 - 部分XPath测试工具验证表达式有效,但freeformatter提示:
Unable to perform XPath operation. Syntax error at char 1 in regular expression: No expression before quantifier
报错原因
PMD依赖的Saxon XPath引擎使用Java原生正则库解析表达式,而Java的Pattern类在兼容模式下不支持反向预查断言(?<=...),这直接导致正则解析失败,抛出报错。
解决方案
替换为Java正则支持的正向预查语法,调整XPath表达式实现驼峰单词统计:
修正后的XPath表达式
//MethodDeclaration[count(tokenize(@Name, '(?=[A-Z])')) + 1 > 5]
或更简洁的写法(直接判断拆分后的片段数,每个大写字母对应一个新单词的开头):
//MethodDeclaration[count(tokenize(@Name, '(?=[A-Z])')) > 4]
完整修正后的规则XML
<?xml version="1.0"?> <ruleset name="Custom Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <rule name="LongMethodName" language="java" message="Method name too long" class="net.sourceforge.pmd.lang.rule.XPathRule"> <description> Method name should be composed by less than five words </description> <priority>4</priority> <properties> <property name="version" value="2.0" /> <property name="xpath"> <value> <![CDATA[ //MethodDeclaration[count(tokenize(@Name, '(?=[A-Z])')) + 1 > 5] ]]> </value> </property> </properties> </rule> </ruleset>
原理说明
(?=[A-Z])是Java正则支持的零宽正向预查,匹配大写字母的位置,用它拆分驼峰命名时,thisMethodName会被拆分为this,Method,Name- 拆分后的片段数量加1就是实际单词数(比如拆分出2个片段,对应3个单词),因此判断
count(...) +1 >5即可筛选出单词数超过5的方法名
内容的提问来源于stack exchange,提问作者fbee
相关产品推荐
相关产品推荐

