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