fix(tool): header parsing fails when prerequisite includes are supplied by the C consumer

Aperta
#1,138 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
64/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c, go, linux

Direzione di ricerca

Start with listHeaderFiles and ParseSources in tool/gen.go, which sort selected headers and invoke ParseTranslationUnit separately for each one. Reproduce with the two listed glibc headers and the documented llcppg command, then use the composed rose-entry.h control to compare field types and exit behavior. Done means prerequisite declarations are visible when parsing context-dependent headers, the generated fields retain their native types, and generation fails when the selected inputs still produce parse errors.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

The prerequisite #include can be supplied by the consuming .c file while being absent from the .h file itself. llcppg currently parses the .h independently, so it does not have the declarations that are already visible when that header is included by a correctly composed C source file.

The difference between compiling the C consumer and parsing its header

For glibc 2.24, this minimal consumer illustrates the required include context:

/* consumer.c — minimal reproduction, not an upstream source excerpt */
#include <netax25/ax25.h>
#include <netrose/rose.h>

The .c includes netax25/ax25.h first. This provides ax25_address, AX25_MAX_DIGIS, and, through bits/sockaddr.h, sa_family_t. When Clang then expands netrose/rose.h inside the same translation unit, those declarations are already visible.

The installed netrose/rose.h itself does not contain that prerequisite include. It uses these names and documents its requirement for ax25.h, relying on the includer to establish the context. The include sequence above passes the recorded C syntax check.

llcppg instead takes netrose/rose.h directly as a translation unit's main input. It does not parse a consuming .c file with the preceding include, nor does listing ax25.h separately in Files put its declarations into that translation unit. Consequently, a header that works when included by the C consumer reports unknown types when parsed in isolation.

The observed downstream result is also incorrect: llcppg returns success and emits sockaddr_rose fields as c.Int where the native declarations require sa_family_t and ax25_address.

Reproduction

Use the Bootlin glibc 2.24 Linux arm64 headers under source/linux-arm64/include, with a source Go module requiring github.com/goplus/lib v0.7.0. The relevant configuration is:

{
  "Name": "libc",
  "Language": "c",
  "Dir": "$GOOS-$GOARCH/include",
  "Files": [
    "netax25/ax25.h",
    "netrose/rose.h"
  ],
  "FailFast": 0,
  "IgnoreInline": true
}

Leave Stdlib unset. In the tested Linux container, Clang's builtin headers and the staged glibc headers were selected with:

GOOS=linux GOARCH=arm64 \
C_INCLUDE_PATH="/usr/lib/llvm-22/lib/clang/22/include:$PWD/source/linux-arm64/include" \
llcppg ./generated ./source

This two-header reproduction emits 13 Clang errors, including:

netrose/rose.h:71:3: error: unknown type name 'sa_family_t'
netrose/rose.h:73:3: error: unknown type name 'ax25_address'
netrose/rose.h:94:28: error: use of undeclared identifier 'AX25_MAX_DIGIS'

All definitions are present in the staged include tree. netax25/ax25.h defines ax25_address and AX25_MAX_DIGIS, and includes bits/sockaddr.h, which defines sa_family_t. netrose/rose.h does not include ax25.h; its comment explicitly states that it requires that header.

The command nevertheless exits 0 and emits recovered, incorrect fields:

type SockaddrRose struct {
    SroseFamily c.Int
    SroseAddr   RoseAddress
    SroseCall   c.Int
    SroseNdigis c.Int
    SroseDigi   c.Int
}

Why ordering Files does not help

At the tested revision, listHeaderFiles and ParseSources sort the input paths and call ParseTranslationUnit separately for each header. Even though ax25.h is parsed first, its declarations are not available when Clang independently parses rose.h.

Clang is correct to reject that standalone input. An include search path only locates files named by #include; it does not load declarations automatically or resolve unknown type names by searching sibling headers.

Verified workaround

Create a single input rose-entry.h containing:

#include <netax25/ax25.h>
#include <netrose/rose.h>

Select only rose-entry.h in Files. With the same native headers and toolchain, generation exits 0 with no diagnostics, and the resulting fields use the declared types:

type SockaddrRose struct {
    SroseFamily SaFamilyT
    SroseAddr   RoseAddress
    SroseCall   Ax25Address
    SroseNdigis c.Int
    SroseDigi   Ax25Address
}

Related C parsing controls also pass when rpcsvc/nislib.h is reached through rpcsvc/nis.h, and when stdint.h precedes scsi/scsi_bsg_fc.h, scsi/scsi_netlink.h, or scsi/scsi_netlink_fc.h. Only the ROSE example above was compared through full llcppg generation in this focused reproduction.

Expected behavior

  • Parse prerequisite-dependent headers with the declarations required by their native include contract visible, and preserve the actual field types shown by the composed-input control.
  • If the selected inputs do not establish a valid include context, report the original parse errors and fail generation. Do not return success or emit bindings from the erroneous recovered declarations.
  • Preserve the native API; removing the affected declarations or changing the installed headers is not a fix.

The composed entry header above is a verified workaround for this bug. The report does not prescribe a new configuration field, public API, or automatic ordering algorithm.

Related issues

#997 covers recursive discovery of context-dependent implementation headers; this reproduction uses an explicit two-file list. #998 concerns preprocessing arguments and include roots; here the files are already reachable. #1137 covers failure handling for fatal parse diagnostics; this case demonstrates ordinary unknown-type errors followed by successful generation of incorrect field types.

Reproduced with llcppg 90bd72cfb16ac56750116353b76ee9e6bf2ae892, LLVM/libclang 22.1.8 and LLGo 1.0.8 on Linux arm64. These are parser/generator results; no LLGo build, runtime, or ABI validation is claimed.

Lingua principale
Go
Stelle
40
Fork
13
Merge medio
2h
PR unite (30g)
150

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di goplus/llcppg

Tutte le issue di goplus/llcppg

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.