cling/lib/Interpreter/ForwardDeclPrinter.cpp
Devajith Valaparambil Sreeramaswamy c5692e71f1 Use StringRef::{starts,ends}_with
Follows the changes introduced in https://reviews.llvm.org/D136030
2024-02-23 09:44:06 +01:00

1455 lines
48 KiB
C++

#include "ForwardDeclPrinter.h"
#include "cling/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/AST.h"
#include "cling/Utils/Output.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/Path.h"
namespace cling {
using namespace clang;
ForwardDeclPrinter::ForwardDeclPrinter(llvm::raw_ostream& OutS,
llvm::raw_ostream& LogS,
Preprocessor& P,
ASTContext& Ctx,
const Transaction& T,
unsigned Indentation,
bool printMacros,
IgnoreFilesFunc_t ignoreFiles)
: m_Policy(clang::PrintingPolicy(clang::LangOptions())), m_Log(LogS),
m_Indentation(Indentation), m_PP(P), m_SMgr(P.getSourceManager()),
m_Ctx(Ctx), m_SkipFlag(false), m_IgnoreFile(ignoreFiles) {
m_PrintInstantiation = false;
m_Policy.SuppressTagKeyword = true;
m_Policy.Bool = true; // Avoid printing _Bool instead of bool
m_StreamStack.push(&OutS);
const clang::Builtin::Context& BuiltinCtx = m_Ctx.BuiltinInfo;
for (unsigned i = clang::Builtin::NotBuiltin+1;
i != clang::Builtin::FirstTSBuiltin; ++i)
m_BuiltinNames.insert(BuiltinCtx.getName(i));
for (auto&& BuiltinInfo: m_Ctx.getTargetInfo().getTargetBuiltins())
m_BuiltinNames.insert(BuiltinInfo.Name);
// Suppress some unfixable warnings.
// TODO: Find proper fix for these issues
Out() << "#pragma clang diagnostic ignored \"-Wkeyword-compat\"" << "\n";
Out() << "#pragma clang diagnostic ignored \"-Wignored-attributes\"" <<"\n";
Out() << "#pragma clang diagnostic ignored \"-Wreturn-type-c-linkage\"" <<"\n";
// Inject a special marker:
Out() << "extern int __Cling_AutoLoading_Map;\n";
std::vector<std::string> macrodefs;
if (printMacros) {
for (auto mit = T.macros_begin(); mit != T.macros_end(); ++mit) {
Transaction::MacroDirectiveInfo macro = *mit;
if (macro.m_MD->getKind() == MacroDirective::MD_Define) {
const MacroInfo* MI = macro.m_MD->getMacroInfo();
if (MI ->getNumTokens() > 1)
//FIXME: We can not display function like macros yet
continue;
Out() << "#define " << macro.m_II->getName() << ' ';
for (unsigned i = 0, e = MI->getNumTokens(); i != e; ++i) {
const Token &Tok = MI->getReplacementToken(i);
Out() << Tok.getName() << ' ';
macrodefs.push_back(macro.m_II->getName().str());
}
Out() << '\n';
}
}
}
for(auto dcit = T.decls_begin(); dcit != T.decls_end(); ++dcit) {
const Transaction::DelayCallInfo& dci = *dcit;
if (dci.m_DGR.isNull()) {
break;
}
if (dci.m_Call == Transaction::kCCIHandleTopLevelDecl) {
for (auto dit = dci.m_DGR.begin(); dit != dci.m_DGR.end(); ++dit) {
// llvm::StringRef filename = m_SMgr.getFilename
// ((*dit)->getSourceRange().getBegin());
//#ifdef _POSIX_C_SOURCE
// //Workaround for differnt expansion of macros to typedefs
// if (filename.endswith("sys/types.h"))
// continue;
//#endif
//This may indicate a bug in cling.
//This condition should ideally never be triggered
//But is needed in case of generating fwd decls for
// c++ <future> header.
//if (!(*dit)->getDeclContext()->isTranslationUnit())
// continue;
Visit(*dit);
resetSkip();
}
}
}
if (printMacros) {
for (const auto &m : macrodefs) {
Out() << "#undef " << m << "\n";
}
}
}
void ForwardDeclPrinter::Visit(clang::QualType QT) {
QT = utils::TypeName::GetFullyQualifiedType(QT, m_Ctx);
Visit(QT.getTypePtr());
}
void ForwardDeclPrinter::Visit(clang::Decl *D) {
auto Insert = m_Visited.insert(std::pair<const clang::Decl*, bool>(
getCanonicalOrNamespace(D), true));
if (!Insert.second) {
// Already fwd declared or skipped.
if (!Insert.first->second) {
// Already skipped before; notify callers.
skipDecl(D, 0);
}
return;
}
if (shouldSkip(D)) {
// shouldSkip() called skipDecl()
m_Visited[getCanonicalOrNamespace(D)] = false;
} else {
clang::DeclVisitor<ForwardDeclPrinter>::Visit(D);
if (m_SkipFlag) {
// D was not good, flag it.
skipDecl(D, "Dependency skipped");
m_Visited[getCanonicalOrNamespace(D)] = false;
}
}
}
void ForwardDeclPrinter::printDeclType(llvm::raw_ostream& Stream, 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(Stream, m_Policy, (Pack ? "..." : "") + DeclName);
}
llvm::raw_ostream& ForwardDeclPrinter::Indent(unsigned Indentation) {
for (unsigned i = 0; i != Indentation; ++i)
Out() << " ";
return Out();
}
void ForwardDeclPrinter::prettyPrintAttributes(Decl *D) {
if (D->getSourceRange().isInvalid())
return;
if (D->hasAttrs() && ! isa<FunctionDecl>(D)) {
AttrVec &Attrs = D->getAttrs();
for (AttrVec::const_iterator i=Attrs.begin(), e=Attrs.end(); i != e; ++i) {
Attr *A = *i;
if (A->isImplicit() || A->isInherited()
|| A->getKind() == attr::Kind::Final)
continue;
//FIXME: Remove when the printing of type_visibility attribute is fixed.
if (!isa<AnnotateAttr>(A))
continue;
A->printPretty(Out(), m_Policy);
}
}
auto &smgr = m_SMgr;
auto getIncludeFileName = [D, &smgr](PresumedLoc loc) {
clang::SourceLocation includeLoc =
smgr.getSpellingLoc(loc.getIncludeLoc());
bool invalid = true;
const char* includeText = smgr.getCharacterData(includeLoc, &invalid);
assert(!invalid && "Invalid source data");
assert(includeText && "Cannot find #include location");
// With C++ modules it's possible that we get 'include <header>'
// instead of just '<header>' here. Let's just skip this text at the
// start in this case as the '<header>' still has the correct value.
// FIXME: Once the C++ modules replaced the forward decls, remove this.
if (D->getASTContext().getLangOpts().Modules &&
llvm::StringRef(includeText).starts_with("include ")) {
includeText += strlen("include ");
}
assert((includeText[0] == '<' || includeText[0] == '"') &&
"Unexpected #include delimiter");
char endMarker = includeText[0] == '<' ? '>' : '"';
++includeText;
const char* includeEnd = includeText;
while (*includeEnd != endMarker && *includeEnd) {
++includeEnd;
}
assert(includeEnd && "Cannot find end of #include file name");
return llvm::StringRef(includeText, includeEnd - includeText);
};
auto& PP = m_PP;
auto isDirectlyReacheable = [&PP](llvm::StringRef FileName) {
SourceLocation fileNameLoc;
bool isAngled = false;
ConstSearchDirIterator FromDir = nullptr;
const FileEntry* FromFile = nullptr;
ConstSearchDirIterator* CurDir = nullptr;
auto FE = PP.LookupFile(fileNameLoc, FileName, isAngled, FromDir,
FromFile,
CurDir, /*SearchPath*/ nullptr,
/*RelativePath*/ nullptr, /*suggestedModule*/ nullptr,
/*IsMapped*/ nullptr, /*IsFramework*/ nullptr,
/*SkipCache*/ false,
/*OpenFile*/ false, /*CacheFail*/ true);
// Return true if we can '#include' the given filename
return FE.has_value();
};
SourceLocation spellingLoc = m_SMgr.getSpellingLoc(D->getBeginLoc());
// Walk up the include chain.
PresumedLoc PLoc = m_SMgr.getPresumedLoc(spellingLoc);
llvm::SmallVector<PresumedLoc, 16> PLocs;
llvm::SmallVector<StringRef, 16> PLocNames;
while (!m_IgnoreFile(PLoc)) {
if (!m_SMgr.getPresumedLoc(PLoc.getIncludeLoc()).isValid()) break;
PLocs.push_back(PLoc);
StringRef name(getIncludeFileName(PLoc));
// We record in PLocNames only the include file names that can be
// reached directly. Whenever a #include is parsed in addition to
// the record include path, the directory where the file containing
// the #include is located is also added implicitly and temporarily
// to the include path. So if the include path is empty and a file
// is include via a full pathname it can still #include file in its
// (sub)directory using their relative path.
// Similarly a file included via a sub-directory of the include path
// (eg. #include "Product/mainheader.h") can include header files in
// the same subdirectory without mentioning it
// (eg. #include "otherheader_in_Product.h")
// Since we do not (want to) record the actual directory in which is
// located the header with the #include we are looking at, if the
// #include is relative to that directory we will not be able to find
// it back and thus there is no point in recording it.
if (isDirectlyReacheable(name)) {
PLocNames.push_back(name);
}
PLoc = m_SMgr.getPresumedLoc(PLoc.getIncludeLoc());
}
if (PLocs.empty() /* declared in dictionary payload*/)
return;
else if (PLocNames.empty()) {
// In this case, all the header file name are of the 'unreacheable' type,
// most likely because the first one was related to the linkdef file and
// the linkdef file was pass using a full path name.
// We are not (easy) to find it back, nonetheless record it as is, just
// in case the user add the missing include path at run-time.
if (PLocs.size() > 1) {
Out() << " __attribute__((annotate(\"$clingAutoload$";
Out() << getIncludeFileName(PLocs[0]);
Out() << "\"))) ";
}
Out() << " __attribute__((annotate(\"$clingAutoload$";
Out() << getIncludeFileName(PLocs[PLocs.size() - 1]);
Out() << "\"))) ";
return;
}
if (PLocNames.size() > 1) {
Out() << " __attribute__((annotate(\"$clingAutoload$";
Out() << PLocNames[0];
Out() << "\"))) ";
}
Out() << " __attribute__((annotate(\"$clingAutoload$";
Out() << PLocNames[PLocNames.size()-1];
Out() << "\"))) ";
}
//----------------------------------------------------------------------------
// Common C declarations
//----------------------------------------------------------------------------
void ForwardDeclPrinter::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
// VisitDeclContext(D, false);
assert(0 && "ForwardDeclPrinter::VisitTranslationUnitDecl unexpected");
for (auto it = D->decls_begin(); it != D->decls_end(); ++it) {
Visit(*it);
}
}
void ForwardDeclPrinter::printTypedefOrAliasDecl(TypedefNameDecl *D) {
QualType q = D->getTypeSourceInfo()->getType();
Visit(q);
if (m_SkipFlag) {
skipDecl(D, "Underlying type failed");
return;
}
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
auto printUnderying = [&]() {
QualType qNoRestrict = q;
if (qNoRestrict.isRestrictQualified())
qNoRestrict.removeLocalRestrict();
qNoRestrict.print(Out(), m_Policy);
};
auto printDeclName = [&]() {
if (D->isModulePrivate())
Out() << "__module_private__ ";
if (q.isRestrictQualified()) {
Out() << " __restrict "; // TODO: Find some policy that does this automatically
}
Out() << D->getName();
prettyPrintAttributes(D);
};
if (llvm::isa<TypedefDecl>(D)) {
Out() << "typedef ";
printUnderying();
Out() << " ";
printDeclName();
} else if (llvm::isa<TypeAliasDecl>(D)) {
Out() << "using ";
printDeclName();
Out() << " = ";
printUnderying();
} else {
skipDecl(D, "Neither a typedef nor a type alias!");
}
Out() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitTypedefDecl(TypedefDecl *D) {
printTypedefOrAliasDecl(D);
}
void ForwardDeclPrinter::VisitTypeAliasDecl(TypeAliasDecl *D) {
printTypedefOrAliasDecl(D);
}
void ForwardDeclPrinter::VisitEnumDecl(EnumDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
if (!m_Policy.SuppressSpecifiers && D->isModulePrivate())
Out() << "__module_private__ ";
Out() << "enum ";
if (D->isScoped()) {
if (D->isScopedUsingClassTag())
Out() << "class ";
else
Out() << "struct ";
}
prettyPrintAttributes(D);
Out() << *D;
// if (D->isFixed())
Out() << " : " << D->getIntegerType().stream(m_Policy)
<< ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitRecordDecl(RecordDecl *D) {
std::string closeBraces;
bool isTemplatePattern = false;
if (CXXRecordDecl* CXXRD = dyn_cast<CXXRecordDecl>(D))
isTemplatePattern = CXXRD->getDescribedClassTemplate();
if (!isTemplatePattern)
closeBraces = PrintEnclosingDeclContexts(Out(), D->getDeclContext());
if (!m_Policy.SuppressSpecifiers && D->isModulePrivate())
Out() << "__module_private__ ";
Out() << D->getKindName();
prettyPrintAttributes(D);
if (D->getIdentifier())
Out() << ' ' << *D << ';' << closeBraces << '\n';
// if (D->isCompleteDefinition()) {
// Out << " {\n";
// VisitDeclContext(D);
// Indent() << "}";
// }
}
static void printExplicitSpecifier(ExplicitSpecifier ES,
llvm::raw_ostream &Out,
PrintingPolicy &Policy,
unsigned Indentation) {
std::string Proto = "explicit";
llvm::raw_string_ostream EOut(Proto);
if (ES.getExpr()) {
EOut << "(";
ES.getExpr()->printPretty(EOut, nullptr, Policy, Indentation);
EOut << ")";
}
EOut << " ";
EOut.flush();
Out << EOut.str();
}
void ForwardDeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
bool hasTrailingReturn = false;
CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
Visit(D->getReturnType());
if (m_SkipFlag) {
skipDecl(D, "Return type failed");
return;
}
StreamRAII stream(*this);
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
if (!m_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->isConstexpr() && !D->isExplicitlyDefaulted())
Out() << "constexpr ";
ExplicitSpecifier ExplicitSpec = ExplicitSpecifier::getFromDecl(D);
if (ExplicitSpec.isSpecified())
printExplicitSpecifier(ExplicitSpec, Out(), m_Policy, /*Indentation*/ 0);
}
PrintingPolicy SubPolicy(m_Policy);
SubPolicy.SuppressSpecifiers = false;
std::string Proto = D->getNameInfo().getAsString();
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) {
StreamRAII subStream(*this, &SubPolicy);
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
if (i) Out() << ", ";
Visit(D->getParamDecl(i));
if (m_SkipFlag) {
skipDecl(D, "Parameter failed");
return;
}
}
if (FT->isVariadic()) {
if (D->getNumParams()) Out() << ", ";
Out() << "...";
}
Proto += subStream.take();
}
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,
m_Indentation);
EOut.flush();
//Proto += EOut.str()
//Commented out to fix swap bug, no idea why this was here
//Print was already being called earlier above
Proto += ")";
}
}
if (CDecl) {
bool HasInitializerList = false;
for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(),
E = CDecl->init_end();
B != E; ++B) {
CXXCtorInitializer *BMInitializer = (*B);
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(m_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, m_Policy, m_Indentation);
else {
for (unsigned I = 0; I != NumArgs; ++I) {
if (isa<CXXDefaultArgExpr>(Args[I]))
break;
if (I)
Out() << ", ";
Args[I]->printPretty(Out(), nullptr, m_Policy, m_Indentation);
}
}
}
Out() << ")";
if (BMInitializer->isPackExpansion())
Out() << "...";
}
}
else if (!ConversionDecl && !isa<CXXDestructorDecl>(D)) {
if (FT && FT->hasTrailingReturn()) {
Out() << "auto " << Proto << " -> ";
Proto.clear();
hasTrailingReturn = true;
}
AFT->getReturnType().print(Out(), m_Policy, Proto);
Proto.clear();
}
Out() << Proto;
}
else {
Ty.print(Out(), m_Policy, Proto);
}
if (!hasTrailingReturn)
prettyPrintAttributes(D);
if (D->isPure())
Out() << " = 0";
else if (D->isDeletedAsWritten())
Out() << " = delete";
else if (D->isExplicitlyDefaulted())
Out() << " = default";
else if (D->doesThisDeclarationHaveABody() && !m_Policy.TerseOutput) {
if (!D->hasPrototype() && D->getNumParams()) {
// This is a K&R function definition, so we need to print the
// parameters.
Out() << '\n';
StreamRAII subStream(*this, &SubPolicy);
m_Indentation += m_Policy.Indentation;
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
Indent();
Visit(D->getParamDecl(i));
Out() << ";\n";
}
m_Indentation -= m_Policy.Indentation;
if (m_SkipFlag) {
return;
}
} else
Out() << ' ';
// D->getBody()->printPretty(Out, 0, SubPolicy, Indentation);
}
Out() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitFriendDecl(FriendDecl *) {
}
void ForwardDeclPrinter::VisitFieldDecl(FieldDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
if (!m_Policy.SuppressSpecifiers && D->isMutable())
Out() << "mutable ";
if (!m_Policy.SuppressSpecifiers && D->isModulePrivate())
Out() << "__module_private__ ";
Out() << m_Ctx.getUnqualifiedObjCPointerType(D->getType()).
stream(m_Policy, D->getName());
if (D->isBitField()) {
Out() << " : ";
D->getBitWidth()->printPretty(Out(), nullptr, m_Policy, m_Indentation);
}
Expr *Init = D->getInClassInitializer();
if (!m_Policy.SuppressInitializers && Init) {
if (D->getInClassInitStyle() == ICIS_ListInit)
Out() << " ";
else
Out() << " = ";
Init->printPretty(Out(), nullptr, m_Policy, m_Indentation);
}
prettyPrintAttributes(D);
Out() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitLabelDecl(LabelDecl *D) {
Out() << *D << ":";
}
void ForwardDeclPrinter::VisitVarDecl(VarDecl *D) {
QualType T = D->getTypeSourceInfo()
? D->getTypeSourceInfo()->getType()
: m_Ctx.getUnqualifiedObjCPointerType(D->getType());
Visit(T);
if (m_SkipFlag) {
skipDecl(D, "Variable type failed.");
return;
}
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
if (D->isDefinedOutsideFunctionOrMethod() && D->getStorageClass() != SC_Extern
&& D->getStorageClass() != SC_Static)
Out() << "extern ";
m_Policy.Bool = true;
//^This should not have been needed (already set in constructor)
//But for some reason,without this _Bool is still printed in this path (eg: <iomanip>)
if (!m_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__ ";
}
//FIXME: It prints restrict as restrict
//which is not valid C++
//Should be __restrict
//So, we ignore restrict here
T.removeLocalRestrict();
// T.print(Out(), m_Policy, D->getName());
printDeclType(Out(), T,D->getName());
// cling::outs()<<D->getName()<<"\n";
T.addRestrict();
Expr *Init = D->getInit();
if (!m_Policy.SuppressInitializers && Init) {
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();
}
}
if (D->isDefinedOutsideFunctionOrMethod())
prettyPrintAttributes(D);
if (!ImplicitInit) {
if ((D->getInitStyle() == VarDecl::CallInit)
&& !isa<ParenListExpr>(Init))
Out() << "(";
else if (D->getInitStyle() == VarDecl::CInit) {
if (!D->isDefinedOutsideFunctionOrMethod())
Out() << " = "; //Comment for skipping default function args
}
if (!D->isDefinedOutsideFunctionOrMethod()) {
//Comment for skipping default function args
bool isEnumConst = false;
if (DeclRefExpr* dre = dyn_cast<DeclRefExpr>(Init)){
if (EnumConstantDecl* decl = dyn_cast<EnumConstantDecl>(dre->getDecl())){
printDeclType(Out(), D->getType(),"");
// "" because we want only the type name, not the argument name.
Out() << "(";
decl->getInitVal().print(Out(),/*isSigned*/true);
Out() << ")";
isEnumConst = true;
}
}
if (! isEnumConst)
Init->printPretty(Out(), nullptr, m_Policy, m_Indentation);
}
if ((D->getInitStyle() == VarDecl::CallInit) && !isa<ParenListExpr>(Init))
Out() << ")";
}
}
Out() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitParmVarDecl(ParmVarDecl *D) {
VisitVarDecl(D);
}
void ForwardDeclPrinter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
Out() << "__asm (";
D->getAsmString()->printPretty(Out(), nullptr, m_Policy, m_Indentation);
Out() << ");" << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitImportDecl(ImportDecl *D) {
Out() << "@import " << D->getImportedModule()->getFullModuleName()
<< ";\n";
}
void ForwardDeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
Out() << "static_assert(";
D->getAssertExpr()->printPretty(Out(), nullptr, m_Policy, m_Indentation);
Out() << ", ";
D->getMessage()->printPretty(Out(), nullptr, m_Policy, m_Indentation);
Out() << ");" << closeBraces << '\n';
}
//----------------------------------------------------------------------------
// C++ declarations
//----------------------------------------------------------------------------
void ForwardDeclPrinter::VisitNamespaceDecl(NamespaceDecl *D) {
// VisitDeclContext(D);
bool haveAnyDecl = false;
for (auto dit=D->decls_begin();dit!=D->decls_end();++dit) {
Visit(*dit);
haveAnyDecl |= !m_SkipFlag;
m_SkipFlag = false;
}
if (!haveAnyDecl) {
// make sure at least one redecl of this namespace is fwd declared.
if (D == D->getCanonicalDecl()) {
PrintNamespaceOpen(Out(), D);
Out() << "}\n";
}
}
}
void ForwardDeclPrinter::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
Visit(D->getNominatedNamespace());
if (m_SkipFlag) {
skipDecl(D, "Using directive's underlying namespace failed");
return;
}
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
Out() << "using namespace ";
if (D->getQualifier())
D->getQualifier()->print(Out(), m_Policy);
Out() << *D->getNominatedNamespaceAsWritten() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitUsingDecl(UsingDecl *D) {
// Visit the shadow decls:
for (auto Shadow: D->shadows())
Visit(Shadow);
if (m_SkipFlag) {
skipDecl(D, "shadow decl failed");
return;
}
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
D->print(Out(),m_Policy);
Out() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitUsingShadowDecl(UsingShadowDecl *D) {
Visit(D->getTargetDecl());
if (m_SkipFlag)
skipDecl(D, "target decl failed.");
}
void ForwardDeclPrinter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *) {
}
void ForwardDeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
Out() << "namespace " << *D << " = ";
if (D->getQualifier())
D->getQualifier()->print(Out(), m_Policy);
Out() << *D->getAliasedNamespace() << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitEmptyDecl(EmptyDecl *) {
// prettyPrintAttributes(D);
}
void ForwardDeclPrinter::VisitTagDecl(TagDecl *D) {
std::string closeBraces = PrintEnclosingDeclContexts(Out(),
D->getDeclContext());
if (!m_Policy.SuppressSpecifiers && D->isModulePrivate())
Out() << "__module_private__ ";
Out() << D->getKindName();
// if (D->isCompleteDefinition())
prettyPrintAttributes(D);
if (D->getIdentifier())
Out() << ' ' << *D << ';' << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
for (auto it = D->decls_begin(); it != D->decls_end(); ++it) {
Visit(*it);
resetSkip();
}
}
void ForwardDeclPrinter::PrintTemplateParameters(llvm::raw_ostream& Stream,
TemplateParameterList *Params,
const TemplateArgumentList *Args) {
assert(Params);
assert(!Args || Params->size() == Args->size());
Stream << "template <";
for (unsigned i = 0, e = Params->size(); i != e; ++i) {
if (i != 0)
Stream << ", ";
Decl *Param = Params->getParam(i);
if (const TemplateTypeParmDecl *TTP =
dyn_cast<TemplateTypeParmDecl>(Param)) {
if (TTP->wasDeclaredWithTypename())
Stream << "typename ";
else
Stream << "class ";
if (TTP->isParameterPack())
Stream << "...";
Stream << *TTP;
QualType ArgQT;
if (Args) {
ArgQT = Args->get(i).getAsType();
}
else if (TTP->hasDefaultArgument()) {
ArgQT = TTP->getDefaultArgument();
}
if (!ArgQT.isNull()) {
QualType ArgFQQT
= utils::TypeName::GetFullyQualifiedType(ArgQT, m_Ctx);
Visit(ArgFQQT);
if (m_SkipFlag) {
skipDecl(nullptr, "type template param default failed");
return;
}
Stream << " = ";
ArgFQQT.print(Stream, m_Policy);
}
}
else if (const NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(Param)) {
StringRef Name;
if (IdentifierInfo *II = NTTP->getIdentifier())
Name = II->getName();
printDeclType(Stream, NTTP->getType(), Name, NTTP->isParameterPack());
if (Args) {
Stream << " = ";
Args->get(i).print(m_Policy, Stream, /*IncludeType=*/true);
}
else if (NTTP->hasDefaultArgument()) {
Expr* DefArg = NTTP->getDefaultArgument()->IgnoreImpCasts();
if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(DefArg)) {
Visit(DRE->getFoundDecl());
if (m_SkipFlag) {
skipDecl(nullptr, "expression template param default failed");
return;
}
} else if (isa<IntegerLiteral>(DefArg)
|| isa<CharacterLiteral>(DefArg)
|| isa<CXXBoolLiteralExpr>(DefArg)
|| isa<CXXNullPtrLiteralExpr>(DefArg)
|| isa<FloatingLiteral>(DefArg)
|| isa<StringLiteral>(DefArg)) {
Stream << " = ";
DefArg->printPretty(Stream, nullptr, m_Policy, m_Indentation);
} else {
skipDecl(nullptr, "expression template param default not a literal");
return;
}
}
}
else if (TemplateTemplateParmDecl *TTPD =
dyn_cast<TemplateTemplateParmDecl>(Param)) {
Visit(TTPD);
// FIXME: print the default argument, if present.
if (m_SkipFlag) {
skipDecl(TTPD, "template template param decl failed");
return;
}
}
}
Stream << "> ";
}
void ForwardDeclPrinter::VisitRedeclarableTemplateDecl(const RedeclarableTemplateDecl *D) {
// Find redecl with template default arguments: that's the one
// we want to forward declare.
for (const RedeclarableTemplateDecl* RD: D->redecls()) {
clang::TemplateParameterList* TPL = RD->getTemplateParameters();
if (TPL->getMinRequiredArguments () < TPL->size())
D = RD;
}
stdstrstream Stream;
std::string closeBraces;
if (!isa<TemplateTemplateParmDecl>(D))
closeBraces = PrintEnclosingDeclContexts(Stream, D->getDeclContext());
PrintTemplateParameters(Stream, D->getTemplateParameters());
if (m_SkipFlag) {
skipDecl(nullptr, "Template parameters failed");
return;
}
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(D)) {
Stream << "class ";
if (TTP->isParameterPack())
Out() << "...";
Stream << D->getName();
}
else {
StreamRAII SubStream(*this);
Visit(D->getTemplatedDecl());
if (m_SkipFlag) {
skipDecl(D->getTemplatedDecl(), "Template pattern failed");
return;
}
Stream << SubStream.take(true);
}
Out() << Stream.str() << closeBraces << '\n';
}
void ForwardDeclPrinter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
if (m_PrintInstantiation) {
StreamRAII stream(*this);
TemplateParameterList *Params = D->getTemplateParameters();
for (FunctionTemplateDecl::spec_iterator I = D->spec_begin(),
E = D->spec_end(); I != E; ++I) {
PrintTemplateParameters(Out(),
Params, (*I)->getTemplateSpecializationArgs());
if (m_SkipFlag) {
skipDecl(D, "Template parameters failed");
return;
}
Visit(*I);
}
if (m_SkipFlag) {
skipDecl(D, "specialization failed");
return;
}
std::string output = stream.take(true).str();
Out() << output;
}
return VisitRedeclarableTemplateDecl(D);
}
void ForwardDeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (m_PrintInstantiation) {
StreamRAII stream(*this);
TemplateParameterList *Params = D->getTemplateParameters();
for (ClassTemplateDecl::spec_iterator I = D->spec_begin(),
E = D->spec_end(); I != E; ++I) {
PrintTemplateParameters(Out(), Params, &(*I)->getTemplateArgs());
if (m_SkipFlag) {
skipDecl(D, "template parameters failed");
return;
}
Visit(*I);
if (m_SkipFlag) {
skipDecl(D, "template instance failed");
return;
}
std::string output = stream.take(true).str();
Out() << output;
Out() << '\n';
}
}
return VisitRedeclarableTemplateDecl(D);
}
void ForwardDeclPrinter::
VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl* D) {
// if (shouldSkip(D)) {
// skipDecl();
// return;
// }
const TemplateArgumentList& iargs = D->getTemplateInstantiationArgs();
for (const TemplateArgument& TA: iargs.asArray()) {
VisitTemplateArgument(TA);
}
if (m_SkipFlag) {
skipDecl(D, "template arguments failed");
return;
}
// Out() << "template <> ";
// VisitCXXRecordDecl(D->getCanonicalDecl());
// Out() << "<";
// for (unsigned int i=0; i < iargs.size(); ++i){
// if (iargs[i].getKind() == TemplateArgument::Pack)
// continue;
// if (i != 0 )
// Out() << ", ";
// iargs[i].print(m_Policy,Out());
// }
// Out() << ">";
// skipDecl(false);
Visit(D->getSpecializedTemplate());
//Above code doesn't work properly
//Must find better and more general way to print specializations
if (m_SkipFlag) {
skipDecl(D, "template specialization failed");
return;
}
}
void ForwardDeclPrinter::Visit(const Type* typ) {
switch (typ->getTypeClass()) {
#define VISIT_DECL(WHAT, HOW) \
case clang::Type::WHAT: \
Visit(static_cast<const clang::WHAT##Type*>(typ)->HOW().getTypePtr()); \
if (m_SkipFlag) { \
skipDecl(nullptr, #WHAT " type failed"); \
return; \
} \
break
VISIT_DECL(ConstantArray, getElementType);
VISIT_DECL(DependentSizedArray, getElementType);
VISIT_DECL(IncompleteArray, getElementType);
VISIT_DECL(VariableArray, getElementType);
VISIT_DECL(Atomic, getValueType);
VISIT_DECL(Auto, getDeducedType);
VISIT_DECL(Decltype, getUnderlyingType);
VISIT_DECL(Paren, getInnerType);
VISIT_DECL(Pointer, getPointeeType);
VISIT_DECL(LValueReference, getPointeeType);
VISIT_DECL(RValueReference, getPointeeType);
VISIT_DECL(TypeOf, getUnmodifiedType);
VISIT_DECL(Elaborated, getNamedType);
VISIT_DECL(UnaryTransform, getUnderlyingType);
#undef VISIT_DECL
case clang::Type::DependentName:
{
VisitNestedNameSpecifier(static_cast<const DependentNameType*>(typ)
->getQualifier());
}
break;
case clang::Type::MemberPointer:
{
const MemberPointerType* MPT
= static_cast<const MemberPointerType*>(typ);
Visit(MPT->getPointeeType().getTypePtr());
if (m_SkipFlag) {
skipDecl(nullptr, "pointee type failed");
return;
}
Visit(MPT->getClass());
}
break;
case clang::Type::Enum:
// intentional fall-through
case clang::Type::Record:
Visit(static_cast<const clang::TagType*>(typ)->getDecl());
break;
case clang::Type::TemplateSpecialization:
{
const TemplateSpecializationType* TST
= static_cast<const TemplateSpecializationType*>(typ);
for (const TemplateArgument& TA : TST->template_arguments()) {
VisitTemplateArgument(TA);
if (m_SkipFlag) {
skipDecl(nullptr, "template argument failed");
return;
}
}
VisitTemplateName(TST->getTemplateName());
if (m_SkipFlag) {
skipDecl(nullptr, "template specialization type failed");
return;
}
}
break;
case clang::Type::Typedef:
Visit(static_cast<const TypedefType*>(typ)->getDecl());
break;
case clang::Type::TemplateTypeParm:
Visit(static_cast<const TemplateTypeParmType*>(typ)->getDecl());
break;
case clang::Type::Builtin:
// Nothing to do.
break;
case clang::Type::TypeOfExpr:
// Nothing to do.
break;
default:
Log() << "addDeclsToTransactionForType: Unexpected "
<< typ->getTypeClassName() << '\n';
break;
}
}
void ForwardDeclPrinter::VisitTemplateArgument(const TemplateArgument& TA) {
switch (TA.getKind()) {
case clang::TemplateArgument::Type:
Visit(TA.getAsType().getTypePtr());
break;
case clang::TemplateArgument::Declaration:
Visit(TA.getAsDecl());
break;
case clang::TemplateArgument::Template:
VisitTemplateName(TA.getAsTemplateOrTemplatePattern());
break;
case clang::TemplateArgument::Pack:
for (const auto& arg : TA.pack_elements())
VisitTemplateArgument(arg);
break;
case clang::TemplateArgument::Expression:
{
Expr* TAExpr = TA.getAsExpr();
if (CastExpr* CastExpr = dyn_cast<clang::CastExpr>(TAExpr))
TAExpr = CastExpr->getSubExpr();
if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(TAExpr)) {
Visit(DRE->getFoundDecl());
if (m_SkipFlag) {
return;
}
}
else {
std::string buf;
{
llvm::raw_string_ostream osbuf(buf);
TAExpr->printPretty(osbuf, nullptr, m_Policy);
}
Log() << "Visit(Type*): cannot forward declare template argument expression: "
<< buf;
skipDecl(nullptr, nullptr);
}
}
break;
default:
Log() << "Visit(Type*): Unexpected TemplateSpecializationType "
<< TA.getKind() << '\n';
skipDecl(nullptr, nullptr);
break;
}
}
void ForwardDeclPrinter::VisitTemplateName(const clang::TemplateName& TN) {
switch (TN.getKind()) {
case clang::TemplateName::Template:
Visit(TN.getAsTemplateDecl());
break;
case clang::TemplateName::QualifiedTemplate:
Visit(TN.getAsQualifiedTemplateName()->getUnderlyingTemplate().getAsTemplateDecl());
break;
case clang::TemplateName::DependentTemplate:
VisitNestedNameSpecifier(TN.getAsDependentTemplateName()->getQualifier());
break;
case clang::TemplateName::SubstTemplateTemplateParm:
VisitTemplateName(TN.getAsSubstTemplateTemplateParm()->getReplacement());
break;
case clang::TemplateName::SubstTemplateTemplateParmPack:
VisitTemplateArgument(TN.getAsSubstTemplateTemplateParmPack()->getArgumentPack());
break;
default:
Log() << "VisitTemplateName: Unexpected kind " << TN.getKind() << '\n';
break;
}
}
void ForwardDeclPrinter::VisitNestedNameSpecifier(
const clang::NestedNameSpecifier* NNS) {
if (const clang::NestedNameSpecifier* Prefix = NNS->getPrefix())
VisitNestedNameSpecifier(Prefix);
switch (NNS->getKind()) {
case clang::NestedNameSpecifier::Namespace:
Visit(NNS->getAsNamespace());
break;
case clang::NestedNameSpecifier::TypeSpec: // fall-through:
case clang::NestedNameSpecifier::TypeSpecWithTemplate:
// We cannot fwd declare nested types.
skipDecl(nullptr, "NestedNameSpec TypeSpec/TypeSpecWithTemplate");
break;
default:
Log() << "VisitNestedNameSpecifier: Unexpected kind "
<< NNS->getKind() << '\n';
skipDecl(nullptr, nullptr);
break;
};
}
bool ForwardDeclPrinter::isOperator(FunctionDecl *D) {
//TODO: Find a better check for this
return D->getNameAsString().find("operator") == 0;
}
bool ForwardDeclPrinter::hasDefaultArgument(FunctionDecl *D) {
auto N = D->getNumParams();
for (unsigned int i=0; i < N; ++i) {
if (D->getParamDecl(i)->hasDefaultArg())
return true;
}
return false;
}
bool ForwardDeclPrinter::shouldSkipImpl(FunctionDecl *D) {
//FIXME: setDeletedAsWritten can be called from the
//InclusionDiretctive callback.
//Implement that if important functions are marked so.
//Not important, as users do not need hints
//about using Deleted functions
if (D->getIdentifier() == nullptr
|| D->getNameAsString()[0] == '_'
|| D->getStorageClass() == SC_Static
|| D->isCXXClassMember()
|| isOperator(D)
|| D->isDeleted()
|| D->isDeletedAsWritten()) {
return true;
}
return false;
}
bool ForwardDeclPrinter::shouldSkipImpl(FunctionTemplateDecl *D) {
return shouldSkipImpl(D->getTemplatedDecl());
}
bool ForwardDeclPrinter::shouldSkipImpl(TagDecl *D) {
return !D->getIdentifier();
}
bool ForwardDeclPrinter::shouldSkipImpl(VarDecl *D) {
if (D->getType().isConstant(m_Ctx)) {
Log() << D->getName() <<" Var : Const\n";
m_Visited[D->getCanonicalDecl()] = false;
return true;
}
return false;
}
bool ForwardDeclPrinter::shouldSkipImpl(EnumDecl *D) {
if (!D->getIdentifier()){
Log() << "Enum: Empty name\n";
return true;
}
return false;
}
void ForwardDeclPrinter::skipDecl(Decl* D, const char* Reason) {
m_SkipFlag = true;
if (Reason) {
if (D)
Log() << D->getDeclKindName() << " " << getNameIfPossible(D) << " ";
Log() << Reason << '\n';
}
}
bool ForwardDeclPrinter::shouldSkipImpl(ClassTemplateSpecializationDecl *D) {
if (llvm::isa<ClassTemplatePartialSpecializationDecl>(D)) {
//TODO: How to print partial specializations?
return true;
}
return false;
}
bool ForwardDeclPrinter::shouldSkipImpl(UsingDirectiveDecl *D) {
if (shouldSkipImpl(D->getNominatedNamespace())) {
Log() << D->getNameAsString() <<" Using Directive : Incompatible Type\n";
return true;
}
return false;
}
bool ForwardDeclPrinter::shouldSkipImpl(TypeAliasTemplateDecl *D) {
D->printName(Log());
Log() << " TypeAliasTemplateDecl: Always Skipped\n";
return true;
}
void ForwardDeclPrinter::printStats() {
size_t bad = 0;
for (auto&& i: m_Visited)
if (!i.second)
++bad;
Log() << bad << " decls skipped out of " << m_Visited.size() << "\n";
}
void ForwardDeclPrinter::PrintNamespaceOpen(llvm::raw_ostream& Stream,
const NamespaceDecl* ND) {
if (ND->isInline())
Stream << "inline ";
Stream << "namespace " << *ND << '{';
}
void ForwardDeclPrinter::PrintLinkageOpen(llvm::raw_ostream& Stream,
const LinkageSpecDecl* LSD) {
assert((LSD->getLanguage() == LinkageSpecDecl::lang_cxx ||
LSD->getLanguage() == LinkageSpecDecl::lang_c) &&
"Unknown linkage spec!");
Stream << "extern \"C";
if (LSD->getLanguage() == LinkageSpecDecl::lang_cxx) {
Stream << "++";
}
Stream << "\" {";
}
std::string ForwardDeclPrinter::PrintEnclosingDeclContexts(llvm::raw_ostream& Stream,
const DeclContext* DC) {
// Return closing "} } } }"...
SmallVector<const DeclContext*, 16> DeclCtxs;
for(; DC && !DC->isTranslationUnit(); DC = DC->getParent()) {
if (!isa<NamespaceDecl>(DC) && !isa<LinkageSpecDecl>(DC)) {
Log() << "Skipping unhandled " << DC->getDeclKindName() << '\n';
skipDecl(nullptr, nullptr);
return "";
}
DeclCtxs.push_back(DC);
}
for (auto I = DeclCtxs.rbegin(), E = DeclCtxs.rend(); I != E; ++I) {
if (const NamespaceDecl* ND = dyn_cast<NamespaceDecl>(*I))
PrintNamespaceOpen(Stream, ND);
else if (const LinkageSpecDecl* LSD = dyn_cast<LinkageSpecDecl>(*I))
PrintLinkageOpen(Stream, LSD);
}
return std::string(DeclCtxs.size(), '}');
}
}//end namespace cling