Program Listing for File parsing.cpp
↰ Return to documentation for file (src/da4cpp/parsing.cpp)
#include <clang-c/CXFile.h>
#include <clang-c/CXSourceLocation.h>
#include <clang-c/CXString.h>
#include <clang-c/Index.h>
#include <da4cpp/graph.hpp>
#include <da4cpp/parsing.hpp>
#include <filesystem>
#include <iostream>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
using da4cpp::graph::Dependencies;
using da4cpp::graph::DependencyGraph;
using da4cpp::graph::Symbol;
using da4cpp::graph::SymbolType;
using SymbolAndDependencies = std::pair<Symbol, Dependencies>;
namespace {
using ProcessingCondition = bool (*)(const CXCursor &);
using CursorProcessor = SymbolAndDependencies (*)(const CXCursor &);
using CursorHandler = std::tuple<ProcessingCondition, CursorProcessor>;
using CursorHandlers = std::vector<CursorHandler>;
std::string to_string(CXString cxStr) {
std::string str = clang_getCString(cxStr);
clang_disposeString(cxStr);
return str;
}
std::string get_symbol_name(const CXCursor &cursor) { return to_string(clang_getCursorSpelling(cursor)); }
Symbol from_cursor_and_name(const CXCursor &cursor, const std::string &symbolName, SymbolType symbolType) {
const CXSourceLocation location = clang_getCursorLocation(cursor);
CXFile file = nullptr;
unsigned line = 0;
unsigned column = 0;
unsigned offset = 0;
clang_getFileLocation(location, &file, &line, &column, &offset);
return {.filePath = std::filesystem::path(to_string(clang_getFileName(file))),
.line = line,
.column = column,
.name = symbolName,
.symbolType = symbolType};
}
Symbol from_cursor(const CXCursor &cursor, SymbolType symbolType) {
return from_cursor_and_name(cursor, get_symbol_name(cursor), symbolType);
}
Symbol function_declaration_from_cursor(const CXCursor &cursor) {
return from_cursor(cursor, SymbolType::FunctionDeclaration);
}
Symbol function_definition_from_cursor(const CXCursor &cursor) {
return from_cursor(cursor, SymbolType::FunctionDefinition);
}
bool is_struct_definition(const CXCursor &cursor) {
return clang_getCursorKind(cursor) == CXCursor_StructDecl && static_cast<bool>(clang_isCursorDefinition(cursor));
}
bool is_struct_declaration(const CXCursor &cursor) {
return clang_getCursorKind(cursor) == CXCursor_StructDecl && !static_cast<bool>(clang_isCursorDefinition(cursor));
}
Symbol type_declaration_from_cursor(const CXCursor &cursor) {
const bool isDefinition{static_cast<bool>(clang_isCursorDefinition(cursor))};
const SymbolType type = isDefinition ? SymbolType::TypeDefinition : SymbolType::TypeDeclaration;
return from_cursor(cursor, type);
}
Symbol type_definition_from_cursor(const CXCursor &cursor) { return from_cursor(cursor, SymbolType::TypeDefinition); }
SymbolAndDependencies process_struct_declaration(const CXCursor &cursor) {
const Symbol definition = type_declaration_from_cursor(cursor);
const Dependencies dependencies{};
return {definition, dependencies};
}
bool is_struct_reference(const CXCursor &cursor) {
const CXCursorKind cursorKind = clang_getCursorKind(cursor);
return (cursorKind == CXCursor_DeclRefExpr || cursorKind == CXCursor_TypeRef
|| cursorKind == CXCursor_MemberRefExpr)
&& clang_getCursorKind(clang_getCursorReferenced(cursor)) == CXCursor_StructDecl;
}
// NOLINTNEXTLINE(misc-unused-parameters,bugprone-easily-swappable-parameters)
CXChildVisitResult visit_struct_definition(CXCursor cursor, [[maybe_unused]] CXCursor parent,
CXClientData clientData) {
auto &dependencies = *static_cast<Dependencies *>(clientData);
if (is_struct_reference(cursor)) {
const Symbol referenced{type_declaration_from_cursor(clang_getCursorReferenced(cursor))};
dependencies.emplace(referenced);
}
return CXChildVisit_Recurse;
}
SymbolAndDependencies process_struct_definition(const CXCursor &cursor) {
const Symbol definition = type_definition_from_cursor(cursor);
Dependencies dependencies{};
clang_visitChildren(cursor, visit_struct_definition, &dependencies);
return {definition, dependencies};
}
bool is_function_declaration(const CXCursor &cursor) {
return clang_getCursorKind(cursor) == CXCursor_FunctionDecl && !static_cast<bool>(clang_isCursorDefinition(cursor));
}
SymbolAndDependencies process_function_declaration(const CXCursor &cursor) {
const Symbol declaration{function_declaration_from_cursor(cursor)};
const Dependencies dependencies{};
return {declaration, dependencies};
}
bool is_function_reference(const CXCursor &cursor) {
const CXCursorKind cursorKind = clang_getCursorKind(cursor);
const CXCursor referenced = clang_getCursorReferenced(cursor);
return (cursorKind == CXCursor_DeclRefExpr || cursorKind == CXCursor_CallExpr)
&& (clang_Cursor_isNull(referenced) == 0) && clang_getCursorKind(referenced) == CXCursor_FunctionDecl;
}
bool is_function_definition(const CXCursor &cursor) {
return clang_getCursorKind(cursor) == CXCursor_FunctionDecl && static_cast<bool>(clang_isCursorDefinition(cursor));
}
// NOLINTNEXTLINE(misc-unused-parameters,bugprone-easily-swappable-parameters)
CXChildVisitResult visit_function_definition(CXCursor cursor, [[maybe_unused]] CXCursor parent,
CXClientData clientData) {
auto &dependencies = *static_cast<Dependencies *>(clientData);
const CXCursor referenced = clang_getCursorReferenced(cursor);
if (is_function_reference(cursor)) {
if (is_function_definition(referenced)) {
dependencies.emplace(function_definition_from_cursor(referenced));
}
} else if (is_struct_reference(cursor)) {
dependencies.emplace(type_definition_from_cursor(referenced));
}
return CXChildVisit_Recurse;
}
SymbolAndDependencies process_function_definition(const CXCursor &cursor) {
const Symbol definition = function_definition_from_cursor(cursor);
Dependencies dependencies{};
clang_visitChildren(cursor, visit_function_definition, &dependencies);
return {definition, dependencies};
}
// NOLINTNEXTLINE(misc-unused-parameters,bugprone-easily-swappable-parameters)
CXChildVisitResult visit_node(CXCursor cursor, [[maybe_unused]] CXCursor parent, CXClientData clientData) {
const CursorHandlers handlers{{{is_function_definition, process_function_definition},
{is_function_declaration, process_function_declaration},
{is_struct_declaration, process_struct_declaration},
{is_struct_definition, process_struct_definition}}};
auto *graph = static_cast<DependencyGraph *>(clientData);
for (auto [condition, processor] : handlers) {
if (condition(cursor)) {
graph->emplace(processor(cursor));
break;
}
}
return CXChildVisit_Recurse;
}
} // namespace
namespace da4cpp::parsing {
std::optional<graph::DependencyGraph> parse_translation_unit(const std::filesystem::path &path) {
CXIndex index = clang_createIndex(0, 0);
CXTranslationUnit unit
= clang_parseTranslationUnit(index, path.c_str(), nullptr, 0, nullptr, 0, CXTranslationUnit_None);
if (unit == nullptr) {
std::cerr << "Unable to parse translation unit!" << std::endl;
return {};
}
graph::DependencyGraph graph{};
const CXCursor cursor = clang_getTranslationUnitCursor(unit);
clang_visitChildren(cursor, visit_node, &graph);
clang_disposeTranslationUnit(unit);
clang_disposeIndex(index);
return graph;
}
} // namespace da4cpp::parsing