mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
aa33160d0c
New clang importer services parse a target file to find clang declarations. For each declaration it tracks back its declaration site and import it to Swift. It then generates a JSON format holding the parsed information and the imported Swift signature.
319 lines
10 KiB
C++
319 lines
10 KiB
C++
//===--- PolyglotAST.cpp --------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024-2026 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/JSON.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclVisitor.h"
|
|
#include "clang/Rewrite/Core/Rewriter.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/PrintOptions.h"
|
|
#include "swift/ClangImporter/ClangImporter.h"
|
|
#include "ImporterImpl.h"
|
|
|
|
using namespace swift;
|
|
|
|
class PolyglotAPIVisitor : public clang::ConstDeclVisitor<PolyglotAPIVisitor> {
|
|
ClangImporter &clangImporter;
|
|
PrintOptions printOptions;
|
|
llvm::json::OStream &Json;
|
|
|
|
// Container currently visited.
|
|
const clang::ObjCContainerDecl *openContainerDecl = nullptr;
|
|
|
|
void PrintCommonAttributes(const clang::NamedDecl* decl) {
|
|
Json.attribute("declKind", decl->getDeclKindName());
|
|
Json.attribute("diagName", decl->getQualifiedNameAsString());
|
|
}
|
|
|
|
std::string GetText(clang::SourceRange range) {
|
|
auto &clangSM = clangImporter.getClangInstance().getSourceManager();
|
|
auto &langOpts = clangImporter.getClangASTContext().getLangOpts();
|
|
auto clangRW = clang::Rewriter(clangSM, langOpts);
|
|
return clangRW.getRewrittenText(range);
|
|
}
|
|
|
|
std::optional<const clang::NamedDecl *>
|
|
FindInterfaceDeclaration(const clang::ObjCMethodDecl *decl) {
|
|
auto prop = decl->findPropertyDecl();
|
|
if (prop) {
|
|
// Look for the property declaring this accessor.
|
|
Json.attribute("debug", "note: findPropertyDecl path");
|
|
return prop;
|
|
} else if (decl->isOverriding()) {
|
|
// Look for inherited decls being overriden.
|
|
Json.attribute("debug", "note: isOverriding path");
|
|
auto overridens = SmallVector<const clang::ObjCMethodDecl*>();
|
|
decl->getOverriddenMethods(overridens);
|
|
decl = *overridens.rbegin();
|
|
return decl;
|
|
} else {
|
|
// Look for local decls introduced in this class.
|
|
Json.attribute("debug", "note: getClassInterface path");
|
|
auto interface = decl->getClassInterface();
|
|
auto sel = decl->getSelector();
|
|
bool isInterfaceMethod = decl->isInstanceMethod();
|
|
assert(interface);
|
|
auto sup = interface;
|
|
while (sup) {
|
|
auto localDecl = sup->getMethod(sel, isInterfaceMethod);
|
|
if (localDecl) {
|
|
return localDecl;
|
|
}
|
|
sup = sup->getSuperClass();
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
public:
|
|
PolyglotAPIVisitor(ClangImporter &clangImporter, llvm::json::OStream &Json):
|
|
clangImporter(clangImporter), Json(Json)
|
|
{
|
|
printOptions = PrintOptions::printForDiagnostics(AccessLevel::FilePrivate,
|
|
/*printFullConvention*/true);
|
|
printOptions.PrintOverrideKeyword = true;
|
|
printOptions.PrintPropertyAccessors = false;
|
|
printOptions.PrintAccess = true;
|
|
printOptions.SkipAttributes = true;
|
|
}
|
|
|
|
void VisitDecl(const clang::Decl *decl) {
|
|
Json.object([&] {
|
|
Json.attribute("formatKind", "unhandled");
|
|
Json.attribute("debug", "Unhandled kind");
|
|
Json.attribute("fullText", GetText(decl->getSourceRange()));
|
|
});
|
|
}
|
|
|
|
void VisitObjCContainerDecl(const clang::ObjCContainerDecl *decl) {
|
|
Json.object([&] {
|
|
PrintCommonAttributes(decl);
|
|
Json.attribute("formatKind", "container");
|
|
|
|
StringRef name = decl->getName();
|
|
if (auto cd = dyn_cast<clang::ObjCCategoryDecl>(decl)) {
|
|
auto interface = cd->getClassInterface();
|
|
name = interface->getIdentifier()->getName();
|
|
}
|
|
Json.attribute("className", name);
|
|
|
|
openContainerDecl = decl;
|
|
|
|
Json.attributeArray("properties", [&] {
|
|
for (auto prop : decl->properties()) {
|
|
if (prop->isImplicit())
|
|
continue;
|
|
VisitObjCPropertyDecl(prop);
|
|
}
|
|
});
|
|
|
|
Json.attributeArray("methods", [&] {
|
|
for (auto meth : decl->methods()) {
|
|
if (meth->isImplicit())
|
|
continue;
|
|
VisitObjCMethodDecl(meth);
|
|
}
|
|
});
|
|
|
|
openContainerDecl = nullptr;
|
|
});
|
|
}
|
|
|
|
void VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl) {
|
|
// Skip methods accessed directly, we look at them through the container.
|
|
if (!openContainerDecl)
|
|
return;
|
|
|
|
Json.object([&] {
|
|
PrintCommonAttributes(decl);
|
|
Json.attribute("formatKind", "method");
|
|
|
|
clang::Selector sel = decl->getSelector();
|
|
Json.attribute("selector", sel.getAsString());
|
|
|
|
auto start = decl->getDeclaratorEndLoc();
|
|
auto end = decl->getBodyRBrace();
|
|
if (end.isValid())
|
|
Json.attribute("body", GetText(clang::SourceRange(start, end)));
|
|
else
|
|
Json.attribute("body", nullptr);
|
|
|
|
// Find the declaration decl.
|
|
auto declRef =
|
|
FindInterfaceDeclaration(decl);
|
|
if (!declRef) {
|
|
Json.attribute("debug", "error: declaration in interfaces not found");
|
|
return;
|
|
}
|
|
|
|
// Import to Swift.
|
|
Decl *swiftDecl = clangImporter.importDeclDirectly(*declRef);
|
|
if (!swiftDecl) {
|
|
Json.attribute("debug", "error: import failed");
|
|
} else if (auto VD = dyn_cast<ValueDecl>(swiftDecl)) {
|
|
llvm::SmallString<64> storage;
|
|
llvm::raw_svector_ostream OS(storage);
|
|
|
|
SourceLoc loc = SourceLoc();
|
|
DeclContext *adopter = VD->getDeclContext();
|
|
Type adopterTy = adopter->getSelfTypeInContext();
|
|
|
|
swift::printRequirementStub(VD, adopter, adopterTy,
|
|
loc, OS,
|
|
/*objCAttr=*/true);
|
|
|
|
Json.attribute("swiftSignature", storage);
|
|
} else {
|
|
auto declKind = swiftDecl->getKindName(swiftDecl->getKind());
|
|
Json.attribute("debug",
|
|
("error: unhandled kind after import: " + declKind).str());
|
|
}
|
|
});
|
|
}
|
|
|
|
void VisitObjCPropertyDecl(const clang::ObjCPropertyDecl *decl) {
|
|
Json.object([&] {
|
|
PrintCommonAttributes(decl);
|
|
Json.attribute("formatKind", "property");
|
|
|
|
Decl *swiftDecl = clangImporter.importDeclDirectly(decl);
|
|
if (swiftDecl) {
|
|
llvm::SmallString<64> storage;
|
|
llvm::raw_svector_ostream OS(storage);
|
|
swiftDecl->print(OS, printOptions);
|
|
Json.attribute("swiftSignature", storage);
|
|
} else {
|
|
Json.attribute("debug", "error: import failed");
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
void ClangImporter::Implementation::lookupImplementationFileDecls(
|
|
llvm::function_ref<bool(ClangNode)> filter,
|
|
llvm::function_ref<void(const clang::Decl *)> receiver,
|
|
llvm::function_ref<void(const swift::ImportDecl *)> receiverImports
|
|
) const {
|
|
for (auto &Import : BridgeHeaderTopLevelImports) {
|
|
if (const auto *ImportD = Import.dyn_cast<swift::ImportDecl*>()) {
|
|
receiverImports(ImportD);
|
|
}
|
|
}
|
|
|
|
for (auto *ClangD : BridgeHeaderTopLevelDecls) {
|
|
if (filter(ClangD)) {
|
|
receiver(ClangD);
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Error
|
|
ClangImporter::Implementation::lookupImplementationFileDeclsFromFile(
|
|
StringRef Filename,
|
|
llvm::function_ref<void(const clang::Decl*)> receiver,
|
|
llvm::function_ref<void(const swift::ImportDecl *)> receiverImports)
|
|
const {
|
|
llvm::Expected<clang::FileEntryRef> ExpectedFile =
|
|
getClangPreprocessor().getFileManager().getFileRef(Filename);
|
|
if (!ExpectedFile) {
|
|
return ExpectedFile.takeError();
|
|
}
|
|
clang::FileEntryRef File = *ExpectedFile;
|
|
|
|
auto &ClangCtx = getClangASTContext();
|
|
auto &ClangSM = ClangCtx.getSourceManager();
|
|
|
|
// Look up the header in the includes of the bridging header.
|
|
if (BridgeHeaderFiles.count(File)) {
|
|
auto headerFilter = [&](ClangNode ClangN) -> bool {
|
|
if (ClangN.isNull())
|
|
return false;
|
|
|
|
auto ClangLoc = ClangSM.getFileLoc(ClangN.getLocation());
|
|
if (ClangLoc.isInvalid())
|
|
return false;
|
|
|
|
clang::OptionalFileEntryRef LocRef =
|
|
ClangSM.getFileEntryRefForID(ClangSM.getFileID(ClangLoc));
|
|
if (!LocRef || *LocRef != File)
|
|
return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
lookupImplementationFileDecls(headerFilter, receiver, receiverImports);
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
return llvm::make_error<llvm::StringError>(
|
|
"No information for target file",
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
void
|
|
ClangImporter::printPolyglotAST(StringRef Filename, raw_ostream &OS) {
|
|
SmallVector<const clang::Decl *, 32> ClangDecls;
|
|
auto headerReceiver = [&](const clang::Decl *D) {
|
|
ClangDecls.push_back(D);
|
|
};
|
|
|
|
SmallVector<const swift::ImportDecl *, 32> Imports;
|
|
auto importReceiver = [&](const swift::ImportDecl *D) {
|
|
Imports.push_back(D);
|
|
};
|
|
|
|
auto error = Impl.lookupImplementationFileDeclsFromFile(Filename,
|
|
headerReceiver, importReceiver);
|
|
// Even if there's an error here, keep on going. No actual work will be
|
|
// executed but we'll still generate the JSON output.
|
|
|
|
// Sort imported declarations in source order.
|
|
auto &ClangSM = getClangASTContext().getSourceManager();
|
|
std::sort(ClangDecls.begin(), ClangDecls.end(),
|
|
[&](const clang::Decl *LHS, const clang::Decl *RHS) -> bool {
|
|
return ClangSM.isBeforeInTranslationUnit(
|
|
LHS->getLocation(),
|
|
RHS->getLocation());
|
|
});
|
|
|
|
auto Json = llvm::json::OStream(OS, /*indent*/2);
|
|
Json.object([&] {
|
|
Json.attribute("formatVersion", 1);
|
|
Json.attribute("compilerVersion", version::getSwiftFullVersion());
|
|
Json.attributeArray("topLevelDecls", [&] {
|
|
for (auto *ID : Imports) {
|
|
Json.object([&] {
|
|
Json.attribute("formatKind", "import");
|
|
if (ID->getModule()) {
|
|
Json.attribute("target", ID->getModule()->getName().str());
|
|
} else {
|
|
auto clangImport = cast<clang::ImportDecl>(ID->getClangDecl());
|
|
auto target = clangImport->getImportedModule();
|
|
Json.attribute("target", target->getTopLevelModuleName());
|
|
}
|
|
});
|
|
}
|
|
for (auto *D : ClangDecls) {
|
|
PolyglotAPIVisitor(*this, Json).Visit(D);
|
|
}
|
|
});
|
|
|
|
if (error) {
|
|
Json.attribute("error", toString(std::move(error)));
|
|
}
|
|
});
|
|
}
|