diff --git a/src/translate_c.zig b/src/translate_c.zig index 41b87ade21..b0fae81475 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -439,6 +439,24 @@ pub fn translate( return ast.render(gpa, context.global_scope.nodes.items); } +/// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens) +/// Macros of this form will not be translated. +fn isSelfDefinedMacro(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) bool { + const source = getMacroText(unit, c, macro); + var tokenizer = std.c.Tokenizer{ + .buffer = source, + }; + const name_tok = tokenizer.next(); + const name = source[name_tok.start..name_tok.end]; + + const first_tok = tokenizer.next(); + // We do not just check for `.Identifier` below because keyword tokens are preferentially matched first by + // the tokenizer. + // In other words we would miss `#define inline inline` (`inline` is a valid c89 identifier) + if (first_tok.id == .Eof) return false; + return mem.eql(u8, name, source[first_tok.start..first_tok.end]); +} + fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) { return error.OutOfMemory; @@ -455,7 +473,10 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { const macro = @ptrCast(*clang.MacroDefinitionRecord, entity); const raw_name = macro.getName_getNameStart(); const name = try c.str(raw_name); - try c.global_names.put(c.gpa, name, {}); + + if (!isSelfDefinedMacro(ast_unit, c, macro)) { + try c.global_names.put(c.gpa, name, {}); + } }, else => {}, } @@ -5446,6 +5467,16 @@ fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!voi } } +fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) []const u8 { + const begin_loc = macro.getSourceRange_getBegin(); + const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit); + + const begin_c = c.source_manager.getCharacterData(begin_loc); + const end_c = c.source_manager.getCharacterData(end_loc); + const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c); + return begin_c[0..slice_len]; +} + fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { // TODO if we see #undef, delete it from the table var it = unit.getLocalPreprocessingEntities_begin(); @@ -5462,22 +5493,18 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { const macro = @ptrCast(*clang.MacroDefinitionRecord, entity); const raw_name = macro.getName_getNameStart(); const begin_loc = macro.getSourceRange_getBegin(); - const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit); const name = try c.str(raw_name); if (scope.containsNow(name)) { continue; } - const begin_c = c.source_manager.getCharacterData(begin_loc); - const end_c = c.source_manager.getCharacterData(end_loc); - const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c); - const slice = begin_c[0..slice_len]; + const source = getMacroText(unit, c, macro); - try tokenizeMacro(slice, &tok_list); + try tokenizeMacro(source, &tok_list); var macro_ctx = MacroCtx{ - .source = slice, + .source = source, .list = tok_list.items, .name = name, .loc = begin_loc, @@ -5490,7 +5517,8 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { // if it equals itself, ignore. for example, from stdio.h: // #define stdin stdin const tok = macro_ctx.list[1]; - if (mem.eql(u8, name, slice[tok.start..tok.end])) { + if (mem.eql(u8, name, source[tok.start..tok.end])) { + assert(!c.global_names.contains(source[tok.start..tok.end])); continue; } }, diff --git a/test/standalone.zig b/test/standalone.zig index 2a7902dfb2..bfd683ec4c 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -9,6 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/6025 cases.add("test/standalone/issue_9693/main.zig"); } + cases.add("test/standalone/issue_12471/main.zig"); cases.add("test/standalone/guess_number/main.zig"); cases.add("test/standalone/main_return_error/error_u8.zig"); cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); diff --git a/test/standalone/issue_12471/main.zig b/test/standalone/issue_12471/main.zig new file mode 100644 index 0000000000..08be1fd471 --- /dev/null +++ b/test/standalone/issue_12471/main.zig @@ -0,0 +1,12 @@ +const c = @cImport({ + @cDefine("FOO", "FOO"); + @cDefine("BAR", "FOO"); + + @cDefine("BAZ", "QUX"); + @cDefine("QUX", "QUX"); +}); + +pub fn main() u8 { + _ = c; + return 0; +}