Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
35295 views
//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"9#include "clang/AST/Stmt.h"10#include "clang/AST/StmtCXX.h"11#include "clang/AST/StmtObjC.h"12#include "clang/Basic/SourceManager.h"13#include "clang/Lex/Lexer.h"14#include <optional>1516using namespace clang;1718namespace {1920/// Returns true if the token at the given location is a semicolon.21bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,22const LangOptions &LangOpts) {23return Lexer::getSourceText(24CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,25LangOpts) == ";";26}2728/// Returns true if there should be a semicolon after the given statement.29bool isSemicolonRequiredAfter(const Stmt *S) {30if (isa<CompoundStmt>(S))31return false;32if (const auto *If = dyn_cast<IfStmt>(S))33return isSemicolonRequiredAfter(If->getElse() ? If->getElse()34: If->getThen());35if (const auto *While = dyn_cast<WhileStmt>(S))36return isSemicolonRequiredAfter(While->getBody());37if (const auto *For = dyn_cast<ForStmt>(S))38return isSemicolonRequiredAfter(For->getBody());39if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S))40return isSemicolonRequiredAfter(CXXFor->getBody());41if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S))42return isSemicolonRequiredAfter(ObjCFor->getBody());43if(const auto *Switch = dyn_cast<SwitchStmt>(S))44return isSemicolonRequiredAfter(Switch->getBody());45if(const auto *Case = dyn_cast<SwitchCase>(S))46return isSemicolonRequiredAfter(Case->getSubStmt());47switch (S->getStmtClass()) {48case Stmt::DeclStmtClass:49case Stmt::CXXTryStmtClass:50case Stmt::ObjCAtSynchronizedStmtClass:51case Stmt::ObjCAutoreleasePoolStmtClass:52case Stmt::ObjCAtTryStmtClass:53return false;54default:55return true;56}57}5859/// Returns true if the two source locations are on the same line.60bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,61const SourceManager &SM) {62return !Loc1.isMacroID() && !Loc2.isMacroID() &&63SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);64}6566} // end anonymous namespace6768namespace clang {69namespace tooling {7071ExtractionSemicolonPolicy72ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange,73const SourceManager &SM,74const LangOptions &LangOpts) {75auto neededInExtractedFunction = []() {76return ExtractionSemicolonPolicy(true, false);77};78auto neededInOriginalFunction = []() {79return ExtractionSemicolonPolicy(false, true);80};8182/// The extracted expression should be terminated with a ';'. The call to83/// the extracted function will replace this expression, so it won't need84/// a terminating ';'.85if (isa<Expr>(S))86return neededInExtractedFunction();8788/// Some statements don't need to be terminated with ';'. The call to the89/// extracted function will be a standalone statement, so it should be90/// terminated with a ';'.91bool NeedsSemi = isSemicolonRequiredAfter(S);92if (!NeedsSemi)93return neededInOriginalFunction();9495/// Some statements might end at ';'. The extraction will move that ';', so96/// the call to the extracted function should be terminated with a ';'.97SourceLocation End = ExtractedRange.getEnd();98if (isSemicolonAtLocation(End, SM, LangOpts))99return neededInOriginalFunction();100101/// Other statements should generally have a trailing ';'. We can try to find102/// it and move it together it with the extracted code.103std::optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts);104if (NextToken && NextToken->is(tok::semi) &&105areOnSameLine(NextToken->getLocation(), End, SM)) {106ExtractedRange.setEnd(NextToken->getLocation());107return neededInOriginalFunction();108}109110/// Otherwise insert semicolons in both places.111return ExtractionSemicolonPolicy(true, true);112}113114} // end namespace tooling115} // end namespace clang116117118