Noir::TreeSitterKotlinParameterExtractor
Tree-sitter-backed parameter extractor for Kotlin Spring.
Mirrors TreeSitterJavaParameterExtractor for the Kotlin AST
(parameter modifiers come before the parameter, primary
constructor properties carry DTO fields, annotation arguments
use value_arguments instead of annotation_argument_list).
Covered:
@PathVariable(skipped — URL carries it)@RequestBody/ messaging@Payload— defaults to "json" (or "form" via consumes=)@RequestParam(value/name = "x", defaultValue = "y")— query@RequestHeader(value/name = "x")/ messaging@Header— header, includingHttpHeaders.X_FOOconstant normalisation@CookieValue(name = "lorem", defaultValue = "ipsum")— cookie- Primitive types (Long/Int/String/Boolean/MultipartFile) emit directly with the declared parameter name
- User-defined classes — DTO field expansion via the
caller-supplied
class_fieldsindex. Kotlin DTOs are usuallydata class Foo(val a, var b)— properties live on the primary constructor and are all exposed as if they werepublicfields with synthesised setters. params = ["x=v"]andheaders = ["X-H=v"]on the method's mapping annotation synthesise extraParams.params=for GET/HEAD/OPTIONS is "query"; otherwise "form" (matches Spring's dispatch convention).
Not covered yet (see #1298):
- HttpServletRequest body scan — Kotlin idiom rarely uses it.
- Meta-annotations (custom annotations composing
@RequestMapping).
Constants
Parameter annotations whose value is supplied by a Spring argument
resolver, not bound from the HTTP request the client controls. A
parameter carrying one of these is NOT an attack-surface input
even when its type is a bindable DTO (the classic
@AuthenticationPrincipal user: User, or a custom meta-annotation
like @CurrentUser), so it must be excluded from the un-annotated
implicit-binding path that would otherwise expand its DTO fields
into phantom params. Validation annotations (@Valid, …) are
intentionally absent — they leave binding semantics untouched.
Scalar types Spring binds from a single request value. Type names
are first resolved to their leaf identifier by leaf_type_name
(so List<Long> reduces to List); annotated request params emit
by name regardless of type. Mirrors the Java extractor's
SIMPLE_PARAM_TYPES so @RequestParam ids: List<Long> surfaces
instead of being silently dropped.
Verbs whose params = [...] constraint emits as query
parameters. Anything else uses form data.
Instance methods
Extract {class_name => [FieldInfo]} from source. Kotlin DTO
fields can come from:
- Primary constructor parameters declared with
val/var(the commondata class Foo(val a: Int, var b: String)idiom). property_declarationnodes inside the class body (var/valproperties on regular classes).
Properties are treated as setter-accessible by default — Kotlin
synthesises setters for var, and val properties usually
serialise just fine for our endpoint-fan-out purposes.
_from(root, source, ...) variants accept a pre-parsed root
so the Kotlin Spring analyzer can amortise the tree-sitter
parse across multiple extractions on the same file. Tree
lifetime is the caller's responsibility.
Map each class to the simple name of its superCLASS (the supertype
invoked with (), e.g. class Owner : Person() → {"Owner" => "Person"}). Interface supertypes (: Foo without parens) carry no
bindable fields and are skipped. Drives the DTO index's cross-file
inheritance merge so a command object that extends a base class
inherits its bindable fields.
Read consumes = ["..."] / consumes = arrayOf("...") off the
method's mapping annotation. Returns "form" / "json" / nil.
Walk method formal parameters + synthesised params=/headers=
constraints on the method's mapping annotation. Returns the
combined parameter list in the order the legacy analyzer
emitted them: formal-parameter sweep first, constraint sweep
appended.