generate_ast.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/cpp/generate_ast.h"
  5. #include <memory>
  6. #include <string>
  7. #include "clang/AST/ASTContext.h"
  8. #include "clang/Basic/FileManager.h"
  9. #include "clang/Frontend/CompilerInstance.h"
  10. #include "clang/Frontend/CompilerInvocation.h"
  11. #include "clang/Frontend/FrontendAction.h"
  12. #include "clang/Frontend/TextDiagnostic.h"
  13. #include "clang/Lex/PreprocessorOptions.h"
  14. #include "clang/Sema/ExternalSemaSource.h"
  15. #include "common/check.h"
  16. #include "common/raw_string_ostream.h"
  17. #include "llvm/ADT/IntrusiveRefCntPtr.h"
  18. #include "llvm/ADT/StringRef.h"
  19. #include "llvm/Support/raw_ostream.h"
  20. #include "toolchain/check/context.h"
  21. #include "toolchain/diagnostics/diagnostic.h"
  22. #include "toolchain/diagnostics/diagnostic_emitter.h"
  23. #include "toolchain/diagnostics/format_providers.h"
  24. #include "toolchain/parse/node_ids.h"
  25. #include "toolchain/sem_ir/cpp_file.h"
  26. namespace Carbon::Check {
  27. // Add a line marker directive pointing at the location of the `import Cpp`
  28. // declaration in the Carbon source file. This will cause Clang's diagnostics
  29. // machinery to track and report the location in Carbon code where the import
  30. // was written.
  31. static auto GenerateLineMarker(Context& context, llvm::raw_ostream& out,
  32. int line) {
  33. out << "# " << line << " \""
  34. << FormatEscaped(context.tokens().source().filename()) << "\"\n";
  35. }
  36. // Generates C++ file contents to #include all requested imports.
  37. static auto GenerateCppIncludesHeaderCode(
  38. Context& context, llvm::ArrayRef<Parse::Tree::PackagingNames> imports)
  39. -> std::string {
  40. std::string code;
  41. llvm::raw_string_ostream code_stream(code);
  42. for (const Parse::Tree::PackagingNames& import : imports) {
  43. if (import.inline_body_id.has_value()) {
  44. // Expand `import Cpp inline "code";` directly into the specified code.
  45. auto code_token = context.parse_tree().node_token(import.inline_body_id);
  46. // Compute the line number on which the C++ code starts. Usually the code
  47. // is specified as a block string literal and starts on the line after the
  48. // start of the string token.
  49. // TODO: Determine if this is a block string literal without calling
  50. // `GetTokenText`, which re-lexes the string.
  51. int line = context.tokens().GetLineNumber(code_token);
  52. if (context.tokens().GetTokenText(code_token).contains('\n')) {
  53. ++line;
  54. }
  55. GenerateLineMarker(context, code_stream, line);
  56. code_stream << context.string_literal_values().Get(
  57. context.tokens().GetStringLiteralValue(code_token))
  58. << "\n";
  59. // TODO: Inject a clang pragma here to produce an error if there are
  60. // unclosed scopes at the end of this inline C++ fragment.
  61. } else if (import.library_id.has_value()) {
  62. // Translate `import Cpp library "foo.h";` into `#include "foo.h"`.
  63. GenerateLineMarker(context, code_stream,
  64. context.tokens().GetLineNumber(
  65. context.parse_tree().node_token(import.node_id)));
  66. auto name = context.string_literal_values().Get(import.library_id);
  67. if (name.starts_with('<') && name.ends_with('>')) {
  68. code_stream << "#include <"
  69. << FormatEscaped(name.drop_front().drop_back()) << ">\n";
  70. } else {
  71. code_stream << "#include \"" << FormatEscaped(name) << "\"\n";
  72. }
  73. }
  74. }
  75. // Inject a declaration of placement operator new, because the code we
  76. // generate in thunks depends on it for placement new expressions. Clang has
  77. // special-case logic for lowering a new-expression using this, so a
  78. // definition is not required.
  79. // TODO: This is a hack. We should be able to directly generate Clang AST to
  80. // construct objects in-place without this.
  81. // TODO: Once we can rely on libc++ being available, consider including
  82. // `<__new/placement_new_delete.h>` instead.
  83. code_stream << R"(# 1 "<carbon-internal>"
  84. #undef constexpr
  85. #if __cplusplus > 202302L
  86. constexpr
  87. #endif
  88. #undef void
  89. #undef operator
  90. #undef new
  91. void* operator new(__SIZE_TYPE__, void*)
  92. #if __cplusplus < 201103L
  93. #undef throw
  94. throw()
  95. #else
  96. #undef noexcept
  97. noexcept
  98. #endif
  99. ;
  100. )";
  101. return code;
  102. }
  103. // Adds the given source location and an `ImportIRInst` referring to it in
  104. // `ImportIRId::Cpp`.
  105. static auto AddImportIRInst(SemIR::File& file,
  106. clang::SourceLocation clang_source_loc)
  107. -> SemIR::ImportIRInstId {
  108. SemIR::ClangSourceLocId clang_source_loc_id =
  109. file.clang_source_locs().Add(clang_source_loc);
  110. return file.import_ir_insts().Add(SemIR::ImportIRInst(clang_source_loc_id));
  111. }
  112. namespace {
  113. // Used to convert Clang diagnostics to Carbon diagnostics.
  114. //
  115. // Handling of Clang notes is a little subtle: as far as Clang is concerned,
  116. // notes are separate diagnostics, not connected to the error or warning that
  117. // precedes them. But in Carbon's diagnostics system, notes are part of the
  118. // enclosing diagnostic. To handle this, we buffer Clang diagnostics until we
  119. // reach a point where we know we're not in the middle of a diagnostic, and then
  120. // emit a diagnostic along with all of its notes. This is triggered when adding
  121. // or removing a Carbon context note, which could otherwise get attached to the
  122. // wrong C++ diagnostics, and at the end of the Carbon program.
  123. class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
  124. public:
  125. // Creates an instance with the location that triggers calling Clang. The
  126. // `context` is not stored here, and the diagnostics consumer is expected to
  127. // outlive it.
  128. explicit CarbonClangDiagnosticConsumer(
  129. Context& context, std::shared_ptr<clang::CompilerInvocation> invocation)
  130. : sem_ir_(&context.sem_ir()),
  131. emitter_(&context.emitter()),
  132. invocation_(std::move(invocation)) {
  133. emitter_->AddFlushFn([this] { EmitDiagnostics(); });
  134. }
  135. ~CarbonClangDiagnosticConsumer() override {
  136. // Do not inspect `emitter_` here; it's typically destroyed before the
  137. // consumer is.
  138. // TODO: If Clang produces diagnostics after check finishes, they'll get
  139. // added to the list of pending diagnostics and never emitted.
  140. CARBON_CHECK(diagnostic_infos_.empty(),
  141. "Missing flush before destroying diagnostic consumer");
  142. }
  143. // Generates a Carbon warning for each Clang warning and a Carbon error for
  144. // each Clang error or fatal.
  145. auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level,
  146. const clang::Diagnostic& info) -> void override {
  147. DiagnosticConsumer::HandleDiagnostic(diag_level, info);
  148. SemIR::ImportIRInstId clang_import_ir_inst_id =
  149. AddImportIRInst(*sem_ir_, info.getLocation());
  150. llvm::SmallString<256> message;
  151. info.FormatDiagnostic(message);
  152. // Render a code snippet including any highlighted ranges and fixit hints.
  153. // TODO: Also include the #include stack and macro expansion stack in the
  154. // diagnostic output in some way.
  155. RawStringOstream snippet_stream;
  156. if (!info.hasSourceManager()) {
  157. // If we don't have a source manager, this is an error from early in the
  158. // frontend. Don't produce a snippet.
  159. CARBON_CHECK(info.getLocation().isInvalid());
  160. } else {
  161. CodeContextRenderer(snippet_stream, invocation_->getLangOpts(),
  162. invocation_->getDiagnosticOpts())
  163. .emitDiagnostic(
  164. clang::FullSourceLoc(info.getLocation(), info.getSourceManager()),
  165. diag_level, message, info.getRanges(), info.getFixItHints());
  166. }
  167. diagnostic_infos_.push_back({.level = diag_level,
  168. .import_ir_inst_id = clang_import_ir_inst_id,
  169. .message = message.str().str(),
  170. .snippet = snippet_stream.TakeStr()});
  171. }
  172. // Returns the diagnostic to use for a given Clang diagnostic level.
  173. static auto GetDiagnostic(clang::DiagnosticsEngine::Level level)
  174. -> const Diagnostics::DiagnosticBase<std::string>& {
  175. switch (level) {
  176. case clang::DiagnosticsEngine::Ignored: {
  177. CARBON_FATAL("Emitting an ignored diagnostic");
  178. break;
  179. }
  180. case clang::DiagnosticsEngine::Note: {
  181. CARBON_DIAGNOSTIC(CppInteropParseNote, Note, "{0}", std::string);
  182. return CppInteropParseNote;
  183. }
  184. case clang::DiagnosticsEngine::Remark:
  185. case clang::DiagnosticsEngine::Warning: {
  186. // TODO: Add a distinct Remark level to Carbon diagnostics, and stop
  187. // mapping remarks to warnings.
  188. CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning, "{0}", std::string);
  189. return CppInteropParseWarning;
  190. }
  191. case clang::DiagnosticsEngine::Error:
  192. case clang::DiagnosticsEngine::Fatal: {
  193. CARBON_DIAGNOSTIC(CppInteropParseError, Error, "{0}", std::string);
  194. return CppInteropParseError;
  195. }
  196. }
  197. }
  198. // Outputs Carbon diagnostics based on the collected Clang diagnostics. Must
  199. // be called after the AST is set in the context.
  200. auto EmitDiagnostics() -> void {
  201. CARBON_CHECK(
  202. sem_ir_->cpp_file(),
  203. "Attempted to emit C++ diagnostics before the C++ file is set");
  204. for (size_t i = 0; i != diagnostic_infos_.size(); ++i) {
  205. const ClangDiagnosticInfo& info = diagnostic_infos_[i];
  206. auto builder = emitter_->Build(SemIR::LocId(info.import_ir_inst_id),
  207. GetDiagnostic(info.level), info.message);
  208. builder.OverrideSnippet(info.snippet);
  209. for (; i + 1 < diagnostic_infos_.size() &&
  210. diagnostic_infos_[i + 1].level == clang::DiagnosticsEngine::Note;
  211. ++i) {
  212. const ClangDiagnosticInfo& note_info = diagnostic_infos_[i + 1];
  213. builder
  214. .Note(SemIR::LocId(note_info.import_ir_inst_id),
  215. GetDiagnostic(note_info.level), note_info.message)
  216. .OverrideSnippet(note_info.snippet);
  217. }
  218. // TODO: This will apply all current Carbon annotation functions. We
  219. // should instead track how Clang's context notes and Carbon's annotation
  220. // functions are interleaved, and interleave the notes in the same order.
  221. builder.Emit();
  222. }
  223. diagnostic_infos_.clear();
  224. }
  225. private:
  226. // A diagnostics renderer based on clang's TextDiagnostic that captures just
  227. // the code context (the snippet).
  228. class CodeContextRenderer : public clang::TextDiagnostic {
  229. protected:
  230. using TextDiagnostic::TextDiagnostic;
  231. void emitDiagnosticMessage(
  232. clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/,
  233. clang::DiagnosticsEngine::Level /*level*/, llvm::StringRef /*message*/,
  234. llvm::ArrayRef<clang::CharSourceRange> /*ranges*/,
  235. clang::DiagOrStoredDiag /*info*/) override {}
  236. void emitDiagnosticLoc(
  237. clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/,
  238. clang::DiagnosticsEngine::Level /*level*/,
  239. llvm::ArrayRef<clang::CharSourceRange> /*ranges*/) override {}
  240. // emitCodeContext is inherited from clang::TextDiagnostic.
  241. void emitIncludeLocation(clang::FullSourceLoc /*loc*/,
  242. clang::PresumedLoc /*ploc*/) override {}
  243. void emitImportLocation(clang::FullSourceLoc /*loc*/,
  244. clang::PresumedLoc /*ploc*/,
  245. llvm::StringRef /*module_name*/) override {}
  246. void emitBuildingModuleLocation(clang::FullSourceLoc /*loc*/,
  247. clang::PresumedLoc /*ploc*/,
  248. llvm::StringRef /*module_name*/) override {}
  249. // beginDiagnostic and endDiagnostic are inherited from
  250. // clang::TextDiagnostic in case it wants to do any setup / teardown work.
  251. };
  252. // Information on a Clang diagnostic that can be converted to a Carbon
  253. // diagnostic.
  254. struct ClangDiagnosticInfo {
  255. // The Clang diagnostic level.
  256. clang::DiagnosticsEngine::Level level;
  257. // The ID of the ImportIR instruction referring to the Clang source
  258. // location.
  259. SemIR::ImportIRInstId import_ir_inst_id;
  260. // The Clang diagnostic textual message.
  261. std::string message;
  262. // The code snippet produced by clang.
  263. std::string snippet;
  264. };
  265. // The Carbon file that this C++ compilation is attached to.
  266. SemIR::File* sem_ir_;
  267. // The diagnostic emitter that we're emitting diagnostics into.
  268. DiagnosticEmitterBase* emitter_;
  269. // The compiler invocation that is producing the diagnostics.
  270. std::shared_ptr<clang::CompilerInvocation> invocation_;
  271. // Collects the information for all Clang diagnostics to be converted to
  272. // Carbon diagnostics after the context has been initialized with the Clang
  273. // AST.
  274. llvm::SmallVector<ClangDiagnosticInfo> diagnostic_infos_;
  275. };
  276. // A wrapper around a clang::CompilerInvocation that allows us to make a shallow
  277. // copy of most of the invocation and only make a deep copy of the parts that we
  278. // want to change.
  279. //
  280. // clang::CowCompilerInvocation almost allows this, but doesn't derive from
  281. // CompilerInvocation or support shallow copies from a CompilerInvocation, so is
  282. // not useful to us as we can't build an ASTUnit from it.
  283. class ShallowCopyCompilerInvocation : public clang::CompilerInvocation {
  284. public:
  285. explicit ShallowCopyCompilerInvocation(
  286. const clang::CompilerInvocation& invocation) {
  287. shallow_copy_assign(invocation);
  288. // Make a deep copy of options that we modify.
  289. FrontendOpts = std::make_shared<clang::FrontendOptions>(*FrontendOpts);
  290. PPOpts = std::make_shared<clang::PreprocessorOptions>(*PPOpts);
  291. }
  292. };
  293. // An AST consumer that tracks top-level declarations so they can be handed off
  294. // to code generation later.
  295. class BufferingConsumer : public clang::ASTConsumer {
  296. public:
  297. explicit BufferingConsumer(SemIR::CppFile& file) : file_(&file) {}
  298. auto HandleTopLevelDecl(clang::DeclGroupRef decl_group) -> bool override {
  299. file_->decl_groups().push_back(decl_group);
  300. return true;
  301. }
  302. private:
  303. SemIR::CppFile* file_;
  304. };
  305. // An action and a set of registered Clang callbacks used to generate an AST
  306. // from a set of Cpp imports.
  307. class GenerateASTAction : public clang::ASTFrontendAction {
  308. public:
  309. explicit GenerateASTAction(Context& context) : context_(&context) {}
  310. protected:
  311. auto CreateASTConsumer(clang::CompilerInstance& /*clang_instance*/,
  312. llvm::StringRef /*file*/)
  313. -> std::unique_ptr<clang::ASTConsumer> override {
  314. return std::make_unique<BufferingConsumer>(*context_->sem_ir().cpp_file());
  315. }
  316. auto BeginSourceFileAction(clang::CompilerInstance& /*clang_instance*/)
  317. -> bool override {
  318. // TODO: Consider creating an `ExternalSemaSource` here and attaching it to
  319. // the compilation.
  320. // TODO: `clang.getPreprocessor().enableIncrementalProcessing();` to avoid
  321. // the TU scope getting torn down before we're done parsing macros.
  322. return true;
  323. }
  324. private:
  325. Context* context_;
  326. };
  327. } // namespace
  328. auto GenerateAst(Context& context,
  329. llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
  330. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
  331. std::shared_ptr<clang::CompilerInvocation> base_invocation)
  332. -> bool {
  333. CARBON_CHECK(!context.cpp_context());
  334. CARBON_CHECK(!context.sem_ir().cpp_file());
  335. auto invocation =
  336. std::make_shared<ShallowCopyCompilerInvocation>(*base_invocation);
  337. // Ask Clang to not leak memory.
  338. invocation->getFrontendOpts().DisableFree = false;
  339. // Build a diagnostics engine.
  340. llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
  341. clang::CompilerInstance::createDiagnostics(
  342. *fs, invocation->getDiagnosticOpts(),
  343. new CarbonClangDiagnosticConsumer(context, invocation),
  344. /*ShouldOwnClient=*/true));
  345. // Extract the input from the frontend invocation and make sure it makes
  346. // sense.
  347. const auto& inputs = invocation->getFrontendOpts().Inputs;
  348. CARBON_CHECK(inputs.size() == 1 &&
  349. inputs[0].getKind().getLanguage() == clang::Language::CXX &&
  350. inputs[0].getKind().getFormat() == clang::InputKind::Source);
  351. llvm::StringRef file_name = inputs[0].getFile();
  352. // Remap the imports file name to the corresponding `#include`s.
  353. // TODO: Modify the frontend options to specify this memory buffer as input
  354. // instead of remapping the file.
  355. std::string includes = GenerateCppIncludesHeaderCode(context, imports);
  356. auto includes_buffer =
  357. llvm::MemoryBuffer::getMemBufferCopy(includes, file_name);
  358. invocation->getPreprocessorOpts().addRemappedFile(file_name,
  359. includes_buffer.release());
  360. clang::DiagnosticErrorTrap trap(*diags);
  361. auto clang_instance_ptr =
  362. std::make_unique<clang::CompilerInstance>(invocation);
  363. auto& clang_instance = *clang_instance_ptr;
  364. context.sem_ir().set_cpp_file(
  365. std::make_unique<SemIR::CppFile>(std::move(clang_instance_ptr)));
  366. clang_instance.setDiagnostics(diags);
  367. clang_instance.setVirtualFileSystem(fs);
  368. clang_instance.createFileManager();
  369. clang_instance.createSourceManager();
  370. if (!clang_instance.createTarget()) {
  371. return false;
  372. }
  373. context.set_cpp_context(std::make_unique<CppContext>(
  374. std::make_unique<GenerateASTAction>(context)));
  375. if (!context.cpp_context()->action().BeginSourceFile(clang_instance,
  376. inputs[0])) {
  377. return false;
  378. }
  379. if (llvm::Error error = context.cpp_context()->action().Execute()) {
  380. // `Execute` currently never fails, but its contract allows it to.
  381. context.TODO(SemIR::LocId::None, "failed to execute clang action: " +
  382. llvm::toString(std::move(error)));
  383. return false;
  384. }
  385. // Flush any diagnostics. We know we're not part-way through emitting a
  386. // diagnostic now.
  387. context.emitter().Flush();
  388. return !trap.hasErrorOccurred();
  389. }
  390. } // namespace Carbon::Check