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 @@ -12,6 +12,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" #include "clang/AST/TypeLoc.h" @@ -114,6 +115,86 @@ }; } // namespace +syntax::NodeKind getOperatorNodeKind(const CXXOperatorCallExpr &E) { + switch (E.getOperator()) { + // Comparison + case OO_EqualEqual: + case OO_ExclaimEqual: + case OO_Greater: + case OO_GreaterEqual: + case OO_Less: + case OO_LessEqual: + case OO_Spaceship: + // Assignment + case OO_Equal: + case OO_SlashEqual: + case OO_PercentEqual: + case OO_CaretEqual: + case OO_PipeEqual: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + case OO_PlusEqual: + case OO_MinusEqual: + case OO_StarEqual: + case OO_AmpEqual: + // Binary computation + case OO_Slash: + case OO_Percent: + case OO_Caret: + case OO_Pipe: + case OO_LessLess: + case OO_GreaterGreater: + case OO_AmpAmp: + case OO_PipePipe: + // Less common binary operators + case OO_ArrowStar: + case OO_Comma: + return syntax::NodeKind::BinaryOperatorExpression; + case OO_Tilde: + case OO_Exclaim: + return syntax::NodeKind::PrefixUnaryOperatorExpression; + // Prefix/Postfix increment/decrement + case OO_PlusPlus: + case OO_MinusMinus: + switch (E.getNumArgs()) { + case 1: + return syntax::NodeKind::PrefixUnaryOperatorExpression; + case 2: + return syntax::NodeKind::PostfixUnaryOperatorExpression; + default: + llvm_unreachable("Invalid number of arguments for operator"); + } + // Operators that can be unary or binary + case OO_Plus: + case OO_Minus: + case OO_Star: + case OO_Amp: + switch (E.getNumArgs()) { + case 1: + return syntax::NodeKind::PrefixUnaryOperatorExpression; + case 2: + return syntax::NodeKind::BinaryOperatorExpression; + default: + llvm_unreachable("Invalid number of arguments for operator"); + } + return syntax::NodeKind::BinaryOperatorExpression; + // Not yet supported by SyntaxTree + case OO_New: + case OO_Delete: + case OO_Array_New: + case OO_Array_Delete: + case OO_Coawait: + case OO_Call: + case OO_Subscript: + case OO_Arrow: + return syntax::NodeKind::UnknownExpression; + case OO_Conditional: // not overloadable + case NUM_OVERLOADED_OPERATORS: + case OO_None: + llvm_unreachable("Not an overloadable operator"); + } +} + /// Gets the range of declarator as defined by the C++ grammar. E.g. /// `int a;` -> range of `a`, /// `int *a;` -> range of `*a`, @@ -658,6 +739,8 @@ } bool WalkUpFromIntegerLiteral(IntegerLiteral *S) { + if (S->getLocation().isInvalid()) + return true; Builder.markChildToken(S->getLocation(), syntax::NodeRole::LiteralToken); Builder.foldNode(Builder.getExprRange(S), new (allocator()) syntax::IntegerLiteralExpression, S); @@ -730,7 +813,8 @@ } bool WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr *S) { - if (S->isInfixBinaryOp()) { + switch (getOperatorNodeKind(*S)) { + case syntax::NodeKind::BinaryOperatorExpression: Builder.markExprChild( S->getArg(0), syntax::NodeRole::BinaryOperatorExpression_leftHandSide); @@ -743,8 +827,31 @@ Builder.foldNode(Builder.getExprRange(S), new (allocator()) syntax::BinaryOperatorExpression, S); return true; + case syntax::NodeKind::PrefixUnaryOperatorExpression: + Builder.markChildToken( + S->getOperatorLoc(), + syntax::NodeRole::OperatorExpression_operatorToken); + Builder.markExprChild(S->getArg(0), + syntax::NodeRole::UnaryOperatorExpression_operand); + Builder.foldNode(Builder.getExprRange(S), + new (allocator()) syntax::PrefixUnaryOperatorExpression, + S); + return true; + case syntax::NodeKind::PostfixUnaryOperatorExpression: + Builder.markChildToken( + S->getOperatorLoc(), + syntax::NodeRole::OperatorExpression_operatorToken); + Builder.markExprChild(S->getArg(0), + syntax::NodeRole::UnaryOperatorExpression_operand); + Builder.foldNode(Builder.getExprRange(S), + new (allocator()) syntax::PostfixUnaryOperatorExpression, + S); + return true; + case syntax::NodeKind::UnknownExpression: + return RecursiveASTVisitor::WalkUpFromCXXOperatorCallExpr(S); + default: + llvm_unreachable("getKind does not implement that"); } - return RecursiveASTVisitor::WalkUpFromCXXOperatorCallExpr(S); } bool WalkUpFromNamespaceDecl(NamespaceDecl *S) { 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 @@ -2189,20 +2189,29 @@ } EXPECT_TRUE(treeDumpEqual( R"cpp( +class osstream {}; struct X { X& operator=(const X&); friend X operator+(X, const X&); friend bool operator<(const X&, const X&); + friend osstream operator<<(osstream&, const X&); }; -void test(X x, X y) { +void test(X x, X y, osstream out) { x = y; x + y; x < y; + out << x; } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration +| |-class +| |-osstream +| |-{ +| |-} +| `-; +|-SimpleDeclaration | |-struct | |-X | |-{ @@ -2262,6 +2271,27 @@ | | | | `-& | | | `-) | | `-; +| |-UnknownDeclaration +| | `-SimpleDeclaration +| | |-friend +| | |-osstream +| | |-SimpleDeclarator +| | | |-operator +| | | |-<< +| | | `-ParametersAndQualifiers +| | | |-( +| | | |-SimpleDeclaration +| | | | |-osstream +| | | | `-SimpleDeclarator +| | | | `-& +| | | |-, +| | | |-SimpleDeclaration +| | | | |-const +| | | | |-X +| | | | `-SimpleDeclarator +| | | | `-& +| | | `-) +| | `-; | |-} | `-; `-SimpleDeclaration @@ -2279,6 +2309,11 @@ | | |-X | | `-SimpleDeclarator | | `-y + | |-, + | |-SimpleDeclaration + | | |-osstream + | | `-SimpleDeclarator + | | `-out | `-) `-CompoundStatement |-{ @@ -2319,6 +2354,242 @@ | | `-UnqualifiedId | | `-y | `-; + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-out + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-<< + | | `-IdExpression + | | `-UnqualifiedId + | | `-x + | `-; + `-} +)txt")); +} + +TEST_P(SyntaxTreeTest, UserDefinedRareBinaryOperators) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(treeDumpEqual( + R"cpp( +struct X { + X operator,(X&); +}; +void test(X x, X y) { + x, y; + // TODO: Test for `->*`. That introduced a crash +} +)cpp", + R"txt( +*: TranslationUnit +|-SimpleDeclaration +| |-struct +| |-X +| |-{ +| |-SimpleDeclaration +| | |-X +| | |-SimpleDeclarator +| | | |-operator +| | | |-, +| | | `-ParametersAndQualifiers +| | | |-( +| | | |-SimpleDeclaration +| | | | |-X +| | | | `-SimpleDeclarator +| | | | `-& +| | | `-) +| | `-; +| |-} +| `-; +`-SimpleDeclaration + |-void + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | |-SimpleDeclaration + | | |-X + | | `-SimpleDeclarator + | | `-x + | |-, + | |-SimpleDeclaration + | | |-X + | | `-SimpleDeclarator + | | `-y + | `-) + `-CompoundStatement + |-{ + |-ExpressionStatement + | |-BinaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-x + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-, + | | `-IdExpression + | | `-UnqualifiedId + | | `-y + | `-; + `-} +)txt")); +} + +TEST_P(SyntaxTreeTest, UserDefinedUnaryPrefixOperator) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(treeDumpEqual( + R"cpp( +struct X { + X operator++(); + bool operator!(); + X* operator&(); +}; +void test(X x) { + ++x; + !x; + &x; +} +)cpp", + R"txt( +*: TranslationUnit +|-SimpleDeclaration +| |-struct +| |-X +| |-{ +| |-SimpleDeclaration +| | |-X +| | |-SimpleDeclarator +| | | |-operator +| | | |-++ +| | | `-ParametersAndQualifiers +| | | |-( +| | | `-) +| | `-; +| |-SimpleDeclaration +| | |-bool +| | |-SimpleDeclarator +| | | |-operator +| | | |-! +| | | `-ParametersAndQualifiers +| | | |-( +| | | `-) +| | `-; +| |-SimpleDeclaration +| | |-X +| | |-SimpleDeclarator +| | | |-* +| | | |-operator +| | | |-& +| | | `-ParametersAndQualifiers +| | | |-( +| | | `-) +| | `-; +| |-} +| `-; +`-SimpleDeclaration + |-void + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | |-SimpleDeclaration + | | |-X + | | `-SimpleDeclarator + | | `-x + | `-) + `-CompoundStatement + |-{ + |-ExpressionStatement + | |-PrefixUnaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-++ + | | `-IdExpression + | | `-UnqualifiedId + | | `-x + | `-; + |-ExpressionStatement + | |-PrefixUnaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-! + | | `-IdExpression + | | `-UnqualifiedId + | | `-x + | `-; + |-ExpressionStatement + | |-PrefixUnaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-& + | | `-IdExpression + | | `-UnqualifiedId + | | `-x + | `-; + `-} +)txt")); +} + +TEST_P(SyntaxTreeTest, UserDefinedUnaryPostfixOperator) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(treeDumpEqual( + R"cpp( +struct X { + X operator++(int); +}; +void test(X x) { + x++; +} +)cpp", + R"txt( +*: TranslationUnit +|-SimpleDeclaration +| |-struct +| |-X +| |-{ +| |-SimpleDeclaration +| | |-X +| | |-SimpleDeclarator +| | | |-operator +| | | |-++ +| | | `-ParametersAndQualifiers +| | | |-( +| | | |-SimpleDeclaration +| | | | `-int +| | | `-) +| | `-; +| |-} +| `-; +`-SimpleDeclaration + |-void + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | |-SimpleDeclaration + | | |-X + | | `-SimpleDeclarator + | | `-x + | `-) + `-CompoundStatement + |-{ + |-ExpressionStatement + | |-PostfixUnaryOperatorExpression + | | |-IdExpression + | | | `-UnqualifiedId + | | | `-x + | | `-IdExpression + | | `-UnqualifiedId + | | `-++ + | `-; `-} )txt")); }