brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.6 KiB · c400318 Raw
301 lines · cpp
1//===- unittests/Frontend/FrontendActionTest.cpp - FrontendAction tests ---===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Frontend/FrontendAction.h"10#include "clang/AST/ASTConsumer.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/DynamicRecursiveASTVisitor.h"13#include "clang/Basic/LangStandard.h"14#include "clang/Frontend/CompilerInstance.h"15#include "clang/Frontend/CompilerInvocation.h"16#include "clang/Frontend/FrontendActions.h"17#include "clang/Lex/Preprocessor.h"18#include "clang/Lex/PreprocessorOptions.h"19#include "clang/Sema/Sema.h"20#include "clang/Serialization/InMemoryModuleCache.h"21#include "clang/Serialization/ModuleCache.h"22#include "llvm/Support/MemoryBuffer.h"23#include "llvm/Support/ToolOutputFile.h"24#include "llvm/Support/VirtualFileSystem.h"25#include "llvm/TargetParser/Triple.h"26#include "gtest/gtest.h"27 28using namespace llvm;29using namespace clang;30 31namespace {32 33class TestASTFrontendAction : public ASTFrontendAction {34public:35  TestASTFrontendAction(bool enableIncrementalProcessing = false,36                        bool actOnEndOfTranslationUnit = false)37    : EnableIncrementalProcessing(enableIncrementalProcessing),38      ActOnEndOfTranslationUnit(actOnEndOfTranslationUnit) { }39 40  bool EnableIncrementalProcessing;41  bool ActOnEndOfTranslationUnit;42  std::vector<std::string> decl_names;43 44  bool BeginSourceFileAction(CompilerInstance &ci) override {45    if (EnableIncrementalProcessing)46      ci.getPreprocessor().enableIncrementalProcessing();47 48    return ASTFrontendAction::BeginSourceFileAction(ci);49  }50 51  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,52                                                 StringRef InFile) override {53    return std::make_unique<Visitor>(CI, ActOnEndOfTranslationUnit,54                                      decl_names);55  }56 57private:58  class Visitor : public ASTConsumer, public DynamicRecursiveASTVisitor {59  public:60    Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,61            std::vector<std::string> &decl_names) :62      CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),63      decl_names_(decl_names) {}64 65    void HandleTranslationUnit(ASTContext &context) override {66      if (ActOnEndOfTranslationUnit) {67        CI.getSema().ActOnEndOfTranslationUnit();68      }69      TraverseDecl(context.getTranslationUnitDecl());70    }71 72    bool VisitNamedDecl(NamedDecl *Decl) override {73      decl_names_.push_back(Decl->getQualifiedNameAsString());74      return true;75    }76 77  private:78    CompilerInstance &CI;79    bool ActOnEndOfTranslationUnit;80    std::vector<std::string> &decl_names_;81  };82};83 84TEST(ASTFrontendAction, Sanity) {85  auto invocation = std::make_shared<CompilerInvocation>();86  invocation->getPreprocessorOpts().addRemappedFile(87      "test.cc",88      MemoryBuffer::getMemBuffer("int main() { float x; }").release());89  invocation->getFrontendOpts().Inputs.push_back(90      FrontendInputFile("test.cc", Language::CXX));91  invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;92  invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";93  CompilerInstance compiler(std::move(invocation));94  compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());95  compiler.createDiagnostics();96 97  TestASTFrontendAction test_action;98  ASSERT_TRUE(compiler.ExecuteAction(test_action));99  ASSERT_EQ(2U, test_action.decl_names.size());100  EXPECT_EQ("main", test_action.decl_names[0]);101  EXPECT_EQ("x", test_action.decl_names[1]);102}103 104TEST(ASTFrontendAction, IncrementalParsing) {105  auto invocation = std::make_shared<CompilerInvocation>();106  invocation->getPreprocessorOpts().addRemappedFile(107      "test.cc",108      MemoryBuffer::getMemBuffer("int main() { float x; }").release());109  invocation->getFrontendOpts().Inputs.push_back(110      FrontendInputFile("test.cc", Language::CXX));111  invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;112  invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";113  CompilerInstance compiler(std::move(invocation));114  compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());115  compiler.createDiagnostics();116 117  TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true);118  ASSERT_TRUE(compiler.ExecuteAction(test_action));119  ASSERT_EQ(2U, test_action.decl_names.size());120  EXPECT_EQ("main", test_action.decl_names[0]);121  EXPECT_EQ("x", test_action.decl_names[1]);122}123 124TEST(ASTFrontendAction, LateTemplateIncrementalParsing) {125  auto invocation = std::make_shared<CompilerInvocation>();126  invocation->getLangOpts().CPlusPlus = true;127  invocation->getLangOpts().DelayedTemplateParsing = true;128  invocation->getPreprocessorOpts().addRemappedFile(129    "test.cc", MemoryBuffer::getMemBuffer(130      "template<typename T> struct A { A(T); T data; };\n"131      "template<typename T> struct B: public A<T> {\n"132      "  B();\n"133      "  B(B const& b): A<T>(b.data) {}\n"134      "};\n"135      "B<char> c() { return B<char>(); }\n").release());136  invocation->getFrontendOpts().Inputs.push_back(137      FrontendInputFile("test.cc", Language::CXX));138  invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;139  invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";140  CompilerInstance compiler(std::move(invocation));141  compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());142  compiler.createDiagnostics();143 144  TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true,145                                    /*actOnEndOfTranslationUnit=*/true);146  ASSERT_TRUE(compiler.ExecuteAction(test_action));147  ASSERT_EQ(13U, test_action.decl_names.size());148  EXPECT_EQ("A", test_action.decl_names[0]);149  EXPECT_EQ("c", test_action.decl_names[12]);150}151 152struct TestPPCallbacks : public PPCallbacks {153  TestPPCallbacks() : SeenEnd(false) {}154 155  void EndOfMainFile() override { SeenEnd = true; }156 157  bool SeenEnd;158};159 160class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {161  TestPPCallbacks *Callbacks;162 163public:164  TestPPCallbacksFrontendAction(TestPPCallbacks *C)165      : Callbacks(C), SeenEnd(false) {}166 167  void ExecuteAction() override {168    Preprocessor &PP = getCompilerInstance().getPreprocessor();169    PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));170    PP.EnterMainSourceFile();171  }172  void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }173 174  bool SeenEnd;175};176 177TEST(PreprocessorFrontendAction, EndSourceFile) {178  auto Invocation = std::make_shared<CompilerInvocation>();179  Invocation->getPreprocessorOpts().addRemappedFile(180      "test.cc",181      MemoryBuffer::getMemBuffer("int main() { float x; }").release());182  Invocation->getFrontendOpts().Inputs.push_back(183      FrontendInputFile("test.cc", Language::CXX));184  Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;185  Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";186  CompilerInstance Compiler(std::move(Invocation));187  Compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());188  Compiler.createDiagnostics();189 190  TestPPCallbacks *Callbacks = new TestPPCallbacks;191  TestPPCallbacksFrontendAction TestAction(Callbacks);192  ASSERT_FALSE(Callbacks->SeenEnd);193  ASSERT_FALSE(TestAction.SeenEnd);194  ASSERT_TRUE(Compiler.ExecuteAction(TestAction));195  // Check that EndOfMainFile was called before EndSourceFileAction.196  ASSERT_TRUE(TestAction.SeenEnd);197}198 199class TypoExternalSemaSource : public ExternalSemaSource {200  CompilerInstance &CI;201 202public:203  TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {}204 205  TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,206                             Scope *S, CXXScopeSpec *SS,207                             CorrectionCandidateCallback &CCC,208                             DeclContext *MemberContext, bool EnteringContext,209                             const ObjCObjectPointerType *OPT) override {210    // Generate a fake typo correction with one attached note.211    ASTContext &Ctx = CI.getASTContext();212    TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo")));213    unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(214        DiagnosticsEngine::Note, "This is a note");215    TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator()));216    return TC;217  }218};219 220struct TypoDiagnosticConsumer : public DiagnosticConsumer {221  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,222                        const Diagnostic &Info) override {223    // Capture errors and notes. There should be one of each.224    if (DiagLevel == DiagnosticsEngine::Error) {225      assert(Error.empty());226      Info.FormatDiagnostic(Error);227    } else {228      assert(Note.empty());229      Info.FormatDiagnostic(Note);230    }231  }232  SmallString<32> Error;233  SmallString<32> Note;234};235 236TEST(ASTFrontendAction, ExternalSemaSource) {237  auto Invocation = std::make_shared<CompilerInvocation>();238  Invocation->getLangOpts().CPlusPlus = true;239  Invocation->getPreprocessorOpts().addRemappedFile(240      "test.cc", MemoryBuffer::getMemBuffer("void fooo();\n"241                                            "int main() { foo(); }")242                     .release());243  Invocation->getFrontendOpts().Inputs.push_back(244      FrontendInputFile("test.cc", Language::CXX));245  Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;246  Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";247  CompilerInstance Compiler(std::move(Invocation));248  auto *TDC = new TypoDiagnosticConsumer;249  Compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());250  Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true);251  Compiler.setExternalSemaSource(252      llvm::makeIntrusiveRefCnt<TypoExternalSemaSource>(Compiler));253 254  SyntaxOnlyAction TestAction;255  ASSERT_TRUE(Compiler.ExecuteAction(TestAction));256  // There should be one error correcting to 'moo' and a note attached to it.257  EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?",258            std::string(TDC->Error));259  EXPECT_EQ("This is a note", std::string(TDC->Note));260}261 262TEST(GeneratePCHFrontendAction, CacheGeneratedPCH) {263  // Create a temporary file for writing out the PCH that will be cleaned up.264  int PCHFD;265  llvm::SmallString<128> PCHFilename;266  ASSERT_FALSE(267      llvm::sys::fs::createTemporaryFile("test.h", "pch", PCHFD, PCHFilename));268  llvm::ToolOutputFile PCHFile(PCHFilename, PCHFD);269 270  for (bool ShouldCache : {false, true}) {271    auto Invocation = std::make_shared<CompilerInvocation>();272    Invocation->getLangOpts().CacheGeneratedPCH = ShouldCache;273    Invocation->getPreprocessorOpts().addRemappedFile(274        "test.h",275        MemoryBuffer::getMemBuffer("int foo(void) { return 1; }\n").release());276    Invocation->getFrontendOpts().Inputs.push_back(277        FrontendInputFile("test.h", Language::C));278    Invocation->getFrontendOpts().OutputFile = PCHFilename.str().str();279    Invocation->getFrontendOpts().ProgramAction = frontend::GeneratePCH;280    Invocation->getTargetOpts().Triple = "x86_64-apple-darwin19.0.0";281    CompilerInstance Compiler(std::move(Invocation));282    Compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());283    Compiler.createDiagnostics();284 285    GeneratePCHAction TestAction;286    ASSERT_TRUE(Compiler.ExecuteAction(TestAction));287 288    // Check whether the PCH was cached.289    if (ShouldCache)290      EXPECT_EQ(InMemoryModuleCache::Final,291                Compiler.getModuleCache().getInMemoryModuleCache().getPCMState(292                    PCHFilename));293    else294      EXPECT_EQ(InMemoryModuleCache::Unknown,295                Compiler.getModuleCache().getInMemoryModuleCache().getPCMState(296                    PCHFilename));297  }298}299 300} // anonymous namespace301