xref: /llvm-project/clang/unittests/Analysis/CFGTest.cpp (revision ab7747b727d79c9cc67b0ffb75528bf9bb67d9e8)
1 //===- unittests/Analysis/CFGTest.cpp - CFG tests -------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/ASTMatchers/ASTMatchFinder.h"
10 #include "clang/Analysis/CFG.h"
11 #include "clang/Tooling/Tooling.h"
12 #include "gtest/gtest.h"
13 #include <string>
14 #include <vector>
15 
16 namespace clang {
17 namespace analysis {
18 namespace {
19 
20 class BuildResult {
21 public:
22   enum Status {
23     ToolFailed,
24     ToolRan,
25     SawFunctionBody,
26     BuiltCFG,
27   };
28 
29   BuildResult(Status S, std::unique_ptr<CFG> Cfg = nullptr)
30       : S(S), Cfg(std::move(Cfg)) {}
31 
32   Status getStatus() const { return S; }
33   CFG *getCFG() const { return Cfg.get(); }
34 
35 private:
36   Status S;
37   std::unique_ptr<CFG> Cfg;
38 };
39 
40 class CFGCallback : public ast_matchers::MatchFinder::MatchCallback {
41 public:
42   BuildResult TheBuildResult = BuildResult::ToolRan;
43 
44   void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
45     const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
46     Stmt *Body = Func->getBody();
47     if (!Body)
48       return;
49     TheBuildResult = BuildResult::SawFunctionBody;
50     CFG::BuildOptions Options;
51     Options.AddImplicitDtors = true;
52     if (std::unique_ptr<CFG> Cfg =
53             CFG::buildCFG(nullptr, Body, Result.Context, Options))
54       TheBuildResult = {BuildResult::BuiltCFG, std::move(Cfg)};
55   }
56 };
57 
58 BuildResult BuildCFG(const char *Code) {
59   CFGCallback Callback;
60 
61   ast_matchers::MatchFinder Finder;
62   Finder.addMatcher(ast_matchers::functionDecl().bind("func"), &Callback);
63   std::unique_ptr<tooling::FrontendActionFactory> Factory(
64       tooling::newFrontendActionFactory(&Finder));
65   std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"};
66   if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args))
67     return BuildResult::ToolFailed;
68   return std::move(Callback.TheBuildResult);
69 }
70 
71 // Constructing a CFG for a range-based for over a dependent type fails (but
72 // should not crash).
73 TEST(CFG, RangeBasedForOverDependentType) {
74   const char *Code = "class Foo;\n"
75                      "template <typename T>\n"
76                      "void f(const T &Range) {\n"
77                      "  for (const Foo *TheFoo : Range) {\n"
78                      "  }\n"
79                      "}\n";
80   EXPECT_EQ(BuildResult::SawFunctionBody, BuildCFG(Code).getStatus());
81 }
82 
83 // Constructing a CFG containing a delete expression on a dependent type should
84 // not crash.
85 TEST(CFG, DeleteExpressionOnDependentType) {
86   const char *Code = "template<class T>\n"
87                      "void f(T t) {\n"
88                      "  delete t;\n"
89                      "}\n";
90   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
91 }
92 
93 // Constructing a CFG on a function template with a variable of incomplete type
94 // should not crash.
95 TEST(CFG, VariableOfIncompleteType) {
96   const char *Code = "template<class T> void f() {\n"
97                      "  class Undefined;\n"
98                      "  Undefined u;\n"
99                      "}\n";
100   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
101 }
102 
103 TEST(CFG, IsLinear) {
104   auto expectLinear = [](bool IsLinear, const char *Code) {
105     BuildResult B = BuildCFG(Code);
106     EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());
107     EXPECT_EQ(IsLinear, B.getCFG()->isLinear());
108   };
109 
110   expectLinear(true,  "void foo() {}");
111   expectLinear(true,  "void foo() { if (true) return; }");
112   expectLinear(true,  "void foo() { if constexpr (false); }");
113   expectLinear(false, "void foo(bool coin) { if (coin) return; }");
114   expectLinear(false, "void foo() { for(;;); }");
115   expectLinear(false, "void foo() { do {} while (true); }");
116   expectLinear(true,  "void foo() { do {} while (false); }");
117   expectLinear(true,  "void foo() { foo(); }"); // Recursion is not our problem.
118 }
119 
120 } // namespace
121 } // namespace analysis
122 } // namespace clang
123