Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/CppInterOp/CppInterOpTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,17 @@ enum QualKind : unsigned char {
All = Const | Volatile | Restrict
};

/// What a template parameter stands for.
enum class TemplateParamKind : unsigned char {
Unknown,
/// A type parameter: template <typename T>.
Type,
/// A value parameter: template <int N>.
NonType,
/// A template parameter: template <template <typename> class C>.
Template
};

/// Enum modelling programming languages.
enum class InterpreterLanguage : unsigned char {
Unknown,
Expand Down
252 changes: 252 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "CppInterOp/CppInterOp.h"
#include "Unwrap.h"
#include "CppInterOp/CppInterOpTypes.h"
#include "CppInterOp/Error.h"

#include "Compatibility.h"
Expand Down Expand Up @@ -59,6 +60,7 @@
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangStandard.h"
#include "clang/Basic/Linkage.h"
Expand Down Expand Up @@ -1100,6 +1102,256 @@ std::string GetDoxygenComment(ConstDeclRef DRef, bool strip_comment_markers) {
return INTEROP_RETURN(RC->getFormattedText(SM, C.getDiagnostics()));
}

// Declarations users can name live inside namespaces, classes and language
// linkage blocks, so descend through those. A linkage block is itself unnamed,
// which is why it cannot simply be filtered out at the top level.
static void collectNamedDecls(clang::DeclContext* DC,
std::vector<clang::Decl*>& Out) {
for (auto* D : DC->decls()) {
if (llvm::isa<clang::NamedDecl>(D))
Out.push_back(D);
if (llvm::isa<clang::NamespaceDecl>(D) ||
llvm::isa<clang::LinkageSpecDecl>(D) || llvm::isa<clang::RecordDecl>(D))
collectNamedDecls(llvm::cast<clang::DeclContext>(D), Out);
// A class template is not a DeclContext; its members live on the
// record it describes.
else if (auto* CTD = llvm::dyn_cast<clang::ClassTemplateDecl>(D))
if (auto* RD = CTD->getTemplatedDecl())
collectNamedDecls(RD, Out);
}
}

// Every partial translation unit the interpreter has parsed. An incremental
// interpreter creates one TranslationUnitDecl per unit, so walk the whole
// redeclaration chain rather than a single node.
// redecls() yields newest-first; report declaration order instead.
static void collectTranslationUnitDecls(std::vector<clang::Decl*>& Out) {
auto* TU = getSema().getASTContext().getTranslationUnitDecl();
llvm::SmallVector<clang::TranslationUnitDecl*, 32> Chain;
for (auto* R : TU->redecls())
Chain.push_back(llvm::cast<clang::TranslationUnitDecl>(R));
for (auto* R : llvm::reverse(Chain))
collectNamedDecls(R, Out);
}

void EnumerateTranslationUnitDecls(std::vector<DeclRef>& Decls) {
INTEROP_TRACE(INTEROP_OUT(Decls));

compat::SynthesizingCodeRAII RAII(&getInterp());

std::vector<clang::Decl*> Found;
collectTranslationUnitDecls(Found);
Decls.reserve(Found.size());
for (auto* D : Found)
Decls.emplace_back(D);

return INTEROP_VOID_RETURN();
Comment thread
conrade-ctc marked this conversation as resolved.
}

bool IsScopedEnum(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* ED =
llvm::dyn_cast_or_null<clang::EnumDecl>(unwrap<clang::Decl>(DRef));
return INTEROP_RETURN(ED && ED->isScoped());
}

bool IsUnion(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* RD =
llvm::dyn_cast_or_null<clang::RecordDecl>(unwrap<clang::Decl>(DRef));
return INTEROP_RETURN(RD && RD->isUnion());
}

bool IsAliasTemplate(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
return INTEROP_RETURN(llvm::isa_and_nonnull<clang::TypeAliasTemplateDecl>(D));
}

bool IsParameterPack(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* ND =
llvm::dyn_cast_or_null<clang::NamedDecl>(unwrap<clang::Decl>(DRef));
return INTEROP_RETURN(ND && ND->isParameterPack());
}

bool IsClassTemplate(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
return INTEROP_RETURN(llvm::isa_and_nonnull<clang::ClassTemplateDecl>(D));
}

bool IsImplicitDecl(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
return INTEROP_RETURN(D && D->isImplicit());
}

bool IsFriendDeclared(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
return INTEROP_RETURN(D && D->getFriendObjectKind() != clang::Decl::FOK_None);
}

bool IsDefinition(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (const auto* TD = llvm::dyn_cast_or_null<clang::TemplateDecl>(D))
D = TD->getTemplatedDecl();
if (const auto* TagD = llvm::dyn_cast_or_null<clang::TagDecl>(D))
return INTEROP_RETURN(TagD->isCompleteDefinition());
if (const auto* FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D))
return INTEROP_RETURN(FD->isThisDeclarationADefinition());
if (const auto* VD = llvm::dyn_cast_or_null<clang::VarDecl>(D))
return INTEROP_RETURN(VD->isThisDeclarationADefinition() !=
clang::VarDecl::DeclarationOnly);
// Anything else -- an alias, a namespace, a template parameter -- only ever
// appears in its defining form.
return INTEROP_RETURN(D != nullptr);
}

unsigned GetOverloadCount(ConstDeclRef DRef, const std::string& Name) {
INTEROP_TRACE(DRef, Name);
const auto* DC =
llvm::dyn_cast_or_null<clang::DeclContext>(unwrap<clang::Decl>(DRef));
if (!DC)
return INTEROP_RETURN(0);

clang::DeclarationName DN =
getSema().getASTContext().DeclarationNames.getIdentifier(
&getSema().getASTContext().Idents.get(Name));

unsigned Count = 0;
for (const auto* R : DC->getPrimaryContext()->lookup(DN))
if (llvm::isa<clang::FunctionDecl, clang::FunctionTemplateDecl>(R))
++Count;
return INTEROP_RETURN(Count);
}

// A template's parameter list, reached from either the template itself or the
// entity it describes, so a caller holding a CXXRecordDecl does not have to
// find the ClassTemplateDecl first.
static const clang::TemplateParameterList*
getTemplateParameterList(const clang::Decl* D) {
if (!D)
return nullptr;
if (const auto* TD = llvm::dyn_cast<clang::TemplateDecl>(D))
return TD->getTemplateParameters();
// A partial specialization declares parameters of its own.
if (const auto* PSD =
llvm::dyn_cast<clang::ClassTemplatePartialSpecializationDecl>(D))
return PSD->getTemplateParameters();
if (const auto* RD = llvm::dyn_cast<clang::CXXRecordDecl>(D))
if (const auto* CTD = RD->getDescribedClassTemplate())
return CTD->getTemplateParameters();
if (const auto* FD = llvm::dyn_cast<clang::FunctionDecl>(D))
if (const auto* FTD = FD->getDescribedFunctionTemplate())
return FTD->getTemplateParameters();
return nullptr;
}

unsigned GetNumTemplateParameters(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* Params = getTemplateParameterList(unwrap<clang::Decl>(DRef));
return INTEROP_RETURN(Params ? Params->size() : 0);
}

DeclRef GetTemplateParameter(ConstDeclRef DRef, unsigned Index) {
INTEROP_TRACE(DRef, Index);
const auto* Params = getTemplateParameterList(unwrap<clang::Decl>(DRef));
if (!Params || Index >= Params->size())
return INTEROP_RETURN(nullptr);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return INTEROP_RETURN(const_cast<clang::NamedDecl*>(Params->getParam(Index)));
Comment thread
conrade-ctc marked this conversation as resolved.
}

TemplateParamKind GetTemplateParameterKind(ConstDeclRef DRef) {
Comment thread
conrade-ctc marked this conversation as resolved.
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (llvm::isa_and_nonnull<clang::TemplateTypeParmDecl>(D))
return INTEROP_RETURN(TemplateParamKind::Type);
if (llvm::isa_and_nonnull<clang::NonTypeTemplateParmDecl>(D))
return INTEROP_RETURN(TemplateParamKind::NonType);
if (llvm::isa_and_nonnull<clang::TemplateTemplateParmDecl>(D))
return INTEROP_RETURN(TemplateParamKind::Template);
return INTEROP_RETURN(TemplateParamKind::Unknown);
}

std::string GetTemplateParameterDefault(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (!D)
return INTEROP_RETURN("");

const clang::TemplateArgumentLoc* Default = nullptr;
if (const auto* TTP = llvm::dyn_cast<clang::TemplateTypeParmDecl>(D)) {
if (TTP->hasDefaultArgument())
Default = &TTP->getDefaultArgument();
} else if (const auto* NTTP =
llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(D)) {
if (NTTP->hasDefaultArgument())
Default = &NTTP->getDefaultArgument();
} else if (const auto* TTPD =
llvm::dyn_cast<clang::TemplateTemplateParmDecl>(D)) {
if (TTPD->hasDefaultArgument())
Default = &TTPD->getDefaultArgument();
}
if (!Default)
return INTEROP_RETURN("");

std::string Spelling;
llvm::raw_string_ostream OS(Spelling);
Default->getArgument().print(D->getASTContext().getPrintingPolicy(), OS,
/*IncludeType=*/false);
return INTEROP_RETURN(Spelling);
}

std::string GetDeclFile(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (!D)
return INTEROP_RETURN("");

clang::SourceLocation Loc = D->getLocation();
if (Loc.isInvalid())
return INTEROP_RETURN("");

const clang::SourceManager& SM = D->getASTContext().getSourceManager();
Loc = SM.getExpansionLoc(Loc);
clang::OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getFileID(Loc));
Comment thread
conrade-ctc marked this conversation as resolved.
// Interpreter input buffers are virtual files with overridden contents
// (input_line_N); they name no on-disk file, so report them as file-less.
if (!FE || SM.isFileOverridden(&FE->getFileEntry()))
return INTEROP_RETURN("");

llvm::StringRef Real = FE->getFileEntry().tryGetRealPathName();
return INTEROP_RETURN((Real.empty() ? FE->getName() : Real).str());
}

unsigned GetDeclLine(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (!D)
return INTEROP_RETURN(0);

clang::SourceLocation Loc = D->getLocation();
if (Loc.isInvalid())
return INTEROP_RETURN(0);

const clang::SourceManager& SM = D->getASTContext().getSourceManager();
return INTEROP_RETURN(SM.getSpellingLineNumber(SM.getExpansionLoc(Loc)));
}

bool IsInSystemHeader(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
if (!D)
return INTEROP_RETURN(false);

const clang::SourceManager& SM = D->getASTContext().getSourceManager();
return INTEROP_RETURN(SM.isInSystemHeader(D->getLocation()));
}

std::vector<DeclRef> GetUsingNamespaces(ConstDeclRef DRef) {
INTEROP_TRACE(DRef);
const auto* D = unwrap<clang::Decl>(DRef);
Expand Down
Loading
Loading