Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/CxxModuleHandler.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- CxxModuleHandler.cpp ----------------------------------------------===//
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 "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
10
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
11
12
#include "lldb/Utility/LLDBLog.h"
13
#include "lldb/Utility/Log.h"
14
#include "clang/Sema/Lookup.h"
15
#include "llvm/Support/Error.h"
16
#include <optional>
17
18
using namespace lldb_private;
19
using namespace clang;
20
21
CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
22
140k
    : m_importer(&importer),
23
140k
      m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {
24
25
140k
  std::initializer_list<const char *> supported_names = {
26
      // containers
27
140k
      "array",
28
140k
      "deque",
29
140k
      "forward_list",
30
140k
      "list",
31
140k
      "queue",
32
140k
      "stack",
33
140k
      "vector",
34
      // pointers
35
140k
      "shared_ptr",
36
140k
      "unique_ptr",
37
140k
      "weak_ptr",
38
      // iterator
39
140k
      "move_iterator",
40
140k
      "__wrap_iter",
41
      // utility
42
140k
      "allocator",
43
140k
      "pair",
44
140k
  };
45
140k
  m_supported_templates.insert(supported_names.begin(), supported_names.end());
46
140k
}
47
48
/// Builds a list of scopes that point into the given context.
49
///
50
/// \param sema The sema that will be using the scopes.
51
/// \param ctxt The context that the scope should look into.
52
/// \param result A list of scopes. The scopes need to be freed by the caller
53
///               (except the TUScope which is owned by the sema).
54
static void makeScopes(Sema &sema, DeclContext *ctxt,
55
1.85k
                       std::vector<Scope *> &result) {
56
  // FIXME: The result should be a list of unique_ptrs, but the TUScope makes
57
  // this currently impossible as it's owned by the Sema.
58
59
1.85k
  if (auto parent = ctxt->getParent()) {
60
660
    makeScopes(sema, parent, result);
61
62
660
    Scope *scope =
63
660
        new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());
64
660
    scope->setEntity(ctxt);
65
660
    result.push_back(scope);
66
660
  } else
67
1.19k
    result.push_back(sema.TUScope);
68
1.85k
}
69
70
/// Uses the Sema to look up the given name in the given DeclContext.
71
static std::unique_ptr<LookupResult>
72
1.19k
emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {
73
1.19k
  IdentifierInfo &ident = sema.getASTContext().Idents.get(name);
74
75
1.19k
  std::unique_ptr<LookupResult> lookup_result;
76
1.19k
  lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),
77
1.19k
                                                 SourceLocation(),
78
1.19k
                                                 Sema::LookupOrdinaryName);
79
80
  // Usually during parsing we already encountered the scopes we would use. But
81
  // here don't have these scopes so we have to emulate the behavior of the
82
  // Sema during parsing.
83
1.19k
  std::vector<Scope *> scopes;
84
1.19k
  makeScopes(sema, ctxt, scopes);
85
86
  // Now actually perform the lookup with the sema.
87
1.19k
  sema.LookupName(*lookup_result, scopes.back());
88
89
  // Delete all the allocated scopes beside the translation unit scope (which
90
  // has depth 0).
91
1.19k
  for (Scope *s : scopes)
92
1.85k
    if (s->getDepth() != 0)
93
660
      delete s;
94
95
1.19k
  return lookup_result;
96
1.19k
}
97
98
/// Error class for handling problems when finding a certain DeclContext.
99
struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {
100
101
  static char ID;
102
103
  MissingDeclContext(DeclContext *context, std::string error)
104
0
      : m_context(context), m_error(error) {}
105
106
  DeclContext *m_context;
107
  std::string m_error;
108
109
0
  void log(llvm::raw_ostream &OS) const override {
110
0
    OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",
111
0
                        m_context->getDeclKindName(), m_error);
112
0
  }
113
114
0
  std::error_code convertToErrorCode() const override {
115
0
    return llvm::inconvertibleErrorCode();
116
0
  }
117
};
118
119
char MissingDeclContext::ID = 0;
120
121
/// Given a foreign decl context, this function finds the equivalent local
122
/// decl context in the ASTContext of the given Sema. Potentially deserializes
123
/// decls from the 'std' module if necessary.
124
static llvm::Expected<DeclContext *>
125
1.19k
getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {
126
127
  // Inline namespaces don't matter for lookups, so let's skip them.
128
1.78k
  while (foreign_ctxt && foreign_ctxt->isInlineNamespace())
129
596
    foreign_ctxt = foreign_ctxt->getParent();
130
131
  // If the foreign context is the TU, we just return the local TU.
132
1.19k
  if (foreign_ctxt->isTranslationUnit())
133
596
    return sema.getASTContext().getTranslationUnitDecl();
134
135
  // Recursively find/build the parent DeclContext.
136
596
  llvm::Expected<DeclContext *> parent =
137
596
      getEqualLocalDeclContext(sema, foreign_ctxt->getParent());
138
596
  if (!parent)
139
0
    return parent;
140
141
  // We currently only support building namespaces.
142
596
  if (foreign_ctxt->isNamespace()) {
143
596
    NamedDecl *ns = llvm::cast<NamedDecl>(foreign_ctxt);
144
596
    llvm::StringRef ns_name = ns->getName();
145
146
596
    auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);
147
596
    for (NamedDecl *named_decl : *lookup_result) {
148
596
      if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))
149
596
        return DC->getPrimaryContext();
150
596
    }
151
0
    return llvm::make_error<MissingDeclContext>(
152
0
        foreign_ctxt,
153
0
        "Couldn't find namespace " + ns->getQualifiedNameAsString());
154
596
  }
155
156
0
  return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");
157
596
}
158
159
/// Returns true iff tryInstantiateStdTemplate supports instantiating a template
160
/// with the given template arguments.
161
596
static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {
162
904
  for (const TemplateArgument &arg : a) {
163
904
    switch (arg.getKind()) {
164
864
    case TemplateArgument::Type:
165
904
    case TemplateArgument::Integral:
166
904
      break;
167
0
    default:
168
      // TemplateArgument kind hasn't been handled yet.
169
0
      return false;
170
904
    }
171
904
  }
172
596
  return true;
173
596
}
174
175
/// Constructor function for Clang declarations. Ensures that the created
176
/// declaration is registered with the ASTImporter.
177
template <typename T, typename... Args>
178
466
T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {
179
466
  T *to_d = T::Create(std::forward<Args>(args)...);
180
466
  importer.RegisterImportedDecl(from_d, to_d);
181
466
  return to_d;
182
466
}
183
184
1.60k
std::optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
185
1.60k
  Log *log = GetLog(LLDBLog::Expressions);
186
187
  // If we don't have a template to instiantiate, then there is nothing to do.
188
1.60k
  auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
189
1.60k
  if (!td)
190
967
    return std::nullopt;
191
192
  // We only care about templates in the std namespace.
193
633
  if (!td->getDeclContext()->isStdNamespace())
194
0
    return std::nullopt;
195
196
  // We have a list of supported template names.
197
633
  if (!m_supported_templates.contains(td->getName()))
198
37
    return std::nullopt;
199
200
  // Early check if we even support instantiating this template. We do this
201
  // before we import anything into the target AST.
202
596
  auto &foreign_args = td->getTemplateInstantiationArgs();
203
596
  if (!templateArgsAreSupported(foreign_args.asArray()))
204
0
    return std::nullopt;
205
206
  // Find the local DeclContext that corresponds to the DeclContext of our
207
  // decl we want to import.
208
596
  llvm::Expected<DeclContext *> to_context =
209
596
      getEqualLocalDeclContext(*m_sema, td->getDeclContext());
210
596
  if (!to_context) {
211
0
    LLDB_LOG_ERROR(log, to_context.takeError(),
212
0
                   "Got error while searching equal local DeclContext for decl "
213
0
                   "'{1}':\n{0}",
214
0
                   td->getName());
215
0
    return std::nullopt;
216
0
  }
217
218
  // Look up the template in our local context.
219
596
  std::unique_ptr<LookupResult> lookup =
220
596
      emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
221
222
596
  ClassTemplateDecl *new_class_template = nullptr;
223
596
  for (auto LD : *lookup) {
224
588
    if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
225
588
      break;
226
588
  }
227
596
  if (!new_class_template)
228
8
    return std::nullopt;
229
230
  // Import the foreign template arguments.
231
588
  llvm::SmallVector<TemplateArgument, 4> imported_args;
232
233
  // If this logic is changed, also update templateArgsAreSupported.
234
896
  for (const TemplateArgument &arg : foreign_args.asArray()) {
235
896
    switch (arg.getKind()) {
236
856
    case TemplateArgument::Type: {
237
856
      llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
238
856
      if (!type) {
239
0
        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
240
0
        return std::nullopt;
241
0
      }
242
856
      imported_args.push_back(
243
856
          TemplateArgument(*type, /*isNullPtr*/ false, arg.getIsDefaulted()));
244
856
      break;
245
856
    }
246
40
    case TemplateArgument::Integral: {
247
40
      llvm::APSInt integral = arg.getAsIntegral();
248
40
      llvm::Expected<QualType> type =
249
40
          m_importer->Import(arg.getIntegralType());
250
40
      if (!type) {
251
0
        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
252
0
        return std::nullopt;
253
0
      }
254
40
      imported_args.push_back(TemplateArgument(d->getASTContext(), integral,
255
40
                                               *type, arg.getIsDefaulted()));
256
40
      break;
257
40
    }
258
0
    default:
259
0
      assert(false && "templateArgsAreSupported not updated?");
260
896
    }
261
896
  }
262
263
  // Find the class template specialization declaration that
264
  // corresponds to these arguments.
265
588
  void *InsertPos = nullptr;
266
588
  ClassTemplateSpecializationDecl *result =
267
588
      new_class_template->findSpecialization(imported_args, InsertPos);
268
269
588
  if (result) {
270
    // We found an existing specialization in the module that fits our arguments
271
    // so we can treat it as the result and register it with the ASTImporter.
272
122
    m_importer->RegisterImportedDecl(d, result);
273
122
    return result;
274
122
  }
275
276
  // Instantiate the template.
277
466
  result = createDecl<ClassTemplateSpecializationDecl>(
278
466
      *m_importer, d, m_sema->getASTContext(),
279
466
      new_class_template->getTemplatedDecl()->getTagKind(),
280
466
      new_class_template->getDeclContext(),
281
466
      new_class_template->getTemplatedDecl()->getLocation(),
282
466
      new_class_template->getLocation(), new_class_template, imported_args,
283
466
      nullptr);
284
285
466
  new_class_template->AddSpecialization(result, InsertPos);
286
466
  if (new_class_template->isOutOfLine())
287
0
    result->setLexicalDeclContext(
288
0
        new_class_template->getLexicalDeclContext());
289
466
  return result;
290
588
}
291
292
1.00M
std::optional<Decl *> CxxModuleHandler::Import(Decl *d) {
293
1.00M
  if (!isValid())
294
1.00M
    return {};
295
296
1.60k
  return tryInstantiateStdTemplate(d);
297
1.00M
}