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 24 using namespace clang::tooling; 25 26 namespace { 27 // For each variable name encountered, whether its initializer was a 28 // constant. 29 typedef std::map<std::string, bool> VarInfoMap; 30 31 /// \brief Records information on variable initializers to a map. 32 class 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 58 class 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 84 TEST(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