//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" using namespace clang; namespace { /// Returns true if the token at the given location is a semicolon. bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, const LangOptions &LangOpts) { return Lexer::getSourceText( CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, LangOpts) == ";"; } /// Returns true if there should be a semicolon after the given statement. bool isSemicolonRequiredAfter(const Stmt *S) { if (isa(S)) return false; if (const auto *If = dyn_cast(S)) return isSemicolonRequiredAfter(If->getElse() ? If->getElse() : If->getThen()); if (const auto *While = dyn_cast(S)) return isSemicolonRequiredAfter(While->getBody()); if (const auto *For = dyn_cast(S)) return isSemicolonRequiredAfter(For->getBody()); if (const auto *CXXFor = dyn_cast(S)) return isSemicolonRequiredAfter(CXXFor->getBody()); if (const auto *ObjCFor = dyn_cast(S)) return isSemicolonRequiredAfter(ObjCFor->getBody()); if(const auto *Switch = dyn_cast(S)) return isSemicolonRequiredAfter(Switch->getBody()); if(const auto *Case = dyn_cast(S)) return isSemicolonRequiredAfter(Case->getSubStmt()); switch (S->getStmtClass()) { case Stmt::DeclStmtClass: case Stmt::CXXTryStmtClass: case Stmt::ObjCAtSynchronizedStmtClass: case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCAtTryStmtClass: return false; default: return true; } } /// Returns true if the two source locations are on the same line. bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, const SourceManager &SM) { return !Loc1.isMacroID() && !Loc2.isMacroID() && SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); } } // end anonymous namespace namespace clang { namespace tooling { ExtractionSemicolonPolicy ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange, const SourceManager &SM, const LangOptions &LangOpts) { auto neededInExtractedFunction = []() { return ExtractionSemicolonPolicy(true, false); }; auto neededInOriginalFunction = []() { return ExtractionSemicolonPolicy(false, true); }; /// The extracted expression should be terminated with a ';'. The call to /// the extracted function will replace this expression, so it won't need /// a terminating ';'. if (isa(S)) return neededInExtractedFunction(); /// Some statements don't need to be terminated with ';'. The call to the /// extracted function will be a standalone statement, so it should be /// terminated with a ';'. bool NeedsSemi = isSemicolonRequiredAfter(S); if (!NeedsSemi) return neededInOriginalFunction(); /// Some statements might end at ';'. The extraction will move that ';', so /// the call to the extracted function should be terminated with a ';'. SourceLocation End = ExtractedRange.getEnd(); if (isSemicolonAtLocation(End, SM, LangOpts)) return neededInOriginalFunction(); /// Other statements should generally have a trailing ';'. We can try to find /// it and move it together it with the extracted code. Optional NextToken = Lexer::findNextToken(End, SM, LangOpts); if (NextToken && NextToken->is(tok::semi) && areOnSameLine(NextToken->getLocation(), End, SM)) { ExtractedRange.setEnd(NextToken->getLocation()); return neededInOriginalFunction(); } /// Otherwise insert semicolons in both places. return ExtractionSemicolonPolicy(true, true); } } // end namespace tooling } // end namespace clang