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