FrontendActionTest.cpp 11 KB

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