StringConcatenationUseCmstrcatCheck.cxx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst 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. MatchFinder::MatchResult const& Result)
  52. {
  53. CXXOperatorCallExpr const* AppendNode =
  54. Result.Nodes.getNodeAs<CXXOperatorCallExpr>("append");
  55. CXXOperatorCallExpr const* 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<CXXOperatorCallExpr const*>{ AppendNode });
  64. CXXOperatorCallExpr const* 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<CXXOperatorCallExpr const*>{ ConcatNode });
  92. }
  93. CXXOperatorCallExpr const* 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. ExprChain const& Chain, MatchFinder::MatchResult const& Result)
  107. {
  108. std::vector<FixItHint> FixIts;
  109. CXXOperatorCallExpr const* ExprNode;
  110. std::vector<clang::CXXOperatorCallExpr const*>::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(
  135. ExprNode->getArg(1)->getSourceRange().getEnd()),
  136. Result.Context->getSourceManager(), Result.Context->getLangOpts());
  137. FixIts.push_back(FixItHint::CreateInsertion(
  138. ExprNode->getEndLoc().getLocWithOffset(LastToken.str().size()), ")"));
  139. It = Chain.second.begin();
  140. ExprNode = *It;
  141. if (Chain.first == OperatorType::PlusEquals) {
  142. this->diag(ExprNode->getOperatorLoc(),
  143. "use cmStrCat() instead of string append")
  144. << FixIts;
  145. } else {
  146. this->diag(ExprNode->getBeginLoc(),
  147. "use cmStrCat() instead of string concatenation")
  148. << FixIts;
  149. }
  150. }
  151. }
  152. }
  153. }