diagnostic_emitter.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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/diagnostic_emitter.h"
  5. #include <algorithm>
  6. #include <optional>
  7. #include <string>
  8. #include "common/raw_string_ostream.h"
  9. #include "toolchain/sem_ir/absolute_node_id.h"
  10. #include "toolchain/sem_ir/stringify.h"
  11. namespace Carbon::Check {
  12. auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
  13. ContextFnT context_fn) const
  14. -> Diagnostics::ConvertedLoc {
  15. // TODO: Instead of special casing Clang location here, support it within
  16. // `GetAbsoluteNodeId()`. See discussion in
  17. // https://github.com/carbon-language/carbon-lang/pull/5262/files/20a3f9dcfab5c6f6c5089554fd5e22d5f1ca75a3#r2040308805.
  18. auto converted_clang_loc =
  19. TryConvertClangDiagnosticLoc(static_cast<SemIR::LocId>(loc_id));
  20. if (converted_clang_loc) {
  21. return *converted_clang_loc;
  22. }
  23. auto converted =
  24. ConvertLocImpl(static_cast<SemIR::LocId>(loc_id), context_fn);
  25. // Use the token when possible, but -1 is the default value.
  26. auto last_offset = -1;
  27. if (last_token_.has_value()) {
  28. last_offset = sem_ir_->parse_tree().tokens().GetByteOffset(last_token_);
  29. }
  30. // When the diagnostic is in the same file, we use the last possible offset;
  31. // otherwise, we ignore the offset because it's probably in that file.
  32. if (converted.loc.filename == sem_ir_->filename()) {
  33. converted.last_byte_offset =
  34. std::max(converted.last_byte_offset, last_offset);
  35. } else {
  36. converted.last_byte_offset = last_offset;
  37. }
  38. return converted;
  39. }
  40. auto DiagnosticEmitter::ConvertLocImpl(SemIR::LocId loc_id,
  41. ContextFnT context_fn) const
  42. -> Diagnostics::ConvertedLoc {
  43. llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
  44. SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
  45. bool token_only =
  46. loc_id.kind() != SemIR::LocId::Kind::InstId && loc_id.is_token_only();
  47. auto final_node_id = absolute_node_ids.pop_back_val();
  48. for (const auto& absolute_node_id : absolute_node_ids) {
  49. if (!absolute_node_id.node_id.has_value()) {
  50. // TODO: Add an "In implicit import of prelude." note for the case where
  51. // we don't have a location.
  52. continue;
  53. }
  54. // TODO: Include the name of the imported library in the diagnostic.
  55. auto diag_loc = ConvertLocInFile(absolute_node_id, token_only, context_fn);
  56. CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
  57. context_fn(diag_loc.loc, InImport);
  58. }
  59. return ConvertLocInFile(final_node_id, token_only, context_fn);
  60. }
  61. auto DiagnosticEmitter::TryConvertClangDiagnosticLoc(SemIR::LocId loc_id) const
  62. -> std::optional<Diagnostics::ConvertedLoc> {
  63. if (loc_id.kind() != SemIR::LocId::Kind::ImportIRInstId) {
  64. return std::nullopt;
  65. }
  66. SemIR::ImportIRInst import_ir_inst =
  67. sem_ir_->import_ir_insts().Get(loc_id.import_ir_inst_id());
  68. if (import_ir_inst.ir_id() != SemIR::ImportIRId::Cpp) {
  69. return std::nullopt;
  70. }
  71. clang::SourceLocation clang_loc =
  72. sem_ir_->clang_source_locs().Get(import_ir_inst.clang_source_loc_id());
  73. CARBON_CHECK(sem_ir_->cpp_ast());
  74. clang::PresumedLoc presumed_loc =
  75. sem_ir_->cpp_ast()->getSourceManager().getPresumedLoc(clang_loc);
  76. return Diagnostics::ConvertedLoc{
  77. .loc = {.filename = presumed_loc.getFilename(),
  78. .line_number = static_cast<int32_t>(presumed_loc.getLine())},
  79. // TODO: Set `last_byte_offset` based on the `import Cpp` location.
  80. .last_byte_offset = 0};
  81. }
  82. auto DiagnosticEmitter::ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id,
  83. bool token_only,
  84. ContextFnT /*context_fn*/) const
  85. -> Diagnostics::ConvertedLoc {
  86. const auto& tree_and_subtrees =
  87. tree_and_subtrees_getters_[absolute_node_id.check_ir_id.index]();
  88. return tree_and_subtrees.NodeToDiagnosticLoc(absolute_node_id.node_id,
  89. token_only);
  90. }
  91. auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
  92. if (auto* library_name_id = llvm::any_cast<SemIR::LibraryNameId>(&arg)) {
  93. std::string library_name;
  94. if (*library_name_id == SemIR::LibraryNameId::Default) {
  95. library_name = "default library";
  96. } else if (!library_name_id->has_value()) {
  97. library_name = "library <none>";
  98. } else {
  99. RawStringOstream stream;
  100. stream << "library \""
  101. << sem_ir_->string_literal_values().Get(
  102. library_name_id->AsStringLiteralValueId())
  103. << "\"";
  104. library_name = stream.TakeStr();
  105. }
  106. return library_name;
  107. }
  108. if (auto* name_id = llvm::any_cast<SemIR::NameId>(&arg)) {
  109. return sem_ir_->names().GetFormatted(*name_id).str();
  110. }
  111. if (auto* type_of_expr = llvm::any_cast<TypeOfInstId>(&arg)) {
  112. if (!type_of_expr->inst_id.has_value()) {
  113. return "<none>";
  114. }
  115. // TODO: Where possible, produce a better description of the type based on
  116. // the expression.
  117. return "`" +
  118. StringifyConstantInst(
  119. *sem_ir_,
  120. sem_ir_->types().GetInstId(
  121. sem_ir_->insts().Get(type_of_expr->inst_id).type_id())) +
  122. "`";
  123. }
  124. if (auto* expr = llvm::any_cast<InstIdAsConstant>(&arg)) {
  125. return "`" + StringifyConstantInst(*sem_ir_, expr->inst_id) + "`";
  126. }
  127. if (auto* type_expr = llvm::any_cast<InstIdAsRawType>(&arg)) {
  128. return StringifyConstantInst(*sem_ir_, type_expr->inst_id);
  129. }
  130. if (auto* type = llvm::any_cast<TypeIdAsRawType>(&arg)) {
  131. return StringifyConstantInst(*sem_ir_,
  132. sem_ir_->types().GetInstId(type->type_id));
  133. }
  134. if (auto* type_id = llvm::any_cast<SemIR::TypeId>(&arg)) {
  135. return "`" +
  136. StringifyConstantInst(*sem_ir_,
  137. sem_ir_->types().GetInstId(*type_id)) +
  138. "`";
  139. }
  140. if (auto* specific_id = llvm::any_cast<SemIR::SpecificId>(&arg)) {
  141. return "`" + StringifySpecific(*sem_ir_, *specific_id) + "`";
  142. }
  143. if (auto* typed_int = llvm::any_cast<TypedInt>(&arg)) {
  144. return llvm::APSInt(typed_int->value,
  145. !sem_ir_->types().IsSignedInt(typed_int->type));
  146. }
  147. return DiagnosticEmitterBase::ConvertArg(arg);
  148. }
  149. } // namespace Carbon::Check