https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/214348
>From 0fedba653fb77500356ad89222eaf1a754d73756 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Mon, 3 Aug 2026 22:53:48 +0500 Subject: [PATCH 1/5] [lldb] Restrict expressions to one assignment in DIL --- lldb/docs/dil-expr-lang.ebnf | 22 ++--- lldb/include/lldb/ValueObject/DILParser.h | 3 +- lldb/source/ValueObject/DILParser.cpp | 89 ++++++++++--------- .../expr/Assignment/TestFrameVarDILAssign.py | 23 +++++ .../TestFrameVarDILCompositeAssign.py | 17 ++++ 5 files changed, 103 insertions(+), 51 deletions(-) diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index a0d49204ceb55..9d0c35f8c8c97 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,18 +3,20 @@ (* This is currently a subset of the final DIL Language, matching the current DIL implementation. *) -expression = conditional_expression ; +expression = assignment_expression ; -conditional_expression = assignment_expression - | assignment_expression "?" expression ":" expression ; - -assignment_expression = logical_or_expression - | logical_or_expression assignment_operator assignment_expression ; +assignment_expression = pure_expression + | pure_expression assignment_operator pure_expression ; assignment_operator = "=" | "+=" | "-=" ; +pure_expression = conditional_expression ; + +conditional_expression = logical_or_expression + | logical_or_expression "?" pure_expression ":" pure_expression ; + logical_or_expression = logical_and_expression {"||" logical_and_expression} ; logical_and_expression = inclusive_or_expression {"&&" inclusive_or_expression} ; @@ -52,16 +54,16 @@ unary_expression = postfix_expression unary_operator = "*" | "&" | "+" | "-" | "~" | "!" ; postfix_expression = primary_expression - | postfix_expression "[" expression "]" - | postfix_expression "[" expression ":" expression "]" + | postfix_expression "[" pure_expression "]" + | postfix_expression "[" pure_expression ":" pure_expression "]" | postfix_expression "." id_expression | postfix_expression "->" id_expression ; primary_expression = numeric_literal | boolean_literal | id_expression - | "(" expression ")" - | "sizeof" "(" expression ")" + | "(" pure_expression ")" + | "sizeof" "(" pure_expression ")" | "sizeof" "(" type_id ")" ; id_expression = unqualified_id diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index cc495d48d93c5..0e0982acd9192 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -82,8 +82,9 @@ class DILParser { ASTNodeUP Run(); ASTNodeUP ParseExpression(); - ASTNodeUP ParseConditionalExpression(); ASTNodeUP ParseAssignmentExpression(); + ASTNodeUP ParsePureExpression(); + ASTNodeUP ParseConditionalExpression(); ASTNodeUP ParseLogicalOrExpression(); ASTNodeUP ParseLogicalAndExpression(); ASTNodeUP ParseInclusiveOrExpression(); diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index dd6e6244461a4..8a442a5d418b8 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -129,41 +129,13 @@ ASTNodeUP DILParser::Run() { // expression: // assignment_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseConditionalExpression(); } - -// Parse a conditional_expression. -// -// conditional_expression: -// assignment_expression -// assignment_expression "?" expression ":" expression -// -ASTNodeUP DILParser::ParseConditionalExpression() { - auto lhs = ParseAssignmentExpression(); - assert(lhs && "ASTNodeUP must not contain a nullptr"); - - // Check if it's a ternary operator. - if (CurToken().Is(Token::question)) { - Token token = CurToken(); - m_dil_lexer.Advance(); - auto true_op = ParseExpression(); - assert(true_op && "ASTNodeUP must not contain a nullptr"); - Expect(Token::colon); - m_dil_lexer.Advance(); - auto false_op = ParseExpression(); - assert(false_op && "ASTNodeUP must not contain a nullptr"); - lhs = std::make_unique<ConditionalNode>(token.GetLocation(), std::move(lhs), - std::move(true_op), - std::move(false_op)); - } - - return lhs; -} +ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); } // Parse an assignment_expression // // assignment_expression -// logical_or_expression -// logical_or_expression assignment_operator assignment_expression +// pure_expression +// pure_expression assignment_operator pure_expression // // assignment_operator: // "=" @@ -171,7 +143,7 @@ ASTNodeUP DILParser::ParseConditionalExpression() { // "-=" // ASTNodeUP DILParser::ParseAssignmentExpression() { - auto lhs = ParseLogicalOrExpression(); + auto lhs = ParsePureExpression(); assert(lhs && "ASTNodeUP must not contain a nullptr"); // Check if it's an assignment expression. @@ -179,7 +151,7 @@ ASTNodeUP DILParser::ParseAssignmentExpression() { // That's an assignment! Token token = CurToken(); m_dil_lexer.Advance(); - auto rhs = ParseAssignmentExpression(); + auto rhs = ParsePureExpression(); assert(rhs && "ASTNodeUP must not contain a nullptr"); lhs = std::make_unique<BinaryOpNode>( token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), @@ -188,6 +160,43 @@ ASTNodeUP DILParser::ParseAssignmentExpression() { return lhs; } +// Parse a pure expression without side effects. +// +// pure_expression: +// conditional_expression +// +ASTNodeUP DILParser::ParsePureExpression() { + return ParseConditionalExpression(); +} + +// Parse a conditional_expression. +// +// conditional_expression: +// logical_or_expression +// logical_or_expression "?" pure_expression ":" pure_expression +// +ASTNodeUP DILParser::ParseConditionalExpression() { + auto lhs = ParseLogicalOrExpression(); + assert(lhs && "ASTNodeUP must not contain a nullptr"); + + // Check if it's a ternary operator. + if (CurToken().Is(Token::question)) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto true_op = ParsePureExpression(); + assert(true_op && "ASTNodeUP must not contain a nullptr"); + Expect(Token::colon); + m_dil_lexer.Advance(); + auto false_op = ParsePureExpression(); + assert(false_op && "ASTNodeUP must not contain a nullptr"); + lhs = std::make_unique<ConditionalNode>(token.GetLocation(), std::move(lhs), + std::move(true_op), + std::move(false_op)); + } + + return lhs; +} + // Parse a logical_or_expression. // // logical_or_expression: @@ -526,8 +535,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // // postfix_expression: // primary_expression -// postfix_expression "[" expression "]" -// postfix_expression "[" expression ":" expression "]" +// postfix_expression "[" pure_expression "]" +// postfix_expression "[" pure_expression ":" pure_expression "]" // postfix_expression "." id_expression // postfix_expression "->" id_expression // @@ -540,11 +549,11 @@ ASTNodeUP DILParser::ParsePostfixExpression() { switch (token.GetKind()) { case Token::l_square: { m_dil_lexer.Advance(); - ASTNodeUP index = ParseExpression(); + ASTNodeUP index = ParsePureExpression(); assert(index && "ASTNodeUP must not contain a nullptr"); if (CurToken().GetKind() == Token::colon) { m_dil_lexer.Advance(); - ASTNodeUP last_index = ParseExpression(); + ASTNodeUP last_index = ParsePureExpression(); assert(last_index && "ASTNodeUP must not contain a nullptr"); lhs = std::make_unique<BitFieldExtractionNode>( loc, std::move(lhs), std::move(index), std::move(last_index)); @@ -584,7 +593,7 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // numeric_literal // boolean_literal // id_expression -// "(" expression ")" +// "(" pure_expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { if (CurToken().IsOneOf({Token::integer_constant, Token::float_constant})) @@ -608,7 +617,7 @@ ASTNodeUP DILParser::ParsePrimaryExpression() { return std::make_unique<SizeOfNode>(loc, *type_id); } TentativeParsingRollback(save_token_idx); - ASTNodeUP expr = ParseExpression(); + ASTNodeUP expr = ParsePureExpression(); Expect(Token::r_paren); m_dil_lexer.Advance(); return std::make_unique<SizeOfNode>(loc, std::move(expr)); @@ -619,7 +628,7 @@ ASTNodeUP DILParser::ParsePrimaryExpression() { if (CurToken().Is(Token::l_paren)) { m_dil_lexer.Advance(); - auto expr = ParseExpression(); + auto expr = ParsePureExpression(); Expect(Token::r_paren); m_dil_lexer.Advance(); return expr; diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py index 5734395cf0ff0..826adc03d0d8d 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py @@ -206,3 +206,26 @@ def test_assignment(self): error=True, substrs=["new value is too big"], ) + + # Check that there can be only one assignment and only at top level + self.expect( + "frame variable 'i = i = 1'", + error=True, + substrs=["expected 'eof', got: <'='"], + ) + self.expect( + "frame variable 'i = 1 - (i = 1)'", + error=True, + substrs=["expected 'r_paren', got: <'='"], + ) + self.expect( + "frame variable '1 + (i = 1)'", + error=True, + substrs=["expected 'r_paren', got: <'='"], + ) + self.expect( + "frame variable '(i = 1) + 1'", + error=True, + substrs=["expected 'r_paren', got: <'='"], + ) + self.expect("frame variable '*(arr + 1) = 5'", substrs=["= 5"]) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py index 6f87fba87c9a5..9b58a8323ea5e 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py @@ -81,3 +81,20 @@ def test_assignment(self): "frame variable 'p = (int *)10'", substrs=["p = 0x000000000000000a"] ) self.expect("frame variable 'p -= 2'", substrs=["p = 0x0000000000000002"]) + + # Check that there can be only one assignment and only at top level + self.expect( + "frame variable 'i = i += 1'", + error=True, + substrs=["expected 'eof', got: <'+='"], + ) + self.expect( + "frame variable 'i += 1 + (i -= 1)'", + error=True, + substrs=["expected 'r_paren', got: <'-='"], + ) + self.expect( + "frame variable '*(arr + 1) = arr[1] += 1'", + error=True, + substrs=["expected 'eof', got: <'+='"], + ) >From 2b3f22ef679ac8f5f2a05f73dfe800fa9405a547 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 6 Aug 2026 01:03:57 +0500 Subject: [PATCH 2/5] Add a verbose error for unexpected assignment --- lldb/source/ValueObject/DILParser.cpp | 4 ++++ .../var-dil/expr/Assignment/TestFrameVarDILAssign.py | 8 ++++---- .../expr/Assignment/TestFrameVarDILCompositeAssign.py | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 8a442a5d418b8..30cb96dd9b1a7 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -1070,6 +1070,10 @@ ASTNodeUP DILParser::ParseFloatingPointLiteral() { } void DILParser::Expect(Token::Kind kind) { + if (CurToken().IsOneOf({Token::equal, Token::plusequal, Token::minusequal})) { + BailOut("Assignment is allowed only at top level.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + } if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), CurToken().GetLocation(), CurToken().GetSpelling().length()); diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py index 826adc03d0d8d..d954467c2cf97 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py @@ -211,21 +211,21 @@ def test_assignment(self): self.expect( "frame variable 'i = i = 1'", error=True, - substrs=["expected 'eof', got: <'='"], + substrs=["Assignment is allowed only at top level"], ) self.expect( "frame variable 'i = 1 - (i = 1)'", error=True, - substrs=["expected 'r_paren', got: <'='"], + substrs=["Assignment is allowed only at top level"], ) self.expect( "frame variable '1 + (i = 1)'", error=True, - substrs=["expected 'r_paren', got: <'='"], + substrs=["Assignment is allowed only at top level"], ) self.expect( "frame variable '(i = 1) + 1'", error=True, - substrs=["expected 'r_paren', got: <'='"], + substrs=["Assignment is allowed only at top level"], ) self.expect("frame variable '*(arr + 1) = 5'", substrs=["= 5"]) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py index 9b58a8323ea5e..17e3dce781d96 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py @@ -86,15 +86,15 @@ def test_assignment(self): self.expect( "frame variable 'i = i += 1'", error=True, - substrs=["expected 'eof', got: <'+='"], + substrs=["Assignment is allowed only at top level"], ) self.expect( "frame variable 'i += 1 + (i -= 1)'", error=True, - substrs=["expected 'r_paren', got: <'-='"], + substrs=["Assignment is allowed only at top level"], ) self.expect( "frame variable '*(arr + 1) = arr[1] += 1'", error=True, - substrs=["expected 'eof', got: <'+='"], + substrs=["Assignment is allowed only at top level"], ) >From e0f12469919611e0e8f50d64e758ceea70f051c7 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 6 Aug 2026 02:51:10 +0500 Subject: [PATCH 3/5] Add errors for -- and ++ --- lldb/include/lldb/ValueObject/DILLexer.h | 2 ++ lldb/source/ValueObject/DILLexer.cpp | 6 +++++ lldb/source/ValueObject/DILParser.cpp | 22 +++++++++++++++++-- .../TestFrameVarDILCompositeAssign.py | 22 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 69d6b49800b29..446891d5ca000 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -51,12 +51,14 @@ class Token { lessless, minus, minusequal, + minusminus, percent, period, pipe, pipepipe, plus, plusequal, + plusplus, question, r_paren, r_square, diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 0f1978e28e512..abc54f1fd2f46 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -72,6 +72,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "minus"; case Kind::minusequal: return "minusequal"; + case Kind::minusminus: + return "minusminus"; case Token::percent: return "percent"; case Kind::period: @@ -84,6 +86,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "plus"; case Kind::plusequal: return "plusequal"; + case Kind::plusplus: + return "plusplus"; case Kind::question: return "question"; case Kind::r_paren: @@ -234,8 +238,10 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, {Token::lessequal, "<="}, {Token::lessless, "<<"}, {Token::minusequal, "-="}, + {Token::minusminus, "--"}, {Token::pipepipe, "||"}, {Token::plusequal, "+="}, + {Token::plusplus, "++"}, {Token::amp, "&"}, {Token::caret, "^"}, {Token::colon, ":"}, diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 30cb96dd9b1a7..08b618dceb203 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -499,7 +499,8 @@ ASTNodeUP DILParser::ParseCastExpression() { // ASTNodeUP DILParser::ParseUnaryExpression() { if (CurToken().IsOneOf({Token::amp, Token::star, Token::minus, Token::plus, - Token::tilde, Token::exclaim})) { + Token::tilde, Token::exclaim, Token::minusminus, + Token::plusplus})) { Token token = CurToken(); uint32_t loc = token.GetLocation(); m_dil_lexer.Advance(); @@ -524,6 +525,14 @@ ASTNodeUP DILParser::ParseUnaryExpression() { case Token::exclaim: return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::LNot, std::move(rhs)); + case Token::minusminus: + BailOut("Decrement operator is not supported. Use `-=` instead.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + case Token::plusplus: + BailOut("Increment operator is not supported. Use `+=` instead.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); default: llvm_unreachable("invalid token kind"); } @@ -543,7 +552,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { ASTNodeUP DILParser::ParsePostfixExpression() { ASTNodeUP lhs = ParsePrimaryExpression(); assert(lhs && "ASTNodeUP must not contain a nullptr"); - while (CurToken().IsOneOf({Token::l_square, Token::period, Token::arrow})) { + while (CurToken().IsOneOf({Token::l_square, Token::period, Token::arrow, + Token::minusminus, Token::plusplus})) { uint32_t loc = CurToken().GetLocation(); Token token = CurToken(); switch (token.GetKind()) { @@ -579,6 +589,14 @@ ASTNodeUP DILParser::ParsePostfixExpression() { token.GetKind() == Token::arrow, member_id); break; } + case Token::minusminus: + BailOut("Decrement operator is not supported. Use `-=` instead.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + case Token::plusplus: + BailOut("Increment operator is not supported. Use `+=` instead.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); default: llvm_unreachable("invalid token"); } diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py index 17e3dce781d96..dd5cd68796e70 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILCompositeAssign.py @@ -98,3 +98,25 @@ def test_assignment(self): error=True, substrs=["Assignment is allowed only at top level"], ) + + # Check that operators `--` and `++` return an error message. + self.expect( + "frame variable 'i++'", + error=True, + substrs=["Increment operator is not supported. Use `+=` instead."], + ) + self.expect( + "frame variable '++i'", + error=True, + substrs=["Increment operator is not supported. Use `+=` instead."], + ) + self.expect( + "frame variable 'i--'", + error=True, + substrs=["Decrement operator is not supported. Use `-=` instead."], + ) + self.expect( + "frame variable -- '--i'", + error=True, + substrs=["Decrement operator is not supported. Use `-=` instead."], + ) >From 127d9ad5bce32d9c48af6fbd8830d161e3878892 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 6 Aug 2026 03:54:43 +0500 Subject: [PATCH 4/5] Fix the shell test --- lldb/test/Shell/Commands/command-dil-diagnostics.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/test/Shell/Commands/command-dil-diagnostics.test b/lldb/test/Shell/Commands/command-dil-diagnostics.test index 5b15cff4e3cd5..b9647bf0a64e3 100644 --- a/lldb/test/Shell/Commands/command-dil-diagnostics.test +++ b/lldb/test/Shell/Commands/command-dil-diagnostics.test @@ -13,9 +13,9 @@ frame var a+b # CHECK-NEXT: {{^ (╰─ )?}}error: use of undeclared identifier 'b' ## Check diagnostics when called from API. -script lldb.frame.GetValueForVariablePath("++foo") +script lldb.frame.GetValueForVariablePath("1+foo") # CHECK: error: <user expression>:1:3: use of undeclared identifier 'foo' -# CHECK-NEXT: {{^ }}1 | ++foo +# CHECK-NEXT: {{^ }}1 | 1+foo # CHECK-NEXT: {{^ }} | ^~~ ## Check that a result that fails to retrieve data is displaying an error. >From aec018e2cc8b0b589e4a9540583c2e55429fcba5 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 20 Aug 2026 23:56:36 +0500 Subject: [PATCH 5/5] Keep conditional operator precedence over assignment --- lldb/docs/dil-expr-lang.ebnf | 4 +- lldb/include/lldb/ValueObject/DILParser.h | 1 + lldb/source/ValueObject/DILParser.cpp | 44 ++++++++++++------- .../expr/Assignment/TestFrameVarDILAssign.py | 23 ++++++++++ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 9d0c35f8c8c97..3dddcbd1ef609 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -5,8 +5,8 @@ expression = assignment_expression ; -assignment_expression = pure_expression - | pure_expression assignment_operator pure_expression ; +assignment_expression = conditional_expression + | logical_or_expression assignment_operator pure_expression ; assignment_operator = "=" | "+=" diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 0e0982acd9192..ad7bc8ee73bba 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -85,6 +85,7 @@ class DILParser { ASTNodeUP ParseAssignmentExpression(); ASTNodeUP ParsePureExpression(); ASTNodeUP ParseConditionalExpression(); + ASTNodeUP ParseConditionalBranches(ASTNodeUP condition); ASTNodeUP ParseLogicalOrExpression(); ASTNodeUP ParseLogicalAndExpression(); ASTNodeUP ParseInclusiveOrExpression(); diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 08b618dceb203..97fd768545e61 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -134,8 +134,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); } // Parse an assignment_expression // // assignment_expression -// pure_expression -// pure_expression assignment_operator pure_expression +// conditional_expression +// logical_or_expression assignment_operator pure_expression // // assignment_operator: // "=" @@ -143,9 +143,15 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); } // "-=" // ASTNodeUP DILParser::ParseAssignmentExpression() { - auto lhs = ParsePureExpression(); + auto lhs = ParseLogicalOrExpression(); assert(lhs && "ASTNodeUP must not contain a nullptr"); + // Check if it's a ternary operator. + // This is done to keep conditional_expression's precedence + // over assignment_expression. + if (CurToken().Is(Token::question)) + return ParseConditionalBranches(std::move(lhs)); + // Check if it's an assignment expression. if (CurToken().IsOneOf({Token::equal, Token::plusequal, Token::minusequal})) { // That's an assignment! @@ -179,24 +185,28 @@ ASTNodeUP DILParser::ParseConditionalExpression() { auto lhs = ParseLogicalOrExpression(); assert(lhs && "ASTNodeUP must not contain a nullptr"); - // Check if it's a ternary operator. - if (CurToken().Is(Token::question)) { - Token token = CurToken(); - m_dil_lexer.Advance(); - auto true_op = ParsePureExpression(); - assert(true_op && "ASTNodeUP must not contain a nullptr"); - Expect(Token::colon); - m_dil_lexer.Advance(); - auto false_op = ParsePureExpression(); - assert(false_op && "ASTNodeUP must not contain a nullptr"); - lhs = std::make_unique<ConditionalNode>(token.GetLocation(), std::move(lhs), - std::move(true_op), - std::move(false_op)); - } + if (CurToken().Is(Token::question)) + lhs = ParseConditionalBranches(std::move(lhs)); return lhs; } +ASTNodeUP DILParser::ParseConditionalBranches(ASTNodeUP condition) { + assert(condition && "ASTNodeUP must not contain a nullptr"); + + Token token = CurToken(); + m_dil_lexer.Advance(); + auto true_op = ParsePureExpression(); + assert(true_op && "ASTNodeUP must not contain a nullptr"); + Expect(Token::colon); + m_dil_lexer.Advance(); + auto false_op = ParsePureExpression(); + assert(false_op && "ASTNodeUP must not contain a nullptr"); + return std::make_unique<ConditionalNode>( + token.GetLocation(), std::move(condition), std::move(true_op), + std::move(false_op)); +} + // Parse a logical_or_expression. // // logical_or_expression: diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py index d954467c2cf97..9b3ab51422064 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py @@ -229,3 +229,26 @@ def test_assignment(self): substrs=["Assignment is allowed only at top level"], ) self.expect("frame variable '*(arr + 1) = 5'", substrs=["= 5"]) + + # Check that assignment is parsed correctly when combined + # with ternary conditional operator + self.expect("frame variable 'i = false ? 1 : 2'", substrs=["i = 2"]) + self.expect( + "frame variable 'false ? i = 1 : i'", + error=True, + substrs=["Assignment is allowed only at top level"], + ) + self.expect( + "frame variable 'false ? i : i = 1'", + error=True, + substrs=["Assignment is allowed only at top level"], + ) + self.expect("frame variable '(false ? i : j) = 1'", substrs=["j = 1"]) + self.expect( + "frame variable '(true ? arr[0] : arr[1]) = false ? 0 : 5'", substrs=["= 5"] + ) + self.expect( + "frame variable 'true ? arr[0] : arr[1] = false ? 0 : 5'", + error=True, + substrs=["Assignment is allowed only at top level"], + ) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
