diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/Tooling/Refactoring/Extract | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/Tooling/Refactoring/Extract')
-rw-r--r-- | contrib/libs/clang16/lib/Tooling/Refactoring/Extract/Extract.cpp | 199 | ||||
-rw-r--r-- | contrib/libs/clang16/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp | 116 |
2 files changed, 315 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/Extract.cpp b/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/Extract.cpp new file mode 100644 index 0000000000..d437f4c21f --- /dev/null +++ b/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/Extract.cpp @@ -0,0 +1,199 @@ +//===--- Extract.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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implements the "extract" refactoring that can pull code into +/// new functions, methods or declare new variables. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/Extract/Extract.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" +#include <optional> + +namespace clang { +namespace tooling { + +namespace { + +/// Returns true if \c E is a simple literal or a reference expression that +/// should not be extracted. +bool isSimpleExpression(const Expr *E) { + if (!E) + return false; + switch (E->IgnoreParenCasts()->getStmtClass()) { + case Stmt::DeclRefExprClass: + case Stmt::PredefinedExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::StringLiteralClass: + return true; + default: + return false; + } +} + +SourceLocation computeFunctionExtractionLocation(const Decl *D) { + if (isa<CXXMethodDecl>(D)) { + // Code from method that is defined in class body should be extracted to a + // function defined just before the class. + while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext())) + D = RD; + } + return D->getBeginLoc(); +} + +} // end anonymous namespace + +const RefactoringDescriptor &ExtractFunction::describe() { + static const RefactoringDescriptor Descriptor = { + "extract-function", + "Extract Function", + "(WIP action; use with caution!) Extracts code into a new function", + }; + return Descriptor; +} + +Expected<ExtractFunction> +ExtractFunction::initiate(RefactoringRuleContext &Context, + CodeRangeASTSelection Code, + std::optional<std::string> DeclName) { + // We would like to extract code out of functions/methods/blocks. + // Prohibit extraction from things like global variable / field + // initializers and other top-level expressions. + if (!Code.isInFunctionLikeBodyOfCode()) + return Context.createDiagnosticError( + diag::err_refactor_code_outside_of_function); + + if (Code.size() == 1) { + // Avoid extraction of simple literals and references. + if (isSimpleExpression(dyn_cast<Expr>(Code[0]))) + return Context.createDiagnosticError( + diag::err_refactor_extract_simple_expression); + + // Property setters can't be extracted. + if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) { + if (!PRE->isMessagingGetter()) + return Context.createDiagnosticError( + diag::err_refactor_extract_prohibited_expression); + } + } + + return ExtractFunction(std::move(Code), DeclName); +} + +// FIXME: Support C++ method extraction. +// FIXME: Support Objective-C method extraction. +Expected<AtomicChanges> +ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) { + const Decl *ParentDecl = Code.getFunctionLikeNearestParent(); + assert(ParentDecl && "missing parent"); + + // Compute the source range of the code that should be extracted. + SourceRange ExtractedRange(Code[0]->getBeginLoc(), + Code[Code.size() - 1]->getEndLoc()); + // FIXME (Alex L): Add code that accounts for macro locations. + + ASTContext &AST = Context.getASTContext(); + SourceManager &SM = AST.getSourceManager(); + const LangOptions &LangOpts = AST.getLangOpts(); + Rewriter ExtractedCodeRewriter(SM, LangOpts); + + // FIXME: Capture used variables. + + // Compute the return type. + QualType ReturnType = AST.VoidTy; + // FIXME (Alex L): Account for the return statement in extracted code. + // FIXME (Alex L): Check for lexical expression instead. + bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]); + if (IsExpr) { + // FIXME (Alex L): Get a more user-friendly type if needed. + ReturnType = cast<Expr>(Code[0])->getType(); + } + + // FIXME: Rewrite the extracted code performing any required adjustments. + + // FIXME: Capture any field if necessary (method -> function extraction). + + // FIXME: Sort captured variables by name. + + // FIXME: Capture 'this' / 'self' if necessary. + + // FIXME: Compute the actual parameter types. + + // Compute the location of the extracted declaration. + SourceLocation ExtractedDeclLocation = + computeFunctionExtractionLocation(ParentDecl); + // FIXME: Adjust the location to account for any preceding comments. + + // FIXME: Adjust with PP awareness like in Sema to get correct 'bool' + // treatment. + PrintingPolicy PP = AST.getPrintingPolicy(); + // FIXME: PP.UseStdFunctionForLambda = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute( + Code[Code.size() - 1], ExtractedRange, SM, LangOpts); + AtomicChange Change(SM, ExtractedDeclLocation); + // Create the replacement for the extracted declaration. + { + std::string ExtractedCode; + llvm::raw_string_ostream OS(ExtractedCode); + // FIXME: Use 'inline' in header. + OS << "static "; + ReturnType.print(OS, PP, DeclName); + OS << '('; + // FIXME: Arguments. + OS << ')'; + + // Function body. + OS << " {\n"; + if (IsExpr && !ReturnType->isVoidType()) + OS << "return "; + OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange); + if (Semicolons.isNeededInExtractedFunction()) + OS << ';'; + OS << "\n}\n\n"; + auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str()); + if (Err) + return std::move(Err); + } + + // Create the replacement for the call to the extracted declaration. + { + std::string ReplacedCode; + llvm::raw_string_ostream OS(ReplacedCode); + + OS << DeclName << '('; + // FIXME: Forward arguments. + OS << ')'; + if (Semicolons.isNeededInOriginalFunction()) + OS << ';'; + + auto Err = Change.replace( + SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str()); + if (Err) + return std::move(Err); + } + + // FIXME: Add support for assocciated symbol location to AtomicChange to mark + // the ranges of the name of the extracted declaration. + return AtomicChanges{std::move(Change)}; +} + +} // end namespace tooling +} // end namespace clang diff --git a/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp new file mode 100644 index 0000000000..5e69fb8051 --- /dev/null +++ b/contrib/libs/clang16/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp @@ -0,0 +1,116 @@ +//===--- 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" +#include <optional> + +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<CompoundStmt>(S)) + return false; + if (const auto *If = dyn_cast<IfStmt>(S)) + return isSemicolonRequiredAfter(If->getElse() ? If->getElse() + : If->getThen()); + if (const auto *While = dyn_cast<WhileStmt>(S)) + return isSemicolonRequiredAfter(While->getBody()); + if (const auto *For = dyn_cast<ForStmt>(S)) + return isSemicolonRequiredAfter(For->getBody()); + if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S)) + return isSemicolonRequiredAfter(CXXFor->getBody()); + if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S)) + return isSemicolonRequiredAfter(ObjCFor->getBody()); + if(const auto *Switch = dyn_cast<SwitchStmt>(S)) + return isSemicolonRequiredAfter(Switch->getBody()); + if(const auto *Case = dyn_cast<SwitchCase>(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<Expr>(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. + std::optional<Token> 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 |