MultipleStatementMacroCheck.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. //===--- MultipleStatementMacroCheck.cpp - clang-tidy----------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "MultipleStatementMacroCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. using namespace clang::ast_matchers;
  12. namespace clang::tidy::bugprone {
  13. namespace {
  14. AST_MATCHER(Expr, isInMacro) { return Node.getBeginLoc().isMacroID(); }
  15. /// Find the next statement after `S`.
  16. const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
  17. auto Parents = Result.Context->getParents(*S);
  18. if (Parents.empty())
  19. return nullptr;
  20. const auto *Parent = Parents[0].get<Stmt>();
  21. if (!Parent)
  22. return nullptr;
  23. const Stmt *Prev = nullptr;
  24. for (const Stmt *Child : Parent->children()) {
  25. if (Prev == S)
  26. return Child;
  27. Prev = Child;
  28. }
  29. return nextStmt(Result, Parent);
  30. }
  31. using ExpansionRanges = std::vector<SourceRange>;
  32. /// \brief Get all the macro expansion ranges related to `Loc`.
  33. ///
  34. /// The result is ordered from most inner to most outer.
  35. ExpansionRanges getExpansionRanges(SourceLocation Loc,
  36. const MatchFinder::MatchResult &Result) {
  37. ExpansionRanges Locs;
  38. while (Loc.isMacroID()) {
  39. Locs.push_back(
  40. Result.SourceManager->getImmediateExpansionRange(Loc).getAsRange());
  41. Loc = Locs.back().getBegin();
  42. }
  43. return Locs;
  44. }
  45. } // namespace
  46. void MultipleStatementMacroCheck::registerMatchers(MatchFinder *Finder) {
  47. const auto Inner = expr(isInMacro(), unless(compoundStmt())).bind("inner");
  48. Finder->addMatcher(
  49. stmt(anyOf(ifStmt(hasThen(Inner)), ifStmt(hasElse(Inner)).bind("else"),
  50. whileStmt(hasBody(Inner)), forStmt(hasBody(Inner))))
  51. .bind("outer"),
  52. this);
  53. }
  54. void MultipleStatementMacroCheck::check(
  55. const MatchFinder::MatchResult &Result) {
  56. const auto *Inner = Result.Nodes.getNodeAs<Expr>("inner");
  57. const auto *Outer = Result.Nodes.getNodeAs<Stmt>("outer");
  58. const auto *Next = nextStmt(Result, Outer);
  59. if (!Next)
  60. return;
  61. SourceLocation OuterLoc = Outer->getBeginLoc();
  62. if (Result.Nodes.getNodeAs<Stmt>("else"))
  63. OuterLoc = cast<IfStmt>(Outer)->getElseLoc();
  64. auto InnerRanges = getExpansionRanges(Inner->getBeginLoc(), Result);
  65. auto OuterRanges = getExpansionRanges(OuterLoc, Result);
  66. auto NextRanges = getExpansionRanges(Next->getBeginLoc(), Result);
  67. // Remove all the common ranges, starting from the top (the last ones in the
  68. // list).
  69. while (!InnerRanges.empty() && !OuterRanges.empty() && !NextRanges.empty() &&
  70. InnerRanges.back() == OuterRanges.back() &&
  71. InnerRanges.back() == NextRanges.back()) {
  72. InnerRanges.pop_back();
  73. OuterRanges.pop_back();
  74. NextRanges.pop_back();
  75. }
  76. // Inner and Next must have at least one more macro that Outer doesn't have,
  77. // and that range must be common to both.
  78. if (InnerRanges.empty() || NextRanges.empty() ||
  79. InnerRanges.back() != NextRanges.back())
  80. return;
  81. diag(InnerRanges.back().getBegin(), "multiple statement macro used without "
  82. "braces; some statements will be "
  83. "unconditionally executed");
  84. }
  85. } // namespace clang::tidy::bugprone