llvm-project/clang/lib/AST/DeclPrinter.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1778 lines
55 KiB
C++
Raw Normal View History

//===--- DeclPrinter.cpp - Printing implementation for Decl ASTs ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the Decl::print method, which pretty prints the
// AST back out to C/Objective-C/C++/Objective-C++ code.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/Expr.h"
Rework base and member initialization in constructors, with several (necessarily simultaneous) changes: - CXXBaseOrMemberInitializer now contains only a single initializer rather than a set of initialiation arguments + a constructor. The single initializer covers all aspects of initialization, including constructor calls as necessary but also cleanup of temporaries created by the initializer (which we never handled before!). - Rework + simplify code generation for CXXBaseOrMemberInitializers, since we can now just emit the initializer as an initializer. - Switched base and member initialization over to the new initialization code (InitializationSequence), so that it - Improved diagnostics for the new initialization code when initializing bases and members, to match the diagnostics produced by the previous (special-purpose) code. - Simplify the representation of type-checked constructor initializers in templates; instead of keeping the fully-type-checked AST, which is rather hard to undo at template instantiation time, throw away the type-checked AST and store the raw expressions in the AST. This simplifies instantiation, but loses a little but of information in the AST. - When type-checking implicit base or member initializers within a dependent context, don't add the generated initializers into the AST, because they'll look like they were explicit. - Record in CXXConstructExpr when the constructor call is to initialize a base class, so that CodeGen does not have to infer it from context. This ensures that we call the right kind of constructor. There are also a few "opportunity" fixes here that were needed to not regress, for example: - Diagnose default-initialization of a const-qualified class that does not have a user-declared default constructor. We had this diagnostic specifically for bases and members, but missed it for variables. That's fixed now. - When defining the implicit constructors, destructor, and copy-assignment operator, set the CurContext to that constructor when we're defining the body. llvm-svn: 94952
2010-01-31 17:12:51 +08:00
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/Module.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
namespace {
class DeclPrinter : public DeclVisitor<DeclPrinter> {
raw_ostream &Out;
PrintingPolicy Policy;
const ASTContext &Context;
unsigned Indentation;
bool PrintInstantiation;
raw_ostream& Indent() { return Indent(Indentation); }
raw_ostream& Indent(unsigned Indentation);
void ProcessDeclGroup(SmallVectorImpl<Decl*>& Decls);
void Print(AccessSpecifier AS);
void PrintConstructorInitializers(CXXConstructorDecl *CDecl,
std::string &Proto);
/// Print an Objective-C method type in parentheses.
///
/// \param Quals The Objective-C declaration qualifiers.
/// \param T The type to print.
void PrintObjCMethodType(ASTContext &Ctx, Decl::ObjCDeclQualifier Quals,
QualType T);
void PrintObjCTypeParams(ObjCTypeParamList *Params);
public:
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
const ASTContext &Context, unsigned Indentation = 0,
bool PrintInstantiation = false)
: Out(Out), Policy(Policy), Context(Context), Indentation(Indentation),
PrintInstantiation(PrintInstantiation) {}
void VisitDeclContext(DeclContext *DC, bool Indent = true);
void VisitTranslationUnitDecl(TranslationUnitDecl *D);
void VisitTypedefDecl(TypedefDecl *D);
void VisitTypeAliasDecl(TypeAliasDecl *D);
void VisitEnumDecl(EnumDecl *D);
void VisitRecordDecl(RecordDecl *D);
void VisitEnumConstantDecl(EnumConstantDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
void VisitFriendDecl(FriendDecl *D);
void VisitFieldDecl(FieldDecl *D);
void VisitVarDecl(VarDecl *D);
void VisitLabelDecl(LabelDecl *D);
void VisitParmVarDecl(ParmVarDecl *D);
void VisitFileScopeAsmDecl(FileScopeAsmDecl *D);
void VisitImportDecl(ImportDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
void VisitNamespaceDecl(NamespaceDecl *D);
void VisitUsingDirectiveDecl(UsingDirectiveDecl *D);
void VisitNamespaceAliasDecl(NamespaceAliasDecl *D);
void VisitCXXRecordDecl(CXXRecordDecl *D);
void VisitLinkageSpecDecl(LinkageSpecDecl *D);
void VisitTemplateDecl(const TemplateDecl *D);
void VisitFunctionTemplateDecl(FunctionTemplateDecl *D);
void VisitClassTemplateDecl(ClassTemplateDecl *D);
void VisitClassTemplateSpecializationDecl(
ClassTemplateSpecializationDecl *D);
void VisitClassTemplatePartialSpecializationDecl(
ClassTemplatePartialSpecializationDecl *D);
void VisitObjCMethodDecl(ObjCMethodDecl *D);
void VisitObjCImplementationDecl(ObjCImplementationDecl *D);
void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D);
void VisitObjCProtocolDecl(ObjCProtocolDecl *D);
void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D);
void VisitObjCCategoryDecl(ObjCCategoryDecl *D);
void VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *D);
void VisitObjCPropertyDecl(ObjCPropertyDecl *D);
void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D);
void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
void VisitUsingDecl(UsingDecl *D);
void VisitUsingEnumDecl(UsingEnumDecl *D);
void VisitUsingShadowDecl(UsingShadowDecl *D);
void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D);
void VisitOMPAllocateDecl(OMPAllocateDecl *D);
void VisitOMPRequiresDecl(OMPRequiresDecl *D);
void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D);
void VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D);
void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D);
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP);
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP);
void printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW = false);
void printTemplateArguments(llvm::ArrayRef<TemplateArgument> Args,
const TemplateParameterList *Params);
void printTemplateArguments(llvm::ArrayRef<TemplateArgumentLoc> Args,
const TemplateParameterList *Params);
void prettyPrintAttributes(Decl *D);
void prettyPrintPragmas(Decl *D);
void printDeclType(QualType T, StringRef DeclName, bool Pack = false);
};
}
void Decl::print(raw_ostream &Out, unsigned Indentation,
bool PrintInstantiation) const {
print(Out, getASTContext().getPrintingPolicy(), Indentation, PrintInstantiation);
}
void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy,
unsigned Indentation, bool PrintInstantiation) const {
DeclPrinter Printer(Out, Policy, getASTContext(), Indentation,
PrintInstantiation);
2009-09-27 05:58:53 +08:00
Printer.Visit(const_cast<Decl*>(this));
}
void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
bool OmitTemplateKW) const {
print(Out, Context, Context.getPrintingPolicy(), OmitTemplateKW);
}
void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
const PrintingPolicy &Policy,
bool OmitTemplateKW) const {
DeclPrinter Printer(Out, Policy, Context);
Printer.printTemplateParameters(this, OmitTemplateKW);
}
static QualType GetBaseType(QualType T) {
// FIXME: This should be on the Type class!
QualType BaseType = T;
while (!BaseType->isSpecifierType()) {
if (const PointerType *PTy = BaseType->getAs<PointerType>())
BaseType = PTy->getPointeeType();
else if (const ObjCObjectPointerType *OPT =
BaseType->getAs<ObjCObjectPointerType>())
BaseType = OPT->getPointeeType();
else if (const BlockPointerType *BPy = BaseType->getAs<BlockPointerType>())
BaseType = BPy->getPointeeType();
else if (const ArrayType *ATy = dyn_cast<ArrayType>(BaseType))
BaseType = ATy->getElementType();
else if (const FunctionType *FTy = BaseType->getAs<FunctionType>())
BaseType = FTy->getReturnType();
else if (const VectorType *VTy = BaseType->getAs<VectorType>())
BaseType = VTy->getElementType();
else if (const ReferenceType *RTy = BaseType->getAs<ReferenceType>())
BaseType = RTy->getPointeeType();
else if (const AutoType *ATy = BaseType->getAs<AutoType>())
BaseType = ATy->getDeducedType();
else if (const ParenType *PTy = BaseType->getAs<ParenType>())
BaseType = PTy->desugar();
else
// This must be a syntax error.
break;
}
return BaseType;
}
static QualType getDeclType(Decl* D) {
if (TypedefNameDecl* TDD = dyn_cast<TypedefNameDecl>(D))
return TDD->getUnderlyingType();
if (ValueDecl* VD = dyn_cast<ValueDecl>(D))
return VD->getType();
return QualType();
}
void Decl::printGroup(Decl** Begin, unsigned NumDecls,
raw_ostream &Out, const PrintingPolicy &Policy,
unsigned Indentation) {
if (NumDecls == 1) {
(*Begin)->print(Out, Policy, Indentation);
return;
}
Decl** End = Begin + NumDecls;
TagDecl* TD = dyn_cast<TagDecl>(*Begin);
if (TD)
++Begin;
PrintingPolicy SubPolicy(Policy);
bool isFirst = true;
for ( ; Begin != End; ++Begin) {
if (isFirst) {
if(TD)
SubPolicy.IncludeTagDefinition = true;
SubPolicy.SuppressSpecifiers = false;
isFirst = false;
} else {
if (!isFirst) Out << ", ";
SubPolicy.IncludeTagDefinition = false;
SubPolicy.SuppressSpecifiers = true;
}
(*Begin)->print(Out, SubPolicy, Indentation);
}
}
LLVM_DUMP_METHOD void DeclContext::dumpDeclContext() const {
2009-12-10 01:27:46 +08:00
// Get the translation unit
const DeclContext *DC = this;
while (!DC->isTranslationUnit())
DC = DC->getParent();
2009-12-10 01:27:46 +08:00
ASTContext &Ctx = cast<TranslationUnitDecl>(DC)->getASTContext();
DeclPrinter Printer(llvm::errs(), Ctx.getPrintingPolicy(), Ctx, 0);
2009-12-10 01:27:46 +08:00
Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false);
}
raw_ostream& DeclPrinter::Indent(unsigned Indentation) {
for (unsigned i = 0; i != Indentation; ++i)
Out << " ";
return Out;
}
void DeclPrinter::prettyPrintAttributes(Decl *D) {
if (Policy.PolishForDeclaration)
return;
if (D->hasAttrs()) {
AttrVec &Attrs = D->getAttrs();
for (auto *A : Attrs) {
if (A->isInherited() || A->isImplicit())
[AST] Print correct tag decl for tag specifier For example, given: void fn() { struct T *p0; struct T { int i; } *p1; } -ast-print produced: void fn() { struct T { int i; } *p0; struct T { int i; } *p1; } Compiling that fails with a redefinition error. Given: void fn() { struct T *p0; struct __attribute__((deprecated)) T *p1; } -ast-print dropped the attribute. Details: For a tag specifier (that is, struct/union/class/enum used as a type specifier in a declaration) that was also a tag declaration (that is, first occurrence of the tag) or tag redeclaration (that is, later occurrence that specifies attributes or a member list), clang printed the tag specifier as either (1) the full tag definition if one existed, or (2) the first tag declaration otherwise. Redefinition errors were sometimes introduced, as in the first example above. Even when that was impossible because no member list was ever specified, attributes were sometimes lost, thus changing semantics and diagnostics, as in the second example above. This patch fixes a major culprit for these problems. It does so by creating an ElaboratedType with a new OwnedDecl member wherever an occurrence of a tag type is a (re)declaration of that tag type. PrintingPolicy's IncludeTagDefinition used to trigger printing of the member list, attributes, etc. for a tag specifier by using a tag (re)declaration selected as described above. Now, it triggers the same thing except it uses the tag (re)declaration stored in the OwnedDecl. Of course, other tooling can now make use of the new OwnedDecl as well. Also, to be more faithful to the original source, this patch suppresses printing of attributes inherited from previous declarations. Reviewed by: rsmith, aaron.ballman Differential Revision: https://reviews.llvm.org/D45463 llvm-svn: 332281
2018-05-15 03:36:45 +08:00
continue;
switch (A->getKind()) {
#define ATTR(X)
#define PRAGMA_SPELLING_ATTR(X) case attr::X:
#include "clang/Basic/AttrList.inc"
break;
default:
A->printPretty(Out, Policy);
break;
}
}
}
}
void DeclPrinter::prettyPrintPragmas(Decl *D) {
if (Policy.PolishForDeclaration)
return;
if (D->hasAttrs()) {
AttrVec &Attrs = D->getAttrs();
for (auto *A : Attrs) {
switch (A->getKind()) {
#define ATTR(X)
#define PRAGMA_SPELLING_ATTR(X) case attr::X:
#include "clang/Basic/AttrList.inc"
A->printPretty(Out, Policy);
Indent();
break;
default:
break;
}
}
}
}
void DeclPrinter::printDeclType(QualType T, StringRef DeclName, bool Pack) {
// Normally, a PackExpansionType is written as T[3]... (for instance, as a
// template argument), but if it is the type of a declaration, the ellipsis
// is placed before the name being declared.
if (auto *PET = T->getAs<PackExpansionType>()) {
Pack = true;
T = PET->getPattern();
}
T.print(Out, Policy, (Pack ? "..." : "") + DeclName, Indentation);
}
void DeclPrinter::ProcessDeclGroup(SmallVectorImpl<Decl*>& Decls) {
this->Indent();
Decl::printGroup(Decls.data(), Decls.size(), Out, Policy, Indentation);
Out << ";\n";
Decls.clear();
}
void DeclPrinter::Print(AccessSpecifier AS) {
const auto AccessSpelling = getAccessSpelling(AS);
if (AccessSpelling.empty())
llvm_unreachable("No access specifier!");
Out << AccessSpelling;
}
void DeclPrinter::PrintConstructorInitializers(CXXConstructorDecl *CDecl,
std::string &Proto) {
bool HasInitializerList = false;
for (const auto *BMInitializer : CDecl->inits()) {
if (BMInitializer->isInClassMemberInitializer())
continue;
if (!HasInitializerList) {
Proto += " : ";
Out << Proto;
Proto.clear();
HasInitializerList = true;
} else
Out << ", ";
if (BMInitializer->isAnyMemberInitializer()) {
FieldDecl *FD = BMInitializer->getAnyMember();
Out << *FD;
} else {
Out << QualType(BMInitializer->getBaseClass(), 0).getAsString(Policy);
}
Out << "(";
if (!BMInitializer->getInit()) {
// Nothing to print
} else {
Expr *Init = BMInitializer->getInit();
if (ExprWithCleanups *Tmp = dyn_cast<ExprWithCleanups>(Init))
Init = Tmp->getSubExpr();
Init = Init->IgnoreParens();
Expr *SimpleInit = nullptr;
Expr **Args = nullptr;
unsigned NumArgs = 0;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
} else if (CXXConstructExpr *Construct =
dyn_cast<CXXConstructExpr>(Init)) {
Args = Construct->getArgs();
NumArgs = Construct->getNumArgs();
} else
SimpleInit = Init;
if (SimpleInit)
SimpleInit->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
else {
for (unsigned I = 0; I != NumArgs; ++I) {
assert(Args[I] != nullptr && "Expected non-null Expr");
if (isa<CXXDefaultArgExpr>(Args[I]))
break;
if (I)
Out << ", ";
Args[I]->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
}
}
}
Out << ")";
if (BMInitializer->isPackExpansion())
Out << "...";
}
}
//----------------------------------------------------------------------------
// Common C declarations
//----------------------------------------------------------------------------
void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
if (Policy.TerseOutput)
return;
if (Indent)
Indentation += Policy.Indentation;
SmallVector<Decl*, 2> Decls;
for (DeclContext::decl_iterator D = DC->decls_begin(), DEnd = DC->decls_end();
D != DEnd; ++D) {
// Don't print ObjCIvarDecls, as they are printed when visiting the
// containing ObjCInterfaceDecl.
if (isa<ObjCIvarDecl>(*D))
continue;
// Skip over implicit declarations in pretty-printing mode.
if (D->isImplicit())
continue;
// Don't print implicit specializations, as they are printed when visiting
// corresponding templates.
if (auto FD = dyn_cast<FunctionDecl>(*D))
if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation &&
!isa<ClassTemplateSpecializationDecl>(DC))
continue;
// The next bits of code handle stuff like "struct {int x;} a,b"; we're
// forced to merge the declarations because there's no other way to
// refer to the struct in question. When that struct is named instead, we
// also need to merge to avoid splitting off a stand-alone struct
// declaration that produces the warning ext_no_declarators in some
// contexts.
//
// This limited merging is safe without a bunch of other checks because it
// only merges declarations directly referring to the tag, not typedefs.
//
// Check whether the current declaration should be grouped with a previous
// non-free-standing tag declaration.
QualType CurDeclType = getDeclType(*D);
if (!Decls.empty() && !CurDeclType.isNull()) {
QualType BaseType = GetBaseType(CurDeclType);
if (!BaseType.isNull() && isa<ElaboratedType>(BaseType) &&
cast<ElaboratedType>(BaseType)->getOwnedTagDecl() == Decls[0]) {
Decls.push_back(*D);
continue;
}
}
// If we have a merged group waiting to be handled, handle it now.
if (!Decls.empty())
ProcessDeclGroup(Decls);
// If the current declaration is not a free standing declaration, save it
// so we can merge it with the subsequent declaration(s) using it.
if (isa<TagDecl>(*D) && !cast<TagDecl>(*D)->isFreeStanding()) {
Decls.push_back(*D);
continue;
}
if (isa<AccessSpecDecl>(*D)) {
Indentation -= Policy.Indentation;
this->Indent();
Print(D->getAccess());
Out << ":\n";
Indentation += Policy.Indentation;
continue;
}
this->Indent();
Visit(*D);
// FIXME: Need to be able to tell the DeclPrinter when
const char *Terminator = nullptr;
if (isa<OMPThreadPrivateDecl>(*D) || isa<OMPDeclareReductionDecl>(*D) ||
isa<OMPDeclareMapperDecl>(*D) || isa<OMPRequiresDecl>(*D) ||
isa<OMPAllocateDecl>(*D))
Terminator = nullptr;
else if (isa<ObjCMethodDecl>(*D) && cast<ObjCMethodDecl>(*D)->hasBody())
Terminator = nullptr;
else if (auto FD = dyn_cast<FunctionDecl>(*D)) {
if (FD->isThisDeclarationADefinition())
Terminator = nullptr;
else
Terminator = ";";
} else if (auto TD = dyn_cast<FunctionTemplateDecl>(*D)) {
if (TD->getTemplatedDecl()->isThisDeclarationADefinition())
Terminator = nullptr;
else
Terminator = ";";
} else if (isa<NamespaceDecl>(*D) || isa<LinkageSpecDecl>(*D) ||
isa<ObjCImplementationDecl>(*D) ||
isa<ObjCInterfaceDecl>(*D) ||
isa<ObjCProtocolDecl>(*D) ||
isa<ObjCCategoryImplDecl>(*D) ||
isa<ObjCCategoryDecl>(*D))
Terminator = nullptr;
else if (isa<EnumConstantDecl>(*D)) {
DeclContext::decl_iterator Next = D;
++Next;
if (Next != DEnd)
Terminator = ",";
} else
Terminator = ";";
if (Terminator)
Out << Terminator;
if (!Policy.TerseOutput &&
((isa<FunctionDecl>(*D) &&
cast<FunctionDecl>(*D)->doesThisDeclarationHaveABody()) ||
(isa<FunctionTemplateDecl>(*D) &&
cast<FunctionTemplateDecl>(*D)->getTemplatedDecl()->doesThisDeclarationHaveABody())))
; // StmtPrinter already added '\n' after CompoundStmt.
else
Out << "\n";
// Declare target attribute is special one, natural spelling for the pragma
// assumes "ending" construct so print it here.
if (D->hasAttr<OMPDeclareTargetDeclAttr>())
Out << "#pragma omp end declare target\n";
}
if (!Decls.empty())
ProcessDeclGroup(Decls);
if (Indent)
Indentation -= Policy.Indentation;
}
void DeclPrinter::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
VisitDeclContext(D, false);
}
void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) {
if (!Policy.SuppressSpecifiers) {
Out << "typedef ";
if (D->isModulePrivate())
Out << "__module_private__ ";
}
QualType Ty = D->getTypeSourceInfo()->getType();
Ty.print(Out, Policy, D->getName(), Indentation);
prettyPrintAttributes(D);
}
void DeclPrinter::VisitTypeAliasDecl(TypeAliasDecl *D) {
Out << "using " << *D;
prettyPrintAttributes(D);
Out << " = " << D->getTypeSourceInfo()->getType().getAsString(Policy);
}
void DeclPrinter::VisitEnumDecl(EnumDecl *D) {
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
Out << "enum";
if (D->isScoped()) {
if (D->isScopedUsingClassTag())
Out << " class";
else
Out << " struct";
}
prettyPrintAttributes(D);
if (D->getDeclName())
Out << ' ' << D->getDeclName();
if (D->isFixed())
Out << " : " << D->getIntegerType().stream(Policy);
if (D->isCompleteDefinition()) {
Out << " {\n";
VisitDeclContext(D);
Indent() << "}";
}
}
void DeclPrinter::VisitRecordDecl(RecordDecl *D) {
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
Out << D->getKindName();
prettyPrintAttributes(D);
if (D->getIdentifier())
Out << ' ' << *D;
if (D->isCompleteDefinition()) {
Out << " {\n";
VisitDeclContext(D);
Indent() << "}";
}
}
void DeclPrinter::VisitEnumConstantDecl(EnumConstantDecl *D) {
Out << *D;
prettyPrintAttributes(D);
if (Expr *Init = D->getInitExpr()) {
Out << " = ";
Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
}
}
static void printExplicitSpecifier(ExplicitSpecifier ES, llvm::raw_ostream &Out,
PrintingPolicy &Policy, unsigned Indentation,
const ASTContext &Context) {
std::string Proto = "explicit";
llvm::raw_string_ostream EOut(Proto);
if (ES.getExpr()) {
EOut << "(";
ES.getExpr()->printPretty(EOut, nullptr, Policy, Indentation, "\n",
&Context);
EOut << ")";
}
EOut << " ";
EOut.flush();
Out << Proto;
}
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (!D->getDescribedFunctionTemplate() &&
!D->isFunctionTemplateSpecialization())
prettyPrintPragmas(D);
if (D->isFunctionTemplateSpecialization())
Out << "template<> ";
else if (!D->getDescribedFunctionTemplate()) {
for (unsigned I = 0, NumTemplateParams = D->getNumTemplateParameterLists();
I < NumTemplateParams; ++I)
printTemplateParameters(D->getTemplateParameterList(I));
}
CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
if (!Policy.SuppressSpecifiers) {
switch (D->getStorageClass()) {
case SC_None: break;
case SC_Extern: Out << "extern "; break;
case SC_Static: Out << "static "; break;
case SC_PrivateExtern: Out << "__private_extern__ "; break;
case SC_Auto: case SC_Register:
llvm_unreachable("invalid for functions");
}
if (D->isInlineSpecified()) Out << "inline ";
if (D->isVirtualAsWritten()) Out << "virtual ";
if (D->isModulePrivate()) Out << "__module_private__ ";
if (D->isConstexprSpecified() && !D->isExplicitlyDefaulted())
Out << "constexpr ";
if (D->isConsteval()) Out << "consteval ";
ExplicitSpecifier ExplicitSpec = ExplicitSpecifier::getFromDecl(D);
if (ExplicitSpec.isSpecified())
printExplicitSpecifier(ExplicitSpec, Out, Policy, Indentation, Context);
}
PrintingPolicy SubPolicy(Policy);
SubPolicy.SuppressSpecifiers = false;
std::string Proto;
if (Policy.FullyQualifiedName) {
Proto += D->getQualifiedNameAsString();
} else {
llvm::raw_string_ostream OS(Proto);
if (!Policy.SuppressScope) {
if (const NestedNameSpecifier *NS = D->getQualifier()) {
NS->print(OS, Policy);
}
}
D->getNameInfo().printName(OS, Policy);
}
if (GuideDecl)
Proto = GuideDecl->getDeducedTemplate()->getDeclName().getAsString();
if (D->isFunctionTemplateSpecialization()) {
llvm::raw_string_ostream POut(Proto);
DeclPrinter TArgPrinter(POut, SubPolicy, Context, Indentation);
const auto *TArgAsWritten = D->getTemplateSpecializationArgsAsWritten();
if (TArgAsWritten && !Policy.PrintCanonicalTypes)
TArgPrinter.printTemplateArguments(TArgAsWritten->arguments(), nullptr);
else if (const TemplateArgumentList *TArgs =
D->getTemplateSpecializationArgs())
TArgPrinter.printTemplateArguments(TArgs->asArray(), nullptr);
}
QualType Ty = D->getType();
while (const ParenType *PT = dyn_cast<ParenType>(Ty)) {
Proto = '(' + Proto + ')';
Ty = PT->getInnerType();
}
if (const FunctionType *AFT = Ty->getAs<FunctionType>()) {
const FunctionProtoType *FT = nullptr;
if (D->hasWrittenPrototype())
FT = dyn_cast<FunctionProtoType>(AFT);
Proto += "(";
if (FT) {
llvm::raw_string_ostream POut(Proto);
DeclPrinter ParamPrinter(POut, SubPolicy, Context, Indentation);
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
if (i) POut << ", ";
ParamPrinter.VisitParmVarDecl(D->getParamDecl(i));
}
if (FT->isVariadic()) {
if (D->getNumParams()) POut << ", ";
POut << "...";
}
} else if (D->doesThisDeclarationHaveABody() && !D->hasPrototype()) {
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
if (i)
Proto += ", ";
Proto += D->getParamDecl(i)->getNameAsString();
}
}
Proto += ")";
if (FT) {
if (FT->isConst())
Proto += " const";
if (FT->isVolatile())
Proto += " volatile";
if (FT->isRestrict())
Proto += " restrict";
switch (FT->getRefQualifier()) {
case RQ_None:
break;
case RQ_LValue:
Proto += " &";
break;
case RQ_RValue:
Proto += " &&";
break;
}
}
if (FT && FT->hasDynamicExceptionSpec()) {
Proto += " throw(";
if (FT->getExceptionSpecType() == EST_MSAny)
Proto += "...";
else
for (unsigned I = 0, N = FT->getNumExceptions(); I != N; ++I) {
if (I)
Proto += ", ";
Proto += FT->getExceptionType(I).getAsString(SubPolicy);
}
Proto += ")";
} else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) {
Proto += " noexcept";
if (isComputedNoexcept(FT->getExceptionSpecType())) {
Proto += "(";
llvm::raw_string_ostream EOut(Proto);
FT->getNoexceptExpr()->printPretty(EOut, nullptr, SubPolicy,
Indentation, "\n", &Context);
EOut.flush();
Proto += ")";
}
}
if (CDecl) {
if (!Policy.TerseOutput)
PrintConstructorInitializers(CDecl, Proto);
} else if (!ConversionDecl && !isa<CXXDestructorDecl>(D)) {
if (FT && FT->hasTrailingReturn()) {
if (!GuideDecl)
Out << "auto ";
Out << Proto << " -> ";
Proto.clear();
}
AFT->getReturnType().print(Out, Policy, Proto);
Proto.clear();
}
Out << Proto;
if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) {
Out << " requires ";
TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation,
"\n", &Context);
}
} else {
Ty.print(Out, Policy, Proto);
}
prettyPrintAttributes(D);
if (D->isPure())
Out << " = 0";
else if (D->isDeletedAsWritten())
Out << " = delete";
else if (D->isExplicitlyDefaulted())
Out << " = default";
else if (D->doesThisDeclarationHaveABody()) {
if (!Policy.TerseOutput) {
if (!D->hasPrototype() && D->getNumParams()) {
// This is a K&R function definition, so we need to print the
// parameters.
Out << '\n';
DeclPrinter ParamPrinter(Out, SubPolicy, Context, Indentation);
Indentation += Policy.Indentation;
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
Indent();
ParamPrinter.VisitParmVarDecl(D->getParamDecl(i));
Out << ";\n";
}
Indentation -= Policy.Indentation;
}
if (D->getBody())
D->getBody()->printPrettyControlled(Out, nullptr, SubPolicy, Indentation, "\n",
&Context);
} else {
if (!Policy.TerseOutput && isa<CXXConstructorDecl>(*D))
Out << " {}";
}
}
}
void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
if (TypeSourceInfo *TSI = D->getFriendType()) {
unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
for (unsigned i = 0; i < NumTPLists; ++i)
printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
Out << "friend ";
Out << " " << TSI->getType().getAsString(Policy);
}
else if (FunctionDecl *FD =
dyn_cast<FunctionDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitFunctionDecl(FD);
}
else if (FunctionTemplateDecl *FTD =
dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitFunctionTemplateDecl(FTD);
}
else if (ClassTemplateDecl *CTD =
dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitRedeclarableTemplateDecl(CTD);
}
}
void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
// FIXME: add printing of pragma attributes if required.
if (!Policy.SuppressSpecifiers && D->isMutable())
Out << "mutable ";
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
Out << D->getASTContext().getUnqualifiedObjCPointerType(D->getType()).
stream(Policy, D->getName(), Indentation);
if (D->isBitField()) {
Out << " : ";
D->getBitWidth()->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
}
Expr *Init = D->getInClassInitializer();
if (!Policy.SuppressInitializers && Init) {
if (D->getInClassInitStyle() == ICIS_ListInit)
Out << " ";
else
Out << " = ";
Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
}
prettyPrintAttributes(D);
}
void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
Out << *D << ":";
}
void DeclPrinter::VisitVarDecl(VarDecl *D) {
prettyPrintPragmas(D);
QualType T = D->getTypeSourceInfo()
? D->getTypeSourceInfo()->getType()
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
if (!Policy.SuppressSpecifiers) {
StorageClass SC = D->getStorageClass();
if (SC != SC_None)
Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
switch (D->getTSCSpec()) {
case TSCS_unspecified:
break;
case TSCS___thread:
Out << "__thread ";
break;
case TSCS__Thread_local:
Out << "_Thread_local ";
break;
case TSCS_thread_local:
Out << "thread_local ";
break;
}
if (D->isModulePrivate())
Out << "__module_private__ ";
if (D->isConstexpr()) {
Out << "constexpr ";
T.removeLocalConst();
}
}
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
D->getIdentifier())
? D->getIdentifier()->deuglifiedName()
: D->getName());
Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
Represent C++ direct initializers as ParenListExprs before semantic analysis instead of having a special-purpose function. - ActOnCXXDirectInitializer, which was mostly duplication of AddInitializerToDecl (leading e.g. to PR10620, which Eli fixed a few days ago), is dropped completely. - MultiInitializer, which was an ugly hack I added, is dropped again. - We now have the infrastructure in place to distinguish between int x = {1}; int x({1}); int x{1}; -- VarDecl now has getInitStyle(), which indicates which of the above was used. -- CXXConstructExpr now has a flag to indicate that it represents list- initialization, although this is not yet used. - InstantiateInitializer was renamed to SubstInitializer and simplified. - ActOnParenOrParenListExpr has been replaced by ActOnParenListExpr, which always produces a ParenListExpr. Placed that so far failed to convert that back to a ParenExpr containing comma operators have been fixed. I'm pretty sure I could have made a crashing test case before this. The end result is a (I hope) considerably cleaner design of initializers. More importantly, the fact that I can now distinguish between the various initialization kinds means that I can get the tricky generalized initializer test cases Johannes Schaub supplied to work. (This is not yet done.) This commit passed self-host, with the resulting compiler passing the tests. I hope it doesn't break more complicated code. It's a pretty big change, but one that I feel is necessary. llvm-svn: 150318
2012-02-12 07:51:47 +08:00
bool ImplicitInit = false;
if (CXXConstructExpr *Construct =
dyn_cast<CXXConstructExpr>(Init->IgnoreImplicit())) {
if (D->getInitStyle() == VarDecl::CallInit &&
!Construct->isListInitialization()) {
ImplicitInit = Construct->getNumArgs() == 0 ||
Construct->getArg(0)->isDefaultArgument();
}
}
Represent C++ direct initializers as ParenListExprs before semantic analysis instead of having a special-purpose function. - ActOnCXXDirectInitializer, which was mostly duplication of AddInitializerToDecl (leading e.g. to PR10620, which Eli fixed a few days ago), is dropped completely. - MultiInitializer, which was an ugly hack I added, is dropped again. - We now have the infrastructure in place to distinguish between int x = {1}; int x({1}); int x{1}; -- VarDecl now has getInitStyle(), which indicates which of the above was used. -- CXXConstructExpr now has a flag to indicate that it represents list- initialization, although this is not yet used. - InstantiateInitializer was renamed to SubstInitializer and simplified. - ActOnParenOrParenListExpr has been replaced by ActOnParenListExpr, which always produces a ParenListExpr. Placed that so far failed to convert that back to a ParenExpr containing comma operators have been fixed. I'm pretty sure I could have made a crashing test case before this. The end result is a (I hope) considerably cleaner design of initializers. More importantly, the fact that I can now distinguish between the various initialization kinds means that I can get the tricky generalized initializer test cases Johannes Schaub supplied to work. (This is not yet done.) This commit passed self-host, with the resulting compiler passing the tests. I hope it doesn't break more complicated code. It's a pretty big change, but one that I feel is necessary. llvm-svn: 150318
2012-02-12 07:51:47 +08:00
if (!ImplicitInit) {
if ((D->getInitStyle() == VarDecl::CallInit) && !isa<ParenListExpr>(Init))
Represent C++ direct initializers as ParenListExprs before semantic analysis instead of having a special-purpose function. - ActOnCXXDirectInitializer, which was mostly duplication of AddInitializerToDecl (leading e.g. to PR10620, which Eli fixed a few days ago), is dropped completely. - MultiInitializer, which was an ugly hack I added, is dropped again. - We now have the infrastructure in place to distinguish between int x = {1}; int x({1}); int x{1}; -- VarDecl now has getInitStyle(), which indicates which of the above was used. -- CXXConstructExpr now has a flag to indicate that it represents list- initialization, although this is not yet used. - InstantiateInitializer was renamed to SubstInitializer and simplified. - ActOnParenOrParenListExpr has been replaced by ActOnParenListExpr, which always produces a ParenListExpr. Placed that so far failed to convert that back to a ParenExpr containing comma operators have been fixed. I'm pretty sure I could have made a crashing test case before this. The end result is a (I hope) considerably cleaner design of initializers. More importantly, the fact that I can now distinguish between the various initialization kinds means that I can get the tricky generalized initializer test cases Johannes Schaub supplied to work. (This is not yet done.) This commit passed self-host, with the resulting compiler passing the tests. I hope it doesn't break more complicated code. It's a pretty big change, but one that I feel is necessary. llvm-svn: 150318
2012-02-12 07:51:47 +08:00
Out << "(";
else if (D->getInitStyle() == VarDecl::CInit) {
Out << " = ";
}
PrintingPolicy SubPolicy(Policy);
SubPolicy.SuppressSpecifiers = false;
SubPolicy.IncludeTagDefinition = false;
Init->printPretty(Out, nullptr, SubPolicy, Indentation, "\n", &Context);
if ((D->getInitStyle() == VarDecl::CallInit) && !isa<ParenListExpr>(Init))
Represent C++ direct initializers as ParenListExprs before semantic analysis instead of having a special-purpose function. - ActOnCXXDirectInitializer, which was mostly duplication of AddInitializerToDecl (leading e.g. to PR10620, which Eli fixed a few days ago), is dropped completely. - MultiInitializer, which was an ugly hack I added, is dropped again. - We now have the infrastructure in place to distinguish between int x = {1}; int x({1}); int x{1}; -- VarDecl now has getInitStyle(), which indicates which of the above was used. -- CXXConstructExpr now has a flag to indicate that it represents list- initialization, although this is not yet used. - InstantiateInitializer was renamed to SubstInitializer and simplified. - ActOnParenOrParenListExpr has been replaced by ActOnParenListExpr, which always produces a ParenListExpr. Placed that so far failed to convert that back to a ParenExpr containing comma operators have been fixed. I'm pretty sure I could have made a crashing test case before this. The end result is a (I hope) considerably cleaner design of initializers. More importantly, the fact that I can now distinguish between the various initialization kinds means that I can get the tricky generalized initializer test cases Johannes Schaub supplied to work. (This is not yet done.) This commit passed self-host, with the resulting compiler passing the tests. I hope it doesn't break more complicated code. It's a pretty big change, but one that I feel is necessary. llvm-svn: 150318
2012-02-12 07:51:47 +08:00
Out << ")";
}
}
prettyPrintAttributes(D);
}
void DeclPrinter::VisitParmVarDecl(ParmVarDecl *D) {
VisitVarDecl(D);
}
void DeclPrinter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) {
Out << "__asm (";
D->getAsmString()->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
Out << ")";
}
void DeclPrinter::VisitImportDecl(ImportDecl *D) {
Out << "@import " << D->getImportedModule()->getFullModuleName()
<< ";\n";
}
void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) {
Out << "static_assert(";
D->getAssertExpr()->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
if (StringLiteral *SL = D->getMessage()) {
Out << ", ";
SL->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
}
Out << ")";
}
//----------------------------------------------------------------------------
// C++ declarations
//----------------------------------------------------------------------------
void DeclPrinter::VisitNamespaceDecl(NamespaceDecl *D) {
if (D->isInline())
Out << "inline ";
Out << "namespace ";
if (D->getDeclName())
Out << D->getDeclName() << ' ';
Out << "{\n";
VisitDeclContext(D);
Indent() << "}";
}
void DeclPrinter::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
Out << "using namespace ";
if (D->getQualifier())
D->getQualifier()->print(Out, Policy);
Out << *D->getNominatedNamespaceAsWritten();
}
void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
Out << "namespace " << *D << " = ";
if (D->getQualifier())
D->getQualifier()->print(Out, Policy);
Out << *D->getAliasedNamespace();
}
void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) {
prettyPrintAttributes(D);
}
void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
// FIXME: add printing of pragma attributes if required.
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
Out << D->getKindName();
prettyPrintAttributes(D);
if (D->getIdentifier()) {
Out << ' ' << *D;
if (auto S = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
ArrayRef<TemplateArgument> Args = S->getTemplateArgs().asArray();
if (!Policy.PrintCanonicalTypes)
if (const auto* TSI = S->getTypeAsWritten())
if (const auto *TST =
dyn_cast<TemplateSpecializationType>(TSI->getType()))
Args = TST->template_arguments();
printTemplateArguments(
Args, S->getSpecializedTemplate()->getTemplateParameters());
}
}
if (D->isCompleteDefinition()) {
// Print the base classes
if (D->getNumBases()) {
Out << " : ";
for (CXXRecordDecl::base_class_iterator Base = D->bases_begin(),
BaseEnd = D->bases_end(); Base != BaseEnd; ++Base) {
if (Base != D->bases_begin())
Out << ", ";
if (Base->isVirtual())
Out << "virtual ";
AccessSpecifier AS = Base->getAccessSpecifierAsWritten();
if (AS != AS_none) {
Print(AS);
Out << " ";
}
Out << Base->getType().getAsString(Policy);
if (Base->isPackExpansion())
Out << "...";
}
}
// Print the class definition
// FIXME: Doesn't print access specifiers, e.g., "public:"
if (Policy.TerseOutput) {
Out << " {}";
} else {
Out << " {\n";
VisitDeclContext(D);
Indent() << "}";
}
}
}
void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
const char *l;
if (D->getLanguage() == LinkageSpecDecl::lang_c)
l = "C";
else {
assert(D->getLanguage() == LinkageSpecDecl::lang_cxx &&
"unknown language in linkage specification");
l = "C++";
}
Out << "extern \"" << l << "\" ";
if (D->hasBraces()) {
Out << "{\n";
VisitDeclContext(D);
Indent() << "}";
} else
Visit(*D->decls_begin());
}
void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW) {
assert(Params);
if (!OmitTemplateKW)
Out << "template ";
Out << '<';
bool NeedComma = false;
for (const Decl *Param : *Params) {
if (Param->isImplicit())
continue;
if (NeedComma)
Out << ", ";
else
NeedComma = true;
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
VisitTemplateTypeParmDecl(TTP);
} else if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
VisitNonTypeTemplateParmDecl(NTTP);
} else if (auto TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
VisitTemplateDecl(TTPD);
// FIXME: print the default argument, if present.
}
}
Out << '>';
if (!OmitTemplateKW)
Out << ' ';
}
void DeclPrinter::printTemplateArguments(ArrayRef<TemplateArgument> Args,
const TemplateParameterList *Params) {
Out << "<";
for (size_t I = 0, E = Args.size(); I < E; ++I) {
if (I)
Out << ", ";
if (!Params)
Args[I].print(Policy, Out, /*IncludeType*/ true);
else
DebugInfo: workaround for context-sensitive use of non-type-template-parameter integer suffixes There's a nuanced check about when to use suffixes on these integer non-type-template-parameters, but when rebuilding names for -gsimple-template-names there isn't enough data in the DWARF to determine when to use suffixes or not. So turn on suffixes always to make it easy to match up names in llvm-dwarfdump --verify. I /think/ if we correctly modelled auto non-type-template parameters maybe we could put suffixes only on those. But there's also some logic in Clang that puts the suffixes on overloaded functions - at least that's what the parameter says (see D77598 and printTemplateArguments "TemplOverloaded" parameter) - but I think maybe it's for anything that /can/ be overloaded, not necessarily only the things that are overloaded (the argument value is hardcoded at the various callsites, doesn't seem to depend on overload resolution/searching for overloaded functions). So maybe with "auto" modeled more accurately, and differentiating between function templates (always using type suffixes there) and class/variable templates (only using the suffix for "auto" types) we could correctly use integer type suffixes only in the minimal set of cases. But that seems all too much fuss, so let's just put integer type suffixes everywhere always in the debug info of integer non-type template parameters in template names. (more context: * https://reviews.llvm.org/D77598#inline-1057607 * https://groups.google.com/g/llvm-dev/c/ekLMllbLIZg/m/-dhJ0hO1AAAJ ) Differential Revision: https://reviews.llvm.org/D111477
2021-09-20 11:58:47 +08:00
Args[I].print(Policy, Out,
TemplateParameterList::shouldIncludeTypeForArgument(
Policy, Params, I));
}
Out << ">";
}
void DeclPrinter::printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
const TemplateParameterList *Params) {
Out << "<";
for (size_t I = 0, E = Args.size(); I < E; ++I) {
if (I)
Out << ", ";
if (!Params)
Args[I].getArgument().print(Policy, Out, /*IncludeType*/ true);
else
Args[I].getArgument().print(
Policy, Out,
DebugInfo: workaround for context-sensitive use of non-type-template-parameter integer suffixes There's a nuanced check about when to use suffixes on these integer non-type-template-parameters, but when rebuilding names for -gsimple-template-names there isn't enough data in the DWARF to determine when to use suffixes or not. So turn on suffixes always to make it easy to match up names in llvm-dwarfdump --verify. I /think/ if we correctly modelled auto non-type-template parameters maybe we could put suffixes only on those. But there's also some logic in Clang that puts the suffixes on overloaded functions - at least that's what the parameter says (see D77598 and printTemplateArguments "TemplOverloaded" parameter) - but I think maybe it's for anything that /can/ be overloaded, not necessarily only the things that are overloaded (the argument value is hardcoded at the various callsites, doesn't seem to depend on overload resolution/searching for overloaded functions). So maybe with "auto" modeled more accurately, and differentiating between function templates (always using type suffixes there) and class/variable templates (only using the suffix for "auto" types) we could correctly use integer type suffixes only in the minimal set of cases. But that seems all too much fuss, so let's just put integer type suffixes everywhere always in the debug info of integer non-type template parameters in template names. (more context: * https://reviews.llvm.org/D77598#inline-1057607 * https://groups.google.com/g/llvm-dev/c/ekLMllbLIZg/m/-dhJ0hO1AAAJ ) Differential Revision: https://reviews.llvm.org/D111477
2021-09-20 11:58:47 +08:00
TemplateParameterList::shouldIncludeTypeForArgument(Policy, Params,
I));
}
Out << ">";
}
void DeclPrinter::VisitTemplateDecl(const TemplateDecl *D) {
printTemplateParameters(D->getTemplateParameters());
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(D)) {
Out << "class";
if (TTP->isParameterPack())
Out << " ...";
else if (TTP->getDeclName())
Out << ' ';
if (TTP->getDeclName()) {
if (Policy.CleanUglifiedParameters && TTP->getIdentifier())
Out << TTP->getIdentifier()->deuglifiedName();
else
Out << TTP->getDeclName();
}
} else if (auto *TD = D->getTemplatedDecl())
Visit(TD);
else if (const auto *Concept = dyn_cast<ConceptDecl>(D)) {
Out << "concept " << Concept->getName() << " = " ;
Concept->getConstraintExpr()->printPretty(Out, nullptr, Policy, Indentation,
"\n", &Context);
}
}
void DeclPrinter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
prettyPrintPragmas(D->getTemplatedDecl());
// Print any leading template parameter lists.
if (const FunctionDecl *FD = D->getTemplatedDecl()) {
for (unsigned I = 0, NumTemplateParams = FD->getNumTemplateParameterLists();
I < NumTemplateParams; ++I)
printTemplateParameters(FD->getTemplateParameterList(I));
}
VisitRedeclarableTemplateDecl(D);
// Declare target attribute is special one, natural spelling for the pragma
// assumes "ending" construct so print it here.
if (D->getTemplatedDecl()->hasAttr<OMPDeclareTargetDeclAttr>())
Out << "#pragma omp end declare target\n";
// Never print "instantiations" for deduction guides (they don't really
// have them).
if (PrintInstantiation &&
!isa<CXXDeductionGuideDecl>(D->getTemplatedDecl())) {
FunctionDecl *PrevDecl = D->getTemplatedDecl();
const FunctionDecl *Def;
if (PrevDecl->isDefined(Def) && Def != PrevDecl)
return;
for (auto *I : D->specializations())
if (I->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) {
if (!PrevDecl->isThisDeclarationADefinition())
Out << ";\n";
Indent();
prettyPrintPragmas(I);
Visit(I);
}
}
}
void DeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
VisitRedeclarableTemplateDecl(D);
if (PrintInstantiation) {
for (auto *I : D->specializations())
if (I->getSpecializationKind() == TSK_ImplicitInstantiation) {
if (D->isThisDeclarationADefinition())
Out << ";";
Out << "\n";
Indent();
Visit(I);
}
}
}
void DeclPrinter::VisitClassTemplateSpecializationDecl(
ClassTemplateSpecializationDecl *D) {
Out << "template<> ";
VisitCXXRecordDecl(D);
}
void DeclPrinter::VisitClassTemplatePartialSpecializationDecl(
ClassTemplatePartialSpecializationDecl *D) {
printTemplateParameters(D->getTemplateParameters());
VisitCXXRecordDecl(D);
}
//----------------------------------------------------------------------------
// Objective-C declarations
//----------------------------------------------------------------------------
void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx,
Decl::ObjCDeclQualifier Quals,
QualType T) {
Out << '(';
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_In)
Out << "in ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Inout)
Out << "inout ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Out)
Out << "out ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Bycopy)
Out << "bycopy ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Byref)
Out << "byref ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Oneway)
Out << "oneway ";
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_CSNullability) {
if (auto nullability = AttributedType::stripOuterNullability(T))
Out << getNullabilitySpelling(*nullability, true) << ' ';
}
Out << Ctx.getUnqualifiedObjCPointerType(T).getAsString(Policy);
Out << ')';
}
void DeclPrinter::PrintObjCTypeParams(ObjCTypeParamList *Params) {
Out << "<";
unsigned First = true;
for (auto *Param : *Params) {
if (First) {
First = false;
} else {
Out << ", ";
}
switch (Param->getVariance()) {
case ObjCTypeParamVariance::Invariant:
break;
case ObjCTypeParamVariance::Covariant:
Out << "__covariant ";
break;
case ObjCTypeParamVariance::Contravariant:
Out << "__contravariant ";
break;
}
Out << Param->getDeclName();
if (Param->hasExplicitBound()) {
Out << " : " << Param->getUnderlyingType().getAsString(Policy);
}
}
Out << ">";
}
void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
if (OMD->isInstanceMethod())
Out << "- ";
else
Out << "+ ";
if (!OMD->getReturnType().isNull()) {
PrintObjCMethodType(OMD->getASTContext(), OMD->getObjCDeclQualifier(),
OMD->getReturnType());
}
std::string name = OMD->getSelector().getAsString();
std::string::size_type pos, lastPos = 0;
for (const auto *PI : OMD->parameters()) {
// FIXME: selector is missing here!
pos = name.find_first_of(':', lastPos);
if (lastPos != 0)
Out << " ";
Out << name.substr(lastPos, pos - lastPos) << ':';
PrintObjCMethodType(OMD->getASTContext(),
PI->getObjCDeclQualifier(),
PI->getType());
Out << *PI;
lastPos = pos + 1;
}
if (OMD->param_begin() == OMD->param_end())
Out << name;
if (OMD->isVariadic())
Out << ", ...";
prettyPrintAttributes(OMD);
if (OMD->getBody() && !Policy.TerseOutput) {
Out << ' ';
OMD->getBody()->printPretty(Out, nullptr, Policy, Indentation, "\n",
&Context);
}
else if (Policy.PolishForDeclaration)
Out << ';';
}
void DeclPrinter::VisitObjCImplementationDecl(ObjCImplementationDecl *OID) {
std::string I = OID->getNameAsString();
ObjCInterfaceDecl *SID = OID->getSuperClass();
bool eolnOut = false;
if (SID)
Out << "@implementation " << I << " : " << *SID;
else
Out << "@implementation " << I;
if (OID->ivar_size() > 0) {
Out << "{\n";
eolnOut = true;
Indentation += Policy.Indentation;
for (const auto *I : OID->ivars()) {
Indent() << I->getASTContext().getUnqualifiedObjCPointerType(I->getType()).
getAsString(Policy) << ' ' << *I << ";\n";
}
Indentation -= Policy.Indentation;
Out << "}\n";
}
else if (SID || (OID->decls_begin() != OID->decls_end())) {
Out << "\n";
eolnOut = true;
}
VisitDeclContext(OID, false);
if (!eolnOut)
Out << "\n";
Out << "@end";
}
void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) {
std::string I = OID->getNameAsString();
ObjCInterfaceDecl *SID = OID->getSuperClass();
if (!OID->isThisDeclarationADefinition()) {
Out << "@class " << I;
if (auto TypeParams = OID->getTypeParamListAsWritten()) {
PrintObjCTypeParams(TypeParams);
}
Out << ";";
return;
}
bool eolnOut = false;
Out << "@interface " << I;
if (auto TypeParams = OID->getTypeParamListAsWritten()) {
PrintObjCTypeParams(TypeParams);
}
if (SID)
Out << " : " << QualType(OID->getSuperClassType(), 0).getAsString(Policy);
// Protocols?
const ObjCList<ObjCProtocolDecl> &Protocols = OID->getReferencedProtocols();
if (!Protocols.empty()) {
for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
E = Protocols.end(); I != E; ++I)
Out << (I == Protocols.begin() ? '<' : ',') << **I;
Out << "> ";
}
if (OID->ivar_size() > 0) {
Out << "{\n";
eolnOut = true;
Indentation += Policy.Indentation;
for (const auto *I : OID->ivars()) {
Indent() << I->getASTContext()
.getUnqualifiedObjCPointerType(I->getType())
.getAsString(Policy) << ' ' << *I << ";\n";
}
Indentation -= Policy.Indentation;
Out << "}\n";
}
else if (SID || (OID->decls_begin() != OID->decls_end())) {
Out << "\n";
eolnOut = true;
}
VisitDeclContext(OID, false);
if (!eolnOut)
Out << "\n";
Out << "@end";
// FIXME: implement the rest...
}
void DeclPrinter::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) {
if (!PID->isThisDeclarationADefinition()) {
Out << "@protocol " << *PID << ";\n";
return;
}
// Protocols?
const ObjCList<ObjCProtocolDecl> &Protocols = PID->getReferencedProtocols();
if (!Protocols.empty()) {
Out << "@protocol " << *PID;
for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
E = Protocols.end(); I != E; ++I)
Out << (I == Protocols.begin() ? '<' : ',') << **I;
Out << ">\n";
} else
Out << "@protocol " << *PID << '\n';
VisitDeclContext(PID, false);
Out << "@end";
}
void DeclPrinter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) {
Out << "@implementation ";
if (const auto *CID = PID->getClassInterface())
Out << *CID;
else
Out << "<<error-type>>";
Out << '(' << *PID << ")\n";
VisitDeclContext(PID, false);
Out << "@end";
// FIXME: implement the rest...
}
void DeclPrinter::VisitObjCCategoryDecl(ObjCCategoryDecl *PID) {
Out << "@interface ";
if (const auto *CID = PID->getClassInterface())
Out << *CID;
else
Out << "<<error-type>>";
if (auto TypeParams = PID->getTypeParamList()) {
PrintObjCTypeParams(TypeParams);
}
Out << "(" << *PID << ")\n";
if (PID->ivar_size() > 0) {
Out << "{\n";
Indentation += Policy.Indentation;
for (const auto *I : PID->ivars())
Indent() << I->getASTContext().getUnqualifiedObjCPointerType(I->getType()).
getAsString(Policy) << ' ' << *I << ";\n";
Indentation -= Policy.Indentation;
Out << "}\n";
}
VisitDeclContext(PID, false);
Out << "@end";
// FIXME: implement the rest...
}
void DeclPrinter::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *AID) {
Out << "@compatibility_alias " << *AID
<< ' ' << *AID->getClassInterface() << ";\n";
}
/// PrintObjCPropertyDecl - print a property declaration.
///
/// Print attributes in the following order:
/// - class
/// - nonatomic | atomic
/// - assign | retain | strong | copy | weak | unsafe_unretained
/// - readwrite | readonly
/// - getter & setter
/// - nullability
void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Required)
Out << "@required\n";
else if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Optional)
Out << "@optional\n";
QualType T = PDecl->getType();
Out << "@property";
if (PDecl->getPropertyAttributes() != ObjCPropertyAttribute::kind_noattr) {
bool first = true;
Out << "(";
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) {
Out << (first ? "" : ", ") << "class";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_direct) {
Implement __attribute__((objc_direct)), __attribute__((objc_direct_members)) __attribute__((objc_direct)) is an attribute on methods declaration, and __attribute__((objc_direct_members)) on implementation, categories or extensions. A `direct` property specifier is added (@property(direct) type name) These attributes / specifiers cause the method to have no associated Objective-C metadata (for the property or the method itself), and the calling convention to be a direct C function call. The symbol for the method has enforced hidden visibility and such direct calls are hence unreachable cross image. An explicit C function must be made if so desired to wrap them. The implicit `self` and `_cmd` arguments are preserved, however to maintain compatibility with the usual `objc_msgSend` semantics, 3 fundamental precautions are taken: 1) for instance methods, `self` is nil-checked. On arm64 backends this typically adds a single instruction (cbz x0, <closest-ret>) to the codegen, for the vast majority of the cases when the return type is a scalar. 2) for class methods, because the class may not be realized/initialized yet, a call to `[self self]` is emitted. When the proper deployment target is used, this is optimized to `objc_opt_self(self)`. However, long term we might want to emit something better that the optimizer can reason about. When inlining kicks in, these calls aren't optimized away as the optimizer has no idea that a single call is really necessary. 3) the calling convention for the `_cmd` argument is changed: the caller leaves the second argument to the call undefined, and the selector is loaded inside the body when it's referenced only. As far as error reporting goes, the compiler refuses: - making any overloads direct, - making an overload of a direct method, - implementations marked as direct when the declaration in the interface isn't (the other way around is allowed, as the direct attribute is inherited from the declaration), - marking methods required for protocol conformance as direct, - messaging an unqualified `id` with a direct method, - forming any @selector() expression with only direct selectors. As warnings: - any inconsistency of direct-related calling convention when @selector() or messaging is used, - forming any @selector() expression with a possibly direct selector. Lastly an `objc_direct_members` attribute is added that can decorate `@implementation` blocks and causes methods only declared there (and in no `@interface`) to be automatically direct. When decorating an `@interface` then all methods and properties declared in this block are marked direct. Radar-ID: rdar://problem/2684889 Differential Revision: https://reviews.llvm.org/D69991 Reviewed-By: John McCall
2019-11-08 15:14:58 +08:00
Out << (first ? "" : ", ") << "direct";
first = false;
}
if (PDecl->getPropertyAttributes() &
ObjCPropertyAttribute::kind_nonatomic) {
Out << (first ? "" : ", ") << "nonatomic";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_atomic) {
Out << (first ? "" : ", ") << "atomic";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_assign) {
Out << (first ? "" : ", ") << "assign";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_retain) {
Out << (first ? "" : ", ") << "retain";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_strong) {
Out << (first ? "" : ", ") << "strong";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_copy) {
Out << (first ? "" : ", ") << "copy";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak) {
Out << (first ? "" : ", ") << "weak";
first = false;
}
if (PDecl->getPropertyAttributes() &
ObjCPropertyAttribute::kind_unsafe_unretained) {
Out << (first ? "" : ", ") << "unsafe_unretained";
first = false;
}
if (PDecl->getPropertyAttributes() &
ObjCPropertyAttribute::kind_readwrite) {
Out << (first ? "" : ", ") << "readwrite";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_readonly) {
Out << (first ? "" : ", ") << "readonly";
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_getter) {
Out << (first ? "" : ", ") << "getter = ";
PDecl->getGetterName().print(Out);
first = false;
}
if (PDecl->getPropertyAttributes() & ObjCPropertyAttribute::kind_setter) {
Out << (first ? "" : ", ") << "setter = ";
PDecl->getSetterName().print(Out);
first = false;
}
if (PDecl->getPropertyAttributes() &
ObjCPropertyAttribute::kind_nullability) {
if (auto nullability = AttributedType::stripOuterNullability(T)) {
if (*nullability == NullabilityKind::Unspecified &&
(PDecl->getPropertyAttributes() &
ObjCPropertyAttribute::kind_null_resettable)) {
Out << (first ? "" : ", ") << "null_resettable";
} else {
Out << (first ? "" : ", ")
<< getNullabilitySpelling(*nullability, true);
}
first = false;
}
}
(void) first; // Silence dead store warning due to idiomatic code.
Out << ")";
}
std::string TypeStr = PDecl->getASTContext().getUnqualifiedObjCPointerType(T).
getAsString(Policy);
Out << ' ' << TypeStr;
if (!StringRef(TypeStr).endswith("*"))
Out << ' ';
Out << *PDecl;
if (Policy.PolishForDeclaration)
Out << ';';
}
void DeclPrinter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PID) {
if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize)
Out << "@synthesize ";
else
Out << "@dynamic ";
Out << *PID->getPropertyDecl();
if (PID->getPropertyIvarDecl())
Out << '=' << *PID->getPropertyIvarDecl();
}
void DeclPrinter::VisitUsingDecl(UsingDecl *D) {
if (!D->isAccessDeclaration())
Out << "using ";
if (D->hasTypename())
Out << "typename ";
D->getQualifier()->print(Out, Policy);
// Use the correct record name when the using declaration is used for
// inheriting constructors.
for (const auto *Shadow : D->shadows()) {
if (const auto *ConstructorShadow =
dyn_cast<ConstructorUsingShadowDecl>(Shadow)) {
assert(Shadow->getDeclContext() == ConstructorShadow->getDeclContext());
Out << *ConstructorShadow->getNominatedBaseClass();
return;
}
}
Out << *D;
}
void DeclPrinter::VisitUsingEnumDecl(UsingEnumDecl *D) {
Out << "using enum " << D->getEnumDecl();
}
void
DeclPrinter::VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) {
Out << "using typename ";
D->getQualifier()->print(Out, Policy);
Out << D->getDeclName();
}
void DeclPrinter::VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) {
if (!D->isAccessDeclaration())
Out << "using ";
D->getQualifier()->print(Out, Policy);
Out << D->getDeclName();
}
void DeclPrinter::VisitUsingShadowDecl(UsingShadowDecl *D) {
// ignore
}
void DeclPrinter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) {
Out << "#pragma omp threadprivate";
if (!D->varlist_empty()) {
for (OMPThreadPrivateDecl::varlist_iterator I = D->varlist_begin(),
E = D->varlist_end();
I != E; ++I) {
Out << (I == D->varlist_begin() ? '(' : ',');
NamedDecl *ND = cast<DeclRefExpr>(*I)->getDecl();
ND->printQualifiedName(Out);
}
Out << ")";
}
}
void DeclPrinter::VisitOMPAllocateDecl(OMPAllocateDecl *D) {
Out << "#pragma omp allocate";
if (!D->varlist_empty()) {
for (OMPAllocateDecl::varlist_iterator I = D->varlist_begin(),
E = D->varlist_end();
I != E; ++I) {
Out << (I == D->varlist_begin() ? '(' : ',');
NamedDecl *ND = cast<DeclRefExpr>(*I)->getDecl();
ND->printQualifiedName(Out);
}
Out << ")";
}
if (!D->clauselist_empty()) {
OMPClausePrinter Printer(Out, Policy);
for (OMPClause *C : D->clauselists()) {
Out << " ";
Printer.Visit(C);
}
}
}
void DeclPrinter::VisitOMPRequiresDecl(OMPRequiresDecl *D) {
Out << "#pragma omp requires ";
if (!D->clauselist_empty()) {
OMPClausePrinter Printer(Out, Policy);
for (auto I = D->clauselist_begin(), E = D->clauselist_end(); I != E; ++I)
Printer.Visit(*I);
}
}
void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) {
if (!D->isInvalidDecl()) {
Out << "#pragma omp declare reduction (";
if (D->getDeclName().getNameKind() == DeclarationName::CXXOperatorName) {
const char *OpName =
getOperatorSpelling(D->getDeclName().getCXXOverloadedOperator());
assert(OpName && "not an overloaded operator");
Out << OpName;
} else {
assert(D->getDeclName().isIdentifier());
D->printName(Out);
}
Out << " : ";
D->getType().print(Out, Policy);
Out << " : ";
D->getCombiner()->printPretty(Out, nullptr, Policy, 0, "\n", &Context);
Out << ")";
if (auto *Init = D->getInitializer()) {
Out << " initializer(";
switch (D->getInitializerKind()) {
case OMPDeclareReductionDecl::DirectInit:
Out << "omp_priv(";
break;
case OMPDeclareReductionDecl::CopyInit:
Out << "omp_priv = ";
break;
case OMPDeclareReductionDecl::CallInit:
break;
}
Init->printPretty(Out, nullptr, Policy, 0, "\n", &Context);
if (D->getInitializerKind() == OMPDeclareReductionDecl::DirectInit)
Out << ")";
Out << ")";
}
}
}
void DeclPrinter::VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D) {
if (!D->isInvalidDecl()) {
Out << "#pragma omp declare mapper (";
D->printName(Out);
Out << " : ";
D->getType().print(Out, Policy);
Out << " ";
Out << D->getVarName();
Out << ")";
if (!D->clauselist_empty()) {
OMPClausePrinter Printer(Out, Policy);
for (auto *C : D->clauselists()) {
Out << " ";
Printer.Visit(C);
}
}
}
}
void DeclPrinter::VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D) {
D->getInit()->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
}
void DeclPrinter::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP) {
if (const TypeConstraint *TC = TTP->getTypeConstraint())
TC->print(Out, Policy);
else if (TTP->wasDeclaredWithTypename())
Out << "typename";
else
Out << "class";
if (TTP->isParameterPack())
Out << " ...";
else if (TTP->getDeclName())
Out << ' ';
if (TTP->getDeclName()) {
if (Policy.CleanUglifiedParameters && TTP->getIdentifier())
Out << TTP->getIdentifier()->deuglifiedName();
else
Out << TTP->getDeclName();
}
if (TTP->hasDefaultArgument()) {
Out << " = ";
Out << TTP->getDefaultArgument().getAsString(Policy);
}
}
void DeclPrinter::VisitNonTypeTemplateParmDecl(
const NonTypeTemplateParmDecl *NTTP) {
StringRef Name;
if (IdentifierInfo *II = NTTP->getIdentifier())
Name =
Policy.CleanUglifiedParameters ? II->deuglifiedName() : II->getName();
printDeclType(NTTP->getType(), Name, NTTP->isParameterPack());
if (NTTP->hasDefaultArgument()) {
Out << " = ";
NTTP->getDefaultArgument()->printPretty(Out, nullptr, Policy, Indentation,
"\n", &Context);
}
}