From 3e22a2be2cda12b7fa434722d91101e200ee0aaa Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:45:37 +0200 Subject: [PATCH 1/7] ast-exporter: Add `macro_fns.c` snapshot test --- c2rust-transpile/tests/snapshots.rs | 5 +++ c2rust-transpile/tests/snapshots/macro_fns.c | 27 ++++++++++++++++ ...s__transpile@macro_fns.c.2021.clang15.snap | 30 ++++++++++++++++++ ...s__transpile@macro_fns.c.2024.clang15.snap | 31 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 c2rust-transpile/tests/snapshots/macro_fns.c create mode 100644 c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap create mode 100644 c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index 5b72ce1293..92bf7bf53d 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -440,6 +440,11 @@ fn test_lift_const() { transpile("lift_const.c").run(); } +#[test] +fn test_macro_fns() { + transpile("macro_fns.c").run(); +} + #[test] fn test_macrocase() { transpile("macrocase.c").run(); diff --git a/c2rust-transpile/tests/snapshots/macro_fns.c b/c2rust-transpile/tests/snapshots/macro_fns.c new file mode 100644 index 0000000000..e6a2a55284 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/macro_fns.c @@ -0,0 +1,27 @@ +#define CONST 42 + +#define ID(x) x +#define PAREN(x) (x) +#define LIT_ID ID(42) +#define LIT_PAREN PAREN(42) +#define CONST_ID ID(CONST) +#define CONST_PAREN PAREN(CONST) +#define NESTED_ID(x) ID(x) +#define NESTED_PAREN(x) PAREN(x) + +void basic(void) { + int lit_id = LIT_ID; + int lit_paren = LIT_PAREN; + int const_id = CONST_ID; + int const_paren = CONST_PAREN; + + int id_with_lit = ID(42); + int paren_with_lit = PAREN(42); + int id_with_const = ID(CONST); + int paren_with_const = PAREN(CONST); + + int nested_id_with_lit = NESTED_ID(42); + int nested_paren_with_lit = NESTED_PAREN(42); + int nested_id_with_const = NESTED_ID(CONST); + int nested_paren_with_const = NESTED_PAREN(CONST); +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap new file mode 100644 index 0000000000..98e356dd6b --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap @@ -0,0 +1,30 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/macro_fns.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +#[no_mangle] +pub unsafe extern "C" fn basic() { + let mut lit_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; + let mut const_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut const_paren: ::core::ffi::c_int = CONST_PAREN; + let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap new file mode 100644 index 0000000000..49b6ec07b2 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap @@ -0,0 +1,31 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/macro_fns.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] +pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +#[unsafe(no_mangle)] +pub unsafe extern "C" fn basic() { + let mut lit_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; + let mut const_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut const_paren: ::core::ffi::c_int = CONST_PAREN; + let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +} From 7ef8fa5b7595c1b33aabe95aee935039c66f3fd0 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 13 May 2026 20:33:08 +0200 Subject: [PATCH 2/7] ast-exporter: Skip over parameter expansions in `VisitExpr` --- c2rust-ast-exporter/src/AstExporter.cpp | 56 ++++++++++++------- ...s__transpile@macro_fns.c.2021.clang15.snap | 6 +- ...s__transpile@macro_fns.c.2024.clang15.snap | 6 +- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 0aa6ecb09a..0010680c1b 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -1439,17 +1439,21 @@ class TranslateASTVisitor final } curMacroExpansionSource = - Lexer::getSourceText(ExpansionStack[0], Mgr, Context->getLangOpts()); + Lexer::getSourceText(ExpansionStack[0].range, Mgr, Context->getLangOpts()); + + for (auto &elem : ExpansionStack) { + if (elem.isParameter) { + continue; + } - for (auto &ExpansionRange : ExpansionStack) { StringRef name; - MacroInfo *mac = getMacroInfo(ExpansionRange.getBegin(), name); + MacroInfo *mac = getMacroInfo(elem.range.getBegin(), name); if (!mac || mac->getNumTokens() == 0) { return true; } - if (VisitMacro(name, ExpansionRange.getBegin(), mac, E)) { + if (VisitMacro(name, elem.range.getBegin(), mac, E)) { curMacroExpansionStack.push_back(mac); } } @@ -1457,20 +1461,34 @@ class TranslateASTVisitor final return true; } - std::vector getMacroExpansionStack(SourceRange Range) const { + struct ExpansionRange { + CharSourceRange range; + bool isParameter; + + bool operator== (const ExpansionRange &rhs) const { + return this->range.getAsRange() == rhs.range.getAsRange() + && this->range.isTokenRange() == rhs.range.isTokenRange() + && this->isParameter == rhs.isParameter; + } + }; + + std::vector getMacroExpansionStack(SourceRange Range) const { auto &Mgr = Context->getSourceManager(); auto Begin = Range.getBegin(); auto End = Range.getEnd(); - std::vector ExpansionStack; + std::vector ExpansionStack; do { if (!isAtStartOfImmediateMacroExpansion(Begin)) { break; } - auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin); - ExpansionStack.push_back(ExpansionRange); - Begin = ExpansionRange.getBegin(); + ExpansionRange elem = { + Mgr.getImmediateExpansionRange(Begin), + Mgr.isMacroArgExpansion(Begin) + }; + Begin = elem.range.getBegin(); + ExpansionStack.push_back(elem); } while (Begin.isMacroID()); // Find the point at which `Begin` and `End` converge on the same expansion range. @@ -1482,16 +1500,12 @@ class TranslateASTVisitor final break; } - auto ExpansionRange = Mgr.getImmediateExpansionRange(End); - ConvergencePoint = std::find_if( - ExpansionStack.begin(), - ExpansionStack.end(), - [ExpansionRange](auto &R) { - return R.getAsRange() == ExpansionRange.getAsRange() && - R.isTokenRange() == ExpansionRange.isTokenRange(); - } - ); - End = ExpansionRange.getEnd(); + ExpansionRange elem = { + Mgr.getImmediateExpansionRange(End), + Mgr.isMacroArgExpansion(End) + }; + End = elem.range.getEnd(); + ConvergencePoint = std::find(ExpansionStack.begin(), ExpansionStack.end(), elem); } while (End.isMacroID() && ConvergencePoint == ExpansionStack.end()); // Remove all elements before the convergence point. @@ -1501,8 +1515,8 @@ class TranslateASTVisitor final auto EraseAfter = std::find_if( ExpansionStack.begin(), ExpansionStack.end(), - [this](auto &R) { - auto End = R.getEnd(); + [this](auto &elem) { + auto End = elem.range.getEnd(); return End.isMacroID() && !isAtEndOfImmediateMacroExpansion(End); } ); diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap index 98e356dd6b..b4120ee024 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap @@ -11,13 +11,15 @@ expression: cat tests/snapshots/macro_fns.2021.clang15.rs unused_assignments, unused_mut )] +pub const LIT_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; #[no_mangle] pub unsafe extern "C" fn basic() { - let mut lit_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut lit_id: ::core::ffi::c_int = LIT_ID; let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; - let mut const_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut const_id: ::core::ffi::c_int = CONST_ID; let mut const_paren: ::core::ffi::c_int = CONST_PAREN; let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap index 49b6ec07b2..e1ed38afc8 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap @@ -12,13 +12,15 @@ expression: cat tests/snapshots/macro_fns.2024.clang15.rs unused_assignments, unused_mut )] +pub const LIT_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; #[unsafe(no_mangle)] pub unsafe extern "C" fn basic() { - let mut lit_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut lit_id: ::core::ffi::c_int = LIT_ID; let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; - let mut const_id: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut const_id: ::core::ffi::c_int = CONST_ID; let mut const_paren: ::core::ffi::c_int = CONST_PAREN; let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; From 05f86c9e5ecabb16653d55262e3a86c103abe979 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:20:06 +0200 Subject: [PATCH 3/7] ast-exporter: Move `getMacroInfo` closer to the code that calls it --- c2rust-ast-exporter/src/AstExporter.cpp | 68 ++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 0010680c1b..589daf4d88 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -903,40 +903,6 @@ class TranslateASTVisitor final isVaList(ast, T), encodeMacroExpansions, childIds, extra); } - MacroInfo* getMacroInfo(SourceLocation loc, StringRef &name) const { - auto &Mgr = Context->getSourceManager(); - Token Result; - if (!Lexer::getRawToken(Mgr.getSpellingLoc(loc), Result, - Mgr, Context->getLangOpts(), false)) { - if (Result.is(tok::raw_identifier)) { - PP.LookUpIdentifierInfo(Result); - } - IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); - if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { - std::pair DecLoc = - Mgr.getDecomposedExpansionLoc(loc); - // Get the definition just before the searched location - // so that a macro referenced in a '#undef MACRO' can - // still be found. - SourceLocation BeforeSearchedLocation = - Mgr.getMacroArgExpandedLocation( - Mgr.getLocForStartOfFile(DecLoc.first) - .getLocWithOffset(DecLoc.second - 1)); - MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( - IdentifierInfo, BeforeSearchedLocation); - MacroInfo *MacroInf = MacroDef.getMacroInfo(); - if (MacroInf) { - LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); - LLVM_DEBUG(MacroInf->dump()); - LLVM_DEBUG(dbgs() << "\n"); - name = IdentifierInfo->getName(); - return MacroInf; - } - } - } - return nullptr; - } - bool VisitMacro(StringRef name, SourceLocation loc, MacroInfo *mac, Expr *E) { // TODO: handle builtin macros if (mac->isBuiltinMacro()) @@ -1541,6 +1507,40 @@ class TranslateASTVisitor final return Mgr.isAtEndOfImmediateMacroExpansion(loc.getLocWithOffset(len)); } + MacroInfo* getMacroInfo(SourceLocation loc, StringRef &name) const { + auto &Mgr = Context->getSourceManager(); + Token Result; + if (!Lexer::getRawToken(Mgr.getSpellingLoc(loc), Result, + Mgr, Context->getLangOpts(), false)) { + if (Result.is(tok::raw_identifier)) { + PP.LookUpIdentifierInfo(Result); + } + IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); + if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { + std::pair DecLoc = + Mgr.getDecomposedExpansionLoc(loc); + // Get the definition just before the searched location + // so that a macro referenced in a '#undef MACRO' can + // still be found. + SourceLocation BeforeSearchedLocation = + Mgr.getMacroArgExpandedLocation( + Mgr.getLocForStartOfFile(DecLoc.first) + .getLocWithOffset(DecLoc.second - 1)); + MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( + IdentifierInfo, BeforeSearchedLocation); + MacroInfo *MacroInf = MacroDef.getMacroInfo(); + if (MacroInf) { + LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); + LLVM_DEBUG(MacroInf->dump()); + LLVM_DEBUG(dbgs() << "\n"); + name = IdentifierInfo->getName(); + return MacroInf; + } + } + } + return nullptr; + } + bool VisitVAArgExpr(VAArgExpr *E) { std::vector childIds{E->getSubExpr()}; encode_entry(E, TagVAArgExpr, childIds); From 1a272639561cf5a4fb28f4d1531fb502d0be1206 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:20:20 +0200 Subject: [PATCH 4/7] ast-exporter: Split off `getIdentifierInfo` --- c2rust-ast-exporter/src/AstExporter.cpp | 80 +++++++++++++++---------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 589daf4d88..d352a10e24 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -1412,15 +1412,21 @@ class TranslateASTVisitor final continue; } - StringRef name; - MacroInfo *mac = getMacroInfo(elem.range.getBegin(), name); + auto loc = elem.range.getBegin(); + auto IdentifierInfo = getIdentifierInfo(loc); - if (!mac || mac->getNumTokens() == 0) { + if (!IdentifierInfo) { return true; } - if (VisitMacro(name, elem.range.getBegin(), mac, E)) { - curMacroExpansionStack.push_back(mac); + auto MacroInfo = getMacroInfo(loc, IdentifierInfo); + + if (!MacroInfo || MacroInfo->getNumTokens() == 0) { + return true; + } + + if (VisitMacro(IdentifierInfo->getName(), loc, MacroInfo, E)) { + curMacroExpansionStack.push_back(MacroInfo); } } @@ -1507,40 +1513,54 @@ class TranslateASTVisitor final return Mgr.isAtEndOfImmediateMacroExpansion(loc.getLocWithOffset(len)); } - MacroInfo* getMacroInfo(SourceLocation loc, StringRef &name) const { + IdentifierInfo *getIdentifierInfo(SourceLocation loc) const { auto &Mgr = Context->getSourceManager(); Token Result; - if (!Lexer::getRawToken(Mgr.getSpellingLoc(loc), Result, - Mgr, Context->getLangOpts(), false)) { + + if (!Lexer::getRawToken( + Mgr.getSpellingLoc(loc), + Result, + Mgr, + Context->getLangOpts(), + false + )) { if (Result.is(tok::raw_identifier)) { PP.LookUpIdentifierInfo(Result); } - IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); - if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { - std::pair DecLoc = - Mgr.getDecomposedExpansionLoc(loc); - // Get the definition just before the searched location - // so that a macro referenced in a '#undef MACRO' can - // still be found. - SourceLocation BeforeSearchedLocation = - Mgr.getMacroArgExpandedLocation( - Mgr.getLocForStartOfFile(DecLoc.first) - .getLocWithOffset(DecLoc.second - 1)); - MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( - IdentifierInfo, BeforeSearchedLocation); - MacroInfo *MacroInf = MacroDef.getMacroInfo(); - if (MacroInf) { - LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); - LLVM_DEBUG(MacroInf->dump()); - LLVM_DEBUG(dbgs() << "\n"); - name = IdentifierInfo->getName(); - return MacroInf; - } - } + + return Result.getIdentifierInfo(); } + return nullptr; } + MacroInfo* getMacroInfo(SourceLocation loc, IdentifierInfo *IdentifierInfo) const { + if (!IdentifierInfo->hadMacroDefinition()) { + return nullptr; + } + + auto &Mgr = Context->getSourceManager(); + std::pair DecLoc = Mgr.getDecomposedExpansionLoc(loc); + // Get the definition just before the searched location + // so that a macro referenced in a '#undef MACRO' can + // still be found. + SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( + Mgr.getLocForStartOfFile(DecLoc.first).getLocWithOffset(DecLoc.second - 1)); + MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( + IdentifierInfo, BeforeSearchedLocation); + MacroInfo *MacroInf = MacroDef.getMacroInfo(); + + if (!MacroInf) { + return nullptr; + } + + LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); + LLVM_DEBUG(MacroInf->dump()); + LLVM_DEBUG(dbgs() << "\n"); + + return MacroInf; + } + bool VisitVAArgExpr(VAArgExpr *E) { std::vector childIds{E->getSubExpr()}; encode_entry(E, TagVAArgExpr, childIds); From 9b797bfd01db99814b36c4d5d7c20e770c1a62ff Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:21:35 +0200 Subject: [PATCH 5/7] ast-exporter: Rename "expansion" to "invocation" --- c2rust-ast-exporter/src/AstExporter.cpp | 54 ++++++++++++------------ c2rust-ast-exporter/src/clang_ast.rs | 12 +++--- c2rust-transpile/src/c_ast/conversion.rs | 4 +- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index d352a10e24..0c44f37f2c 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -711,7 +711,7 @@ class TypeEncoder final : public TypeVisitor { class TranslateASTVisitor final : public RecursiveASTVisitor { - struct MacroExpansionInfo { + struct MacroDeclInfo { StringRef Name; }; @@ -723,13 +723,13 @@ class TranslateASTVisitor final // Mapping from SourceManager FileID to index in files DenseMap file_id_mapping; std::set> exportedTags; - std::unordered_map macros; + std::unordered_map macros; // This stores a raw encoding of the macro call site SourceLocation, since // SourceLocation isn't hashable. std::unordered_set macroCallSites; - SmallVector curMacroExpansionStack; - StringRef curMacroExpansionSource; + SmallVector curMacroInvocationStack; + StringRef curMacroInvocationSource; // Returns true when a new entry is added to exportedTags bool markForExport(void *ptr, ASTEntryTag tag) { @@ -769,7 +769,7 @@ class TranslateASTVisitor final // Template required because Decl and Stmt don't share a common base class void encode_entry_raw(void *ast, ASTEntryTag tag, SourceRange loc, const QualType ty, bool rvalue, - bool isVaList, bool encodeMacroExpansions, + bool isVaList, bool encodeMacroInvocations, const std::vector &childIds, std::function extra) { if (!markForExport(ast, tag)) @@ -808,21 +808,21 @@ class TranslateASTVisitor final // 9 - Is Rvalue (only for expressions) cbor_encode_boolean(&local, rvalue); - // 10 - Macro expansion stack, starting with initial macro call and ending + // 10 - Macro invocation stack, starting with initial macro call and ending // with the innermost replacement. cbor_encoder_create_array(&local, &childEnc, - encodeMacroExpansions ? curMacroExpansionStack.size() : 0); - if (encodeMacroExpansions) { - for (auto I = curMacroExpansionStack.rbegin(), E = curMacroExpansionStack.rend(); + encodeMacroInvocations ? curMacroInvocationStack.size() : 0); + if (encodeMacroInvocations) { + for (auto I = curMacroInvocationStack.rbegin(), E = curMacroInvocationStack.rend(); I != E; ++I) { cbor_encode_uint(&childEnc, uintptr_t(*I)); } } cbor_encoder_close_container(&local, &childEnc); - // 11 - Macro expansion source string, if applicable. - if (!curMacroExpansionSource.empty()) { - cbor_encode_string(&local, curMacroExpansionSource.str()); + // 11 - Macro invocation source string, if applicable. + if (!curMacroInvocationSource.empty()) { + cbor_encode_string(&local, curMacroInvocationSource.str()); } else { cbor_encode_null(&local); } @@ -846,7 +846,7 @@ class TranslateASTVisitor final std::function extra = [](CborEncoder *) {}) { auto ty = ast->getType(); auto isVaList = false; - auto encodeMacroExpansions = true; + auto encodeMacroInvocations = true; #if CLANG_VERSION_MAJOR < 13 bool isRValue = ast->isRValue(); #else @@ -860,7 +860,7 @@ class TranslateASTVisitor final auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, ty, isRValue, isVaList, - encodeMacroExpansions, childIds, extra); + encodeMacroInvocations, childIds, extra); typeEncoder.VisitQualTypeOf(ty, ast); } @@ -870,11 +870,11 @@ class TranslateASTVisitor final QualType s = QualType(static_cast(nullptr), 0); auto rvalue = false; auto isVaList = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, s, rvalue, isVaList, - encodeMacroExpansions, childIds, extra); + encodeMacroInvocations, childIds, extra); } void encode_entry( @@ -882,11 +882,11 @@ class TranslateASTVisitor final const QualType T, std::function extra = [](CborEncoder *) {}) { auto rvalue = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, T, rvalue, - isVaList(ast, T), encodeMacroExpansions, childIds, extra); + isVaList(ast, T), encodeMacroInvocations, childIds, extra); } /// Explicitly override the source location of this decl for cases where the @@ -897,10 +897,10 @@ class TranslateASTVisitor final const std::vector &childIds, const QualType T, std::function extra = [](CborEncoder *) {}) { auto rvalue = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; expandSpanToFinalChar(loc, Context); encode_entry_raw(ast, tag, loc, T, rvalue, - isVaList(ast, T), encodeMacroExpansions, childIds, extra); + isVaList(ast, T), encodeMacroInvocations, childIds, extra); } bool VisitMacro(StringRef name, SourceLocation loc, MacroInfo *mac, Expr *E) { @@ -973,11 +973,11 @@ class TranslateASTVisitor final void encodeMacros() { // Sort macros by source location - std::vector> macro_vec( + std::vector> macro_vec( macros.begin(), macros.end()); std::sort(macro_vec.begin(), macro_vec.end(), - [](const std::pair &a, - const std::pair &b) { + [](const std::pair &a, + const std::pair &b) { return a.first->getDefinitionLoc() < b.first->getDefinitionLoc(); }); @@ -1369,8 +1369,8 @@ class TranslateASTVisitor final // bool VisitExpr(Expr *E) { - curMacroExpansionStack.clear(); - curMacroExpansionSource = StringRef(); + curMacroInvocationStack.clear(); + curMacroInvocationSource = StringRef(); // We only translate constant macro objects to Rust consts, so this // expression must be constant. @@ -1404,7 +1404,7 @@ class TranslateASTVisitor final return true; } - curMacroExpansionSource = + curMacroInvocationSource = Lexer::getSourceText(ExpansionStack[0].range, Mgr, Context->getLangOpts()); for (auto &elem : ExpansionStack) { @@ -1426,7 +1426,7 @@ class TranslateASTVisitor final } if (VisitMacro(IdentifierInfo->getName(), loc, MacroInfo, E)) { - curMacroExpansionStack.push_back(MacroInfo); + curMacroInvocationStack.push_back(MacroInfo); } } diff --git a/c2rust-ast-exporter/src/clang_ast.rs b/c2rust-ast-exporter/src/clang_ast.rs index bac54c9302..4d44e74baf 100644 --- a/c2rust-ast-exporter/src/clang_ast.rs +++ b/c2rust-ast-exporter/src/clang_ast.rs @@ -108,8 +108,8 @@ pub struct AstNode { // Stack of macros this node was expanded from, beginning with the initial // macro call and ending with the leaf. This needs to be a stack for nested // macro definitions. - pub macro_expansions: Vec, - pub macro_expansion_text: Option, + pub macro_invocations: Vec, + pub macro_invocation_text: Option, pub extras: Vec, } @@ -260,9 +260,9 @@ pub fn process(items: Value) -> error::Result { }; // entry[10] - let macro_expansions = from_value::>(entry.pop_front().unwrap()).unwrap(); + let macro_invocations = from_value::>(entry.pop_front().unwrap()).unwrap(); - let macro_expansion_text = expect_opt_str(&entry.pop_front().unwrap()) + let macro_invocation_text = expect_opt_str(&entry.pop_front().unwrap()) .unwrap() .map(|s| s.to_string()); @@ -278,8 +278,8 @@ pub fn process(items: Value) -> error::Result { }, type_id, rvalue, - macro_expansions, - macro_expansion_text, + macro_invocations, + macro_invocation_text, extras: entry.into_iter().collect(), }; diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index e6e7225491..52f7edabb6 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1069,7 +1069,7 @@ impl ConversionContext { }; if expected_ty & EXPR != 0 { - for mac_id in &node.macro_expansions { + for mac_id in &node.macro_invocations { let mac = CDeclId(self.visit_node_type(*mac_id, MACRO_DECL)); self.typed_context .macro_invocations @@ -1079,7 +1079,7 @@ impl ConversionContext { } } - if let Some(text) = &node.macro_expansion_text { + if let Some(text) = &node.macro_invocation_text { self.typed_context .macro_expansion_text .insert(CExprId(new_id), text.clone()); From e470201b74e90e76ce32a06515b65dd887fdf775 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:28:09 +0200 Subject: [PATCH 6/7] ast-exporter: Add parameter information to macro invocations --- c2rust-ast-exporter/src/AstExporter.cpp | 36 ++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 0c44f37f2c..36aa2d300e 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -725,10 +725,17 @@ class TranslateASTVisitor final std::set> exportedTags; std::unordered_map macros; + struct MacroInvocationInfo { + uint32_t key; + MacroInfo* macro; + StringRef parameter; + }; + // This stores a raw encoding of the macro call site SourceLocation, since // SourceLocation isn't hashable. - std::unordered_set macroCallSites; - SmallVector curMacroInvocationStack; + // TODO: Can this be made to use `unordered_set` for speed? + std::set> macroCallSites; + SmallVector curMacroInvocationStack; StringRef curMacroInvocationSource; // Returns true when a new entry is added to exportedTags @@ -815,7 +822,7 @@ class TranslateASTVisitor final if (encodeMacroInvocations) { for (auto I = curMacroInvocationStack.rbegin(), E = curMacroInvocationStack.rend(); I != E; ++I) { - cbor_encode_uint(&childEnc, uintptr_t(*I)); + cbor_encode_uint(&childEnc, uintptr_t(I->macro)); } } cbor_encoder_close_container(&local, &childEnc); @@ -903,14 +910,15 @@ class TranslateASTVisitor final isVaList(ast, T), encodeMacroInvocations, childIds, extra); } - bool VisitMacro(StringRef name, SourceLocation loc, MacroInfo *mac, Expr *E) { + bool VisitMacro(StringRef name, StringRef parameter, SourceLocation loc, MacroInfo *mac, Expr *E) { // TODO: handle builtin macros if (mac->isBuiltinMacro()) return false; // If this isn't the first time we've seen this macro call site, we // shouldn't associate this expression with the macro as it is a subexpr // of a previously seen expression. - if (!macroCallSites.insert(loc.getRawEncoding()).second) + std::pair key { loc.getRawEncoding(), parameter }; + if (!macroCallSites.insert(key).second) return false; auto &info = macros[mac]; if (info.Name.empty()) @@ -1408,11 +1416,20 @@ class TranslateASTVisitor final Lexer::getSourceText(ExpansionStack[0].range, Mgr, Context->getLangOpts()); for (auto &elem : ExpansionStack) { + auto loc = elem.range.getBegin(); + StringRef parameter; + if (elem.isParameter) { - continue; + auto IdentifierInfo = getIdentifierInfo(loc); + + if (!IdentifierInfo) { + return true; + } + + parameter = IdentifierInfo->getName(); + loc = Mgr.getImmediateExpansionRange(loc).getBegin(); } - auto loc = elem.range.getBegin(); auto IdentifierInfo = getIdentifierInfo(loc); if (!IdentifierInfo) { @@ -1425,8 +1442,9 @@ class TranslateASTVisitor final return true; } - if (VisitMacro(IdentifierInfo->getName(), loc, MacroInfo, E)) { - curMacroInvocationStack.push_back(MacroInfo); + if (VisitMacro(IdentifierInfo->getName(), parameter, loc, MacroInfo, E)) { + MacroInvocationInfo info = { loc.getRawEncoding(), MacroInfo, parameter }; + curMacroInvocationStack.push_back(info); } } From 025413b14af5ca4afd1a86be7a69cf9316b67bd7 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 11:36:54 +0200 Subject: [PATCH 7/7] ast-exporter: Export macro parameter information to Rust --- c2rust-ast-exporter/src/AstExporter.cpp | 14 +++++++++++++- c2rust-ast-exporter/src/clang_ast.rs | 13 +++++++++++-- c2rust-transpile/src/c_ast/conversion.rs | 4 ++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 36aa2d300e..a713327c1d 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -822,7 +822,19 @@ class TranslateASTVisitor final if (encodeMacroInvocations) { for (auto I = curMacroInvocationStack.rbegin(), E = curMacroInvocationStack.rend(); I != E; ++I) { - cbor_encode_uint(&childEnc, uintptr_t(I->macro)); + CborEncoder expansionEnc; + cbor_encoder_create_array(&childEnc, &expansionEnc, 3); + + cbor_encode_uint(&expansionEnc, I->key); + cbor_encode_uint(&expansionEnc, uintptr_t(I->macro)); + + if (!I->parameter.empty()) { + cbor_encode_string(&expansionEnc, I->parameter.str()); + } else { + cbor_encode_null(&expansionEnc); + } + + cbor_encoder_close_container(&childEnc, &expansionEnc); } } cbor_encoder_close_container(&local, &childEnc); diff --git a/c2rust-ast-exporter/src/clang_ast.rs b/c2rust-ast-exporter/src/clang_ast.rs index 4d44e74baf..a96ee1c5cc 100644 --- a/c2rust-ast-exporter/src/clang_ast.rs +++ b/c2rust-ast-exporter/src/clang_ast.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use serde_bytes::ByteBuf; use serde_cbor::error; use std::collections::{HashMap, VecDeque}; @@ -108,11 +109,18 @@ pub struct AstNode { // Stack of macros this node was expanded from, beginning with the initial // macro call and ending with the leaf. This needs to be a stack for nested // macro definitions. - pub macro_invocations: Vec, + pub macro_invocations: Vec, pub macro_invocation_text: Option, pub extras: Vec, } +#[derive(Debug, Clone, Deserialize)] +pub struct MacroInvocationInfoRaw { + pub key: u32, + pub macro_id: u64, + pub parameter: Option, +} + #[derive(Debug, Clone)] pub struct TypeNode { pub tag: TypeTag, @@ -260,7 +268,8 @@ pub fn process(items: Value) -> error::Result { }; // entry[10] - let macro_invocations = from_value::>(entry.pop_front().unwrap()).unwrap(); + let macro_invocations = + from_value::>(entry.pop_front().unwrap()).unwrap(); let macro_invocation_text = expect_opt_str(&entry.pop_front().unwrap()) .unwrap() diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 52f7edabb6..37e2fcf76a 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1069,8 +1069,8 @@ impl ConversionContext { }; if expected_ty & EXPR != 0 { - for mac_id in &node.macro_invocations { - let mac = CDeclId(self.visit_node_type(*mac_id, MACRO_DECL)); + for info in &node.macro_invocations { + let mac = CDeclId(self.visit_node_type(info.macro_id, MACRO_DECL)); self.typed_context .macro_invocations .entry(CExprId(new_id))