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 "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 switch (S->getStmtClass()) { 44 case Stmt::SwitchStmtClass: 45 case Stmt::CXXTryStmtClass: 46 case Stmt::ObjCAtSynchronizedStmtClass: 47 case Stmt::ObjCAutoreleasePoolStmtClass: 48 case Stmt::ObjCAtTryStmtClass: 49 return false; 50 default: 51 return true; 52 } 53 } 54 55 /// Returns true if the two source locations are on the same line. 56 bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, 57 const SourceManager &SM) { 58 return !Loc1.isMacroID() && !Loc2.isMacroID() && 59 SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); 60 } 61 62 } // end anonymous namespace 63 64 namespace clang { 65 namespace tooling { 66 67 ExtractionSemicolonPolicy 68 ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange, 69 const SourceManager &SM, 70 const LangOptions &LangOpts) { 71 auto neededInExtractedFunction = []() { 72 return ExtractionSemicolonPolicy(true, false); 73 }; 74 auto neededInOriginalFunction = []() { 75 return ExtractionSemicolonPolicy(false, true); 76 }; 77 78 /// The extracted expression should be terminated with a ';'. The call to 79 /// the extracted function will replace this expression, so it won't need 80 /// a terminating ';'. 81 if (isa<Expr>(S)) 82 return neededInExtractedFunction(); 83 84 /// Some statements don't need to be terminated with ';'. The call to the 85 /// extracted function will be a standalone statement, so it should be 86 /// terminated with a ';'. 87 bool NeedsSemi = isSemicolonRequiredAfter(S); 88 if (!NeedsSemi) 89 return neededInOriginalFunction(); 90 91 /// Some statements might end at ';'. The extraction will move that ';', so 92 /// the call to the extracted function should be terminated with a ';'. 93 SourceLocation End = ExtractedRange.getEnd(); 94 if (isSemicolonAtLocation(End, SM, LangOpts)) 95 return neededInOriginalFunction(); 96 97 /// Other statements should generally have a trailing ';'. We can try to find 98 /// it and move it together it with the extracted code. 99 Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts); 100 if (NextToken && NextToken->is(tok::semi) && 101 areOnSameLine(NextToken->getLocation(), End, SM)) { 102 ExtractedRange.setEnd(NextToken->getLocation()); 103 return neededInOriginalFunction(); 104 } 105 106 /// Otherwise insert semicolons in both places. 107 return ExtractionSemicolonPolicy(true, true); 108 } 109 110 } // end namespace tooling 111 } // end namespace clang 112