tools/apiview/parsers/cpp-api-parser/ApiViewProcessor/AstNode.cpp (3,285 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "AstNode.hpp"
#include "CommentExtractor.hpp"
#include "ProcessorImpl.hpp"
#include <clang/AST/Type.h>
#include <clang/AST/TypeVisitor.h>
#include <list>
#include <vector>
using namespace clang;
class AstMethod;
std::string AccessSpecifierToString(AccessSpecifier specifier)
{
switch (specifier)
{
case AS_none:
return "none";
case AS_private:
return "private";
case AS_protected:
return "protected";
case AS_public:
return "public";
}
throw std::runtime_error(
"Unknown access specifier: " + std::to_string(static_cast<int>(specifier)));
}
std::unique_ptr<AstDocumentation> AstNode::GetCommentForNode(ASTContext& context, Decl const* decl)
{
return ExtractCommentForDeclaration(context, decl);
}
AstNode::AstNode() {}
struct AstTerminalNode : public AstNode
{
AstTerminalNode() : AstNode() {}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->SetNamespace("");
}
};
/** An AST Type represents a type in the C++ language.
*
*/
class AstType {
public:
AstType(QualType type)
: m_isBuiltinType{type->isBuiltinType()}, m_isConstQualified{type.isLocalConstQualified()},
m_isVolatile{type.isLocalVolatileQualified()}, m_hasQualifiers{type.hasLocalQualifiers()},
m_isReference{type.getTypePtr()->isReferenceType()},
m_isRValueReference(type.getTypePtr()->isRValueReferenceType()),
m_isPointer{type.getTypePtr()->isPointerType()}
{
PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
m_internalTypeName = QualType::getAsString(type.split(), pp);
m_isInGlobalNamespace = IsTypeInGlobalNamespace(type.getTypePtr());
}
AstType(QualType type, const ASTContext& context)
: m_isBuiltinType{type->isBuiltinType()}, m_isConstQualified{type.isLocalConstQualified()},
m_isVolatile{type.isLocalVolatileQualified()}, m_hasQualifiers{type.hasLocalQualifiers()},
m_isReference{type.getTypePtr()->isReferenceType()},
m_isRValueReference(type.getTypePtr()->isRValueReferenceType()),
m_isPointer{type.getTypePtr()->isPointerType()}
{
PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
m_internalTypeName = QualType::getAsString(type.split(), pp);
m_isInGlobalNamespace = IsTypeInGlobalNamespace(type.getTypePtr());
// Walk the type looking for an inner type which appears to be a reasonable inner type.
// if (typePtr->getTypeClass() != Type::Elaborated && typePtr->getTypeClass() !=
// Type::Builtin)
// {
// AstTypeVisitor visitTypes;
// m_underlyingType = visitTypes.Visit(typePtr);
// }
}
void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const;
bool IsTypeInGlobalNamespace() const { return m_isInGlobalNamespace; }
private:
struct AstTypeVisitor : public TypeVisitor<AstTypeVisitor, std::unique_ptr<AstType>>
{
std::unique_ptr<AstType> VisitQualType(QualType qt)
{
llvm::outs() << "Visit QualType"
<< "\n";
return TypeVisitor::Visit(qt.split().Ty);
}
std::unique_ptr<AstType> VisitType(const Type* t)
{
llvm::outs() << "Visit Type " << QualType::getAsString(QualType(t, 0).split(), LangOptions())
<< "Type class: " << t->getTypeClassName() << "\n";
return nullptr;
}
std::unique_ptr<AstType> VisitElaboratedType(const ElaboratedType* et)
{
llvm::outs() << "Visit Elaborated type.\n";
return nullptr;
// return std::make_unique<AstType>(QualType(et, 0), xxx);
}
std::unique_ptr<AstType> VisitRValueReferenceType(const RValueReferenceType* rv)
{
llvm::outs() << "Visit RValueReferenceType" << rv->isSugared() << "\n";
return VisitQualType(rv->desugar());
}
std::unique_ptr<AstType> VisitLValueReferenceType(const LValueReferenceType* lv)
{
llvm::outs() << "Visit LValueReferenceType" << lv->isSugared() << "\n";
if (lv->isSugared())
{
return VisitQualType(lv->desugar());
}
else
{
return VisitQualType(lv->getPointeeType());
}
}
};
std::string m_internalTypeName;
bool m_isBuiltinType;
bool m_isConstQualified;
bool m_isVolatile;
bool m_hasQualifiers;
bool m_isReference;
bool m_isRValueReference;
bool m_isPointer;
bool m_isInGlobalNamespace; /// True if the type references a typedef in the global namespace.
std::unique_ptr<AstType> m_underlyingType;
// Returns true if the type contains a reference to a type which is in the global namespace.
static bool IsTypeInGlobalNamespace(Type const* typePtr)
{
// clang TypeVisitor which will iterate over the type and return true if it finds a type which
// is a typedef in the global namespace.
struct IsTypeInGlobalNamespaceVisitor : public TypeVisitor<IsTypeInGlobalNamespaceVisitor, bool>
{
IsTypeInGlobalNamespaceVisitor(){};
bool VisitTypedefType(TypedefType const* typeDef)
{
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
auto typeName{QualType::getAsString(QualType(typeDef, 0).split(), pp)};
if (typeName.find(':') == std::string::npos)
{
// size_t is valid in both the global namespace and std namespace.
if (typeName == "size_t")
{
return false;
}
return true;
}
return false;
}
bool VisitTemplateSpecializationType(TemplateSpecializationType const* tst)
{
for (const auto& arg : tst->template_arguments())
{
// We only care about type arguments.
if (arg.getKind() == TemplateArgument::ArgKind::Type)
{
if (TypeVisitor::Visit(arg.getAsType().split().Ty))
{
return true;
}
}
}
return false;
}
bool VisitTemplateArgumentType(TemplateArgument const* ta)
{
return TypeVisitor::Visit(ta->getAsType().split().Ty);
}
bool VisitElaboratedType(ElaboratedType const* et)
{
return TypeVisitor::Visit(et->getNamedType().split().Ty);
}
bool VisitLValueReferenceType(LValueReferenceType const* lrt)
{
return TypeVisitor::Visit(lrt->getPointeeType().split().Ty);
}
bool VisitRValueReferenceType(RValueReferenceType const* rrt)
{
return TypeVisitor::Visit(rrt->getPointeeType().split().Ty);
}
bool VisitPointerType(PointerType const* pt)
{
return TypeVisitor::Visit(pt->getPointeeType().split().Ty);
}
bool VisitPackExpansionType(PackExpansionType const* pe)
{
return TypeVisitor::Visit(pe->getPattern().split().Ty);
}
bool VisitIncompleteArrayType(IncompleteArrayType const* ia)
{
return TypeVisitor::Visit(ia->getArrayElementTypeNoTypeQual());
}
bool VisitConstantArrayType(ConstantArrayType const* ca)
{
return TypeVisitor::Visit(ca->getArrayElementTypeNoTypeQual());
}
bool VisitParenType(ParenType const* pt)
{
return TypeVisitor::Visit(pt->getInnerType().split().Ty);
}
// This type was declared with a "using" declaration.
//
bool VisitUsingType(UsingType const* ut)
{
bool rv{TypeVisitor::Visit(ut->getUnderlyingType().split().Ty)};
// If the underlying type is in the global namespace, but has a shadow declaration (a
// declaration introduced by a using declaration), then we treat it as if it wasn't in the
// global namespace (because it's not).
if (rv)
{
if (ut->getFoundDecl())
{
return false;
}
}
return rv;
}
bool VisitFunctionProtoType(FunctionProtoType const* fp)
{
for (const auto& arg : fp->param_types())
{
if (TypeVisitor::Visit(arg.split().Ty))
{
return true;
}
}
if (TypeVisitor::Visit(fp->getReturnType().split().Ty))
{
return true;
}
return false;
}
// Dependent names don't contain underlying types.
bool VisitDependentNameType(DependentNameType const* dn) { return false; }
// Template type params don't contain underlying types.
bool VisitTemplateTypeParmType(TemplateTypeParmType const* ttp) { return false; }
// An injected class name is a template name referenced without template parameters.
bool VisitInjectedClassNameType(InjectedClassNameType const* ic) { return false; }
// Record type params don't contain underlying types.
bool VisitRecordType(RecordType const* record) { return false; }
// Enum type params don't contain underlying types.
bool VisitEnumType(EnumType const* et) { return false; }
// Builtin type params don't contain underlying types.
bool VisitBuiltinType(BuiltinType const* bt) { return false; }
bool VisitType(const Type* t)
{
llvm::outs() << "Visit Type "
<< QualType::getAsString(QualType(t, 0).split(), LangOptions())
<< "Type class: " << t->getTypeClassName() << "\n";
t->dump();
return false;
}
};
IsTypeInGlobalNamespaceVisitor visitTypes;
return visitTypes.Visit(typePtr);
}
};
class AstStatement {
public:
AstStatement(Stmt const* statement, ASTContext& context) {}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const;
};
class AstExpr : public AstStatement {
protected:
AstType m_type;
AstExpr(Expr const* expression, ASTContext& context)
: AstStatement(expression, context), m_type{expression->getType(), context}
{
}
public:
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
static std::unique_ptr<AstExpr> Create(Stmt const* expression, ASTContext& context);
virtual bool IsEmptyExpression() const { return false; }
};
class AstIntExpr : public AstExpr {
int m_intValue{};
public:
AstIntExpr(Expr const* expression, ASTContext& context) : AstExpr(expression, context)
{
Expr::EvalResult result;
if (expression->EvaluateAsInt(result, context))
{
m_intValue = result.Val.getInt().getExtValue();
}
}
public:
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertLiteral(std::to_string(m_intValue));
}
};
class AstStringExpr : public AstExpr {
std::string m_stringValue;
public:
AstStringExpr(StringLiteral const* expression, ASTContext& context)
: AstExpr(expression, context), m_stringValue{expression->getBytes()}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertPunctuation('"');
dumper->InsertStringLiteral(m_stringValue);
dumper->InsertPunctuation('"');
}
};
class AstFloatExpr : public AstExpr {
double m_doubleValue{};
bool m_isFloat{};
public:
AstFloatExpr(FloatingLiteral const* expression, ASTContext& context)
: AstExpr(expression, context), m_doubleValue{expression->getValue().convertToDouble()}
{
auto const typePtr = expression->getType().getTypePtr();
if (isa<BuiltinType>(typePtr))
{
m_isFloat = cast<BuiltinType>(typePtr)->getKind() == BuiltinType::Kind::Float;
}
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertLiteral(std::to_string(m_doubleValue));
if (m_isFloat)
{
dumper->InsertLiteral("f");
}
}
};
class AstBoolExpr : public AstExpr {
bool m_boolValue{};
public:
AstBoolExpr(CXXBoolLiteralExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_boolValue{expression->getValue()}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (m_boolValue)
{
dumper->InsertKeyword("true");
}
else
{
dumper->InsertKeyword("false");
}
}
};
class AstImplicitCastExpr : public AstExpr {
AstType m_underlyingType;
std::unique_ptr<AstExpr> m_castValue;
public:
AstImplicitCastExpr(ImplicitCastExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_underlyingType{expression->getType(), context},
m_castValue{AstExpr::Create(*expression->child_begin(), context)}
{
// Assert that there is a single child of the ImplicitCastExprobject.
assert(++expression->child_begin() == expression->child_end());
}
std::unique_ptr<AstExpr> const& GetCastValue() const { return m_castValue; }
AstType const& GetCastType() const { return m_underlyingType; }
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override {}
};
class AstCastExpr : public AstExpr {
AstType m_underlyingType;
std::unique_ptr<AstExpr> m_castValue;
public:
AstCastExpr(CastExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_underlyingType{expression->getType(), context},
m_castValue{AstExpr::Create(*expression->child_begin(), context)}
{
// Assert that there is a single child of the ImplicitCastExprobject.
assert(++expression->child_begin() == expression->child_end());
}
std::unique_ptr<AstExpr> const& GetCastValue() const { return m_castValue; }
AstType const& GetCastType() const { return m_underlyingType; }
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_underlyingType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('(');
m_castValue->Dump(dumper, dumpOptions);
dumper->InsertPunctuation(')');
}
};
class AstCStyleCastExpr : public AstExpr {
AstType m_underlyingType;
std::unique_ptr<AstExpr> m_castValue;
public:
AstCStyleCastExpr(CStyleCastExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_underlyingType{expression->getType(), context},
m_castValue{AstExpr::Create(*expression->child_begin(), context)}
{
// Assert that there is a single child of the ImplicitCastExprobject.
assert(++expression->child_begin() == expression->child_end());
}
std::unique_ptr<AstExpr> const& GetCastValue() const { return m_castValue; }
AstType const& GetCastType() const { return m_underlyingType; }
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_underlyingType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('(');
m_castValue->Dump(dumper, dumpOptions);
dumper->InsertPunctuation(')');
}
};
// static_cast<float>(3.7);
// becomes:
// CXXStaticCastExpr 0x249a20ddb18
// <C:\Users\larryo.REDMOND\source\repos\ParseAzureSdkCpp\out\build\x64-debug\ParseTests\tests\ExpressionTests.cpp:19:22,
// col:44> 'float' static_cast<float> <NoOp>
//`-ImplicitCastExpr 0x249a20ddb00 <col:41> 'float' <FloatingCast> part_of_explicit_cast
// `-FloatingLiteral 0x249a20ddac0 <col:41> 'double' 3.700000e+00
class AstNamedCastExpr : public AstExpr {
std::unique_ptr<AstExpr> m_underlyingCast;
std::string m_castName;
public:
AstNamedCastExpr(CXXNamedCastExpr const* expression, ASTContext& context)
: AstExpr(expression, context),
m_underlyingCast{AstExpr::Create(*expression->child_begin(), context)},
m_castName{expression->getCastName()}
{
// Assert that there is a single child of the CXXStaticCastExpr object.
assert(++expression->child_begin() == expression->child_end());
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertKeyword(m_castName);
dumper->InsertPunctuation('<');
m_type.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('>');
dumper->InsertPunctuation('(');
m_underlyingCast->Dump(dumper, dumpOptions);
dumper->InsertPunctuation(')');
}
};
template <typename T>
void DumpList(
T start,
T end,
AstDumper* dumper,
DumpNodeOptions const& dumpOptions,
std::function<void(AstDumper* dumper, decltype(*start)& item)> dumpItemFunction,
std::function<void(AstDumper* dumper, DumpNodeOptions const& dumpOptions)>
insertSeparatorFunction
= [](AstDumper* dumper, DumpNodeOptions const& dumpOptions) {
dumper->InsertPunctuation(',');
if (dumpOptions.NeedsLeadingNewline)
{
dumper->Newline();
}
else
{
dumper->InsertWhitespace();
}
})
{
bool firstArg{true};
for (T& it = start; it != end; ++it)
{
if (!firstArg)
{
insertSeparatorFunction(dumper, dumpOptions);
}
firstArg = false;
dumpItemFunction(dumper, *it);
}
}
class AstCtorExpr : public AstExpr {
std::vector<std::unique_ptr<AstExpr>> m_args;
public:
AstCtorExpr(CXXConstructExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
// Reset the type to the type of the constructor.
m_type = AstType{expression->getType(), context};
int argn = 0;
for (auto const& arg : expression->arguments())
{
m_args.push_back(AstExpr::Create(arg, context));
argn += 1;
}
}
bool IsEmptyExpression() const override
{
for (const auto& initializer : m_args)
{
if (!initializer->IsEmptyExpression())
{
return false;
}
}
return true;
}
public:
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('{');
}
else
{
m_type.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('(');
}
DumpList(
m_args.begin(),
m_args.end(),
dumper,
dumpOptions,
[&](AstDumper* dumper, std::unique_ptr<AstExpr> const& expr) {
expr->Dump(dumper, dumpOptions);
});
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('}');
}
else
{
dumper->InsertPunctuation(')');
}
}
};
class AstDeclRefExpr : public AstExpr {
std::string m_referencedName;
public:
AstDeclRefExpr(DeclRefExpr const* expression, ASTContext& context)
: AstExpr(expression, context),
m_referencedName{expression->getFoundDecl()->getQualifiedNameAsString()}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (m_referencedName.empty())
{
m_type.Dump(dumper, dumpOptions);
}
else
{
dumper->InsertIdentifier(m_referencedName);
}
}
};
class AstDependentDeclRefExpr : public AstExpr {
std::string m_referencedName;
public:
AstDependentDeclRefExpr(DependentScopeDeclRefExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_referencedName{expression->getDeclName().getAsString()}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_type.Dump(dumper, dumpOptions);
dumper->InsertPunctuation(':');
dumper->InsertPunctuation(':');
dumper->InsertIdentifier(m_referencedName);
}
};
class AstNullptrRefExpr : public AstExpr {
public:
AstNullptrRefExpr(CXXNullPtrLiteralExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertKeyword("nullptr");
}
};
class AstMethodCallExpr : public AstExpr {
std::string m_calledMethod;
std::unique_ptr<AstExpr> m_memberAccessor;
std::list<std::unique_ptr<AstExpr>> m_methodParams;
public:
AstMethodCallExpr(CXXMemberCallExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
m_calledMethod = expression->getMethodDecl()->getNameAsString();
m_type = AstType{expression->getObjectType()};
assert(++expression->child_begin() == expression->child_end());
assert(isa<MemberExpr>(*expression->child_begin()));
m_memberAccessor = AstExpr::Create(*expression->child_begin(), context);
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
class AstInitializerList : public AstExpr {
std::vector<std::unique_ptr<AstExpr>> m_initializerValues;
protected:
bool IsEmptyExpression() const override
{
for (const auto& initializer : m_initializerValues)
{
if (!initializer->IsEmptyExpression())
{
return false;
}
}
return true;
}
public:
AstInitializerList(InitListExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
for (const auto& initializer : expression->children())
{
m_initializerValues.push_back(AstExpr::Create(initializer, context));
}
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
class AstMemberExpr : public AstExpr {
std::string m_memberMethod;
std::unique_ptr<AstExpr> m_member;
public:
AstMemberExpr(MemberExpr const* expression, ASTContext& context)
: AstExpr(expression, context),
m_memberMethod{expression->getMemberDecl()->getNameAsString()},
m_member{AstExpr::Create(*expression->child_begin(), context)}
{
assert(++expression->child_begin() == expression->child_end());
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
class AstCallExpr : public AstExpr {
std::string m_methodToCall;
std::list<std::unique_ptr<AstExpr>> m_arguments;
public:
AstCallExpr(CallExpr const* expression, ASTContext& context) : AstExpr(expression, context)
{
m_methodToCall = expression->getDirectCallee()->getQualifiedNameAsString();
for (auto const& arg : expression->arguments())
{
m_arguments.push_back(AstExpr::Create(arg, context));
}
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('{');
}
dumper->InsertIdentifier(m_methodToCall);
dumper->InsertPunctuation('(');
DumpList(
m_arguments.begin(),
m_arguments.end(),
dumper,
dumpOptions,
[&](AstDumper* dumper, std::unique_ptr<AstExpr> const& expr) {
expr->Dump(dumper, dumpOptions);
});
dumper->InsertPunctuation(')');
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('}');
}
};
};
class AstBinaryOperatorExpr : public AstExpr {
std::unique_ptr<AstExpr> m_leftOperator;
std::unique_ptr<AstExpr> m_rightOperator;
BinaryOperator::Opcode m_opcode;
std::string m_opcodeString;
public:
AstBinaryOperatorExpr(BinaryOperator const* expression, ASTContext& context)
: AstExpr(expression, context),
m_leftOperator{AstExpr::Create(expression->getLHS(), context)},
m_rightOperator{AstExpr::Create(expression->getRHS(), context)},
m_opcode(expression->getOpcode()), m_opcodeString(expression->getOpcodeStr())
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_leftOperator->Dump(dumper, dumpOptions);
for (const auto ch : m_opcodeString)
{
dumper->InsertPunctuation(ch);
}
m_rightOperator->Dump(dumper, dumpOptions);
};
};
class AstUnaryOperatorExpr : public AstExpr {
std::unique_ptr<AstExpr> m_subExpr;
bool m_isPrefix;
bool m_isPostfix;
std::string m_opcode;
public:
AstUnaryOperatorExpr(UnaryOperator const* expression, ASTContext& context)
: AstExpr(expression, context), m_subExpr{AstExpr::Create(expression->getSubExpr(), context)},
m_opcode(expression->getOpcodeStr(expression->getOpcode())),
m_isPrefix(expression->isPrefix()), m_isPostfix(expression->isPostfix())
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (m_isPrefix)
{
if (m_opcode.size() == 1)
{
dumper->InsertPunctuation(m_opcode[0]);
}
else
{
dumper->InsertKeyword(m_opcode);
}
}
m_subExpr->Dump(dumper, dumpOptions);
if (m_isPostfix)
{
if (m_opcode.size() == 1)
{
dumper->InsertPunctuation(m_opcode[0]);
}
else
{
dumper->InsertKeyword(m_opcode);
}
}
};
};
class AstScalarValueInit : public AstExpr {
AstType m_underlyingType;
public:
AstScalarValueInit(CXXScalarValueInitExpr const* expression, ASTContext& context)
: AstExpr(expression, context),
m_underlyingType{expression->getTypeSourceInfo()->getType(), context}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_underlyingType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('(');
dumper->InsertPunctuation(')');
}
};
class AstImplicitValueInit : public AstExpr {
AstType m_underlyingType;
bool IsEmptyExpression() const override { return true; }
public:
AstImplicitValueInit(ImplicitValueInitExpr const* expression, ASTContext& context)
: AstExpr(expression, context), m_underlyingType{expression->getType(), context}
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_underlyingType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('{');
dumper->InsertPunctuation('}');
}
};
class AstDefaultInitExpr : public AstExpr {
bool IsEmptyExpression() const override { return true; }
public:
AstDefaultInitExpr(CXXDefaultInitExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
// dumper->InsertPunctuation('{');
// dumper->InsertPunctuation('}');
}
};
class AstDefaultArgExpr : public AstExpr {
bool IsEmptyExpression() const override { return true; }
public:
AstDefaultArgExpr(CXXDefaultArgExpr const* expression, ASTContext& context)
: AstExpr(expression, context)
{
}
virtual void Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
// dumper->InsertPunctuation('{');
// dumper->InsertPunctuation('}');
}
};
std::unique_ptr<AstExpr> AstExpr::Create(Stmt const* statement, ASTContext& context)
{
if (statement)
{
if (isa<Expr>(statement))
{
auto expression = cast<Expr>(statement);
const Expr* actualExpr = expression->IgnoreUnlessSpelledInSource();
switch (actualExpr->getStmtClass())
{
case Stmt::IntegerLiteralClass:
return std::make_unique<AstIntExpr>(cast<IntegerLiteral>(actualExpr), context);
case Stmt::StringLiteralClass:
return std::make_unique<AstStringExpr>(cast<StringLiteral>(actualExpr), context);
case Stmt::FloatingLiteralClass:
return std::make_unique<AstFloatExpr>(cast<FloatingLiteral>(actualExpr), context);
case Stmt::CXXBoolLiteralExprClass:
return std::make_unique<AstBoolExpr>(cast<CXXBoolLiteralExpr>(actualExpr), context);
case Stmt::ImplicitCastExprClass:
return std::make_unique<AstImplicitCastExpr>(cast<ImplicitCastExpr>(actualExpr), context);
case Stmt::CXXDefaultInitExprClass:
return std::make_unique<AstDefaultInitExpr>(
cast<CXXDefaultInitExpr>(actualExpr), context);
case Stmt::CXXDefaultArgExprClass:
return std::make_unique<AstDefaultArgExpr>(cast<CXXDefaultArgExpr>(actualExpr), context);
case Stmt::CXXConstructExprClass:
return std::make_unique<AstCtorExpr>(cast<CXXConstructExpr>(actualExpr), context);
case Stmt::DependentScopeDeclRefExprClass:
return std::make_unique<AstDependentDeclRefExpr>(
cast<DependentScopeDeclRefExpr>(actualExpr), context);
case Stmt::DeclRefExprClass:
return std::make_unique<AstDeclRefExpr>(cast<DeclRefExpr>(actualExpr), context);
case Stmt::CXXNullPtrLiteralExprClass:
return std::make_unique<AstNullptrRefExpr>(
cast<CXXNullPtrLiteralExpr>(actualExpr), context);
case Stmt::MemberExprClass:
return std::make_unique<AstMemberExpr>(cast<MemberExpr>(actualExpr), context);
case Stmt::CXXMemberCallExprClass:
return std::make_unique<AstMethodCallExpr>(cast<CXXMemberCallExpr>(actualExpr), context);
case Stmt::InitListExprClass:
return std::make_unique<AstInitializerList>(cast<InitListExpr>(actualExpr), context);
case Stmt::UnaryOperatorClass:
return std::make_unique<AstUnaryOperatorExpr>(cast<UnaryOperator>(actualExpr), context);
case Stmt::BinaryOperatorClass:
return std::make_unique<AstBinaryOperatorExpr>(cast<BinaryOperator>(actualExpr), context);
case Stmt::CXXScalarValueInitExprClass:
return std::make_unique<AstScalarValueInit>(
cast<CXXScalarValueInitExpr>(actualExpr), context);
case Stmt::ImplicitValueInitExprClass:
return std::make_unique<AstImplicitValueInit>(
cast<ImplicitValueInitExpr>(actualExpr), context);
case Stmt::MaterializeTemporaryExprClass:
// Assert that there is a single child of the MaterializeTemporaryExpr object.
assert(++actualExpr->child_begin() == actualExpr->child_end());
return Create(*actualExpr->child_begin(), context);
case Stmt::CXXTemporaryObjectExprClass:
return std::make_unique<AstCtorExpr>(cast<CXXConstructExpr>(actualExpr), context);
case Stmt::CXXConstCastExprClass:
case Stmt::CXXStaticCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
return std::make_unique<AstCastExpr>(cast<CastExpr>(actualExpr), context);
case Stmt::CXXStdInitializerListExprClass:
// Assert that there is a single child of the CxxStdInitializerListExpr object.
assert(++actualExpr->child_begin() == actualExpr->child_end());
return Create(*actualExpr->child_begin(), context);
case Stmt::CallExprClass:
return std::make_unique<AstCallExpr>(cast<CallExpr>(actualExpr), context);
case Stmt::ExprWithCleanupsClass:
// Assert that there is a single child of the ExprWithCleanupsClass.
assert(++actualExpr->child_begin() == actualExpr->child_end());
return Create(*actualExpr->child_begin(), context);
case Stmt::CStyleCastExprClass:
return std::make_unique<AstCStyleCastExpr>(cast<CStyleCastExpr>(actualExpr), context);
default:
llvm::errs() << raw_ostream::Colors::RED
<< "Unknown expression type : " << actualExpr->getStmtClassName()
<< raw_ostream::Colors::RESET << "\n ";
actualExpr->dump(llvm::outs(), context);
return nullptr;
}
// else if (isa<CXXNamedCastExpr>(actualExpr))
//{
// return std::make_unique<AstNamedCastExpr>(cast<CXXNamedCastExpr>(actualExpr), context);
// }
// else if (isa<CastExpr>(actualExpr))
//{
// return std::make_unique<AstCastExpr>(cast<CastExpr>(actualExpr), context);
// }
}
else
{
assert(isa<Stmt>(statement));
llvm::errs() << raw_ostream::Colors::RED
<< "Unknown statement type : " << statement->getStmtClassName()
<< raw_ostream::Colors::RESET << "\n ";
statement->dump(llvm::outs(), context);
return nullptr;
}
}
else
{
return nullptr;
}
}
class AstAttribute : public AstNode {
public:
AstAttribute(clang::Attr const* attribute)
: AstNode(), m_syntax{attribute->getSyntax()}, m_attributeKind{attribute->getKind()},
m_attributeName{attribute->getSpelling()}
{
switch (m_attributeKind)
{
case attr::Kind::Deprecated: {
auto deprecated = cast<DeprecatedAttr>(attribute);
m_deprecatedMessage = deprecated->getMessage();
m_deprecatedReplacement = deprecated->getReplacement();
break;
}
case attr::Kind::WarnUnusedResult: {
auto nodiscard = cast<WarnUnusedResultAttr>(attribute);
m_nodiscardMessage = nodiscard->getMessage();
break;
}
case attr::Kind::CXX11NoReturn:
break;
default: {
std::string printedAttribute;
llvm::raw_string_ostream os{printedAttribute};
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
attribute->printPretty(os, pp);
llvm::errs() << raw_ostream::Colors::RED
<< "Unknown deprecated type : " << attribute->getKind()
<< "Name: " << attribute->getSpelling() << "Pretty Print: " << printedAttribute
<< raw_ostream::Colors::RESET << "\n ";
}
break;
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& options) const override
{
switch (m_attributeKind)
{
case attr::Kind::Deprecated:
DumpDeprecated(dumper, options);
break;
case attr::Kind::WarnUnusedResult:
DumpNoDiscard(dumper, options);
break;
case attr::Kind::CXX11NoReturn:
dumper->InsertPunctuation('[');
dumper->InsertPunctuation('[');
dumper->InsertKeyword("noreturn");
dumper->InsertPunctuation(']');
dumper->InsertPunctuation(']');
break;
default:
llvm::outs() << llvm::raw_ostream::Colors::RED
<< "Unknown attribute kind: " << m_attributeKind
<< llvm::raw_ostream::Colors::RESET;
break;
}
}
void DumpNoDiscard(AstDumper* dumper, DumpNodeOptions const& options) const
{
if (options.NeedsLeftAlign)
{
dumper->LeftAlign();
}
switch (m_syntax)
{
case Attr::Syntax::AS_C23:
case Attr::Syntax::AS_CXX11:
dumper->InsertPunctuation('[');
dumper->InsertPunctuation('[');
dumper->InsertKeyword(m_attributeName);
if (!m_nodiscardMessage.empty())
{
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_nodiscardMessage);
dumper->InsertPunctuation('"');
dumper->InsertPunctuation(')');
}
dumper->InsertPunctuation(']');
dumper->InsertPunctuation(']');
break;
default:
llvm::outs() << "UNknown syntax for nodiscard\n";
break;
}
}
void DumpDeprecated(AstDumper* dumper, DumpNodeOptions const& options) const
{
if (options.NeedsLeftAlign)
{
dumper->LeftAlign();
}
switch (m_syntax)
{
case Attr::Syntax::AS_C23:
case Attr::Syntax::AS_CXX11:
dumper->InsertPunctuation('[');
dumper->InsertPunctuation('[');
dumper->InsertKeyword(m_attributeName);
if (!m_deprecatedMessage.empty())
{
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_deprecatedMessage);
dumper->InsertPunctuation('"');
if (!m_deprecatedReplacement.empty())
{
dumper->InsertPunctuation(',');
dumper->InsertWhitespace();
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_deprecatedReplacement);
dumper->InsertPunctuation('"');
}
dumper->InsertPunctuation(')');
}
dumper->InsertPunctuation(']');
dumper->InsertPunctuation(']');
break;
case Attr::Syntax::AS_GNU: {
// __attribute__((deprecated("<message>"[,"<replacement>")))
dumper->InsertKeyword("__attribute__");
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('(');
dumper->InsertKeyword("deprecated");
if (!m_deprecatedMessage.empty())
{
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_deprecatedMessage);
dumper->InsertPunctuation('"');
if (!m_deprecatedReplacement.empty())
{
dumper->InsertPunctuation(',');
dumper->InsertWhitespace();
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_deprecatedReplacement);
dumper->InsertPunctuation('"');
}
dumper->InsertPunctuation(')');
}
dumper->InsertPunctuation(')');
dumper->InsertPunctuation(')');
}
break;
case Attr::Syntax::AS_Declspec:
dumper->InsertKeyword("__declspec");
dumper->InsertPunctuation('(');
dumper->InsertKeyword("deprecated");
if (!m_deprecatedMessage.empty())
{
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('"');
dumper->InsertLiteral(m_deprecatedMessage);
dumper->InsertPunctuation('"');
dumper->InsertPunctuation(')');
}
dumper->InsertPunctuation(')');
break;
default:
break;
}
}
private:
Attr::Syntax m_syntax;
attr::Kind m_attributeKind;
std::string m_attributeName;
std::string m_deprecatedMessage;
std::string m_deprecatedReplacement;
std::string m_nodiscardMessage;
};
AstNamedNode::AstNamedNode(
NamedDecl const* namedDecl,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNode(), m_namespace{AstNode::GetNamespaceForDecl(namedDecl)},
m_name{namedDecl->getNameAsString()}, m_classDatabase(database),
m_navigationId{namedDecl->getQualifiedNameAsString()},
m_nodeDocumentation{AstNode::GetCommentForNode(namedDecl->getASTContext(), namedDecl)},
m_nodeAccess{namedDecl->getAccess()}
{
{
auto location = namedDecl->getLocation();
auto const& sourceManager = namedDecl->getASTContext().getSourceManager();
auto const& presumedLocation = sourceManager.getPresumedLoc(location);
std::string typeLocation{presumedLocation.getFilename()};
// Remove the root directory from the location if the location is within the root directory.
if (typeLocation.find(database->GetProcessor()->RootDirectory()) == 0)
{
typeLocation.erase(0, database->GetProcessor()->RootDirectory().size() + 1);
}
if (!database->GetProcessor()->SourceRepository().empty())
{
m_typeUrl = database->GetProcessor()->SourceRepository();
m_typeUrl += "/" + typeLocation;
m_typeUrl += "#L" + std::to_string(presumedLocation.getLine());
}
m_typeLocation = typeLocation;
m_typeLocation += ":" + std::to_string(presumedLocation.getLine());
m_typeLocation += ":" + std::to_string(presumedLocation.getColumn());
}
if (namedDecl->hasAttrs())
{
for (const auto& attr : namedDecl->attrs())
{
// We want to skip certain attributes like Final.
if ((attr->getKind() != attr::Kind::Final) && (attr->getKind() != attr::Kind::Override))
{
m_nodeAttributes.push_back(std::make_unique<AstAttribute>(attr));
}
}
}
}
void AstNamedNode::DumpAttributes(AstDumper* dumper, DumpNodeOptions const& options) const
{
if (!m_nodeAttributes.empty())
{
DumpNodeOptions innerOptions{options};
innerOptions.NeedsLeftAlign = true;
innerOptions.NeedsTrailingNewline = true;
innerOptions.NeedsTrailingSemi = false;
innerOptions.NeedsLeadingNewline = true;
DumpList(
m_nodeAttributes.begin(),
m_nodeAttributes.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& node) {
node->DumpNode(dumper, innerOptions);
},
[](AstDumper* dumper, DumpNodeOptions const& options) { dumper->Newline(); });
}
}
void AstNamedNode::DumpDocumentation(AstDumper* dumper, DumpNodeOptions const& options) const
{
if (options.NeedsDocumentation)
{
if (m_nodeDocumentation)
{
dumper->AddDocumentRangeStart();
if (options.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertComment("/**");
{
DumpNodeOptions innerOptions{options};
innerOptions.NeedsLeftAlign = true;
innerOptions.NeedsLeadingNewline = true;
innerOptions.NeedsTrailingNewline = false;
m_nodeDocumentation->DumpNode(dumper, innerOptions);
}
dumper->Newline(); // We need to insert a newline here to ensure that the comment is
// properly closed.
dumper->LeftAlign();
dumper->InsertComment(" */");
if (options.NeedsTrailingNewline)
{
dumper->Newline();
}
dumper->AddDocumentRangeEnd();
}
}
}
// Dump a comment showing where the node is located within the source code.
// If the customer gave us a source URL for the ApiView, include a link to the type.
void AstNamedNode::DumpSourceComment(AstDumper* dumper, DumpNodeOptions const& options) const
{
if (options.NeedsSourceComment)
{
dumper->AddSkipDiffRangeStart();
if (options.NeedsLeadingNewline)
{
dumper->Newline();
}
if (options.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertComment("// ");
if (!m_typeUrl.empty())
{
dumper->AddExternalLinkStart(m_typeUrl);
dumper->InsertComment(m_typeLocation);
dumper->AddExternalLinkEnd();
}
else
{
dumper->InsertComment(m_typeLocation);
}
if (options.NeedsTrailingNewline)
{
dumper->Newline();
}
dumper->AddSkipDiffRangeEnd();
}
}
class AstBaseClass {
AstType m_baseClass;
AccessSpecifier m_access;
public:
AstBaseClass(CXXBaseSpecifier const& base)
: m_baseClass{base.getType()}, m_access{base.getAccessSpecifierAsWritten()} {};
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions)
{
if (m_access != AS_none)
{
dumper->InsertKeyword(AccessSpecifierToString(m_access));
dumper->InsertWhitespace();
}
m_baseClass.Dump(dumper, dumpOptions);
}
};
// For functions, we want the navigation ID to be the full signature, including the return type
// to handle overloads.
static std::string GetNavigationId(clang::FunctionDecl const* func)
{
std::string navigationId;
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
pp.FullyQualifiedName = true;
pp.TerseOutput = true;
llvm::raw_string_ostream os(navigationId);
func->print(os, pp);
return navigationId;
}
static std::string GetNavigationId(clang::CXXRecordDecl const* record)
{
return record->getQualifiedNameAsString();
}
/**
* Get the navigation ID for a qualified type declaration.
*
* The navigation ID uniquely
* identifies a declaration in the ApiView. For qualified type
* declarations, we want to include
* the full type name, including the namespace.
*
* @param type The type to get the navigation ID
* for.
* @return The navigation ID for the type.
*
*/
static std::string GetNavigationId(clang::QualType const& type)
{
std::string navigationId;
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
pp.FullyQualifiedName = true;
pp.TerseOutput = true;
llvm::raw_string_ostream os(navigationId);
type.print(os, pp);
return navigationId;
}
/**
* Get the navigation ID for a friend declaration.
*
* The navigation ID uniquely identifies a
* declaration in the ApiView. For friend declarations, we
* want to include the navigation ID of
* the parent node, followed by "_friend_", followed by the
* navigation ID of the friend
* declaration.
*
* @param friendDecl The friend declaration to get the navigation ID for.
* @param
* parentNodeNavigationId The navigation ID of the parent node.
* @return The navigation ID for the
* friend declaration.
*
*/
static std::string GetNavigationId(
clang::FriendDecl const* friendDecl,
std::string const& parentNodeNavigationId)
{
if (parentNodeNavigationId.empty())
{
llvm::errs() << "Parent node navigation ID is empty\n";
}
std::string navigationId = parentNodeNavigationId + "_friend_";
auto dc = friendDecl->getFriendDecl();
if (dc)
{
if (isa<FunctionDecl>(dc))
{
auto fd = cast<FunctionDecl>(dc);
navigationId += GetNavigationId(fd);
}
else if (isa<CXXRecordDecl>(dc))
{
auto rd = cast<CXXRecordDecl>(dc);
navigationId += GetNavigationId(rd);
}
else
{
llvm::errs() << "Unknown friend declaration type.\n";
dc->dump(llvm::outs());
}
}
else
{
auto friendType = friendDecl->getFriendType();
if (friendType)
{
navigationId += GetNavigationId(friendType->getType());
navigationId
+= QualType::getAsString(friendDecl->getFriendType()->getType().split(), LangOptions{});
}
else
{
llvm::errs() << "Unknown friend declaration.\n";
friendDecl->dump(llvm::outs());
}
}
return navigationId;
}
static std::string GetNavigationId(clang::ParmVarDecl const* param)
{
std::string navigationId;
auto dc = param->getParentFunctionOrMethod();
if (dc)
{
auto fd = cast<FunctionDecl>(dc);
navigationId = GetNavigationId(fd) + " param " + param->getNameAsString();
}
else
{
param->dump(llvm::outs());
}
return navigationId;
}
static std::string GetNavigationId(
clang::ClassTemplateSpecializationDecl const* templateSpecialization)
{
std::string navigationId = templateSpecialization->getQualifiedNameAsString();
navigationId += "<";
for (const auto& arg : templateSpecialization->getTemplateArgs().asArray())
{
navigationId += " " + arg.getAsType().getAsString();
}
navigationId += ">";
return navigationId;
}
static std::string GetNavigationId(UsingDecl const* usingDeclaration)
{
std::string navigationId = "using ";
navigationId += usingDeclaration->getQualifiedNameAsString();
return navigationId;
}
class AstParamVariable : public AstNamedNode {
std::string m_typeAsString;
std::string m_fullTypeAsString;
AstType m_type;
bool m_isStatic{};
bool m_isArray{};
std::string m_variableInitializer;
std::unique_ptr<AstExpr> m_defaultExpression;
public:
AstParamVariable(
ParmVarDecl const* var,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(var, database, parentNode), m_type{var->getType(), var->getASTContext()},
m_fullTypeAsString{var->getQualifiedNameAsString()}, m_isStatic(var->isStaticDataMember())
{
m_navigationId = GetNavigationId(var);
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_type.IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
m_typeAsString = QualType::getAsString(var->getType().split(), pp);
if (var->getType().getTypePtr()->isArrayType())
{
m_isArray = true;
m_typeAsString = QualType::getAsString(
QualType{var->getType().getTypePtr()->getArrayElementTypeNoTypeQual(), 0}.split(), pp);
}
auto value = var->getEvaluatedValue();
if (value)
{
llvm::raw_string_ostream os{m_variableInitializer};
value->printPretty(os, var->getASTContext(), var->getType());
}
if (var->getDefaultArg())
{
m_defaultExpression = AstExpr::Create(var->getDefaultArg(), var->getASTContext());
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
if (m_isStatic)
{
dumper->InsertKeyword("static");
dumper->InsertWhitespace();
}
dumper->InsertLiteral(m_typeAsString);
dumper->InsertWhitespace();
// If the parameter name isn't provided, there's nothing to insert into the output.
if (!Name().empty())
{
dumper->InsertMemberName(Name(), m_navigationId);
}
if (m_isArray)
{
dumper->InsertPunctuation('[');
dumper->InsertPunctuation(']');
}
if (m_defaultExpression || !m_variableInitializer.empty())
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
if (m_defaultExpression)
{
m_defaultExpression->Dump(dumper, dumpOptions);
}
else
{
dumper->InsertLiteral(m_variableInitializer);
}
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstVariable : public AstNamedNode {
std::string m_typeAsString;
AstType m_type;
bool m_isStatic{};
bool m_isConstexpr{};
bool m_isConst{};
bool m_isArray{};
std::string m_variableInitializer;
public:
AstVariable(
VarDecl const* var,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(var, database, parentNode), m_type{var->getType(), var->getASTContext()},
m_isStatic(var->isStaticDataMember()), m_isConstexpr(var->isConstexpr()),
m_isConst(var->getType().isConstQualified())
{
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_type.IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
m_typeAsString = QualType::getAsString(var->getType().split(), pp);
if (var->getType().getTypePtr()->isArrayType())
{
m_isArray = true;
m_typeAsString = QualType::getAsString(
QualType{var->getType().getTypePtr()->getArrayElementTypeNoTypeQual(), 0}.split(), pp);
}
auto value = var->getEvaluatedValue();
if (value)
{
llvm::raw_string_ostream os{m_variableInitializer};
value->printPretty(os, var->getASTContext(), var->getType());
}
if (m_isStatic && !(m_isConstexpr || m_isConst))
{
database->CreateApiViewMessage(ApiViewMessages::NonConstStaticFields, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
if (m_isStatic)
{
dumper->InsertKeyword("static");
dumper->InsertWhitespace();
}
if (m_isConstexpr)
{
dumper->InsertKeyword("constexpr");
dumper->InsertWhitespace();
}
dumper->InsertLiteral(m_typeAsString);
dumper->InsertWhitespace();
dumper->InsertMemberName(Name(), m_navigationId);
if (m_isArray)
{
dumper->InsertPunctuation('[');
dumper->InsertPunctuation(']');
}
if (!m_variableInitializer.empty())
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertLiteral(m_variableInitializer);
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstTemplateParameter : public AstNamedNode {
bool m_wasDeclaredWithTypename{};
bool m_isParameterPack{};
std::string m_paramName;
std::unique_ptr<AstType> m_defaultValue;
public:
AstTemplateParameter(
TemplateTypeParmDecl const* param,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(param, database, parentNode),
m_wasDeclaredWithTypename{param->wasDeclaredWithTypename()},
m_paramName{param->getNameAsString()}, m_isParameterPack{param->isParameterPack()}
{
if (param->hasDefaultArgument())
{
m_defaultValue
= std::make_unique<AstType>(param->getDefaultArgument(), param->getASTContext());
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_defaultValue->IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
}
for (auto attr : param->attrs())
{
llvm::outs() << "Attribute: " << attr->getSpelling() << "\n";
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (m_wasDeclaredWithTypename)
{
dumper->InsertKeyword("typename");
}
else
{
dumper->InsertKeyword("class");
}
if (m_isParameterPack)
{
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
}
if (!m_paramName.empty())
{
dumper->InsertWhitespace();
dumper->InsertIdentifier(m_paramName);
if (m_defaultValue)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
m_defaultValue->Dump(dumper, dumpOptions);
}
}
}
};
// Template parameters which are template declarations. For example:
// template <typename T, template <typename> class U = UniqueHandleHelper>
// using UniqueHandle = typename U<T>::type;
// Also "T" in
// @code
// template <template <typename> class T> class container { };
// @endcode
class AstTemplateTemplateParameter : public AstNamedNode {
bool m_isParameterPack{};
std::string m_paramName;
std::list<std::unique_ptr<AstNode>> m_parameters;
std::unique_ptr<AstNode> m_templateBody;
std::string m_defaultTypeName;
public:
AstTemplateTemplateParameter(
TemplateTemplateParmDecl const* templateParam,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(templateParam, database, parentNode),
m_paramName{templateParam->getNameAsString()},
m_isParameterPack{templateParam->isParameterPack()}
{
for (auto attr : templateParam->attrs())
{
llvm::outs() << "Attribute: " << attr->getSpelling() << "\n";
}
for (auto& param : templateParam->getTemplateParameters()->asArray())
{
m_parameters.push_back(AstNode::Create(param, database, parentNode));
}
if (templateParam->hasDefaultArgument())
{
auto const& defaultArg = templateParam->getDefaultArgument().getArgument();
switch (defaultArg.getKind())
{
case TemplateArgument::Template: {
llvm::raw_string_ostream os(m_defaultTypeName);
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
defaultArg.getAsTemplate().print(os, pp);
}
break;
default:
llvm::errs() << "Unknown TemplateTemplate parameter default argument type: "
<< defaultArg.getKind() << "\n";
break;
}
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertKeyword("template");
dumper->InsertWhitespace();
dumper->InsertPunctuation('<');
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsTrailingNewline = false;
innerOptions.NeedsTrailingSemi = false;
innerOptions.NeedsLeadingNewline = false;
DumpList(
m_parameters.begin(),
m_parameters.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& param) {
param->DumpNode(dumper, innerOptions);
});
}
dumper->InsertPunctuation('>');
if (m_isParameterPack)
{
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
}
dumper->InsertWhitespace();
dumper->InsertKeyword("class");
dumper->InsertWhitespace();
dumper->InsertIdentifier(m_paramName);
if (!m_defaultTypeName.empty())
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertIdentifier(m_defaultTypeName);
}
}
};
class AstNonTypeTemplateParam : public AstNamedNode {
std::unique_ptr<AstExpr> m_defaultArgument;
AstType m_templateType;
public:
AstNonTypeTemplateParam(
NonTypeTemplateParmDecl const* param,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(param, database, parentNode),
m_defaultArgument(AstExpr::Create(param->getDefaultArgument(), param->getASTContext())),
m_templateType{param->getType(), param->getASTContext()}
{
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_templateType.IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
m_templateType.Dump(dumper, dumpOptions);
if (m_defaultArgument)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
m_defaultArgument->Dump(dumper, dumpOptions);
}
}
};
// using rep = int64_t becomes:
//
// TypeAliasDecl 0x122e6738270
// <G:\Az\LarryO\azure-sdk-for-cpp\sdk\core\azure-core\inc\azure/core/datetime.hpp:22:5,
// col:17> col:11 referenced rep 'int64_t':'long long'
//`- TypedefType 0x122e5ed47f0 'int64_t' sugar
// | -Typedef 0x122e505f3e0 'int64_t'
// `
// - BuiltinType 0x122e4762c70 'long long'
class AstTypeAlias : public AstNamedNode {
AstType m_aliasedType;
public:
AstTypeAlias(
TypeAliasDecl const* alias,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(alias, database, parentNode),
m_aliasedType{alias->getUnderlyingType(), alias->getASTContext()}
{
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_aliasedType.IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("using");
dumper->InsertWhitespace();
dumper->InsertTypeName(Name(), m_navigationId);
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
m_aliasedType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation(';');
dumper->Newline();
}
};
class AstFunction : public AstNamedNode {
protected:
bool m_isConstexpr;
bool m_isStatic;
std::vector<std::unique_ptr<AstNode>> m_parameters;
AstType m_returnValue;
bool m_isMemberOfClass;
bool m_isSpecialFunction;
ExceptionSpecificationType m_exceptionSpecification;
std::string m_exceptionExpression;
std::string m_parentClass;
protected:
void DumpExceptionSpecification(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
switch (m_exceptionSpecification)
{
case EST_None: ///< no exception specification
case EST_NoThrow: ///< Microsoft __declspec(nothrow) extension
break;
case EST_DynamicNone: ///< throw()
dumper->InsertWhitespace();
dumper->InsertKeyword("throw");
dumper->InsertPunctuation('(');
dumper->InsertPunctuation(')');
break;
case EST_Dynamic: ///< throw(T1, T2)
break;
case EST_MSAny: ///< Microsoft throw(...) extension
dumper->InsertWhitespace();
dumper->InsertKeyword("throw");
dumper->InsertPunctuation('(');
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
dumper->InsertPunctuation('.');
dumper->InsertPunctuation(')');
break;
case EST_BasicNoexcept: ///< noexcept
dumper->InsertWhitespace();
dumper->InsertKeyword("noexcept");
break;
case EST_NoexceptFalse: ///< noexcept(expression), evals to 'false'
dumper->InsertWhitespace();
dumper->InsertKeyword("noexcept");
dumper->InsertPunctuation('(');
dumper->InsertKeyword("false");
dumper->InsertPunctuation(')');
break;
case EST_NoexceptTrue: ///< noexcept(expression), evals to 'true'
dumper->InsertWhitespace();
dumper->InsertKeyword("noexcept");
dumper->InsertPunctuation('(');
dumper->InsertKeyword("true");
dumper->InsertPunctuation(')');
break;
case EST_DependentNoexcept: ///< noexcept(expression), value-dependent
{
dumper->InsertWhitespace();
dumper->InsertKeyword("noexcept");
dumper->InsertPunctuation('(');
dumper->InsertLiteral(m_exceptionExpression);
dumper->InsertPunctuation(')');
break;
}
default:
llvm::errs() << "Unknown exception specification: " << m_exceptionSpecification << "\n";
break;
}
}
public:
AstFunction(
FunctionDecl const* func,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(func, database, parentNode), m_isConstexpr(func->isConstexpr()),
m_isStatic(func->isStatic()), m_returnValue(func->getReturnType(), func->getASTContext()),
m_isMemberOfClass{func->isCXXClassMember()},
m_isSpecialFunction{
func->getKind() == Decl::CXXConstructor || func->getKind() == Decl::CXXDestructor},
m_exceptionSpecification{func->getExceptionSpecType()}
{
m_navigationId = GetNavigationId(func);
// If the type of the return value is in the global namespace, then flag it as an error.
if (m_returnValue.IsTypeInGlobalNamespace())
{
database->CreateApiViewMessage(ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
if (m_exceptionSpecification == EST_DependentNoexcept)
{
auto typePtr = func->getType().getTypePtr();
if (isa<FunctionProtoType>(typePtr))
{
auto functionPrototype = cast<FunctionProtoType>(typePtr);
if (functionPrototype->getNoexceptExpr())
{
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
llvm::raw_string_ostream os(m_exceptionExpression);
functionPrototype->getNoexceptExpr()->printPretty(os, nullptr, pp);
}
}
}
for (auto param : func->parameters())
{
m_parameters.push_back(AstNode::Create(param, database, parentNode));
}
if (Namespace().empty())
{
database->CreateApiViewMessage(
ApiViewMessages::TypeDeclaredInGlobalNamespace, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
DumpDocumentation(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
DumpAttributes(dumper, dumpOptions);
if (m_exceptionSpecification == EST_NoThrow)
{
dumper->InsertKeyword("__declspec");
dumper->InsertPunctuation('(');
dumper->InsertKeyword("nothrow");
dumper->InsertPunctuation(')');
dumper->InsertWhitespace();
}
if (!m_isSpecialFunction)
{
if (m_isStatic)
{
dumper->InsertKeyword("static");
dumper->InsertWhitespace();
}
if (m_isConstexpr)
{
dumper->InsertKeyword("constexpr");
dumper->InsertWhitespace();
}
m_returnValue.Dump(dumper, dumpOptions);
dumper->InsertWhitespace();
}
if (dumpOptions.IncludeNamespace)
{
dumper->InsertIdentifier(Namespace());
dumper->InsertPunctuation(':');
dumper->InsertPunctuation(':');
}
if (dumpOptions.IncludeContainingClass && !m_parentClass.empty())
{
dumper->InsertIdentifier(m_parentClass);
dumper->InsertPunctuation(':');
dumper->InsertPunctuation(':');
}
dumper->InsertTypeName(Name(), m_navigationId);
dumper->InsertPunctuation('(');
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsTrailingNewline = false;
innerOptions.NeedsTrailingSemi = false;
innerOptions.NeedsLeadingNewline = false;
DumpList(
m_parameters.begin(),
m_parameters.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& node) {
node->DumpNode(dumper, innerOptions);
});
}
dumper->InsertPunctuation(')');
if (!m_isMemberOfClass)
{
DumpExceptionSpecification(dumper, dumpOptions);
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstMethod : public AstFunction {
protected:
bool m_isVirtual{};
bool m_isConst{};
bool m_isPure{};
bool m_isOverride{};
bool m_isImplicitOverride{};
bool m_isFinal{};
bool m_isImplicitFinal{};
RefQualifierKind m_refQualifier;
public:
AstMethod(
CXXMethodDecl const* method,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstFunction(method, database, parentNode), m_isVirtual(method->isVirtual()),
m_isPure(method->isPureVirtual()), m_isConst(method->isConst())
{
// We assume that this is an implicit override if there are overriden methods. If we later
// find an override attribute, we know it's not an implicit override.
//
// Note that we don't do this for destructors, because they typically won't have an override
// attribute.
//
// Also note that if size_overriden_methods is non-zero, it means that the base class method
// is already virtual.
//
if (method->getKind() == Decl::Kind::CXXMethod)
{
if (method->size_overridden_methods() > 0)
{
m_isOverride = true;
m_isImplicitOverride = true;
}
}
for (auto& attr : method->attrs())
{
auto location{attr->getLocation()};
switch (attr->getKind())
{
case attr::Override:
m_isImplicitOverride = false;
break;
case attr::Final:
if (attr->isImplicit())
{
method->dump(llvm::outs());
// database->CreateApiViewMessage(ApiViewMessages::ImplicitOverride,
// m_navigationId);
m_isImplicitFinal = true;
}
else
{
m_isFinal = true;
}
break;
case attr::Deprecated:
case attr::WarnUnusedResult:
break;
default:
llvm::outs() << "Unknown Method Attribute: ";
attr->printPretty(llvm::outs(), LangOptions());
llvm::outs() << "\n";
break;
}
}
if (m_isOverride && m_isImplicitOverride)
{
database->CreateApiViewMessage(ApiViewMessages::ImplicitOverride, m_navigationId);
}
auto typePtr = method->getType().getTypePtr()->castAs<FunctionProtoType>();
m_refQualifier = typePtr->getRefQualifier();
m_parentClass = method->getParent()->getNameAsString();
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
DumpDocumentation(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
if (m_isVirtual)
{
dumper->InsertKeyword("virtual");
dumper->InsertWhitespace();
}
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsTrailingNewline = false;
innerOptions.NeedsTrailingSemi = false;
// We already dumped the documentation for this node, we don't need to do it again.
innerOptions.NeedsDocumentation = false;
AstFunction::DumpNode(dumper, innerOptions);
}
if (m_isConst)
{
dumper->InsertWhitespace();
dumper->InsertKeyword("const");
}
switch (m_refQualifier)
{
case RQ_None:
break;
case RQ_RValue:
dumper->InsertWhitespace();
dumper->InsertPunctuation('&');
dumper->InsertPunctuation('&');
break;
case RQ_LValue:
dumper->InsertWhitespace();
dumper->InsertPunctuation('&');
break;
}
DumpExceptionSpecification(dumper, dumpOptions);
if (m_isPure)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertLiteral("0");
}
if (m_isOverride)
{
dumper->InsertWhitespace();
dumper->InsertKeyword("override");
}
if (m_isFinal)
{
dumper->InsertWhitespace();
dumper->InsertKeyword("final");
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstConstructor : public AstMethod {
bool m_isDefault{false};
bool m_isDeleted{false};
bool m_isExplicit{false};
bool m_isExplicitlyDefaulted{false};
public:
AstConstructor(
CXXConstructorDecl const* ctor,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstMethod(ctor, database, parentNode), m_isDefault{ctor->isDefaulted()},
m_isDeleted{ctor->isDeleted()}, m_isExplicit{ctor->isExplicit()},
m_isExplicitlyDefaulted{ctor->isExplicitlyDefaulted()}
{
if (m_isDefault && m_isDeleted)
{
llvm::outs() << "?? Defaulted deleted constructor?";
}
// Noisy diagnostic. Consider making it less noisy in the future.
if (!m_isExplicit)
{
if (!ctor->getParent()->isEffectivelyFinal())
{
database->CreateApiViewMessage(ApiViewMessages::ImplicitConstructor, m_navigationId);
}
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
DumpDocumentation(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
if (m_isExplicit)
{
dumper->InsertKeyword("explicit");
dumper->InsertWhitespace();
}
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsTrailingNewline = false;
innerOptions.NeedsTrailingSemi = false;
innerOptions.NeedsDocumentation = false;
AstMethod::DumpNode(dumper, innerOptions);
}
if (m_isDefault)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertKeyword("default");
}
if (m_isDeleted)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertKeyword("delete");
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstDestructor : public AstMethod {
bool m_isDefault{false};
bool m_isDeleted{false};
bool m_isExplicitlyDefaulted{false};
bool m_isVirtual{false};
public:
AstDestructor(
CXXDestructorDecl const* dtor,
AzureClassesDatabase* const database,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstMethod(dtor, database, parentNode), m_isDefault{dtor->isDefaulted()},
m_isDeleted{dtor->isDeleted()}, m_isExplicitlyDefaulted{dtor->isExplicitlyDefaulted()},
m_isVirtual{dtor->isVirtual()}
{
bool isBaseClassDtor{true};
// If this destructor overrides a base class destructor, it's not a base class destructor.
if (dtor->size_overridden_methods() != 0)
{
isBaseClassDtor = false;
}
if (dtor->getAccess() == AS_protected)
{
if (dtor->isVirtual())
{
database->CreateApiViewMessage(ApiViewMessages::NonVirtualDestructor, m_navigationId);
}
}
else if (dtor->getAccess() == AS_public)
{
if (!dtor->isVirtual())
{
auto parentClass = dtor->getParent();
if (!parentClass->isEffectivelyFinal())
{
database->CreateApiViewMessage(ApiViewMessages::NonVirtualDestructor, m_navigationId);
}
}
}
else
{
database->CreateApiViewMessage(ApiViewMessages::NonVirtualDestructor, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsTrailingNewline = false;
innerOptions.NeedsTrailingSemi = false;
innerOptions.NeedsDocumentation = false;
AstMethod::DumpNode(dumper, innerOptions);
}
if (m_isDefault)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertKeyword("default");
}
if (m_isDeleted)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
dumper->InsertKeyword("delete");
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstAccessSpec : public AstNode {
AccessSpecifier m_accessSpecifier;
public:
AstAccessSpec(clang::AccessSpecDecl const* accessSpec, AzureClassesDatabase* const)
: AstNode(), m_accessSpecifier{accessSpec->getAccess()}
{
}
AstAccessSpec(AccessSpecifier specifier) : AstNode(), m_accessSpecifier{specifier} {}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
// We want to left-indent the "public:", "private:" and "protected" items so they stick
// out from the fields in the class.
dumper->AdjustIndent(-2);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword(AccessSpecifierToString(m_accessSpecifier));
dumper->InsertPunctuation(':');
dumper->AdjustIndent(2);
dumper->Newline();
}
};
/**
* Represents an AST class or structure.
*/
class AstClassLike : public AstNamedNode {
bool m_isFinal{};
bool m_hasDefinition{};
bool m_isForwardDeclaration{};
bool m_isAnonymousNamedStruct{};
TagDecl::TagKind m_tagUsed;
std::string m_anonymousNamedStructName;
std::vector<std::unique_ptr<AstBaseClass>> m_baseClasses;
std::vector<std::unique_ptr<AstNode>> m_children;
private:
void DumpTag(AstDumper* dumper, DumpNodeOptions const& options) const
{
switch (m_tagUsed)
{
/// The "struct" keyword.
case TagDecl::TagKind::Struct:
dumper->InsertKeyword("struct");
break;
/// The "__interface" keyword.
case TagDecl::TagKind::Interface:
dumper->InsertKeyword("__interface");
break;
/// The "union" keyword.
case TagDecl::TagKind::Union:
dumper->InsertKeyword("union");
break;
/// The "class" keyword.
case TagDecl::TagKind::Class:
dumper->InsertKeyword("class");
break;
/// The "enum" keyword.
case TagDecl::TagKind::Enum:
dumper->InsertKeyword("enum");
break;
default:
throw std::runtime_error("Unknown tagKind: " + std::to_string(static_cast<int>(m_tagUsed)));
}
}
virtual void DumpTemplateSpecializationArguments(
AstDumper* dumper,
DumpNodeOptions const& options) const
{
}
public:
AstClassLike(
CXXRecordDecl const* decl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode);
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
class AstClassTemplate : public AstNamedNode {
std::vector<std::unique_ptr<AstNode>> m_parameters;
std::unique_ptr<AstNode> m_templateBody;
public:
AstClassTemplate(
ClassTemplateDecl const* templateDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(templateDecl, azureClassesDatabase, parentNode)
{
for (auto param : templateDecl->getTemplateParameters()->asArray())
{
m_parameters.push_back(AstNode::Create(param, azureClassesDatabase, parentNode));
}
m_templateBody
= AstNode::Create(templateDecl->getTemplatedDecl(), azureClassesDatabase, parentNode);
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (!Namespace().empty())
{
dumper->SetNamespace(Namespace());
}
DumpSourceComment(dumper, dumpOptions);
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("template");
dumper->InsertWhitespace();
dumper->InsertPunctuation('<');
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeadingNewline = false;
innerOptions.NeedsSourceComment
= false; // We've already dumped the source comment for this node.
DumpList(
m_parameters.begin(),
m_parameters.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& param) {
param->DumpNode(dumper, innerOptions);
});
}
dumper->InsertPunctuation('>');
dumper->Newline();
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = true;
innerOptions.NeedsLeadingNewline = false;
innerOptions.NeedsSourceComment
= false; // We've already dumped the source comment for this node.
m_templateBody->DumpNode(dumper, innerOptions);
}
}
};
class AstFunctionTemplate : public AstNamedNode {
std::vector<std::unique_ptr<AstNode>> m_parameters;
std::unique_ptr<AstNode> m_functionNode;
public:
AstFunctionTemplate(
FunctionTemplateDecl const* functionTemplate,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(functionTemplate, azureClassesDatabase, parentNode),
m_functionNode{
AstNode::Create(functionTemplate->getTemplatedDecl(), azureClassesDatabase, parentNode)}
{
for (auto param : functionTemplate->getTemplateParameters()->asArray())
{
m_parameters.push_back(AstNode::Create(param, azureClassesDatabase, parentNode));
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (!Namespace().empty())
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
}
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("template");
dumper->InsertWhitespace();
dumper->InsertPunctuation('<');
DumpList(
m_parameters.begin(),
m_parameters.end(),
dumper,
dumpOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& param) {
param->DumpNode(dumper, dumpOptions);
});
dumper->InsertPunctuation('>');
dumper->Newline();
m_functionNode->DumpNode(dumper, dumpOptions);
}
};
class AstTypeAliasTemplate : public AstNamedNode {
std::vector<std::unique_ptr<AstNode>> m_parameters;
std::unique_ptr<AstNode> m_typeAliasNode;
public:
AstTypeAliasTemplate(
TypeAliasTemplateDecl const* typeAliasTemplate,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(typeAliasTemplate, azureClassesDatabase, parentNode),
m_typeAliasNode{AstNode::Create(
typeAliasTemplate->getTemplatedDecl(),
azureClassesDatabase,
parentNode)}
{
for (auto param : typeAliasTemplate->getTemplateParameters()->asArray())
{
m_parameters.push_back(AstNode::Create(param, azureClassesDatabase, parentNode));
if (!m_parameters.back())
{
llvm::outs() << raw_ostream::Colors::CYAN << "Unknown or unsupported AST node type."
<< raw_ostream::Colors::RESET << "\n";
param->dump(llvm::outs());
}
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (!Namespace().empty())
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
}
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("template");
dumper->InsertWhitespace();
dumper->InsertPunctuation('<');
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeadingNewline = false;
DumpList(
m_parameters.begin(),
m_parameters.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& param) {
param->DumpNode(dumper, innerOptions);
});
}
dumper->InsertPunctuation('>');
dumper->Newline();
m_typeAliasNode->DumpNode(dumper, dumpOptions);
}
};
class AstClassTemplateSpecialization : public AstClassLike {
std::vector<std::unique_ptr<AstType>> m_arguments;
virtual void DumpTemplateSpecializationArguments(
AstDumper* dumper,
DumpNodeOptions const& dumpOptions) const override
{
dumper->InsertPunctuation('<');
for (auto const& arg : m_arguments)
{
arg->Dump(dumper, dumpOptions);
}
dumper->InsertPunctuation('>');
}
public:
AstClassTemplateSpecialization(
ClassTemplateSpecializationDecl const* templateDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstClassLike(templateDecl, azureClassesDatabase, parentNode)
{
m_navigationId = GetNavigationId(templateDecl);
for (const auto& arg : templateDecl->getTemplateArgs().asArray())
{
m_arguments.push_back(std::make_unique<AstType>(arg.getAsType()));
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (!Namespace().empty())
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
}
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("template");
dumper->InsertWhitespace();
dumper->InsertPunctuation('<');
dumper->InsertPunctuation('>');
AstClassLike::DumpNode(dumper, dumpOptions);
}
};
class AstConversion : public AstNamedNode {
bool m_isExplicit;
bool m_isConstexpr;
AstType m_conversionType;
public:
AstConversion(
CXXConversionDecl const* conversion,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(conversion, azureClassesDatabase, parentNode),
m_isExplicit{conversion->isExplicit()}, m_isConstexpr{conversion->isConstexpr()},
m_conversionType{conversion->getConversionType(), conversion->getASTContext()}
{
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
if (m_isConstexpr)
{
dumper->InsertKeyword("constexpr");
dumper->InsertWhitespace();
}
if (m_isExplicit)
{
dumper->InsertKeyword("explicit");
dumper->InsertWhitespace();
}
dumper->InsertKeyword("operator");
dumper->InsertWhitespace();
m_conversionType.Dump(dumper, dumpOptions);
dumper->InsertPunctuation('(');
dumper->InsertPunctuation(')');
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
};
};
class AstField : public AstNamedNode {
AstType m_fieldType;
std::unique_ptr<AstExpr> m_initializer;
InClassInitStyle m_classInitializerStyle;
bool m_hasDefaultMemberInitializer{};
bool m_isMutable{};
bool m_isConst{};
public:
AstField(
FieldDecl const* fieldDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(fieldDecl, azureClassesDatabase, parentNode),
m_fieldType{fieldDecl->getType(), fieldDecl->getASTContext()},
m_initializer{
AstExpr::Create(fieldDecl->getInClassInitializer(), fieldDecl->getASTContext())},
m_classInitializerStyle{fieldDecl->getInClassInitStyle()},
m_hasDefaultMemberInitializer{fieldDecl->hasInClassInitializer()},
m_isMutable{fieldDecl->isMutable()}, m_isConst{fieldDecl->getType().isConstQualified()}
{
// If the type of the parameter is in the global namespace, then flag it as an error.
if (m_fieldType.IsTypeInGlobalNamespace())
{
azureClassesDatabase->CreateApiViewMessage(
ApiViewMessages::TypedefInGlobalNamespace, m_navigationId);
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
class AstFriend : public AstNode {
std::string m_friendType;
std::string m_navigationId;
std::unique_ptr<AstNode> m_friendFunction;
public:
static bool ShouldIncludeFriendDeclaration(FriendDecl const* friendDecl)
{
if (!friendDecl->getFriendType() && !friendDecl->getFriendDecl())
{
return false;
}
if (friendDecl->getFriendType())
{
auto friendTypeName
= QualType::getAsString(friendDecl->getFriendType()->getType().split(), LangOptions{});
// If the friend type is in the std namespace, we don't want to include it.
if (friendTypeName.find("std::") == 0)
{
return false;
}
// If the friend type is in the detail namespace, we don't want to include it.
if (friendTypeName.find("_detail::") != std::string::npos)
{
return false;
}
}
return true;
}
AstFriend(
FriendDecl const* friendDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNode()
{
if (ShouldIncludeFriendDeclaration(friendDecl))
{
m_navigationId = GetNavigationId(friendDecl, parentNode->NavigationId);
if (friendDecl->getFriendType())
{
m_friendType
= QualType::getAsString(friendDecl->getFriendType()->getType().split(), LangOptions{});
}
else if (friendDecl->getFriendDecl())
{
m_friendFunction
= AstNode::Create(friendDecl->getFriendDecl(), azureClassesDatabase, parentNode);
}
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
// If we're skipping this friend declaration, don't do anything.
if (m_friendFunction == nullptr && m_friendType.empty())
{
return;
}
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("friend");
dumper->InsertWhitespace();
if (m_friendFunction)
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeftAlign = false;
innerOptions.NeedsNamespaceAdjustment = false;
// innerOptions.IncludeContainingClass = true;
// innerOptions.IncludeNamespace = true;
m_friendFunction->DumpNode(dumper, innerOptions);
}
else
{
dumper->InsertTypeName(m_friendType, m_navigationId);
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
}
};
class AstUsingDirective : public AstNode {
std::string m_namedNamespace;
public:
AstUsingDirective(
UsingDirectiveDecl const* usingDirective,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNode(), m_namedNamespace{
usingDirective->getNominatedNamespaceAsWritten()->getQualifiedNameAsString()}
{
// Determine the location for the error message.
auto location = usingDirective->getLocation();
auto const& sourceManager = usingDirective->getASTContext().getSourceManager();
auto const& presumedLocation = sourceManager.getPresumedLoc(location);
std::string typeLocation{presumedLocation.getFilename()};
// Remove the root directory from the location if the location is within the root directory.
if (typeLocation.find(azureClassesDatabase->GetProcessor()->RootDirectory()) == 0)
{
typeLocation.erase(0, azureClassesDatabase->GetProcessor()->RootDirectory().size() + 1);
}
typeLocation += ":" + std::to_string(presumedLocation.getLine());
typeLocation += ":" + std::to_string(presumedLocation.getColumn());
llvm::errs() << raw_ostream::Colors::RED
<< "Found `using namespace` directive in public headers for `m_namedNamespace`, "
"at location " << typeLocation << raw_ostream::Colors::RESET << "\n";
azureClassesDatabase->CreateApiViewMessage(
ApiViewMessages::UsingDirectiveFound, m_namedNamespace);
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("using");
dumper->InsertWhitespace();
dumper->InsertKeyword("namespace");
dumper->InsertWhitespace();
dumper->InsertTypeName(m_namedNamespace, m_namedNamespace);
dumper->InsertPunctuation(';');
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstUsingDecl : public AstNamedNode {
std::string m_fullName;
public:
AstUsingDecl(
UsingDecl const* usingDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(usingDecl, azureClassesDatabase, parentNode)
{
m_navigationId = GetNavigationId(usingDecl);
if (usingDecl->getQualifier())
{
std::string qualifier;
llvm::raw_string_ostream os{qualifier};
clang::PrintingPolicy pp{LangOptions{}};
pp.adjustForCPlusPlus();
usingDecl->getQualifier()->print(os, pp);
m_fullName += qualifier;
}
m_fullName += usingDecl->getDeclName().getAsString();
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("using");
dumper->InsertWhitespace();
// If the full name starts with the namespace, remove the namespace from the type name.
std::string fullName{m_fullName};
if (m_fullName.find(Namespace()) == 0)
{
fullName.erase(0, Namespace().size());
}
dumper->InsertTypeName(fullName, m_navigationId);
dumper->InsertPunctuation(';');
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
};
class AstEnumerator : public AstNamedNode {
std::unique_ptr<AstExpr> m_initializer;
public:
AstEnumerator(
EnumConstantDecl const* enumerator,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(enumerator, azureClassesDatabase, parentNode)
{
if (enumerator->getInitExpr())
{
m_initializer = AstExpr::Create(enumerator->getInitExpr(), enumerator->getASTContext());
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override
{
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
dumper->LeftAlign();
dumper->InsertMemberName(Name(), m_navigationId);
if (m_initializer)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation('=');
dumper->InsertWhitespace();
m_initializer->Dump(dumper, dumpOptions);
}
}
};
class AstEnum : public AstNamedNode {
// std::vector<std::tuple<std::string, std::unique_ptr<AstExpr>>> m_enumerators;
std::vector<std::unique_ptr<AstNode>> m_enumerators;
std::string m_underlyingType;
bool m_isScoped;
bool m_isScopedWithClass;
bool m_isFixed;
bool m_isForwardDeclaration;
public:
AstEnum(
EnumDecl const* enumDecl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(enumDecl, azureClassesDatabase, parentNode),
m_underlyingType{enumDecl->getIntegerType().getAsString()},
m_isScoped{enumDecl->isScoped()}, m_isScopedWithClass{enumDecl->isScopedUsingClassTag()},
m_isFixed{enumDecl->isFixed()},
m_isForwardDeclaration{enumDecl != enumDecl->getDefinition()}
{
if (!m_isScoped)
{
azureClassesDatabase->CreateApiViewMessage(
ApiViewMessages::UnscopedEnumeration, m_navigationId);
}
// All the types created under this node use a newly created node for their parent.
if (parentNode)
{
parentNode = parentNode->InsertChildNode(
Name(), m_navigationId, TypeHierarchy::TypeHierarchyClass::Enum);
}
for (auto enumerator : enumDecl->enumerators())
{
m_enumerators.push_back(AstNode::Create(enumerator, azureClassesDatabase, parentNode));
}
}
void DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const override;
};
AstClassLike::AstClassLike(
CXXRecordDecl const* decl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
: AstNamedNode(decl, azureClassesDatabase, parentNode), m_tagUsed{decl->getTagKind()},
m_hasDefinition{decl->hasDefinition()}, m_isForwardDeclaration{decl != decl->getDefinition()}
{
// All the types created under this node use a newly created node for their parent.
TypeHierarchy::TypeHierarchyClass classType;
switch (m_tagUsed)
{
case TagDecl::TagKind::Class:
classType = TypeHierarchy::TypeHierarchyClass::Class;
break;
case TagDecl::TagKind::Enum:
classType = TypeHierarchy::TypeHierarchyClass::Enum;
break;
case TagDecl::TagKind::Interface:
classType = TypeHierarchy::TypeHierarchyClass::Interface;
break;
case TagDecl::TagKind::Struct:
classType = TypeHierarchy::TypeHierarchyClass::Struct;
break;
case TagDecl::TagKind::Union:
classType = TypeHierarchy::TypeHierarchyClass::Unknown;
break;
default:
break;
}
// We want to special case anonymous structures which are embedded in another type. It's
// possible that the following declaration is a field declaration referencing the
// anonymous structure:
//
// struct Foo {
// int Field1;
// struct {
// bool InnerField1;
// } InnerStruct
// };
//
// This is parsed as an anonymous struct containing a single field named "InnerField1"
// followed by a field declaration referencing the anonymous struct.
if (Name().empty() && decl->isEmbeddedInDeclarator()
&& decl->getNextDeclInContext()->getKind() == Decl::Kind::Field)
{
m_isAnonymousNamedStruct = true;
m_anonymousNamedStructName = cast<FieldDecl>(decl->getNextDeclInContext())->getNameAsString();
}
if (parentNode)
{
parentNode = parentNode->InsertChildNode(
!m_isAnonymousNamedStruct ? Name() : m_anonymousNamedStructName, m_navigationId, classType);
}
AccessSpecifier currentAccessSpecifier = AccessSpecifier::AS_none;
if (classType == TypeHierarchy::TypeHierarchyClass::Class)
{
currentAccessSpecifier = AccessSpecifier::AS_private;
}
else
{
currentAccessSpecifier = AccessSpecifier::AS_public;
}
for (auto& attr : decl->attrs())
{
switch (attr->getKind())
{
case attr::Final:
m_isFinal = true;
break;
// This is an implicit attribute that won't ever appear explicitly, so we can
// ignore it.
case attr::MaxFieldAlignment:
break;
default:
llvm::outs() << "Unknown Attribute: ";
attr->printPretty(llvm::outs(), LangOptions());
llvm::outs() << "\n";
break;
}
}
if (decl->hasDefinition())
{
for (auto const& base : decl->bases())
{
m_baseClasses.push_back(std::make_unique<AstBaseClass>(base));
}
bool shouldSkipNextChild = false;
for (auto child : decl->decls())
{
// We want to ignore any and all auto-generated types - we only care about explictly
// mentioned types.
bool shouldIncludeChild = !child->isImplicit();
// If the child is private and we're not including private types, don't include it.
if (shouldIncludeChild)
{
if (child->getAccess() == AS_private) //&& !options.IncludePrivate)
{
shouldIncludeChild = false;
// If the method is a virtual method, then we need to include it because it's
// functionally a protected method.
if (child->getKind() == Decl::Kind::CXXMethod)
{
if (cast<CXXMethodDecl>(child)->isVirtual())
{
shouldIncludeChild = true;
}
}
}
}
if (shouldIncludeChild)
{
if (shouldSkipNextChild)
{
shouldIncludeChild = false;
shouldSkipNextChild = false;
}
}
if (shouldIncludeChild)
{
// Correct the access specifier if necessary.
//
// We track and emit access specifiers for protected and public nodes, however private
// virtual functions are always included in the API View. We need to adjust the access
// specifier for these functions so that they are emitted as private.
//
// Friend nodes and static_assert nodes ignore access specifiers, so there is no need to
// adjust access specifiers for them.
if (child->getKind() != Decl::Kind::Friend && child->getKind() != Decl::Kind::StaticAssert)
{
if (child->getKind() == Decl::Kind::AccessSpec)
{
// Update the current access specifier to reflect the value in the current node.
currentAccessSpecifier = cast<AccessSpecDecl>(child)->getAccess();
}
else if (currentAccessSpecifier != child->getAccess())
{
// The access specifier for the child doesn't match the current access specifier.
// Because we don't have an existing AccessSpecDecl node, we need to create one.
currentAccessSpecifier = child->getAccess();
m_children.push_back(std::make_unique<AstAccessSpec>(currentAccessSpecifier));
}
}
switch (child->getKind())
{
case Decl::Kind::Var: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::CXXRecord: {
m_children.push_back(std::make_unique<AstClassLike>(
cast<CXXRecordDecl>(child), azureClassesDatabase, parentNode));
// For an anonymous named structure, we want to skip the next field because
// it's been embedded in the anonymous struct definition.
if (static_cast<AstClassLike*>(m_children.back().get())->m_isAnonymousNamedStruct)
{
assert(child->getNextDeclInContext()->getKind() == Decl::Kind::Field);
shouldSkipNextChild = true;
}
break;
}
case Decl::Kind::CXXMethod: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::CXXConstructor: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::CXXDestructor: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::Field: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::AccessSpec: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::FunctionTemplate: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::Friend: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::Enum: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::TypeAlias: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::CXXConversion: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::StaticAssert: {
// static_assert nodes are generated after the preprocessor and they don't
// really add any value to the ApiView.
break;
}
case Decl::Kind::Using: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
case Decl::Kind::TypeAliasTemplate: {
m_children.push_back(AstNode::Create(child, azureClassesDatabase, parentNode));
break;
}
default: {
llvm::errs() << raw_ostream::Colors::RED
<< "Unhandled Decl Type: " << std::string(child->getDeclKindName())
<< raw_ostream::Colors::RESET << "\n";
}
}
if (m_isFinal && child->getAccess() == AS_protected)
{
// protected types in final classes should be flagged.
if (child->getKind() != Decl::AccessSpec && cast<NamedDecl>(child)->getIdentifier())
{
azureClassesDatabase->CreateApiViewMessage(
ApiViewMessages::ProtectedFieldsInFinalClass,
cast<NamedDecl>(child)->getQualifiedNameAsString());
}
}
}
}
}
}
void AstClassLike::DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
if (!Namespace().empty())
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
}
DumpSourceComment(dumper, dumpOptions);
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
// If we're a templated class, don't insert the extra newline before the class
// definition.
if (dumpOptions.NeedsLeadingNewline)
{
dumper->Newline();
}
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
DumpTag(dumper, dumpOptions);
dumper->InsertWhitespace();
if (m_isForwardDeclaration)
{
dumper->InsertIdentifier(Name());
}
else
{
dumper->InsertTypeName(Name(), m_navigationId);
}
DumpTemplateSpecializationArguments(dumper, dumpOptions);
if (!m_isForwardDeclaration)
{
if (m_hasDefinition)
{
if (m_isFinal)
{
dumper->InsertWhitespace();
dumper->InsertKeyword("final");
}
if (!m_baseClasses.empty())
{
dumper->InsertWhitespace();
dumper->InsertPunctuation(':');
dumper->InsertWhitespace();
// Enumerate the base classes, dumping each of them.
DumpList(
m_baseClasses.begin(),
m_baseClasses.end(),
dumper,
dumpOptions,
[&](AstDumper* dumper, std::unique_ptr<AstBaseClass> const& base) {
base->DumpNode(dumper, dumpOptions);
});
}
dumper->Newline();
dumper->LeftAlign();
dumper->InsertPunctuation('{');
dumper->AdjustIndent(2);
dumper->Newline();
for (auto const& child : m_children)
{
assert(child);
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeadingNewline = false;
child->DumpNode(dumper, innerOptions);
}
dumper->AdjustIndent(-2);
dumper->LeftAlign();
dumper->InsertPunctuation('}');
}
if (m_isAnonymousNamedStruct && !m_anonymousNamedStructName.empty())
{
dumper->InsertWhitespace();
dumper->InsertTypeName(
m_anonymousNamedStructName, m_navigationId + m_anonymousNamedStructName);
}
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
void AstEnum::DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
if (!Namespace().empty())
{
if (dumpOptions.NeedsNamespaceAdjustment)
{
dumper->SetNamespace(Namespace());
}
}
DumpSourceComment(dumper, dumpOptions);
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
dumper->InsertKeyword("enum");
dumper->InsertWhitespace();
if (m_isScoped)
{
if (m_isScopedWithClass)
{
dumper->InsertKeyword("class");
}
else
{
dumper->InsertKeyword("struct");
}
}
dumper->InsertWhitespace();
if (m_isForwardDeclaration)
{
dumper->InsertIdentifier(Name());
}
else
{
dumper->InsertTypeName(Name(), m_navigationId);
if (m_isFixed)
{
dumper->InsertWhitespace();
dumper->InsertPunctuation(':');
dumper->InsertWhitespace();
dumper->InsertIdentifier(m_underlyingType);
}
dumper->Newline();
dumper->LeftAlign();
dumper->InsertPunctuation('{');
dumper->AdjustIndent(2);
dumper->Newline();
{
DumpNodeOptions innerOptions{dumpOptions};
innerOptions.NeedsLeadingNewline = true;
DumpList(
m_enumerators.begin(),
m_enumerators.end(),
dumper,
innerOptions,
[&](AstDumper* dumper, std::unique_ptr<AstNode> const& enumerator) {
enumerator->DumpNode(dumper, dumpOptions);
});
}
dumper->Newline();
dumper->AdjustIndent(-2);
dumper->LeftAlign();
dumper->InsertPunctuation('}');
}
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
void AstField::DumpNode(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
DumpDocumentation(dumper, dumpOptions);
DumpAttributes(dumper, dumpOptions);
if (dumpOptions.NeedsLeftAlign)
{
dumper->LeftAlign();
}
m_fieldType.Dump(dumper, dumpOptions);
dumper->InsertWhitespace();
dumper->InsertMemberName(Name(), m_navigationId);
// if (m_initializer)
//{
// DumpNodeOptions innerOptions{dumpOptions};
// if (m_classInitializerStyle == ICIS_CopyInit)
// {
// dumper->InsertWhitespace();
// dumper->InsertPunctuation('=');
// dumper->InsertWhitespace();
// }
// else if (m_classInitializerStyle == ICIS_ListInit)
// {
// innerOptions.DumpListInitializer = true;
// }
// m_initializer->Dump(dumper, innerOptions);
// }
if (dumpOptions.NeedsTrailingSemi)
{
dumper->InsertPunctuation(';');
}
if (dumpOptions.NeedsTrailingNewline)
{
dumper->Newline();
}
}
void AstType::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
dumper->InsertIdentifier(m_internalTypeName);
}
void AstExpr::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
dumper->InsertComment("/* UNSUPPORTED EXPRESSION */");
}
void AstStatement::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
dumper->InsertComment("/* UNSUPPORTED STATEMENT */");
}
void AstMethodCallExpr::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
// Dump the class and member field to be called.
m_memberAccessor->Dump(dumper, dumpOptions);
}
void AstMemberExpr::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('{');
}
m_member->Dump(dumper, dumpOptions);
dumper->InsertPunctuation('.');
dumper->InsertIdentifier(m_memberMethod);
dumper->InsertPunctuation('(');
dumper->InsertPunctuation(')');
if (dumpOptions.DumpListInitializer)
{
dumper->InsertPunctuation('}');
}
}
void AstInitializerList::Dump(AstDumper* dumper, DumpNodeOptions const& dumpOptions) const
{
if (!m_initializerValues.empty())
{
if (IsEmptyExpression())
{
dumper->InsertPunctuation('{');
dumper->InsertPunctuation('}');
}
else
{
// If the initializer list has multiple values, dump them as a list, one per line.
if (m_initializerValues.size() != 1)
{
dumper->InsertPunctuation('{');
dumper->AdjustIndent(4);
DumpList(
m_initializerValues.begin(),
m_initializerValues.end(),
dumper,
dumpOptions,
[&](AstDumper* dumper, std::unique_ptr<AstExpr> const& initializer) {
dumper->Newline();
dumper->LeftAlign();
initializer->Dump(dumper, dumpOptions);
});
dumper->AdjustIndent(-4);
dumper->InsertPunctuation('}');
}
else
{
// If the initializer list has only a single value, just dump it.
m_initializerValues.front()->Dump(dumper, dumpOptions);
}
}
}
}
std::unique_ptr<AstNode> AstNode::Create(
clang::Decl const* decl,
AzureClassesDatabase* const azureClassesDatabase,
std::shared_ptr<TypeHierarchy::TypeHierarchyNode> parentNode)
{
switch (decl->getKind())
{
case Decl::Kind::CXXConstructor:
return std::make_unique<AstConstructor>(
cast<CXXConstructorDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::CXXDestructor:
return std::make_unique<AstDestructor>(
cast<CXXDestructorDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::CXXConversion:
return std::make_unique<AstConversion>(
cast<CXXConversionDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::CXXMethod:
return std::make_unique<AstMethod>(
cast<CXXMethodDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::Function:
return std::make_unique<AstFunction>(
cast<FunctionDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::ParmVar: {
return std::make_unique<AstParamVariable>(
cast<ParmVarDecl>(decl), azureClassesDatabase, parentNode);
}
case Decl::Kind::Var:
return std::make_unique<AstVariable>(cast<VarDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::ClassTemplateSpecialization:
return std::make_unique<AstClassTemplateSpecialization>(
cast<ClassTemplateSpecializationDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::Enum:
return std::make_unique<AstEnum>(cast<EnumDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::EnumConstant:
return std::make_unique<AstEnumerator>(
cast<EnumConstantDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::Field:
return std::make_unique<AstField>(cast<FieldDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::FunctionTemplate:
return std::make_unique<AstFunctionTemplate>(
cast<FunctionTemplateDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::ClassTemplate:
return std::make_unique<AstClassTemplate>(
cast<ClassTemplateDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::TemplateTypeParm:
return std::make_unique<AstTemplateParameter>(
cast<TemplateTypeParmDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::TemplateTemplateParm:
return std::make_unique<AstTemplateTemplateParameter>(
cast<TemplateTemplateParmDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::NonTypeTemplateParm:
return std::make_unique<AstNonTypeTemplateParam>(
cast<NonTypeTemplateParmDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::TypeAliasTemplate:
return std::make_unique<AstTypeAliasTemplate>(
cast<TypeAliasTemplateDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::TypeAlias:
return std::make_unique<AstTypeAlias>(
cast<TypeAliasDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::CXXRecord:
return std::make_unique<AstClassLike>(
cast<CXXRecordDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::AccessSpec:
return std::make_unique<AstAccessSpec>(cast<AccessSpecDecl>(decl), azureClassesDatabase);
case Decl::Kind::Friend:
return std::make_unique<AstFriend>(cast<FriendDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::UsingDirective:
// A "UsingDirective" is a "using namespace" directive. We consider this an error
// condition, add an AstNode so the error appears in the ApiView.
return std::make_unique<AstUsingDirective>(
cast<UsingDirectiveDecl>(decl), azureClassesDatabase, parentNode);
case Decl::Kind::NamespaceAlias:
return nullptr;
// return std::make_unique<AstNamespaceAlias>(cast<NamespaceAliasDecl>(decl,
// azureClassesDatabase));
case Decl::Kind::Namespace:
return nullptr;
// return std::make_unique<AstNamespace>(cast<NamespaceDecl>(decl,
// azureClassesDatabase));
case Decl::Kind::Using:
return std::make_unique<AstUsingDecl>(
cast<UsingDecl>(decl), azureClassesDatabase, parentNode);
default: {
llvm::errs() << raw_ostream::Colors::RED << "Unknown DECL node "
<< cast<NamedDecl>(decl)->getNameAsString()
<< " type : " << decl->getDeclKindName() << raw_ostream::Colors::RESET << "\n ";
return nullptr;
}
}
}
std::string AstNode::GetNamespaceForDecl(Decl const* decl)
{
auto typeNamespace{decl->getDeclContext()->getEnclosingNamespaceContext()};
if (typeNamespace->isNamespace())
{
return cast<NamespaceDecl>(typeNamespace)->getQualifiedNameAsString();
}
return "";
}
// Classes database implementation.
bool AzureClassesDatabase::IsMemberOfObject(NamedDecl const* decl)
{
if (decl->isCXXClassMember())
{
return true;
}
// If this object is the target of a friend declaration, then there is a friend
// declaration that actually defines the object.
if (decl->getFriendObjectKind() != clang::Decl::FOK_None)
{
return true;
}
// Not strictly true, but if this decl has a describing template, then it's covered by
// another node type.
if (decl->getDescribedTemplate() != nullptr)
{
return true;
}
// Method or template parameters or enumerators are by definition members of an object.
if (decl->getKind() == Decl::ParmVar || decl->getKind() == Decl::EnumConstant
|| decl->getKind() == Decl::TemplateTypeParm || decl->getKind() == Decl::NonTypeTemplateParm
|| decl->getKind() == Decl::TemplateTemplateParm)
{
return true;
}
// Local variables are obviously members of objects.
if (decl->getKind() == Decl::Var)
{
if (cast<VarDecl>(decl)->isLocalVarDecl())
{
return true;
}
}
// If this node has a parent function or method, it's a member of something.
auto parent = decl->getParentFunctionOrMethod();
if (parent)
{
return true;
}
return false;
}
void AzureClassesDatabase::CreateAstNode()
{
// Create a terminal node to force closing of all outstanding namespaces.
m_typeList.push_back(std::make_unique<AstTerminalNode>());
}
void AzureClassesDatabase::CreateAstNode(clang::NamedDecl* namedDecl)
{
if (m_processor->IncludePrivate() || namedDecl->getAccess() != AS_private)
{
// Perform a couple of verification checks that should apply to every type we add to the API
// Review, regardless of the type of object:
//
// 1) If there's a namespace filter specified, flag all types outside the namespace filter.
// 2) If the type is in the _internal namespace, flag it if we're not allowed to have
// _internal types.
//
// We don't want to consider namespaces in these checks, since they're not discrete entries in
// our resulting output.
if (namedDecl->getKind() != Decl::Namespace)
{
const std::string typeName{namedDecl->getQualifiedNameAsString()};
if (!m_processor->FilterNamespaces().empty())
{
// We have a namespace filter set. Verify that the type name starts with one of the filter
// namespaces.
if (std::find_if(
std::begin(m_processor->FilterNamespaces()),
std::end(m_processor->FilterNamespaces()),
[&typeName](std::string const& ns) { return typeName.find(ns) == 0; })
== std::end(m_processor->FilterNamespaces()))
{
bool generateError = true;
// Don't flag "using" declarations or forward declarations, because they don't introduce
// new types.
if ((namedDecl->getKind() == Decl::Using) || (namedDecl->getKind() == Decl::TypeAlias)
|| (namedDecl->getKind() == Decl::CXXRecord
&& cast<CXXRecordDecl>(namedDecl)->getDefinition() != namedDecl))
{
generateError = false;
}
if (generateError)
{
m_processor->GetClassesDatabase()->CreateApiViewMessage(
ApiViewMessages::TypeDeclaredInNamespaceOutsideFilter, typeName);
}
}
}
// If the type is in the _internal namespace, then we want to flag it unless we are
// allowing internal types.
if (typeName.find("::_internal") != std::string::npos)
{
if (!m_processor->AllowInternal())
{
m_processor->GetClassesDatabase()->CreateApiViewMessage(
ApiViewMessages::InternalTypesInNonCorePackage, typeName);
}
}
}
// Now create the node. Note that for some types AstNode::Create will return null, so we skip
// adding them before we add them.
auto node = AstNode::Create(
namedDecl, this, m_typeHierarchy.GetNamespaceRoot(AstNode::GetNamespaceForDecl(namedDecl)));
if (node)
{
m_typeList.push_back(std::move(node));
}
}
}