StringConcatenationUseCmstrcatCheck.cxx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "StringConcatenationUseCmstrcatCheck.h"
  4. #include <cassert>
  5. #include <clang/ASTMatchers/ASTMatchFinder.h>
  6. #include <clang/Lex/Lexer.h>
  7. namespace clang {
  8. namespace tidy {
  9. namespace cmake {
  10. using namespace ast_matchers;
  11. StringConcatenationUseCmstrcatCheck::StringConcatenationUseCmstrcatCheck(
  12. StringRef Name, ClangTidyContext* Context)
  13. : ClangTidyCheck(Name, Context)
  14. {
  15. }
  16. void StringConcatenationUseCmstrcatCheck::registerMatchers(MatchFinder* Finder)
  17. {
  18. auto IsString = expr(hasType(qualType(hasUnqualifiedDesugaredType(
  19. recordType(hasDeclaration(classTemplateSpecializationDecl(
  20. hasName("::std::basic_string"),
  21. hasTemplateArgument(
  22. 0, templateArgument(refersToType(asString("char")))))))))));
  23. auto IsChar = expr(hasType(asString("char")));
  24. auto IsCharPtr = expr(hasType(pointerType(pointee(asString("const char")))));
  25. auto IsStringConcat =
  26. cxxOperatorCallExpr(hasOperatorName("+"),
  27. anyOf(allOf(hasLHS(IsString), hasRHS(IsString)),
  28. allOf(hasLHS(IsString), hasRHS(IsChar)),
  29. allOf(hasLHS(IsString), hasRHS(IsCharPtr)),
  30. allOf(hasLHS(IsChar), hasRHS(IsString)),
  31. allOf(hasLHS(IsCharPtr), hasRHS(IsString))));
  32. auto IsStringAppend = cxxOperatorCallExpr(
  33. hasOperatorName("+="), hasLHS(IsString),
  34. anyOf(hasRHS(IsString), hasRHS(IsChar), hasRHS(IsCharPtr)));
  35. auto IsStringConcatWithLHS =
  36. cxxOperatorCallExpr(
  37. IsStringConcat,
  38. optionally(hasLHS(materializeTemporaryExpr(
  39. has(cxxBindTemporaryExpr(has(IsStringConcat.bind("lhs"))))))))
  40. .bind("concat");
  41. auto IsStringAppendWithRHS =
  42. cxxOperatorCallExpr(
  43. IsStringAppend,
  44. optionally(hasRHS(materializeTemporaryExpr(has(implicitCastExpr(
  45. has(cxxBindTemporaryExpr(has(IsStringConcat.bind("rhs"))))))))))
  46. .bind("append");
  47. Finder->addMatcher(IsStringConcatWithLHS, this);
  48. Finder->addMatcher(IsStringAppendWithRHS, this);
  49. }
  50. void StringConcatenationUseCmstrcatCheck::check(
  51. const MatchFinder::MatchResult& Result)
  52. {
  53. const CXXOperatorCallExpr* AppendNode =
  54. Result.Nodes.getNodeAs<CXXOperatorCallExpr>("append");
  55. const CXXOperatorCallExpr* ConcatNode =
  56. Result.Nodes.getNodeAs<CXXOperatorCallExpr>("concat");
  57. if (AppendNode != nullptr) {
  58. if (AppendNode->getBeginLoc().isValid()) {
  59. assert(InProgressExprChains.find(AppendNode) ==
  60. InProgressExprChains.end());
  61. ExprChain TmpExprChain =
  62. std::make_pair(OperatorType::PlusEquals,
  63. std::vector<const CXXOperatorCallExpr*>{ AppendNode });
  64. const CXXOperatorCallExpr* RHSNode =
  65. Result.Nodes.getNodeAs<CXXOperatorCallExpr>("rhs");
  66. if (RHSNode != nullptr) {
  67. if (RHSNode->getBeginLoc().isValid()) {
  68. InProgressExprChains[RHSNode] = std::move(TmpExprChain);
  69. }
  70. } else {
  71. issueCorrection(TmpExprChain, Result);
  72. }
  73. }
  74. }
  75. if (ConcatNode != nullptr) {
  76. if (ConcatNode->getBeginLoc().isValid()) {
  77. ExprChain TmpExprChain;
  78. if (!(InProgressExprChains.find(ConcatNode) ==
  79. InProgressExprChains.end())) {
  80. TmpExprChain = std::move(InProgressExprChains[ConcatNode]);
  81. InProgressExprChains.erase(ConcatNode);
  82. if (TmpExprChain.first == OperatorType::PlusEquals) {
  83. TmpExprChain.second.insert(TmpExprChain.second.begin() + 1,
  84. ConcatNode);
  85. } else {
  86. TmpExprChain.second.insert(TmpExprChain.second.begin(), ConcatNode);
  87. }
  88. } else {
  89. TmpExprChain = std::make_pair(
  90. OperatorType::Plus,
  91. std::vector<const CXXOperatorCallExpr*>{ ConcatNode });
  92. }
  93. const CXXOperatorCallExpr* LHSNode =
  94. Result.Nodes.getNodeAs<CXXOperatorCallExpr>("lhs");
  95. if (LHSNode != nullptr) {
  96. if (LHSNode->getBeginLoc().isValid()) {
  97. InProgressExprChains[LHSNode] = std::move(TmpExprChain);
  98. }
  99. } else {
  100. issueCorrection(TmpExprChain, Result);
  101. }
  102. }
  103. }
  104. }
  105. void StringConcatenationUseCmstrcatCheck::issueCorrection(
  106. const ExprChain& Chain, const MatchFinder::MatchResult& Result)
  107. {
  108. std::vector<FixItHint> FixIts;
  109. const CXXOperatorCallExpr* ExprNode;
  110. std::vector<const clang::CXXOperatorCallExpr*>::const_iterator It =
  111. Chain.second.begin();
  112. if (Chain.first == OperatorType::PlusEquals) {
  113. ExprNode = *It;
  114. StringRef LHS = Lexer::getSourceText(
  115. CharSourceRange::getTokenRange(ExprNode->getArg(0)->getSourceRange()),
  116. Result.Context->getSourceManager(), Result.Context->getLangOpts());
  117. FixIts.push_back(FixItHint::CreateReplacement(
  118. ExprNode->getExprLoc(), "= cmStrCat(" + LHS.str() + ","));
  119. It++;
  120. } else {
  121. ExprNode = *It;
  122. FixIts.push_back(
  123. FixItHint::CreateInsertion(ExprNode->getBeginLoc(), "cmStrCat("));
  124. }
  125. while (It != std::end(Chain.second)) {
  126. ExprNode = *It;
  127. FixIts.push_back(
  128. FixItHint::CreateReplacement(ExprNode->getOperatorLoc(), ","));
  129. It++;
  130. }
  131. It--;
  132. ExprNode = *It;
  133. StringRef LastToken = Lexer::getSourceText(
  134. CharSourceRange::getTokenRange(ExprNode->getArg(1)->getSourceRange()),
  135. Result.Context->getSourceManager(), Result.Context->getLangOpts());
  136. FixIts.push_back(FixItHint::CreateInsertion(
  137. ExprNode->getEndLoc().getLocWithOffset(LastToken.str().size()), ")"));
  138. It = Chain.second.begin();
  139. ExprNode = *It;
  140. if (Chain.first == OperatorType::PlusEquals) {
  141. this->diag(ExprNode->getOperatorLoc(),
  142. "use cmStrCat() instead of string append")
  143. << FixIts;
  144. } else {
  145. this->diag(ExprNode->getBeginLoc(),
  146. "use cmStrCat() instead of string concatenation")
  147. << FixIts;
  148. }
  149. }
  150. }
  151. }
  152. }