Coverage Report

Created: 2022-01-15 10:30

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