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