var_decl.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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 "migrate_cpp/cpp_refactoring/var_decl.h"
  5. #include "clang/ASTMatchers/ASTMatchers.h"
  6. #include "clang/Lex/Lexer.h"
  7. namespace cam = ::clang::ast_matchers;
  8. namespace Carbon {
  9. VarDecl::VarDecl(std::map<std::string, Replacements>& in_replacements,
  10. cam::MatchFinder* finder)
  11. : Matcher(in_replacements) {
  12. // Skip range-based for loops in this var-inserter.
  13. finder->addMatcher(cam::varDecl(cam::unless(cam::hasParent(cam::declStmt(
  14. cam::hasParent(cam::cxxForRangeStmt())))))
  15. .bind(Label),
  16. this);
  17. }
  18. // Helper function for printing TypeLocClass. Useful for debugging.
  19. LLVM_ATTRIBUTE_UNUSED
  20. static auto TypeLocClassToString(clang::TypeLoc::TypeLocClass c)
  21. -> std::string {
  22. switch (c) {
  23. // Mirrors the definition in clang/AST/TypeLoc.h in order to print names.
  24. #define ABSTRACT_TYPE(Class, Base)
  25. #define TYPE(Class, Base) \
  26. case clang::TypeLoc::Class: \
  27. return #Class;
  28. #include "clang/AST/TypeNodes.inc"
  29. case clang::TypeLoc::Qualified:
  30. return "Qualified";
  31. }
  32. }
  33. // Returns a string for the type.
  34. static auto GetTypeStr(const clang::VarDecl* decl,
  35. const clang::SourceManager& sm,
  36. const clang::LangOptions& lang_opts) -> std::string {
  37. // Built a vector of class information, because we'll be traversing reverse
  38. // order to construct the final type.
  39. auto type_loc = decl->getTypeSourceInfo()->getTypeLoc();
  40. std::vector<std::pair<clang::TypeLoc::TypeLocClass, std::string>> segments;
  41. while (!type_loc.isNull()) {
  42. std::string text;
  43. auto qualifiers = type_loc.getType().getLocalQualifiers();
  44. std::string qual_str;
  45. if (!qualifiers.empty()) {
  46. qual_str = qualifiers.getAsString();
  47. }
  48. auto range =
  49. clang::CharSourceRange::getTokenRange(type_loc.getLocalSourceRange());
  50. std::string range_str =
  51. clang::Lexer::getSourceText(range, sm, lang_opts).str();
  52. // Make a list of segments with their TypeLocClass for reconstruction of the
  53. // string. Locally, we will have a qualifier (such as `const`) and a type
  54. // string (such as `int`) which is also used.
  55. auto type_loc_class = type_loc.getTypeLocClass();
  56. if (qual_str.empty()) {
  57. segments.push_back({type_loc_class, range_str});
  58. } else if (range_str.empty()) {
  59. segments.push_back({type_loc_class, qual_str});
  60. } else {
  61. segments.push_back({type_loc_class, qual_str + " " + range_str});
  62. }
  63. type_loc = type_loc.getNextTypeLoc();
  64. }
  65. // Construct the final type based on the class of each step. This reverses to
  66. // start from the "inside" of the type and go "out" when constructing
  67. // type_str.
  68. std::string type_str;
  69. auto prev_class = clang::TypeLoc::Auto; // Placeholder class, used in loop.
  70. for (const auto& [type_loc_class, text] : llvm::reverse(segments)) {
  71. switch (type_loc_class) {
  72. case clang::TypeLoc::Elaborated:
  73. type_str.insert(0, text);
  74. break;
  75. case clang::TypeLoc::Qualified:
  76. if (prev_class == clang::TypeLoc::Pointer) {
  77. type_str += " " + text;
  78. } else {
  79. if (!type_str.empty()) {
  80. type_str.insert(0, " ");
  81. }
  82. type_str.insert(0, text);
  83. }
  84. break;
  85. default:
  86. type_str += text;
  87. break;
  88. }
  89. prev_class = type_loc_class;
  90. }
  91. return type_str;
  92. }
  93. void VarDecl::run(const cam::MatchFinder::MatchResult& result) {
  94. const auto* decl = result.Nodes.getNodeAs<clang::VarDecl>(Label);
  95. if (!decl) {
  96. llvm::report_fatal_error(std::string("getNodeAs failed for ") + Label);
  97. }
  98. if (decl->getTypeSourceInfo() == nullptr) {
  99. // TODO: Need to understand what's happening in this case. Not sure if we
  100. // need to address it.
  101. return;
  102. }
  103. auto& sm = *(result.SourceManager);
  104. auto lang_opts = result.Context->getLangOpts();
  105. std::string after;
  106. if (decl->getType().isConstQualified()) {
  107. after = "let ";
  108. } else if (result.Nodes.getNodeAs<clang::ParmVarDecl>(Label) == nullptr) {
  109. // Start the replacement with "var" unless it's a parameter.
  110. after = "var ";
  111. }
  112. // Add "identifier: type" to the replacement.
  113. after += decl->getNameAsString() + ": " + GetTypeStr(decl, sm, lang_opts);
  114. // This decides the range to replace. Normally the entire decl is replaced,
  115. // but for code like `int i, j` we need to detect the comma between the
  116. // declared names. That case currently results in `var i: int, var j: int`.
  117. // If there's a comma, this range will be non-empty.
  118. auto type_loc = decl->getTypeSourceInfo()->getTypeLoc();
  119. auto after_type_loc =
  120. clang::Lexer::getLocForEndOfToken(type_loc.getEndLoc(), 0, sm, lang_opts);
  121. auto comma_source_text = clang::Lexer::getSourceText(
  122. clang::CharSourceRange::getCharRange(after_type_loc, decl->getLocation()),
  123. sm, lang_opts);
  124. bool has_comma = !comma_source_text.trim().empty();
  125. clang::CharSourceRange replace_range = clang::CharSourceRange::getTokenRange(
  126. has_comma ? decl->getLocation() : decl->getBeginLoc(), decl->getEndLoc());
  127. AddReplacement(sm, replace_range, after);
  128. }
  129. } // namespace Carbon