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; |
42 | 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 | |
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. |
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 | |
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. |
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. |
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 | |
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(N: 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 | |
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(Fmt: "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(Fmt: "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(Suffix: "DECLARATOR_ID" )) |
162 | return error(Fmt: "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(SM: SrcMgr, Replacements: tooling::Replacements(Expansion)); |
169 | } |
170 | |
171 | } // namespace |
172 | } // namespace clangd |
173 | } // namespace clang |
174 | |