Skip to content

Commit c64516a

Browse files
committed
Resolve deduced type
1 parent 2775db5 commit c64516a

File tree

2 files changed

+235
-35
lines changed

2 files changed

+235
-35
lines changed

src/Feature/Hover.cpp

Lines changed: 233 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Support/Struct.h"
1111
#include "Support/Doxygen.h"
1212
#include "llvm/Support/raw_ostream.h"
13+
#include "clang/Sema/HeuristicResolver.h"
1314
#include "clang/Lex/Lexer.h"
1415
#include "clang/AST/ASTTypeTraits.h"
1516

@@ -39,7 +40,7 @@ static std::vector<HoverItem> get_hover_items(CompilationUnit& unit,
3940

4041
/// TODO: Add other hover items.
4142
if(auto fd = llvm::dyn_cast<clang::FieldDecl>(decl)) {
42-
clice::logging::warn("Got a field decl");
43+
LOGGING_WARN("Got a field decl");
4344
const auto record = fd->getParent();
4445
add_item(HoverItem::Type, fd->getType().getAsString());
4546

@@ -62,12 +63,12 @@ static std::vector<HoverItem> get_hover_items(CompilationUnit& unit,
6263

6364
if(fd->isBitField()) {
6465
add_item(HoverItem::BitWidth, llvm::Twine(fd->getBitWidthValue()).str());
65-
clice::logging::warn("Got bit field, name: {}, bitwidth: {}",
66-
fd->getName(),
67-
fd->getBitWidthValue());
66+
LOGGING_WARN("Got bit field, name: {}, bitwidth: {}",
67+
fd->getName(),
68+
fd->getBitWidthValue());
6869
}
6970
} else if(auto vd = llvm::dyn_cast<clang::VarDecl>(decl)) {
70-
clice::logging::warn("Got a var decl");
71+
LOGGING_WARN("Got a var decl");
7172
add_item(HoverItem::Type, vd->getType().getAsString());
7273
}
7374

@@ -114,6 +115,208 @@ static std::string get_source_code(CompilationUnit& unit, clang::SourceRange ran
114115
lo)};
115116
}
116117

118+
static clang::TemplateTypeParmTypeLoc getContainedAutoParamType(clang::TypeLoc TL) {
119+
if(auto QTL = TL.getAs<clang::QualifiedTypeLoc>())
120+
return getContainedAutoParamType(QTL.getUnqualifiedLoc());
121+
if(llvm::isa<clang::PointerType, clang::ReferenceType, clang::ParenType>(TL.getTypePtr()))
122+
return getContainedAutoParamType(TL.getNextTypeLoc());
123+
if(auto FTL = TL.getAs<clang::FunctionTypeLoc>())
124+
return getContainedAutoParamType(FTL.getReturnLoc());
125+
if(auto TTPTL = TL.getAs<clang::TemplateTypeParmTypeLoc>()) {
126+
if(TTPTL.getTypePtr()->getDecl()->isImplicit())
127+
return TTPTL;
128+
}
129+
return {};
130+
}
131+
132+
template <typename TemplateDeclTy>
133+
static clang::NamedDecl* getOnlyInstantiationImpl(TemplateDeclTy* TD) {
134+
clang::NamedDecl* Only = nullptr;
135+
for(auto* Spec: TD->specializations()) {
136+
if(Spec->getTemplateSpecializationKind() == clang::TSK_ExplicitSpecialization)
137+
continue;
138+
if(Only != nullptr)
139+
return nullptr;
140+
Only = Spec;
141+
}
142+
return Only;
143+
}
144+
145+
static clang::NamedDecl* getOnlyInstantiation(clang::NamedDecl* TemplatedDecl) {
146+
if(clang::TemplateDecl* TD = TemplatedDecl->getDescribedTemplate()) {
147+
if(auto* CTD = llvm::dyn_cast<clang::ClassTemplateDecl>(TD))
148+
return getOnlyInstantiationImpl(CTD);
149+
if(auto* FTD = llvm::dyn_cast<clang::FunctionTemplateDecl>(TD))
150+
return getOnlyInstantiationImpl(FTD);
151+
if(auto* VTD = llvm::dyn_cast<clang::VarTemplateDecl>(TD))
152+
return getOnlyInstantiationImpl(VTD);
153+
}
154+
return nullptr;
155+
}
156+
157+
/// Computes the deduced type at a given location by visiting the relevant
158+
/// nodes. We use this to display the actual type when hovering over an "auto"
159+
/// keyword or "decltype()" expression.
160+
/// FIXME: This could have been a lot simpler by visiting AutoTypeLocs but it
161+
/// seems that the AutoTypeLocs that can be visited along with their AutoType do
162+
/// not have the deduced type set. Instead, we have to go to the appropriate
163+
/// DeclaratorDecl/FunctionDecl and work our back to the AutoType that does have
164+
/// a deduced type set. The AST should be improved to simplify this scenario.
165+
class DeducedTypeVisitor : public clang::RecursiveASTVisitor<DeducedTypeVisitor> {
166+
clang::SourceLocation SearchedLocation;
167+
const clang::HeuristicResolver* Resolver;
168+
169+
public:
170+
DeducedTypeVisitor(clang::SourceLocation SearchedLocation,
171+
const clang::HeuristicResolver* Resolver) :
172+
SearchedLocation(SearchedLocation), Resolver(Resolver) {}
173+
174+
// Handle auto initializers:
175+
//- auto i = 1;
176+
//- decltype(auto) i = 1;
177+
//- auto& i = 1;
178+
//- auto* i = &a;
179+
bool VisitDeclaratorDecl(clang::DeclaratorDecl* D) {
180+
if(!D->getTypeSourceInfo() ||
181+
!D->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc() ||
182+
D->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc().getNameLoc() !=
183+
SearchedLocation)
184+
return true;
185+
186+
if(auto* AT = D->getType()->getContainedAutoType()) {
187+
if(AT->isUndeducedAutoType()) {
188+
if(const auto* VD = dyn_cast<clang::VarDecl>(D)) {
189+
if(Resolver && VD->hasInit()) {
190+
// FIXME:
191+
// DeducedType = Resolver->resolveExprToType(VD->getInit());
192+
DeducedType = VD->getType();
193+
return true;
194+
}
195+
}
196+
}
197+
DeducedType = AT->desugar();
198+
}
199+
return true;
200+
}
201+
202+
// Handle auto return types:
203+
//- auto foo() {}
204+
//- auto& foo() {}
205+
//- auto foo() -> int {}
206+
//- auto foo() -> decltype(1+1) {}
207+
//- operator auto() const { return 10; }
208+
bool VisitFunctionDecl(clang::FunctionDecl* D) {
209+
if(!D->getTypeSourceInfo())
210+
return true;
211+
// Loc of auto in return type (c++14).
212+
auto CurLoc = D->getReturnTypeSourceRange().getBegin();
213+
// Loc of "auto" in operator auto()
214+
if(CurLoc.isInvalid() && isa<clang::CXXConversionDecl>(D))
215+
CurLoc = D->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
216+
// Loc of "auto" in function with trailing return type (c++11).
217+
if(CurLoc.isInvalid())
218+
CurLoc = D->getSourceRange().getBegin();
219+
if(CurLoc != SearchedLocation)
220+
return true;
221+
222+
const clang::AutoType* AT = D->getReturnType()->getContainedAutoType();
223+
if(AT && !AT->getDeducedType().isNull()) {
224+
DeducedType = AT->getDeducedType();
225+
} else if(auto* DT = dyn_cast<clang::DecltypeType>(D->getReturnType())) {
226+
// auto in a trailing return type just points to a DecltypeType and
227+
// getContainedAutoType does not unwrap it.
228+
if(!DT->getUnderlyingType().isNull())
229+
DeducedType = DT->getUnderlyingType();
230+
} else if(!D->getReturnType().isNull()) {
231+
DeducedType = D->getReturnType();
232+
}
233+
return true;
234+
}
235+
236+
// Handle non-auto decltype, e.g.:
237+
// - auto foo() -> decltype(expr) {}
238+
// - decltype(expr);
239+
bool VisitDecltypeTypeLoc(clang::DecltypeTypeLoc TL) {
240+
if(TL.getBeginLoc() != SearchedLocation)
241+
return true;
242+
243+
// A DecltypeType's underlying type can be another DecltypeType! E.g.
244+
// int I = 0;
245+
// decltype(I) J = I;
246+
// decltype(J) K = J;
247+
const clang::DecltypeType* DT = dyn_cast<clang::DecltypeType>(TL.getTypePtr());
248+
while(DT && !DT->getUnderlyingType().isNull()) {
249+
DeducedType = DT->getUnderlyingType();
250+
DT = dyn_cast<clang::DecltypeType>(DeducedType.getTypePtr());
251+
}
252+
return true;
253+
}
254+
255+
// Handle functions/lambdas with `auto` typed parameters.
256+
// We deduce the type if there's exactly one instantiation visible.
257+
bool VisitParmVarDecl(clang::ParmVarDecl* PVD) {
258+
if(!PVD->getType()->isDependentType())
259+
return true;
260+
// 'auto' here does not name an AutoType, but an implicit template param.
261+
clang::TemplateTypeParmTypeLoc Auto =
262+
getContainedAutoParamType(PVD->getTypeSourceInfo()->getTypeLoc());
263+
if(Auto.isNull() || Auto.getNameLoc() != SearchedLocation)
264+
return true;
265+
266+
// We expect the TTP to be attached to this function template.
267+
// Find the template and the param index.
268+
auto* Templated = llvm::dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
269+
if(!Templated)
270+
return true;
271+
auto* FTD = Templated->getDescribedFunctionTemplate();
272+
if(!FTD)
273+
return true;
274+
int ParamIndex = paramIndex(*FTD, *Auto.getDecl());
275+
if(ParamIndex < 0) {
276+
assert(false && "auto TTP is not from enclosing function?");
277+
return true;
278+
}
279+
280+
// Now find the instantiation and the deduced template type arg.
281+
auto* Instantiation =
282+
llvm::dyn_cast_or_null<clang::FunctionDecl>(getOnlyInstantiation(Templated));
283+
if(!Instantiation)
284+
return true;
285+
const auto* Args = Instantiation->getTemplateSpecializationArgs();
286+
if(Args->size() != FTD->getTemplateParameters()->size())
287+
return true; // no weird variadic stuff
288+
DeducedType = Args->get(ParamIndex).getAsType();
289+
return true;
290+
}
291+
292+
static int paramIndex(const clang::TemplateDecl& TD, clang::NamedDecl& Param) {
293+
unsigned I = 0;
294+
for(auto* ND: *TD.getTemplateParameters()) {
295+
if(&Param == ND)
296+
return I;
297+
++I;
298+
}
299+
return -1;
300+
}
301+
302+
clang::QualType DeducedType;
303+
};
304+
305+
// FIXME: Do as clangd did(?) a more simple way?
306+
static std::optional<clang::QualType> getDeducedType(clang::ASTContext& ASTCtx,
307+
const clang::HeuristicResolver* Resolver,
308+
clang::SourceLocation Loc) {
309+
if(!Loc.isValid()) {
310+
return {};
311+
}
312+
DeducedTypeVisitor V(Loc, Resolver);
313+
V.TraverseAST(ASTCtx);
314+
if(V.DeducedType.isNull()) {
315+
return std::nullopt;
316+
}
317+
return V.DeducedType;
318+
}
319+
117320
// TODO: How does clangd put together decl, name, scope and sometimes initialized value?
118321
// ```
119322
// // scope
@@ -140,22 +343,19 @@ static std::optional<Hover> hover(CompilationUnit& unit,
140343
}
141344

142345
static std::optional<Hover> hover(CompilationUnit& unit,
143-
const clang::TypeLoc* typeloc,
346+
const clang::QualType& ty,
144347
const config::HoverOptions& opt) {
145348
// TODO: Hover for type
146-
clice::logging::warn("Hit a typeloc");
147-
typeloc->dump(llvm::errs(), unit.context());
148-
auto ty = typeloc->getType();
149-
// FIXME: AutoTypeLoc / DecltypeTypeLoc
349+
// TODO: Add source code
150350
return Hover{.kind = SymbolKind::Type, .name = ty.getAsString()};
151351
}
152352

153353
static std::optional<Hover> hover(CompilationUnit& unit,
154354
const SelectionTree::Node* node,
155355
const config::HoverOptions& opt) {
156356
using namespace clang;
357+
auto wanted_node = node;
157358
auto Kind = node->data.getNodeKind();
158-
clice::logging::warn("Node kind is: {}", Kind.asStringRef());
159359

160360
#define kind_flag_def(Ty) static constexpr auto Flag##Ty = ASTNodeKind::getFromNodeKind<Ty>()
161361
kind_flag_def(QualType);
@@ -174,42 +374,42 @@ static std::optional<Hover> hover(CompilationUnit& unit,
174374
kind_flag_def(Attr);
175375
kind_flag_def(ObjCProtocolLoc);
176376

177-
#define is_in_range(LHS, RHS) \
178-
(!((Kind < Flag##LHS) && (Kind.isSame(Flag##LHS))) && (Kind < Flag##RHS))
179-
180377
#define is(flag) (Kind.isSame(Flag##flag))
181378

379+
#define is_in_range(LHS, RHS) (!((Kind < Flag##LHS) && is(LHS)) && (Kind < Flag##RHS))
380+
381+
// auto and decltype is specially processed
382+
if(is(AutoTypeLoc) || is(DecltypeTypeLoc)) {
383+
auto resolver = HeuristicResolver(unit.context());
384+
if(auto ty = getDeducedType(unit.context(), &resolver, node->source_range().getBegin())) {
385+
return hover(unit, *ty, opt);
386+
} else {
387+
LOGGING_WARN("Cannot get deduced type");
388+
}
389+
}
390+
182391
if(is(NestedNameSpecifierLoc)) {
183-
clice::logging::warn("Hit a `NestedNameSpecifierLoc`");
392+
LOGGING_WARN("Hit a `NestedNameSpecifierLoc`");
184393
} else if(is_in_range(QualType, TypeLoc)) {
185394
// Typeloc
186-
clice::logging::warn("Hit a `TypeLoc`");
187-
// auto and decltype is specially processed
188-
if(is(AutoTypeLoc)) {
189-
clice::logging::warn("Hit a `AutoTypeLoc`");
190-
return std::nullopt;
191-
}
192-
if(is(DecltypeTypeLoc)) {
193-
clice::logging::warn("Hit a `DecltypeTypeLoc`");
194-
return std::nullopt;
195-
}
395+
LOGGING_WARN("Hit a `TypeLoc`");
196396
if(auto typeloc = node->get<clang::TypeLoc>()) {
197-
return hover(unit, typeloc, opt);
397+
return hover(unit, typeloc->getType(), opt);
198398
}
199399
} else if(is_in_range(Decl, Stmt)) {
200400
// Decl
201-
clice::logging::warn("Hit a `Decl`");
401+
LOGGING_WARN("Hit a `Decl`");
202402
if(auto decl = node->get<clang::NamedDecl>()) {
203403
return hover(unit, decl, opt);
204404
} else {
205-
clice::logging::warn("Not intersted");
405+
LOGGING_WARN("Not intersted");
206406
}
207407
} else if(is_in_range(Attr, ObjCProtocolLoc)) {
208-
clice::logging::warn("Hit an `Attr`");
408+
LOGGING_WARN("Hit an `Attr`");
209409
// TODO: Attr
210410
} else {
211411
// Not interested
212-
clice::logging::warn("Not interested");
412+
LOGGING_WARN("Not interested");
213413
}
214414

215415
#undef is
@@ -266,10 +466,9 @@ std::optional<Hover> hover(CompilationUnit& unit,
266466
}
267467
}
268468

269-
// clice::logging::warn("Hit a macro");
270469
auto tokens_under_cursor = unit.spelled_tokens_touch(*loc);
271470
if(tokens_under_cursor.empty()) {
272-
clice::logging::warn("Cannot detect tokens");
471+
LOGGING_WARN("Cannot detect tokens");
273472
return std::nullopt;
274473
}
275474
auto hl_range = tokens_under_cursor.back().range(sm).toCharRange(sm).getAsRange();
@@ -314,13 +513,14 @@ std::optional<Hover> hover(CompilationUnit& unit,
314513

315514
auto tree = SelectionTree::create_right(unit, {offset, offset});
316515
if(auto node = tree.common_ancestor()) {
516+
LOGGING_WARN("Got node: {}", node->kind());
317517
if(auto info = hover(unit, node, opt)) {
318518
info->hl_range = to_proto_range(sm, hl_range);
319519
return info;
320520
}
321521
return std::nullopt;
322522
} else {
323-
clice::logging::warn("Not an ast node");
523+
LOGGING_WARN("Not an ast node");
324524
}
325525

326526
return std::nullopt;

src/Server/Lifecycle.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ async::Task<json::Value> Server::on_initialize(proto::InitializeParams params) {
3535
/// Set server options.
3636
opening_files.set_capability(config.project.max_active_file);
3737

38-
/// /// Load compile commands.json
39-
/// database.load_compile_database(config.project.compile_commands_dirs, workspace);
38+
/// Load compile commands.json
39+
database.load_compile_database(config.project.compile_commands_dirs, workspace);
4040

4141
/// Load cache info.
4242
load_cache_info();

0 commit comments

Comments
 (0)