diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 2ce728aad16..f1dbf8e5f73 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Preserve doc comments when using route macros. [#2022] + +[#2022]: https://github.com/actix/actix-web/pull/2022 ## 0.5.0-beta.1 - 2021-02-10 diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index ddbd42454c6..e3c5f036823 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -176,6 +176,9 @@ pub struct Route { args: Args, ast: syn::ItemFn, resource_type: ResourceType, + + /// The doc comment attributes to copy to generated struct, if any. + doc_attributes: Vec, } fn guess_resource_type(typ: &syn::Type) -> ResourceType { @@ -221,6 +224,18 @@ impl Route { let ast: syn::ItemFn = syn::parse(input)?; let name = ast.sig.ident.clone(); + // Try and pull out the doc comments so that we can reapply them to the + // generated struct. + // + // Note that multi line doc comments are converted to multiple doc + // attributes. + let doc_attributes = ast + .attrs + .iter() + .filter(|attr| attr.path.is_ident("doc")) + .cloned() + .collect(); + let args = Args::new(args, method)?; if args.methods.is_empty() { return Err(syn::Error::new( @@ -248,6 +263,7 @@ impl Route { args, ast, resource_type, + doc_attributes, }) } } @@ -265,6 +281,7 @@ impl ToTokens for Route { methods, }, resource_type, + doc_attributes, } = self; let resource_name = name.to_string(); let method_guards = { @@ -287,6 +304,7 @@ impl ToTokens for Route { }; let stream = quote! { + #(#doc_attributes)* #[allow(non_camel_case_types, missing_docs)] pub struct #name; diff --git a/actix-web-codegen/tests/trybuild.rs b/actix-web-codegen/tests/trybuild.rs index d2d8a38f5b5..afbe7b728c6 100644 --- a/actix-web-codegen/tests/trybuild.rs +++ b/actix-web-codegen/tests/trybuild.rs @@ -9,6 +9,8 @@ fn compile_macros() { t.compile_fail("tests/trybuild/route-missing-method-fail.rs"); t.compile_fail("tests/trybuild/route-duplicate-method-fail.rs"); t.compile_fail("tests/trybuild/route-unexpected-method-fail.rs"); + + t.pass("tests/trybuild/docstring-ok.rs"); } // #[rustversion::not(nightly)] diff --git a/actix-web-codegen/tests/trybuild/docstring-ok.rs b/actix-web-codegen/tests/trybuild/docstring-ok.rs new file mode 100644 index 00000000000..2910976c7be --- /dev/null +++ b/actix-web-codegen/tests/trybuild/docstring-ok.rs @@ -0,0 +1,17 @@ +use actix_web::{Responder, HttpResponse, App, test}; +use actix_web_codegen::*; + +/// Docstrings shouldn't break anything. +#[get("/")] +async fn index() -> impl Responder { + HttpResponse::Ok() +} + +#[actix_web::main] +async fn main() { + let srv = test::start(|| App::new().service(index)); + + let request = srv.get("/"); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); +}