Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion c_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,27 @@ def _build_type(
)
return type_, function_specifiers

def _adjust_parameter_type(self, declared_type: CType) -> CType:
if isinstance(declared_type, CFunctionType):
return CComposedType(
components=[CPointer(), declared_type],
source_text=declared_type.source_text,
)
if (
isinstance(declared_type, CComposedType)
and declared_type.components
and isinstance(declared_type.components[0], CArray)
):
outer_array = declared_type.components[0]
return CComposedType(
components=[
CPointer(qualifiers=list(outer_array.qualifiers)),
*declared_type.components[1:],
],
source_text=declared_type.source_text,
)
return declared_type

def _parse_parameter(self, text: str) -> CParameter | None:
stripped = text.strip()
if not stripped or stripped == "void":
Expand All @@ -638,7 +659,7 @@ def _parse_parameter(self, text: str) -> CParameter | None:
)
return CParameter(
name=name,
type=type_,
type=self._adjust_parameter_type(type_),
declared_type=type_,
)

Expand Down
25 changes: 14 additions & 11 deletions docs/c_parser/c_parser_architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ Implemented now:
Inline tag definitions followed by aliases or objects produce concrete
`CTypedef` or `CVariable` records linked to the aggregate object. Function
models expose `result_type` and named `parameters`; their derived
`CFunctionType` is the nameless callable signature. Selected unsupported
`CFunctionType` is the nameless callable signature. Array and function
parameter declarations preserve their written `declared_type` and expose
pointer-adjusted effective `type` values. Selected unsupported
declaration forms, including attributes, alignment specifiers,
`_Atomic(type)`, nested aggregate member definitions, and static assertions,
are reported as diagnostics with
Expand All @@ -76,7 +78,6 @@ Deferred:
- typedef/tag resolution beyond an inline aggregate declaration and callback
policy metadata, for example resolving `size_t count(void);` to a prior
`typedef unsigned long size_t;`
- parameter adjustment, for example `void process(int values[4]);`
- nested aggregate member definitions, braced initializers, compiler
attributes, alignment specifiers, and `_Atomic(type)` declarations, for
example `struct outer { struct { int x; } inner; };` and
Expand Down Expand Up @@ -212,9 +213,9 @@ Current and planned responsibilities:
- `c_parser/models.py`
- Implemented: typed parser models, `CParseError`, compiler-style
diagnostic rendering, concrete `CType` composition, and JSON-stable
dataclass serialization.
dataclass serialization, including declared/effective parameter type facts.
- Planned: resolved symbol links, richer macro/preprocessing provenance,
parameter-adjustment facts, and project indexes.
and project indexes.
- `c_parser/lexer.py`
- Implemented: safe comment removal that preserves line mapping, logical
record folding for backslash-newline, string/character literal awareness,
Expand All @@ -237,8 +238,10 @@ Current and planned responsibilities:
unspecified empty parameter lists, function definitions preserve start/end
locations, K&R-style definitions are rejected with `CParseError`, and
invalid primitive-specifier combinations are rejected with `CPARSE003`.
- Planned: symbol resolution, parameter array/function adjustment, and
additional declaration-specifier and extension coverage.
Array and function parameters preserve `declared_type` while effective
`type` uses C parameter adjustment.
- Planned: symbol resolution and additional declaration-specifier and
extension coverage.
- `c_parser/project.py`
- Placeholder now.
- Planned: file discovery for `.c`, `.h`, and possibly `.i`.
Expand All @@ -250,7 +253,6 @@ Current and planned responsibilities:
- Planned: resolve the concrete primitive/tag/typedef types constructed by
the parser across declarations and files.
- Resolve typedef chains and aggregate references.
- Validate or adjust parameter array/function forms.
- Safely fold simple compile-time constant expressions.
- `c_parser/cli.py`
- Implemented: report formatting and serialization helpers called by
Expand Down Expand Up @@ -333,8 +335,9 @@ Declaration objects are separate from the type components:
function specifiers, `is_variadic`, prototype style, and source/definition
locations. Its `type` property builds the corresponding nameless
`CFunctionType`.
- `CParameter` has a source name, a `type`, and a reserved `declared_type` for
later C parameter adjustment handling.
- `CParameter` has a source name, written `declared_type`, and effective
`type`; outer array parameters and direct function parameters adjust to
pointer `type` values while their source form is retained.
- `CInitializer` preserves initializer source text without claiming evaluation.
- `CStruct` and `CUnion` expose `members` and `is_incomplete`; `CEnum` exposes
`constants`; `CEnumerator` preserves enumerator name and value text.
Expand Down Expand Up @@ -380,8 +383,8 @@ reserved for semantic type relationships such as `CVariable.type` and
spellings, such as `"const"`. Reused aggregate/typedef objects serialize as
references to avoid cycles.

Future parser phases can add symbol links, parameter adjustment, conditional
region metadata, include graphs, and project diagnostics when the corresponding
Future parser phases can add symbol links, conditional region metadata,
include graphs, and project diagnostics when the corresponding
behavior lands. Additions should be documented and tested with stable
serialization expectations.

Expand Down
11 changes: 9 additions & 2 deletions docs/c_parser/c_parser_cli_workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ macros, `#undef` provenance, metadata diagnostics, variables, typedefs,
aggregate declarations, function prototypes, prototype-style metadata, and
function-definition signatures with start/end locations. Declarator output can
represent parenthesized pointer/array precedence through concrete
`CComposedType` components and nameless `CFunctionType` signatures.
`CComposedType` components and nameless `CFunctionType` signatures; function
parameters expose both declared and C-adjusted effective type facts.

This document records the implemented C parse command shape, output schema,
and diagnostic contract, plus deferred CLI behavior.
Expand All @@ -33,7 +34,9 @@ can populate `functions`, `typedefs`, `variables`, `structs`, `unions`, and
`enums` in the supported subset. Typedefs, variables, parameters, and aggregate
members can include concrete composed types for pointer/array/function forms,
including function pointers, functions returning function pointers, and
legal final flexible struct members marked with `is_flexible=True`. Raw
legal final flexible struct members marked with `is_flexible=True`. Array and
function parameters preserve written `declared_type` forms while effective
`type` values use pointer adjustment. Raw
`includes`, `macros`, and metadata `diagnostics` can also be
populated. The object class distinguishes declarations (`CFunction`,
`CVariable`, `CTypedef`, `CStruct`, `CUnion`, or `CEnum`), and incomplete tag
Expand Down Expand Up @@ -405,6 +408,8 @@ The active CLI/parser tests cover the current partial subset:
- flexible array member classification/validation, per-member source
locations, and named/unnamed/zero-width bit-field source facts are covered
by focused C tests.
- array and function parameter declared/effective type adjustment is covered
by focused C tests.
- `--show-vars` and `--print-limit` are rejected in C mode until C-specific
display controls exist.
- `--semantics` with `--language c` is rejected until C semantic conversion is
Expand Down Expand Up @@ -444,6 +449,8 @@ Completed order:
typedef-name references for later resolution.
14. Added field-level source locations, flexible array member classification
and invalid-use diagnostics, plus explicit bit-field regression coverage.
15. Added C array/function parameter adjustment while preserving written
parameter type facts in `declared_type`.

Next implementation work should continue with tag/typedef resolution,
preprocessed-input line mapping, compiler extension policy, and project
Expand Down
12 changes: 8 additions & 4 deletions docs/c_parser/c_parser_implementation_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extensions are diagnosed, and invalid primitive-specifier combinations raise
`CParseError` without treating unresolved single typedef-name uses as invalid.
Aggregate members carry their own source locations, and flexible array
members are classified and checked for supported struct/union constraints.
Function parameters preserve written array/function forms in `declared_type`
while exposing C-adjusted pointer forms in `type`.

This checklist is intentionally detailed so future work can proceed one branch,
one checklist item, and one tested capability at a time. The C parser initiative
Expand All @@ -24,7 +26,7 @@ stable.
## Progress Snapshot

- Last updated: 2026-05-24
- Checklist progress: 516/848 checked (60.8%).
- Checklist progress: 517/849 checked (60.9%).
- Current parser status: partial C parser with raw directive metadata, top-level
source splitting, simple declarations/variables/typedefs, prototype-style
metadata, K&R diagnostics, simple function signatures, and start/end
Expand All @@ -39,7 +41,9 @@ stable.
distinguished by their concrete declaration objects rather than a kind field.
Struct and union fields now preserve per-member locations; legal final
flexible struct members are marked through `CArray.is_flexible`, with error
diagnostics for invalid placement or union use.
diagnostics for invalid placement or union use. Array and function
parameter declarations preserve their source form in `declared_type` while
their effective `type` applies C parameter-to-pointer adjustment.

## Global Rules

Expand Down Expand Up @@ -729,6 +733,8 @@ Scope:

- [x] Implement a helper analogous to `_helper_parse_declaration_line`.
- [x] Feed procedure parameters through the same declaration backend.
- [x] Preserve array/function parameter `declared_type` while exposing the
C-adjusted pointer `type`.
- [x] Feed function return types through the same declaration backend.
- [x] Feed struct/union members through the same declaration backend.
- [x] Feed typedefs through the same declaration backend.
Expand All @@ -752,8 +758,6 @@ Scope:

Known declaration implementation gaps, with representative syntax:

- parameter array/function adjustment:
`void process(int values[4], int callback(int));`
- braced/designated initializer preservation:
`int values[3] = {1, 2, 3};`
- nested anonymous aggregate members:
Expand Down
22 changes: 17 additions & 5 deletions docs/c_parser/c_parser_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Implemented:
- recursive declarator extraction for parenthesized pointer/array precedence
- nameless `CFunctionType` signatures for function pointer typedefs and
parameter source facts
- parameter array/function adjustment that keeps written `declared_type` facts
and exposes effective pointer `type` facts
- simple file-scope variable and `typedef` extraction
- incomplete `struct name;` and `union name;` extraction as concrete tag types
with `is_incomplete=True`
Expand All @@ -95,8 +97,7 @@ Still deferred:

- callback policy metadata beyond parser-side callback candidates
- nested aggregate member definitions and broad compiler-extension declarators
- parameter array/function adjustment and braced/designated initializer
preservation
- braced/designated initializer preservation
- cross-declaration and cross-file typedef/tag resolution
- project include graph and cross-file type resolution
- preprocessed-input parsing with `#line`/linemarker source mapping
Expand Down Expand Up @@ -261,6 +262,17 @@ int add(int a, int b); # CFunction(name="add", result_type=CInt(), parameter
int (*compare)(int, int); # CVariable(type=CComposedType([CPointer(), CFunctionType(...)]))
```

Function parameters preserve both the written type and C's adjusted callable
type:

```python
void process(int values[4], int callback(int));
# values.declared_type: CComposedType([CArray(bound="4"), CInt()])
# values.type: CComposedType([CPointer(), CInt()])
# callback.declared_type: CFunctionType(...)
# callback.type: CComposedType([CPointer(), CFunctionType(...)])
```

Callback-bearing parameters are marked as parser-side callback candidates,
without claiming semantic wrappability. Struct and union `members` are
`CVariable` objects; optional `bit_width` and `initializer` fields preserve
Expand Down Expand Up @@ -452,8 +464,9 @@ Active declaration tests currently cover:
- all qualifier objects, storage metadata, simple expression initializers, and
multiple declarators
- pointer/array precedence, multidimensional arrays, parameter VLA/static
metadata, function pointers, callback arrays, and functions returning
function pointers
metadata and pointer adjustment, function-declared callback adjustment,
function pointers, callback arrays, and functions returning function
pointers
- functions, variables, typedefs, struct/union members, enums, incomplete
tags, inline aggregate aliases, anonymous aggregate typedefs, and recursive
struct pointers
Expand All @@ -474,7 +487,6 @@ declarations.

| Capability | C example | Current parser boundary | Needed behavior |
| --- | --- | --- | --- |
| Parameter adjustment | `void process(int values[4], int callback(int));` | Preserves the declared array and function parameter types; it does not expose C's adjusted pointer parameter type. | Keep `declared_type`, and expose the adjusted effective type (`int *` and pointer-to-function). |
| Braced/designated initializers | `int values[3] = {1, 2, 3};` and `struct point origin = {.x = 1, .y = 2};` | Simple initializer text such as `int answer = 42;` is preserved; braced forms are not reliably emitted as `CVariable` initializer facts. | Parse or preserve balanced initializer source without treating its braces as an aggregate declaration. |
| Nested aggregate members | `struct outer { struct { int x; } inner; };` | Produces an unsupported-member diagnostic and does not model `inner`. | Build an anonymous `CStruct`/`CUnion` type used by the member variable. |
| Typedef/tag resolution | `typedef unsigned long size_t; size_t count(void);` and `struct state { int id; }; void step(struct state *s);` | Preserves uses as unresolved `CTypedef` or incomplete tag-type objects unless attached inline. | Link uses to declarations across a file/project and diagnose conflicts. |
Expand Down
40 changes: 26 additions & 14 deletions tests/parser/c/test_c_declarations_and_declarators.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,38 @@ def test_pointer_qualifiers_belong_to_the_component_they_qualify():
assert dst.components[0].qualifiers == [CRestrict()]


def test_array_components_preserve_bounds_static_minimum_and_qualifiers():
from c_parser import CArray, CComposedType, CConst, parse_c_file
def test_array_parameters_preserve_declarations_and_expose_adjusted_pointer_types():
from c_parser import CArray, CComposedType, CConst, CDouble, CInt, CPointer, parse_c_file

parsed = parse_c_file(
"void solve(size_t n, double a[static 4], const int shape[2], int work[const *]);\n",
"void solve(size_t n, double a[static 4], const int shape[2], int work[const *], int matrix[3][4]);\n",
filename="arrays.h",
)

params = {parameter.name: parameter for parameter in parsed.functions[0].parameters}
a = params["a"].type
shape = params["shape"].type
work = params["work"].type
assert isinstance(a, CComposedType)
assert isinstance(a.components[0], CArray)
assert a.components[0].bound == "4"
assert a.components[0].is_static_minimum is True
assert shape.components[0].bound == "2"
assert shape.components[-1].qualifiers == [CConst()]
assert work.components[0].qualifiers == [CConst()]
assert work.components[0].is_variable_length is True
a_declared = params["a"].declared_type
assert isinstance(a_declared, CComposedType)
assert [type(component) for component in a_declared.components] == [CArray, CDouble]
assert a_declared.components[0].bound == "4"
assert a_declared.components[0].is_static_minimum is True
assert [type(component) for component in params["a"].type.components] == [CPointer, CDouble]

shape_declared = params["shape"].declared_type
assert shape_declared.components[0].bound == "2"
assert shape_declared.components[-1].qualifiers == [CConst()]
assert [type(component) for component in params["shape"].type.components] == [CPointer, CInt]
assert params["shape"].type.components[-1].qualifiers == [CConst()]

work_declared = params["work"].declared_type
assert work_declared.components[0].qualifiers == [CConst()]
assert work_declared.components[0].is_variable_length is True
assert params["work"].type.components[0].qualifiers == [CConst()]

matrix_declared = params["matrix"].declared_type
assert [type(component) for component in matrix_declared.components] == [CArray, CArray, CInt]
assert [component.bound for component in matrix_declared.components[:2]] == ["3", "4"]
assert [type(component) for component in params["matrix"].type.components] == [CPointer, CArray, CInt]
assert params["matrix"].type.components[1].bound == "4"


def test_multiple_declarators_share_specifiers_but_have_distinct_compositions():
Expand Down
14 changes: 14 additions & 0 deletions tests/parser/c/test_c_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ def test_function_pointer_parameter_is_a_callback_candidate_with_nameless_signat
assert len(signature.parameter_types) == 2


def test_function_parameter_preserves_declaration_and_adjusts_to_callback_pointer():
from c_parser import CComposedType, CFunctionType, CPointer, parse_c_file

parsed = parse_c_file("void apply(int callback(int));\n", filename="adjusted_callback.h")

callback = parsed.functions[0].parameters[0]
assert isinstance(callback.declared_type, CFunctionType)
assert isinstance(callback.type, CComposedType)
assert [type(component) for component in callback.type.components] == [CPointer, CFunctionType]
assert callback.type.components[1] is callback.declared_type
assert callback.callback_candidate is True
assert parsed.functions[0].type.parameter_types[0] is callback.type


@pytest.mark.skip(reason="function pointer typedef resolution is not implemented yet.")
def test_callback_typedef_parameter_links_to_typedef_signature():
from c_parser import CFunctionType, CTypedef, parse_c_file
Expand Down
17 changes: 17 additions & 0 deletions tests/parser/c/test_c_public_api_skeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,23 @@ def test_concrete_type_serialization_preserves_semantic_type_fields_and_location
assert function["source_location"]["line"] == 2


def test_parameter_adjustment_serialization_preserves_declared_and_effective_types():
from c_parser import parse_c_file

payload = parse_c_file(
"void process(int values[4], int callback(int));\n",
filename="adjustment.h",
).to_dict()
values, callback = payload["functions"][0]["parameters"]

assert values["declared_type"]["components"][0]["model"] == "CArray"
assert values["declared_type"]["components"][0]["bound"] == "4"
assert values["type"]["components"][0]["model"] == "CPointer"
assert callback["declared_type"]["model"] == "CFunctionType"
assert callback["type"]["components"][0]["model"] == "CPointer"
assert callback["type"]["components"][1]["model"] == "CFunctionType"


def test_inline_aggregate_typedef_serialization_uses_references_without_cycles():
from c_parser import parse_c_file

Expand Down
Loading