glean/lang/clang/db.h (174 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <filesystem>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Config/llvm-config.h>
#include <folly/gen/Base.h>
#include <folly/MapUtil.h>
#include "glean/lang/clang/schema.h"
namespace facebook {
namespace glean {
namespace clangx {
namespace Buck = schema::Buck;
namespace Cxx = schema::Cxx1;
namespace Pp = schema::Pp1;
namespace Src = schema::Src;
namespace Sys = schema::Sys;
using SCHEMA = schema::SCHEMA;
/**
* A Clang AST batch
*
* Mostly provides utility functions for converting Clang things to
* Glean data.
*
*/
class ClangDB {
public:
struct Env {
Fact<Buck::Locator> locator;
folly::Optional<Fact<Buck::Platform>> platform;
std::filesystem::path root;
folly::Optional<std::string> subdir;
folly::Optional<std::string> path_prefix;
Batch<SCHEMA>& batch;
};
ClangDB(const Env& env, clang::CompilerInstance& ci)
: locator(env.locator),
platform(env.platform),
root(env.root),
subdir(env.subdir),
path_prefix(env.path_prefix),
batch(env.batch),
compilerInstance(ci) {}
ClangDB(const ClangDB&) = delete;
ClangDB operator=(const ClangDB&) = delete;
// Files
Fact<Src::File> fileFromEntry(const clang::FileEntry& entry);
bool isPhysicalFile(clang::FileID id) {
return sourceManager().getFileEntryForID(id);
}
folly::Optional<Fact<Src::File>> physicalFile(clang::FileID id) {
folly::Optional<Fact<Src::File>> res = folly::none;
if (auto data = folly::get_default(files, id, nullptr)) {
res = data->fact;
} else if (auto entry = sourceManager().getFileEntryForID(id)) {
res = fileFromEntry(*entry);
}
if (res) {
batch.fact<Src::FileLanguage>(res.value(), getLanguage());
}
return res;
}
Fact<Src::File> file(clang::FileID id) {
if (auto x = physicalFile(id)) {
return x.value();
} else {
return batch.fact<Src::File>(std::string("<builtin>"));
}
}
void IndexFailure(const clang::ASTContext& ctx) {
const auto* client = ctx.getDiagnostics().getClient();
batch.fact<Src::IndexFailure>(
file(ctx.getSourceManager().getMainFileID()),
Src::IndexFailureReason::CompileError,
client
? fmt::format(
"{} error(s) occurred while indexing", client->getNumErrors())
: "");
}
// Names
Fact<Cxx::Name> name(const clang::Token& name) {
return batch.fact<Cxx::Name>(
static_cast<std::string>(name.getIdentifierInfo()->getName()));
}
Fact<Cxx::Name> name(clang::StringRef ref) {
return batch.fact<Cxx::Name>(static_cast<std::string>(ref));
}
struct Include {
clang::SourceLocation hash;
clang::CharSourceRange name;
const clang::FileEntry *entry;
};
struct PreInclude {
Fact<Pp::Include> include;
clang::FileID file;
};
using PrePPEvent = boost::variant<Cxx::PPEvent, PreInclude>;
struct CrossRef {
Src::ByteSpan span;
bool local;
Cxx::XRefTarget target;
};
struct FileData {
clang::FileID id;
Fact<Src::File> fact;
std::vector<std::pair<Src::ByteSpan, Cxx::Declaration>> declarations;
std::vector<PrePPEvent> events;
std::vector<CrossRef> xrefs;
folly::Optional<Fact<Cxx::Trace>> trace;
};
struct SourceRange {
FileData *file;
Src::ByteSpan span;
Src::Range range;
};
void ppevent(
PrePPEvent event,
SourceRange range);
void include(
const Include& inc,
Fact<Src::File> file,
folly::Optional<clang::FileID> id);
void enterFile(clang::SourceLocation loc, folly::Optional<Include> inc);
void skipFile(folly::Optional<Include> inc, const clang::FileEntry *entry);
void declaration(const SourceRange& range, Cxx::Declaration decl) {
if (range.file) {
range.file->declarations.push_back({range.span, decl});
}
}
void xref(
clang::SourceRange range,
folly::Optional<clang::SourceLocation> loc,
Cxx::XRefTarget target);
// Locations
Src::Loc srcLoc(clang::SourceLocation loc);
/// Return the SourceRange of the token starting at the beginning of the range
/// if it isn't a macro expansion and the range itself otherwise.
clang::SourceRange rangeOfToken(clang::SourceRange range) const;
template<typename T>
ClangDB::SourceRange srcRange(T x) {
return immediateSrcRange(sourceManager().getExpansionRange(x));
}
SourceRange immediateSrcRange(clang::CharSourceRange r);
clang::SourceRange spellingRange(clang::SourceRange range) const;
clang::StringRef srcText(clang::SourceRange range) const;
void finish();
template<typename P, typename... Ts>
Fact<P> fact(Ts&&... xs) {
return batch.fact<P>(std::forward<Ts>(xs)...);
}
template<typename P, typename Key, typename Value>
Fact<P> factV(Key&& key, Value&& value) {
return batch.factV<P>(std::forward<Key>(key), std::forward<Value>(value));
}
clang::SourceManager& sourceManager() const {
return compilerInstance.getSourceManager();
}
private:
Fact<Buck::Locator> locator;
folly::Optional<Fact<Buck::Platform>> platform;
const std::filesystem::path root;
const folly::Optional<std::string> subdir;
const folly::Optional<std::string> path_prefix;
Batch<SCHEMA>& batch;
clang::CompilerInstance& compilerInstance;
struct HashFileID {
size_t operator()(clang::FileID id) const {
return folly::Hash()(id.getHashValue());
}
};
std::deque<FileData> file_data;
folly::F14FastMap<clang::FileID, FileData *, HashFileID> files;
/// returns the language processed by the compilerInstance
Src::Language getLanguage() const {
auto opts = compilerInstance.getLangOpts();
#if LLVM_VERSION_MAJOR >= 9
if (opts.ObjC) {
#else
// From
// https://our.internmc.facebook.com/intern/diffusion/OMEXTLLVMPROJECT/browse/toolchain%252Fdev/clang/lib/CodeGen/CGObjCGNU.cpp?lines=431
/// The version of the protocol class. Used to differentiate between ObjC1
/// and ObjC2 protocols. Objective-C 1 protocols can not contain optional
/// components and can not contain declared properties. We always emit
/// Objective-C 2 property structures, but we have to pretend that they're
/// Objective-C 1 property structures when targeting the GCC runtime or it
/// will abort.
if (opts.ObjC1 || opts.ObjC2) {
#endif
return opts.CPlusPlus ? Src::Language::ObjCpp : Src::Language::ObjC;
}
return opts.CPlusPlus ? Src::Language::Cpp : Src::Language::C;
}
};
}
}
}