diagnostic_emitter.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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/check/diagnostic_helpers.h"
  10. #include "toolchain/sem_ir/absolute_node_id.h"
  11. #include "toolchain/sem_ir/ids.h"
  12. #include "toolchain/sem_ir/stringify.h"
  13. namespace Carbon::Check {
  14. auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
  15. ContextFnT context_fn) const
  16. -> Diagnostics::ConvertedLoc {
  17. auto converted =
  18. ConvertLocImpl(loc_id.loc_id(), loc_id.is_token_only(), context_fn);
  19. // Use the token when possible, but -1 is the default value.
  20. auto last_offset = -1;
  21. if (last_token_.has_value()) {
  22. last_offset = sem_ir_->parse_tree().tokens().GetByteOffset(last_token_);
  23. }
  24. // When the diagnostic is in the same file, we use the last possible offset;
  25. // otherwise, we ignore the offset because it's probably in that file.
  26. if (converted.loc.filename == sem_ir_->filename()) {
  27. converted.last_byte_offset =
  28. std::max(converted.last_byte_offset, last_offset);
  29. } else {
  30. converted.last_byte_offset = last_offset;
  31. }
  32. return converted;
  33. }
  34. auto DiagnosticEmitter::ConvertLocImpl(SemIR::LocId loc_id, bool is_token_only,
  35. ContextFnT context_fn) const
  36. -> Diagnostics::ConvertedLoc {
  37. llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
  38. SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
  39. auto final_node_id = absolute_node_ids.pop_back_val();
  40. for (const auto& absolute_node_id : absolute_node_ids) {
  41. if (!absolute_node_id.node_id().has_value()) {
  42. // TODO: Add an "In implicit import of prelude." note for the case where
  43. // we don't have a location.
  44. continue;
  45. }
  46. // TODO: Include the name of the imported library in the diagnostic.
  47. auto diag_loc =
  48. ConvertLocInFile(absolute_node_id, is_token_only, context_fn);
  49. AddInImport(diag_loc.loc, context_fn);
  50. }
  51. return ConvertLocInFile(final_node_id, is_token_only, context_fn);
  52. }
  53. auto DiagnosticEmitter::ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id,
  54. bool token_only,
  55. ContextFnT context_fn) const
  56. -> Diagnostics::ConvertedLoc {
  57. if (absolute_node_id.check_ir_id() == SemIR::CheckIRId::Cpp) {
  58. // Special handling of Clang source locations.
  59. CARBON_CHECK(sem_ir_->import_cpps().size() > 0);
  60. // TODO: Use information on the specific C++ import extract from Clang error
  61. // message and propagated here instead of using first C++ import
  62. // arbitrarily.
  63. Parse::NodeId import_node_id =
  64. sem_ir_->import_cpps().values().begin()->node_id;
  65. AddInImport(ConvertLocInCarbonFile(sem_ir_->check_ir_id(), import_node_id,
  66. /*token_only=*/false)
  67. .loc,
  68. context_fn);
  69. clang::SourceLocation clang_loc = sem_ir_->clang_source_locs().Get(
  70. absolute_node_id.clang_source_loc_id());
  71. CARBON_CHECK(sem_ir_->cpp_ast());
  72. clang::PresumedLoc presumed_loc =
  73. sem_ir_->cpp_ast()->getSourceManager().getPresumedLoc(clang_loc);
  74. return Diagnostics::ConvertedLoc{
  75. .loc = {.filename = presumed_loc.getFilename(),
  76. .line_number = static_cast<int32_t>(presumed_loc.getLine())},
  77. // TODO: Set `last_byte_offset` based on the `import Cpp` location.
  78. .last_byte_offset = 0};
  79. }
  80. return ConvertLocInCarbonFile(absolute_node_id.check_ir_id(),
  81. absolute_node_id.node_id(), token_only);
  82. }
  83. auto DiagnosticEmitter::ConvertLocInCarbonFile(SemIR::CheckIRId check_ir_id,
  84. Parse::NodeId node_id,
  85. bool token_only) const
  86. -> Diagnostics::ConvertedLoc {
  87. CARBON_CHECK(check_ir_id != SemIR::CheckIRId::Cpp);
  88. const auto& tree_and_subtrees =
  89. tree_and_subtrees_getters_[check_ir_id.index]();
  90. return tree_and_subtrees.NodeToDiagnosticLoc(node_id, token_only);
  91. }
  92. auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
  93. if (auto* library_name_id = llvm::any_cast<SemIR::LibraryNameId>(&arg)) {
  94. std::string library_name;
  95. if (*library_name_id == SemIR::LibraryNameId::Default) {
  96. library_name = "default library";
  97. } else if (!library_name_id->has_value()) {
  98. library_name = "library <none>";
  99. } else {
  100. RawStringOstream stream;
  101. stream << "library \""
  102. << sem_ir_->string_literal_values().Get(
  103. library_name_id->AsStringLiteralValueId())
  104. << "\"";
  105. library_name = stream.TakeStr();
  106. }
  107. return library_name;
  108. }
  109. if (auto* name_id = llvm::any_cast<SemIR::NameId>(&arg)) {
  110. return sem_ir_->names().GetFormatted(*name_id).str();
  111. }
  112. if (auto* type_of_expr = llvm::any_cast<TypeOfInstId>(&arg)) {
  113. if (!type_of_expr->inst_id.has_value()) {
  114. return "<none>";
  115. }
  116. // TODO: Where possible, produce a better description of the type based on
  117. // the expression.
  118. return "`" +
  119. StringifyConstantInst(
  120. *sem_ir_,
  121. sem_ir_->types().GetInstId(
  122. sem_ir_->insts().Get(type_of_expr->inst_id).type_id())) +
  123. "`";
  124. }
  125. if (auto* expr = llvm::any_cast<InstIdAsConstant>(&arg)) {
  126. return "`" + StringifyConstantInst(*sem_ir_, expr->inst_id) + "`";
  127. }
  128. if (auto* type_expr = llvm::any_cast<InstIdAsRawType>(&arg)) {
  129. return StringifyConstantInst(*sem_ir_, type_expr->inst_id);
  130. }
  131. if (auto* type = llvm::any_cast<TypeIdAsRawType>(&arg)) {
  132. return StringifyConstantInst(*sem_ir_,
  133. sem_ir_->types().GetInstId(type->type_id));
  134. }
  135. if (auto* type_id = llvm::any_cast<SemIR::TypeId>(&arg)) {
  136. return "`" +
  137. StringifyConstantInst(*sem_ir_,
  138. sem_ir_->types().GetInstId(*type_id)) +
  139. "`";
  140. }
  141. if (auto* specific_id = llvm::any_cast<SemIR::SpecificId>(&arg)) {
  142. return "`" + StringifySpecific(*sem_ir_, *specific_id) + "`";
  143. }
  144. if (auto* typed_int = llvm::any_cast<TypedInt>(&arg)) {
  145. return llvm::APSInt(typed_int->value,
  146. !sem_ir_->types().IsSignedInt(typed_int->type));
  147. }
  148. if (auto* specific_interface_id =
  149. llvm::any_cast<SemIR::SpecificInterfaceId>(&arg)) {
  150. auto specific_interface =
  151. sem_ir_->specific_interfaces().Get(*specific_interface_id);
  152. return "`" + StringifySpecificInterface(*sem_ir_, specific_interface) + "`";
  153. }
  154. if (auto* specific_interface_raw =
  155. llvm::any_cast<SpecificInterfaceIdAsRawType>(&arg)) {
  156. auto specific_interface = sem_ir_->specific_interfaces().Get(
  157. specific_interface_raw->specific_interface_id);
  158. return StringifySpecificInterface(*sem_ir_, specific_interface);
  159. }
  160. return DiagnosticEmitterBase::ConvertArg(arg);
  161. }
  162. auto DiagnosticEmitter::AddInImport(Diagnostics::Loc loc, ContextFnT context_fn)
  163. -> void {
  164. CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
  165. context_fn(loc, InImport);
  166. }
  167. } // namespace Carbon::Check