Skip to content

Commit c5434a8

Browse files
committed
Add ast_canopy macro define extraction
1 parent c7820d8 commit c5434a8

8 files changed

Lines changed: 110 additions & 1 deletion

File tree

ast_canopy/ast_canopy/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class Declarations:
5757
class_template_specializations: list[ClassTemplateSpecialization]
5858
typedefs: list[bindings.Typedef]
5959
enums: list[bindings.Enum]
60+
macro_defines: dict[str, str]
6061

6162

6263
def paths_to_include_flags(paths: list[str]) -> list[str]:
@@ -543,6 +544,7 @@ def parse_declarations_from_source(
543544
class_template_specializations,
544545
decls.typedefs,
545546
decls.enums,
547+
decls.macro_defines,
546548
)
547549

548550

ast_canopy/ast_canopy/pylibastcanopy.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ PYBIND11_MODULE(pylibastcanopy, m) {
303303
.def_readwrite("class_template_specializations",
304304
&Declarations::class_template_specializations)
305305
.def_readwrite("typedefs", &Declarations::typedefs)
306-
.def_readwrite("enums", &Declarations::enums);
306+
.def_readwrite("enums", &Declarations::enums)
307+
.def_readwrite("macro_defines", &Declarations::macro_defines);
307308

308309
m.def("parse_declarations_from_command_line",
309310
&parse_declarations_from_command_line,

ast_canopy/ast_canopy/pylibastcanopy.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Declarations:
3030
enums: list[Enum]
3131
function_templates: list[FunctionTemplate]
3232
functions: list[Function]
33+
macro_defines: dict[str, str]
3334
records: list[Record]
3435
typedefs: list[Typedef]
3536
def __init__(self, *args, **kwargs) -> None: ...

ast_canopy/cpp/include/ast_canopy/ast_canopy.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ struct Declarations {
270270
std::vector<ClassTemplateSpecialization> class_template_specializations;
271271
std::vector<Typedef> typedefs;
272272
std::vector<Enum> enums;
273+
std::unordered_map<std::string, std::string> macro_defines;
273274
};
274275

275276
Declarations

ast_canopy/cpp/src/ast_canopy.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
#include <clang/Frontend/ASTUnit.h>
1010
#include <clang/Frontend/CompilerInstance.h>
1111
#include <clang/Frontend/TextDiagnosticPrinter.h>
12+
#include <clang/Lex/MacroInfo.h>
13+
#include <clang/Lex/Preprocessor.h>
1214

15+
#include <algorithm>
1316
#include <filesystem>
1417
#include <utility>
1518

@@ -66,6 +69,69 @@ class AstCanopyDiagnosticsConsumer : public DiagnosticConsumer {
6669
}
6770
};
6871

72+
bool filename_is_retained(const std::string &file_name,
73+
const std::vector<std::string> &files_to_retain) {
74+
return std::any_of(files_to_retain.begin(), files_to_retain.end(),
75+
[&file_name](const std::string &file_to_retain) {
76+
return file_name == file_to_retain;
77+
});
78+
}
79+
80+
std::string replacement_text_from_macro_info(const MacroInfo &macro_info,
81+
const Preprocessor &preprocessor) {
82+
std::string replacement_text;
83+
for (const Token &token : macro_info.tokens()) {
84+
bool invalid = false;
85+
std::string spelling = preprocessor.getSpelling(token, &invalid);
86+
if (invalid) {
87+
continue;
88+
}
89+
90+
if (!replacement_text.empty()) {
91+
replacement_text += " ";
92+
}
93+
replacement_text += spelling;
94+
}
95+
return replacement_text;
96+
}
97+
98+
void collect_macro_defines_from_ast(
99+
ASTUnit *ast, const std::vector<std::string> &files_to_retain,
100+
Declarations *decls) {
101+
decls->macro_defines.clear();
102+
103+
Preprocessor &preprocessor = ast->getPreprocessor();
104+
const SourceManager &source_manager = ast->getSourceManager();
105+
106+
for (const auto &entry : preprocessor.getIdentifierTable()) {
107+
const IdentifierInfo *identifier_info = entry.second;
108+
if (!identifier_info) {
109+
continue;
110+
}
111+
112+
MacroDefinition macro_definition =
113+
preprocessor.getMacroDefinition(identifier_info);
114+
const MacroInfo *macro_info = macro_definition.getMacroInfo();
115+
if (!macro_info || macro_info->isFunctionLike()) {
116+
continue;
117+
}
118+
119+
SourceLocation spelling_location =
120+
source_manager.getSpellingLoc(macro_info->getDefinitionLoc());
121+
if (!spelling_location.isValid()) {
122+
continue;
123+
}
124+
125+
std::string file_name = source_manager.getFilename(spelling_location).str();
126+
if (!filename_is_retained(file_name, files_to_retain)) {
127+
continue;
128+
}
129+
130+
decls->macro_defines[identifier_info->getName().str()] =
131+
replacement_text_from_macro_info(*macro_info, preprocessor);
132+
}
133+
}
134+
69135
/**
70136
* @brief Return the source filename of the declaration.
71137
*/
@@ -215,6 +281,7 @@ parse_declarations_from_command_line(std::vector<std::string> options,
215281
&ctsd_callback);
216282

217283
finder.matchAST(ast->getASTContext());
284+
detail::collect_macro_defines_from_ast(ast.get(), files_to_retain, &decls);
218285

219286
#ifndef NDEBUG
220287
std::cout << "Records: " << decls.records.size() << std::endl;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "sample_macro_defines_include.cuh"
2+
3+
#define foo 123
4+
#define FLAG
5+
#define MAKE_VALUE(x) ((x) + 1)
6+
7+
__device__ int use_macro_define() { return foo; }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#define INCLUDED_VALUE 456

ast_canopy/tests/test_parse_macro.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,32 @@ def test_macro_expansions(sample_macro_source):
3232
assert forty_two_int.return_type.name == "int"
3333
assert forty_two_float.return_type.name == "float"
3434
assert forty_two_double.return_type.name == "double"
35+
36+
37+
def test_macro_defines(data_folder):
38+
srcstr = str(data_folder / "sample_macro_defines.cu")
39+
40+
decls = parse_declarations_from_source(
41+
srcstr,
42+
[srcstr],
43+
"sm_80",
44+
)
45+
46+
assert decls.macro_defines["foo"] == "123"
47+
assert decls.macro_defines["FLAG"] == ""
48+
assert "MAKE_VALUE" not in decls.macro_defines
49+
assert "INCLUDED_VALUE" not in decls.macro_defines
50+
51+
52+
def test_macro_defines_retained_include(data_folder):
53+
srcstr = str(data_folder / "sample_macro_defines.cu")
54+
include = str(data_folder / "sample_macro_defines_include.cuh")
55+
56+
decls = parse_declarations_from_source(
57+
srcstr,
58+
[srcstr, include],
59+
"sm_80",
60+
)
61+
62+
assert decls.macro_defines["foo"] == "123"
63+
assert decls.macro_defines["INCLUDED_VALUE"] == "456"

0 commit comments

Comments
 (0)