SourceExtraction.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
  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 "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
  9. #include "clang/AST/Stmt.h"
  10. #include "clang/AST/StmtCXX.h"
  11. #include "clang/AST/StmtObjC.h"
  12. #include "clang/Basic/SourceManager.h"
  13. #include "clang/Lex/Lexer.h"
  14. using namespace clang;
  15. namespace {
  16. /// Returns true if the token at the given location is a semicolon.
  17. bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,
  18. const LangOptions &LangOpts) {
  19. return Lexer::getSourceText(
  20. CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,
  21. LangOpts) == ";";
  22. }
  23. /// Returns true if there should be a semicolon after the given statement.
  24. bool isSemicolonRequiredAfter(const Stmt *S) {
  25. if (isa<CompoundStmt>(S))
  26. return false;
  27. if (const auto *If = dyn_cast<IfStmt>(S))
  28. return isSemicolonRequiredAfter(If->getElse() ? If->getElse()
  29. : If->getThen());
  30. if (const auto *While = dyn_cast<WhileStmt>(S))
  31. return isSemicolonRequiredAfter(While->getBody());
  32. if (const auto *For = dyn_cast<ForStmt>(S))
  33. return isSemicolonRequiredAfter(For->getBody());
  34. if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S))
  35. return isSemicolonRequiredAfter(CXXFor->getBody());
  36. if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S))
  37. return isSemicolonRequiredAfter(ObjCFor->getBody());
  38. if(const auto *Switch = dyn_cast<SwitchStmt>(S))
  39. return isSemicolonRequiredAfter(Switch->getBody());
  40. if(const auto *Case = dyn_cast<SwitchCase>(S))
  41. return isSemicolonRequiredAfter(Case->getSubStmt());
  42. switch (S->getStmtClass()) {
  43. case Stmt::DeclStmtClass:
  44. case Stmt::CXXTryStmtClass:
  45. case Stmt::ObjCAtSynchronizedStmtClass:
  46. case Stmt::ObjCAutoreleasePoolStmtClass:
  47. case Stmt::ObjCAtTryStmtClass:
  48. return false;
  49. default:
  50. return true;
  51. }
  52. }
  53. /// Returns true if the two source locations are on the same line.
  54. bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
  55. const SourceManager &SM) {
  56. return !Loc1.isMacroID() && !Loc2.isMacroID() &&
  57. SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);
  58. }
  59. } // end anonymous namespace
  60. namespace clang {
  61. namespace tooling {
  62. ExtractionSemicolonPolicy
  63. ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange,
  64. const SourceManager &SM,
  65. const LangOptions &LangOpts) {
  66. auto neededInExtractedFunction = []() {
  67. return ExtractionSemicolonPolicy(true, false);
  68. };
  69. auto neededInOriginalFunction = []() {
  70. return ExtractionSemicolonPolicy(false, true);
  71. };
  72. /// The extracted expression should be terminated with a ';'. The call to
  73. /// the extracted function will replace this expression, so it won't need
  74. /// a terminating ';'.
  75. if (isa<Expr>(S))
  76. return neededInExtractedFunction();
  77. /// Some statements don't need to be terminated with ';'. The call to the
  78. /// extracted function will be a standalone statement, so it should be
  79. /// terminated with a ';'.
  80. bool NeedsSemi = isSemicolonRequiredAfter(S);
  81. if (!NeedsSemi)
  82. return neededInOriginalFunction();
  83. /// Some statements might end at ';'. The extraction will move that ';', so
  84. /// the call to the extracted function should be terminated with a ';'.
  85. SourceLocation End = ExtractedRange.getEnd();
  86. if (isSemicolonAtLocation(End, SM, LangOpts))
  87. return neededInOriginalFunction();
  88. /// Other statements should generally have a trailing ';'. We can try to find
  89. /// it and move it together it with the extracted code.
  90. Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts);
  91. if (NextToken && NextToken->is(tok::semi) &&
  92. areOnSameLine(NextToken->getLocation(), End, SM)) {
  93. ExtractedRange.setEnd(NextToken->getLocation());
  94. return neededInOriginalFunction();
  95. }
  96. /// Otherwise insert semicolons in both places.
  97. return ExtractionSemicolonPolicy(true, true);
  98. }
  99. } // end namespace tooling
  100. } // end namespace clang