1 //===--- ExpandDeducedType.cpp -----------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "refactor/Tweak.h"
9
10 #include "support/Logger.h"
11 #include "clang/AST/Type.h"
12 #include "clang/AST/TypeLoc.h"
13 #include "clang/Basic/LLVM.h"
14 #include "llvm/Support/Error.h"
15 #include <AST.h>
16 #include <climits>
17 #include <memory>
18 #include <optional>
19 #include <string>
20
21 namespace clang {
22 namespace clangd {
23 namespace {
24
25 /// Expand the "auto" type to the derived type
26 /// Before:
27 /// auto x = Something();
28 /// ^^^^
29 /// After:
30 /// MyClass x = Something();
31 /// ^^^^^^^
32 /// Expand `decltype(expr)` to the deduced type
33 /// Before:
34 /// decltype(0) i;
35 /// ^^^^^^^^^^^
36 /// After:
37 /// int i;
38 /// ^^^
39 class ExpandDeducedType : public Tweak {
40 public:
41 const char *id() const final;
kind() const42 llvm::StringLiteral kind() const override {
43 return CodeAction::REFACTOR_KIND;
44 }
45 bool prepare(const Selection &Inputs) override;
46 Expected<Effect> apply(const Selection &Inputs) override;
47 std::string title() const override;
48
49 private:
50 SourceRange Range;
51 };
52
REGISTER_TWEAK(ExpandDeducedType)53 REGISTER_TWEAK(ExpandDeducedType)
54
55 std::string ExpandDeducedType::title() const {
56 return "Replace with deduced type";
57 }
58
59 // Structured bindings must use auto, e.g. `const auto& [a,b,c] = ...;`.
60 // Return whether N (an AutoTypeLoc) is such an auto that must not be expanded.
isStructuredBindingType(const SelectionTree::Node * N)61 bool isStructuredBindingType(const SelectionTree::Node *N) {
62 // Walk up the TypeLoc chain, because auto may be qualified.
63 while (N && N->ASTNode.get<TypeLoc>())
64 N = N->Parent;
65 // The relevant type is the only direct type child of a Decomposition.
66 return N && N->ASTNode.get<DecompositionDecl>();
67 }
68
isLambda(QualType QT)69 bool isLambda(QualType QT) {
70 if (!QT.isNull())
71 if (const auto *RD = QT->getAsRecordDecl())
72 return RD->isLambda();
73 return false;
74 }
75
76 // Returns true iff Node is a lambda, and thus should not be expanded. Loc is
77 // the location of the auto type.
isDeducedAsLambda(const SelectionTree::Node * Node,SourceLocation Loc)78 bool isDeducedAsLambda(const SelectionTree::Node *Node, SourceLocation Loc) {
79 // getDeducedType() does a traversal, which we want to avoid in prepare().
80 // But at least check this isn't auto x = []{...};, which can't ever be
81 // expanded.
82 // (It would be nice if we had an efficient getDeducedType(), instead).
83 for (const auto *It = Node; It; It = It->Parent) {
84 if (const auto *DD = It->ASTNode.get<DeclaratorDecl>()) {
85 if (DD->getTypeSourceInfo() &&
86 DD->getTypeSourceInfo()->getTypeLoc().getBeginLoc() == Loc &&
87 isLambda(DD->getType()))
88 return true;
89 }
90 }
91 return false;
92 }
93
94 // Returns true iff "auto" in Node is really part of the template parameter,
95 // which we cannot expand.
isTemplateParam(const SelectionTree::Node * Node)96 bool isTemplateParam(const SelectionTree::Node *Node) {
97 if (Node->Parent)
98 if (Node->Parent->ASTNode.get<NonTypeTemplateParmDecl>())
99 return true;
100 return false;
101 }
102
prepare(const Selection & Inputs)103 bool ExpandDeducedType::prepare(const Selection &Inputs) {
104 if (auto *Node = Inputs.ASTSelection.commonAncestor()) {
105 if (auto *TypeNode = Node->ASTNode.get<TypeLoc>()) {
106 if (const AutoTypeLoc Result = TypeNode->getAs<AutoTypeLoc>()) {
107 if (!isStructuredBindingType(Node) &&
108 !isDeducedAsLambda(Node, Result.getBeginLoc()) &&
109 !isTemplateParam(Node))
110 Range = Result.getSourceRange();
111 }
112 if (auto TTPAuto = TypeNode->getAs<TemplateTypeParmTypeLoc>()) {
113 // We exclude concept constraints for now, as the SourceRange is wrong.
114 // void foo(C auto x) {};
115 // ^^^^
116 // TTPAuto->getSourceRange only covers "auto", not "C auto".
117 if (TTPAuto.getDecl()->isImplicit() &&
118 !TTPAuto.getDecl()->hasTypeConstraint())
119 Range = TTPAuto.getSourceRange();
120 }
121
122 if (auto DTTL = TypeNode->getAs<DecltypeTypeLoc>()) {
123 if (!isLambda(cast<DecltypeType>(DTTL.getType())->getUnderlyingType()))
124 Range = DTTL.getSourceRange();
125 }
126 }
127 }
128
129 return Range.isValid();
130 }
131
apply(const Selection & Inputs)132 Expected<Tweak::Effect> ExpandDeducedType::apply(const Selection &Inputs) {
133 auto &SrcMgr = Inputs.AST->getSourceManager();
134
135 std::optional<clang::QualType> DeducedType =
136 getDeducedType(Inputs.AST->getASTContext(), Range.getBegin());
137
138 // if we can't resolve the type, return an error message
139 if (DeducedType == std::nullopt || (*DeducedType)->isUndeducedAutoType())
140 return error("Could not deduce type for 'auto' type");
141
142 // we shouldn't replace a dependent type which is likely not to print
143 // usefully, e.g.
144 // template <class T>
145 // struct Foobar {
146 // decltype(T{}) foobar;
147 // ^^^^^^^^^^^^^ would turn out to be `<dependent-type>`
148 // };
149 if ((*DeducedType)->isDependentType())
150 return error("Could not expand a dependent type");
151
152 // Some types aren't written as single chunks of text, e.g:
153 // auto fptr = &func; // auto is void(*)()
154 // ==>
155 // void (*fptr)() = &func;
156 // Replacing these requires examining the declarator, we don't support it yet.
157 std::string PrettyDeclarator = printType(
158 *DeducedType, Inputs.ASTSelection.commonAncestor()->getDeclContext(),
159 "DECLARATOR_ID");
160 llvm::StringRef PrettyTypeName = PrettyDeclarator;
161 if (!PrettyTypeName.consume_back("DECLARATOR_ID"))
162 return error("Could not expand type that isn't a simple string");
163 PrettyTypeName = PrettyTypeName.rtrim();
164
165 tooling::Replacement Expansion(SrcMgr, CharSourceRange(Range, true),
166 PrettyTypeName);
167
168 return Effect::mainFileEdit(SrcMgr, tooling::Replacements(Expansion));
169 }
170
171 } // namespace
172 } // namespace clangd
173 } // namespace clang
174