1//===- unittests/AST/EvaluateAsRValueTest.cpp -----------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// \file
11// \brief Unit tests for evaluation of constant initializers.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/ASTConsumer.h"
16#include "clang/AST/ASTConsumer.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/AST/RecursiveASTVisitor.h"
19#include "clang/Tooling/Tooling.h"
20#include "gtest/gtest.h"
21#include <map>
22#include <string>
23
24using namespace clang::tooling;
25
26namespace {
27// For each variable name encountered, whether its initializer was a
28// constant.
29typedef std::map<std::string, bool> VarInfoMap;
30
31/// \brief Records information on variable initializers to a map.
32class EvaluateConstantInitializersVisitor
33    : public clang::RecursiveASTVisitor<EvaluateConstantInitializersVisitor> {
34 public:
35  explicit EvaluateConstantInitializersVisitor(VarInfoMap &VarInfo)
36      : VarInfo(VarInfo) {}
37
38  /// \brief Checks that isConstantInitializer and EvaluateAsRValue agree
39  /// and don't crash.
40  ///
41  /// For each VarDecl with an initializer this also records in VarInfo
42  /// whether the initializer could be evaluated as a constant.
43  bool VisitVarDecl(const clang::VarDecl *VD) {
44    if (const clang::Expr *Init = VD->getInit()) {
45      clang::Expr::EvalResult Result;
46      bool WasEvaluated = Init->EvaluateAsRValue(Result, VD->getASTContext());
47      VarInfo[VD->getNameAsString()] = WasEvaluated;
48      EXPECT_EQ(WasEvaluated, Init->isConstantInitializer(VD->getASTContext(),
49                                                          false /*ForRef*/));
50    }
51    return true;
52  }
53
54 private:
55  VarInfoMap &VarInfo;
56};
57
58class EvaluateConstantInitializersAction : public clang::ASTFrontendAction {
59 public:
60   std::unique_ptr<clang::ASTConsumer>
61   CreateASTConsumer(clang::CompilerInstance &Compiler,
62                     llvm::StringRef FilePath) override {
63     return llvm::make_unique<Consumer>();
64  }
65
66 private:
67  class Consumer : public clang::ASTConsumer {
68   public:
69    ~Consumer() override {}
70
71    void HandleTranslationUnit(clang::ASTContext &Ctx) override {
72      VarInfoMap VarInfo;
73      EvaluateConstantInitializersVisitor Evaluator(VarInfo);
74      Evaluator.TraverseDecl(Ctx.getTranslationUnitDecl());
75      EXPECT_EQ(2u, VarInfo.size());
76      EXPECT_FALSE(VarInfo["Dependent"]);
77      EXPECT_TRUE(VarInfo["Constant"]);
78      EXPECT_EQ(2u, VarInfo.size());
79    }
80  };
81};
82}
83
84TEST(EvaluateAsRValue, FailsGracefullyForUnknownTypes) {
85  // This is a regression test; the AST library used to trigger assertion
86  // failures because it assumed that the type of initializers was always
87  // known (which is true only after template instantiation).
88  std::string ModesToTest[] = {"-std=c++03", "-std=c++11", "-std=c++1y"};
89  for (std::string const &Mode : ModesToTest) {
90    std::vector<std::string> Args(1, Mode);
91    Args.push_back("-fno-delayed-template-parsing");
92    ASSERT_TRUE(runToolOnCodeWithArgs(
93      new EvaluateConstantInitializersAction(),
94      "template <typename T>"
95      "struct vector {"
96      "  explicit vector(int size);"
97      "};"
98      "template <typename R>"
99      "struct S {"
100      "  vector<R> intervals() const {"
101      "    vector<R> Dependent(2);"
102      "    return Dependent;"
103      "  }"
104      "};"
105      "void doSomething() {"
106      "  int Constant = 2 + 2;"
107      "  (void) Constant;"
108      "}",
109      Args));
110  }
111}
112