summaryrefslogtreecommitdiffstats
path: root/lib/Tooling/Core/QualTypeNames.cpp
blob: 721c2c92fc2749beb18a449ea6fe24ca3ede2373 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===//
//
//                     The LLVM Compiler Infrastructure
//
//===----------------------------------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "clang/Tooling/Core/QualTypeNames.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"

#include <stdio.h>
#include <memory>

namespace clang {

namespace TypeName {
/// \brief Generates a QualType that can be used to name the same type
/// if used at the end of the current translation unit. This ignores
/// issues such as type shadowing.
///
/// \param[in] QT - the type for which the fully qualified type will be
/// returned.
/// \param[in] Ctx - the ASTContext to be used.
/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
/// specifier "::" should be prepended or not.
static QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
                                      bool WithGlobalNsPrefix);

/// \brief Create a NestedNameSpecifier for Namesp and its enclosing
/// scopes.
///
/// \param[in] Ctx - the AST Context to be used.
/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
/// is requested.
/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
/// specifier "::" should be prepended or not.
static NestedNameSpecifier *createNestedNameSpecifier(
    const ASTContext &Ctx,
    const NamespaceDecl *Namesp,
    bool WithGlobalNsPrefix);

/// \brief Create a NestedNameSpecifier for TagDecl and its enclosing
/// scopes.
///
/// \param[in] Ctx - the AST Context to be used.
/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
/// requested.
/// \param[in] FullyQualify - Convert all template arguments into fully
/// qualified names.
/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
/// specifier "::" should be prepended or not.
static NestedNameSpecifier *createNestedNameSpecifier(
    const ASTContext &Ctx, const TypeDecl *TD,
    bool FullyQualify, bool WithGlobalNsPrefix);

static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
    const ASTContext &Ctx, const Decl *decl,
    bool FullyQualified, bool WithGlobalNsPrefix);

static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
    const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix);

static bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
                                          TemplateName &TName,
                                          bool WithGlobalNsPrefix) {
  bool Changed = false;
  NestedNameSpecifier *NNS = nullptr;

  TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
  // ArgTDecl won't be NULL because we asserted that this isn't a
  // dependent context very early in the call chain.
  assert(ArgTDecl != nullptr);
  QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();

  if (QTName && !QTName->hasTemplateKeyword()) {
    NNS = QTName->getQualifier();
    NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(
        Ctx, NNS, WithGlobalNsPrefix);
    if (QNNS != NNS) {
      Changed = true;
      NNS = QNNS;
    } else {
      NNS = nullptr;
    }
  } else {
    NNS = createNestedNameSpecifierForScopeOf(
        Ctx, ArgTDecl, true, WithGlobalNsPrefix);
  }
  if (NNS) {
    TName = Ctx.getQualifiedTemplateName(NNS,
                                         /*TemplateKeyword=*/false, ArgTDecl);
    Changed = true;
  }
  return Changed;
}

static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
                                              TemplateArgument &Arg,
                                              bool WithGlobalNsPrefix) {
  bool Changed = false;

  // Note: we do not handle TemplateArgument::Expression, to replace it
  // we need the information for the template instance decl.

  if (Arg.getKind() == TemplateArgument::Template) {
    TemplateName TName = Arg.getAsTemplate();
    Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
    if (Changed) {
      Arg = TemplateArgument(TName);
    }
  } else if (Arg.getKind() == TemplateArgument::Type) {
    QualType SubTy = Arg.getAsType();
    // Check if the type needs more desugaring and recurse.
    QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix);
    if (QTFQ != SubTy) {
      Arg = TemplateArgument(QTFQ);
      Changed = true;
    }
  }
  return Changed;
}

static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
                                                 const Type *TypePtr,
                                                 bool WithGlobalNsPrefix) {
  // DependentTemplateTypes exist within template declarations and
  // definitions. Therefore we shouldn't encounter them at the end of
  // a translation unit. If we do, the caller has made an error.
  assert(!isa<DependentTemplateSpecializationType>(TypePtr));
  // In case of template specializations, iterate over the arguments
  // and fully qualify them as well.
  if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) {
    bool MightHaveChanged = false;
    SmallVector<TemplateArgument, 4> FQArgs;
    for (TemplateSpecializationType::iterator I = TST->begin(), E = TST->end();
         I != E; ++I) {
      // Cheap to copy and potentially modified by
      // getFullyQualifedTemplateArgument.
      TemplateArgument Arg(*I);
      MightHaveChanged |= getFullyQualifiedTemplateArgument(
          Ctx, Arg, WithGlobalNsPrefix);
      FQArgs.push_back(Arg);
    }

    // If a fully qualified arg is different from the unqualified arg,
    // allocate new type in the AST.
    if (MightHaveChanged) {
      QualType QT = Ctx.getTemplateSpecializationType(
          TST->getTemplateName(), FQArgs,
          TST->getCanonicalTypeInternal());
      // getTemplateSpecializationType returns a fully qualified
      // version of the specialization itself, so no need to qualify
      // it.
      return QT.getTypePtr();
    }
  } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) {
    // We are asked to fully qualify and we have a Record Type,
    // which can point to a template instantiation with no sugar in any of
    // its template argument, however we still need to fully qualify them.

    if (const auto *TSTDecl =
        dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
      const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();

      bool MightHaveChanged = false;
      SmallVector<TemplateArgument, 4> FQArgs;
      for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
        // cheap to copy and potentially modified by
        // getFullyQualifedTemplateArgument
        TemplateArgument Arg(TemplateArgs[I]);
        MightHaveChanged |= getFullyQualifiedTemplateArgument(
            Ctx, Arg, WithGlobalNsPrefix);
        FQArgs.push_back(Arg);
      }

      // If a fully qualified arg is different from the unqualified arg,
      // allocate new type in the AST.
      if (MightHaveChanged) {
        TemplateName TN(TSTDecl->getSpecializedTemplate());
        QualType QT = Ctx.getTemplateSpecializationType(
            TN, FQArgs,
            TSTRecord->getCanonicalTypeInternal());
        // getTemplateSpecializationType returns a fully qualified
        // version of the specialization itself, so no need to qualify
        // it.
        return QT.getTypePtr();
      }
    }
  }
  return TypePtr;
}

static NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
                                           bool FullyQualify,
                                           bool WithGlobalNsPrefix) {
  const DeclContext *DC = D->getDeclContext();
  if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
    while (NS && NS->isInline()) {
      // Ignore inline namespace;
      NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
    }
    if (NS->getDeclName()) {
      return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);
    }
    return nullptr;  // no starting '::', no anonymous
  } else if (const auto *TD = dyn_cast<TagDecl>(DC)) {
    return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);
  } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) {
    return createNestedNameSpecifier(
        Ctx, TDD, FullyQualify, WithGlobalNsPrefix);
  } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
    return NestedNameSpecifier::GlobalSpecifier(Ctx);
  }
  return nullptr;  // no starting '::' if |WithGlobalNsPrefix| is false
}

/// \brief Return a fully qualified version of this name specifier.
static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
    const ASTContext &Ctx, NestedNameSpecifier *Scope,
    bool WithGlobalNsPrefix) {
  switch (Scope->getKind()) {
    case NestedNameSpecifier::Global:
      // Already fully qualified
      return Scope;
    case NestedNameSpecifier::Namespace:
      return TypeName::createNestedNameSpecifier(
          Ctx, Scope->getAsNamespace(), WithGlobalNsPrefix);
    case NestedNameSpecifier::NamespaceAlias:
      // Namespace aliases are only valid for the duration of the
      // scope where they were introduced, and therefore are often
      // invalid at the end of the TU.  So use the namespace name more
      // likely to be valid at the end of the TU.
      return TypeName::createNestedNameSpecifier(
          Ctx,
          Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(),
          WithGlobalNsPrefix);
    case NestedNameSpecifier::Identifier:
      // A function or some other construct that makes it un-namable
      // at the end of the TU. Skip the current component of the name,
      // but use the name of it's prefix.
      return getFullyQualifiedNestedNameSpecifier(
          Ctx, Scope->getPrefix(), WithGlobalNsPrefix);
    case NestedNameSpecifier::Super:
    case NestedNameSpecifier::TypeSpec:
    case NestedNameSpecifier::TypeSpecWithTemplate: {
      const Type *Type = Scope->getAsType();
      // Find decl context.
      const TagDecl *TD = nullptr;
      if (const TagType *TagDeclType = Type->getAs<TagType>()) {
        TD = TagDeclType->getDecl();
      } else {
        TD = Type->getAsCXXRecordDecl();
      }
      if (TD) {
        return TypeName::createNestedNameSpecifier(Ctx, TD,
                                                   true /*FullyQualified*/,
                                                   WithGlobalNsPrefix);
      } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) {
        return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(),
                                                   true /*FullyQualified*/,
                                                   WithGlobalNsPrefix);
      }
      return Scope;
    }
  }
  llvm_unreachable("bad NNS kind");
}

/// \brief Create a nested name specifier for the declaring context of
/// the type.
static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
    const ASTContext &Ctx, const Decl *Decl,
    bool FullyQualified, bool WithGlobalNsPrefix) {
  assert(Decl);

  const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
  const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
  const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
  if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
    if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
      if (ClassTemplateDecl *ClassTempl =
              CxxDecl->getDescribedClassTemplate()) {
        // We are in the case of a type(def) that was declared in a
        // class template but is *not* type dependent.  In clang, it
        // gets attached to the class template declaration rather than
        // any specific class template instantiation.  This result in
        // 'odd' fully qualified typename:
        //
        //    vector<_Tp,_Alloc>::size_type
        //
        // Make the situation is 'useable' but looking a bit odd by
        // picking a random instance as the declaring context.
        if (ClassTempl->spec_begin() != ClassTempl->spec_end()) {
          Decl = *(ClassTempl->spec_begin());
          Outer = dyn_cast<NamedDecl>(Decl);
          OuterNS = dyn_cast<NamespaceDecl>(Decl);
        }
      }
    }

    if (OuterNS) {
      return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
    } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
      return createNestedNameSpecifier(
          Ctx, TD, FullyQualified, WithGlobalNsPrefix);
    } else if (dyn_cast<TranslationUnitDecl>(Outer)) {
      // Context is the TU. Nothing needs to be done.
      return nullptr;
    } else {
      // Decl's context was neither the TU, a namespace, nor a
      // TagDecl, which means it is a type local to a scope, and not
      // accessible at the end of the TU.
      return nullptr;
    }
  } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
    return NestedNameSpecifier::GlobalSpecifier(Ctx);
  }
  return nullptr;
}

/// \brief Create a nested name specifier for the declaring context of
/// the type.
static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
    const ASTContext &Ctx, const Type *TypePtr,
    bool FullyQualified, bool WithGlobalNsPrefix) {
  if (!TypePtr) return nullptr;

  Decl *Decl = nullptr;
  // There are probably other cases ...
  if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
    Decl = TDT->getDecl();
  } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
    Decl = TagDeclType->getDecl();
  } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
    Decl = TST->getTemplateName().getAsTemplateDecl();
  } else {
    Decl = TypePtr->getAsCXXRecordDecl();
  }

  if (!Decl) return nullptr;

  return createNestedNameSpecifierForScopeOf(
      Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
}

NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
                                               const NamespaceDecl *Namespace,
                                               bool WithGlobalNsPrefix) {
  while (Namespace && Namespace->isInline()) {
    // Ignore inline namespace;
    Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
  }
  if (!Namespace) return nullptr;

  bool FullyQualified = true;  // doesn't matter, DeclContexts are namespaces
  return NestedNameSpecifier::Create(
      Ctx,
      createOuterNNS(Ctx, Namespace, FullyQualified, WithGlobalNsPrefix),
      Namespace);
}

NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
                                               const TypeDecl *TD,
                                               bool FullyQualify,
                                               bool WithGlobalNsPrefix) {
  return NestedNameSpecifier::Create(
      Ctx,
      createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),
      false /*No TemplateKeyword*/,
      TD->getTypeForDecl());
}

/// \brief Return the fully qualified type, including fully-qualified
/// versions of any template parameters.
QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
                               bool WithGlobalNsPrefix) {
  // In case of myType* we need to strip the pointer first, fully
  // qualify and attach the pointer once again.
  if (isa<PointerType>(QT.getTypePtr())) {
    // Get the qualifiers.
    Qualifiers Quals = QT.getQualifiers();
    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
    QT = Ctx.getPointerType(QT);
    // Add back the qualifiers.
    QT = Ctx.getQualifiedType(QT, Quals);
    return QT;
  }

  // In case of myType& we need to strip the reference first, fully
  // qualify and attach the reference once again.
  if (isa<ReferenceType>(QT.getTypePtr())) {
    // Get the qualifiers.
    bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
    Qualifiers Quals = QT.getQualifiers();
    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
    // Add the r- or l-value reference type back to the fully
    // qualified one.
    if (IsLValueRefTy)
      QT = Ctx.getLValueReferenceType(QT);
    else
      QT = Ctx.getRValueReferenceType(QT);
    // Add back the qualifiers.
    QT = Ctx.getQualifiedType(QT, Quals);
    return QT;
  }

  // Remove the part of the type related to the type being a template
  // parameter (we won't report it as part of the 'type name' and it
  // is actually make the code below to be more complex (to handle
  // those)
  while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
    // Get the qualifiers.
    Qualifiers Quals = QT.getQualifiers();

    QT = dyn_cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();

    // Add back the qualifiers.
    QT = Ctx.getQualifiedType(QT, Quals);
  }

  NestedNameSpecifier *Prefix = nullptr;
  // Local qualifiers are attached to the QualType outside of the
  // elaborated type.  Retrieve them before descending into the
  // elaborated type.
  Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
  QT = QualType(QT.getTypePtr(), 0);
  ElaboratedTypeKeyword Keyword = ETK_None;
  if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
    QT = ETypeInput->getNamedType();
    assert(!QT.hasLocalQualifiers());
    Keyword = ETypeInput->getKeyword();
  }
  // Create a nested name specifier if needed.
  Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
                                               true /*FullyQualified*/,
                                               WithGlobalNsPrefix);

  // In case of template specializations iterate over the arguments and
  // fully qualify them as well.
  if (isa<const TemplateSpecializationType>(QT.getTypePtr()) ||
      isa<const RecordType>(QT.getTypePtr())) {
    // We are asked to fully qualify and we have a Record Type (which
    // may point to a template specialization) or Template
    // Specialization Type. We need to fully qualify their arguments.

    const Type *TypePtr = getFullyQualifiedTemplateType(
        Ctx, QT.getTypePtr(), WithGlobalNsPrefix);
    QT = QualType(TypePtr, 0);
  }
  if (Prefix || Keyword != ETK_None) {
    QT = Ctx.getElaboratedType(Keyword, Prefix, QT);
  }
  QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
  return QT;
}

std::string getFullyQualifiedName(QualType QT,
                                  const ASTContext &Ctx,
                                  bool WithGlobalNsPrefix) {
  PrintingPolicy Policy(Ctx.getPrintingPolicy());
  Policy.SuppressScope = false;
  Policy.AnonymousTagLocations = false;
  Policy.PolishForDeclaration = true;
  Policy.SuppressUnwrittenScope = true;
  QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
  return FQQT.getAsString(Policy);
}

}  // end namespace TypeName
}  // end namespace clang