Skip to content

Commit 62e5c19

Browse files
committed
[cc] preproc bug fix
1 parent 23b1d6a commit 62e5c19

File tree

2 files changed

+79
-11
lines changed

2 files changed

+79
-11
lines changed

cc/README.md

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,6 @@ Not yet implemented (features we want to add):
9191
- C11 Thread-Local Storage (_Thread_local) and atomics (_Atomic)
9292
- Other C11 features: _Static_assert, _Generic, anonymous structs
9393

94-
## Known Issues
95-
96-
### Preprocessor
97-
98-
- **Chained macro expansion after token paste**: When a token paste (`##`) creates an identifier that is itself a function-like macro, the subsequent macro expansion may not work correctly. For example:
99-
```c
100-
#define BAR_test(y) y
101-
#define FOO(x) BAR_ ## x(1)
102-
FOO(test) // Should expand to 1, but may produce incorrect output
103-
```
104-
10594
## Code Quality
10695

10796
Please run `cargo fmt` before committing code, and `cargo clippy` regularly while working. Code should build without warnings.

cc/token/preprocess.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,65 @@ impl<'a> Preprocessor<'a> {
617617
self.try_expand_macro(&name, &token.pos, &mut iter, idents)
618618
{
619619
output.extend(expanded);
620+
621+
// Check if the last expanded token is a function-like macro
622+
// followed by '(' in the remaining input stream.
623+
// This handles cases like: CALL(ADD)(10, 32) where token paste
624+
// creates a function-like macro name and args come from outside.
625+
while let Some(last) = output.last() {
626+
if last.typ != TokenType::Ident {
627+
break;
628+
}
629+
let TokenValue::Ident(id) = &last.value else {
630+
break;
631+
};
632+
let Some(macro_name) = idents.get_opt(*id) else {
633+
break;
634+
};
635+
let macro_name = macro_name.to_string();
636+
637+
// Check if it's a function-like macro
638+
let Some(mac) = self.macros.get(&macro_name) else {
639+
break;
640+
};
641+
if !mac.is_function {
642+
break;
643+
}
644+
645+
// Check if next token is '('
646+
let Some(next) = iter.peek() else { break };
647+
let TokenValue::Special(code) = &next.value else {
648+
break;
649+
};
650+
if *code != b'(' as u32 {
651+
break;
652+
}
653+
654+
// Check for recursion
655+
if self.expanding.contains(&macro_name) {
656+
break;
657+
}
658+
659+
// Pop the identifier and expand as function-like macro
660+
let last_token = output.pop().unwrap();
661+
iter.next(); // consume '('
662+
let args = self.collect_macro_args(&mut iter, idents);
663+
let mac = mac.clone();
664+
if let Some(more_expanded) = self.expand_function_macro(
665+
&mac,
666+
&args,
667+
&last_token.pos,
668+
idents,
669+
) {
670+
output.extend(more_expanded);
671+
// Loop to handle chained expansions
672+
} else {
673+
// Put back the identifier if expansion failed
674+
output.push(last_token);
675+
break;
676+
}
677+
}
678+
620679
continue;
621680
}
622681
}
@@ -3532,4 +3591,24 @@ PASTE(foo, bar)
35323591
assert!(strs.contains(&"b".to_string()));
35333592
assert!(strs.contains(&"c".to_string()));
35343593
}
3594+
3595+
#[test]
3596+
fn test_chained_paste_expansion() {
3597+
// Token paste creates function-like macro name, arguments come from outside.
3598+
// This tests the case: CALL(ADD)(10, 32) where ## creates ADD_func,
3599+
// and then (10, 32) from outside should trigger ADD_func expansion.
3600+
let (tokens, idents) = preprocess_str(
3601+
"#define ADD_func(x, y) ((x) + (y))\n\
3602+
#define CONCAT(a, b) a ## b\n\
3603+
#define CALL(name) CONCAT(name, _func)\n\
3604+
CALL(ADD)(10, 32)",
3605+
);
3606+
let strs = get_token_strings(&tokens, &idents);
3607+
// Should expand to ((10) + (32))
3608+
assert!(strs.contains(&"10".to_string()));
3609+
assert!(strs.contains(&"32".to_string()));
3610+
assert!(strs.contains(&"+".to_string()));
3611+
// Should NOT contain ADD_func as unexpanded identifier
3612+
assert!(!strs.contains(&"ADD_func".to_string()));
3613+
}
35353614
}

0 commit comments

Comments
 (0)