Skip to content

Commit 1805dec

Browse files
committed
AI refactoring
1 parent 98de70c commit 1805dec

5 files changed

Lines changed: 64 additions & 57 deletions

File tree

crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ impl Parse for UseTypeAttribute {
3535
let _: At = input.parse()?;
3636

3737
// The context type is followed by a `::`-separated trait path, so it
38-
// must parse only a single identifier head; a full path parser would
39-
// greedily consume the trailing `::Trait::Type`.
38+
// must parse only a single identifier head. This is the one place
39+
// that deliberately keeps `IdentWithTypeArgs` rather than the
40+
// otherwise-dominant `PathWithTypeArgs`: a path parser is greedy
41+
// across `::` and would silently consume the trailing `::Trait::Type`
42+
// here, with no parse error. Do NOT swap this for `PathWithTypeArgs`.
4043
let context_type: Type = input.parse::<IdentWithTypeArgs>()?.into();
4144

4245
let _: Colon = input.parse()?;

crates/macros/cgp-macro-core/src/types/generics/type_generics.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ use syn::{Error, Generics};
77

88
use crate::functions::parse_internal;
99

10+
/// A validated newtype around [`syn::Generics`] restricted to a definition-site
11+
/// generic list (no bounds). Because it `Deref`s to [`syn::Generics`], the full
12+
/// `syn` API (`split_for_impl`, mutating `params`, …) is available, and its
13+
/// `TryFrom<&Generics>` adapts a generic list already parsed off an item.
14+
///
15+
/// Prefer this when adapting or manipulating an existing `syn::Generics`. When
16+
/// instead *parsing tokens* and you want strict, kind-classified parameters,
17+
/// prefer [`TypeGenericParams`]. The two are intentionally kept separate; see
18+
/// [`TypeGenericParams`] for the full rationale (notably, `TryFrom` here
19+
/// normalizes a `const N: T` parameter down to a bare `N`).
20+
///
21+
/// [`TypeGenericParams`]: crate::types::ident::TypeGenericParams
1022
#[derive(Debug, Clone, Default)]
1123
pub struct TypeGenerics {
1224
pub generics: Generics,

crates/macros/cgp-macro-core/src/types/ident/path_with_type_args.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use proc_macro2::TokenStream;
22
use quote::ToTokens;
33
use syn::parse::{Parse, ParseStream};
4-
use syn::punctuated::Punctuated;
5-
use syn::{Error, Ident, Path, PathArguments, Type, parse_quote};
4+
use syn::{Error, Ident, Path, PathArguments, Type, parse_quote, parse2};
65

76
use crate::traits::ToType;
8-
use crate::types::ident::{IdentWithTypeArgs, TypeArg, TypeArgs};
7+
use crate::types::ident::{IdentWithTypeArgs, TypeArgs};
98

109
/// A full Rust path followed by an optional type-expression argument list, e.g.
1110
/// `Foo`, `Foo<A, B>`, `path::to::Foo`, or `path::to::Bar<(A, B), B>`.
@@ -15,8 +14,8 @@ use crate::types::ident::{IdentWithTypeArgs, TypeArg, TypeArgs};
1514
/// generic arguments buried inside the last [`syn::PathSegment`], which is
1615
/// awkward to read and rewrite. This type lifts those arguments out into a
1716
/// separate [`TypeArgs`] field while keeping the remaining path in `path`,
18-
/// applying the same restrictions as [`TypeArg`] (no associated bindings or
19-
/// bounds).
17+
/// applying the same restrictions as [`TypeArg`](crate::types::ident::TypeArg)
18+
/// (no associated bindings or bounds).
2019
///
2120
/// Only the final segment may carry generic arguments. Intermediate generics
2221
/// (e.g. `path::to<X>::Foo`) and parenthesized arguments (e.g. `Fn(A) -> B`)
@@ -34,8 +33,12 @@ impl PathWithTypeArgs {
3433
/// The identifier of the final path segment, e.g. `Foo` in
3534
/// `path::to::Foo<A, B>`.
3635
pub fn ident(&self) -> &Ident {
37-
// A parsed `syn::Path` always has at least one segment.
38-
&self.path.segments.last().unwrap().ident
36+
&self
37+
.path
38+
.segments
39+
.last()
40+
.expect("PathWithTypeArgs always wraps a non-empty syn::Path")
41+
.ident
3942
}
4043
}
4144

@@ -59,9 +62,7 @@ impl Parse for PathWithTypeArgs {
5962
let last_segment = path.segments.last_mut().unwrap();
6063

6164
let type_args = match &last_segment.arguments {
62-
PathArguments::None => TypeArgs {
63-
args: Punctuated::new(),
64-
},
65+
PathArguments::None => TypeArgs::default(),
6566
PathArguments::AngleBracketed(arguments) => {
6667
// Reject turbofish (`Foo::<A>`); only the type-position form
6768
// `Foo<A>` is accepted, matching `IdentWithTypeArgs`.
@@ -72,17 +73,13 @@ impl Parse for PathWithTypeArgs {
7273
));
7374
}
7475

75-
let mut args = Punctuated::new();
76-
77-
for pair in arguments.args.pairs() {
78-
let (arg, punct) = pair.into_tuple();
79-
args.push_value(TypeArg::from_generic_argument(arg)?);
80-
if let Some(comma) = punct {
81-
args.push_punct(*comma);
82-
}
83-
}
84-
85-
TypeArgs { args }
76+
// Re-parse the already-parsed `<...>` through `TypeArgs` so the
77+
// argument-form restrictions (no associated bindings or bounds)
78+
// live in exactly one place — `TypeArg`'s own parser — rather
79+
// than being duplicated here against `syn::GenericArgument`.
80+
// With the turbofish ruled out above, `arguments` re-emits as a
81+
// plain `< .. >`, which is exactly what `TypeArgs` expects.
82+
parse2::<TypeArgs>(arguments.to_token_stream())?
8683
}
8784
PathArguments::Parenthesized(arguments) => {
8885
return Err(Error::new_spanned(

crates/macros/cgp-macro-core/src/types/ident/type_arg.rs

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use quote::ToTokens;
33
use syn::parse::{Parse, ParseStream};
44
use syn::punctuated::Punctuated;
55
use syn::token::{Brace, Comma};
6-
use syn::{Error, Expr, ExprBlock, ExprLit, GenericArgument, Lifetime, Lit, Token, Type};
6+
use syn::{Error, Expr, ExprBlock, ExprLit, Lifetime, Lit, Token, Type};
77

88
use crate::types::ident::{parse_angle_bracketed, to_tokens_angle_bracketed};
99

@@ -32,35 +32,6 @@ pub enum TypeArg {
3232
Const(Expr),
3333
}
3434

35-
impl TypeArg {
36-
/// Convert a [`syn::GenericArgument`] into a [`TypeArg`], rejecting the
37-
/// associated-binding and bound forms that are not valid in type-argument
38-
/// positions.
39-
///
40-
/// This is useful when post-processing a value that `syn` has already
41-
/// parsed into a [`syn::Path`] (see [`PathWithTypeArgs`]).
42-
///
43-
/// [`PathWithTypeArgs`]: crate::types::ident::PathWithTypeArgs
44-
pub fn from_generic_argument(arg: &GenericArgument) -> syn::Result<Self> {
45-
match arg {
46-
GenericArgument::Lifetime(life) => Ok(Self::Lifetime(life.clone())),
47-
GenericArgument::Type(ty) => Ok(Self::Type(ty.clone())),
48-
GenericArgument::Const(expr) => Ok(Self::Const(expr.clone())),
49-
GenericArgument::AssocType(_) | GenericArgument::AssocConst(_) => {
50-
Err(Error::new_spanned(
51-
arg,
52-
"associated bindings (`Name = ...`) are not allowed in type arguments",
53-
))
54-
}
55-
GenericArgument::Constraint(_) => Err(Error::new_spanned(
56-
arg,
57-
"associated type bounds (`Name: ...`) are not allowed in type arguments",
58-
)),
59-
_ => Err(Error::new_spanned(arg, "unsupported generic argument")),
60-
}
61-
}
62-
}
63-
6435
impl Parse for TypeArg {
6536
fn parse(input: ParseStream) -> syn::Result<Self> {
6637
if input.peek(Lifetime) {

crates/macros/cgp-macro-core/src/types/ident/type_generic_param.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ use crate::types::ident::{parse_angle_bracketed, to_tokens_angle_bracketed};
1919
/// - defaults, e.g. `A = B` or `const N: usize = 0`,
2020
/// - composite forms, e.g. `(A, B)`.
2121
///
22-
/// The existing `TypeGenerics` type approximates this by parsing a full
23-
/// `syn::Generics` and then round-tripping it through `split_for_impl` to
24-
/// detect bounds. Modelling the valid forms directly is both clearer and
25-
/// catches more invalid inputs (such as defaults) up front.
22+
/// This complements (rather than replaces) [`TypeGenerics`], which detects
23+
/// bounds by round-tripping a full `syn::Generics` through `split_for_impl`.
24+
/// Modelling the valid forms directly here is clearer and catches more invalid
25+
/// inputs (such as defaults) up front when *parsing tokens*; see
26+
/// [`TypeGenericParams`] for guidance on which of the two to use.
27+
///
28+
/// [`TypeGenerics`]: crate::types::generics::TypeGenerics
2629
#[derive(Debug, Clone)]
2730
pub enum TypeGenericParam {
2831
/// A lifetime parameter, e.g. the `'a` in `Bar<'a>`.
@@ -117,6 +120,27 @@ impl ToTokens for TypeGenericParam {
117120
/// The angle-bracketed parameter list at a type definition site, e.g. the
118121
/// `<'a, C>` in `Bar<'a, C>`.
119122
///
123+
/// # `TypeGenericParams` vs [`TypeGenerics`]
124+
///
125+
/// Both model a definition-site generic list, but they are different tools:
126+
///
127+
/// - Reach for `TypeGenericParams` when **parsing tokens** where you want the
128+
/// restrictions enforced strictly and the parameters classified by kind. It
129+
/// is a hand-written parser that rejects bounds and defaults up front and
130+
/// exposes each parameter as a [`TypeGenericParam`] variant.
131+
/// - Reach for [`TypeGenerics`] when adapting an **already-parsed
132+
/// [`syn::Generics`]** (e.g. off an `ItemTrait`/`ItemStruct`). It is a thin
133+
/// newtype that `Deref`s to `syn::Generics`, so `split_for_impl()` and the
134+
/// usual `syn` manipulation are available, and its `TryFrom<&Generics>`
135+
/// normalizes through `split_for_impl` (which, notably, collapses a
136+
/// `const N: T` parameter down to a bare type-like `N`).
137+
///
138+
/// They are intentionally not merged: the normalization behavior above is
139+
/// load-bearing for some callers, so a faithful conversion into the strict
140+
/// `TypeGenericParam` model would change behavior around const generics.
141+
///
142+
/// [`TypeGenerics`]: crate::types::generics::TypeGenerics
143+
///
120144
/// Both the absence of angle brackets and an explicit empty `<>` are
121145
/// represented as an empty [`Punctuated`]. An empty list renders as nothing,
122146
/// so a parsed `<>` round-trips back to no angle brackets.

0 commit comments

Comments
 (0)