Clang Project

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