From 43a3bc588371028ef77c0645b6728592dcc1cf20 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 3 Jun 2023 10:14:07 +0200 Subject: [PATCH] HHH-16022 fix extremely ugly errors when HQL parsing failed the formatting of syntax errors was terrible --- .../hql/internal/StandardHqlTranslator.java | 68 +++++++++++++------ .../orm/test/hql/ASTParserLoadingTest.java | 2 +- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java index 3554430fe5..90c822d1de 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java @@ -8,6 +8,9 @@ package org.hibernate.query.hql.internal; import java.util.BitSet; +import org.antlr.v4.runtime.CommonToken; +import org.antlr.v4.runtime.InputMismatchException; +import org.antlr.v4.runtime.NoViableAltException; import org.hibernate.QueryException; import org.hibernate.grammars.hql.HqlLexer; import org.hibernate.grammars.hql.HqlParser; @@ -32,6 +35,8 @@ import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.misc.ParseCancellationException; +import static java.util.stream.Collectors.toList; + /** * Standard implementation of {@link HqlTranslator}. * @@ -39,26 +44,6 @@ import org.antlr.v4.runtime.misc.ParseCancellationException; */ public class StandardHqlTranslator implements HqlTranslator { - protected static final ANTLRErrorListener ERR_LISTENER = new ANTLRErrorListener() { - - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - throw new ParsingException( "At position " + line + ":" + charPositionInLine + ", " + msg); - } - - @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) { - } - }; - private final SqmCreationContext sqmCreationContext; private final SqmCreationOptions sqmCreationOptions; @@ -105,11 +90,50 @@ public class StandardHqlTranslator implements HqlTranslator { // Build the parse tree final HqlParser hqlParser = HqlParseTreeBuilder.INSTANCE.buildHqlParser( hql, hqlLexer ); + ANTLRErrorListener errorListener = new ANTLRErrorListener() { + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + String errorText = "At " + line + ":" + charPositionInLine; + if ( offendingSymbol instanceof CommonToken ) { + String token = ( (CommonToken) offendingSymbol ).getText(); + if ( token != null && !token.isEmpty() ) { + errorText += " and token '" + token + "'"; + } + } + errorText += ", "; + if ( e instanceof NoViableAltException ) { + errorText += msg.substring( 0, msg.indexOf("'") ); + String lineText = hql.lines().collect( toList() ).get( line-1 ); + String text = lineText.substring( 0, charPositionInLine ) + "*" + lineText.substring( charPositionInLine ); + errorText += "'" + text + "'"; + } + else if ( e instanceof InputMismatchException ) { + errorText += msg.substring( 0,msg.length()-1 ).replace(" expecting {", ", expecting one of the following tokens: "); + } + else { + errorText += msg; + } + throw new ParsingException( errorText ); + } + + @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) { + } + }; + // try to use SLL(k)-based parsing first - its faster - hqlLexer.addErrorListener( ERR_LISTENER ); + hqlLexer.addErrorListener( errorListener ); hqlParser.getInterpreter().setPredictionMode( PredictionMode.SLL ); hqlParser.removeErrorListeners(); - hqlParser.addErrorListener( ERR_LISTENER ); + hqlParser.addErrorListener( errorListener ); hqlParser.setErrorHandler( new BailErrorStrategy() ); try { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ASTParserLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ASTParserLoadingTest.java index 2feeb6c798..86ee6f90aa 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ASTParserLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ASTParserLoadingTest.java @@ -3745,7 +3745,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { catch (IllegalArgumentException e) { final Throwable cause = e.getCause(); assertThat( cause, instanceOf( ParsingException.class ) ); - assertTrue( cause.getMessage().contains( "mismatched input ')' expecting {" ) ); + assertTrue( cause.getMessage().contains( "mismatched input ')'" ) ); } }