brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.9 KiB · 95aa29e Raw
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