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