From 012161d90d6a8a6bb930b90601fb89ff6cc3ae60 Mon Sep 17 00:00:00 2001 From: Tobias Guggenmos Date: Mon, 16 Mar 2020 15:47:47 +0100 Subject: [PATCH] PromQL: Fix lexer error handling (#6958) * PromQL: Fix lexer error handling This fixes bugs in the handling of lexer errors that are only noticeable for users of the language server and caused https://github.com/prometheus-community/promql-langserver/issues/104 . Signed-off-by: Tobias Guggenmos * Add test for error position ranges Signed-off-by: Tobias Guggenmos --- promql/parser/parse.go | 16 +++++++++++++--- promql/parser/parse_test.go | 10 ++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/promql/parser/parse.go b/promql/parser/parse.go index 79424dd98d..1b6d8a9306 100644 --- a/promql/parser/parse.go +++ b/promql/parser/parse.go @@ -242,6 +242,11 @@ func (p *parser) addParseErr(positionRange PositionRange, err error) { func (p *parser) unexpected(context string, expected string) { var errMsg strings.Builder + // Do not report lexer errors twice + if p.yyParser.lval.item.Typ == ERROR { + return + } + errMsg.WriteString("unexpected ") errMsg.WriteString(p.yyParser.lval.item.desc()) @@ -302,10 +307,15 @@ func (p *parser) Lex(lval *yySymType) int { } switch typ { - case ERROR: - p.addParseErrf(lval.item.PositionRange(), "%s", lval.item.Val) - p.InjectItem(0) + pos := PositionRange{ + Start: p.lex.start, + End: Pos(len(p.lex.input)), + } + p.addParseErr(pos, errors.New(p.yyParser.lval.item.Val)) + + // Tells yacc that this is the end of input. + return 0 case EOF: lval.item.Typ = EOF p.InjectItem(0) diff --git a/promql/parser/parse_test.go b/promql/parser/parse_test.go index f99c4762c8..2cb9467b0d 100644 --- a/promql/parser/parse_test.go +++ b/promql/parser/parse_test.go @@ -2592,6 +2592,16 @@ func TestParseExpressions(t *testing.T) { } else { testutil.NotOk(t, err) testutil.Assert(t, strings.Contains(err.Error(), test.errMsg), "unexpected error on input '%s', expected '%s', got '%s'", test.input, test.errMsg, err.Error()) + + errorList, ok := err.(ParseErrors) + + testutil.Assert(t, ok, "unexpected error type") + + for _, e := range errorList { + testutil.Assert(t, 0 <= e.PositionRange.Start, "parse error has negative position\nExpression '%s'\nError: %v", test.input, e) + testutil.Assert(t, e.PositionRange.Start <= e.PositionRange.End, "parse error has negative length\nExpression '%s'\nError: %v", test.input, e) + testutil.Assert(t, e.PositionRange.End <= Pos(len(test.input)), "parse error is not contained in input\nExpression '%s'\nError: %v", test.input, e) + } } } }