cling/lib/Interpreter/ForwardDeclPrinter.h
2023-12-11 08:59:20 +01:00

300 lines
11 KiB
C++

//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author: Manasij Mukherjee <manasij7479@gmail.com>
// author: Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
#ifndef CLING_AUTOLOADING_VISITOR_H
#define CLING_AUTOLOADING_VISITOR_H
#include "cling/Utils/Output.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/DenseMap.h"
#include <stack>
#include <set>
///\brief Generates forward declarations for a Decl or Transaction
/// by implementing a DeclVisitor
///
/// Important Points:
/// 1. Function arguments having an EnumConstant as a default value
/// are printed in the following way:
/// enum E {E_a, E_b};
/// void foo(E e = E_b){}
/// Generates:
/// enum E : unsigned int;
/// void foo(E e = E(1));
/// 1 is the integral value of E_b.
///
/// 2. Decls, in general, are skipped when they depend on things
/// that were previously skipped.
/// The set of strings, m_IncompatibleNames facilitate this.
/// Examine the shouldSkip functions to see why specific types
/// are skipped.
///
/// 3. Log file:
/// The name of the file depends on the name of the file where
/// the forward declarations are written.
/// So, fwd.h produces a corresponding fwd.h.skipped, when
/// output logging is enabled.
/// The log messages are written in the shouldSkip functions to
/// simplify the design.
///
///
namespace clang {
class ClassTemplateDecl;
class ClassTemplateSpecializationDecl;
class CXXRecordDecl;
class Decl;
class DeclContext;
class EmptyDecl;
class EnumDecl;
class EnumConstantDecl;
class FieldDecl;
class FileScopeAsmDecl;
class FriendDecl;
class FunctionDecl;
class FunctionTemplateDecl;
class ImportDecl;
class LabelDecl;
class LinkageSpecDecl;
class NamespaceDecl;
class NamespaceAliasDecl;
class ParmVarDecl;
class QualType;
class RecordDecl;
class Sema;
class StaticAssertDecl;
class TemplateArgumentList;
class TemplateDecl;
class TemplateParameterList;
class TranslationUnitDecl;
class TypeAliasDecl;
class TypedefDecl;
class TypedefNameDecl;
class VarDecl;
class UsingDirectiveDecl;
}
namespace llvm {
class raw_ostream;
}
namespace cling {
class Transaction;
class ForwardDeclPrinter : public clang::DeclVisitor<ForwardDeclPrinter> {
private:
using IgnoreFilesFunc_t = bool (*)(const clang::PresumedLoc&);
clang::PrintingPolicy m_Policy; // intentional copy
llvm::raw_ostream& m_Log;
unsigned m_Indentation;
bool m_PrintInstantiation;
clang::Preprocessor& m_PP;
clang::SourceManager& m_SMgr;
clang::ASTContext& m_Ctx;
bool m_SkipFlag;
//False by default, true if current item is not to be printed
llvm::DenseMap<const clang::Decl*, bool> m_Visited; // fwd decl success
std::stack<llvm::raw_ostream*> m_StreamStack;
std::set<llvm::StringRef> m_BuiltinNames;
IgnoreFilesFunc_t m_IgnoreFile; // Call back to ignore some top level files.
void printTypedefOrAliasDecl(clang::TypedefNameDecl* D);
public:
ForwardDeclPrinter(llvm::raw_ostream& OutS,
llvm::raw_ostream& LogS,
clang::Preprocessor& P,
clang::ASTContext& Ctx,
const Transaction& T,
unsigned Indentation = 0,
bool printMacros = false,
IgnoreFilesFunc_t ignoreFiles =
[](const clang::PresumedLoc&) { return false; } );
// void VisitDeclContext(clang::DeclContext *DC, bool shouldIndent = true);
void Visit(clang::Decl *D);
void VisitTranslationUnitDecl(clang::TranslationUnitDecl *D);
void VisitTypedefDecl(clang::TypedefDecl *D);
void VisitTypeAliasDecl(clang::TypeAliasDecl *D);
void VisitEnumDecl(clang::EnumDecl *D);
void VisitRecordDecl(clang::RecordDecl *D);
void VisitEmptyDecl(clang::EmptyDecl *D);
void VisitFunctionDecl(clang::FunctionDecl *D);
void VisitFriendDecl(clang::FriendDecl *D);
void VisitFieldDecl(clang::FieldDecl *D);
void VisitVarDecl(clang::VarDecl *D);
void VisitLabelDecl(clang::LabelDecl *D);
void VisitParmVarDecl(clang::ParmVarDecl *D);
void VisitFileScopeAsmDecl(clang::FileScopeAsmDecl *D);
void VisitImportDecl(clang::ImportDecl *D);
void VisitStaticAssertDecl(clang::StaticAssertDecl *D);
void VisitNamespaceDecl(clang::NamespaceDecl *D);
void VisitUsingDirectiveDecl(clang::UsingDirectiveDecl *D);
void VisitUsingDecl(clang::UsingDecl* D);
void VisitUsingShadowDecl(clang::UsingShadowDecl* D);
void VisitNamespaceAliasDecl(clang::NamespaceAliasDecl *D);
void VisitTagDecl(clang::TagDecl *D);
void VisitLinkageSpecDecl(clang::LinkageSpecDecl *D);
void VisitRedeclarableTemplateDecl(const clang::RedeclarableTemplateDecl *D);
void VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *D);
void VisitClassTemplateDecl(clang::ClassTemplateDecl *D);
void VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl* D);
void VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl* D);
// Not coming from the RecursiveASTVisitor
void Visit(clang::QualType QT);
void Visit(const clang::Type* T);
void VisitNestedNameSpecifier(const clang::NestedNameSpecifier* NNS);
void VisitTemplateArgument(const clang::TemplateArgument& TA);
void VisitTemplateName(const clang::TemplateName& TN);
void printDeclType(llvm::raw_ostream& Stream, clang::QualType T,
llvm::StringRef DeclName, bool Pack = false);
void PrintTemplateParameters(llvm::raw_ostream& Stream,
clang::TemplateParameterList *Params,
const clang::TemplateArgumentList *Args = nullptr);
void prettyPrintAttributes(clang::Decl *D);
bool isOperator(clang::FunctionDecl* D);
bool hasDefaultArgument(clang::FunctionDecl* D);
bool shouldSkip(clang::Decl* D) {
switch (D->getKind()) {
#define DECL(TYPE, BASE) \
case clang::Decl::TYPE: return shouldSkip((clang::TYPE##Decl*)D); break;
#define ABSTRACT_DECL(DECL)
#include "clang/AST/DeclNodes.inc"
#undef DECL
#undef ABSTRACT_DECL
}
return false;
}
std::string getNameIfPossible(clang::Decl*) { return "<not named>"; }
std::string getNameIfPossible(clang::NamedDecl* D) {
return D->getNameAsString();
}
template <typename T> bool shouldSkip(T* D) {
// Anything inside DCs except those below cannot be fwd declared.
clang::Decl::Kind DCKind = D->getDeclContext()->getDeclKind();
if (DCKind != clang::Decl::Namespace
&& DCKind != clang::Decl::TranslationUnit
&& DCKind != clang::Decl::LinkageSpec) {
Log() << getNameIfPossible(D) <<" \n";
skipDecl(D, "Incompatible DeclContext");
} else {
if (clang::NamedDecl* ND = clang::dyn_cast<clang::NamedDecl>(D)) {
if (clang::IdentifierInfo* II = ND->getIdentifier()) {
if (m_BuiltinNames.find(II->getNameStart()) != m_BuiltinNames.end()
|| !strncmp(II->getNameStart(), "__builtin_", 10))
skipDecl(D, "builtin");
}
}
if (!m_SkipFlag)
if (shouldSkipImpl(D))
skipDecl(D, "shouldSkip");
}
if (m_SkipFlag) {
// Remember that we have tried to fwd declare this already.
m_Visited.insert(std::pair<const clang::Decl*, bool>(
getCanonicalOrNamespace(D), false));
}
return m_SkipFlag;
}
bool ContainsIncompatibleName(clang::TemplateParameterList* Params);
void skipDecl(clang::Decl* D, const char* Reason);
void resetSkip() { m_SkipFlag = false; }
void printStats();
private:
llvm::raw_ostream& Indent() { return Indent(m_Indentation); }
llvm::raw_ostream& Indent(unsigned Indentation);
// void ProcessDeclGroup(llvm::SmallVectorImpl<clang::Decl*>& Decls);
void Print(clang::AccessSpecifier AS);
llvm::raw_ostream& Out() { return *m_StreamStack.top(); }
llvm::raw_ostream& Log() { return m_Log; }
bool shouldSkipImpl(clang::Decl*){return false;}
bool shouldSkipImpl(clang::FunctionDecl* D);
bool shouldSkipImpl(clang::FunctionTemplateDecl* D);
bool shouldSkipImpl(clang::TagDecl* D);
bool shouldSkipImpl(clang::VarDecl* D);
bool shouldSkipImpl(clang::EnumDecl* D);
bool shouldSkipImpl(clang::ClassTemplateSpecializationDecl* D);
bool shouldSkipImpl(clang::UsingDirectiveDecl* D);
bool shouldSkipImpl(clang::TypeAliasTemplateDecl* D);
bool shouldSkipImpl(clang::EnumConstantDecl*) { return false; };
bool haveSkippedBefore(const clang::Decl* D) const {
auto Found = m_Visited.find(getCanonicalOrNamespace(D));
return (Found != m_Visited.end() && !Found->second);
}
const clang::Decl* getCanonicalOrNamespace(const clang::Decl* D) const {
if (D->getKind() == clang::Decl::Namespace)
return D;
return D->getCanonicalDecl();
}
const clang::Decl* getCanonicalOrNamespace(const clang::NamespaceDecl* D) const {
return D;
}
std::string PrintEnclosingDeclContexts(llvm::raw_ostream& Stream,
const clang::DeclContext* DC);
void PrintNamespaceOpen(llvm::raw_ostream& Stream,
const clang::NamespaceDecl* ND);
void PrintLinkageOpen(llvm::raw_ostream& Stream,
const clang::LinkageSpecDecl* LSD);
class StreamRAII {
ForwardDeclPrinter& m_pr;
clang::PrintingPolicy m_oldPol;
largestream m_Stream;
bool m_HavePopped;
public:
StreamRAII(ForwardDeclPrinter& pr, clang::PrintingPolicy* pol = nullptr):
m_pr(pr), m_oldPol(pr.m_Policy), m_HavePopped(false) {
m_pr.m_StreamStack.push(&static_cast<llvm::raw_ostream&>(m_Stream));
if (pol)
m_pr.m_Policy = *pol;
}
~StreamRAII() {
if (!m_HavePopped) {
m_pr.m_StreamStack.pop();
if (!m_pr.m_SkipFlag) {
m_pr.Out() << m_Stream.str();
}
}
m_pr.m_Policy = m_oldPol;
}
llvm::StringRef take(bool pop = false) {
if (pop) {
assert(!m_HavePopped && "No popping twice");
m_HavePopped = true;
m_pr.m_StreamStack.pop();
}
return m_Stream.str();
}
};
};
}
#endif