433 lines · cpp
1//===- unittest/Tooling/SourceCodeBuildersTest.cpp ------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/Transformer/SourceCodeBuilders.h"10#include "clang/AST/Type.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Tooling/Tooling.h"14#include "llvm/Testing/Support/SupportHelpers.h"15#include "gmock/gmock.h"16#include "gtest/gtest.h"17#include <optional>18 19using namespace clang;20using namespace tooling;21using namespace ast_matchers;22 23namespace {24using MatchResult = MatchFinder::MatchResult;25using llvm::ValueIs;26 27// Create a valid translation unit from a statement.28static std::string wrapSnippet(StringRef StatementCode) {29 return ("namespace std {\n"30 "template <typename T> struct unique_ptr {\n"31 " T* operator->() const;\n"32 " T& operator*() const;\n"33 "};\n"34 "template <typename T> struct shared_ptr {\n"35 " T* operator->() const;\n"36 " T& operator*() const;\n"37 "};\n"38 "}\n"39 "struct A { void super(); };\n"40 "struct S : public A { S(); S(int); int Field; };\n"41 "S operator+(const S &a, const S &b);\n"42 "struct Smart {\n"43 " S* operator->() const;\n"44 " S& operator*() const;\n"45 "};\n"46 "auto test_snippet = []{" +47 StatementCode + "};")48 .str();49}50 51static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {52 return varDecl(hasName("test_snippet"),53 hasDescendant(compoundStmt(hasAnySubstatement(Matcher))));54}55 56struct TestMatch {57 // The AST unit from which `result` is built. We bundle it because it backs58 // the result. Users are not expected to access it.59 std::unique_ptr<ASTUnit> AstUnit;60 // The result to use in the test. References `ast_unit`.61 MatchResult Result;62};63 64// Matches `Matcher` against the statement `StatementCode` and returns the65// result. Handles putting the statement inside a function and modifying the66// matcher correspondingly. `Matcher` should match one of the statements in67// `StatementCode` exactly -- that is, produce exactly one match. However,68// `StatementCode` may contain other statements not described by `Matcher`.69static std::optional<TestMatch> matchStmt(StringRef StatementCode,70 StatementMatcher Matcher) {71 auto AstUnit = buildASTFromCodeWithArgs(wrapSnippet(StatementCode),72 {"-Wno-unused-value"});73 if (AstUnit == nullptr) {74 ADD_FAILURE() << "AST construction failed";75 return std::nullopt;76 }77 ASTContext &Context = AstUnit->getASTContext();78 auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context);79 // We expect a single, exact match for the statement.80 if (Matches.size() != 1) {81 ADD_FAILURE() << "Wrong number of matches: " << Matches.size();82 return std::nullopt;83 }84 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};85}86 87static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet,88 bool Expected) {89 auto StmtMatch = matchStmt(Snippet, expr().bind("expr"));90 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;91 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr")))92 << "Snippet: " << Snippet;93}94 95// Tests the predicate on the call argument, assuming `Snippet` is a function96// call.97static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet,98 bool Expected) {99 auto StmtMatch = matchStmt(100 Snippet, expr(ignoringImplicit(callExpr(hasArgument(101 0, ignoringElidableConstructorCall(expr().bind("arg")))))));102 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;103 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg")))104 << "Snippet: " << Snippet;105}106 107TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) {108 testPredicate(needParensAfterUnaryOperator, "3 + 5;", true);109 testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true);110 testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true);111 112 testPredicate(needParensAfterUnaryOperator, "int x; x;", false);113 testPredicate(needParensAfterUnaryOperator, "int(3.0);", false);114 testPredicate(needParensAfterUnaryOperator, "void f(); f();", false);115 testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false);116 testPredicate(needParensAfterUnaryOperator, "S x; x.Field;", false);117 testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false);118 testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false);119}120 121TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) {122 // The binary operation will be embedded in various implicit123 // expressions. Verify they are ignored.124 testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);",125 true);126}127 128TEST(SourceCodeBuildersTest, mayEverNeedParens) {129 testPredicate(mayEverNeedParens, "3 + 5;", true);130 testPredicate(mayEverNeedParens, "true ? 3 : 5;", true);131 testPredicate(mayEverNeedParens, "int x = 1; --x;", true);132 testPredicate(mayEverNeedParens, "int x = 1; -x;", true);133 134 testPredicate(mayEverNeedParens, "int x; x;", false);135 testPredicate(mayEverNeedParens, "int(3.0);", false);136 testPredicate(mayEverNeedParens, "void f(); f();", false);137 testPredicate(mayEverNeedParens, "int a[3]; a[0];", false);138 testPredicate(mayEverNeedParens, "S x; x.Field;", false);139}140 141TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) {142 // The binary operation will be embedded in various implicit143 // expressions. Verify they are ignored.144 testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true);145}146 147TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUniquePtr) {148 std::string Snippet = "std::unique_ptr<int> P; P;";149 auto StmtMatch =150 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty"))));151 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;152 EXPECT_TRUE(153 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),154 *StmtMatch->Result.Context))155 << "Snippet: " << Snippet;156}157 158TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeSharedPtr) {159 std::string Snippet = "std::shared_ptr<int> P; P;";160 auto StmtMatch =161 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty"))));162 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;163 EXPECT_TRUE(164 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),165 *StmtMatch->Result.Context))166 << "Snippet: " << Snippet;167}168 169TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUnknownTypeFalse) {170 std::string Snippet = "Smart P; P;";171 auto StmtMatch =172 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty"))));173 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;174 EXPECT_FALSE(175 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),176 *StmtMatch->Result.Context))177 << "Snippet: " << Snippet;178}179 180TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeNormalTypeFalse) {181 std::string Snippet = "int *P; P;";182 auto StmtMatch =183 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty"))));184 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;185 EXPECT_FALSE(186 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),187 *StmtMatch->Result.Context))188 << "Snippet: " << Snippet;189}190 191static void testBuilder(192 std::optional<std::string> (*Builder)(const Expr &, const ASTContext &),193 StringRef Snippet, StringRef Expected) {194 auto StmtMatch = matchStmt(Snippet, expr().bind("expr"));195 ASSERT_TRUE(StmtMatch);196 EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),197 *StmtMatch->Result.Context),198 ValueIs(std::string(Expected)));199}200 201static void testBuildAccess(StringRef Snippet, StringRef Expected,202 PLTClass C = PLTClass::Pointer) {203 auto StmtMatch = matchStmt(Snippet, expr().bind("expr"));204 ASSERT_TRUE(StmtMatch);205 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),206 *StmtMatch->Result.Context, C),207 ValueIs(std::string(Expected)));208}209 210TEST(SourceCodeBuildersTest, BuildParensUnaryOp) {211 testBuilder(buildParens, "-4;", "(-4)");212}213 214TEST(SourceCodeBuildersTest, BuildParensBinOp) {215 testBuilder(buildParens, "4 + 4;", "(4 + 4)");216}217 218TEST(SourceCodeBuildersTest, BuildParensValue) {219 testBuilder(buildParens, "4;", "4");220}221 222TEST(SourceCodeBuildersTest, BuildParensSubscript) {223 testBuilder(buildParens, "int a[3]; a[0];", "a[0]");224}225 226TEST(SourceCodeBuildersTest, BuildParensCall) {227 testBuilder(buildParens, "int f(int); f(4);", "f(4)");228}229 230TEST(SourceCodeBuildersTest, BuildAddressOfValue) {231 testBuilder(buildAddressOf, "S x; x;", "&x");232}233 234TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) {235 testBuilder(buildAddressOf, "S *x; *x;", "x");236}237 238TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) {239 testBuilder(buildAddressOf, "S *x; *(x);", "x");240}241 242TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) {243 testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)");244}245 246TEST(SourceCodeBuildersTest, BuildAddressOfImplicitThis) {247 StringRef Snippet = R"cc(248 struct Struct {249 void foo() {}250 void bar() {251 foo();252 }253 };254 )cc";255 auto StmtMatch = matchStmt(256 Snippet,257 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr"))));258 ASSERT_TRUE(StmtMatch);259 EXPECT_THAT(buildAddressOf(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),260 *StmtMatch->Result.Context),261 ValueIs(std::string("this")));262}263 264TEST(SourceCodeBuildersTest, BuildDereferencePointer) {265 testBuilder(buildDereference, "S *x; x;", "*x");266}267 268TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) {269 testBuilder(buildDereference, "S x; &x;", "x");270}271 272TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) {273 testBuilder(buildDereference, "S x; &(x);", "x");274}275 276TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) {277 testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)");278}279 280TEST(SourceCodeBuildersTest, BuildDotValue) {281 testBuilder(buildDot, "S x; x;", "x.");282}283 284TEST(SourceCodeBuildersTest, BuildDotPointerDereference) {285 testBuilder(buildDot, "S *x; *x;", "x->");286}287 288TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) {289 testBuilder(buildDot, "S *x; *(x);", "x->");290}291 292TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) {293 testBuilder(buildDot, "S x; x + x;", "(x + x).");294}295 296TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) {297 testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->");298}299 300TEST(SourceCodeBuildersTest, BuildArrowPointer) {301 testBuilder(buildArrow, "S *x; x;", "x->");302}303 304TEST(SourceCodeBuildersTest, BuildArrowValueAddress) {305 testBuilder(buildArrow, "S x; &x;", "x.");306}307 308TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) {309 testBuilder(buildArrow, "S x; &(x);", "x.");310}311 312TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) {313 testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->");314}315 316TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) {317 testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x).");318}319 320TEST(SourceCodeBuildersTest, BuildAccessValue) {321 testBuildAccess("S x; x;", "x.");322}323 324TEST(SourceCodeBuildersTest, BuildAccessPointerDereference) {325 testBuildAccess("S *x; *x;", "x->");326}327 328TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceIgnoresParens) {329 testBuildAccess("S *x; *(x);", "x->");330}331 332TEST(SourceCodeBuildersTest, BuildAccessValueBinaryOperation) {333 testBuildAccess("S x; x + x;", "(x + x).");334}335 336TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceExprWithParens) {337 testBuildAccess("S *x; *(x + 1);", "(x + 1)->");338}339 340TEST(SourceCodeBuildersTest, BuildAccessPointer) {341 testBuildAccess("S *x; x;", "x->");342}343 344TEST(SourceCodeBuildersTest, BuildAccessValueAddress) {345 testBuildAccess("S x; &x;", "x.");346}347 348TEST(SourceCodeBuildersTest, BuildAccessValueAddressIgnoresParens) {349 testBuildAccess("S x; &(x);", "x.");350}351 352TEST(SourceCodeBuildersTest, BuildAccessPointerBinaryOperation) {353 testBuildAccess("S *x; x + 1;", "(x + 1)->");354}355 356TEST(SourceCodeBuildersTest, BuildAccessValueAddressWithParens) {357 testBuildAccess("S x; &(true ? x : x);", "(true ? x : x).");358}359 360TEST(SourceCodeBuildersTest, BuildAccessSmartPointer) {361 testBuildAccess("std::unique_ptr<int> x; x;", "x->");362}363 364TEST(SourceCodeBuildersTest, BuildAccessSmartPointerAsValue) {365 testBuildAccess("std::unique_ptr<int> x; x;", "x.", PLTClass::Value);366}367 368TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDeref) {369 testBuildAccess("std::unique_ptr<int> x; *x;", "x->");370}371 372TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDerefAsValue) {373 testBuildAccess("std::unique_ptr<int> x; *x;", "(*x).", PLTClass::Value);374}375 376TEST(SourceCodeBuildersTest, BuildAccessSmartPointerMemberCall) {377 StringRef Snippet = R"cc(378 Smart x;379 x->Field;380 )cc";381 auto StmtMatch =382 matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind("expr"))));383 ASSERT_TRUE(StmtMatch);384 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),385 *StmtMatch->Result.Context),386 ValueIs(std::string("x->")));387}388 389TEST(SourceCodeBuildersTest, BuildAccessIgnoreImplicit) {390 StringRef Snippet = R"cc(391 S x;392 A *a;393 a = &x;394 )cc";395 auto StmtMatch =396 matchStmt(Snippet, binaryOperator(isAssignmentOperator(),397 hasRHS(expr().bind("expr"))));398 ASSERT_TRUE(StmtMatch);399 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),400 *StmtMatch->Result.Context),401 ValueIs(std::string("x.")));402}403 404TEST(SourceCodeBuildersTest, BuildAccessImplicitThis) {405 StringRef Snippet = R"cc(406 struct Struct {407 void foo() {}408 void bar() {409 foo();410 }411 };412 )cc";413 auto StmtMatch = matchStmt(414 Snippet,415 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr"))));416 ASSERT_TRUE(StmtMatch);417 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),418 *StmtMatch->Result.Context),419 ValueIs(std::string()));420}421 422TEST(SourceCodeBuildersTest, BuildAccessImplicitThisIgnoreImplicitCasts) {423 StringRef Snippet = "struct B : public A { void f() { super(); } };";424 auto StmtMatch = matchStmt(425 Snippet,426 cxxMemberCallExpr(onImplicitObjectArgument(expr().bind("expr"))));427 ASSERT_TRUE(StmtMatch);428 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),429 *StmtMatch->Result.Context),430 ValueIs(std::string()));431}432} // namespace433