SourceExtraction.cpp 4.0 KB

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