1 | //===--- ExpectedTypes.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 | |
9 | #include "ExpectedTypes.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/AST/DeclTemplate.h" |
12 | #include "clang/AST/Type.h" |
13 | #include "clang/Index/USRGeneration.h" |
14 | #include "clang/Sema/CodeCompleteConsumer.h" |
15 | #include <optional> |
16 | |
17 | namespace clang { |
18 | namespace clangd { |
19 | namespace { |
20 | |
21 | static const Type *toEquivClass(ASTContext &Ctx, QualType T) { |
22 | if (T.isNull() || T->isDependentType()) |
23 | return nullptr; |
24 | // Drop references, we do not handle reference inits properly anyway. |
25 | T = T.getCanonicalType().getNonReferenceType(); |
26 | // Numeric types are the simplest case. |
27 | if (T->isBooleanType()) |
28 | return Ctx.BoolTy.getTypePtr(); |
29 | if (T->isIntegerType() && !T->isEnumeralType()) |
30 | return Ctx.IntTy.getTypePtr(); // All integers are equivalent. |
31 | if (T->isFloatingType() && !T->isComplexType()) |
32 | return Ctx.FloatTy.getTypePtr(); // All floats are equivalent. |
33 | |
34 | // Do some simple transformations. |
35 | if (T->isArrayType()) // Decay arrays to pointers. |
36 | return Ctx.getPointerType(T: QualType(T->getArrayElementTypeNoTypeQual(), 0)) |
37 | .getTypePtr(); |
38 | // Drop the qualifiers and return the resulting type. |
39 | // FIXME: also drop qualifiers from pointer types, e.g. 'const T* => T*' |
40 | return T.getTypePtr(); |
41 | } |
42 | |
43 | static std::optional<QualType> typeOfCompletion(const CodeCompletionResult &R) { |
44 | const NamedDecl *D = R.Declaration; |
45 | // Templates do not have a type on their own, look at the templated decl. |
46 | if (auto *Template = dyn_cast_or_null<TemplateDecl>(Val: D)) |
47 | D = Template->getTemplatedDecl(); |
48 | auto *VD = dyn_cast_or_null<ValueDecl>(Val: D); |
49 | if (!VD) |
50 | return std::nullopt; // We handle only variables and functions below. |
51 | auto T = VD->getType(); |
52 | if (T.isNull()) |
53 | return std::nullopt; |
54 | if (auto *FuncT = T->getAs<FunctionType>()) { |
55 | // Functions are a special case. They are completed as 'foo()' and we want |
56 | // to match their return type rather than the function type itself. |
57 | // FIXME(ibiryukov): in some cases, we might want to avoid completing `()` |
58 | // after the function name, e.g. `std::cout << std::endl`. |
59 | return FuncT->getReturnType(); |
60 | } |
61 | return T; |
62 | } |
63 | } // namespace |
64 | |
65 | std::optional<OpaqueType> OpaqueType::encode(ASTContext &Ctx, QualType T) { |
66 | if (T.isNull()) |
67 | return std::nullopt; |
68 | const Type *C = toEquivClass(Ctx, T); |
69 | if (!C) |
70 | return std::nullopt; |
71 | llvm::SmallString<128> Encoded; |
72 | if (index::generateUSRForType(T: QualType(C, 0), Ctx, Buf&: Encoded)) |
73 | return std::nullopt; |
74 | return OpaqueType(std::string(Encoded)); |
75 | } |
76 | |
77 | OpaqueType::OpaqueType(std::string Data) : Data(std::move(Data)) {} |
78 | |
79 | std::optional<OpaqueType> OpaqueType::fromType(ASTContext &Ctx, QualType Type) { |
80 | return encode(Ctx, T: Type); |
81 | } |
82 | |
83 | std::optional<OpaqueType> |
84 | OpaqueType::fromCompletionResult(ASTContext &Ctx, |
85 | const CodeCompletionResult &R) { |
86 | auto T = typeOfCompletion(R); |
87 | if (!T) |
88 | return std::nullopt; |
89 | return encode(Ctx, T: *T); |
90 | } |
91 | |
92 | } // namespace clangd |
93 | } // namespace clang |
94 | |