Ctrl K
ComunidadGuías…

doc/custom_attributes.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Attribute schemas

A struct marked with `@[attribute]` declares a metadata schema. Schemas cannot be initialized,
embedded, or supplied as generic type arguments. This restriction covers generic types,
function and method calls, and generic functions taken as values.

## Unknown attribute names

An attribute that is neither a builtin nor a registered schema warns (warn-first migration,
[vlang/rfcs#35](https://github.com/vlang/rfcs/pull/35)):

```text
unknown attribute `Rout`. Did you mean `Route`? Unvalidated attributes are deprecated;
declare a schema with `@[attribute]` or drop the attribute.
```

The suggestion appears when a same-named schema exists in another module (qualify it,
`mod.Name`), or when a schema or builtin name is close (first letter shared, small edit
distance). An attribute call `@[Name(args)]` warns once, on its name, never per argument.

Whole-string metadata such as `@['legacy']` does not warn. Independently written names,
such as `@[foo; foo_bar]`, are checked separately, even when one name prefixes another.

Struct attributes such as `heap`, `minify`, and `autostr` are builtins and do not require schemas.

The standard `flag` module's metadata names, including `doc`, `xdoc`, `footer`, `name`,
`version`, and `ignore`, are recognized builtins and do not require a custom attribute schema.

The `veb` metadata names `description` and `example`, and its HTTP method attributes, including
`connect` and `trace`, are also recognized. Builtin names are case-sensitive.

## Structured reflection on fields and enum values

Struct fields and enum values expose the same typed `.attributes` view as
declarations, alongside the legacy `.attrs` (`[]string`, kept working):

```v
struct Cfg {
	user_name string @[json: 'userName'; required]
}

fn main() {
	$for field in Cfg.fields {
		$for a in field.attributes {
			// user_name: json|true|userName, then user_name: required|false|
			println('${field.name}: ${a.name}|${a.has_arg}|${a.arg}')
		}
	}
}
```

Each element is a `VAttribute` with `name`/`has_arg`/`arg`/`kind`, exactly
like `Type.attributes` on declarations: `@['x']` has the `.string` kind and
`@[x]` the `.plain` kind. A runtime `for a in field.attributes` reads the same
values.

## Reading attributes at compile time

An attribute whose name matches a registered schema exposes its fields when a
`$for attr in X.attributes` loop is unrolled.
Narrow with `$if attr is Schema` before touching schema fields; other names
keep the `VAttribute` view (`name`/`has_arg`/`arg`/`kind`) exactly as before:

```v
@[attribute]
struct Route {
	path   string
	method string
}

@[Route('/index', method: 'get')]
@[deprecated: 'old']
fn index() {}

fn main() {
	$for attr in index.attributes {
		$if attr is Route {
			println('route: ${attr.path} ${attr.method}')
		} $else {
			println('other: ${attr.name}')
		}
	}
}
```

Supported schema field types are `string`, integer types (including `uint` and `rune`),
`bool`, enums, and aliases of those types.
A use the checker rejects (unknown field, wrong arity) keeps the legacy pieces
so the checker diagnostic stays authoritative. Copies of the loop variable
(`r := attr`) keep the `VAttribute` view; narrowing applies to direct `attr`
uses. Inside that narrowed branch, schema fields named `name`, `arg`, `has_arg`, or
`kind` take precedence over the metadata members. Method calls such as `attr.str()`
keep the `VAttribute` method even when a schema declares a `str` field.
Outside narrowing, metadata members retain the `VAttribute` view.

## Checking schema field access

Inside an `attributes` loop, read a schema field after narrowing the loop variable with
`$if attr is Schema`. The checker validates field access in initializers, assignments,
interpolations, calls, and runtime conditions. This also applies to chained field access.
The four metadata members `name`, `arg`, `has_arg`, and `kind` remain available without
narrowing. A narrowed schema's own field takes precedence over a metadata member of the same name.

Narrowing follows `&&`, `||`, parentheses, negation, and both branches of nested `$if`
conditions. A branch that can contain multiple schemas may read a field only when every
possible schema declares it with the same type. Compile-time conditions currently accept
the four metadata members; schema field values in those conditions require further work.