clang_invocation.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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/base/clang_invocation.h"
  5. #include "clang/Frontend/CompilerInstance.h"
  6. #include "clang/Frontend/Utils.h"
  7. namespace Carbon {
  8. // The fake file name to use for the synthesized includes file.
  9. static constexpr const char IncludesFileName[] = "<carbon Cpp imports>";
  10. namespace {
  11. // Used to convert diagnostics from the Clang driver to Carbon diagnostics.
  12. class ClangDriverDiagnosticConsumer : public clang::DiagnosticConsumer {
  13. public:
  14. // Creates an instance with the location that triggers calling Clang.
  15. // `context` must not be null.
  16. explicit ClangDriverDiagnosticConsumer(Diagnostics::NoLocEmitter* emitter)
  17. : emitter_(emitter) {}
  18. // Generates a Carbon warning for each Clang warning and a Carbon error for
  19. // each Clang error or fatal.
  20. auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level,
  21. const clang::Diagnostic& info) -> void override {
  22. DiagnosticConsumer::HandleDiagnostic(diag_level, info);
  23. llvm::SmallString<256> message;
  24. info.FormatDiagnostic(message);
  25. switch (diag_level) {
  26. case clang::DiagnosticsEngine::Ignored:
  27. case clang::DiagnosticsEngine::Note:
  28. case clang::DiagnosticsEngine::Remark: {
  29. // TODO: Emit notes and remarks.
  30. break;
  31. }
  32. case clang::DiagnosticsEngine::Warning:
  33. case clang::DiagnosticsEngine::Error:
  34. case clang::DiagnosticsEngine::Fatal: {
  35. CARBON_DIAGNOSTIC(CppInteropDriverWarning, Warning, "{0}", std::string);
  36. CARBON_DIAGNOSTIC(CppInteropDriverError, Error, "{0}", std::string);
  37. emitter_->Emit(diag_level == clang::DiagnosticsEngine::Warning
  38. ? CppInteropDriverWarning
  39. : CppInteropDriverError,
  40. message.str().str());
  41. break;
  42. }
  43. }
  44. }
  45. private:
  46. // Diagnostic emitter. Note that driver diagnostics don't have meaningful
  47. // locations attached.
  48. Diagnostics::NoLocEmitter* emitter_;
  49. };
  50. } // namespace
  51. static auto BuildClangInvocationImpl(
  52. Diagnostics::NoLocEmitter& emitter,
  53. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
  54. llvm::ArrayRef<std::string> clang_path_and_args)
  55. -> std::unique_ptr<clang::CompilerInvocation> {
  56. ClangDriverDiagnosticConsumer diagnostics_consumer(&emitter);
  57. // The clang driver inconveniently wants an array of `const char*`, so convert
  58. // the arguments.
  59. llvm::SmallVector<const char*> driver_args(llvm::map_range(
  60. clang_path_and_args, [](const std::string& str) { return str.c_str(); }));
  61. // Add our include file name as the input file, and force it to be interpreted
  62. // as C++.
  63. driver_args.push_back("-x");
  64. driver_args.push_back("c++");
  65. driver_args.push_back(IncludesFileName);
  66. // Build a diagnostics engine. Note that we don't have any diagnostic options
  67. // yet; they're produced by running the driver.
  68. clang::DiagnosticOptions driver_diag_opts;
  69. llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> driver_diags(
  70. clang::CompilerInstance::createDiagnostics(*fs, driver_diag_opts,
  71. &diagnostics_consumer,
  72. /*ShouldOwnClient=*/false));
  73. // Ask the driver to process the arguments and build a corresponding clang
  74. // frontend invocation.
  75. return clang::createInvocation(driver_args,
  76. {.Diags = driver_diags, .VFS = fs});
  77. }
  78. auto BuildClangInvocation(Diagnostics::Consumer& consumer,
  79. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
  80. llvm::ArrayRef<std::string> clang_path_and_args)
  81. -> std::unique_ptr<clang::CompilerInvocation> {
  82. Diagnostics::ErrorTrackingConsumer error_tracker(consumer);
  83. Diagnostics::NoLocEmitter emitter(&error_tracker);
  84. // Forward to the implementation to avoid exposing `import_cpp` outside check.
  85. auto invocation = BuildClangInvocationImpl(emitter, fs, clang_path_and_args);
  86. // If Clang produced an error, throw away its invocation.
  87. if (error_tracker.seen_error()) {
  88. return nullptr;
  89. }
  90. return invocation;
  91. }
  92. } // namespace Carbon