From ec75c195c64d14b1deb5486e10dbfd4953dee8ce Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:54:08 +0100 Subject: [PATCH 1/2] chore: Remove stale references to old f/t macros --- ARCHITECTURE.md | 45 ++++++-------- .../analyses/components/db/diagnostics.rs | 62 +------------------ .../annotations/coordinates.rs | 4 +- .../user_components/annotations/diagnostic.rs | 6 +- .../user_components/annotations/mod.rs | 4 +- .../analyses/user_components/router.rs | 6 +- compiler/pavexc/src/diagnostic/mod.rs | 4 +- .../src/diagnostic/registration_locations.rs | 45 +++++++------- compiler/pavexc/src/diagnostic/sink.rs | 20 +----- 9 files changed, 59 insertions(+), 137 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 416a0e155..366acf95e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -59,22 +59,15 @@ pub fn stream_file( how the application is supposed to behave at runtime. ```rust -use pavex::blueprint::{Blueprint, constructor::Lifecycle}; -use pavex::blueprint::route::GET; -use pavex::f; - -/// The blueprint for our application. -/// It lists all its routes and provides constructors for all the types -/// that will be needed to invoke `stream_file`, our request handler. -/// -/// This will be turned into a ready-to-run web server by `pavex_cli`. +use pavex::Blueprint; + pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.constructor(f!(crate::load_configuration), Lifecycle::Singleton); - bp.constructor(f!(crate::http_client), Lifecycle::Singleton); - bp.constructor(f!(crate::extract_path), Lifecycle::RequestScoped); - bp.constructor(f!(crate::logger), Lifecycle::Transient); - bp.route(GET, "/home", f!(crate::stream_file)); + bp.singleton(LOAD_CONFIGURATION); + bp.singleton(HTTP_CLIENT); + bp.request_scoped(EXTRACT_PATH); + bp.transient(LOGGER); + bp.route(STREAM_FILE); bp } ``` @@ -187,25 +180,23 @@ You can get a structured representation of all the types in `library_name`.\ This is what Pavex does: for each registered route handler and constructor, it builds the documentation for the crate it belongs to and extracts the relevant bits of information from `rustdoc`'s output. -If you are going through the source code, this is the process that converts a `RawCallableIdentifiers` into a `Callable`, -with `ResolvedPath` as an intermediate step. +If you are going through the source code, this is the process that converts a `RawCallableIdentifiers` into a `Callable`. -`Callable` looks like this: +`Callable` is an enum: ```rust -struct Callable { - pub output_fq_path: Type, - pub callable_fq_path: ResolvedPath, - pub inputs: Vec, -} - -pub struct Type { - pub package_id: PackageId, - pub base_type: Vec, - pub generic_arguments: Vec, +enum Callable { + FreeFunction(FreeFunction), + InherentMethod(InherentMethod), + TraitMethod(TraitMethod), + StructLiteralInit(StructLiteralInit), + EnumVariantInit(EnumVariantInit), } ``` +Each variant carries its inputs (as `Vec`) and output type. `Type` is a `rustdoc_types::Type` enum +with variants like `ResolvedPath`, `Generic`, `Primitive`, etc. + After this phase, we have a collection of `Callable` instances representing our constructors and handlers.\ It's a puzzle that we need to solve, starting from the handlers: how do we build instances of the types that they take as inputs? diff --git a/compiler/pavexc/src/compiler/analyses/components/db/diagnostics.rs b/compiler/pavexc/src/compiler/analyses/components/db/diagnostics.rs index 5ed83ade1..319d56185 100644 --- a/compiler/pavexc/src/compiler/analyses/components/db/diagnostics.rs +++ b/compiler/pavexc/src/compiler/analyses/components/db/diagnostics.rs @@ -105,16 +105,6 @@ impl ComponentDb { "All unassigned generic parameters must be used by the output type.\n\ `{}`, one of your constructors, breaks this rule: {free_parameters} {subject_verb} only used by its input parameters.", callable)); - let help = if db.registration(id).kind.is_blueprint() { - Some("Assign concrete type(s) to the problematic \ - generic parameter(s) when registering the constructor against the blueprint: \n\ - | bp.constructor(\n\ - | f!(my_crate::my_constructor::), \n\ - | ..\n\ - | )".to_string()) - } else { - None - }; let d = CompilerDiagnostic::builder(error) .optional_source(source) .optional_source(definition_snippet) @@ -122,9 +112,6 @@ impl ComponentDb { "Can you restructure your constructor to remove those generic parameters from its signature?" .into(), ) - .optional_help(help) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); } @@ -309,7 +296,6 @@ impl ComponentDb { buffer }; let verb = if parameters.len() == 1 { "does" } else { "do" }; - let plural = if parameters.len() == 1 { "" } else { "s" }; let error = anyhow::anyhow!(e) .context( format!( @@ -317,16 +303,9 @@ impl ComponentDb { There should no unassigned generic parameters in request handlers, but {free_parameters} {verb} \ not seem to have been assigned a concrete type.", callable)); - let d = CompilerDiagnostic::builder(error).optional_source(source) + let d = CompilerDiagnostic::builder(error) + .optional_source(source) .optional_source(definition_snippet) - .help( - format!("Specify the concrete type{plural} for {free_parameters} when registering the request handler against the blueprint: \n\ - | bp.route(\n\ - | ..\n\ - | f!(my_crate::my_handler::), \n\ - | )")) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); } @@ -417,7 +396,6 @@ impl ComponentDb { buffer }; let verb = if parameters.len() == 1 { "does" } else { "do" }; - let plural = if parameters.len() == 1 { "" } else { "s" }; let error = anyhow::anyhow!(e) .context( format!( @@ -428,13 +406,6 @@ impl ComponentDb { let d = CompilerDiagnostic::builder(error) .optional_source(source) .optional_source(definition_snippet) - .help( - format!("Specify the concrete type{plural} for {free_parameters} when registering the wrapping middleware against the blueprint: \n\ - | bp.wrap(\n\ - | f!(my_crate::my_middleware::), \n\ - | )")) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); } @@ -495,7 +466,6 @@ impl ComponentDb { buffer }; let verb = if parameters.len() == 1 { "does" } else { "do" }; - let plural = if parameters.len() == 1 { "" } else { "s" }; let error = anyhow::anyhow!(e) .context( format!( @@ -505,13 +475,6 @@ impl ComponentDb { let d = CompilerDiagnostic::builder(error) .optional_source(source) .optional_source(definition_snippet) - .help( - format!("Specify the concrete type{plural} for {free_parameters} when registering the pre-processing middleware against the blueprint: \n\ - | bp.pre_process(\n\ - | f!(my_crate::my_middleware::), \n\ - | )")) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); } @@ -584,7 +547,6 @@ impl ComponentDb { buffer }; let verb = if parameters.len() == 1 { "does" } else { "do" }; - let plural = if parameters.len() == 1 { "" } else { "s" }; let error = anyhow::anyhow!(e) .context( format!( @@ -595,13 +557,6 @@ impl ComponentDb { let d = CompilerDiagnostic::builder(error) .optional_source(source) .optional_source(definition_snippet) - .help( - format!("Specify the concrete type{plural} for {free_parameters} when registering the post-processing middleware against the blueprint: \n\ - | bp.post_process(\n\ - | f!(my_crate::my_middleware::), \n\ - | )")) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); } @@ -717,11 +672,6 @@ impl ComponentDb { get_snippet(callable, parameters, krate_collection); let d = CompilerDiagnostic::builder(e).optional_source(source) .optional_source(definition_snippet) - .help( - "Specify the concrete type(s) for the problematic \ - generic parameter(s) when registering the error observer against the blueprint: `f!(my_crate::my_observer::)`".into()) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(d); }, @@ -821,14 +771,6 @@ impl ComponentDb { callable)); let diagnostic = CompilerDiagnostic::builder(error).optional_source(source) .optional_source(definition_snippet) - .help( - "Specify the concrete type(s) for the problematic \ - generic parameter(s) when registering the error handler against the blueprint: \n\ - | .error_handler(\n\ - | f!(my_crate::my_error_handler::)\n\ - | )".into()) - // ^ TODO: add a proper code snippet here, using the actual function that needs - // to be amended instead of a made signature .build(); diagnostics.push(diagnostic); } diff --git a/compiler/pavexc/src/compiler/analyses/user_components/annotations/coordinates.rs b/compiler/pavexc/src/compiler/analyses/user_components/annotations/coordinates.rs index 9a9467ecf..66f5ae471 100644 --- a/compiler/pavexc/src/compiler/analyses/user_components/annotations/coordinates.rs +++ b/compiler/pavexc/src/compiler/analyses/user_components/annotations/coordinates.rs @@ -8,7 +8,7 @@ use crate::{ use pavexc_attr_parser::AnnotationProperties; use super::{ - AuxiliaryData, ConfigType, ImplInfo, annotated_item2type, cannot_resolve_callable_path, + AuxiliaryData, ConfigType, ImplInfo, annotated_item2type, callable_resolution_error, invalid_config_type, validate_route_path, }; use crate::compiler::analyses::user_components::{UserComponent, UserComponentId}; @@ -220,7 +220,7 @@ pub(crate) fn resolve_annotation_coordinates( let callable = match outcome { Ok(callable) => callable, Err(e) => { - cannot_resolve_callable_path( + callable_resolution_error( e.into(), component_id, aux, diff --git a/compiler/pavexc/src/compiler/analyses/user_components/annotations/diagnostic.rs b/compiler/pavexc/src/compiler/analyses/user_components/annotations/diagnostic.rs index 621f1ec58..7ca19f6e8 100644 --- a/compiler/pavexc/src/compiler/analyses/user_components/annotations/diagnostic.rs +++ b/compiler/pavexc/src/compiler/analyses/user_components/annotations/diagnostic.rs @@ -336,7 +336,7 @@ pub(super) fn invalid_config_type( .unwrap(); write!(&mut error_msg, ".").unwrap(); } - Some("Set the generic parameters to concrete types when registering the type as configuration. E.g. `bp.config(t!(crate::MyType))` for `struct MyType(T)`.".to_string()) + Some("Set the generic parameters to concrete types when registering the type as configuration.".to_string()) } } }; @@ -348,7 +348,7 @@ pub(super) fn invalid_config_type( diagnostics.push(diagnostic); } -pub(super) fn cannot_resolve_callable_path( +pub(super) fn callable_resolution_error( e: CallableResolutionError, id: UserComponentId, db: &AuxiliaryData, @@ -369,7 +369,7 @@ pub(super) fn cannot_resolve_callable_path( def.annotated_source }); let source = diagnostics.annotated( - TargetSpan::RawIdentifiers(&db.id2registration[id], kind), + db.registration_target(&id), format!("The {kind} was registered here"), ); let diagnostic = CompilerDiagnostic::builder(e.clone()) diff --git a/compiler/pavexc/src/compiler/analyses/user_components/annotations/mod.rs b/compiler/pavexc/src/compiler/analyses/user_components/annotations/mod.rs index f4ca76055..21751d439 100644 --- a/compiler/pavexc/src/compiler/analyses/user_components/annotations/mod.rs +++ b/compiler/pavexc/src/compiler/analyses/user_components/annotations/mod.rs @@ -6,7 +6,7 @@ mod diagnostic; pub(super) use coordinates::resolve_annotation_coordinates; use diagnostic::{ - cannot_resolve_callable_path, const_generics_are_not_supported, invalid_config_type, + callable_resolution_error, const_generics_are_not_supported, invalid_config_type, invalid_prebuilt_type, not_a_module, not_a_type_reexport, type_resolution_error, unknown_module_path, unresolved_external_reexport, }; @@ -187,7 +187,7 @@ pub(super) fn register_imported_components( let callable = match outcome { Ok(callable) => callable, Err(e) => { - cannot_resolve_callable_path( + callable_resolution_error( CallableResolutionError::from(e), user_component_id, aux, diff --git a/compiler/pavexc/src/compiler/analyses/user_components/router.rs b/compiler/pavexc/src/compiler/analyses/user_components/router.rs index c48c75d0f..ed6aa6217 100644 --- a/compiler/pavexc/src/compiler/analyses/user_components/router.rs +++ b/compiler/pavexc/src/compiler/analyses/user_components/router.rs @@ -643,10 +643,10 @@ impl PathRouter { // We are looking at a situation like the following: // // bp.nest_at("/path_prefix", { - // bp.fallback(f!(...)); + // bp.fallback(MY_FALLBACK); // bp.nest({ - // bp.route(GET, "/yo", f!(...)); - // bp.fallback(f!(...)); + // bp.route(MY_ROUTE); + // bp.fallback(MY_FALLBACK); // }); // }); // diff --git a/compiler/pavexc/src/diagnostic/mod.rs b/compiler/pavexc/src/diagnostic/mod.rs index b649d0977..00c8176a8 100644 --- a/compiler/pavexc/src/diagnostic/mod.rs +++ b/compiler/pavexc/src/diagnostic/mod.rs @@ -12,8 +12,8 @@ pub(crate) use pavex_cli_diagnostic::{ pub(crate) use proc_macro_utils::ProcMacroSpanExt; pub(crate) use registration::{Registration, RegistrationKind}; pub(crate) use registration_locations::{ - bp_new_span, config_key_span, domain_span, f_macro_span, imported_sources_span, - nest_blueprint_span, prefix_span, registration_span, route_path_span, + bp_new_span, config_key_span, domain_span, imported_sources_span, nest_blueprint_span, + prefix_span, registration_span, route_path_span, }; pub use sink::DiagnosticSink; pub(crate) use sink::TargetSpan; diff --git a/compiler/pavexc/src/diagnostic/registration_locations.rs b/compiler/pavexc/src/diagnostic/registration_locations.rs index f0fad97d5..34e25f68f 100644 --- a/compiler/pavexc/src/diagnostic/registration_locations.rs +++ b/compiler/pavexc/src/diagnostic/registration_locations.rs @@ -17,14 +17,16 @@ use crate::diagnostic::{ParsedSourceFile, ProcMacroSpanExt, convert_proc_macro_s /// For attributes, returns a span covering the attribute (e.g. `#[pavex::constructor]`) as well as the function/method /// signature. /// For blueprint registrations, returns a span pointing at the method argument that accepts the -/// raw identifiers for that component. +/// the component identifier. pub(crate) fn registration_span( source: &ParsedSourceFile, registration: &Registration, kind: ComponentKind, ) -> Option { match registration.kind { - RegistrationKind::Blueprint => f_macro_span(source, ®istration.location), + RegistrationKind::Blueprint => { + blueprint_registration_arg_span(source, ®istration.location) + } RegistrationKind::Attribute => attribute_span(source, ®istration.location, kind), } } @@ -141,26 +143,27 @@ pub(crate) fn impl_header_span( } /// Location, obtained via `#[track_caller]` and `std::panic::Location::caller`, points at the -/// `.` in the method invocation for `route` and `constructor`. +/// `.` in the method invocation for `route`, `constructor`, etc. /// E.g. /// /// ```rust,ignore -/// bp.route(GET, "/home", f!(crate::stream_file::)) +/// bp.route(PING) /// //^ `location` points here! /// ``` /// -/// We build a `SourceSpan` that matches the `f!` invocation. +/// We build a `SourceSpan` that matches the component argument. /// E.g. /// /// ```rust,ignore -/// bp.route(GET, "/home", f!(crate::stream_file::)) -/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -/// // We want a SourceSpan that points at this for routes -/// bp.constructor(f!(crate::extract_file), Lifecycle::Singleton) -/// // ^^^^^^^^^^^^^^^^^^^^^^^ -/// // We want a SourceSpan that points at this for constructors +/// bp.route(PING) +/// // ^^^^ <- we want this span +/// bp.constructor(NEW_LOGGER) +/// // ^^^^^^^^^^ <- we want this span /// ``` -pub(crate) fn f_macro_span(source: &ParsedSourceFile, location: &Location) -> Option { +pub(crate) fn blueprint_registration_arg_span( + source: &ParsedSourceFile, + location: &Location, +) -> Option { let raw_source = &source.contents; let node = find_method_call(location, &source.parsed)?; match node { @@ -235,7 +238,7 @@ pub(crate) fn f_macro_span(source: &ParsedSourceFile, location: &Location) -> Op _ => { tracing::trace!( node = ?node, - "We couldn't extract an f-macro invocation span from this function call node", + "We couldn't extract a registration argument span from this function call node", ); return None; } @@ -249,7 +252,7 @@ pub(crate) fn f_macro_span(source: &ParsedSourceFile, location: &Location) -> Op } tracing::trace!( node = ?node, - "We couldn't extract an f-macro invocation span from this function call node", + "We couldn't extract a component identifier span from this function call node", ); None } @@ -261,7 +264,7 @@ pub(crate) fn f_macro_span(source: &ParsedSourceFile, location: &Location) -> Op /// E.g. /// /// ```rust,ignore -/// bp.route(GET, "/home", f!(crate::stream_file::)) +/// bp.route(GET, "/home", handler) /// //^ `location` points here! /// ``` /// @@ -269,7 +272,7 @@ pub(crate) fn f_macro_span(source: &ParsedSourceFile, location: &Location) -> Op /// E.g. /// /// ```rust,ignore -/// bp.route(GET, "/home", f!(crate::stream_file::)) +/// bp.route(GET, "/home", handler) /// // ^^^^^^^ /// // We want a SourceSpan that points at this for routes /// ``` @@ -366,17 +369,17 @@ pub(crate) fn attribute_config_key_span( /// E.g. /// /// ```rust,ignore -/// bp.config("home", t!(crate::Streamer)) +/// bp.config(MY_CONFIG) /// //^ `location` points here! /// ``` /// -/// We build a `SourceSpan` that matches the key argument. +/// We build a `SourceSpan` that matches the config argument. /// E.g. /// /// ```rust,ignore -/// bp.config("home", t!(crate::Streamer)) -/// // ^^^^^^ -/// // We want a SourceSpan that points at this for routes +/// bp.config(MY_CONFIG) +/// // ^^^^^^^^^ +/// // We want a SourceSpan that points at this /// ``` pub(crate) fn bp_config_key_span( source: &ParsedSourceFile, diff --git a/compiler/pavexc/src/diagnostic/sink.rs b/compiler/pavexc/src/diagnostic/sink.rs index ce66ac335..6b3850cc5 100644 --- a/compiler/pavexc/src/diagnostic/sink.rs +++ b/compiler/pavexc/src/diagnostic/sink.rs @@ -9,8 +9,8 @@ use pavex_cli_diagnostic::AnnotatedSource; use super::{ ComponentKind, OptionalLabeledSpanExt, OptionalSourceSpanExt, ParsedSourceFile, Registration, - RegistrationKind, config_key_span, f_macro_span, imported_sources_span, - registration_locations::{attribute_span, impl_header_span, route_path_attr_span}, + RegistrationKind, config_key_span, imported_sources_span, + registration_locations::{impl_header_span, route_path_attr_span}, registration_span, route_path_span, }; @@ -115,13 +115,6 @@ impl DiagnosticSink { } }, TargetSpan::ConfigKeySpan(registration) => config_key_span(s.source(), registration), - TargetSpan::RawIdentifiers(registration, kind) => match registration.kind { - RegistrationKind::Attribute => { - // TODO: Refine the span to point at the specific attribute/property we care about. - attribute_span(s.source(), ®istration.location, kind) - } - RegistrationKind::Blueprint => f_macro_span(s.source(), ®istration.location), - }, TargetSpan::ImportedSources(registration) => { imported_sources_span(s.source(), ®istration.location) } @@ -144,12 +137,6 @@ pub enum TargetSpan<'a> { /// A span covering the config key argument specified when registering /// a configuration type. ConfigKeySpan(&'a Registration), - /// A span covering the raw identifiers for a component registration. - /// - /// This works for both blueprint invocations and macro attributes that - /// may include raw identifiers as arguments (e.g. an error handler specified - /// inside a `#[pavex::constructor]` attribute). - RawIdentifiers(&'a Registration, ComponentKind), } impl TargetSpan<'_> { @@ -162,8 +149,7 @@ impl TargetSpan<'_> { | RoutePath(registration) | Impl(registration) | ImportedSources(registration) - | ConfigKeySpan(registration) - | RawIdentifiers(registration, ..) => ®istration.location.file, + | ConfigKeySpan(registration) => ®istration.location.file, } } From 6075a8f8ceae3f3cea798c84baf353eebb223438 Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:10:25 +0100 Subject: [PATCH 2/2] chore: Index constants too --- rustdoc/rustdoc_processor/src/indexing/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rustdoc/rustdoc_processor/src/indexing/mod.rs b/rustdoc/rustdoc_processor/src/indexing/mod.rs index 0ad9212c1..60743da57 100644 --- a/rustdoc/rustdoc_processor/src/indexing/mod.rs +++ b/rustdoc/rustdoc_processor/src/indexing/mod.rs @@ -329,9 +329,10 @@ pub(crate) fn index_local_types<'a, V: IndexingVisitor>( | ItemEnum::Struct(_) | ItemEnum::TypeAlias(_) | ItemEnum::Static(_) - | ItemEnum::Union(_) => { + | ItemEnum::Union(_) + | ItemEnum::Constant { .. } => { let name = current_item.name.as_deref().expect( - "All 'struct', 'function', 'enum', 'type_alias', 'primitive', 'trait', 'static' and 'union' items have a 'name' property", + "All 'struct', 'function', 'enum', 'type_alias', 'primitive', 'trait', 'static', 'union' and 'constant' items have a 'name' property", ); if matches!(current_item.inner, ItemEnum::Primitive(_)) { // E.g. `std::bool` won't work, `std::primitive::bool` does work but the `primitive` module