You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 12:21:13