================ @@ -0,0 +1,262 @@ +//===--- InlineConceptRequirement.cpp ----------------------------*- C++-*-===// +// +// 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 "ParsedAST.h" +#include "SourceCode.h" +#include "refactor/Tweak.h" +#include "support/Logger.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExprConcepts.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { +namespace { +/// Inlines a concept requirement. +/// +/// Before: +/// template <typename T> void f(T) requires foo<T> {} +/// ^^^^^^ +/// After: +/// template <foo T> void f(T) {} +class InlineConceptRequirement : public Tweak { +public: + const char *id() const final; + + auto prepare(const Selection &Inputs) -> bool override; + auto apply(const Selection &Inputs) -> Expected<Effect> override; + auto title() const -> std::string override { + return "Inline concept requirement"; + } + auto kind() const -> llvm::StringLiteral override { + return CodeAction::REFACTOR_KIND; + } + +private: + const ConceptSpecializationExpr *ConceptSpecializationExpression; + const TemplateTypeParmDecl *TemplateTypeParameterDeclaration; + const syntax::Token *RequiresToken; + + static auto getTemplateParameterIndexOfTemplateArgument( + const TemplateArgument &TemplateArgument) -> std::optional<int>; + auto generateRequiresReplacement(ASTContext &) + -> std::variant<tooling::Replacement, llvm::Error>; + auto generateRequiresTokenReplacement(const syntax::TokenBuffer &) + -> tooling::Replacement; + auto generateTemplateParameterReplacement(ASTContext &Context) + -> tooling::Replacement; + + static auto findToken(const ParsedAST *, const SourceRange &, + const tok::TokenKind) -> const syntax::Token *; + + template <typename T, typename NodeKind> + static auto findNode(const SelectionTree::Node &Root) + -> std::tuple<const T *, const SelectionTree::Node *>; + + template <typename T> + static auto findExpression(const SelectionTree::Node &Root) + -> std::tuple<const T *, const SelectionTree::Node *> { + return findNode<T, Expr>(Root); + } + + template <typename T> + static auto findDeclaration(const SelectionTree::Node &Root) + -> std::tuple<const T *, const SelectionTree::Node *> { + return findNode<T, Decl>(Root); + } +}; + +REGISTER_TWEAK(InlineConceptRequirement) + +auto InlineConceptRequirement::prepare(const Selection &Inputs) -> bool { + // Check if C++ version is 20 or higher + if (!Inputs.AST->getLangOpts().CPlusPlus20) + return false; + + const auto *Root = Inputs.ASTSelection.commonAncestor(); + if (!Root) + return false; + + const SelectionTree::Node *ConceptSpecializationExpressionTreeNode; + std::tie(ConceptSpecializationExpression, + ConceptSpecializationExpressionTreeNode) = + findExpression<ConceptSpecializationExpr>(*Root); + if (!ConceptSpecializationExpression) + return false; + + // Only allow concepts that are direct children of function template + // declarations or function declarations. This excludes conjunctions of + // concepts which are not handled. + const auto *ParentDeclaration = + ConceptSpecializationExpressionTreeNode->Parent->ASTNode.get<Decl>(); + if (!isa_and_nonnull<FunctionTemplateDecl>(ParentDeclaration) && + !isa_and_nonnull<FunctionDecl>(ParentDeclaration)) + return false; + + const FunctionTemplateDecl *FunctionTemplateDeclaration = + std::get<0>(findDeclaration<FunctionTemplateDecl>(*Root)); + if (!FunctionTemplateDeclaration) + return false; + + auto TemplateArguments = + ConceptSpecializationExpression->getTemplateArguments(); + if (TemplateArguments.size() != 1) + return false; + + auto TemplateParameterIndex = + getTemplateParameterIndexOfTemplateArgument(TemplateArguments[0]); + if (!TemplateParameterIndex) + return false; + + TemplateTypeParameterDeclaration = dyn_cast_or_null<TemplateTypeParmDecl>( + FunctionTemplateDeclaration->getTemplateParameters()->getParam( + *TemplateParameterIndex)); + if (!TemplateTypeParameterDeclaration->wasDeclaredWithTypename()) + return false; + + RequiresToken = + findToken(Inputs.AST, FunctionTemplateDeclaration->getSourceRange(), + tok::kw_requires); + if (!RequiresToken) + return false; + + return true; +} + +auto InlineConceptRequirement::apply(const Selection &Inputs) + -> Expected<Tweak::Effect> { + auto &Context = Inputs.AST->getASTContext(); + auto &TokenBuffer = Inputs.AST->getTokens(); + + tooling::Replacements Replacements{}; + + if (auto Err = + Replacements.add(generateTemplateParameterReplacement(Context))) + return Err; + + auto RequiresReplacement = generateRequiresReplacement(Context); + + if (std::holds_alternative<llvm::Error>(RequiresReplacement)) + return std::move(std::get<llvm::Error>(RequiresReplacement)); + + if (auto Err = + Replacements.add(std::get<tooling::Replacement>(RequiresReplacement))) + return Err; + + if (auto Err = + Replacements.add(generateRequiresTokenReplacement(TokenBuffer))) + return Err; + + return Effect::mainFileEdit(Context.getSourceManager(), Replacements); +} + +auto InlineConceptRequirement::getTemplateParameterIndexOfTemplateArgument( + const TemplateArgument &TemplateArgument) -> std::optional<int> { + if (TemplateArgument.getKind() != TemplateArgument.Type) + return {}; + + auto TemplateArgumentType = TemplateArgument.getAsType(); + if (!TemplateArgumentType->isTemplateTypeParmType()) + return {}; + + const auto *TemplateTypeParameterType = + TemplateArgumentType->getAs<TemplateTypeParmType>(); + if (!TemplateTypeParameterType) + return {}; + + return TemplateTypeParameterType->getIndex(); +} + +auto InlineConceptRequirement::generateRequiresReplacement(ASTContext &Context) + -> std::variant<tooling::Replacement, llvm::Error> { ---------------- 5chmidti wrote:
LLVM uses `llvm::Expected<tooling::Replacement>` to do this kind of error handling (https://llvm.org/doxygen/classllvm_1_1Expected.html). https://github.com/llvm/llvm-project/pull/69693 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits