diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h --- a/clang/include/clang/Tooling/Syntax/Nodes.h +++ b/clang/include/clang/Tooling/Syntax/Nodes.h @@ -40,6 +40,7 @@ // Expressions. UnknownExpression, + BinaryOperatorExpression, // Statements. UnknownStatement, @@ -104,6 +105,9 @@ BodyStatement, // Roles specific to particular node kinds. + BinaryOperatorExpression_leftHandSide, + BinaryOperatorExpression_operatorToken, + BinaryOperatorExpression_rightHandSide, CaseStatement_value, IfStatement_thenStatement, IfStatement_elseKeyword, @@ -158,6 +162,24 @@ } }; +/// +/// +/// For example: +/// a + b +/// a bitor 1 +/// a |= b +/// a and_eq b +class BinaryOperatorExpression final : public Expression { +public: + BinaryOperatorExpression() : Expression(NodeKind::BinaryOperatorExpression) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::BinaryOperatorExpression; + } + syntax::Expression *lhs(); + syntax::Leaf *operatorToken(); + syntax::Expression *rhs(); +}; + /// An abstract node for C++ statements, e.g. 'while', 'if', etc. /// FIXME: add accessors for semicolon of statements that have it. class Statement : public Tree { diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp --- a/clang/lib/Tooling/Syntax/BuildTree.cpp +++ b/clang/lib/Tooling/Syntax/BuildTree.cpp @@ -11,6 +11,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" #include "clang/AST/TypeLoc.h" @@ -594,10 +595,7 @@ for (auto *D : DS->decls()) Builder.noticeDeclWithoutSemicolon(D); } else if (auto *E = llvm::dyn_cast_or_null(S)) { - // Do not recurse into subexpressions. - // We do not have syntax trees for expressions yet, so we only want to see - // the first top-level expression. - return WalkUpFromExpr(E->IgnoreImplicit()); + return RecursiveASTVisitor::TraverseStmt(E->IgnoreImplicit()); } return RecursiveASTVisitor::TraverseStmt(S); } @@ -610,6 +608,19 @@ return true; } + bool WalkUpFromBinaryOperator(BinaryOperator *S) { + Builder.markExprChild( + S->getLHS(), syntax::NodeRole::BinaryOperatorExpression_leftHandSide); + Builder.markChildToken( + S->getOperatorLoc(), + syntax::NodeRole::BinaryOperatorExpression_operatorToken); + Builder.markExprChild( + S->getRHS(), syntax::NodeRole::BinaryOperatorExpression_rightHandSide); + Builder.foldNode(Builder.getExprRange(S), + new (allocator()) syntax::BinaryOperatorExpression, S); + return true; + } + bool WalkUpFromNamespaceDecl(NamespaceDecl *S) { auto Tokens = Builder.getDeclarationRange(S); if (Tokens.front().kind() == tok::coloncolon) { diff --git a/clang/lib/Tooling/Syntax/Nodes.cpp b/clang/lib/Tooling/Syntax/Nodes.cpp --- a/clang/lib/Tooling/Syntax/Nodes.cpp +++ b/clang/lib/Tooling/Syntax/Nodes.cpp @@ -18,6 +18,8 @@ return OS << "TranslationUnit"; case NodeKind::UnknownExpression: return OS << "UnknownExpression"; + case NodeKind::BinaryOperatorExpression: + return OS << "BinaryOperatorExpression"; case NodeKind::UnknownStatement: return OS << "UnknownStatement"; case NodeKind::DeclarationStatement: @@ -110,6 +112,12 @@ return OS << "IfStatement_elseKeyword"; case syntax::NodeRole::IfStatement_elseStatement: return OS << "IfStatement_elseStatement"; + case syntax::NodeRole::BinaryOperatorExpression_leftHandSide: + return OS << "BinaryOperatorExpression_leftHandSide"; + case syntax::NodeRole::BinaryOperatorExpression_operatorToken: + return OS << "BinaryOperatorExpression_operatorToken"; + case syntax::NodeRole::BinaryOperatorExpression_rightHandSide: + return OS << "BinaryOperatorExpression_rightHandSide"; case syntax::NodeRole::ReturnStatement_value: return OS << "ReturnStatement_value"; case syntax::NodeRole::ExpressionStatement_expression: @@ -142,6 +150,21 @@ llvm_unreachable("invalid role"); } +syntax::Expression *syntax::BinaryOperatorExpression::lhs() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::BinaryOperatorExpression_leftHandSide)); +} + +syntax::Leaf *syntax::BinaryOperatorExpression::operatorToken() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::BinaryOperatorExpression_operatorToken)); +} + +syntax::Expression *syntax::BinaryOperatorExpression::rhs() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::BinaryOperatorExpression_rightHandSide)); +} + syntax::Leaf *syntax::SwitchStatement::switchKeyword() { return llvm::cast_or_null( findChild(syntax::NodeRole::IntroducerKeyword)); diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp --- a/clang/unittests/Tooling/Syntax/TreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -564,7 +564,8 @@ |-{ |-ExpressionStatement | |-UnknownExpression - | | |-test + | | |-UnknownExpression + | | | `-test | | |-( | | `-) | `-; @@ -576,14 +577,16 @@ | |-) | |-ExpressionStatement | | |-UnknownExpression - | | | |-test + | | | |-UnknownExpression + | | | | `-test | | | |-( | | | `-) | | `-; | |-else | `-ExpressionStatement | |-UnknownExpression - | | |-test + | | |-UnknownExpression + | | | `-test | | |-( | | `-) | `-; @@ -591,6 +594,237 @@ )txt"); } +TEST_F(SyntaxTreeTest, BinaryOperator) { + expectTreeDumpEqual( + R"cpp( +void test(int a) { + 1 - 2; + 1 == 2; + a = 1; + a <<= 1; + + true || false; + true or false; + + 1 & 2; + 1 bitand 2; + + a ^= 3; + a xor_eq 3; +} + )cpp", + R"txt( +*: TranslationUnit +`-SimpleDeclaration + |-void + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | |-SimpleDeclaration + | | |-int + | | `-SimpleDeclarator + | | `-a + | `-) + `-CompoundStatement + |-{ + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-1 + | | |-- + | | `-UnknownExpression + | | `-2 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-1 + | | |-== + | | `-UnknownExpression + | | `-2 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-a + | | |-= + | | `-UnknownExpression + | | `-1 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-a + | | |-<<= + | | `-UnknownExpression + | | `-1 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-true + | | |-|| + | | `-UnknownExpression + | | `-false + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-true + | | |-or + | | `-UnknownExpression + | | `-false + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-1 + | | |-& + | | `-UnknownExpression + | | `-2 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-1 + | | |-bitand + | | `-UnknownExpression + | | `-2 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-a + | | |-^= + | | `-UnknownExpression + | | `-3 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-a + | | |-xor_eq + | | `-UnknownExpression + | | `-3 + | `-; + `-} +)txt"); +} + +TEST_F(SyntaxTreeTest, NestedBinaryOperator) { + expectTreeDumpEqual( + R"cpp( +void test(int a, int b) { + (1 + 2) * (4 / 2); + a + b + 42; + a = b = 42; + a + b * 4 + 2; + a % 2 + b * 42; +} + )cpp", + R"txt( +*: TranslationUnit +`-SimpleDeclaration + |-void + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | |-SimpleDeclaration + | | |-int + | | `-SimpleDeclarator + | | `-a + | |-, + | |-SimpleDeclaration + | | |-int + | | `-SimpleDeclarator + | | `-b + | `-) + `-CompoundStatement + |-{ + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | |-( + | | | |-BinaryOperatorExpression + | | | | |-UnknownExpression + | | | | | `-1 + | | | | |-+ + | | | | `-UnknownExpression + | | | | `-2 + | | | `-) + | | |-* + | | `-UnknownExpression + | | |-( + | | |-BinaryOperatorExpression + | | | |-UnknownExpression + | | | | `-4 + | | | |-/ + | | | `-UnknownExpression + | | | `-2 + | | `-) + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-BinaryOperatorExpression + | | | |-UnknownExpression + | | | | `-a + | | | |-+ + | | | `-UnknownExpression + | | | `-b + | | |-+ + | | `-UnknownExpression + | | `-42 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-a + | | |-= + | | `-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-b + | | |-= + | | `-UnknownExpression + | | `-42 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-BinaryOperatorExpression + | | | |-UnknownExpression + | | | | `-a + | | | |-+ + | | | `-BinaryOperatorExpression + | | | |-UnknownExpression + | | | | `-b + | | | |-* + | | | `-UnknownExpression + | | | `-4 + | | |-+ + | | `-UnknownExpression + | | `-2 + | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-BinaryOperatorExpression + | | | |-UnknownExpression + | | | | `-a + | | | |-% + | | | `-UnknownExpression + | | | `-2 + | | |-+ + | | `-BinaryOperatorExpression + | | |-UnknownExpression + | | | `-b + | | |-* + | | `-UnknownExpression + | | `-42 + | `-; + `-} +)txt"); +} + TEST_F(SyntaxTreeTest, MultipleDeclaratorsGrouping) { expectTreeDumpEqual( R"cpp( @@ -1201,10 +1435,12 @@ |-IfStatement | |-I: if | |-I: ( - | |-I: UnknownExpression - | | |-I: 1 + | |-I: BinaryOperatorExpression + | | |-I: UnknownExpression + | | | `-I: 1 | | |-I: + - | | `-I: 1 + | | `-I: UnknownExpression + | | `-I: 1 | |-I: ) | |-I: CompoundStatement | | |-I: { @@ -1312,13 +1548,17 @@ | | | `-] | | |-= | | `-UnknownExpression -| | |-{ -| | |-1 -| | |-, -| | |-2 -| | |-, -| | |-3 -| | `-} +| | `-UnknownExpression +| | |-{ +| | |-UnknownExpression +| | | `-1 +| | |-, +| | |-UnknownExpression +| | | `-2 +| | |-, +| | |-UnknownExpression +| | | `-3 +| | `-} | `-; `-SimpleDeclaration |-void @@ -1628,7 +1868,8 @@ | | |-= | | `-UnknownExpression | | |-- -| | `-1 +| | `-UnknownExpression +| | `-1 | `-; |-SimpleDeclaration | |-int