//===- unittests/AST/EvaluateAsRValueTest.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // \file // \brief Unit tests for evaluation of constant initializers. // //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" #include #include using namespace clang::tooling; namespace { // For each variable name encountered, whether its initializer was a // constant. typedef std::map VarInfoMap; /// \brief Records information on variable initializers to a map. class EvaluateConstantInitializersVisitor : public clang::RecursiveASTVisitor { public: explicit EvaluateConstantInitializersVisitor(VarInfoMap &VarInfo) : VarInfo(VarInfo) {} /// \brief Checks that isConstantInitializer and EvaluateAsRValue agree /// and don't crash. /// /// For each VarDecl with an initializer this also records in VarInfo /// whether the initializer could be evaluated as a constant. bool VisitVarDecl(const clang::VarDecl *VD) { if (const clang::Expr *Init = VD->getInit()) { clang::Expr::EvalResult Result; bool WasEvaluated = Init->EvaluateAsRValue(Result, VD->getASTContext()); VarInfo[VD->getNameAsString()] = WasEvaluated; EXPECT_EQ(WasEvaluated, Init->isConstantInitializer(VD->getASTContext(), false /*ForRef*/)); } return true; } private: VarInfoMap &VarInfo; }; class EvaluateConstantInitializersAction : public clang::ASTFrontendAction { public: std::unique_ptr CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef FilePath) override { return std::make_unique(); } private: class Consumer : public clang::ASTConsumer { public: ~Consumer() override {} void HandleTranslationUnit(clang::ASTContext &Ctx) override { VarInfoMap VarInfo; EvaluateConstantInitializersVisitor Evaluator(VarInfo); Evaluator.TraverseDecl(Ctx.getTranslationUnitDecl()); EXPECT_EQ(2u, VarInfo.size()); EXPECT_FALSE(VarInfo["Dependent"]); EXPECT_TRUE(VarInfo["Constant"]); EXPECT_EQ(2u, VarInfo.size()); } }; }; } TEST(EvaluateAsRValue, FailsGracefullyForUnknownTypes) { // This is a regression test; the AST library used to trigger assertion // failures because it assumed that the type of initializers was always // known (which is true only after template instantiation). std::string ModesToTest[] = {"-std=c++03", "-std=c++11", "-std=c++1y"}; for (std::string const &Mode : ModesToTest) { std::vector Args(1, Mode); Args.push_back("-fno-delayed-template-parsing"); ASSERT_TRUE(runToolOnCodeWithArgs( std::make_unique(), "template " "struct vector {" " explicit vector(int size);" "};" "template " "struct S {" " vector intervals() const {" " vector Dependent(2);" " return Dependent;" " }" "};" "void doSomething() {" " int Constant = 2 + 2;" " (void) Constant;" "}", Args)); } }