| //===--- ExpandAutoType.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 "refactor/Tweak.h" |
| |
| #include "XRefs.h" |
| #include "support/Logger.h" |
| #include "clang/AST/Type.h" |
| #include "clang/AST/TypeLoc.h" |
| #include "clang/Basic/LLVM.h" |
| #include "llvm/ADT/None.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Error.h" |
| #include <AST.h> |
| #include <climits> |
| #include <memory> |
| #include <string> |
| |
| namespace clang { |
| namespace clangd { |
| namespace { |
| |
| /// Expand the "auto" type to the derived type |
| /// Before: |
| /// auto x = Something(); |
| /// ^^^^ |
| /// After: |
| /// MyClass x = Something(); |
| /// ^^^^^^^ |
| /// FIXME: Handle decltype as well |
| class ExpandAutoType : public Tweak { |
| public: |
| const char *id() const final; |
| llvm::StringLiteral kind() const override { |
| return CodeAction::REFACTOR_KIND; |
| } |
| bool prepare(const Selection &Inputs) override; |
| Expected<Effect> apply(const Selection &Inputs) override; |
| std::string title() const override; |
| |
| private: |
| /// Cache the AutoTypeLoc, so that we do not need to search twice. |
| llvm::Optional<clang::AutoTypeLoc> CachedLocation; |
| }; |
| |
| REGISTER_TWEAK(ExpandAutoType) |
| |
| std::string ExpandAutoType::title() const { return "Expand auto type"; } |
| |
| // Structured bindings must use auto, e.g. `const auto& [a,b,c] = ...;`. |
| // Return whether N (an AutoTypeLoc) is such an auto that must not be expanded. |
| bool isStructuredBindingType(const SelectionTree::Node *N) { |
| // Walk up the TypeLoc chain, because auto may be qualified. |
| while (N && N->ASTNode.get<TypeLoc>()) |
| N = N->Parent; |
| // The relevant type is the only direct type child of a Decomposition. |
| return N && N->ASTNode.get<DecompositionDecl>(); |
| } |
| |
| // Returns true iff Node is a lambda, and thus should not be expanded. Loc is |
| // the location of the auto type. |
| bool isDeducedAsLambda(const SelectionTree::Node *Node, SourceLocation Loc) { |
| // getDeducedType() does a traversal, which we want to avoid in prepare(). |
| // But at least check this isn't auto x = []{...};, which can't ever be |
| // expanded. |
| // (It would be nice if we had an efficient getDeducedType(), instead). |
| for (const auto *It = Node; It; It = It->Parent) { |
| if (const auto *DD = It->ASTNode.get<DeclaratorDecl>()) { |
| if (DD->getTypeSourceInfo() && |
| DD->getTypeSourceInfo()->getTypeLoc().getBeginLoc() == Loc) { |
| if (auto *RD = DD->getType()->getAsRecordDecl()) |
| return RD->isLambda(); |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Returns true iff "auto" in Node is really part of the template parameter, |
| // which we cannot expand. |
| bool isTemplateParam(const SelectionTree::Node *Node) { |
| if (Node->Parent) |
| if (Node->Parent->ASTNode.get<NonTypeTemplateParmDecl>()) |
| return true; |
| return false; |
| } |
| |
| bool ExpandAutoType::prepare(const Selection& Inputs) { |
| CachedLocation = llvm::None; |
| if (auto *Node = Inputs.ASTSelection.commonAncestor()) { |
| if (auto *TypeNode = Node->ASTNode.get<TypeLoc>()) { |
| if (const AutoTypeLoc Result = TypeNode->getAs<AutoTypeLoc>()) { |
| // Code in apply() does handle 'decltype(auto)' yet. |
| if (!Result.getTypePtr()->isDecltypeAuto() && |
| !isStructuredBindingType(Node) && |
| !isDeducedAsLambda(Node, Result.getBeginLoc()) && |
| !isTemplateParam(Node)) |
| CachedLocation = Result; |
| } |
| } |
| } |
| |
| return (bool) CachedLocation; |
| } |
| |
| Expected<Tweak::Effect> ExpandAutoType::apply(const Selection& Inputs) { |
| auto &SrcMgr = Inputs.AST->getSourceManager(); |
| |
| llvm::Optional<clang::QualType> DeducedType = getDeducedType( |
| Inputs.AST->getASTContext(), CachedLocation->getBeginLoc()); |
| |
| // if we can't resolve the type, return an error message |
| if (DeducedType == llvm::None || (*DeducedType)->isUndeducedAutoType()) |
| return error("Could not deduce type for 'auto' type"); |
| |
| // if it's a lambda expression, return an error message |
| if (isa<RecordType>(*DeducedType) && |
| cast<RecordType>(*DeducedType)->getDecl()->isLambda()) { |
| return error("Could not expand type of lambda expression"); |
| } |
| |
| // if it's a function expression, return an error message |
| // naively replacing 'auto' with the type will break declarations. |
| // FIXME: there are other types that have similar problems |
| if (DeducedType->getTypePtr()->isFunctionPointerType()) { |
| return error("Could not expand type of function pointer"); |
| } |
| |
| std::string PrettyTypeName = printType(*DeducedType, |
| Inputs.ASTSelection.commonAncestor()->getDeclContext()); |
| |
| tooling::Replacement |
| Expansion(SrcMgr, CharSourceRange(CachedLocation->getSourceRange(), true), |
| PrettyTypeName); |
| |
| return Effect::mainFileEdit(SrcMgr, tooling::Replacements(Expansion)); |
| } |
| |
| } // namespace |
| } // namespace clangd |
| } // namespace clang |