attribute-dsl
Shared parser helpers for Rust proc-macro attribute DSLs built from path roots, dot-call chains, and comma-separated entries.
This crate is intended for derive and attribute macro implementation crates.
Parsed Model
The core parser accepts a Rust path root followed by zero or more method calls:
AttributeChain := Path ("." Ident Turbofish? "(" Expr,* ")")* CompletionProbe?
CompletionProbe := "." CompletionMarker
ChainEntry := Ident "=" AttributeChain | AttributeChain
ChainList := ChainEntry ("," ChainEntry)* ","?
NamedChainGroup := Ident "(" ChainList ")"
Examples of accepted chain shapes:
RootType::<_>
RootType::<_>.first(1)
RootType::<i32>.first(1)
RootType::<_>.
RootType::<_>.first(1).raCompletionMarker
The root is kept as a syn::Path. Each dot-call is represented as a
ChainCall containing the method Ident, optional turbofish, and call
arguments as syn::Expr values. Parser errors are reported as syn::Error.
Associated constructors and other expression forms belong in the generated code
around the parsed root. For example, parse RootType::<_> as the root, then
emit RootType::<i32>::builder_for(stringify!(value)) from your macro
expansion.
Quick Parsing
use ;
use parse_str;
let chain: AttributeChain =
parse_str?;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert!;
let list: ChainList = parse_str?;
assert_eq!;
assert_eq!;
assert!;
let group: NamedChainGroup =
parse_str?;
assert_eq!;
assert_eq!;
# Ok::
Derive Macro Example
use ;
use TokenStream;
use quote;
use ;
The example parses #[attribute_dsl(RootType::<_>.first(1).)], substitutes the
field type for _ in the root path, preserves the parsed method calls, and emits
a typed completion probe expression for rust-analyzer.
Completion Probe
Incomplete input such as RootType::<_>. is parsed as if it ended with
RootType::<_>.raCompletionMarker.
Macro expanders can detect AttributeChain::has_completion_probe() and emit a
method access on the real generated builder. That gives rust-analyzer a typed
receiver inside an attribute token tree, so it can offer normal method
completion at the original dot.
Consumers that accept complete chains but do not emit typed probe code can parse
with ChainParseOptions::new().allow_completion_probe(CompletionProbeParsing::Disabled)
to reject both trailing-dot recovery and explicit marker syntax.
use ;
use quote;
let options = new.completion_marker;
let chain = parse_tokens_with_options?;
assert!;
assert_eq!;
let strict = new
.allow_completion_probe;
assert!;
# Ok::
Completion probes are also recognized before a comma in a ChainList, which
lets an attribute parser recover a partially typed entry while preserving the
remaining entries.
Infer Helpers
Many attribute DSLs use _ as a placeholder for a subject type, such as the
field type in a derive macro. This crate provides helpers that operate directly
on syn syntax trees:
split_terminal_single_type_arg(path, subject)removes the final path segment's single type argument and returnsSingleTypeArg::None,SingleTypeArg::Infer, orSingleTypeArg::Explicit.substitute_infer_in_path(path, replacement)substitutes_inside path arguments.substitute_infer_in_type(ty, replacement)substitutes_inside supportedsyn::Typeforms, including arrays, slices, pointers, function types, trait objects,impl Trait, tuples, references, parenthesized types, and grouped types.substitute_infer_in_expr(expr, replacement)visits expression syntax and substitutes_in nested paths and types.
use ;
use ToTokens as _;
use ;
let path: Path = parse_quote!;
let = split_terminal_single_type_arg?;
assert_eq!;
assert!;
let replacement: Type = parse_quote!;
let path: Path = parse_quote!;
let substituted_path = substitute_infer_in_path;
assert!;
let ty: Type = parse_quote!;
let substituted_type = substitute_infer_in_type;
assert!;
let expr: Expr = parse_quote!;
let substituted_expr = substitute_infer_in_expr;
assert!;
# Ok::