From c6b7b0ea27b590df861b2278e594e353b0d43edc Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Wed, 19 Oct 2022 20:53:31 -0700 Subject: [PATCH 01/24] Start of transition to ANTLR4 --- bom/pom.xml | 6 +- core/pom.xml | 29 +--- .../model/labels/LabelExpressionLexer.g4 | 39 +++++ .../model/labels/LabelExpressionParser.g4 | 58 +++++++ .../antlr4/hudson/scheduler/CrontabLexer.g4 | 25 +++ .../antlr4/hudson/scheduler/CrontabParser.g4 | 89 ++++++++++ core/src/main/grammar/crontab.g | 164 ------------------ core/src/main/grammar/labelExpr.g | 133 -------------- .../java/hudson/model/AbstractProject.java | 12 +- core/src/main/java/hudson/model/Label.java | 12 +- .../hudson/model/labels/LabelExpression.java | 9 +- .../java/hudson/scheduler/BaseParser.java | 50 ++---- .../main/java/hudson/scheduler/CronTab.java | 25 +-- .../java/hudson/scheduler/CronTabList.java | 17 +- .../SimpleScheduledRetentionStrategy.java | 8 +- .../main/java/hudson/triggers/SCMTrigger.java | 6 +- .../java/hudson/triggers/TimerTrigger.java | 6 +- .../main/java/hudson/triggers/Trigger.java | 8 +- core/src/main/java/jenkins/model/Jenkins.java | 11 +- .../scheduler/CronTabEventualityTest.java | 22 +-- .../java/hudson/scheduler/CronTabTest.java | 14 +- .../hudson/triggers/TimerTriggerTest.java | 4 +- pom.xml | 14 +- .../test/java/hudson/model/ProjectTest.java | 4 +- .../model/labels/LabelExpressionTest.java | 6 +- .../java/hudson/triggers/TriggerTest.java | 4 +- .../java/jenkins/triggers/TriggerTest.java | 4 +- war/pom.xml | 34 ---- 28 files changed, 335 insertions(+), 478 deletions(-) create mode 100644 core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 create mode 100644 core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 create mode 100644 core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 create mode 100644 core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 delete mode 100644 core/src/main/grammar/crontab.g delete mode 100644 core/src/main/grammar/labelExpr.g diff --git a/bom/pom.xml b/bom/pom.xml index 961dd434c7e7..ef53cfe010b9 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -69,9 +69,9 @@ THE SOFTWARE. import - antlr - antlr - 2.7.7 + org.antlr + antlr4 + 4.11.1 diff --git a/core/pom.xml b/core/pom.xml index 69b6a167f479..4b9015b314cd 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -68,8 +68,8 @@ THE SOFTWARE. remoting - antlr - antlr + org.antlr + antlr4 args4j @@ -571,7 +571,8 @@ THE SOFTWARE. generate-sources - ${project.build.directory}/generated-sources/antlr + ${project.build.directory}/generated-sources/antlr4/hudson/model/labels + ${project.build.directory}/generated-sources/antlr4/hudson/scheduler ${project.build.directory}/generated-sources/localizer ${project.build.directory}/generated-sources/taglib-interface @@ -632,29 +633,15 @@ THE SOFTWARE. - org.codehaus.mojo - antlr-maven-plugin + org.antlr + antlr4-maven-plugin - cron + antlr - generate + antlr4 - - ${basedir}/src/main/grammar - crontab.g - - - - labelExpr - - generate - - - ${basedir}/src/main/grammar - labelExpr.g - diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 new file mode 100644 index 000000000000..e825199cede2 --- /dev/null +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 @@ -0,0 +1,39 @@ +lexer grammar LabelExpressionLexer; + +AND: '&&'; +OR: '||'; +NOT: '!'; +IMPLIES:'->'; +IFF: '<->'; +LPAREN: '('; +RPAREN: ')'; + +IDENTIFIER_PART + : ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-' ) + ; + +ATOM +/* + the real check of valid identifier happens in LabelAtom.get() + + https://www.antlr2.org/doc/lexer.html#usingexplicit + If we are seeing currently a '-', we check that the next char is not a '>' which will be a IMPLIES. + Otherwise the ATOM and the IMPLIES will collide and expr like a->b will just be parsed as ATOM (without spaces) +*/ + : ( + { _input.LA(2) != '>' }? '-' + | IDENTIFIER_PART + )+ + ; + +WS + : (' '|'\t')+ -> skip + ; + +STRINGLITERAL + : '"' + ( '\\' ( 'b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\' ) /* escape */ + | ~( '\\' | '"' | '\r' | '\n' ) + )* + '"' + ; \ No newline at end of file diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 new file mode 100644 index 000000000000..dcdf1daec787 --- /dev/null +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 @@ -0,0 +1,58 @@ +parser grammar LabelExpressionParser; + +@header { +import hudson.model.Label; +} + +options { + tokenVocab = LabelExpressionLexer; +} + +// order of precedence is as per http://en.wikipedia.org/wiki/Logical_connective#Order_of_precedence + +expr +returns [Label l] + : term1 EOF { $l=$term1.ctx.l; } + ; + +term1 +returns [Label l] +locals [Label r] + : term2 { $l=$term2.ctx.l; } ( IFF term2 { $r=$term2.ctx.l; $l=$l.iff($r); } )* + ; + +// (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining +term2 +returns [Label l] +locals [Label r] + : term3 { $l=$term3.ctx.l; } ( IMPLIES term3 { $r=$term3.ctx.l; $l=$l.implies($r); } )? + ; + +term3 +returns [Label l] +locals [Label r] + : term4 { $l=$term4.ctx.l; } ( OR term4 { $r=$term4.ctx.l; $l=$l.or($r); } )* + ; + +term4 +returns [Label l] +locals [Label r] + : term5 { $l=$term5.ctx.l; } ( AND term5 { $r=$term5.ctx.l; $l=$l.and($r); } )* + ; + +term5 +returns [Label l] + : term6 { $l=$term6.ctx.l; } + | NOT term6 + { $l=$term6.ctx.l; $l=$l.not(); } + ; + +term6 +returns [Label l] + : LPAREN term1 RPAREN + { $l=$term1.ctx.l ; $l=$l.paren(); } + | ATOM + { $l=LabelAtom.get($ATOM.getText()); } + | STRINGLITERAL + { $l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote($STRINGLITERAL.getText())); } + ; \ No newline at end of file diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 new file mode 100644 index 000000000000..9122a8e04e80 --- /dev/null +++ b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 @@ -0,0 +1,25 @@ +lexer grammar CrontabLexer; + +TOKEN : ('0'..'9')+ + ; + +WS : (' '|'\t')+ + ; + +MINUS: '-'; +STAR: '*'; +DIV: '/'; +OR: ','; +AT: '@'; +H: 'H'; +LPAREN: '('; +RPAREN: ')'; +COMMA: ','; + +YEARLY: 'yearly'; +ANNUALLY: 'annually'; +MONTHLY: 'monthly'; +WEEKLY: 'weekly'; +DAILY: 'daily'; +MIDNIGHT: 'midnight'; +HOURLY: 'hourly'; diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 new file mode 100644 index 000000000000..5f71efdde28c --- /dev/null +++ b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 @@ -0,0 +1,89 @@ +parser grammar CrontabParser; + +options { + tokenVocab = CrontabLexer; + superClass = BaseParser; +} + +startRule [CronTab table] + : expr[0] { $table.bits[0]=$expr.ctx.bits; } WS expr[1] { $table.bits[1]=$expr.ctx.bits; } WS expr[2] { $table.bits[2]=$expr.ctx.bits; } WS expr[3] { $table.bits[3]=$expr.ctx.bits; } WS expr[4] { $table.dayOfWeek=(int)$expr.ctx.bits; } EOF + | ( AT + ( + 'yearly' + { + $table.set("H H H H *",getHashForTokens()); + } + | 'annually' + { + $table.set("H H H H *",getHashForTokens()); + } + | 'monthly' + { + $table.set("H H H * *",getHashForTokens()); + } + | 'weekly' + { + $table.set("H H * * H",getHashForTokens()); + } + | 'daily' + { + $table.set("H H * * *",getHashForTokens()); + } + | 'midnight' + { + $table.set("H H(0-2) * * *",getHashForTokens()); + } + | 'hourly' + { + $table.set("H * * * *",getHashForTokens()); + } + ) + ) + ; + + +expr [int field] +returns [long bits=0] +locals[long lhs, long rhs=0] + : term[field] { $lhs = $term.ctx.bits; } ( COMMA expr[field] { $rhs = $expr.ctx.bits; } )? + { + $bits = $lhs|$rhs; + } + ; + +term [int field] +returns [long bits=0] +locals [int d=NO_STEP, int s, int e, int t] + : token { $s=$token.ctx.value; } MINUS token { $e=$token.ctx.value; } ( DIV token { $d=$token.ctx.value; } )? + { + $bits = doRange($s,$e,$d,$field); + } + | token + { + rangeCheck($token.ctx.value,$field); + $bits = 1L<<$token.ctx.value; + } + | STAR ( DIV token { $d=$token.ctx.value; } )? + { + $bits = doRange($d,$field); + } + | 'H' LPAREN token { $s=$token.ctx.value; } MINUS token { $e=$token.ctx.value; } RPAREN ( DIV token { $d=$token.ctx.value; } )? + { + $bits = doHash($s,$e,$d,$field); + } + | 'H' ( DIV token { $d=$token.ctx.value; })? + { + $bits = doHash($d,$field); + } + ; + + token + returns [int value=0] + : TOKEN + { + $value = Integer.parseInt($TOKEN.getText()); + } + ; + + + diff --git a/core/src/main/grammar/crontab.g b/core/src/main/grammar/crontab.g deleted file mode 100644 index 4ace2d3adf9c..000000000000 --- a/core/src/main/grammar/crontab.g +++ /dev/null @@ -1,164 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -header { - package hudson.scheduler; -} - -class CrontabParser extends Parser("BaseParser"); -options { - defaultErrorHandler=false; -} - -startRule [CronTab table] -throws ANTLRException -{ - long m,h,d,mnth,dow; -} - : m=expr[0] WS h=expr[1] WS d=expr[2] WS mnth=expr[3] WS dow=expr[4] EOF - { - table.bits[0]=m; - table.bits[1]=h; - table.bits[2]=d; - table.bits[3]=mnth; - table.dayOfWeek=(int)dow; - } - | ( AT - ( - "yearly" - { - table.set("H H H H *",getHashForTokens()); - } - | "annually" - { - table.set("H H H H *",getHashForTokens()); - } - | "monthly" - { - table.set("H H H * *",getHashForTokens()); - } - | "weekly" - { - table.set("H H * * H",getHashForTokens()); - } - | "daily" - { - table.set("H H * * *",getHashForTokens()); - } - | "midnight" - { - table.set("H H(0-2) * * *",getHashForTokens()); - } - | "hourly" - { - table.set("H * * * *",getHashForTokens()); - } - ) - ) - ; - -expr [int field] -returns [long bits=0] -throws ANTLRException -{ - long lhs,rhs=0; -} - : lhs=term[field] ("," rhs=expr[field])? - { - bits = lhs|rhs; - } - ; - -term [int field] -returns [long bits=0] -throws ANTLRException -{ - int d=NO_STEP,s,e,t; -} - : (token "-")=> s=token "-" e=token ( "/" d=token )? - { - bits = doRange(s,e,d,field); - } - | t=token - { - rangeCheck(t,field); - bits = 1L< "H" "(" s=token "-" e=token ")" ( "/" d=token )? - { - bits = doHash(s,e,d,field); - } - | "H" ( "/" d=token )? - { - bits = doHash(d,field); - } - ; - -token -returns [int value=0] - : t:TOKEN - { - value = Integer.parseInt(t.getText()); - } - ; - -class CrontabLexer extends Lexer; -options { - k=2; // I'm sure there's a better way to do this than using lookahead. ANTLR sucks... - defaultErrorHandler=false; -} - -TOKEN -options { - paraphrase="a number"; -} - : ('0'..'9')+ - ; - -WS -options { - paraphrase="space"; -} - : (' '|'\t')+ - ; - -MINUS: '-'; -STAR: '*'; -DIV: '/'; -OR: ','; -AT: '@'; -H: 'H'; -LPAREN: '('; -RPAREN: ')'; - -YEARLY: "yearly"; -ANNUALLY: "annually"; -MONTHLY: "monthly"; -WEEKLY: "weekly"; -DAILY: "daily"; -MIDNIGHT: "midnight"; -HOURLY: "hourly"; diff --git a/core/src/main/grammar/labelExpr.g b/core/src/main/grammar/labelExpr.g deleted file mode 100644 index c100f9e11cd9..000000000000 --- a/core/src/main/grammar/labelExpr.g +++ /dev/null @@ -1,133 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010, InfraDNA, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -header { - package hudson.model.labels; - import hudson.model.Label; -} - -class LabelExpressionParser extends Parser; -options { - defaultErrorHandler=false; -} - -// order of precedence is as per http://en.wikipedia.org/wiki/Logical_connective#Order_of_precedence - -expr -returns [Label l] - : l=term1 EOF - ; - -term1 -returns [Label l] -{ Label r; } - : l=term2( IFF r=term2 {l=l.iff(r);} )* - ; - -// (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining -term2 -returns [Label l] -{ Label r; } - : l=term3( IMPLIES r=term3 {l=l.implies(r);} )? - ; - -term3 -returns [Label l] -{ Label r; } - : l=term4 ( OR r=term4 {l=l.or(r);} )* - ; - -term4 -returns [Label l] -{ Label r; } - : l=term5 ( AND r=term5 {l=l.and(r);} )* - ; - -term5 -returns [Label l] -{ Label x; } - : l=term6 - | NOT x=term6 - { l=x.not(); } - ; - -term6 -returns [Label l] -options { generateAmbigWarnings=false; } - : LPAREN l=term1 RPAREN - { l=l.paren(); } - | a:ATOM - { l=LabelAtom.get(a.getText()); } - | s:STRINGLITERAL - { l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote(s.getText())); } - ; - -class LabelExpressionLexer extends Lexer; -options { - // There must be an options section to satisfy - // org.codehaus.mojo.antlr.metadata.MetadataExtracter#intrepret, even if it is empty. - - // https://www.antlr2.org/doc/lexer.html#Common_prefixes - // to prevent nondeterminism between IMPLIES and ATOM related to the first "-" - k=2; -} - -AND: "&&"; -OR: "||"; -NOT: "!"; -IMPLIES:"->"; -IFF: "<->"; -LPAREN: "("; -RPAREN: ")"; - -protected -IDENTIFIER_PART - : ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '\"' | '\'' | '-' ) - ; - -ATOM -/* - the real check of valid identifier happens in LabelAtom.get() - - https://www.antlr2.org/doc/lexer.html#usingexplicit - If we are seeing currently a '-', we check that the next char is not a '>' which will be a IMPLIES. - Otherwise the ATOM and the IMPLIES will collide and expr like a->b will just be parsed as ATOM (without spaces) -*/ - : ( - { LA(2) != '>' }? '-' - | IDENTIFIER_PART - )+ - ; - -WS - : (' '|'\t')+ - { $setType(Token.SKIP); } - ; - -STRINGLITERAL - : '"' - ( '\\' ( 'b' | 't' | 'n' | 'f' | 'r' | '\"' | '\'' | '\\' ) /* escape */ - | ~( '\\' | '"' | '\r' | '\n' ) - )* - '"' - ; diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index ad7f2f9f7830..319abfe8d905 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -31,7 +31,6 @@ import static hudson.scm.PollingResult.BUILD_NOW; import static hudson.scm.PollingResult.NO_CHANGES; -import antlr.ANTLRException; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; @@ -50,7 +49,6 @@ import hudson.model.Node.Mode; import hudson.model.Queue.Executable; import hudson.model.Queue.Task; -import hudson.model.labels.LabelAtom; import hudson.model.labels.LabelExpression; import hudson.model.listeners.SCMPollListener; import hudson.model.queue.CauseOfBlockage; @@ -411,13 +409,13 @@ public Set - - org.antlr - antlr4 - 4.11.1 - args4j @@ -190,6 +185,11 @@ THE SOFTWARE. kxml2 2.3.0 + + org.antlr + antlr4 + 4.11.1 + org.apache.ant ant diff --git a/core/pom.xml b/core/pom.xml index 4b9015b314cd..a2ec5feb0c2d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -571,8 +571,6 @@ THE SOFTWARE. generate-sources - ${project.build.directory}/generated-sources/antlr4/hudson/model/labels - ${project.build.directory}/generated-sources/antlr4/hudson/scheduler ${project.build.directory}/generated-sources/localizer ${project.build.directory}/generated-sources/taglib-interface diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 index e825199cede2..55c0ae7e33bb 100644 --- a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 @@ -8,8 +8,8 @@ IFF: '<->'; LPAREN: '('; RPAREN: ')'; -IDENTIFIER_PART - : ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-' ) +fragment IDENTIFIER_PART : + ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-' ) ; ATOM diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 index dcdf1daec787..06fceaab3f17 100644 --- a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 @@ -12,7 +12,7 @@ options { expr returns [Label l] - : term1 EOF { $l=$term1.ctx.l; } + : term1 { $l=$term1.ctx.l; } EOF ; term1 diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 index 9122a8e04e80..36fd70433fe2 100644 --- a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 +++ b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 @@ -14,7 +14,6 @@ AT: '@'; H: 'H'; LPAREN: '('; RPAREN: ')'; -COMMA: ','; YEARLY: 'yearly'; ANNUALLY: 'annually'; diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 index 5f71efdde28c..34aaffa7c83c 100644 --- a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 +++ b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 @@ -45,7 +45,7 @@ startRule [CronTab table] expr [int field] returns [long bits=0] locals[long lhs, long rhs=0] - : term[field] { $lhs = $term.ctx.bits; } ( COMMA expr[field] { $rhs = $expr.ctx.bits; } )? + : term[field] { $lhs = $term.ctx.bits; } ( OR expr[field] { $rhs = $expr.ctx.bits; } )? { $bits = $lhs|$rhs; } @@ -53,7 +53,7 @@ locals[long lhs, long rhs=0] term [int field] returns [long bits=0] -locals [int d=NO_STEP, int s, int e, int t] +locals [int d=NO_STEP, int s, int e] : token { $s=$token.ctx.value; } MINUS token { $e=$token.ctx.value; } ( DIV token { $d=$token.ctx.value; } )? { $bits = doRange($s,$e,$d,$field); diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 319abfe8d905..c9c6930ae5f1 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -49,6 +49,7 @@ import hudson.model.Node.Mode; import hudson.model.Queue.Executable; import hudson.model.Queue.Task; +import hudson.model.labels.LabelAtom; import hudson.model.labels.LabelExpression; import hudson.model.listeners.SCMPollListener; import hudson.model.queue.CauseOfBlockage; @@ -109,6 +110,7 @@ import jenkins.triggers.SCMTriggerItem; import jenkins.util.TimeDuration; import net.sf.json.JSONObject; +import org.antlr.v4.runtime.misc.ParseCancellationException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -409,13 +411,13 @@ public Set - - org.antlr - antlr4 - args4j args4j @@ -278,6 +274,10 @@ THE SOFTWARE. net.sf.kxml kxml2 + + org.antlr + antlr4 + org.apache.ant ant From 34a86778c77dfbca5f6abf1896a44e35011c4598 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 21 Oct 2022 12:46:40 -0700 Subject: [PATCH 04/24] Ignore SpotBugs warnings in generated code --- src/spotbugs/spotbugs-excludes.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index d57cc5fb6aff..0971834a231e 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -22,11 +22,21 @@ + + + + + + + + + + @@ -38,6 +48,11 @@ + + + + + + + maven-enforcer-plugin + + + display-info + + + + 1.8 + + + 1.8 + + org.antlr:antlr4 + org.jenkins-ci:commons-jelly + org.jenkins-ci:task-reactor + org.jenkins-ci.main:cli + org.jenkins-ci.main:jenkins-core + org.jenkins-ci.main:remoting + org.jenkins-ci.main:websocket-jetty9 + org.jenkins-ci.main:websocket-jetty10 + org.jenkins-ci.main:websocket-spi + org.jenkins-ci:memory-monitor + org.jvnet.winp:winp + org.kohsuke.stapler:stapler + org.kohsuke.stapler:stapler-groovy + org.kohsuke.stapler:stapler-jelly + + + + + + + maven-compiler-plugin From dbb8236e5b4b1898630e19239216279af2882330 Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Fri, 21 Oct 2022 13:56:06 -0700 Subject: [PATCH 06/24] Add files/dirs for ANTLR4 --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 021201b717a3..365fb4df9fde 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ war/node_modules/ war/yarn-error.log .java-version .checkstyle + +# ANTLR generated files +*.tokens +core/gen/ From 0ff8df739c4ed47acbe09659ef4b95a31f112d4e Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Sat, 22 Oct 2022 07:20:43 -0700 Subject: [PATCH 07/24] Reformat .g4 files add ANTLRException for backward compat. --- .../model/labels/LabelExpressionLexer.g4 | 66 +++++++----- .../model/labels/LabelExpressionParser.g4 | 100 +++++++++--------- .../antlr4/hudson/scheduler/CrontabLexer.g4 | 80 +++++++++++--- .../antlr4/hudson/scheduler/CrontabParser.g4 | 94 ++++++++-------- core/src/main/java/antlr/ANTLRException.java | 17 +++ pom.xml | 3 + 6 files changed, 218 insertions(+), 142 deletions(-) create mode 100644 core/src/main/java/antlr/ANTLRException.java diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 index 55c0ae7e33bb..45e8ca01b233 100644 --- a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 @@ -1,16 +1,36 @@ lexer grammar LabelExpressionLexer; -AND: '&&'; -OR: '||'; -NOT: '!'; -IMPLIES:'->'; -IFF: '<->'; -LPAREN: '('; -RPAREN: ')'; - -fragment IDENTIFIER_PART : - ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-' ) - ; +AND + : '&&' + ; + +OR + : '||' + ; + +NOT + : '!' + ; + +IMPLIES + : '->' + ; + +IFF + : '<->' + ; + +LPAREN + : '(' + ; + +RPAREN + : ')' + ; + +fragment IDENTIFIER_PART + : ~ ('&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-') + ; ATOM /* @@ -20,20 +40,18 @@ ATOM If we are seeing currently a '-', we check that the next char is not a '>' which will be a IMPLIES. Otherwise the ATOM and the IMPLIES will collide and expr like a->b will just be parsed as ATOM (without spaces) */ - : ( - { _input.LA(2) != '>' }? '-' - | IDENTIFIER_PART - )+ - ; + + : ( + { _input.LA(2) != '>' }? '-' | IDENTIFIER_PART)+ + ; WS - : (' '|'\t')+ -> skip - ; + : (' ' | '\t')+ -> skip + ; STRINGLITERAL - : '"' - ( '\\' ( 'b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\' ) /* escape */ - | ~( '\\' | '"' | '\r' | '\n' ) - )* - '"' - ; \ No newline at end of file + : '"' ('\\' ('b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\') /* escape */ + + | ~ ('\\' | '"' | '\r' | '\n'))* '"' + ; + diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 index 06fceaab3f17..2ac7f2fedb51 100644 --- a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 @@ -1,58 +1,56 @@ parser grammar LabelExpressionParser; -@header { +@ header +{ import hudson.model.Label; } -options { - tokenVocab = LabelExpressionLexer; -} - +options { tokenVocab = LabelExpressionLexer; } // order of precedence is as per http://en.wikipedia.org/wiki/Logical_connective#Order_of_precedence -expr -returns [Label l] - : term1 { $l=$term1.ctx.l; } EOF - ; - -term1 -returns [Label l] -locals [Label r] - : term2 { $l=$term2.ctx.l; } ( IFF term2 { $r=$term2.ctx.l; $l=$l.iff($r); } )* - ; - -// (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining -term2 -returns [Label l] -locals [Label r] - : term3 { $l=$term3.ctx.l; } ( IMPLIES term3 { $r=$term3.ctx.l; $l=$l.implies($r); } )? - ; - -term3 -returns [Label l] -locals [Label r] - : term4 { $l=$term4.ctx.l; } ( OR term4 { $r=$term4.ctx.l; $l=$l.or($r); } )* - ; - -term4 -returns [Label l] -locals [Label r] - : term5 { $l=$term5.ctx.l; } ( AND term5 { $r=$term5.ctx.l; $l=$l.and($r); } )* - ; - -term5 -returns [Label l] - : term6 { $l=$term6.ctx.l; } - | NOT term6 - { $l=$term6.ctx.l; $l=$l.not(); } - ; - -term6 -returns [Label l] - : LPAREN term1 RPAREN - { $l=$term1.ctx.l ; $l=$l.paren(); } - | ATOM - { $l=LabelAtom.get($ATOM.getText()); } - | STRINGLITERAL - { $l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote($STRINGLITERAL.getText())); } - ; \ No newline at end of file +expr returns[Label l] + : term1 + { $l=$term1.ctx.l; } EOF + ; + +term1 returns[Label l] locals[Label r] + : term2 + { $l=$term2.ctx.l; } (IFF term2 + { $r=$term2.ctx.l; $l=$l.iff($r); })* + ; + // (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining + +term2 returns[Label l] locals[Label r] + : term3 + { $l=$term3.ctx.l; } (IMPLIES term3 + { $r=$term3.ctx.l; $l=$l.implies($r); })? + ; + +term3 returns[Label l] locals[Label r] + : term4 + { $l=$term4.ctx.l; } (OR term4 + { $r=$term4.ctx.l; $l=$l.or($r); })* + ; + +term4 returns[Label l] locals[Label r] + : term5 + { $l=$term5.ctx.l; } (AND term5 + { $r=$term5.ctx.l; $l=$l.and($r); })* + ; + +term5 returns[Label l] + : term6 + { $l=$term6.ctx.l; } + | NOT term6 + { $l=$term6.ctx.l; $l=$l.not(); } + ; + +term6 returns[Label l] + : LPAREN term1 RPAREN + { $l=$term1.ctx.l ; $l=$l.paren(); } + | ATOM + { $l=LabelAtom.get($ATOM.getText()); } + | STRINGLITERAL + { $l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote($STRINGLITERAL.getText())); } + ; + diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 index 36fd70433fe2..a8e471c4c8f1 100644 --- a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 +++ b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 @@ -1,24 +1,70 @@ lexer grammar CrontabLexer; -TOKEN : ('0'..'9')+ +TOKEN + : ('0' .. '9')+ ; -WS : (' '|'\t')+ +WS + : (' ' | '\t')+ ; -MINUS: '-'; -STAR: '*'; -DIV: '/'; -OR: ','; -AT: '@'; -H: 'H'; -LPAREN: '('; -RPAREN: ')'; +MINUS + : '-' + ; + +STAR + : '*' + ; + +DIV + : '/' + ; + +OR + : ',' + ; + +AT + : '@' + ; + +H + : 'H' + ; + +LPAREN + : '(' + ; + +RPAREN + : ')' + ; + +YEARLY + : 'yearly' + ; + +ANNUALLY + : 'annually' + ; + +MONTHLY + : 'monthly' + ; + +WEEKLY + : 'weekly' + ; + +DAILY + : 'daily' + ; + +MIDNIGHT + : 'midnight' + ; + +HOURLY + : 'hourly' + ; -YEARLY: 'yearly'; -ANNUALLY: 'annually'; -MONTHLY: 'monthly'; -WEEKLY: 'weekly'; -DAILY: 'daily'; -MIDNIGHT: 'midnight'; -HOURLY: 'hourly'; diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 index 34aaffa7c83c..4495afa62a34 100644 --- a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 +++ b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 @@ -1,60 +1,52 @@ parser grammar CrontabParser; -options { - tokenVocab = CrontabLexer; - superClass = BaseParser; -} -startRule [CronTab table] - : expr[0] { $table.bits[0]=$expr.ctx.bits; } WS expr[1] { $table.bits[1]=$expr.ctx.bits; } WS expr[2] { $table.bits[2]=$expr.ctx.bits; } WS expr[3] { $table.bits[3]=$expr.ctx.bits; } WS expr[4] { $table.dayOfWeek=(int)$expr.ctx.bits; } EOF - | ( AT - ( - 'yearly' - { +options { tokenVocab = CrontabLexer; superClass = BaseParser; } +startRule[CronTab table] + : expr[0] + { $table.bits[0]=$expr.ctx.bits; } WS expr[1] + { $table.bits[1]=$expr.ctx.bits; } WS expr[2] + { $table.bits[2]=$expr.ctx.bits; } WS expr[3] + { $table.bits[3]=$expr.ctx.bits; } WS expr[4] + { $table.dayOfWeek=(int)$expr.ctx.bits; } EOF + | (AT ('yearly' + { $table.set("H H H H *",getHashForTokens()); - } - | 'annually' - { + } | 'annually' + { $table.set("H H H H *",getHashForTokens()); - } - | 'monthly' - { + } | 'monthly' + { $table.set("H H H * *",getHashForTokens()); - } - | 'weekly' - { + } | 'weekly' + { $table.set("H H * * H",getHashForTokens()); - } - | 'daily' - { + } | 'daily' + { $table.set("H H * * *",getHashForTokens()); - } - | 'midnight' - { + } | 'midnight' + { $table.set("H H(0-2) * * *",getHashForTokens()); - } - | 'hourly' - { + } | 'hourly' + { $table.set("H * * * *",getHashForTokens()); - } - ) - ) - ; - + })) + ; -expr [int field] -returns [long bits=0] -locals[long lhs, long rhs=0] - : term[field] { $lhs = $term.ctx.bits; } ( OR expr[field] { $rhs = $expr.ctx.bits; } )? - { +expr[int field] returns[long bits=0] locals[long lhs, long rhs=0] + : term[field] + { $lhs = $term.ctx.bits; } (OR expr[field] + { $rhs = $expr.ctx.bits; })? + { $bits = $lhs|$rhs; } - ; + ; -term [int field] -returns [long bits=0] -locals [int d=NO_STEP, int s, int e] - : token { $s=$token.ctx.value; } MINUS token { $e=$token.ctx.value; } ( DIV token { $d=$token.ctx.value; } )? +term[int field] returns[long bits=0] locals[int d=NO_STEP, int s, int e] + : token + { $s=$token.ctx.value; } MINUS token + { $e=$token.ctx.value; } (DIV token + { $d=$token.ctx.value; })? { $bits = doRange($s,$e,$d,$field); } @@ -63,27 +55,29 @@ locals [int d=NO_STEP, int s, int e] rangeCheck($token.ctx.value,$field); $bits = 1L<<$token.ctx.value; } - | STAR ( DIV token { $d=$token.ctx.value; } )? + | STAR (DIV token + { $d=$token.ctx.value; })? { $bits = doRange($d,$field); } - | 'H' LPAREN token { $s=$token.ctx.value; } MINUS token { $e=$token.ctx.value; } RPAREN ( DIV token { $d=$token.ctx.value; } )? + | 'H' LPAREN token + { $s=$token.ctx.value; } MINUS token + { $e=$token.ctx.value; } RPAREN (DIV token + { $d=$token.ctx.value; })? { $bits = doHash($s,$e,$d,$field); } - | 'H' ( DIV token { $d=$token.ctx.value; })? + | 'H' (DIV token + { $d=$token.ctx.value; })? { $bits = doHash($d,$field); } ; - token - returns [int value=0] +token returns[int value=0] : TOKEN { $value = Integer.parseInt($TOKEN.getText()); } ; - - diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java new file mode 100644 index 000000000000..9470af55c14a --- /dev/null +++ b/core/src/main/java/antlr/ANTLRException.java @@ -0,0 +1,17 @@ +package antlr; + +import org.antlr.v4.runtime.misc.ParseCancellationException; + +/** + * This class is for binary compatibility for older plugins that + * import ANTLRException. + */ +public class ANTLRException extends ParseCancellationException { + public ANTLRException() { + // nothing needs to be done her + } + + public ANTLRException(String msg) { + super(msg); + } +} diff --git a/pom.xml b/pom.xml index b1ef08a9b05e..840a7c449d39 100644 --- a/pom.xml +++ b/pom.xml @@ -391,6 +391,9 @@ THE SOFTWARE. spotless-maven-plugin ${spotless.version} + + + From 9b6d09d0a310ce6b3837db4879456bb744f1e617 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 22 Oct 2022 09:17:55 -0700 Subject: [PATCH 08/24] fix typo --- core/src/main/java/antlr/ANTLRException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java index 9470af55c14a..301fcedf98ce 100644 --- a/core/src/main/java/antlr/ANTLRException.java +++ b/core/src/main/java/antlr/ANTLRException.java @@ -8,7 +8,7 @@ */ public class ANTLRException extends ParseCancellationException { public ANTLRException() { - // nothing needs to be done her + // nothing needs to be done here } public ANTLRException(String msg) { From ff1f8a6621c20ff13a7e1f41e28ff72ecc507163 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 22 Oct 2022 09:18:04 -0700 Subject: [PATCH 09/24] full set of exception constructors --- core/src/main/java/antlr/ANTLRException.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java index 301fcedf98ce..ddb4174bcc73 100644 --- a/core/src/main/java/antlr/ANTLRException.java +++ b/core/src/main/java/antlr/ANTLRException.java @@ -11,7 +11,15 @@ public ANTLRException() { // nothing needs to be done here } - public ANTLRException(String msg) { - super(msg); + public ANTLRException(String message) { + super(message); + } + + public ANTLRException(String message, Throwable cause) { + super(message, cause); + } + + public ANTLRException(Throwable cause) { + super(cause); } } From bed2789e7686f42f78498b214cf031dddb723553 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 22 Oct 2022 09:25:10 -0700 Subject: [PATCH 10/24] Remove unnecessary throws from tests --- .../scheduler/CronTabEventualityTest.java | 21 +++++++++---------- .../java/hudson/scheduler/CronTabTest.java | 6 +++--- .../hudson/triggers/TimerTriggerTest.java | 3 +-- .../model/labels/LabelExpressionTest.java | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java b/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java index 8710d4f43dfc..73f1e55c5a77 100644 --- a/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java @@ -7,7 +7,6 @@ import java.util.Calendar; import java.util.Collection; import java.util.GregorianCalendar; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -42,7 +41,7 @@ public CronTabEventualityTest(String name, Hash hash) { @Test @Issue("JENKINS-12388") - public void testYearlyWillBeEventuallyTriggeredWithinOneYear() throws ParseCancellationException { + public void testYearlyWillBeEventuallyTriggeredWithinOneYear() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.YEAR, 1); checkEventuality(start, "@yearly", limit); @@ -50,56 +49,56 @@ public void testYearlyWillBeEventuallyTriggeredWithinOneYear() throws ParseCance @Test @Issue("JENKINS-12388") - public void testAnnuallyWillBeEventuallyTriggeredWithinOneYear() throws ParseCancellationException { + public void testAnnuallyWillBeEventuallyTriggeredWithinOneYear() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.YEAR, 1); checkEventuality(start, "@annually", limit); } @Test - public void testMonthlyWillBeEventuallyTriggeredWithinOneMonth() throws ParseCancellationException { + public void testMonthlyWillBeEventuallyTriggeredWithinOneMonth() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.MONTH, 1); checkEventuality(start, "@monthly", limit); } @Test - public void testWeeklyWillBeEventuallyTriggeredWithinOneWeek() throws ParseCancellationException { + public void testWeeklyWillBeEventuallyTriggeredWithinOneWeek() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.WEEK_OF_YEAR, 1); checkEventuality(start, "@weekly", limit); } @Test - public void testDailyWillBeEventuallyTriggeredWithinOneDay() throws ParseCancellationException { + public void testDailyWillBeEventuallyTriggeredWithinOneDay() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 1); checkEventuality(start, "@daily", limit); } @Test - public void testMidnightWillBeEventuallyTriggeredWithinOneDay() throws ParseCancellationException { + public void testMidnightWillBeEventuallyTriggeredWithinOneDay() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 1); checkEventuality(start, "@midnight", limit); } @Test - public void testHourlyWillBeEventuallyTriggeredWithinOneHour() throws ParseCancellationException { + public void testHourlyWillBeEventuallyTriggeredWithinOneHour() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.HOUR, 1); checkEventuality(start, "@hourly", limit); } @Test - public void testFirstDayOfMonthWillBeEventuallyTriggeredWithinOneMonth() throws ParseCancellationException { + public void testFirstDayOfMonthWillBeEventuallyTriggeredWithinOneMonth() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.MONTH, 1); checkEventuality(start, "H H 1 * *", limit); } @Test - public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneWeek() throws ParseCancellationException { + public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneWeek() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 31 + 7); // If both day of month and day of week are specified: @@ -108,7 +107,7 @@ public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneW checkEventuality(start, "H H 1-7 * 0", limit); } - private void checkEventuality(Calendar start, String crontabFormat, Calendar limit) throws ParseCancellationException { + private void checkEventuality(Calendar start, String crontabFormat, Calendar limit) { CronTab cron = new CronTab(crontabFormat, hash); Calendar next = cron.ceil(start); if (next.after(limit)) { diff --git a/core/src/test/java/hudson/scheduler/CronTabTest.java b/core/src/test/java/hudson/scheduler/CronTabTest.java index e2c157c06816..73dd5253f182 100644 --- a/core/src/test/java/hudson/scheduler/CronTabTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabTest.java @@ -46,7 +46,7 @@ public class CronTabTest { @Test - public void test1() throws ParseCancellationException { + public void test1() { new CronTab("@yearly"); new CronTab("@weekly"); new CronTab("@midnight"); @@ -97,7 +97,7 @@ public void testCeil3_DoW7() throws Exception { */ @Issue("HUDSON-8656") // This is _not_ JENKINS-8656 @Test - public void testCeil4() throws ParseCancellationException { + public void testCeil4() { final Calendar cal = Calendar.getInstance(new Locale("de", "de")); cal.set(2011, Calendar.JANUARY, 16, 0, 0, 0); // Sunday, Jan 16th 2011, 00:00 final String cronStr = "0 23 * * 1-5"; // execute on weekdays @23:00 @@ -119,7 +119,7 @@ public void testCeil4() throws ParseCancellationException { */ @Issue("HUDSON-8656") // This is _not_ JENKINS-8656 @Test - public void testCeil5() throws ParseCancellationException { + public void testCeil5() { final Calendar cal = Calendar.getInstance(new Locale("de", "at")); cal.set(2011, Calendar.JANUARY, 16, 0, 0, 0); // Sunday, Jan 16th 2011, 00:00 final String cronStr = "0 23 * * 1-5"; // execute on weekdays @23:00 diff --git a/core/src/test/java/hudson/triggers/TimerTriggerTest.java b/core/src/test/java/hudson/triggers/TimerTriggerTest.java index 3a906608683a..3d2a6ab01007 100644 --- a/core/src/test/java/hudson/triggers/TimerTriggerTest.java +++ b/core/src/test/java/hudson/triggers/TimerTriggerTest.java @@ -27,7 +27,6 @@ import hudson.scheduler.CronTabList; import hudson.scheduler.Hash; import java.util.TimeZone; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Assert; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -38,7 +37,7 @@ public class TimerTriggerTest { @Issue("JENKINS-29790") @Test - public void testNoNPE() throws ParseCancellationException { + public void testNoNPE() { new TimerTrigger("").run(); } diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index ad98eb780fcb..b05db32c4472 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -182,7 +182,7 @@ public void parser2() throws Exception { parseAndVerify("aaa&&bbb&&ccc", "aaa&&bbb&&ccc"); } - private void parseAndVerify(String expected, String expr) throws ParseCancellationException { + private void parseAndVerify(String expected, String expr) { assertEquals(expected, Label.parseExpression(expr).getName()); } From c63d928c9efddd45ec411322927ddbc4749568e6 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 22 Oct 2022 09:28:17 -0700 Subject: [PATCH 11/24] Deprecate compatibility layer --- core/src/main/java/antlr/ANTLRException.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java index ddb4174bcc73..79860d69669c 100644 --- a/core/src/main/java/antlr/ANTLRException.java +++ b/core/src/main/java/antlr/ANTLRException.java @@ -5,7 +5,10 @@ /** * This class is for binary compatibility for older plugins that * import ANTLRException. + * + * @deprecated use {@link ParseCancellationException} */ +@Deprecated public class ANTLRException extends ParseCancellationException { public ANTLRException() { // nothing needs to be done here From ffc1efd96177ee58f1a72171ec7ca51c81fda931 Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Fri, 28 Oct 2022 06:20:09 -0700 Subject: [PATCH 12/24] Implement feedback suggestions - Apply spotless formatting for .g4 files. - Add ANTLRException.java for backward compat - Add SemanticException.java to support debug --- .../main/java/antlr/SemanticException.java | 44 +++++++++++++++++++ core/src/main/java/hudson/model/Label.java | 23 +++++----- .../java/hudson/scheduler/BaseParser.java | 11 ++++- .../model/labels/LabelExpressionTest.java | 18 ++++---- 4 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/antlr/SemanticException.java diff --git a/core/src/main/java/antlr/SemanticException.java b/core/src/main/java/antlr/SemanticException.java new file mode 100644 index 000000000000..30531cece7fe --- /dev/null +++ b/core/src/main/java/antlr/SemanticException.java @@ -0,0 +1,44 @@ +package antlr; + +public class SemanticException extends ANTLRException { + public int line = -1; + public int column = -1; + + /** + * RecognitionException constructor comment. + * @param s java.lang.String + */ + public SemanticException(String s, int line, int column) { + super(s); + this.line = line; + this.column = column + 1; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + public String getFormatString(int line, int column) { + StringBuffer buf = new StringBuffer(); + + if (line != -1) { + buf.append("line "); + buf.append(line); + + if (column != -1) + buf.append(":" + column); + buf.append(":"); + } + + buf.append(" "); + return buf.toString(); + } + + public String toString() { + return getFormatString(line, column) + getMessage(); + } +} diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index d589fc1ba6e4..cccf14bd460c 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -26,6 +26,8 @@ import static hudson.Util.fixNull; +import antlr.ANTLRException; +import antlr.SemanticException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; @@ -68,7 +70,6 @@ import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import org.antlr.v4.runtime.ANTLRErrorListener; -import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.Parser; @@ -76,7 +77,6 @@ import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.atn.ATNConfigSet; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.StaplerRequest; @@ -620,13 +620,11 @@ public static Label get(String l) { * * TODO: replace this with a real parser later */ - public static Label parseExpression(@NonNull String labelExpression) throws ParseCancellationException { - LabelExpressionLexer lexer = new LabelExpressionLexer(CharStreams.fromString(labelExpression)); - lexer.removeErrorListeners(); - lexer.addErrorListener(new ANTLRErrorListener() { + public static Label parseExpression(@NonNull String labelExpression) throws ANTLRException { + ANTLRErrorListener listener = new ANTLRErrorListener() { @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - throw new ParseCancellationException(msg); + throw new SemanticException(msg, line, charPositionInLine); } @Override @@ -643,10 +641,13 @@ public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startInd public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) { } - }); - CommonTokenStream inputTokenStream = new CommonTokenStream(lexer); - LabelExpressionParser parser = new LabelExpressionParser(inputTokenStream); - parser.setErrorHandler(new BailErrorStrategy()); + }; + LabelExpressionLexer lexer = new LabelExpressionLexer(CharStreams.fromString(labelExpression)); + lexer.removeErrorListeners(); + lexer.addErrorListener(listener); + LabelExpressionParser parser = new LabelExpressionParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(listener); return parser.expr().l; } diff --git a/core/src/main/java/hudson/scheduler/BaseParser.java b/core/src/main/java/hudson/scheduler/BaseParser.java index 750eb2bdc65e..f068fa0c1bbb 100644 --- a/core/src/main/java/hudson/scheduler/BaseParser.java +++ b/core/src/main/java/hudson/scheduler/BaseParser.java @@ -24,8 +24,10 @@ package hudson.scheduler; +import antlr.SemanticException; import jenkins.util.SystemProperties; import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.misc.ParseCancellationException; @@ -113,8 +115,13 @@ protected void rangeCheck(int value, int field) throws ParseCancellationExceptio } } - private void error(String msg) throws ParseCancellationException { - throw new ParseCancellationException(msg); + private void error(String msg) throws SemanticException { + Token t = _input.LT(-1); + throw new SemanticException( + msg, + t.getLine(), + t.getCharPositionInLine() + ); } protected Hash getHashForTokens() { diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index bcb738d4e814..b87b481d25ac 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -188,15 +188,15 @@ private void parseAndVerify(String expected, String expr) { @Test public void parserError() { - parseShouldFail("foo bar", "line 1:5: unexpected token: bar"); - parseShouldFail("foo (bar)", "line 1:5: unexpected token: ("); - parseShouldFail("foo(bar)", "line 1:4: unexpected token: ("); - parseShouldFail("a <- b", "line 1:5: expecting '>', found ' '"); - parseShouldFail("a -< b", "line 1:3: unexpected token: -"); - parseShouldFail("a - b", "line 1:3: unexpected token: -"); - parseShouldFail("->", "line 1:1: unexpected token: ->"); - parseShouldFail("-<", "line 1:3: expecting '-', found ''"); - parseShouldFail("-!", "line 1:2: unexpected token: !"); + parseShouldFail("foo bar", "line 1:5: extraneous input 'bar' expecting "); + parseShouldFail("foo (bar)", "line 1:5: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("foo(bar)", "line 1:4: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("a <- b", "line 1:3: token recognition error at: '<- '"); + parseShouldFail("a -< b", "line 1:4: token recognition error at: '< '"); + parseShouldFail("a - b", "line 1:3: mismatched input '-' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("->", "line 1:1: mismatched input '->' expecting {'!', '(', ATOM, STRINGLITERAL}"); + parseShouldFail("-<", "line 1:2: token recognition error at: '<'"); + parseShouldFail("-!", "line 1:2: extraneous input '!' expecting "); } @Test From d3e4abd4e273f51fb973cabc02e6ed625a8f0851 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 10:08:49 -0700 Subject: [PATCH 13/24] Clean up error handling --- core/src/main/java/antlr/ANTLRException.java | 13 +---- .../main/java/antlr/SemanticException.java | 44 --------------- .../java/hudson/model/AbstractProject.java | 3 +- core/src/main/java/hudson/model/Label.java | 40 +++---------- .../hudson/model/labels/LabelExpression.java | 3 +- .../java/hudson/scheduler/BaseParser.java | 37 +++++++----- .../main/java/hudson/scheduler/CronTab.java | 32 +++++++++-- .../java/hudson/scheduler/CronTabList.java | 20 +++++-- .../SimpleScheduledRetentionStrategy.java | 9 ++- .../main/java/hudson/triggers/SCMTrigger.java | 5 +- .../java/hudson/triggers/TimerTrigger.java | 3 +- .../main/java/hudson/triggers/Trigger.java | 10 ++-- core/src/main/java/jenkins/model/Jenkins.java | 3 +- .../util/antlr/JenkinsANTLRErrorListener.java | 56 +++++++++++++++++++ .../java/hudson/scheduler/CronTabTest.java | 19 ++++--- .../test/java/hudson/model/ProjectTest.java | 3 +- .../model/labels/LabelExpressionTest.java | 27 ++++----- .../java/hudson/triggers/TriggerTest.java | 3 +- .../java/jenkins/triggers/TriggerTest.java | 3 +- 19 files changed, 175 insertions(+), 158 deletions(-) delete mode 100644 core/src/main/java/antlr/SemanticException.java create mode 100644 core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java index 79860d69669c..77447338a64b 100644 --- a/core/src/main/java/antlr/ANTLRException.java +++ b/core/src/main/java/antlr/ANTLRException.java @@ -1,19 +1,12 @@ package antlr; -import org.antlr.v4.runtime.misc.ParseCancellationException; - /** - * This class is for binary compatibility for older plugins that - * import ANTLRException. + * This class is for binary compatibility for older plugins that import {@link ANTLRException}. * - * @deprecated use {@link ParseCancellationException} + * @deprecated use {@link IllegalArgumentException} */ @Deprecated -public class ANTLRException extends ParseCancellationException { - public ANTLRException() { - // nothing needs to be done here - } - +public class ANTLRException extends IllegalArgumentException { public ANTLRException(String message) { super(message); } diff --git a/core/src/main/java/antlr/SemanticException.java b/core/src/main/java/antlr/SemanticException.java deleted file mode 100644 index 30531cece7fe..000000000000 --- a/core/src/main/java/antlr/SemanticException.java +++ /dev/null @@ -1,44 +0,0 @@ -package antlr; - -public class SemanticException extends ANTLRException { - public int line = -1; - public int column = -1; - - /** - * RecognitionException constructor comment. - * @param s java.lang.String - */ - public SemanticException(String s, int line, int column) { - super(s); - this.line = line; - this.column = column + 1; - } - - public int getLine() { - return line; - } - - public int getColumn() { - return column; - } - - public String getFormatString(int line, int column) { - StringBuffer buf = new StringBuffer(); - - if (line != -1) { - buf.append("line "); - buf.append(line); - - if (column != -1) - buf.append(":" + column); - buf.append(":"); - } - - buf.append(" "); - return buf.toString(); - } - - public String toString() { - return getFormatString(line, column) + getMessage(); - } -} diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 6d458c19929a..fa1a7777df7b 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -110,7 +110,6 @@ import jenkins.triggers.SCMTriggerItem; import jenkins.util.TimeDuration; import net.sf.json.JSONObject; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -414,7 +413,7 @@ public String getAssignedLabelString() { try { Label.parseExpression(assignedNode); return assignedNode; - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { // must be old label or host name that includes whitespace or other unsafe chars return LabelAtom.escape(assignedNode); } diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index cccf14bd460c..43604d408aac 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -26,8 +26,6 @@ import static hudson.Util.fixNull; -import antlr.ANTLRException; -import antlr.SemanticException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; @@ -57,7 +55,6 @@ import hudson.util.QuotedStringTokenizer; import hudson.util.VariableResolver; import java.io.Serializable; -import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -69,14 +66,9 @@ import java.util.stream.StreamSupport; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; -import org.antlr.v4.runtime.ANTLRErrorListener; +import jenkins.util.antlr.JenkinsANTLRErrorListener; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.Parser; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.atn.ATNConfigSet; -import org.antlr.v4.runtime.dfa.DFA; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.StaplerRequest; @@ -619,35 +611,17 @@ public static Label get(String l) { * Parses the expression into a label expression tree. * * TODO: replace this with a real parser later + * + * @param labelExpression the label expression to be parsed + * @throws IllegalArgumentException if the label expression cannot be parsed */ - public static Label parseExpression(@NonNull String labelExpression) throws ANTLRException { - ANTLRErrorListener listener = new ANTLRErrorListener() { - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - throw new SemanticException(msg, line, charPositionInLine); - } - - @Override - public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) { - - } - - @Override - public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) { - - } - - @Override - public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) { - - } - }; + public static Label parseExpression(@NonNull String labelExpression) { LabelExpressionLexer lexer = new LabelExpressionLexer(CharStreams.fromString(labelExpression)); lexer.removeErrorListeners(); - lexer.addErrorListener(listener); + lexer.addErrorListener(new JenkinsANTLRErrorListener()); LabelExpressionParser parser = new LabelExpressionParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); - parser.addErrorListener(listener); + parser.addErrorListener(new JenkinsANTLRErrorListener()); return parser.expr().l; } diff --git a/core/src/main/java/hudson/model/labels/LabelExpression.java b/core/src/main/java/hudson/model/labels/LabelExpression.java index 2bf9ca55a4fd..20e666963883 100644 --- a/core/src/main/java/hudson/model/labels/LabelExpression.java +++ b/core/src/main/java/hudson/model/labels/LabelExpression.java @@ -42,7 +42,6 @@ import jenkins.model.Jenkins; import jenkins.model.labels.LabelAutoCompleteSeeder; import jenkins.model.labels.LabelValidator; -import org.antlr.v4.runtime.misc.ParseCancellationException; /** * Boolean expression of labels. @@ -282,7 +281,7 @@ public static FormValidation validate(@Nullable String expression, @CheckForNull } try { Label.parseExpression(expression); - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { return FormValidation.error(e, Messages.LabelExpression_InvalidBooleanExpression(e.getMessage())); } final Jenkins j = Jenkins.get(); diff --git a/core/src/main/java/hudson/scheduler/BaseParser.java b/core/src/main/java/hudson/scheduler/BaseParser.java index f068fa0c1bbb..2c386cb3bdbc 100644 --- a/core/src/main/java/hudson/scheduler/BaseParser.java +++ b/core/src/main/java/hudson/scheduler/BaseParser.java @@ -24,12 +24,10 @@ package hudson.scheduler; -import antlr.SemanticException; import jenkins.util.SystemProperties; +import org.antlr.v4.runtime.InputMismatchException; import org.antlr.v4.runtime.Parser; -import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.misc.ParseCancellationException; /** * @author Kohsuke Kawaguchi @@ -44,6 +42,11 @@ abstract class BaseParser extends Parser { */ protected Hash hash = Hash.zero(); + /** + * Custom error message overriding ANTLR's {@link InputMismatchException} + */ + private String errorMessage; + BaseParser(TokenStream input) { super(input); } @@ -53,7 +56,15 @@ public void setHash(Hash hash) { this.hash = hash; } - protected long doRange(int start, int end, int step, int field) throws ParseCancellationException { + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + protected long doRange(int start, int end, int step, int field) { rangeCheck(start, field); rangeCheck(end, field); if (step <= 0) @@ -68,7 +79,7 @@ protected long doRange(int start, int end, int step, int field) throws ParseCanc return bits; } - protected long doRange(int step, int field) throws ParseCancellationException { + protected long doRange(int step, int field) { return doRange(LOWER_BOUNDS[field], UPPER_BOUNDS[field], step, field); } @@ -79,14 +90,14 @@ protected long doRange(int step, int field) throws ParseCancellationException { * Increments. For example, 15 if "H/15". Or {@link #NO_STEP} to indicate * the special constant for "H" without the step value. */ - protected long doHash(int step, int field) throws ParseCancellationException { + protected long doHash(int step, int field) { int u = UPPER_BOUNDS[field]; if (field == 2) u = 28; // day of month can vary depending on month, so to make life simpler, just use [1,28] that's always safe if (field == 4) u = 6; // Both 0 and 7 of day of week are Sunday. For better distribution, limit upper bound to 6 return doHash(LOWER_BOUNDS[field], u, step, field); } - protected long doHash(int s, int e, int step, int field) throws ParseCancellationException { + protected long doHash(int s, int e, int step, int field) { rangeCheck(s, field); rangeCheck(e, field); if (step > e - s + 1) { @@ -109,19 +120,15 @@ protected long doHash(int s, int e, int step, int field) throws ParseCancellatio } } - protected void rangeCheck(int value, int field) throws ParseCancellationException { + protected void rangeCheck(int value, int field) { if (value < LOWER_BOUNDS[field] || UPPER_BOUNDS[field] < value) { error(Messages.BaseParser_OutOfRange(value, LOWER_BOUNDS[field], UPPER_BOUNDS[field])); } } - private void error(String msg) throws SemanticException { - Token t = _input.LT(-1); - throw new SemanticException( - msg, - t.getLine(), - t.getCharPositionInLine() - ); + private void error(String msg) { + setErrorMessage(msg); + throw new InputMismatchException(this); } protected Hash getHashForTokens() { diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index edc4663c03b0..bafe063f438f 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -36,6 +36,7 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jenkins.util.antlr.JenkinsANTLRErrorListener; import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -68,36 +69,49 @@ public final class CronTab { */ private @CheckForNull String specTimezone; + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ public CronTab(String format) { this(format, null); } + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ public CronTab(String format, Hash hash) { this(format, 1, hash); } /** - * @deprecated as of 1.448 - * Use {@link #CronTab(String, int, Hash)} + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + * @deprecated use {@link #CronTab(String, int, Hash)} */ - @Deprecated + @Deprecated(since = "1.448") public CronTab(String format, int line) { set(format, line, null); } /** + * @param format the crontab entry to be parsed * @param hash * Used to spread out token like "@daily". Null to preserve the legacy behaviour * of not spreading it out at all. + * @throws IllegalArgumentException if the crontab entry cannot be parsed */ public CronTab(String format, int line, Hash hash) { this(format, line, hash, null); } /** + * @param format the crontab entry to be parsed * @param timezone * Used to schedule cron in a different timezone. Null to use the default system * timezone + * @throws IllegalArgumentException if the crontab entry cannot be parsed * @since 1.615 */ public CronTab(String format, int line, Hash hash, @CheckForNull String timezone) { @@ -113,14 +127,16 @@ private void set(String format, int line, Hash hash) { */ private void set(String format, int line, Hash hash, String timezone) { CrontabLexer lexer = new CrontabLexer(CharStreams.fromString(format)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new JenkinsANTLRErrorListener()); lexer.setLine(line); - CommonTokenStream inputTokenStream = new CommonTokenStream(lexer); - CrontabParser parser = new CrontabParser(inputTokenStream); + CrontabParser parser = new CrontabParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(new JenkinsANTLRErrorListener(() -> parser.getErrorMessage())); parser.setErrorHandler(new BailErrorStrategy()); parser.setHash(hash); spec = format; specTimezone = timezone; - parser.startRule(this); if ((dayOfWeek & (1 << 7)) != 0) { dayOfWeek |= 1; // copy bit 7 over to bit 0 @@ -468,6 +484,10 @@ public Calendar floor(Calendar cal) { } } + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ void set(String format, Hash hash) { set(format, 1, hash); } diff --git a/core/src/main/java/hudson/scheduler/CronTabList.java b/core/src/main/java/hudson/scheduler/CronTabList.java index 4fa5c910cbd1..f9be65fe12d9 100644 --- a/core/src/main/java/hudson/scheduler/CronTabList.java +++ b/core/src/main/java/hudson/scheduler/CronTabList.java @@ -24,6 +24,7 @@ package hudson.scheduler; +import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Calendar; @@ -32,7 +33,6 @@ import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -91,11 +91,19 @@ public String checkSanity() { return null; } - public static CronTabList create(@NonNull String format) throws ParseCancellationException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public static CronTabList create(@NonNull String format) { return create(format, null); } - public static CronTabList create(@NonNull String format, Hash hash) throws ParseCancellationException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public static CronTabList create(@NonNull String format, Hash hash) { Vector r = new Vector<>(); int lineNumber = 0; String timezone = null; @@ -110,7 +118,7 @@ public static CronTabList create(@NonNull String format, Hash hash) throws Parse if (timezone != null) { LOGGER.log(Level.CONFIG, "CRON with timezone {0}", timezone); } else { - throw new ParseCancellationException("Invalid or unsupported timezone '" + timezoneString + "'"); + throw new ANTLRException("Invalid or unsupported timezone '" + timezoneString + "'"); } continue; } @@ -119,8 +127,8 @@ public static CronTabList create(@NonNull String format, Hash hash) throws Parse continue; // ignorable line try { r.add(new CronTab(line, lineNumber, hash, timezone)); - } catch (ParseCancellationException e) { - throw new ParseCancellationException(Messages.CronTabList_InvalidInput(line, e.toString()), e); + } catch (IllegalArgumentException e) { + throw new ANTLRException(Messages.CronTabList_InvalidInput(line, e.getMessage()), e); } } diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index 62fac5db831c..b9a0684c2ec7 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -42,7 +42,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.jcip.annotations.GuardedBy; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; @@ -67,6 +66,10 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy { private boolean ignorePostCommitHooks; @DataBoundConstructor - public SCMTrigger(String scmpoll_spec) throws ParseCancellationException { + public SCMTrigger(String scmpoll_spec) { super(scmpoll_spec); } @@ -125,7 +124,7 @@ public SCMTrigger(String scmpoll_spec) throws ParseCancellationException { * @deprecated since 2.21 */ @Deprecated - public SCMTrigger(String scmpoll_spec, boolean ignorePostCommitHooks) throws ParseCancellationException { + public SCMTrigger(String scmpoll_spec, boolean ignorePostCommitHooks) { super(scmpoll_spec); this.ignorePostCommitHooks = ignorePostCommitHooks; } diff --git a/core/src/main/java/hudson/triggers/TimerTrigger.java b/core/src/main/java/hudson/triggers/TimerTrigger.java index 45e70919c764..8d7cc16e92bd 100644 --- a/core/src/main/java/hudson/triggers/TimerTrigger.java +++ b/core/src/main/java/hudson/triggers/TimerTrigger.java @@ -40,7 +40,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.jenkinsci.Symbol; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; @@ -95,7 +94,7 @@ public FormValidation doCheckSpec(@QueryParameter String value, @AncestorInPath updateValidationsForSanity(validations, ctl); updateValidationsForNextRun(validations, ctl); return FormValidation.aggregate(validations); - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { if (value.trim().indexOf('\n') == -1 && value.contains("**")) return FormValidation.error(Messages.TimerTrigger_MissingWhitespace()); return FormValidation.error(e.getMessage()); diff --git a/core/src/main/java/hudson/triggers/Trigger.java b/core/src/main/java/hudson/triggers/Trigger.java index c77ea181f075..f7415597d1a7 100644 --- a/core/src/main/java/hudson/triggers/Trigger.java +++ b/core/src/main/java/hudson/triggers/Trigger.java @@ -64,7 +64,6 @@ import jenkins.model.Jenkins; import jenkins.triggers.TriggeredItem; import jenkins.util.SystemProperties; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -100,7 +99,7 @@ public void start(J project, boolean newInstance) { } else { LOGGER.log(Level.WARNING, "The job {0} has a null crontab spec which is incorrect", job.getFullName()); } - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { // this shouldn't fail because we've already parsed stuff in the constructor, // so if it fails, use whatever 'tabs' that we already have. LOGGER.log(Level.WARNING, String.format("Failed to parse crontab spec %s in job %s", spec, project.getFullName()), e); @@ -170,8 +169,11 @@ public TriggerDescriptor getDescriptor() { * Creates a new {@link Trigger} that gets {@link #run() run} * periodically. This is useful when your trigger does * some polling work. + * + * @param cronTabSpec the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed */ - protected Trigger(@NonNull String cronTabSpec) throws ParseCancellationException { + protected Trigger(@NonNull String cronTabSpec) { this.spec = cronTabSpec; this.tabs = CronTabList.create(cronTabSpec); } @@ -196,7 +198,7 @@ public final String getSpec() { protected Object readResolve() throws ObjectStreamException { try { tabs = CronTabList.create(spec); - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { InvalidObjectException x = new InvalidObjectException(e.getMessage()); x.initCause(e); throw x; diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index a73d91abaf42..eb9b8f58596a 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -289,7 +289,6 @@ import jenkins.util.xml.XMLUtils; import net.jcip.annotations.GuardedBy; import net.sf.json.JSONObject; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.Script; import org.apache.commons.logging.LogFactory; @@ -2071,7 +2070,7 @@ public Label getLabel(String expr) { // For the record, this method creates temporary labels but there is a periodic task // calling "trimLabels" to remove unused labels running every 5 minutes. labels.putIfAbsent(expr, Label.parseExpression(expr)); - } catch (ParseCancellationException e) { + } catch (IllegalArgumentException e) { // laxly accept it as a single label atom for backward compatibility return getLabelAtom(expr); } diff --git a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java new file mode 100644 index 000000000000..a09ddda04ab9 --- /dev/null +++ b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java @@ -0,0 +1,56 @@ +package jenkins.util.antlr; + +import antlr.ANTLRException; +import java.util.function.Supplier; +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +@Restricted(NoExternalUse.class) +public class JenkinsANTLRErrorListener extends BaseErrorListener { + + private final Supplier errorMessageSupplier; + + public JenkinsANTLRErrorListener() { + errorMessageSupplier = () -> null; + } + + public JenkinsANTLRErrorListener(Supplier errorMessageSupplier) { + this.errorMessageSupplier = errorMessageSupplier; + } + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + String errorMessage = errorMessageSupplier.get(); + if (errorMessage != null) { + msg = errorMessage; + } + throw new ANTLRException(formatMessage(line, charPositionInLine, msg), e); + } + + public static String formatMessage(int line, int column, String message) { + StringBuilder sb = new StringBuilder(); + + if (line != -1) { + sb.append("line "); + sb.append(line); + + if (column != -1) { + sb.append(":" + column); + } + sb.append(":"); + sb.append(" "); + } + + sb.append(message); + return sb.toString(); + } +} diff --git a/core/src/test/java/hudson/scheduler/CronTabTest.java b/core/src/test/java/hudson/scheduler/CronTabTest.java index 5ed88f00a92f..813ae6c375c5 100644 --- a/core/src/test/java/hudson/scheduler/CronTabTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabTest.java @@ -25,10 +25,13 @@ package hudson.scheduler; import static java.util.Calendar.MONDAY; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import antlr.ANTLRException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; @@ -36,7 +39,6 @@ import java.util.List; import java.util.Locale; import java.util.TimeZone; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -266,9 +268,10 @@ public int next(int n) { compare(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 0))); compare(new GregorianCalendar(2013, Calendar.MARCH, 21, 1, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 5))); - ParseCancellationException e = assertThrows(ParseCancellationException.class, () -> + ANTLRException e = assertThrows(ANTLRException.class, () -> compare(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 0)))); - assertEquals("line 1:8: 15 is an invalid value. Must be within 1 and 4", e.toString()); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:9: 15 is an invalid value. Must be within 1 and 4", e.getMessage()); } @Test public void repeatedHash() throws Exception { @@ -287,13 +290,15 @@ public int next(int n) { } @Test public void rangeBoundsCheckFailHour() { - ParseCancellationException e = assertThrows(ParseCancellationException.class, () -> new CronTab("H H(12-24) * * *")); - assertEquals("line 1:10: 24 is an invalid value. Must be within 0 and 23", e.toString()); + ANTLRException e = assertThrows(ANTLRException.class, () -> new CronTab("H H(12-24) * * *")); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:10: 24 is an invalid value. Must be within 0 and 23", e.getMessage()); } @Test public void rangeBoundsCheckFailMinute() { - ParseCancellationException e = assertThrows(ParseCancellationException.class, () -> new CronTab("H(33-66) * * * *")); - assertEquals("line 1:8: 66 is an invalid value. Must be within 0 and 59", e.toString()); + ANTLRException e = assertThrows(ANTLRException.class, () -> new CronTab("H(33-66) * * * *")); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:8: 66 is an invalid value. Must be within 0 and 59", e.getMessage()); } @Issue("JENKINS-9283") diff --git a/test/src/test/java/hudson/model/ProjectTest.java b/test/src/test/java/hudson/model/ProjectTest.java index 64c62ec1a1c3..3a19eb27e0b8 100644 --- a/test/src/test/java/hudson/model/ProjectTest.java +++ b/test/src/test/java/hudson/model/ProjectTest.java @@ -97,7 +97,6 @@ import jenkins.model.WorkspaceWriter; import jenkins.scm.DefaultSCMCheckoutStrategyImpl; import jenkins.scm.SCMCheckoutStrategy; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -328,7 +327,7 @@ public void testScheduleBuild2() throws Exception { @Test - public void testSchedulePolling() throws IOException, ParseCancellationException { + public void testSchedulePolling() throws IOException { FreeStyleProject p = j.createFreeStyleProject("project"); assertFalse("Project should not schedule polling because no scm trigger is set.", p.schedulePolling()); SCMTrigger trigger = new SCMTrigger("0 0 * * *"); diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index b87b481d25ac..9bc939ab1972 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; +import antlr.ANTLRException; import hudson.Functions; import hudson.Launcher; import hudson.model.AbstractBuild; @@ -47,7 +48,6 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Future; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -188,15 +188,15 @@ private void parseAndVerify(String expected, String expr) { @Test public void parserError() { - parseShouldFail("foo bar", "line 1:5: extraneous input 'bar' expecting "); - parseShouldFail("foo (bar)", "line 1:5: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); - parseShouldFail("foo(bar)", "line 1:4: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); - parseShouldFail("a <- b", "line 1:3: token recognition error at: '<- '"); - parseShouldFail("a -< b", "line 1:4: token recognition error at: '< '"); - parseShouldFail("a - b", "line 1:3: mismatched input '-' expecting {, '&&', '||', '->', '<->'}"); - parseShouldFail("->", "line 1:1: mismatched input '->' expecting {'!', '(', ATOM, STRINGLITERAL}"); - parseShouldFail("-<", "line 1:2: token recognition error at: '<'"); - parseShouldFail("-!", "line 1:2: extraneous input '!' expecting "); + parseShouldFail("foo bar", "line 1:4: extraneous input 'bar' expecting "); + parseShouldFail("foo (bar)", "line 1:4: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("foo(bar)", "line 1:3: mismatched input '(' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("a <- b", "line 1:2: token recognition error at: '<- '"); + parseShouldFail("a -< b", "line 1:3: token recognition error at: '< '"); + parseShouldFail("a - b", "line 1:2: mismatched input '-' expecting {, '&&', '||', '->', '<->'}"); + parseShouldFail("->", "line 1:0: mismatched input '->' expecting {'!', '(', ATOM, STRINGLITERAL}"); + parseShouldFail("-<", "line 1:1: token recognition error at: '<'"); + parseShouldFail("-!", "line 1:1: extraneous input '!' expecting "); } @Test @@ -345,11 +345,12 @@ public void expression_and_withoutSpaces() throws Exception { } private void parseShouldFail(String expr, String message) { - ParseCancellationException e = assertThrows( + ANTLRException e = assertThrows( expr + " should fail to parse", - ParseCancellationException.class, + ANTLRException.class, () -> Label.parseExpression(expr)); - assertEquals(message, e.toString()); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals(message, e.getMessage()); } @Test diff --git a/test/src/test/java/hudson/triggers/TriggerTest.java b/test/src/test/java/hudson/triggers/TriggerTest.java index 60c61306fb7f..180859f9759f 100644 --- a/test/src/test/java/hudson/triggers/TriggerTest.java +++ b/test/src/test/java/hudson/triggers/TriggerTest.java @@ -30,7 +30,6 @@ import java.nio.charset.StandardCharsets; import java.util.Calendar; import java.util.GregorianCalendar; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -60,7 +59,7 @@ public static class MockTrigger extends Trigger { public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); @DataBoundConstructor - public MockTrigger(String cron) throws ParseCancellationException { + public MockTrigger(String cron) { super(cron); } diff --git a/test/src/test/java/jenkins/triggers/TriggerTest.java b/test/src/test/java/jenkins/triggers/TriggerTest.java index 0f808ac3f0e5..b05b5d469578 100644 --- a/test/src/test/java/jenkins/triggers/TriggerTest.java +++ b/test/src/test/java/jenkins/triggers/TriggerTest.java @@ -15,7 +15,6 @@ import hudson.triggers.TriggerDescriptor; import java.util.logging.Level; import java.util.logging.Logger; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -64,7 +63,7 @@ public static class BadTimerTrigger extends TimerTrigger { private static final Logger LOGGER = Logger.getLogger(BadTimerTrigger.class.getName()); - BadTimerTrigger(@NonNull final String specs) throws ParseCancellationException { + BadTimerTrigger(@NonNull final String specs) { super(specs); } From 8f52de2aa0eaa57c815abd34f93dd434ea6ffa1f Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 10:56:51 -0700 Subject: [PATCH 14/24] fixups --- core/src/main/java/hudson/scheduler/CronTab.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index bafe063f438f..9625feecccfd 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -132,11 +132,12 @@ private void set(String format, int line, Hash hash, String timezone) { lexer.setLine(line); CrontabParser parser = new CrontabParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); - parser.addErrorListener(new JenkinsANTLRErrorListener(() -> parser.getErrorMessage())); + parser.addErrorListener(new JenkinsANTLRErrorListener(parser::getErrorMessage)); parser.setErrorHandler(new BailErrorStrategy()); parser.setHash(hash); spec = format; specTimezone = timezone; + parser.startRule(this); if ((dayOfWeek & (1 << 7)) != 0) { dayOfWeek |= 1; // copy bit 7 over to bit 0 From f4b0ec8280a8cecd11356955e413023bee592aaf Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 11:25:09 -0700 Subject: [PATCH 15/24] No need for BailErrorStrategy and it is inconsistent with label expression parsing --- core/src/main/java/hudson/scheduler/CronTab.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index 9625feecccfd..2c4b188a0de2 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -37,7 +37,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import jenkins.util.antlr.JenkinsANTLRErrorListener; -import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -133,7 +132,6 @@ private void set(String format, int line, Hash hash, String timezone) { CrontabParser parser = new CrontabParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); parser.addErrorListener(new JenkinsANTLRErrorListener(parser::getErrorMessage)); - parser.setErrorHandler(new BailErrorStrategy()); parser.setHash(hash); spec = format; specTimezone = timezone; From 6c533d56b6331bdd7414560e0169057202ccc05c Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 11:25:13 -0700 Subject: [PATCH 16/24] fixups --- .../java/jenkins/util/antlr/JenkinsANTLRErrorListener.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java index a09ddda04ab9..af16c9347f34 100644 --- a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java +++ b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java @@ -38,18 +38,15 @@ public void syntaxError( public static String formatMessage(int line, int column, String message) { StringBuilder sb = new StringBuilder(); - if (line != -1) { sb.append("line "); sb.append(line); - if (column != -1) { sb.append(":" + column); } sb.append(":"); sb.append(" "); } - sb.append(message); return sb.toString(); } From c96cf97fbff9b34577ae0318acc163fb7389ae08 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 11:35:27 -0700 Subject: [PATCH 17/24] we like stacks --- core/src/main/java/hudson/triggers/TimerTrigger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/triggers/TimerTrigger.java b/core/src/main/java/hudson/triggers/TimerTrigger.java index 8d7cc16e92bd..bed439528659 100644 --- a/core/src/main/java/hudson/triggers/TimerTrigger.java +++ b/core/src/main/java/hudson/triggers/TimerTrigger.java @@ -97,7 +97,7 @@ public FormValidation doCheckSpec(@QueryParameter String value, @AncestorInPath } catch (IllegalArgumentException e) { if (value.trim().indexOf('\n') == -1 && value.contains("**")) return FormValidation.error(Messages.TimerTrigger_MissingWhitespace()); - return FormValidation.error(e.getMessage()); + return FormValidation.error(e, e.getMessage()); } } From 0a434b445d714b30c808c2b1e6f73f9065dbeea8 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 11:48:17 -0700 Subject: [PATCH 18/24] Extract version into property for easy updating --- bom/pom.xml | 2 +- pom.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 4ef1b551ccb8..484dc64f981b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -188,7 +188,7 @@ THE SOFTWARE. org.antlr antlr4 - 4.11.1 + ${antlr.version} org.apache.ant diff --git a/pom.xml b/pom.xml index c1d98b957fef..bd42c721083a 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,7 @@ THE SOFTWARE. ${maven.multiModuleProjectDirectory}/src/spotbugs/spotbugs-excludes.xml 1.27 + 4.11.1 1.23 2.27.2 6.6 @@ -299,7 +300,7 @@ THE SOFTWARE. org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} antlr From 9e394c4f977b59f8e1c7fb3228aeddc9dfd1b24e Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 11:48:34 -0700 Subject: [PATCH 19/24] no need for executions in plugin management section --- pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pom.xml b/pom.xml index bd42c721083a..d90f8d83244e 100644 --- a/pom.xml +++ b/pom.xml @@ -301,14 +301,6 @@ THE SOFTWARE. org.antlr antlr4-maven-plugin ${antlr.version} - - - antlr - - antlr4 - - - org.apache.maven.plugins From 2011696084c9c594bad683d58bb638e3c61d9adb Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 12:18:29 -0700 Subject: [PATCH 20/24] Remove unnecessary JARs from WAR --- bom/pom.xml | 2 +- core/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 484dc64f981b..e187ab08f7ea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -187,7 +187,7 @@ THE SOFTWARE. org.antlr - antlr4 + antlr4-runtime ${antlr.version} diff --git a/core/pom.xml b/core/pom.xml index 880934047208..be76f35c8bbe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -276,7 +276,7 @@ THE SOFTWARE. org.antlr - antlr4 + antlr4-runtime org.apache.ant From 84d8e35b0996ec11b8cd2a8a80417660eaccd649 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 28 Oct 2022 13:00:27 -0700 Subject: [PATCH 21/24] stack traces are wonderful --- .../java/hudson/slaves/SimpleScheduledRetentionStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index b9a0684c2ec7..4ec380870d0c 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -268,7 +268,7 @@ public FormValidation doCheck(@QueryParameter String value) { return FormValidation.warning(msg); return FormValidation.ok(); } catch (IllegalArgumentException e) { - return FormValidation.error(e.getMessage()); + return FormValidation.error(e, e.getMessage()); } } } From f19185d84eeb14d9237843410615866bc53ceb69 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 29 Oct 2022 07:53:43 -0700 Subject: [PATCH 22/24] Update .gitignore --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 365fb4df9fde..021201b717a3 100644 --- a/.gitignore +++ b/.gitignore @@ -59,7 +59,3 @@ war/node_modules/ war/yarn-error.log .java-version .checkstyle - -# ANTLR generated files -*.tokens -core/gen/ From e4f50b39a0809dd9838e2024155338280682b955 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 29 Oct 2022 07:55:40 -0700 Subject: [PATCH 23/24] fixups --- .../hudson/slaves/SimpleScheduledRetentionStrategy.java | 3 +-- .../java/jenkins/util/antlr/JenkinsANTLRErrorListener.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index 4ec380870d0c..6f09e8109973 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -71,8 +71,7 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy Date: Sun, 30 Oct 2022 10:29:33 -0700 Subject: [PATCH 24/24] fixup --- .../main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java index 2b5940f70861..0d150b10b698 100644 --- a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java +++ b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java @@ -36,7 +36,7 @@ public void syntaxError( throw new ANTLRException(formatMessage(line, charPositionInLine, msg), e); } - public static String formatMessage(int line, int column, String message) { + private static String formatMessage(int line, int column, String message) { StringBuilder sb = new StringBuilder(); if (line != -1) { sb.append("line ");