/************************************************************************ * * CppSharp * Licensed under the simplified BSD license. All rights reserved. * ************************************************************************/ #include "Parser.h" #include "Interop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //-----------------------------------// Parser::Parser(ParserOptions^ Opts) : Lib(Opts->Library), Opts(Opts), Index(0) { } //-----------------------------------// static std::string GetClangResourceDir(const std::string& Dir) { using namespace llvm; using namespace clang; // Compute the path to the resource directory. StringRef ClangResourceDir(CLANG_RESOURCE_DIR); SmallString<128> P(Dir); if (ClangResourceDir != "") llvm::sys::path::append(P, ClangResourceDir); else llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING); return P.str(); } static std::string GetClangBuiltinIncludeDir() { using namespace llvm; SmallString<128> P( GetClangResourceDir(".") ); llvm::sys::path::append(P, "include"); return P.str(); } //-----------------------------------// #ifdef _MSC_VER std::vector GetWindowsSystemIncludeDirs(); #endif void Parser::SetupHeader() { using namespace clang; using namespace clix; std::vector args; args.push_back("-cc1"); // Enable C++ language mode args.push_back("-xc++"); args.push_back("-std=gnu++11"); //args.push_back("-Wno-undefined-inline"); args.push_back("-fno-rtti"); // Enable the Microsoft parsing extensions if (Opts->MicrosoftMode) { args.push_back("-fms-extensions"); args.push_back("-fms-compatibility"); args.push_back("-fdelayed-template-parsing"); } C.reset(new CompilerInstance()); C->createDiagnostics(); CompilerInvocation* Inv = new CompilerInvocation(); CompilerInvocation::CreateFromArgs(*Inv, args.data(), args.data() + args.size(), C->getDiagnostics()); C->setInvocation(Inv); TargetOptions& TO = Inv->getTargetOpts(); if (!System::String::IsNullOrWhiteSpace(Opts->TargetTriple)) TO.Triple = marshalString(Opts->TargetTriple); else TO.Triple = llvm::sys::getDefaultTargetTriple(); TargetABI = Opts->MicrosoftMode ? TargetCXXABI::Microsoft : TargetCXXABI::GenericItanium; TargetInfo* TI = TargetInfo::CreateTargetInfo(C->getDiagnostics(), &TO); TI->setCXXABI(TargetABI); C->setTarget(TI); C->createFileManager(); C->createSourceManager(C->getFileManager()); if (Opts->NoStandardIncludes) { auto HSOpts = C->getHeaderSearchOpts(); HSOpts.UseStandardSystemIncludes = false; HSOpts.UseStandardCXXIncludes = false; } if (Opts->NoBuiltinIncludes) { auto HSOpts = C->getHeaderSearchOpts(); HSOpts.UseBuiltinIncludes = false; } if (Opts->Verbose) C->getHeaderSearchOpts().Verbose = true; for each(System::String^% include in Opts->IncludeDirs) { String s = marshalString(include); C->getHeaderSearchOpts().AddPath(s, frontend::Angled, false, false); } for each(System::String^% include in Opts->SystemIncludeDirs) { String s = marshalString(include); C->getHeaderSearchOpts().AddPath(s, frontend::System, false, false); } for each(System::String^% def in Opts->Defines) { String s = marshalString(def); C->getPreprocessorOpts().addMacroDef(s); } // Initialize the default platform headers. std::string ResourceDir = GetClangResourceDir("."); C->getHeaderSearchOpts().ResourceDir = ResourceDir; C->getHeaderSearchOpts().AddPath(GetClangBuiltinIncludeDir(), clang::frontend::System, false, false); #ifdef _MSC_VER if (!Opts->NoBuiltinIncludes) { std::vector SystemDirs = GetWindowsSystemIncludeDirs(); clang::HeaderSearchOptions& HSOpts = C->getHeaderSearchOpts(); for(size_t i = 0; i < SystemDirs.size(); ++i) { HSOpts.AddPath(SystemDirs[i], frontend::System, false, false); } } #endif C->createPreprocessor(); C->createASTContext(); if (C->hasPreprocessor()) { Preprocessor& P = C->getPreprocessor(); P.createPreprocessingRecord(); P.getBuiltinInfo().InitializeBuiltins(P.getIdentifierTable(), P.getLangOpts()); } } //-----------------------------------// std::string Parser::GetDeclMangledName(clang::Decl* D, clang::TargetCXXABI ABI, bool IsDependent) { using namespace clang; if(!D || !isa(D)) return ""; bool CanMangle = isa(D) || isa(D) || isa(D) || isa(D); if (!CanMangle) return ""; NamedDecl* ND = cast(D); llvm::OwningPtr MC; switch(ABI.getKind()) { default: llvm_unreachable("Unknown mangling ABI"); break; case TargetCXXABI::GenericItanium: MC.reset(createItaniumMangleContext(*AST, AST->getDiagnostics())); //AST->setCXXABI(CreateItaniumCXXABI(*AST)); break; case TargetCXXABI::Microsoft: MC.reset(createMicrosoftMangleContext(*AST, AST->getDiagnostics())); //AST->setCXXABI(CreateMicrosoftCXXABI(*AST)); break; } std::string Mangled; llvm::raw_string_ostream Out(Mangled); if (const ValueDecl *VD = dyn_cast(ND)) IsDependent = VD->getType()->isDependentType(); if (!MC->shouldMangleDeclName(ND) || IsDependent) return ND->getDeclName().getAsString(); if (const CXXConstructorDecl *CD = dyn_cast(ND)) MC->mangleCXXCtor(CD, Ctor_Base, Out); else if (const CXXDestructorDecl *DD = dyn_cast(ND)) MC->mangleCXXDtor(DD, Dtor_Base, Out); else MC->mangleName(ND, Out); Out.flush(); // Strip away LLVM name marker. if(!Mangled.empty() && Mangled[0] == '\01') Mangled = Mangled.substr(1); return Mangled; } //-----------------------------------// static std::string GetDeclName(const clang::NamedDecl* D) { if (const clang::IdentifierInfo *II = D->getIdentifier()) return II->getName(); return D->getNameAsString(); } static std::string GetTagDeclName(const clang::TagDecl* D) { using namespace clang; if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); return GetDeclName(Typedef); } return GetDeclName(D); } static clang::Decl* GetPreviousDeclInContext(const clang::Decl* D) { assert(!D->getLexicalDeclContext()->decls_empty()); clang::Decl* prevDecl = nullptr; for(auto it = D->getDeclContext()->decls_begin(); it != D->getDeclContext()->decls_end(); it++) { if((*it) == D) return prevDecl; prevDecl = (*it); } return nullptr; } static clang::SourceLocation GetDeclStartLocation(clang::CompilerInstance* C, const clang::Decl* D) { auto &SM = C->getSourceManager(); auto startLoc = SM.getExpansionLoc(D->getLocStart()); auto startOffset = SM.getFileOffset(startLoc); auto lineNo = SM.getExpansionLineNumber(startLoc); auto lineBeginLoc = SM.translateLineCol(SM.getFileID(startLoc), lineNo, 1); auto lineBeginOffset = SM.getFileOffset(lineBeginLoc); assert(lineBeginOffset <= startOffset); auto prevDecl = GetPreviousDeclInContext(D); if(!prevDecl) return lineBeginLoc; auto prevDeclEndLoc = SM.getExpansionLoc(prevDecl->getLocEnd()); auto prevDeclEndOffset = SM.getFileOffset(prevDeclEndLoc); if(SM.getFileID(prevDeclEndLoc) != SM.getFileID(startLoc)) return lineBeginLoc; assert(prevDeclEndOffset <= startOffset); if(prevDeclEndOffset < lineBeginOffset) return lineBeginLoc; // Declarations don't share same macro expansion if(SM.getExpansionLoc(prevDecl->getLocStart()) != startLoc) return prevDeclEndLoc; return GetDeclStartLocation(C, prevDecl); } std::string Parser::GetTypeName(const clang::Type* Type) { using namespace clang; if(Type->isAnyPointerType() || Type->isReferenceType()) Type = Type->getPointeeType().getTypePtr(); if(Type->isEnumeralType() || Type->isRecordType()) { const clang::TagType* Tag = Type->getAs(); return GetTagDeclName(Tag->getDecl()); } PrintingPolicy pp(C->getLangOpts()); pp.SuppressTagKeyword = true; std::string TypeName; QualType::getAsStringInternal(Type, Qualifiers(), TypeName, pp); return TypeName; } CppSharp::AST::TypeQualifiers GetTypeQualifiers(clang::QualType Type) { CppSharp::AST::TypeQualifiers quals; quals.IsConst = Type.isLocalConstQualified(); quals.IsRestrict = Type.isLocalRestrictQualified(); quals.IsVolatile = Type.isVolatileQualified(); return quals; } CppSharp::AST::QualifiedType GetQualifiedType(clang::QualType qual, CppSharp::AST::Type^ type) { CppSharp::AST::QualifiedType qualType; qualType.Type = type; qualType.Qualifiers = GetTypeQualifiers(qual); return qualType; } //-----------------------------------// static CppSharp::AST::AccessSpecifier ConvertToAccess(clang::AccessSpecifier AS) { switch(AS) { case clang::AS_private: return CppSharp::AST::AccessSpecifier::Private; case clang::AS_protected: return CppSharp::AST::AccessSpecifier::Protected; case clang::AS_public: return CppSharp::AST::AccessSpecifier::Public; } return CppSharp::AST::AccessSpecifier::Public; } CppSharp::AST::Class^ Parser::WalkRecordCXX(clang::CXXRecordDecl* Record) { using namespace clang; using namespace clix; if (Record->isInjectedClassName()) return nullptr; auto NS = GetNamespace(Record); assert(NS && "Expected a valid namespace"); bool isCompleteDefinition = Record->isCompleteDefinition(); auto Name = marshalString(GetTagDeclName(Record)); auto RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/false); if (RC) return RC; RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/true); if (!isCompleteDefinition) return RC; auto headStartLoc = GetDeclStartLocation(C.get(), Record); auto headEndLoc = Record->getLocation(); // identifier location auto bodyEndLoc = Record->getLocEnd(); auto headRange = clang::SourceRange(headStartLoc, headEndLoc); auto bodyRange = clang::SourceRange(headEndLoc, bodyEndLoc); HandlePreprocessedEntities(RC, headRange, CppSharp::AST::MacroLocation::ClassHead); HandlePreprocessedEntities(RC, bodyRange, CppSharp::AST::MacroLocation::ClassBody); RC->IsPOD = Record->isPOD(); RC->IsUnion = Record->isUnion(); RC->IsAbstract = Record->isAbstract(); RC->IsDependent = Record->isDependentType(); auto &Sema = C->getSema(); Sema.ForceDeclarationOfImplicitMembers(Record); // Get the record layout information. const ASTRecordLayout* Layout = 0; if (!Record->isDependentType()) { Layout = &C->getASTContext().getASTRecordLayout(Record); RC->Layout->Alignment = (int)Layout-> getAlignment().getQuantity(); RC->Layout->Size = (int)Layout->getSize().getQuantity(); RC->Layout->DataSize = (int)Layout->getDataSize().getQuantity(); } CppSharp::AST::AccessSpecifierDecl^ AccessDecl = nullptr; for(auto it = Record->decls_begin(); it != Record->decls_end(); ++it) { auto D = *it; switch(D->getKind()) { case Decl::CXXConstructor: case Decl::CXXDestructor: case Decl::CXXConversion: case Decl::CXXMethod: { auto MD = cast(D); auto Method = WalkMethodCXX(MD); Method->AccessDecl = AccessDecl; RC->Methods->Add(Method); HandleComments(MD, Method); break; } case Decl::Field: { auto FD = cast(D); auto Field = WalkFieldCXX(FD, RC); if (Layout) Field->Offset = Layout->getFieldOffset(FD->getFieldIndex()); RC->Fields->Add(Field); HandleComments(FD, Field); break; } case Decl::AccessSpec: { AccessSpecDecl* AS = cast(D); AccessDecl = gcnew CppSharp::AST::AccessSpecifierDecl(); AccessDecl->Access = ConvertToAccess(AS->getAccess()); AccessDecl->Namespace = RC; auto startLoc = GetDeclStartLocation(C.get(), AS); auto range = SourceRange(startLoc, AS->getColonLoc()); HandlePreprocessedEntities(AccessDecl, range, CppSharp::AST::MacroLocation::Unknown); RC->Specifiers->Add(AccessDecl); break; } case Decl::IndirectField: // FIXME: Handle indirect fields default: { auto Decl = WalkDeclaration(D); break; } } } // Iterate through the record bases. for(auto it = Record->bases_begin(); it != Record->bases_end(); ++it) { clang::CXXBaseSpecifier &BS = *it; CppSharp::AST::BaseClassSpecifier^ Base = gcnew CppSharp::AST::BaseClassSpecifier(); Base->Access = ConvertToAccess(BS.getAccessSpecifier()); Base->IsVirtual = BS.isVirtual(); Base->Type = WalkType(BS.getType(), &BS.getTypeSourceInfo()->getTypeLoc()); RC->Bases->Add(Base); } return RC; } //-----------------------------------// CppSharp::AST::ClassTemplate^ Parser::WalkClassTemplate(clang::ClassTemplateDecl* TD) { using namespace clang; using namespace clix; auto Class = WalkRecordCXX(TD->getTemplatedDecl()); CppSharp::AST::ClassTemplate^ CT = gcnew CppSharp::AST::ClassTemplate(Class); auto TPL = TD->getTemplateParameters(); for(auto it = TPL->begin(); it != TPL->end(); ++it) { auto ND = *it; auto TP = CppSharp::AST::TemplateParameter(); TP.Name = clix::marshalString(ND->getNameAsString()); CT->Parameters->Add(TP); } return CT; } //-----------------------------------// CppSharp::AST::FunctionTemplate^ Parser::WalkFunctionTemplate(clang::FunctionTemplateDecl* TD) { using namespace clang; using namespace clix; auto Function = WalkFunction(TD->getTemplatedDecl(), /*IsDependent=*/true, /*AddToNamespace=*/false); CppSharp::AST::FunctionTemplate^ FT = gcnew CppSharp::AST::FunctionTemplate(Function); auto TPL = TD->getTemplateParameters(); for(auto it = TPL->begin(); it != TPL->end(); ++it) { auto ND = *it; auto TP = CppSharp::AST::TemplateParameter(); TP.Name = clix::marshalString(ND->getNameAsString()); FT->Parameters->Add(TP); } return FT; } //-----------------------------------// static CppSharp::AST::CXXMethodKind GetMethodKindFromDecl(clang::DeclarationName Name) { using namespace clang; switch(Name.getNameKind()) { case DeclarationName::Identifier: case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: return CppSharp::AST::CXXMethodKind::Normal; case DeclarationName::CXXConstructorName: return CppSharp::AST::CXXMethodKind::Constructor; case DeclarationName::CXXDestructorName: return CppSharp::AST::CXXMethodKind::Destructor; case DeclarationName::CXXConversionFunctionName: return CppSharp::AST::CXXMethodKind::Conversion; case DeclarationName::CXXOperatorName: case DeclarationName::CXXLiteralOperatorName: return CppSharp::AST::CXXMethodKind::Operator; case DeclarationName::CXXUsingDirective: return CppSharp::AST::CXXMethodKind::UsingDirective; } return CppSharp::AST::CXXMethodKind::Normal; } static CppSharp::AST::CXXOperatorKind GetOperatorKindFromDecl(clang::DeclarationName Name) { using namespace clang; if (Name.getNameKind() != DeclarationName::CXXOperatorName) return CppSharp::AST::CXXOperatorKind::None; switch(Name.getCXXOverloadedOperator()) { #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ case OO_##Name: return CppSharp::AST::CXXOperatorKind::Name; #include "clang/Basic/OperatorKinds.def" } return CppSharp::AST::CXXOperatorKind::None; } CppSharp::AST::Method^ Parser::WalkMethodCXX(clang::CXXMethodDecl* MD) { using namespace clang; DeclarationName Name = MD->getDeclName(); CppSharp::AST::Method^ Method = gcnew CppSharp::AST::Method(); Method->Access = ConvertToAccess(MD->getAccess()); Method->Kind = GetMethodKindFromDecl(Name); Method->OperatorKind = GetOperatorKindFromDecl(Name); Method->IsStatic = MD->isStatic(); WalkFunction(MD, Method); if (const CXXConstructorDecl* CD = dyn_cast(MD)) { Method->IsDefaultConstructor = CD->isDefaultConstructor(); Method->IsCopyConstructor = CD->isCopyConstructor(); Method->IsMoveConstructor = CD->isMoveConstructor(); } else if (const CXXDestructorDecl* DD = dyn_cast(MD)) { } else if (const CXXConversionDecl* CD = dyn_cast(MD)) { } return Method; } //-----------------------------------// CppSharp::AST::Field^ Parser::WalkFieldCXX(clang::FieldDecl* FD, CppSharp::AST::Class^ Class) { using namespace clang; using namespace clix; CppSharp::AST::Field^ F = gcnew CppSharp::AST::Field(); F->Namespace = Class; F->Name = marshalString(FD->getName()); auto TL = FD->getTypeSourceInfo()->getTypeLoc(); F->QualifiedType = GetQualifiedType(FD->getType(), WalkType(FD->getType(), &TL)); F->Access = ConvertToAccess(FD->getAccess()); F->Class = Class; HandleComments(FD, F); return F; } //-----------------------------------// CppSharp::AST::TranslationUnit^ Parser::GetTranslationUnit(clang::SourceLocation Loc, SourceLocationKind *Kind) { using namespace clang; SourceManager& SM = C->getSourceManager(); if (Loc.isMacroID()) Loc = SM.getExpansionLoc(Loc); StringRef File; auto LocKind = GetLocationKind(Loc); switch(LocKind) { case SourceLocationKind::Invalid: File = ""; break; case SourceLocationKind::Builtin: File = ""; break; case SourceLocationKind::CommandLine: File = ""; break; default: File = SM.getFilename(Loc); assert(!File.empty() && "Expected to find a valid file"); break; } if (Kind) *Kind = LocKind; auto Unit = Lib->FindOrCreateModule(clix::marshalString(File)); if (LocKind != SourceLocationKind::Invalid) Unit->IsSystemHeader = SM.isInSystemHeader(Loc); return Unit; } //-----------------------------------// CppSharp::AST::TranslationUnit^ Parser::GetTranslationUnit(const clang::Decl* D) { clang::SourceLocation Loc = D->getLocation(); SourceLocationKind Kind; CppSharp::AST::TranslationUnit^ Unit = GetTranslationUnit(Loc, &Kind); return Unit; } CppSharp::AST::DeclarationContext^ Parser::GetNamespace(clang::Decl* D, clang::DeclContext *Ctx) { using namespace clang; // If the declaration is at global scope, just early exit. if (Ctx->isTranslationUnit()) return GetTranslationUnit(D); CppSharp::AST::TranslationUnit^ Unit = GetTranslationUnit(cast(Ctx)); // Else we need to do a more expensive check to get all the namespaces, // and then perform a reverse iteration to get the namespaces in order. typedef SmallVector ContextsTy; ContextsTy Contexts; for(; Ctx != nullptr; Ctx = Ctx->getParent()) Contexts.push_back(Ctx); assert(Contexts.back()->isTranslationUnit()); Contexts.pop_back(); CppSharp::AST::DeclarationContext^ DC = Unit; for (auto I = Contexts.rbegin(), E = Contexts.rend(); I != E; ++I) { DeclContext* Ctx = *I; switch(Ctx->getDeclKind()) { case Decl::Namespace: { const NamespaceDecl* ND = cast(Ctx); if (ND->isAnonymousNamespace()) continue; auto Name = clix::marshalString(ND->getName()); DC = DC->FindCreateNamespace(Name); continue; } case Decl::LinkageSpec: { const LinkageSpecDecl* LD = cast(Ctx); continue; } case Decl::CXXRecord: { auto RD = cast(Ctx); DC = WalkRecordCXX(RD); continue; } case Decl::ClassTemplateSpecialization: { // FIXME: Ignore ClassTemplateSpecialization namespaces... // We might be able to translate these to C# nested types. continue; } default: { StringRef Kind = Ctx->getDeclKindName(); printf("Unhandled declaration context kind: %s\n", Kind); assert(0 && "Unhandled declaration context kind"); } } } return DC; } CppSharp::AST::DeclarationContext^ Parser::GetNamespace(clang::Decl *D) { return GetNamespace(D, D->getDeclContext()); } static CppSharp::AST::PrimitiveType WalkBuiltinType(const clang::BuiltinType* Builtin) { using namespace CppSharp::AST; assert(Builtin && "Expected a builtin type"); switch(Builtin->getKind()) { case clang::BuiltinType::Void: return PrimitiveType::Void; case clang::BuiltinType::Bool: return PrimitiveType::Bool; case clang::BuiltinType::SChar: case clang::BuiltinType::Char_S: return PrimitiveType::Int8; case clang::BuiltinType::UChar: case clang::BuiltinType::Char_U: return PrimitiveType::UInt8; case clang::BuiltinType::WChar_S: case clang::BuiltinType::WChar_U: return PrimitiveType::WideChar; case clang::BuiltinType::Short: return PrimitiveType::Int16; case clang::BuiltinType::UShort: return PrimitiveType::UInt16; case clang::BuiltinType::Int: return PrimitiveType::Int32; case clang::BuiltinType::UInt: return PrimitiveType::UInt32; case clang::BuiltinType::Long: return PrimitiveType::Int32; case clang::BuiltinType::ULong: return PrimitiveType::UInt32; case clang::BuiltinType::LongLong: return PrimitiveType::Int64; case clang::BuiltinType::ULongLong: return PrimitiveType::UInt64; case clang::BuiltinType::Float: return PrimitiveType::Float; case clang::BuiltinType::Double: return PrimitiveType::Double; case clang::BuiltinType::NullPtr: return PrimitiveType::Null; default: break; } return PrimitiveType::Null; } //-----------------------------------// clang::TypeLoc ResolveTypeLoc(clang::TypeLoc TL, clang::TypeLoc::TypeLocClass Class) { using namespace clang; auto TypeLocClass = TL.getTypeLocClass(); if (TypeLocClass == Class) { return TL; } if (TypeLocClass == TypeLoc::Qualified) { auto UTL = TL.getUnqualifiedLoc(); TL = UTL; } else if (TypeLocClass == TypeLoc::Elaborated) { auto ETL = TL.getAs(); auto ITL = ETL.getNextTypeLoc(); TL = ITL; } else if (TypeLocClass == TypeLoc::Paren) { auto PTL = TL.getAs(); TL = PTL.getNextTypeLoc(); } assert(TL.getTypeLocClass() == Class); return TL; } CppSharp::AST::Type^ Parser::WalkType(clang::QualType QualType, clang::TypeLoc* TL, bool DesugarType) { using namespace clang; using namespace clix; if (QualType.isNull()) return nullptr; const clang::Type* Type = QualType.getTypePtr(); if (DesugarType) { clang::QualType Desugared = QualType.getDesugaredType(*AST); assert(!Desugared.isNull() && "Expected a valid desugared type"); Type = Desugared.getTypePtr(); } assert(Type && "Expected a valid type"); switch(Type->getTypeClass()) { case Type::Builtin: { auto Builtin = Type->getAs(); assert(Builtin && "Expected a builtin type"); auto BT = gcnew CppSharp::AST::BuiltinType(); BT->Type = WalkBuiltinType(Builtin); return BT; } case Type::Enum: { auto ET = Type->getAs(); EnumDecl* ED = ET->getDecl(); //auto Name = marshalString(GetTagDeclName(ED)); auto TT = gcnew CppSharp::AST::TagType(); TT->Declaration = WalkDeclaration(ED, 0, /*IgnoreSystemDecls=*/false); return TT; } case Type::Pointer: { auto Pointer = Type->getAs(); auto P = gcnew CppSharp::AST::PointerType(); P->Modifier = CppSharp::AST::PointerType::TypeModifier::Pointer; auto Next = TL->getNextTypeLoc(); auto Pointee = Pointer->getPointeeType(); P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); return P; } case Type::Typedef: { auto TT = Type->getAs(); TypedefNameDecl* TD = TT->getDecl(); auto TTL = TD->getTypeSourceInfo()->getTypeLoc(); auto TDD = safe_cast(WalkDeclaration(TD, /*IgnoreSystemDecls=*/false)); auto Type = gcnew CppSharp::AST::TypedefType(); Type->Declaration = TDD; return Type; } case Type::Decayed: { auto DT = Type->getAs(); auto Next = TL->getNextTypeLoc(); auto Type = gcnew CppSharp::AST::DecayedType(); Type->Decayed = GetQualifiedType(DT->getDecayedType(), WalkType(DT->getDecayedType(), &Next)); Type->Original = GetQualifiedType(DT->getOriginalType(), WalkType(DT->getOriginalType(), &Next)); Type->Pointee = GetQualifiedType(DT->getPointeeType(), WalkType(DT->getPointeeType(), &Next)); return Type; } case Type::Elaborated: { auto ET = Type->getAs(); auto Next = TL->getNextTypeLoc(); return WalkType(ET->getNamedType(), &Next); } case Type::Record: { auto RT = Type->getAs(); RecordDecl* RD = RT->getDecl(); auto TT = gcnew CppSharp::AST::TagType(); TT->Declaration = WalkDeclaration(RD, /*IgnoreSystemDecls=*/false); return TT; } case Type::Paren: { auto PT = Type->getAs(); auto Next = TL->getNextTypeLoc(); return WalkType(PT->getInnerType(), &Next); } case Type::ConstantArray: { auto AT = AST->getAsConstantArrayType(QualType); auto A = gcnew CppSharp::AST::ArrayType(); auto Next = TL->getNextTypeLoc(); A->Type = WalkType(AT->getElementType(), &Next); A->SizeType = CppSharp::AST::ArrayType::ArraySize::Constant; A->Size = AST->getConstantArrayElementCount(AT); return A; } case Type::IncompleteArray: { auto AT = AST->getAsIncompleteArrayType(QualType); auto A = gcnew CppSharp::AST::ArrayType(); auto Next = TL->getNextTypeLoc(); A->Type = WalkType(AT->getElementType(), &Next); A->SizeType = CppSharp::AST::ArrayType::ArraySize::Incomplete; return A; } case Type::DependentSizedArray: { auto AT = AST->getAsDependentSizedArrayType(QualType); auto A = gcnew CppSharp::AST::ArrayType(); auto Next = TL->getNextTypeLoc(); A->Type = WalkType(AT->getElementType(), &Next); A->SizeType = CppSharp::AST::ArrayType::ArraySize::Dependent; //A->Size = AT->getSizeExpr(); return A; } case Type::FunctionProto: { auto FP = Type->getAs(); auto FTL = TL->getAs(); auto RL = FTL.getResultLoc(); auto F = gcnew CppSharp::AST::FunctionType(); F->ReturnType = GetQualifiedType(FP->getResultType(), WalkType(FP->getResultType(), &RL)); for (unsigned i = 0; i < FP->getNumArgs(); ++i) { auto FA = gcnew CppSharp::AST::Parameter(); auto PVD = FTL.getArg(i); auto PTL = PVD->getTypeSourceInfo()->getTypeLoc(); FA->Name = marshalString(PVD->getNameAsString()); FA->QualifiedType = GetQualifiedType(PVD->getType(), WalkType(PVD->getType(), &PTL)); F->Parameters->Add(FA); } return F; } case Type::TypeOf: { auto TO = Type->getAs(); return WalkType(TO->getUnderlyingType()); } case Type::TypeOfExpr: { auto TO = Type->getAs(); return WalkType(TO->getUnderlyingExpr()->getType()); } case Type::MemberPointer: { auto MP = Type->getAs(); auto Next = TL->getNextTypeLoc(); auto MPT = gcnew CppSharp::AST::MemberPointerType(); MPT->Pointee = WalkType(MP->getPointeeType(), &Next); return MPT; } case Type::TemplateSpecialization: { auto TS = Type->getAs(); auto TST = gcnew CppSharp::AST::TemplateSpecializationType(); TemplateName Name = TS->getTemplateName(); TST->Template = safe_cast(WalkDeclaration( Name.getAsTemplateDecl(), 0, /*IgnoreSystemDecls=*/false)); if (TS->isSugared()) TST->Desugared = WalkType(TS->desugar()); auto TypeLocClass = TL->getTypeLocClass(); if (TypeLocClass == TypeLoc::Qualified) { auto UTL = TL->getUnqualifiedLoc(); TL = &UTL; } else if (TypeLocClass == TypeLoc::Elaborated) { auto ETL = TL->getAs(); auto ITL = ETL.getNextTypeLoc(); TL = &ITL; } assert(TL->getTypeLocClass() == TypeLoc::TemplateSpecialization); auto TSTL = TL->getAs(); for (unsigned I = 0, E = TS->getNumArgs(); I != E; ++I) { const TemplateArgument& TA = TS->getArg(I); auto Arg = CppSharp::AST::TemplateArgument(); TemplateArgumentLoc ArgLoc; ArgLoc = TSTL.getArgLoc(I); switch(TA.getKind()) { case TemplateArgument::Type: { Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Type; TypeLoc ArgTL; ArgTL = ArgLoc.getTypeSourceInfo()->getTypeLoc(); Arg.Type = GetQualifiedType(TA.getAsType(), WalkType(TA.getAsType(), &ArgTL)); break; } case TemplateArgument::Declaration: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Declaration; Arg.Declaration = WalkDeclaration(TA.getAsDecl(), 0); break; case TemplateArgument::NullPtr: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::NullPtr; break; case TemplateArgument::Integral: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Integral; //Arg.Type = WalkType(TA.getIntegralType(), 0); Arg.Integral = TA.getAsIntegral().getLimitedValue(); break; case TemplateArgument::Template: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Template; break; case TemplateArgument::TemplateExpansion: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::TemplateExpansion; break; case TemplateArgument::Expression: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Expression; break; case TemplateArgument::Pack: Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Pack; break; } TST->Arguments->Add(Arg); } return TST; } case Type::TemplateTypeParm: { auto TP = Type->getAs(); auto TPT = gcnew CppSharp::AST::TemplateParameterType(); if (auto Ident = TP->getIdentifier()) TPT->Parameter.Name = marshalString(Ident->getName()); return TPT; } case Type::SubstTemplateTypeParm: { auto TP = Type->getAs(); auto Ty = TP->getReplacementType(); auto TPT = gcnew CppSharp::AST::TemplateParameterSubstitutionType(); auto Next = TL->getNextTypeLoc(); TPT->Replacement = GetQualifiedType(Ty, WalkType(Ty, &Next)); return TPT; } case Type::InjectedClassName: { auto ICN = Type->getAs(); auto ICNT = gcnew CppSharp::AST::InjectedClassNameType(); ICNT->Class = safe_cast(WalkDeclaration( ICN->getDecl(), 0, /*IgnoreSystemDecls=*/false)); return ICNT; } case Type::DependentName: { auto DN = Type->getAs(); auto DNT = gcnew CppSharp::AST::DependentNameType(); return DNT; } case Type::LValueReference: { auto LR = Type->getAs(); auto P = gcnew CppSharp::AST::PointerType(); P->Modifier = CppSharp::AST::PointerType::TypeModifier::LVReference; TypeLoc Next; if (!TL->isNull()) Next = TL->getNextTypeLoc(); auto Pointee = LR->getPointeeType(); P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); return P; } case Type::RValueReference: { auto LR = Type->getAs(); auto P = gcnew CppSharp::AST::PointerType(); P->Modifier = CppSharp::AST::PointerType::TypeModifier::RVReference; TypeLoc Next; if (!TL->isNull()) Next = TL->getNextTypeLoc(); auto Pointee = LR->getPointeeType(); P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); return P; } case Type::Vector: { // GCC-specific / __attribute__((vector_size(n)) return nullptr; } case Type::PackExpansion: { // Ignored. return nullptr; } default: { Debug("Unhandled type class '%s'\n", Type->getTypeClassName()); return nullptr; } } } //-----------------------------------// CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) { using namespace clang; using namespace clix; auto NS = GetNamespace(ED); assert(NS && "Expected a valid namespace"); auto Name = marshalString(GetTagDeclName(ED)); auto E = NS->FindEnum(Name, /*Create=*/false); if (E && !E->IsIncomplete) return E; if (!E) E = NS->FindEnum(Name, /*Create=*/true); if (ED->isScoped()) E->Modifiers |= CppSharp::AST::Enumeration::EnumModifiers::Scoped; // Get the underlying integer backing the enum. QualType IntType = ED->getIntegerType(); E->Type = WalkType(IntType, 0); E->BuiltinType = safe_cast(WalkType(IntType, 0, /*DesugarType=*/true)); if (!ED->isThisDeclarationADefinition()) { E->IsIncomplete = true; return E; } E->IsIncomplete = false; for(auto it = ED->enumerator_begin(); it != ED->enumerator_end(); ++it) { EnumConstantDecl* ECD = (*it); std::string BriefText; if (const RawComment* Comment = AST->getRawCommentForAnyRedecl(ECD)) BriefText = Comment->getBriefText(*AST); auto EnumItem = gcnew CppSharp::AST::Enumeration::Item(); EnumItem->Name = marshalString(ECD->getNameAsString()); auto Value = ECD->getInitVal(); EnumItem->Value = Value.isSigned() ? Value.getSExtValue() : Value.getZExtValue(); EnumItem->Comment = marshalString(BriefText); std::string Text; if (GetDeclText(ECD->getSourceRange(), Text)) EnumItem->Expression = marshalString(Text); E->AddItem(EnumItem); } return E; } //-----------------------------------// clang::CallingConv Parser::GetAbiCallConv(clang::CallingConv CC, bool IsInstMethod, bool IsVariadic) { using namespace clang; // TODO: Itanium ABI if (CC == CC_Default) { if (IsInstMethod) { CC = AST->getDefaultCXXMethodCallConv(IsVariadic); } else { CC = CC_C; } } return CC; } static CppSharp::AST::CallingConvention ConvertCallConv(clang::CallingConv CC) { using namespace clang; switch(CC) { case CC_Default: case CC_C: return CppSharp::AST::CallingConvention::C; case CC_X86StdCall: return CppSharp::AST::CallingConvention::StdCall; case CC_X86FastCall: return CppSharp::AST::CallingConvention::FastCall; case CC_X86ThisCall: return CppSharp::AST::CallingConvention::ThisCall; case CC_X86Pascal: case CC_AAPCS: case CC_AAPCS_VFP: return CppSharp::AST::CallingConvention::Unknown; } return CppSharp::AST::CallingConvention::Default; } void Parser::WalkFunction(clang::FunctionDecl* FD, CppSharp::AST::Function^ F, bool IsDependent) { using namespace clang; using namespace clix; assert (FD->getBuiltinID() == 0); auto FT = FD->getType()->getAs(); auto CC = FT->getCallConv(); auto NS = GetNamespace(FD); assert(NS && "Expected a valid namespace"); F->Name = marshalString(FD->getNameAsString()); F->Namespace = NS; F->IsVariadic = FD->isVariadic(); F->IsInline = FD->isInlined(); F->IsDependent = FD->isDependentContext(); F->IsPure = FD->isPure(); auto AbiCC = GetAbiCallConv(CC, FD->isCXXInstanceMember(), FD->isVariadic()); F->CallingConvention = ConvertCallConv(AbiCC); TypeLoc RTL; if (auto TSI = FD->getTypeSourceInfo()) { FunctionTypeLoc FTL = TSI->getTypeLoc().getAs(); RTL = FTL.getResultLoc(); auto &SM = C->getSourceManager(); auto headStartLoc = GetDeclStartLocation(C.get(), FD); auto headEndLoc = SM.getExpansionLoc(FTL.getLParenLoc()); auto headRange = clang::SourceRange(headStartLoc, headEndLoc); HandlePreprocessedEntities(F, headRange, CppSharp::AST::MacroLocation::FunctionHead); HandlePreprocessedEntities(F, FTL.getParensRange(), CppSharp::AST::MacroLocation::FunctionParameters); //auto bodyRange = clang::SourceRange(FTL.getRParenLoc(), FD->getLocEnd()); //HandlePreprocessedEntities(F, bodyRange, CppSharp::AST::MacroLocation::FunctionBody); } F->ReturnType = GetQualifiedType(FD->getResultType(), WalkType(FD->getResultType(), &RTL)); String Mangled = GetDeclMangledName(FD, TargetABI, IsDependent); F->Mangled = marshalString(Mangled); for(auto it = FD->param_begin(); it != FD->param_end(); ++it) { ParmVarDecl* VD = (*it); auto P = gcnew CppSharp::AST::Parameter(); P->Name = marshalString(VD->getNameAsString()); TypeLoc PTL; if (auto TSI = VD->getTypeSourceInfo()) PTL = VD->getTypeSourceInfo()->getTypeLoc(); P->QualifiedType = GetQualifiedType(VD->getType(), WalkType(VD->getType(), &PTL)); P->HasDefaultValue = VD->hasDefaultArg(); F->Parameters->Add(P); } } CppSharp::AST::Function^ Parser::WalkFunction(clang::FunctionDecl* FD, bool IsDependent, bool AddToNamespace) { using namespace clang; using namespace clix; assert (FD->getBuiltinID() == 0); auto NS = GetNamespace(FD); assert(NS && "Expected a valid namespace"); auto Name = marshalString(FD->getNameAsString()); CppSharp::AST::Function^ F = NS->FindFunction(Name, /*Create=*/ false); if (F != nullptr) return F; F = gcnew CppSharp::AST::Function(); WalkFunction(FD, F, IsDependent); if (AddToNamespace) NS->Functions->Add(F); return F; } //-----------------------------------// SourceLocationKind Parser::GetLocationKind(const clang::SourceLocation& Loc) { using namespace clang; SourceManager& SM = C->getSourceManager(); PresumedLoc PLoc = SM.getPresumedLoc(Loc); if(PLoc.isInvalid()) return SourceLocationKind::Invalid; const char *FileName = PLoc.getFilename(); if(strcmp(FileName, "") == 0) return SourceLocationKind::Builtin; if(strcmp(FileName, "") == 0) return SourceLocationKind::CommandLine; if(SM.getFileCharacteristic(Loc) == clang::SrcMgr::C_User) return SourceLocationKind::User; return SourceLocationKind::System; } bool Parser::IsValidDeclaration(const clang::SourceLocation& Loc) { auto Kind = GetLocationKind(Loc); return Kind == SourceLocationKind::User; } //-----------------------------------// void Parser::WalkAST() { using namespace clang; if (C->hasPreprocessor()) { Preprocessor& P = C->getPreprocessor(); PreprocessingRecord* PR = P.getPreprocessingRecord(); if (PR) { assert(PR && "Expected a valid preprocessing record"); WalkMacros(PR); } } TranslationUnitDecl* TU = AST->getTranslationUnitDecl(); for(auto it = TU->decls_begin(); it != TU->decls_end(); ++it) { Decl* D = (*it); WalkDeclarationDef(D); } } //-----------------------------------// void Parser::WalkMacros(clang::PreprocessingRecord* PR) { using namespace clang; using namespace clix; Preprocessor& P = C->getPreprocessor(); for(auto it = PR->begin(); it != PR->end(); ++it) { const PreprocessedEntity* PE = (*it); switch(PE->getKind()) { case PreprocessedEntity::MacroDefinitionKind: { const MacroDefinition* MD = cast(PE); if (!IsValidDeclaration(MD->getLocation())) break; const IdentifierInfo* II = MD->getName(); assert(II && "Expected valid identifier info"); MacroInfo* MI = P.getMacroInfo((IdentifierInfo*)II); if (!MI || MI->isBuiltinMacro() || MI->isFunctionLike()) continue; SourceManager& SM = C->getSourceManager(); const LangOptions &LangOpts = C->getLangOpts(); auto Loc = MI->getDefinitionLoc(); if (!IsValidDeclaration(Loc)) break; SourceLocation BeginExpr = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); auto Range = CharSourceRange::getTokenRange( BeginExpr, MI->getDefinitionEndLoc()); bool Invalid; StringRef Expression = Lexer::getSourceText(Range, SM, LangOpts, &Invalid); if (Invalid || Expression.empty()) break; auto macro = gcnew CppSharp::AST::MacroDefinition(); macro->Name = marshalString(II->getName())->Trim(); macro->Expression = marshalString(Expression)->Trim(); auto M = GetTranslationUnit(BeginExpr); if( M != nullptr ) M->Macros->Add(macro); break; } default: break; } } } //-----------------------------------// CppSharp::AST::Variable^ Parser::WalkVariable(clang::VarDecl *VD) { using namespace clang; using namespace clix; auto Var = gcnew CppSharp::AST::Variable(); Var->Name = marshalString(VD->getName()); Var->Access = ConvertToAccess(VD->getAccess()); auto TL = VD->getTypeSourceInfo()->getTypeLoc(); Var->QualifiedType = GetQualifiedType(VD->getType(), WalkType(VD->getType(), &TL)); auto Mangled = GetDeclMangledName(VD, TargetABI, /*IsDependent=*/false); Var->Mangled = marshalString(Mangled); return Var; } //-----------------------------------// void Parser::HandleComments(clang::Decl* D, CppSharp::AST::Declaration^ Decl) { using namespace clang; using namespace clix; // Get the declaration comment. std::string BriefText; if (const RawComment* Comment = AST->getRawCommentForAnyRedecl(D)) BriefText = Comment->getBriefText(*AST); Decl->BriefComment = marshalString(BriefText); SourceManager& SM = C->getSourceManager(); const LangOptions& LangOpts = C->getLangOpts(); auto Range = CharSourceRange::getTokenRange(D->getSourceRange()); bool Invalid; StringRef DeclText = Lexer::getSourceText(Range, SM, LangOpts, &Invalid); //assert(!Invalid && "Should have a valid location"); if (!Invalid) Decl->DebugText = marshalString(DeclText); } //-----------------------------------// bool Parser::GetDeclText(clang::SourceRange SR, std::string& Text) { using namespace clang; SourceManager& SM = C->getSourceManager(); const LangOptions &LangOpts = C->getLangOpts(); auto Range = CharSourceRange::getTokenRange(SR); bool Invalid; Text = Lexer::getSourceText(Range, SM, LangOpts, &Invalid); return !Invalid && !Text.empty(); } void Parser::HandlePreprocessedEntities(CppSharp::AST::Declaration^ Decl, clang::SourceRange sourceRange, CppSharp::AST::MacroLocation macroLocation) { using namespace clang; auto PPRecord = C->getPreprocessor().getPreprocessingRecord(); auto Range = PPRecord->getPreprocessedEntitiesInRange(sourceRange); for (auto it = Range.first; it != Range.second; ++it) { PreprocessedEntity* PPEntity = (*it); CppSharp::AST::PreprocessedEntity^ Entity; switch(PPEntity->getKind()) { case PreprocessedEntity::MacroExpansionKind: { const MacroExpansion* MD = cast(PPEntity); Entity = gcnew CppSharp::AST::MacroExpansion(); std::string Text; if (!GetDeclText(PPEntity->getSourceRange(), Text)) continue; static_cast(Entity)->Text = clix::marshalString(Text); break; } case PreprocessedEntity::MacroDefinitionKind: { const MacroDefinition* MD = cast(PPEntity); Entity = gcnew CppSharp::AST::MacroDefinition(); break; } default: continue; } Entity->Location = macroLocation; Decl->PreprocessedEntities->Add(Entity); } } //-----------------------------------// CppSharp::AST::Declaration^ Parser::WalkDeclarationDef(clang::Decl* D) { return WalkDeclaration(D, /*IgnoreSystemDecls=*/true, /*CanBeDefinition=*/true); } CppSharp::AST::Declaration^ Parser::WalkDeclaration(clang::Decl* D, bool IgnoreSystemDecls, bool CanBeDefinition) { using namespace clang; using namespace clix; // Ignore declarations that do not come from user-provided // header files. if (IgnoreSystemDecls && !IsValidDeclaration(D->getLocation())) return nullptr; for(auto it = D->attr_begin(); it != D->attr_end(); ++it) { Attr* Attr = (*it); if(Attr->getKind() != clang::attr::Annotate) continue; AnnotateAttr* Annotation = cast(Attr); assert(Annotation != nullptr); StringRef AnnotationText = Annotation->getAnnotation(); } CppSharp::AST::Declaration^ Decl = nullptr; auto Kind = D->getKind(); switch(D->getKind()) { case Decl::CXXRecord: { CXXRecordDecl* RD = cast(D); auto Class = WalkRecordCXX(RD); // We store a definition order index into the declarations. // This is needed because declarations are added to their contexts as // soon as they are referenced and we need to know the original order // of the declarations. if (CanBeDefinition && Class->DefinitionOrder == 0) { Class->DefinitionOrder = Index++; //Debug("%d: %s\n", Index++, GetTagDeclName(RD).c_str()); } Decl = Class; break; } case Decl::ClassTemplate: { ClassTemplateDecl* TD = cast(D); auto Template = WalkClassTemplate(TD); auto NS = GetNamespace(TD); Template->Namespace = NS; NS->Templates->Add(Template); Decl = Template; break; } case Decl::ClassTemplateSpecialization: { auto TS = cast(D); auto CT = gcnew CppSharp::AST::ClassTemplateSpecialization(); Decl = CT; break; } case Decl::ClassTemplatePartialSpecialization: { auto TS = cast(D); auto CT = gcnew CppSharp::AST::ClassTemplatePartialSpecialization(); Decl = CT; break; } case Decl::FunctionTemplate: { FunctionTemplateDecl* TD = cast(D); auto Template = WalkFunctionTemplate(TD); auto NS = GetNamespace(TD); Template->Namespace = NS; NS->Templates->Add(Template); Decl = Template; break; } case Decl::Enum: { EnumDecl* ED = cast(D); Decl = WalkEnum(ED); break; } case Decl::Function: { FunctionDecl* FD = cast(D); if (!FD->isFirstDeclaration()) break; // Check for and ignore built-in functions. if (FD->getBuiltinID() != 0) break; Decl = WalkFunction(FD); break; } case Decl::LinkageSpec: { LinkageSpecDecl* LS = cast(D); for (auto it = LS->decls_begin(); it != LS->decls_end(); ++it) { clang::Decl* D = (*it); Decl = WalkDeclarationDef(D); } break; } case Decl::Typedef: { TypedefDecl* TD = cast(D); auto NS = GetNamespace(TD); auto Name = marshalString(GetDeclName(TD)); auto Typedef = NS->FindTypedef(Name, /*Create=*/false); if (Typedef) return Typedef; Typedef = NS->FindTypedef(Name, /*Create=*/true); auto TTL = TD->getTypeSourceInfo()->getTypeLoc(); Typedef->QualifiedType = GetQualifiedType(TD->getUnderlyingType(), WalkType(TD->getUnderlyingType(), &TTL)); Decl = Typedef; break; } case Decl::Namespace: { NamespaceDecl* ND = cast(D); for (auto it = ND->decls_begin(); it != ND->decls_end(); ++it) { clang::Decl* D = (*it); Decl = WalkDeclarationDef(D); } break; } case Decl::Var: { auto VD = cast(D); Decl = WalkVariable(VD); auto NS = GetNamespace(VD); Decl->Namespace = NS; NS->Variables->Add(static_cast(Decl)); break; } // Ignore these declarations since they must have been declared in // a class already. case Decl::CXXConstructor: case Decl::CXXDestructor: case Decl::CXXConversion: case Decl::CXXMethod: break; case Decl::Empty: case Decl::AccessSpec: case Decl::Friend: case Decl::Using: case Decl::UsingShadow: case Decl::UnresolvedUsingTypename: case Decl::UnresolvedUsingValue: case Decl::IndirectField: break; default: { Debug("Unhandled declaration kind: %s\n", D->getDeclKindName()); auto &SM = C->getSourceManager(); auto Loc = D->getLocation(); auto FileName = SM.getFilename(Loc); auto Offset = SM.getFileOffset(Loc); auto LineNo = SM.getLineNumber(SM.getFileID(Loc), Offset); Debug(" %s (line %u)\n", FileName, LineNo); break; } }; if (Decl) { if (Decl->PreprocessedEntities->Count == 0) { auto startLoc = GetDeclStartLocation(C.get(), D); auto endLoc = D->getLocEnd(); auto range = clang::SourceRange(startLoc, endLoc); HandlePreprocessedEntities(Decl, range); } HandleComments(D, Decl); if (const ValueDecl *VD = dyn_cast_or_null(D)) Decl->IsDependent = VD->getType()->isDependentType(); } return Decl; } //-----------------------------------// struct Diagnostic { clang::SourceLocation Location; llvm::SmallString<100> Message; clang::DiagnosticsEngine::Level Level; }; struct DiagnosticConsumer : public clang::DiagnosticConsumer { virtual ~DiagnosticConsumer() { } virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level Level, const clang::Diagnostic& Info) override { auto Diag = Diagnostic(); Diag.Location = Info.getLocation(); Diag.Level = Level; Info.FormatDiagnostic(Diag.Message); Diagnostics.push_back(Diag); } std::vector Diagnostics; }; ParserResult^ Parser::ParseHeader(const std::string& File) { auto res = gcnew ParserResult(); res->Library = Lib; if (File.empty()) { res->Kind = ParserResultKind::FileNotFound; return res; } SetupHeader(); auto SC = new clang::SemaConsumer(); C->setASTConsumer(SC); C->createSema(clang::TU_Complete, 0); SC->InitializeSema(C->getSema()); auto DiagClient = new DiagnosticConsumer(); C->getDiagnostics().setClient(DiagClient); // Check that the file is reachable. const clang::DirectoryLookup *Dir; if (!C->getPreprocessor().getHeaderSearchInfo().LookupFile(File, /*isAngled*/true, nullptr, Dir, nullptr, nullptr, nullptr, nullptr)) { res->Kind = ParserResultKind::FileNotFound; return res; } // Create a virtual file that includes the header. This gets rid of some // Clang warnings about parsing an header file as the main file. std::string str; str += "#include \"" + File + "\"" + "\n"; str += "\0"; auto buffer = llvm::MemoryBuffer::getMemBuffer(str); C->getSourceManager().createMainFileIDForMemBuffer(buffer); clang::DiagnosticConsumer* client = C->getDiagnostics().getClient(); client->BeginSourceFile(C->getLangOpts(), &C->getPreprocessor()); ParseAST(C->getSema(), /*PrintStats=*/false, /*SkipFunctionBodies=*/true); client->EndSourceFile(); // Convert the diagnostics to the managed types for each (auto& Diag in DiagClient->Diagnostics) { using namespace clix; auto& Source = C->getSourceManager(); auto FileName = Source.getFilename(Diag.Location); auto PDiag = ParserDiagnostic(); PDiag.FileName = marshalString(FileName.str()); PDiag.Message = marshalString(Diag.Message.str()); PDiag.LineNumber = 0; PDiag.ColumnNumber = 0; if( !Diag.Location.isInvalid() ) { clang::PresumedLoc PLoc = Source.getPresumedLoc(Diag.Location); if( PLoc.isValid() ) { PDiag.LineNumber = PLoc.getLine(); PDiag.ColumnNumber = PLoc.getColumn(); } } switch( Diag.Level ) { case clang::DiagnosticsEngine::Ignored: PDiag.Level = ParserDiagnosticLevel::Ignored; break; case clang::DiagnosticsEngine::Note: PDiag.Level = ParserDiagnosticLevel::Note; break; case clang::DiagnosticsEngine::Warning: PDiag.Level = ParserDiagnosticLevel::Warning; break; case clang::DiagnosticsEngine::Error: PDiag.Level = ParserDiagnosticLevel::Error; break; case clang::DiagnosticsEngine::Fatal: PDiag.Level = ParserDiagnosticLevel::Fatal; break; default: assert(0); } res->Diagnostics->Add(PDiag); } if(C->getDiagnosticClient().getNumErrors() != 0) { res->Kind = ParserResultKind::Error; return res; } AST = &C->getASTContext(); WalkAST(); res->Kind = ParserResultKind::Success; return res; } ParserResultKind Parser::ParseArchive(llvm::StringRef File, llvm::MemoryBuffer *Buffer) { llvm::error_code Code; llvm::object::Archive Archive(Buffer, Code); if (Code) return ParserResultKind::Error; auto LibName = clix::marshalString(File); auto NativeLib = Lib->FindOrCreateLibrary(LibName); for(auto it = Archive.begin_symbols(); it != Archive.end_symbols(); ++it) { llvm::StringRef SymRef; if (it->getName(SymRef)) continue; System::String^ SymName = clix::marshalString(SymRef); NativeLib->Symbols->Add(SymName); } return ParserResultKind::Success; } ParserResultKind Parser::ParseSharedLib(llvm::StringRef File, llvm::MemoryBuffer *Buffer) { auto Object = llvm::object::ObjectFile::createObjectFile(Buffer); if (!Object) return ParserResultKind::Error; auto LibName = clix::marshalString(File); auto NativeLib = Lib->FindOrCreateLibrary(LibName); llvm::error_code ec; for(auto it = Object->begin_symbols(); it != Object->end_symbols(); it.increment(ec)) { llvm::StringRef SymRef; if (it->getName(SymRef)) continue; System::String^ SymName = clix::marshalString(SymRef); NativeLib->Symbols->Add(SymName); } for(auto it = Object->begin_dynamic_symbols(); it != Object->end_dynamic_symbols(); it.increment(ec)) { llvm::StringRef SymRef; if (it->getName(SymRef)) continue; System::String^ SymName = clix::marshalString(SymRef); NativeLib->Symbols->Add(SymName); } return ParserResultKind::Success; } ParserResult^ Parser::ParseLibrary(const std::string& File) { auto res = gcnew ParserResult(); res->Library = Lib; if (File.empty()) { res->Kind = ParserResultKind::FileNotFound; return res; } C.reset(new clang::CompilerInstance()); C->createFileManager(); auto &FM = C->getFileManager(); const clang::FileEntry* FileEntry = 0; for each(System::String^ LibDir in Opts->LibraryDirs) { auto DirName = clix::marshalString(LibDir); llvm::SmallString<256> Path(DirName); llvm::sys::path::append(Path, File); if (FileEntry = FM.getFile(Path.str())) break; } if (!FileEntry) { res->Kind = ParserResultKind::FileNotFound; return res; } res->Kind = ParseArchive(File, FM.getBufferForFile(FileEntry)); if (res->Kind == ParserResultKind::Success) return res; res->Kind = ParseSharedLib(File, FM.getBufferForFile(FileEntry)); if (res->Kind == ParserResultKind::Success) return res; return res; }