Files
swift-mirror/lib/ClangImporter/PolyglotAST.cpp
T
Alexis Laferrière aa33160d0c ClangImporter: Intro services to parse ObjC implementation file
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.
2026-03-25 11:36:15 -07:00

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)));
}
});
}