Rocket

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 360b0e80b0025419927df48d13fb4b41f60502aa
parent b9bf1ee37de21f90f0ed1a64cde4ed611a1325ac
Author: Sergio Benitez <sb@sergio.bz>
Date:   Thu,  4 Oct 2018 21:44:42 -0700

Port all codegen tests to codegen_next.

Diffstat:
MCargo.toml | 1-
Mcontrib/codegen/Cargo.toml | 2+-
Mcontrib/lib/Cargo.toml | 3---
Mcontrib/lib/src/databases.rs | 15++++++---------
Mcontrib/lib/src/msgpack.rs | 10++++------
Mcontrib/lib/src/templates/metadata.rs | 5++---
Mcontrib/lib/tests/static_files.rs | 3+--
Mcontrib/lib/tests/templates.rs | 3+--
Dcore/codegen/Cargo.toml | 22----------------------
Dcore/codegen/build.rs | 58----------------------------------------------------------
Dcore/codegen/tests/compile-fail/absolute-mount-paths.rs | 13-------------
Dcore/codegen/tests/compile-fail/bad-attribute-form.rs | 13-------------
Dcore/codegen/tests/compile-fail/bad-attribute-param.rs | 12------------
Dcore/codegen/tests/compile-fail/bad-ident-argument.rs | 9---------
Dcore/codegen/tests/compile-fail/bad-value-types-in-attribute.rs | 20--------------------
Dcore/codegen/tests/compile-fail/decorate-enum.rs | 12------------
Dcore/codegen/tests/compile-fail/decorate-impl.rs | 12------------
Dcore/codegen/tests/compile-fail/decorate-struct.rs | 11-----------
Dcore/codegen/tests/compile-fail/decorate-trait.rs | 11-----------
Dcore/codegen/tests/compile-fail/empty-segments.rs | 28----------------------------
Dcore/codegen/tests/compile-fail/ignored_params.rs | 16----------------
Dcore/codegen/tests/compile-fail/malformed-media-type.rs | 38--------------------------------------
Dcore/codegen/tests/compile-fail/phantom-declared-param.rs | 17-----------------
Dcore/codegen/tests/compile-fail/unknown-attribute-param.rs | 12------------
Dcore/codegen/tests/compile-fail/unknown-media-type.rs | 17-----------------
Dcore/codegen/tests/compiletest.rs | 104-------------------------------------------------------------------------------
Dcore/codegen/tests/typed-uris.rs | 322-------------------------------------------------------------------------------
Dcore/codegen/tests/ui/bad-error-fn.stderr | 18------------------
Dcore/codegen/tests/ui/data-without-post.rs | 9---------
Dcore/codegen/tests/ui/data-without-post.stderr | 9---------
Dcore/codegen/tests/ui/malformed-param-list.rs | 29-----------------------------
Dcore/codegen/tests/ui/malformed-param-list.stderr | 72------------------------------------------------------------------------
Dcore/codegen/tests/ui/route-bad-method.rs | 19-------------------
Dcore/codegen/tests/ui/route-bad-method.stderr | 42------------------------------------------
Dcore/codegen/tests/ui/typed-uri-bad-type.rs | 31-------------------------------
Dcore/codegen/tests/ui/typed-uri-bad-type.stderr | 33---------------------------------
Dcore/codegen/tests/ui/typed-uris-bad-params.rs | 30------------------------------
Dcore/codegen/tests/ui/typed-uris-bad-params.stderr | 126-------------------------------------------------------------------------------
Dcore/codegen/tests/ui/typed-uris-invalid-syntax.rs | 21---------------------
Dcore/codegen/tests/ui/typed-uris-invalid-syntax.stderr | 62--------------------------------------------------------------
Mcore/codegen_next/Cargo.toml | 3+--
Mcore/codegen_next/src/attribute/route.rs | 43++++++++++++++++++++++++++++++++-----------
Mcore/codegen_next/src/attribute/segments.rs | 17++++++++++-------
Mcore/codegen_next/src/bang/uri.rs | 13+++++++------
Mcore/codegen_next/src/http_codegen.rs | 33++++++++++++++++++++-------------
Mcore/codegen_next/src/lib.rs | 233+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mcore/codegen_next/src/proc_macro_ext.rs | 55++++++++++++++++++++++++++++++-------------------------
Mcore/codegen_next/tests/compile-test.rs | 1-
Mcore/codegen_next/tests/responder.rs | 2++
Mcore/codegen_next/tests/route-format.rs | 21+++++++++++++++------
Mcore/codegen_next/tests/route.rs | 30++++++++++++++++++++++++++++++
Acore/codegen_next/tests/typed-uris.rs | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-attribute-general-syntax.rs | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-attribute-general-syntax.stderr | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-path-bad-syntax.rs | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-path-bad-syntax.stderr | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-type-errors.rs | 33+++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-type-errors.stderr | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-warnings.rs | 26++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/route-warnings.stderr | 42++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uri-bad-type.rs | 29+++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uri-bad-type.stderr | 33+++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uris-bad-params.rs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uris-bad-params.stderr | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.rs | 19+++++++++++++++++++
Acore/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.stderr | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcore/codegen_next/tests/ui-fail/update-references.sh | 2+-
Mcore/http/Cargo.toml | 1-
Mcore/http/src/method.rs | 10+++++++++-
Mcore/http/src/route.rs | 3++-
Mcore/http/src/uri/uri_display.rs | 7++++---
Mcore/lib/Cargo.toml | 1-
Mcore/lib/src/lib.rs | 9+++------
Mcore/lib/src/request/form/from_form.rs | 12+++++-------
Mcore/lib/src/request/request.rs | 10+++++++---
Mcore/lib/src/router/collider.rs | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mexamples/config/tests/development.rs | 3+--
Mexamples/config/tests/production.rs | 3+--
Mexamples/config/tests/staging.rs | 3+--
Mexamples/redirect/Cargo.toml | 1-
Mexamples/redirect/src/main.rs | 3+--
Mexamples/session/Cargo.toml | 1-
Mexamples/session/src/main.rs | 3+--
Mexamples/tera_templates/Cargo.toml | 1-
Mexamples/tera_templates/src/main.rs | 3+--
Mscripts/config.sh | 6+++---
Msite/guide/getting-started.md | 5++---
Msite/guide/overview.md | 15+++++++--------
Msite/guide/pastebin.md | 5++---
Msite/index.toml | 5++---
90 files changed, 2167 insertions(+), 1531 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -4,7 +4,6 @@ codegen-units = 4 [workspace] members = [ "core/lib/", - "core/codegen/", "core/codegen_next/", "core/http/", "contrib/lib", diff --git a/contrib/codegen/Cargo.toml b/contrib/codegen/Cargo.toml @@ -21,7 +21,7 @@ proc-macro = true [dependencies.derive_utils] git = "https://github.com/SergioBenitez/derive-utils" -rev = "f14fb4bc855" +rev = "62f361f" [dependencies] quote = "0.6" diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml @@ -68,9 +68,6 @@ r2d2_redis = { version = "0.8", optional = true } # Contrib codegen dependencies rocket_contrib_codegen = { path = "../codegen", optional = true } -[dev-dependencies] -rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" } - [target.'cfg(debug_assertions)'.dependencies] notify = { version = "^4.0" } diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs @@ -74,10 +74,9 @@ //! Whenever a connection to the database is needed: //! //! ```rust -//! # #![feature(plugin, decl_macro)] -//! # #![plugin(rocket_codegen)] +//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] //! # -//! # extern crate rocket; +//! # #[macro_use] extern crate rocket; //! # extern crate rocket_contrib; //! # //! # use rocket_contrib::databases::{database, diesel}; @@ -263,10 +262,9 @@ //! connection to a given database: //! //! ```rust -//! # #![feature(plugin, decl_macro)] -//! # #![plugin(rocket_codegen)] +//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] //! # -//! # extern crate rocket; +//! # #[macro_use] extern crate rocket; //! # extern crate rocket_contrib; //! # use rocket_contrib::databases::{database, diesel}; //! #[database("my_db")] @@ -285,10 +283,9 @@ //! connection type: //! //! ```rust -//! # #![feature(plugin, decl_macro)] -//! # #![plugin(rocket_codegen)] +//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] //! # -//! # extern crate rocket; +//! # #[macro_use] extern crate rocket; //! # extern crate rocket_contrib; //! # use rocket_contrib::databases::{database, diesel}; //! # diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs @@ -27,9 +27,8 @@ pub use self::rmp_serde::decode::Error as MsgPackError; /// HTTP request body. /// /// ```rust -/// # #![feature(plugin, decl_macro)] -/// # #![plugin(rocket_codegen)] -/// # extern crate rocket; +/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] +/// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; /// # fn main() { } @@ -55,9 +54,8 @@ pub use self::rmp_serde::decode::Error as MsgPackError; /// is set to `application/msgpack` automatically. /// /// ```rust -/// # #![feature(plugin, decl_macro)] -/// # #![plugin(rocket_codegen)] -/// # extern crate rocket; +/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] +/// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; /// # fn main() { } diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs @@ -30,9 +30,8 @@ use super::ContextManager; /// can be used as a request guard in any request handler. /// /// ```rust -/// # #![feature(plugin, decl_macro)] -/// # #![plugin(rocket_codegen)] -/// # extern crate rocket; +/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] +/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// # fn main() { } /// # diff --git a/contrib/lib/tests/static_files.rs b/contrib/lib/tests/static_files.rs @@ -1,5 +1,4 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] extern crate rocket; extern crate rocket_contrib; diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs @@ -1,5 +1,4 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; extern crate rocket_contrib; diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml @@ -1,22 +0,0 @@ -[package] -name = "rocket_codegen" -version = "0.4.0-dev" -authors = ["Sergio Benitez <sb@sergio.bz>"] -description = "Code generation for the Rocket web framework." -documentation = "https://api.rocket.rs/rocket_codegen/" -homepage = "https://rocket.rs" -repository = "https://github.com/SergioBenitez/Rocket" -readme = "../../README.md" -keywords = ["rocket", "web", "framework", "code", "generation"] -license = "MIT/Apache-2.0" -build = "build.rs" - -[dependencies] - -[dev-dependencies] -compiletest_rs = "0.3.14" -rocket = { version = "0.4.0-dev", path = "../lib" } - -[build-dependencies] -yansi = "0.4" -version_check = "0.1.3" diff --git a/core/codegen/build.rs b/core/codegen/build.rs @@ -1,58 +0,0 @@ -//! This tiny build script ensures that rocket_codegen is not compiled with an -//! incompatible version of rust. - -extern crate yansi; -extern crate version_check; - -use yansi::Color::{Red, Yellow, Blue, White}; -use version_check::{supports_features, is_min_version, is_min_date}; - -// Specifies the minimum nightly version needed to compile Rocket's codegen. -const MIN_DATE: &'static str = "2018-08-23"; -const MIN_VERSION: &'static str = "1.30.0-nightly"; - -fn main() { - let ok_channel = supports_features(); - let ok_version = is_min_version(MIN_VERSION); - let ok_date = is_min_date(MIN_DATE); - - let print_version_err = |version: &str, date: &str| { - eprintln!("{} {}. {} {}.", - White.paint("Installed version is:"), - Yellow.paint(format!("{} ({})", version, date)), - White.paint("Minimum required:"), - Yellow.paint(format!("{} ({})", MIN_VERSION, MIN_DATE))); - }; - - match (ok_channel, ok_version, ok_date) { - (Some(ok_channel), Some((ok_version, version)), Some((ok_date, date))) => { - if !ok_channel { - eprintln!("{} {}", - Red.paint("Error:").bold(), - White.paint("Rocket requires a nightly or dev version of Rust.")); - print_version_err(&*version, &*date); - eprintln!("{}{}{}", - Blue.paint("See the getting started guide ("), - White.paint("https://rocket.rs/guide/getting-started/"), - Blue.paint(") for more information.")); - panic!("Aborting compilation due to incompatible compiler.") - } - - if !ok_version || !ok_date { - eprintln!("{} {}", - Red.paint("Error:").bold(), - White.paint("Rocket codegen requires a more recent version of rustc.")); - eprintln!("{}{}{}", - Blue.paint("Use `"), - White.paint("rustup update"), - Blue.paint("` or your preferred method to update Rust.")); - print_version_err(&*version, &*date); - panic!("Aborting compilation due to incompatible compiler.") - } - }, - _ => { - println!("cargo:warning={}", "Rocket was unable to check rustc compatibility."); - println!("cargo:warning={}", "Build may fail due to incompatible rustc version."); - } - } -} diff --git a/core/codegen/tests/compile-fail/absolute-mount-paths.rs b/core/codegen/tests/compile-fail/absolute-mount-paths.rs @@ -1,13 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -#[get("a")] //~ ERROR invalid -fn get() -> &'static str { "hi" } - -#[get("")] //~ ERROR invalid -fn get1(id: usize) -> &'static str { "hi" } - -#[get("a/b/c")] //~ ERROR invalid -fn get2(id: usize) -> &'static str { "hi" } - -fn main() { } diff --git a/core/codegen/tests/compile-fail/bad-attribute-form.rs b/core/codegen/tests/compile-fail/bad-attribute-form.rs @@ -1,13 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get] //~ ERROR incorrect use of attribute -//~^ ERROR malformed attribute -fn get() -> &'static str { "hi" } - -fn main() { - let _ = routes![get]; -} - diff --git a/core/codegen/tests/compile-fail/bad-attribute-param.rs b/core/codegen/tests/compile-fail/bad-attribute-param.rs @@ -1,12 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get(path = "/hello", 123)] //~ ERROR expected -fn get() -> &'static str { "hi" } - -fn main() { - let _ = routes![get]; -} - diff --git a/core/codegen/tests/compile-fail/bad-ident-argument.rs b/core/codegen/tests/compile-fail/bad-ident-argument.rs @@ -1,9 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("/")] -fn get(_: usize) -> &'static str { "hi" } //~ ERROR argument - -fn main() { } diff --git a/core/codegen/tests/compile-fail/bad-value-types-in-attribute.rs b/core/codegen/tests/compile-fail/bad-value-types-in-attribute.rs @@ -1,20 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get(1)] //~ ERROR expected `path = string` -fn get0() -> &'static str { "hi" } - -#[get(path = 1)] //~ ERROR must be a string -fn get1() -> &'static str { "hi" } - -#[get(path = "/", rank = "2")] //~ ERROR must be an int -fn get2() -> &'static str { "hi" } - -#[get(path = "/", format = 100)] //~ ERROR must be a "media/type" -fn get3() -> &'static str { "hi" } - -fn main() { -} - diff --git a/core/codegen/tests/compile-fail/decorate-enum.rs b/core/codegen/tests/compile-fail/decorate-enum.rs @@ -1,12 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("")] //~ ERROR can only be used on functions -enum B { } //~ ERROR but was applied - -fn main() { - let _ = routes![get]; -} - diff --git a/core/codegen/tests/compile-fail/decorate-impl.rs b/core/codegen/tests/compile-fail/decorate-impl.rs @@ -1,12 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("")] //~ ERROR can only be used on functions -impl C for A { } //~ ERROR but was applied - -fn main() { - let _ = routes![get]; -} - diff --git a/core/codegen/tests/compile-fail/decorate-struct.rs b/core/codegen/tests/compile-fail/decorate-struct.rs @@ -1,11 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("")] //~ ERROR can only be used on functions -struct A; //~ ERROR but was applied - -fn main() { - let _ = routes![get]; -} diff --git a/core/codegen/tests/compile-fail/decorate-trait.rs b/core/codegen/tests/compile-fail/decorate-trait.rs @@ -1,11 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("")] //~ ERROR can only be used on functions -trait C { } //~ ERROR but was applied - -fn main() { - let _ = routes![get]; -} diff --git a/core/codegen/tests/compile-fail/empty-segments.rs b/core/codegen/tests/compile-fail/empty-segments.rs @@ -1,28 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -#[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments -fn get() -> &'static str { "hi" } - -#[get("//")] //~ ERROR paths cannot contain empty segments -fn get1(name: &str) -> &'static str { "hi" } - -#[get("/a/")] //~ ERROR paths cannot contain empty segments -fn get2(name: &str) -> &'static str { "hi" } - -#[get("////")] //~ ERROR paths cannot contain empty segments -fn get3() -> &'static str { "hi" } - -#[get("/a///")] //~ ERROR paths cannot contain empty segments -fn get4() -> &'static str { "hi" } - -#[get("/a/b//")] //~ ERROR paths cannot contain empty segments -fn get5() -> &'static str { "hi" } - -#[get("/a/b/c/")] //~ ERROR paths cannot contain empty segments -fn get6() -> &'static str { "hi" } - -#[get("/a/b/c/d//e/")] //~ ERROR paths cannot contain empty segments -fn get7() -> &'static str { "hi" } - -fn main() { } diff --git a/core/codegen/tests/compile-fail/ignored_params.rs b/core/codegen/tests/compile-fail/ignored_params.rs @@ -1,16 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -#[get("/<name>")] //~ ERROR unused dynamic parameter: `name` -fn get(other: usize) -> &'static str { "hi" } //~ NOTE expected - -#[get("/a?<r>")] //~ ERROR unused dynamic parameter: `r` -fn get1() -> &'static str { "hi" } //~ NOTE expected - -#[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter: `test` -fn post() -> &'static str { "hi" } //~ NOTE expected - -#[get("/<_r>")] //~ ERROR unused dynamic parameter: `_r` -fn get2(r: usize) -> &'static str { "hi" } //~ NOTE expected - -fn main() { } diff --git a/core/codegen/tests/compile-fail/malformed-media-type.rs b/core/codegen/tests/compile-fail/malformed-media-type.rs @@ -1,38 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("/", format = "applicationx-custom")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn one() -> &'static str { "hi" } - -#[get("/", format = "")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn two() -> &'static str { "hi" } - -#[get("/", format = "//")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn three() -> &'static str { "hi" } - -#[get("/", format = "/")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn four() -> &'static str { "hi" } - -#[get("/", format = "a/")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn five() -> &'static str { "hi" } - -#[get("/", format = "/a")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn six() -> &'static str { "hi" } - -#[get("/", format = "/a/")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn seven() -> &'static str { "hi" } - -#[get("/", format = "a/b/")] //~ ERROR malformed -//~^ ERROR `format` must be a "media/type" -fn eight() -> &'static str { "hi" } - -fn main() { } diff --git a/core/codegen/tests/compile-fail/phantom-declared-param.rs b/core/codegen/tests/compile-fail/phantom-declared-param.rs @@ -1,17 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -#[get("/<param>")] //~ ERROR unused dynamic parameter: `param` -fn get() { } //~ NOTE expected - -#[get("/<a>")] //~ ERROR unused dynamic parameter: `a` -fn get2() { } //~ NOTE expected - -#[get("/a/b/c/<a>/<b>")] - //~^ ERROR unused dynamic parameter: `a` - //~^^ ERROR unused dynamic parameter: `b` -fn get32() { } - //~^ NOTE expected - //~^^ NOTE expected - -fn main() { } diff --git a/core/codegen/tests/compile-fail/unknown-attribute-param.rs b/core/codegen/tests/compile-fail/unknown-attribute-param.rs @@ -1,12 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get(path = "/hello", unknown = 123)] //~ ERROR 'unknown' is not a known param -fn get() -> &'static str { "hi" } - -fn main() { - let _ = routes![get]; -} - diff --git a/core/codegen/tests/compile-fail/unknown-media-type.rs b/core/codegen/tests/compile-fail/unknown-media-type.rs @@ -1,17 +0,0 @@ -// must-compile-successfully - -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("/", format = "application/x-custom")] //~ WARNING not a known media type -fn one() -> &'static str { "hi" } - -#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type -fn two() -> &'static str { "hi" } - -#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type -fn three() -> &'static str { "hi" } - -fn main() { } diff --git a/core/codegen/tests/compiletest.rs b/core/codegen/tests/compiletest.rs @@ -1,104 +0,0 @@ -extern crate compiletest_rs as compiletest; - -use std::path::{Path, PathBuf}; -use std::{io, fs::Metadata, time::SystemTime}; - -#[derive(Copy, Clone)] -enum Kind { - Dynamic, Static -} - -impl Kind { - fn extension(self) -> &'static str { - match self { - #[cfg(windows)] Kind::Dynamic => ".dll", - #[cfg(all(unix, target_os = "macos"))] Kind::Dynamic => ".dylib", - #[cfg(all(unix, not(target_os = "macos")))] Kind::Dynamic => ".so", - Kind::Static => ".rlib" - } - } - - fn prefix(self) -> &'static str { - #[cfg(windows)] { "" } - #[cfg(not(windows))] { "lib" } - } -} - -fn target_path() -> PathBuf { - #[cfg(debug_assertions)] const ENVIRONMENT: &str = "debug"; - #[cfg(not(debug_assertions))] const ENVIRONMENT: &str = "release"; - - Path::new(env!("CARGO_MANIFEST_DIR")) - .parent().unwrap().parent().unwrap() - .join("target") - .join(ENVIRONMENT) -} - -fn link_flag(flag: &str, lib: &str, rel_path: &[&str]) -> String { - let mut path = target_path(); - for component in rel_path { - path = path.join(component); - } - - format!("{} {}={}", flag, lib, path.display()) -} - -fn best_time_for(metadata: &Metadata) -> SystemTime { - metadata.created() - .or_else(|_| metadata.modified()) - .or_else(|_| metadata.accessed()) - .unwrap_or_else(|_| SystemTime::now()) -} - -fn extern_dep(name: &str, kind: Kind) -> io::Result<String> { - let deps_root = target_path().join("deps"); - let dep_name = format!("{}{}", kind.prefix(), name); - - let mut dep_path: Option<PathBuf> = None; - for entry in deps_root.read_dir().expect("read_dir call failed") { - let entry = match entry { - Ok(entry) => entry, - Err(_) => continue - }; - - let filename = entry.file_name(); - let filename = filename.to_string_lossy(); - let lib_name = filename.split('.').next().unwrap().split('-').next().unwrap(); - - if lib_name == dep_name && filename.ends_with(kind.extension()) { - if let Some(ref mut existing) = dep_path { - if best_time_for(&entry.metadata()?) > best_time_for(&existing.metadata()?) { - *existing = entry.path().into(); - } - } else { - dep_path = Some(entry.path().into()); - } - } - } - - let dep = dep_path.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?; - let filename = dep.file_name().ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?; - Ok(link_flag("--extern", name, &["deps", &filename.to_string_lossy()])) -} - -fn run_mode(mode: &'static str) { - let mut config = compiletest::Config::default(); - config.mode = mode.parse().expect("Invalid mode"); - config.src_base = format!("tests/{}", mode).into(); - config.clean_rmeta(); - - config.target_rustcflags = Some([ - link_flag("-L", "crate", &[]), - link_flag("-L", "dependency", &["deps"]), - extern_dep("rocket_codegen", Kind::Dynamic).expect("find codegen dep"), - extern_dep("rocket", Kind::Static).expect("find core dep") - ].join(" ")); - - compiletest::run_tests(&config); -} - -#[test] -fn compile_test() { - run_mode("compile-fail"); - run_mode("ui"); -} diff --git a/core/codegen/tests/typed-uris.rs b/core/codegen/tests/typed-uris.rs @@ -1,322 +0,0 @@ -#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)] - -#![allow(dead_code, unused_variables)] - -#[macro_use] extern crate rocket; - -use std::fmt; -use std::path::PathBuf; - -use rocket::http::{RawStr, Cookies}; -use rocket::http::uri::{Origin, UriDisplay, FromUriParam}; -use rocket::request::Form; - -#[derive(FromForm)] -struct User<'a> { - name: &'a RawStr, - nickname: String, -} - -// TODO: Make this deriveable. -impl<'a> UriDisplay for User<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "name={}&nickname={}", - &self.name.replace(' ', "+") as &UriDisplay, - &self.nickname.replace(' ', "+") as &UriDisplay) - } -} - -impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> { - type Target = User<'a>; - fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> { - User { name: name.into(), nickname: nickname.to_string() } - } -} - -// This one has no `UriDisplay`. It exists to ensure that this file still -// compiles even though it's used a URI parameter's type. As long as a user -// doesn't request a URI from that route, things should be okay. -#[derive(FromForm)] -struct Second { - nickname: String, -} - -#[post("/<id>")] -fn simple(id: i32) -> &'static str { "" } - -#[post("/<id>/<name>")] -fn simple2(id: i32, name: String) -> &'static str { "" } - -#[post("/<id>/<name>")] -fn simple2_flipped(name: String, id: i32) -> &'static str { "" } - -#[post("/<used>/<_unused>")] -fn unused_param(used: i32, _unused: i32) -> &'static str { "" } - -#[post("/<id>")] -fn guard_1(cookies: Cookies, id: i32) -> &'static str { "" } - -#[post("/<id>/<name>")] -fn guard_2(name: String, cookies: Cookies, id: i32) -> &'static str { "" } - -#[post("/a/<id>/hi/<name>/hey")] -fn guard_3(id: i32, name: String, cookies: Cookies) -> &'static str { "" } - -#[post("/<id>", data = "<form>")] -fn no_uri_display_okay(id: i32, form: Form<Second>) -> &'static str { - "Typed URI testing." -} - -#[post("/<name>?<query..>", data = "<user>", rank = 2)] -fn complex<'r>( - name: &RawStr, - query: Form<User<'r>>, - user: Form<User<'r>>, - cookies: Cookies -) -> &'static str { "" } - -#[post("/a/<path..>")] -fn segments(path: PathBuf) -> &'static str { "" } - -#[post("/a/<id>/then/<path..>")] -fn param_and_segments(path: PathBuf, id: usize) -> &'static str { "" } - -#[post("/a/<id>/then/<path..>")] -fn guarded_segments(cookies: Cookies, path: PathBuf, id: usize) -> &'static str { "" } - -macro assert_uri_eq($($uri:expr => $expected:expr,)+) { - $(assert_eq!($uri, Origin::parse($expected).expect("valid origin URI"));)+ -} - -#[test] -fn check_simple_unnamed() { - assert_uri_eq! { - uri!(simple: 100) => "/100", - uri!(simple: -23) => "/-23", - uri!(unused_param: 1, 2) => "/1/2", - } - - // The "flipped" test ensures that the order of parameters depends on the - // route's URI, not on the order in the function signature. - assert_uri_eq! { - uri!(simple2: 100, "hello".to_string()) => "/100/hello", - uri!(simple2: 1349, "hey".to_string()) => "/1349/hey", - uri!(simple2_flipped: 100, "hello".to_string()) => "/100/hello", - } - - // Ensure that `.from_uri_param()` is called. - assert_uri_eq! { - uri!(simple2: 100, "hello") => "/100/hello", - uri!(simple2_flipped: 1349, "hey") => "/1349/hey", - } - - // Ensure that the `UriDisplay` trait is being used. - assert_uri_eq! { - uri!(simple2: 100, "hello there") => "/100/hello%20there", - uri!(simple2_flipped: 100, "hello there") => "/100/hello%20there", - } -} - -#[test] -fn check_simple_named() { - assert_uri_eq! { - uri!(simple: id = 100) => "/100", - uri!(simple: id = -23) => "/-23", - uri!(unused_param: used = 1, _unused = 2) => "/1/2", - } - - assert_uri_eq! { - uri!(simple2: id = 100, name = "hello".to_string()) => "/100/hello", - uri!(simple2: name = "hi".to_string(), id = 123) => "/123/hi", - uri!(simple2_flipped: id = 1349, name = "hey".to_string()) => "/1349/hey", - uri!(simple2_flipped: name = "hello".to_string(), id = 100) => "/100/hello", - } - - // Ensure that `.from_uri_param()` is called. - assert_uri_eq! { - uri!(simple2: id = 100, name = "hello") => "/100/hello", - uri!(simple2: id = 100, name = "hi") => "/100/hi", - uri!(simple2: id = 1349, name = "hey") => "/1349/hey", - uri!(simple2: name = "hello", id = 100) => "/100/hello", - uri!(simple2: name = "hi", id = 100) => "/100/hi", - uri!(simple2_flipped: id = 1349, name = "hey") => "/1349/hey", - } - - // Ensure that the `UriDisplay` trait is being used. - assert_uri_eq! { - uri!(simple2: id = 100, name = "hello there") => "/100/hello%20there", - uri!(simple2: name = "hello there", id = 100) => "/100/hello%20there", - uri!(simple2_flipped: id = 100, name = "hello there") => "/100/hello%20there", - uri!(simple2_flipped: name = "hello there", id = 100) => "/100/hello%20there", - } -} - -#[test] -fn check_mount_point() { - assert_uri_eq! { - uri!("/mount", simple: 100) => "/mount/100", - uri!("/mount", simple: id = 23) => "/mount/23", - uri!("/another", simple: 100) => "/another/100", - uri!("/another", simple: id = 23) => "/another/23", - } - - assert_uri_eq! { - uri!("/a", simple2: 100, "hey") => "/a/100/hey", - uri!("/b", simple2: id = 23, name = "hey") => "/b/23/hey", - } -} - -#[test] -fn check_guards_ignored() { - assert_uri_eq! { - uri!("/mount", guard_1: 100) => "/mount/100", - uri!("/mount", guard_2: 2938, "boo") => "/mount/2938/boo", - uri!("/mount", guard_3: 340, "Bob") => "/mount/a/340/hi/Bob/hey", - uri!(guard_1: 100) => "/100", - uri!(guard_2: 2938, "boo") => "/2938/boo", - uri!(guard_3: 340, "Bob") => "/a/340/hi/Bob/hey", - uri!("/mount", guard_1: id = 100) => "/mount/100", - uri!("/mount", guard_2: id = 2938, name = "boo") => "/mount/2938/boo", - uri!("/mount", guard_3: id = 340, name = "Bob") => "/mount/a/340/hi/Bob/hey", - uri!(guard_1: id = 100) => "/100", - uri!(guard_2: name = "boo", id = 2938) => "/2938/boo", - uri!(guard_3: name = "Bob", id = 340) => "/a/340/hi/Bob/hey", - } -} - -#[test] -fn check_with_segments() { - assert_uri_eq! { - uri!(segments: PathBuf::from("one/two/three")) => "/a/one/two/three", - uri!(segments: path = PathBuf::from("one/two/three")) => "/a/one/two/three", - uri!("/c", segments: PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/", - uri!("/c", segments: path = PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/", - uri!(segments: PathBuf::from("one/ tw?o/")) => "/a/one/%20tw%3Fo/", - uri!(param_and_segments: 10, PathBuf::from("a/b")) => "/a/10/then/a/b", - uri!(param_and_segments: id = 10, path = PathBuf::from("a/b")) - => "/a/10/then/a/b", - uri!(guarded_segments: 10, PathBuf::from("a/b")) => "/a/10/then/a/b", - uri!(guarded_segments: id = 10, path = PathBuf::from("a/b")) - => "/a/10/then/a/b", - } - - // Now check the `from_uri_param()` conversions for `PathBuf`. - assert_uri_eq! { - uri!(segments: "one/two/three") => "/a/one/two/three", - uri!("/oh", segments: path = "one/two/three") => "/oh/a/one/two/three", - uri!(segments: "one/ tw?o/") => "/a/one/%20tw%3Fo/", - uri!(param_and_segments: id = 10, path = "a/b") => "/a/10/then/a/b", - uri!(guarded_segments: 10, "a/b") => "/a/10/then/a/b", - uri!(guarded_segments: id = 10, path = "a/b") => "/a/10/then/a/b", - } -} - -#[test] -fn check_complex() { - assert_uri_eq! { - uri!(complex: "no idea", ("A B C", "a c")) => "/no%20idea?name=A+B+C&nickname=a+c", - uri!(complex: "Bob", User { name: "Robert".into(), nickname: "Bob".into() }) - => "/Bob?name=Robert&nickname=Bob", - uri!(complex: "Bob", &User { name: "Robert".into(), nickname: "Bob".into() }) - => "/Bob?name=Robert&nickname=Bob", - uri!(complex: "no idea", User { name: "Robert Mike".into(), nickname: "Bob".into() }) - => "/no%20idea?name=Robert+Mike&nickname=Bob", - uri!("/some/path", complex: "no idea", ("A B C", "a c")) - => "/some/path/no%20idea?name=A+B+C&nickname=a+c", - uri!(complex: name = "Bob", query = &User { name: "Robert".into(), nickname: "Bob".into() }) - => "/Bob?name=Robert&nickname=Bob", - uri!(complex: query = User { name: "Robert".into(), nickname: "Bob".into() }, name = "Bob") - => "/Bob?name=Robert&nickname=Bob", - uri!(complex: name = "no idea", query = ("A B C", "a c")) - => "/no%20idea?name=A+B+C&nickname=a+c", - uri!(complex: query = ("A B C", "a c"), name = "no idea") - => "/no%20idea?name=A+B+C&nickname=a+c", - uri!("/hey", complex: name = "no idea", query = ("A B C", "a c")) - => "/hey/no%20idea?name=A+B+C&nickname=a+c", - } - - // Ensure variables are correctly processed. - let user = User { name: "Robert".into(), nickname: "Bob".into() }; - assert_uri_eq! { - uri!(complex: "complex", &user) => "/complex?name=Robert&nickname=Bob", - uri!(complex: "complex", user) => "/complex?name=Robert&nickname=Bob", - } -} - -#[test] -fn check_location_promotion() { - struct S1(String); - struct S2 { name: String }; - - let s1 = S1("Bob".into()); - let s2 = S2 { name: "Bob".into() }; - - assert_uri_eq! { - uri!(simple2: 1, &S1("A".into()).0) => "/1/A", - uri!(simple2: 1, S1("A".into()).0) => "/1/A", - uri!(simple2: 1, &S2 { name: "A".into() }.name) => "/1/A", - uri!(simple2: 1, S2 { name: "A".into() }.name) => "/1/A", - uri!(simple2: 1, &s1.0) => "/1/Bob", - uri!(simple2: 1, &s2.name) => "/1/Bob", - uri!(simple2: 2, &s1.0) => "/2/Bob", - uri!(simple2: 2, &s2.name) => "/2/Bob", - uri!(simple2: 2, s1.0) => "/2/Bob", - uri!(simple2: 2, s2.name) => "/2/Bob", - } - - let mut s1 = S1("Bob".into()); - let mut s2 = S2 { name: "Bob".into() }; - assert_uri_eq! { - uri!(simple2: 1, &mut S1("A".into()).0) => "/1/A", - uri!(simple2: 1, S1("A".into()).0) => "/1/A", - uri!(simple2: 1, &mut S2 { name: "A".into() }.name) => "/1/A", - uri!(simple2: 1, S2 { name: "A".into() }.name) => "/1/A", - uri!(simple2: 1, &mut s1.0) => "/1/Bob", - uri!(simple2: 1, &mut s2.name) => "/1/Bob", - uri!(simple2: 2, &mut s1.0) => "/2/Bob", - uri!(simple2: 2, &mut s2.name) => "/2/Bob", - uri!(simple2: 2, s1.0) => "/2/Bob", - uri!(simple2: 2, s2.name) => "/2/Bob", - } -} - -#[test] -fn check_scoped() { - assert_uri_eq!{ - uri!(typed_uris::simple: 100) => "/typed_uris/100", - uri!(typed_uris::simple: id = 100) => "/typed_uris/100", - uri!(typed_uris::deeper::simple: 100) => "/typed_uris/deeper/100", - } -} - -mod typed_uris { - use super::assert_uri_eq; - - #[post("/typed_uris/<id>")] - fn simple(id: i32) -> &'static str { "" } - - #[test] - fn check_simple_scoped() { - assert_uri_eq! { - uri!(simple: id = 100) => "/typed_uris/100", - uri!(::simple: id = 100) => "/100", - uri!("/mount", ::simple: id = 100) => "/mount/100", - uri!(::simple2: id = 100, name = "hello") => "/100/hello", - } - } - - pub mod deeper { - use super::assert_uri_eq; - - #[post("/typed_uris/deeper/<id>")] - fn simple(id: i32) -> &'static str { "" } - - #[test] - fn check_deep_scoped() { - assert_uri_eq! { - uri!(super::simple: id = 100) => "/typed_uris/100", - uri!(::simple: id = 100) => "/100", - } - } - } -} diff --git a/core/codegen/tests/ui/bad-error-fn.stderr b/core/codegen/tests/ui/bad-error-fn.stderr @@ -1,18 +0,0 @@ -error: error catchers can have at most 2 arguments - --> $DIR/bad-error-fn.rs:9:1 - | -9 | fn err_a(_a: Error, _b: Request, _c: Error) -> &'static str { "hi" } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: error catchers can take either a `rocket::Error`, `rocket::Request` type, or both. - -error: unknown error catcher argument - --> $DIR/bad-error-fn.rs:12:14 - | -12 | fn err_b(_a: (isize, usize)) -> &'static str { "hi" } - | ^^^^^^^^^^^^^^ - | - = help: error catchers can take either a `rocket::Error`, `rocket::Request` type, or both. - -error: aborting due to 2 previous errors - diff --git a/core/codegen/tests/ui/data-without-post.rs b/core/codegen/tests/ui/data-without-post.rs @@ -1,9 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[get("/", data = "<something>")] -fn get(something: rocket::Data) -> &'static str { "hi" } - -fn main() { } diff --git a/core/codegen/tests/ui/data-without-post.stderr b/core/codegen/tests/ui/data-without-post.stderr @@ -1,9 +0,0 @@ -warning: `data` route parameter used with non-payload-supporting method - --> $DIR/data-without-post.rs:6:12 - | -6 | #[get("/", data = "<something>")] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: 'GET' does not typically support payloads - = note: the 'format' attribute parameter will match against the 'Accept' header - diff --git a/core/codegen/tests/ui/malformed-param-list.rs b/core/codegen/tests/ui/malformed-param-list.rs @@ -1,29 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -#[get("/><")] -fn get() -> &'static str { "hi" } - -#[get("/<id><")] -fn get1(id: usize) -> &'static str { "hi" } - -#[get("/<<<<id><")] -fn get2(id: usize) -> &'static str { "hi" } - -#[get("/<!>")] -fn get3() -> &'static str { "hi" } - -#[get("/<_>")] -fn get4() -> &'static str { "hi" } - -#[get("/<1>")] -fn get5() -> &'static str { "hi" } - -#[get("/<>name><")] -fn get6() -> &'static str { "hi" } - -#[get("/<name>:<id>")] -fn get7() -> &'static str { "hi" } - -#[get("/<>")] -fn get8() -> &'static str { "hi" } diff --git a/core/codegen/tests/ui/malformed-param-list.stderr b/core/codegen/tests/ui/malformed-param-list.stderr @@ -1,72 +0,0 @@ -error: malformed parameter - --> $DIR/malformed-param-list.rs:4:9 - | -4 | #[get("/><")] - | ^^ - | - = help: parameters must be of the form '<param>' - -error: malformed parameter - --> $DIR/malformed-param-list.rs:7:9 - | -7 | #[get("/<id><")] - | ^^^^^ - | - = help: parameters must be of the form '<param>' - -error: malformed parameter - --> $DIR/malformed-param-list.rs:10:9 - | -10 | #[get("/<<<<id><")] - | ^^^^^^^^ - | - = help: parameters must be of the form '<param>' - -error: parameter names must be valid identifiers - --> $DIR/malformed-param-list.rs:13:9 - | -13 | #[get("/<!>")] - | ^^^ - | - = note: "!" is not a valid identifier - -error: parameters must be named - --> $DIR/malformed-param-list.rs:16:9 - | -16 | #[get("/<_>")] - | ^^^ - | - = help: use a name such as `_guard` or `_param` - -error: parameter names must be valid identifiers - --> $DIR/malformed-param-list.rs:19:9 - | -19 | #[get("/<1>")] - | ^^^ - | - = note: "1" is not a valid identifier - -error: malformed parameter - --> $DIR/malformed-param-list.rs:22:9 - | -22 | #[get("/<>name><")] - | ^^^^^^^^ - | - = help: parameters must be of the form '<param>' - -error: parameter names must be valid identifiers - --> $DIR/malformed-param-list.rs:25:9 - | -25 | #[get("/<name>:<id>")] - | ^^^^^^^^^^^ - | - = note: "name>:<id" is not a valid identifier - -error: parameters cannot be empty - --> $DIR/malformed-param-list.rs:28:9 - | -28 | #[get("/<>")] - | ^^ - -error: aborting due to 9 previous errors - diff --git a/core/codegen/tests/ui/route-bad-method.rs b/core/codegen/tests/ui/route-bad-method.rs @@ -1,19 +0,0 @@ -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] - -extern crate rocket; - -#[route(FIX, "/hello")] -fn get1() -> &'static str { "hi" } - -#[route("hi", "/hello")] -fn get2() -> &'static str { "hi" } - -#[route("GET", "/hello")] -fn get3() -> &'static str { "hi" } - -#[route(120, "/hello")] -fn get4() -> &'static str { "hi" } - -#[route(CONNECT, "/hello")] -fn get5() -> &'static str { "hi" } diff --git a/core/codegen/tests/ui/route-bad-method.stderr b/core/codegen/tests/ui/route-bad-method.stderr @@ -1,42 +0,0 @@ -error: 'FIX' is not a valid method - --> $DIR/route-bad-method.rs:6:9 - | -6 | #[route(FIX, "/hello")] - | ^^^ - | - = help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` - -error: expected a valid HTTP method identifier - --> $DIR/route-bad-method.rs:9:9 - | -9 | #[route("hi", "/hello")] - | ^^^^ - | - = help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` - -error: expected a valid HTTP method identifier - --> $DIR/route-bad-method.rs:12:9 - | -12 | #[route("GET", "/hello")] - | ^^^^^ - | - = help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` - -error: expected a valid HTTP method identifier - --> $DIR/route-bad-method.rs:15:9 - | -15 | #[route(120, "/hello")] - | ^^^ - | - = help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` - -error: 'CONNECT' is not a valid method - --> $DIR/route-bad-method.rs:18:9 - | -18 | #[route(CONNECT, "/hello")] - | ^^^^^^^ - | - = help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` - -error: aborting due to 5 previous errors - diff --git a/core/codegen/tests/ui/typed-uri-bad-type.rs b/core/codegen/tests/ui/typed-uri-bad-type.rs @@ -1,31 +0,0 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] -#![allow(dead_code, unused_variables)] - -#[macro_use] extern crate rocket; - -use rocket::http::RawStr; -use rocket::request::FromParam; - -struct S; - -impl<'a> FromParam<'a> for S { - type Error = (); - fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> { Ok(S) } -} - -#[post("/<id>")] -fn simple(id: i32) -> &'static str { "" } - -#[post("/<id>/<name>")] -fn not_uri_display(id: i32, name: S) -> &'static str { "" } - -#[post("/<id>/<name>")] -fn not_uri_display_but_unused(id: i32, name: S) -> &'static str { "" } - -fn main() { - uri!(simple: id = "hi"); - uri!(simple: "hello"); - uri!(simple: id = 239239i64); - uri!(not_uri_display: 10, S); -} diff --git a/core/codegen/tests/ui/typed-uri-bad-type.stderr b/core/codegen/tests/ui/typed-uri-bad-type.stderr @@ -1,33 +0,0 @@ -error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied - --> $DIR/typed-uri-bad-type.rs:27:23 - | -27 | uri!(simple: id = "hi"); - | ^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32` - | - = note: required by `rocket::http::uri::FromUriParam::from_uri_param` - -error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied - --> $DIR/typed-uri-bad-type.rs:28:18 - | -28 | uri!(simple: "hello"); - | ^^^^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32` - | - = note: required by `rocket::http::uri::FromUriParam::from_uri_param` - -error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<i64>` is not satisfied - --> $DIR/typed-uri-bad-type.rs:29:23 - | -29 | uri!(simple: id = 239239i64); - | ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<i64>` is not implemented for `i32` - | - = note: required by `rocket::http::uri::FromUriParam::from_uri_param` - -error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<_>` is not satisfied - --> $DIR/typed-uri-bad-type.rs:30:31 - | -30 | uri!(not_uri_display: 10, S); - | ^ the trait `rocket::http::uri::FromUriParam<_>` is not implemented for `S` - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/core/codegen/tests/ui/typed-uris-bad-params.rs b/core/codegen/tests/ui/typed-uris-bad-params.rs @@ -1,30 +0,0 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] -#![allow(dead_code, unused_variables)] - -#[macro_use] extern crate rocket; - -use std::fmt; - -use rocket::http::Cookies; - -#[post("/<id>")] -fn simple(id: i32) -> &'static str { "" } - -#[post("/<id>")] -fn guard_1(cookies: Cookies, id: i32) -> &'static str { "" } - -fn main() { - uri!(simple); - uri!(simple: 1, 23); - uri!(simple: "Hello", 23, ); - uri!(guard_1: "hi", 100); - - uri!(simple: id = 100, name = "hi"); - uri!(simple: id = 100, id = 100); - uri!(simple: name = 100, id = 100); - uri!(simple: id = 100, id = 100, ); - uri!(simple: name = "hi"); - uri!(guard_1: cookies = "hi", id = 100); - uri!(guard_1: id = 100, cookies = "hi"); -} diff --git a/core/codegen/tests/ui/typed-uris-bad-params.stderr b/core/codegen/tests/ui/typed-uris-bad-params.stderr @@ -1,126 +0,0 @@ -error: `simple` route uri expects 1 parameter but 0 were supplied - --> $DIR/typed-uris-bad-params.rs:18:10 - | -18 | uri!(simple); - | ^^^^^^ - | - = note: expected parameter: id: i32 - -error: `simple` route uri expects 1 parameter but 2 were supplied - --> $DIR/typed-uris-bad-params.rs:19:18 - | -19 | uri!(simple: 1, 23); - | ^^^^^ - | - = note: expected parameter: id: i32 - -error: `simple` route uri expects 1 parameter but 2 were supplied - --> $DIR/typed-uris-bad-params.rs:20:18 - | -20 | uri!(simple: "Hello", 23, ); - | ^^^^^^^^^^^^ - | - = note: expected parameter: id: i32 - -error: `guard_1` route uri expects 1 parameter but 2 were supplied - --> $DIR/typed-uris-bad-params.rs:21:19 - | -21 | uri!(guard_1: "hi", 100); - | ^^^^^^^^^ - | - = note: expected parameter: id: i32 - -error: invalid parameters for `simple` route uri - --> $DIR/typed-uris-bad-params.rs:23:18 - | -23 | uri!(simple: id = 100, name = "hi"); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: unknown parameter: `name` - --> $DIR/typed-uris-bad-params.rs:23:28 - | -23 | uri!(simple: id = 100, name = "hi"); - | ^^^^ - -error: invalid parameters for `simple` route uri - --> $DIR/typed-uris-bad-params.rs:24:18 - | -24 | uri!(simple: id = 100, id = 100); - | ^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: duplicate parameter: `id` - --> $DIR/typed-uris-bad-params.rs:24:28 - | -24 | uri!(simple: id = 100, id = 100); - | ^^ - -error: invalid parameters for `simple` route uri - --> $DIR/typed-uris-bad-params.rs:25:18 - | -25 | uri!(simple: name = 100, id = 100); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: unknown parameter: `name` - --> $DIR/typed-uris-bad-params.rs:25:18 - | -25 | uri!(simple: name = 100, id = 100); - | ^^^^ - -error: invalid parameters for `simple` route uri - --> $DIR/typed-uris-bad-params.rs:26:18 - | -26 | uri!(simple: id = 100, id = 100, ); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: duplicate parameter: `id` - --> $DIR/typed-uris-bad-params.rs:26:28 - | -26 | uri!(simple: id = 100, id = 100, ); - | ^^ - -error: invalid parameters for `simple` route uri - --> $DIR/typed-uris-bad-params.rs:27:18 - | -27 | uri!(simple: name = "hi"); - | ^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: unknown parameter: `name` - --> $DIR/typed-uris-bad-params.rs:27:18 - | -27 | uri!(simple: name = "hi"); - | ^^^^ - = help: missing parameter: `id` - -error: invalid parameters for `guard_1` route uri - --> $DIR/typed-uris-bad-params.rs:28:19 - | -28 | uri!(guard_1: cookies = "hi", id = 100); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: unknown parameter: `cookies` - --> $DIR/typed-uris-bad-params.rs:28:19 - | -28 | uri!(guard_1: cookies = "hi", id = 100); - | ^^^^^^^ - -error: invalid parameters for `guard_1` route uri - --> $DIR/typed-uris-bad-params.rs:29:19 - | -29 | uri!(guard_1: id = 100, cookies = "hi"); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: uri parameters are: id: i32 -help: unknown parameter: `cookies` - --> $DIR/typed-uris-bad-params.rs:29:29 - | -29 | uri!(guard_1: id = 100, cookies = "hi"); - | ^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/core/codegen/tests/ui/typed-uris-invalid-syntax.rs b/core/codegen/tests/ui/typed-uris-invalid-syntax.rs @@ -1,21 +0,0 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] -#![allow(dead_code, unused_variables)] - -#[macro_use] extern crate rocket; - -#[post("/<id>/<name>")] -fn simple(id: i32, name: String) -> &'static str { "" } - -fn main() { - uri!(simple: id = 100, "Hello"); - uri!(simple: "Hello", id = 100); - uri!(simple,); - uri!(simple:); - uri!("/mount"); - uri!("/mount",); - uri!("mount", simple); - uri!("/mount/<id>", simple); - uri!(); - uri!(simple: id = ); -} diff --git a/core/codegen/tests/ui/typed-uris-invalid-syntax.stderr b/core/codegen/tests/ui/typed-uris-invalid-syntax.stderr @@ -1,62 +0,0 @@ -error: named and unnamed parameters cannot be mixed - --> $DIR/typed-uris-invalid-syntax.rs:11:18 - | -11 | uri!(simple: id = 100, "Hello"); - | ^^^^^^^^^^^^^^^^^ - -error: named and unnamed parameters cannot be mixed - --> $DIR/typed-uris-invalid-syntax.rs:12:18 - | -12 | uri!(simple: "Hello", id = 100); - | ^^^^^^^^^^^^^^^^^ - -error: expected `:` - --> $DIR/typed-uris-invalid-syntax.rs:13:16 - | -13 | uri!(simple,); - | ^ - -error: expected argument list after `:` - --> $DIR/typed-uris-invalid-syntax.rs:14:16 - | -14 | uri!(simple:); - | ^ - -error: unexpected end of input: expected ',' followed by route path - --> $DIR/typed-uris-invalid-syntax.rs:15:10 - | -15 | uri!("/mount"); - | ^^^^^^^^ - -error: unexpected end of input, expected identifier - --> $DIR/typed-uris-invalid-syntax.rs:16:5 - | -16 | uri!("/mount",); - | ^^^^^^^^^^^^^^^^ - -error: invalid mount point; mount points must be static, absolute URIs: `/example` - --> $DIR/typed-uris-invalid-syntax.rs:17:10 - | -17 | uri!("mount", simple); - | ^^^^^^^ - -error: invalid mount point; mount points must be static, absolute URIs: `/example` - --> $DIR/typed-uris-invalid-syntax.rs:18:10 - | -18 | uri!("/mount/<id>", simple); - | ^^^^^^^^^^^^^ - -error: unexpected end of input, call to `uri!` cannot be empty - --> $DIR/typed-uris-invalid-syntax.rs:19:5 - | -19 | uri!(); - | ^^^^^^^ - -error: unexpected end of input, expected expression - --> $DIR/typed-uris-invalid-syntax.rs:20:5 - | -20 | uri!(simple: id = ); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors - diff --git a/core/codegen_next/Cargo.toml b/core/codegen_next/Cargo.toml @@ -23,9 +23,8 @@ rocket_http = { version = "0.4.0-dev", path = "../http/" } [dependencies.derive_utils] git = "https://github.com/SergioBenitez/derive-utils" -rev = "f14fb4bc855" +rev = "62f361f" [dev-dependencies] rocket = { version = "0.4.0-dev", path = "../lib" } -rocket_codegen = { version = "0.4.0-dev", path = "../codegen" } compiletest_rs = "0.3.14" diff --git a/core/codegen_next/src/attribute/route.rs b/core/codegen_next/src/attribute/route.rs @@ -1,9 +1,9 @@ use proc_macro::{TokenStream, Span}; use proc_macro2::TokenStream as TokenStream2; -use derive_utils::{syn, Spanned, Result, FromMeta, ext::TypeExt}; +use derive_utils::{syn, Spanned, SpanWrapped, Result, FromMeta, ext::TypeExt}; use indexmap::IndexSet; -use proc_macro_ext::Diagnostics; +use proc_macro_ext::{Diagnostics, SpanExt}; use syn_ext::{syn_to_diag, IdentExt}; use self::syn::{Attribute, parse::Parser}; @@ -15,9 +15,9 @@ use {ROUTE_FN_PREFIX, ROUTE_STRUCT_PREFIX, URI_MACRO_PREFIX, ROCKET_PARAM_PREFIX #[derive(Debug, FromMeta)] struct RouteAttribute { #[meta(naked)] - method: Method, + method: SpanWrapped<Method>, path: RoutePath, - data: Option<DataSegment>, + data: Option<SpanWrapped<DataSegment>>, format: Option<MediaType>, rank: Option<isize>, } @@ -27,7 +27,7 @@ struct RouteAttribute { struct MethodRouteAttribute { #[meta(naked)] path: RoutePath, - data: Option<DataSegment>, + data: Option<SpanWrapped<DataSegment>>, format: Option<MediaType>, rank: Option<isize>, } @@ -51,6 +51,16 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> { // Gather diagnostics as we proceed. let mut diags = Diagnostics::new(); + // Emit a warning if a `data` param was supplied for non-payload methods. + if let Some(ref data) = attr.data { + if !attr.method.0.supports_payload() { + let msg = format!("'{}' does not typically support payloads", attr.method.0); + data.full_span.warning("`data` used with non-payload-supporting method") + .span_note(attr.method.span, msg) + .emit() + } + } + // Collect all of the dynamic segments in an `IndexSet`, checking for dups. let mut segments: IndexSet<Segment> = IndexSet::new(); fn dup_check<I>(set: &mut IndexSet<Segment>, iter: I, diags: &mut Diagnostics) @@ -67,7 +77,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> { dup_check(&mut segments, attr.path.path.iter().cloned(), &mut diags); attr.path.query.as_ref().map(|q| dup_check(&mut segments, q.iter().cloned(), &mut diags)); - dup_check(&mut segments, attr.data.clone().map(|s| s.0).into_iter(), &mut diags); + dup_check(&mut segments, attr.data.clone().map(|s| s.value.0).into_iter(), &mut diags); // Check the validity of function arguments. let mut inputs = vec![]; @@ -100,7 +110,11 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> { } // Check that all of the declared parameters are function inputs. - let span = function.decl.inputs.span(); + let span = match function.decl.inputs.is_empty() { + false => function.decl.inputs.span(), + true => function.span() + }; + for missing in segments.difference(&fn_segments) { diags.push(missing.span.error("unused dynamic parameter") .span_note(span, format!("expected argument named `{}` here", missing.name))) @@ -210,13 +224,16 @@ fn query_exprs(route: &Route) -> Option<TokenStream2> { let matcher = match segment.kind { Kind::Single => quote_spanned! { span => (_, #name, __v) => { - #ident = Some(match <#ty as FromFormValue>::from_form_value(__v) { + #[allow(unreachable_patterns, unreachable_code)] + let __v = match <#ty as FromFormValue>::from_form_value(__v) { Ok(__v) => __v, Err(__e) => { log_warn_(&format!("Failed to parse '{}': {:?}", #name, __e)); return Outcome::Forward(__data); } - }); + }; + + #ident = Some(__v); } }, Kind::Static => quote! { @@ -408,7 +425,9 @@ fn incomplete_route( input: TokenStream ) -> Result<TokenStream> { let method_str = method.to_string().to_lowercase(); - let method_ident = syn::Ident::new(&method_str, args.span().into()); + // FIXME(proc_macro): there should be a way to get this `Span`. + let method_span = Span::call_site().subspan(2..2 + method_str.len()).unwrap(); + let method_ident = syn::Ident::new(&method_str, method_span.into()); let function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag) .map_err(|d| d.help(format!("#[{}] can only be used on functions", method_str)))?; @@ -421,7 +440,9 @@ fn incomplete_route( }; let attribute = RouteAttribute { - method: Method(method), + method: SpanWrapped { + full_span: method_span, span: method_span, value: Method(method) + }, path: method_attribute.path, data: method_attribute.data, format: method_attribute.format, diff --git a/core/codegen_next/src/attribute/segments.rs b/core/codegen_next/src/attribute/segments.rs @@ -52,13 +52,16 @@ impl Hash for Segment { fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; - let remaining = haystack.len() - (index + needle.len()); - span.trimmed(index, remaining) + span.subspan(index..index + needle.len()) } fn trailspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; - span.trimmed(index - 1, 0) + if needle.as_ptr() as usize > haystack.as_ptr() as usize { + span.subspan((index - 1)..) + } else { + span.subspan(index..) + } } fn into_diagnostic( @@ -67,7 +70,7 @@ fn into_diagnostic( span: Span, // The `Span` of `Source`. error: &Error, // The error. ) -> Diagnostic { - let seg_span = subspan(segment, source, span).unwrap(); + let seg_span = subspan(segment, source, span).expect("seg_span"); match error { Error::Empty => { seg_span.error("parameter names cannot be empty") @@ -94,8 +97,8 @@ fn into_diagnostic( .note("components cannot contain '%' and '+' characters") } Error::Trailing(multi) => { - let multi_span = subspan(multi, source, span).unwrap(); - trailspan(segment, source, span).unwrap() + let multi_span = subspan(multi, source, span).expect("mutli_span"); + trailspan(segment, source, span).expect("trailspan") .error("unexpected trailing text after a '..' param") .help("a multi-segment param must be the final component") .span_note(multi_span, "multi-segment param is here") @@ -125,7 +128,7 @@ crate fn parse_segments( break; } } else if let Ok(segment) = result { - let seg_span = subspan(&segment.string, string, span).unwrap(); + let seg_span = subspan(&segment.string, string, span).expect("seg"); segments.push(Segment::from(segment, seg_span)); } } diff --git a/core/codegen_next/src/bang/uri.rs b/core/codegen_next/src/bang/uri.rs @@ -54,10 +54,16 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> { .note(format!("uri parameters are: {}", internal.fn_args_str())); fn join<S: Display, T: Iterator<Item = S>>(iter: T) -> (&'static str, String) { - let items: Vec<_> = iter.map(|i| format!("`{}`", i)).collect(); + let mut items: Vec<_> = iter.map(|i| format!("`{}`", i)).collect(); + items.dedup(); (p!("parameter", items.len()), items.join(", ")) } + if !missing.is_empty() { + let (ps, msg) = join(missing.iter()); + diag = diag.help(format!("missing {}: {}", ps, msg)); + } + if !extra.is_empty() { let (ps, msg) = join(extra.iter()); let spans: Vec<_> = extra.iter().map(|ident| ident.span().unstable()).collect(); @@ -70,11 +76,6 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> { diag = diag.span_help(spans, format!("duplicate {}: {}", ps, msg)); } - if !missing.is_empty() { - let (ps, msg) = join(missing.iter()); - diag = diag.help(format!("missing {}: {}", ps, msg)); - } - Err(diag) } } diff --git a/core/codegen_next/src/http_codegen.rs b/core/codegen_next/src/http_codegen.rs @@ -70,9 +70,16 @@ impl ToTokens for ContentType { impl FromMeta for MediaType { fn from_meta(meta: MetaItem) -> Result<Self> { - http::MediaType::parse_flexible(&String::from_meta(meta)?) - .map(MediaType) - .ok_or(meta.value_span().error("invalid or unknown media type")) + let mt = http::MediaType::parse_flexible(&String::from_meta(meta)?) + .ok_or(meta.value_span().error("invalid or unknown media type"))?; + + if !mt.is_known() { + meta.value_span() + .warning(format!("'{}' is not a known media type", mt)) + .emit(); + } + + Ok(MediaType(mt)) } } @@ -158,7 +165,7 @@ impl FromMeta for Origin { let uri = http::uri::Origin::parse_route(&string) .map_err(|e| { let span = e.index() - .map(|i| span.trimmed(i + 1, 0).unwrap()) + .map(|i| span.subspan(i + 1..).expect("origin")) .unwrap_or(span); span.error(format!("invalid path URI: {}", e)) @@ -178,7 +185,9 @@ impl FromMeta for Origin { impl FromMeta for Segment { fn from_meta(meta: MetaItem) -> Result<Self> { let string = String::from_meta(meta)?; - let span = meta.value_span().trimmed(1, 1).unwrap(); + let span = meta.value_span() + .subspan(1..(string.len() + 1)) + .expect("segment"); let segment = parse_segment(&string, span)?; if segment.kind != Kind::Single { @@ -201,20 +210,18 @@ impl FromMeta for DataSegment { impl FromMeta for RoutePath { fn from_meta(meta: MetaItem) -> Result<Self> { - let origin = Origin::from_meta(meta)?; - - let span = meta.value_span().trimmed(1, 1).unwrap(); - let query_len = origin.0.query().map(|q| q.len() + 1).unwrap_or(0); - let path_span = span.trimmed(0, query_len).unwrap(); + let (origin, span) = (Origin::from_meta(meta)?, meta.value_span()); + let path_span = span.subspan(1..origin.0.path().len() + 1).expect("path"); let path = parse_segments(origin.0.path(), '/', Source::Path, path_span); let query = origin.0.query() .map(|q| { - let len_to_q = origin.0.path().len() + 1; - let query_span = span.trimmed(len_to_q, 0).unwrap(); + let len_to_q = 1 + origin.0.path().len() + 1; + let end_of_q = len_to_q + q.len(); + let query_span = span.subspan(len_to_q..end_of_q).expect("query"); if q.starts_with('&') || q.contains("&&") || q.ends_with('&') { // TODO: Show a help message with what's expected. - Err(query_span.error("query cannot contain empty components").into()) + Err(query_span.error("query cannot contain empty segments").into()) } else { parse_segments(q, '&', Source::Query, query_span) } diff --git a/core/codegen_next/src/lib.rs b/core/codegen_next/src/lib.rs @@ -14,11 +14,15 @@ //! ## **Table of Contents** //! //! 1. [Custom Attributes](#custom-attributes) +//! * [`route`, `get`, `put`, ...](#route-attributes) +//! * [`catch`](#catch-attribute) //! 2. [Custom Derives](#custom-derives) //! * [`FromForm`](#fromform) //! * [`FromFormValue`](#fromformvalue) //! * [`Responder`](#responder) //! 3. [Procedural Macros](#procedural-macros) +//! * [`routes`, `catchers`](#routes-and-catchers) +//! * [`uri`](#typed-uris-uri) //! 4. [Debugging Generated Code](#debugging-codegen) //! //! ## Custom Attributes @@ -35,11 +39,13 @@ //! * **options** //! * **catch** //! +//! ### Route Attributes +//! //! The grammar for all _route_ attributes, including **route**, **get**, //! **put**, **post**, **delete**, **head**, **patch**, and **options** is //! defined as: //! -//! <pre> +//! ```text //! route := METHOD? '(' ('path' '=')? path (',' kv_param)* ')' //! //! path := URI_SEG @@ -58,26 +64,34 @@ //! //! URI_SEG := valid HTTP URI Segment //! DYNAMIC_PARAM := '<' IDENT '..'? '>' (string literal) -//! </pre> +//! ``` //! //! Note that the **route** attribute takes a method as its first argument, //! while the remaining do not. That is, **route** looks like: //! -//! #[route(GET, path = "/hello")] +//! ```rust,ignore +//! #[route(GET, path = "/hello")] +//! ``` //! //! while the equivalent using **get** looks like: //! -//! #[get("/hello")] +//! ```rust,ignore +//! #[get("/hello")] +//! ``` +//! +//! ### Catch Attribute //! //! The syntax for the **catch** attribute is: //! -//! <pre> +//! ```text //! catch := INTEGER -//! </pre> +//! ``` //! //! A use of the `catch` attribute looks like: //! -//! #[catch(404)] +//! ```rust,ignore +//! #[catch(404)] +//! ``` //! //! ## Custom Derives //! @@ -87,37 +101,43 @@ //! * **FromFormValue** //! * **Responder** //! -//! <small>* In reality, all of these custom derives are currently implemented -//! by the `rocket_codegen_next` crate. Nonetheless, they are documented -//! here.</small> +//! <small> +//! * In reality, all of these custom derives are currently implemented by the +//! `rocket_codegen_next` crate. Nonetheless, they are documented here. +//! </small> +//! //! ### `FromForm` //! //! The [`FromForm`] derive can be applied to structures with named fields: //! -//! #[derive(FromForm)] -//! struct MyStruct { -//! field: usize, -//! other: String -//! } +//! ```rust,ignore +//! #[derive(FromForm)] +//! struct MyStruct { +//! field: usize, +//! other: String +//! } +//! ``` //! //! Each field's type is required to implement [`FromFormValue`]. //! //! The derive accepts one field attribute: `form`, with the following syntax: //! -//! <pre> +//! ```text //! form := 'field' '=' '"' IDENT '"' //! //! IDENT := valid identifier, as defined by Rust -//! </pre> +//! ``` //! //! When applied, the attribute looks as follows: //! -//! #[derive(FromForm)] -//! struct MyStruct { -//! field: usize, -//! #[form(field = "renamed_field")] -//! other: String -//! } +//! ```rust,ignore +//! #[derive(FromForm)] +//! struct MyStruct { +//! field: usize, +//! #[form(field = "renamed_field")] +//! other: String +//! } +//! ``` //! //! The derive generates an implementation for the [`FromForm`] trait. The //! implementation parses a form whose field names match the field names of the @@ -137,12 +157,14 @@ //! The [`FromFormValue`] derive can be applied to enums with nullary //! (zero-length) fields: //! -//! #[derive(FromFormValue)] -//! enum MyValue { -//! First, -//! Second, -//! Third, -//! } +//! ```rust,ignore +//! #[derive(FromFormValue)] +//! enum MyValue { +//! First, +//! Second, +//! Third, +//! } +//! ``` //! //! The derive generates an implementation of the [`FromFormValue`] trait for //! the decorated `enum`. The implementation returns successfully when the form @@ -157,21 +179,23 @@ //! The `form` field attribute can be used to change the string that is compared //! against for a given variant: //! -//! #[derive(FromFormValue)] -//! enum MyValue { -//! First, -//! Second, -//! #[form(value = "fourth")] -//! Third, -//! } +//! ```rust,ignore +//! #[derive(FromFormValue)] +//! enum MyValue { +//! First, +//! Second, +//! #[form(value = "fourth")] +//! Third, +//! } +//! ``` //! //! The attribute's grammar is: //! -//! <pre> +//! ```text //! form := 'field' '=' STRING_LIT //! //! STRING_LIT := any valid string literal, as defined by Rust -//! </pre> +//! ``` //! //! The attribute accepts a single string parameter of name `value` //! corresponding to the string to use to match against for the decorated @@ -184,17 +208,19 @@ //! applied to enums, variants must have at least one field. When applied to //! structs, the struct must have at least one field. //! -//! #[derive(Responder)] -//! enum MyResponder { -//! A(String), -//! B(OtherResponse, ContentType), -//! } +//! ```rust,ignore +//! #[derive(Responder)] +//! enum MyResponder { +//! A(String), +//! B(OtherResponse, ContentType), +//! } //! -//! #[derive(Responder)] -//! struct MyResponder { -//! inner: OtherResponder, -//! header: ContentType, -//! } +//! #[derive(Responder)] +//! struct MyResponder { +//! inner: OtherResponder, +//! header: ContentType, +//! } +//! ``` //! //! The derive generates an implementation of the [`Responder`] trait for the //! decorated enum or structure. The derive uses the _first_ field of a variant @@ -207,19 +233,21 @@ //! Except for the first field, fields decorated with `#[response(ignore)]` are //! ignored by the derive: //! -//! #[derive(Responder)] -//! enum MyResponder { -//! A(String), -//! B(OtherResponse, ContentType, #[response(ignore)] Other), -//! } +//! ```rust,ignore +//! #[derive(Responder)] +//! enum MyResponder { +//! A(String), +//! B(OtherResponse, ContentType, #[response(ignore)] Other), +//! } //! -//! #[derive(Responder)] -//! struct MyResponder { -//! inner: InnerResponder, -//! header: ContentType, -//! #[response(ignore)] -//! other: Other, -//! } +//! #[derive(Responder)] +//! struct MyResponder { +//! inner: InnerResponder, +//! header: ContentType, +//! #[response(ignore)] +//! other: Other, +//! } +//! ``` //! //! Decorating the first field with `#[response(ignore)]` has no effect. //! @@ -228,7 +256,7 @@ //! produced by the generated implementation. The `response` attribute used in //! these positions has the following grammar: //! -//! <pre> +//! ```text //! response := parameter (',' parameter)? //! //! parameter := 'status' '=' STATUS @@ -237,26 +265,28 @@ //! STATUS := unsigned integer >= 100 and < 600 //! CONTENT_TYPE := string literal, as defined by Rust, identifying a valid //! Content-Type, as defined by Rocket -//! </pre> +//! ``` //! //! It can be used as follows: //! -//! #[derive(Responder)] -//! enum Error { -//! #[response(status = 500, content_type = "json")] -//! A(String), -//! #[response(status = 404)] -//! B(OtherResponse, ContentType), -//! } -//! -//! #[derive(Responder)] -//! #[response(status = 400)] -//! struct MyResponder { -//! inner: InnerResponder, -//! header: ContentType, -//! #[response(ignore)] -//! other: Other, -//! } +//! ```rust,ignore +//! #[derive(Responder)] +//! enum Error { +//! #[response(status = 500, content_type = "json")] +//! A(String), +//! #[response(status = 404)] +//! B(OtherResponse, ContentType), +//! } +//! +//! #[derive(Responder)] +//! #[response(status = 400)] +//! struct MyResponder { +//! inner: InnerResponder, +//! header: ContentType, +//! #[response(ignore)] +//! other: Other, +//! } +//! ``` //! //! The attribute accepts two key/value pairs: `status` and `content_type`. The //! value of `status` must be an unsigned integer representing a valid status @@ -288,13 +318,15 @@ //! * **catchers** //! * **uri** //! +//! ## Routes and Catchers +//! //! The syntax for `routes!` and `catchers!` is defined as: //! -//! <pre> +//! ```text //! macro := PATH (',' PATH)* //! //! PATH := a path, as defined by Rust -//! </pre> +//! ``` //! //! ### Typed URIs: `uri!` //! @@ -330,7 +362,7 @@ //! //! The grammar for the `uri!` macro is as follows: //! -//! <pre> +//! ```text //! uri := (mount ',')? PATH (':' params)? //! //! mount = STRING @@ -342,7 +374,7 @@ //! IDENT := a valid Rust identifier (examples: `name`, `age`) //! STRING := an uncooked string literal, as defined by Rust (example: `"hi"`) //! PATH := a path, as defined by Rust (examples: `route`, `my_mod::route`) -//! </pre> +//! ``` //! //! #### Semantics //! @@ -361,7 +393,7 @@ //! implementation, provided by Rocket, allows an `&str` to be used in a `uri!` //! invocation for route URI parameters declared as `String`: //! -//! ``` +//! ```rust,ignore //! impl<'a> FromUriParam<&'a str> for String //! ``` //! @@ -383,7 +415,7 @@ //! might run the following to build a Rocket application with codegen logging //! enabled: //! -//! ``` +//! ```sh //! ROCKET_CODEGEN_DEBUG=1 cargo build //! ``` @@ -413,11 +445,25 @@ crate static ROUTE_FN_PREFIX: &str = "rocket_route_fn_"; crate static URI_MACRO_PREFIX: &str = "rocket_uri_macro_"; crate static ROCKET_PARAM_PREFIX: &str = "__rocket_param_"; +macro_rules! emit { + ($tokens:expr) => ({ + let tokens = $tokens; + if ::std::env::var_os("ROCKET_CODEGEN_DEBUG").is_some() { + ::proc_macro::Span::call_site() + .note("emitting Rocket code generation debug output") + .note(tokens.to_string()) + .emit() + } + + tokens + }) +} + macro_rules! route_attribute { ($name:ident => $method:expr) => ( #[proc_macro_attribute] pub fn $name(args: TokenStream, input: TokenStream) -> TokenStream { - attribute::route::route_attribute($method, args, input) + emit!(attribute::route::route_attribute($method, args, input)) } ) } @@ -433,40 +479,41 @@ route_attribute!(options => Method::Options); #[proc_macro_derive(FromFormValue, attributes(form))] pub fn derive_from_form_value(input: TokenStream) -> TokenStream { - derive::from_form_value::derive_from_form_value(input) + emit!(derive::from_form_value::derive_from_form_value(input)) } #[proc_macro_derive(FromForm, attributes(form))] pub fn derive_from_form(input: TokenStream) -> TokenStream { - derive::from_form::derive_from_form(input) + emit!(derive::from_form::derive_from_form(input)) } #[proc_macro_derive(Responder, attributes(response))] pub fn derive_responder(input: TokenStream) -> TokenStream { - derive::responder::derive_responder(input) + emit!(derive::responder::derive_responder(input)) } #[proc_macro_attribute] pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream { - attribute::catch::catch_attribute(args, input) + emit!(attribute::catch::catch_attribute(args, input)) } #[proc_macro] pub fn routes(input: TokenStream) -> TokenStream { - bang::routes_macro(input) + emit!(bang::routes_macro(input)) } #[proc_macro] pub fn catchers(input: TokenStream) -> TokenStream { - bang::catchers_macro(input) + emit!(bang::catchers_macro(input)) } #[proc_macro] pub fn uri(input: TokenStream) -> TokenStream { - bang::uri_macro(input) + emit!(bang::uri_macro(input)) } +#[doc(hidden)] #[proc_macro] pub fn rocket_internal_uri(input: TokenStream) -> TokenStream { - bang::uri_internal_macro(input) + emit!(bang::uri_internal_macro(input)) } diff --git a/core/codegen_next/src/proc_macro_ext.rs b/core/codegen_next/src/proc_macro_ext.rs @@ -1,3 +1,5 @@ +use std::ops::{Bound, RangeBounds}; + use proc_macro::{Span, Diagnostic, /* MultiSpan */}; use syntax_pos::{Span as InnerSpan, Pos, BytePos}; @@ -61,37 +63,40 @@ impl From<Vec<Diagnostic>> for Diagnostics { } pub trait SpanExt { - fn trimmed(&self, left: usize, right: usize) -> Option<Span>; + fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span>; } impl SpanExt for Span { - /// Trim the span on the left by `left` characters and on the right by - /// `right` characters. - fn trimmed(&self, left: usize, right: usize) -> Option<Span> { - let inner: InnerSpan = unsafe { ::std::mem::transmute(*self) }; - if left > u32::max_value() as usize || right > u32::max_value() as usize { - return None; - } - - // Ensure that the addition won't overflow. - let (left, right) = (left as u32, right as u32); - if u32::max_value() - left < inner.lo().to_u32() { - return None; - } - - // Ensure that the subtraction won't underflow. - if right > inner.hi().to_u32() { - return None; - } - - let new_lo = inner.lo() + BytePos(left); - let new_hi = inner.hi() - BytePos(right); - - // Ensure we're still inside the old `Span` and didn't cross paths. - if new_lo >= new_hi { + /// Create a `subspan` from `start` to `end`. + fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span> { + let inner: InnerSpan = unsafe { ::std::mem::transmute(self) }; + let length = inner.hi().to_usize() - inner.lo().to_usize(); + + let start = match range.start_bound() { + Bound::Included(&lo) => lo, + Bound::Excluded(&lo) => lo + 1, + Bound::Unbounded => 0, + }; + + let end = match range.end_bound() { + Bound::Included(&hi) => hi + 1, + Bound::Excluded(&hi) => hi, + Bound::Unbounded => length, + }; + + // Bounds check the values, preventing addition overflow and OOB spans. + if start > u32::max_value() as usize + || end > u32::max_value() as usize + || (u32::max_value() - start as u32) < inner.lo().to_u32() + || (u32::max_value() - end as u32) < inner.lo().to_u32() + || start >= end + || end > length + { return None; } + let new_lo = inner.lo() + BytePos(start as u32); + let new_hi = inner.lo() + BytePos(end as u32); let new_inner = inner.with_lo(new_lo).with_hi(new_hi); Some(unsafe { ::std::mem::transmute(new_inner) }) } diff --git a/core/codegen_next/tests/compile-test.rs b/core/codegen_next/tests/compile-test.rs @@ -90,7 +90,6 @@ fn run_mode(mode: &'static str, path: &'static str) { config.target_rustcflags = Some([ link_flag("-L", "crate", &[]), link_flag("-L", "dependency", &["deps"]), - extern_dep("rocket_codegen_next", Kind::Dynamic).expect("find codegen dep"), extern_dep("rocket_http", Kind::Static).expect("find http dep"), extern_dep("rocket", Kind::Static).expect("find core dep"), ].join(" ")); diff --git a/core/codegen_next/tests/responder.rs b/core/codegen_next/tests/responder.rs @@ -1,3 +1,5 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + #[macro_use] extern crate rocket; use rocket::local::Client; diff --git a/core/codegen_next/tests/route-format.rs b/core/codegen_next/tests/route-format.rs @@ -27,10 +27,10 @@ fn msgpack() -> &'static str { "msgpack" } #[get("/", format = "plain")] fn plain() -> &'static str { "plain" } -#[get("/", format = "binary")] +#[get("/", format = "binary", rank = 2)] fn binary() -> &'static str { "binary" } -#[get("/", rank = 2)] +#[get("/", rank = 3)] fn other() -> &'static str { "other" } #[test] @@ -57,10 +57,13 @@ fn test_formats() { assert_eq!(response.body_string().unwrap(), "binary"); let mut response = client.get("/").header(ContentType::JSON).dispatch(); - assert_eq!(response.body_string().unwrap(), "other"); + assert_eq!(response.body_string().unwrap(), "plain"); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string().unwrap(), "other"); + assert_eq!(response.body_string().unwrap(), "plain"); + + let response = client.put("/").header(ContentType::HTML).dispatch(); + assert_eq!(response.status(), Status::NotFound); } // Test custom formats. @@ -71,7 +74,7 @@ fn get_foo() -> &'static str { "get_foo" } #[post("/", format = "application/foo")] fn post_foo() -> &'static str { "post_foo" } -#[get("/", format = "bar/baz")] +#[get("/", format = "bar/baz", rank = 2)] fn get_bar_baz() -> &'static str { "get_bar_baz" } #[put("/", format = "bar/baz")] @@ -101,6 +104,12 @@ fn test_custom_formats() { let mut response = client.put("/").header(bar_baz_ct).dispatch(); assert_eq!(response.body_string().unwrap(), "put_bar_baz"); - let response = client.get("/").dispatch(); + let mut response = client.get("/").dispatch(); + assert_eq!(response.body_string().unwrap(), "get_foo"); + + let response = client.put("/").header(ContentType::HTML).dispatch(); + assert_eq!(response.status(), Status::NotFound); + + let response = client.post("/").header(ContentType::HTML).dispatch(); assert_eq!(response.status(), Status::NotFound); } diff --git a/core/codegen_next/tests/route.rs b/core/codegen_next/tests/route.rs @@ -119,3 +119,33 @@ fn test_full_route() { assert_eq!(response.body_string().unwrap(), format!("({}, {}, {}, {}, {}, {}) ({})", sky, name, "A A", "inside", path, simple, expected_uri)); } + +// Check that we propogate span information correctly to allow re-expansion. + +#[get("/easy/<id>")] +fn easy(id: i32) -> String { + format!("easy id: {}", id) +} + +macro_rules! make_handler { + () => { + #[get("/hard/<id>")] + fn hard(id: i32) -> String { + format!("hard id: {}", id) + } + } +} + +make_handler!(); + +#[test] +fn test_reexpansion() { + let rocket = rocket::ignite().mount("/", routes![easy, hard]); + let client = Client::new(rocket).unwrap(); + + let mut response = client.get("/easy/327").dispatch(); + assert_eq!(response.body_string().unwrap(), "easy id: 327"); + + let mut response = client.get("/hard/72").dispatch(); + assert_eq!(response.body_string().unwrap(), "hard id: 72"); +} diff --git a/core/codegen_next/tests/typed-uris.rs b/core/codegen_next/tests/typed-uris.rs @@ -0,0 +1,362 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] +#![allow(dead_code, unused_variables)] + +#[macro_use] extern crate rocket; + +use std::fmt; +use std::path::PathBuf; + +use rocket::http::{RawStr, Cookies}; +use rocket::http::uri::{Origin, UriDisplay, FromUriParam}; +use rocket::request::Form; + +#[derive(FromForm)] +struct User<'a> { + name: &'a RawStr, + nickname: String, +} + +// TODO: Make this deriveable. +impl<'a> UriDisplay for User<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "name={}&nickname={}", + &self.name.replace(' ', "+") as &UriDisplay, + &self.nickname.replace(' ', "+") as &UriDisplay) + } +} + +impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> { + type Target = User<'a>; + fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> { + User { name: name.into(), nickname: nickname.to_string() } + } +} + +// This one has no `UriDisplay`. It exists to ensure that this file still +// compiles even though it's used a URI parameter's type. As long as a user +// doesn't request a URI from that route, things should be okay. +#[derive(FromForm)] +struct Second { + nickname: String, +} + +#[post("/<id>")] +fn simple(id: i32) { } + +#[post("/<id>/<name>")] +fn simple2(id: i32, name: String) { } + +#[post("/<id>/<name>")] +fn simple2_flipped(name: String, id: i32) { } + +#[post("/?<id>")] +fn simple3(id: i32) { } + +#[post("/?<id>&<name>")] +fn simple4(id: i32, name: String) { } + +#[post("/?<id>&<name>")] +fn simple4_flipped(name: String, id: i32) { } + +#[post("/<used>/<_unused>")] +fn unused_param(used: i32, _unused: i32) { } + +#[post("/<id>")] +fn guard_1(cookies: Cookies, id: i32) { } + +#[post("/<id>/<name>")] +fn guard_2(name: String, cookies: Cookies, id: i32) { } + +#[post("/a/<id>/hi/<name>/hey")] +fn guard_3(id: i32, name: String, cookies: Cookies) { } + +#[post("/<id>", data = "<form>")] +fn no_uri_display_okay(id: i32, form: Form<Second>) { } + +#[post("/name/<name>?<foo>&bar=10&<bar>&<query..>", data = "<user>", rank = 2)] +fn complex<'r>( + foo: usize, + name: &RawStr, + query: Form<User<'r>>, + user: Form<User<'r>>, + bar: &RawStr, + cookies: Cookies +) { } + +#[post("/a/<path..>")] +fn segments(path: PathBuf) { } + +#[post("/a/<id>/then/<path..>")] +fn param_and_segments(path: PathBuf, id: usize) { } + +#[post("/a/<id>/then/<path..>")] +fn guarded_segments(cookies: Cookies, path: PathBuf, id: usize) { } + +macro assert_uri_eq($($uri:expr => $expected:expr,)+) { + $(assert_eq!($uri, Origin::parse($expected).expect("valid origin URI"));)+ +} + +#[test] +fn check_simple_unnamed() { + assert_uri_eq! { + uri!(simple: 100) => "/100", + uri!(simple: -23) => "/-23", + uri!(unused_param: 1, 2) => "/1/2", + } + + // The "flipped" test ensures that the order of parameters depends on the + // route's URI, not on the order in the function signature. + assert_uri_eq! { + uri!(simple2: 100, "hello".to_string()) => "/100/hello", + uri!(simple2: 1349, "hey".to_string()) => "/1349/hey", + uri!(simple2_flipped: 100, "hello".to_string()) => "/100/hello", + } + + // Ensure that `.from_uri_param()` is called. + assert_uri_eq! { + uri!(simple2: 100, "hello") => "/100/hello", + uri!(simple2_flipped: 1349, "hey") => "/1349/hey", + } + + // Ensure that the `UriDisplay` trait is being used. + assert_uri_eq! { + uri!(simple2: 100, "hello there") => "/100/hello%20there", + uri!(simple2_flipped: 100, "hello there") => "/100/hello%20there", + } + + // Ensure that query parameters are handled properly. + assert_uri_eq! { + uri!(simple3: 100) => "/?id=100", + uri!(simple3: 1349) => "/?id=1349", + uri!(simple4: 100, "bob") => "/?id=100&name=bob", + uri!(simple4: 1349, "Bob Anderson") => "/?id=1349&name=Bob%20Anderson", + uri!(simple4_flipped: 100, "bob") => "/?id=100&name=bob", + uri!(simple4_flipped: 1349, "Bob Anderson") => "/?id=1349&name=Bob%20Anderson", + } +} + +#[test] +fn check_simple_named() { + assert_uri_eq! { + uri!(simple: id = 100) => "/100", + uri!(simple: id = -23) => "/-23", + uri!(unused_param: used = 1, _unused = 2) => "/1/2", + } + + assert_uri_eq! { + uri!(simple2: id = 100, name = "hello".to_string()) => "/100/hello", + uri!(simple2: name = "hi".to_string(), id = 123) => "/123/hi", + uri!(simple2_flipped: id = 1349, name = "hey".to_string()) => "/1349/hey", + uri!(simple2_flipped: name = "hello".to_string(), id = 100) => "/100/hello", + } + + // Ensure that `.from_uri_param()` is called. + assert_uri_eq! { + uri!(simple2: id = 100, name = "hello") => "/100/hello", + uri!(simple2: id = 100, name = "hi") => "/100/hi", + uri!(simple2: id = 1349, name = "hey") => "/1349/hey", + uri!(simple2: name = "hello", id = 100) => "/100/hello", + uri!(simple2: name = "hi", id = 100) => "/100/hi", + uri!(simple2_flipped: id = 1349, name = "hey") => "/1349/hey", + } + + // Ensure that the `UriDisplay` trait is being used. + assert_uri_eq! { + uri!(simple2: id = 100, name = "hello there") => "/100/hello%20there", + uri!(simple2: name = "hello there", id = 100) => "/100/hello%20there", + uri!(simple2_flipped: id = 100, name = "hello there") => "/100/hello%20there", + uri!(simple2_flipped: name = "hello there", id = 100) => "/100/hello%20there", + } + + // Ensure that query parameters are handled properly. + assert_uri_eq! { + uri!(simple3: id = 100) => "/?id=100", + uri!(simple3: id = 1349) => "/?id=1349", + uri!(simple4: id = 100, name = "bob") => "/?id=100&name=bob", + uri!(simple4: id = 1349, name = "Bob A") => "/?id=1349&name=Bob%20A", + uri!(simple4: name = "Bob A", id = 1349) => "/?id=1349&name=Bob%20A", + uri!(simple4_flipped: id = 1349, name = "Bob A") => "/?id=1349&name=Bob%20A", + uri!(simple4_flipped: name = "Bob A", id = 1349) => "/?id=1349&name=Bob%20A", + } +} + +#[test] +fn check_mount_point() { + assert_uri_eq! { + uri!("/mount", simple: 100) => "/mount/100", + uri!("/mount", simple: id = 23) => "/mount/23", + uri!("/another", simple: 100) => "/another/100", + uri!("/another", simple: id = 23) => "/another/23", + } + + assert_uri_eq! { + uri!("/a", simple2: 100, "hey") => "/a/100/hey", + uri!("/b", simple2: id = 23, name = "hey") => "/b/23/hey", + } +} + +#[test] +fn check_guards_ignored() { + assert_uri_eq! { + uri!("/mount", guard_1: 100) => "/mount/100", + uri!("/mount", guard_2: 2938, "boo") => "/mount/2938/boo", + uri!("/mount", guard_3: 340, "Bob") => "/mount/a/340/hi/Bob/hey", + uri!(guard_1: 100) => "/100", + uri!(guard_2: 2938, "boo") => "/2938/boo", + uri!(guard_3: 340, "Bob") => "/a/340/hi/Bob/hey", + uri!("/mount", guard_1: id = 100) => "/mount/100", + uri!("/mount", guard_2: id = 2938, name = "boo") => "/mount/2938/boo", + uri!("/mount", guard_3: id = 340, name = "Bob") => "/mount/a/340/hi/Bob/hey", + uri!(guard_1: id = 100) => "/100", + uri!(guard_2: name = "boo", id = 2938) => "/2938/boo", + uri!(guard_3: name = "Bob", id = 340) => "/a/340/hi/Bob/hey", + } +} + +#[test] +fn check_with_segments() { + assert_uri_eq! { + uri!(segments: PathBuf::from("one/two/three")) => "/a/one/two/three", + uri!(segments: path = PathBuf::from("one/two/three")) => "/a/one/two/three", + uri!("/c", segments: PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/", + uri!("/c", segments: path = PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/", + uri!(segments: PathBuf::from("one/ tw?o/")) => "/a/one/%20tw%3Fo/", + uri!(param_and_segments: 10, PathBuf::from("a/b")) => "/a/10/then/a/b", + uri!(param_and_segments: id = 10, path = PathBuf::from("a/b")) + => "/a/10/then/a/b", + uri!(guarded_segments: 10, PathBuf::from("a/b")) => "/a/10/then/a/b", + uri!(guarded_segments: id = 10, path = PathBuf::from("a/b")) + => "/a/10/then/a/b", + } + + // Now check the `from_uri_param()` conversions for `PathBuf`. + assert_uri_eq! { + uri!(segments: "one/two/three") => "/a/one/two/three", + uri!("/oh", segments: path = "one/two/three") => "/oh/a/one/two/three", + uri!(segments: "one/ tw?o/") => "/a/one/%20tw%3Fo/", + uri!(param_and_segments: id = 10, path = "a/b") => "/a/10/then/a/b", + uri!(guarded_segments: 10, "a/b") => "/a/10/then/a/b", + uri!(guarded_segments: id = 10, path = "a/b") => "/a/10/then/a/b", + } +} + +#[test] +fn check_complex() { + assert_uri_eq! { + uri!(complex: "no idea", 10, "high", ("A B C", "a c")) => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: "Bob", 248, "?", User { name: "Robert".into(), nickname: "Bob".into() }) => + "/name/Bob?foo=248&bar=10&bar=%3F&name=Robert&nickname=Bob", + uri!(complex: "Bob", 248, "a a", &User { name: "Robert".into(), nickname: "B".into() }) => + "/name/Bob?foo=248&bar=10&bar=a%20a&name=Robert&nickname=B", + uri!(complex: "no idea", 248, "", &User { name: "A B".into(), nickname: "A".into() }) => + "/name/no%20idea?foo=248&bar=10&bar=&name=A+B&nickname=A", + uri!(complex: "hi", 3, "b", &User { name: "A B C".into(), nickname: "a b".into() }) => + "/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b", + uri!(complex: name = "no idea", foo = 10, bar = "high", query = ("A B C", "a c")) => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: foo = 10, name = "no idea", bar = "high", query = ("A B C", "a c")) => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: query = ("A B C", "a c"), foo = 10, name = "no idea", bar = "high", ) => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: query = ("A B C", "a c"), foo = 10, name = "no idea", bar = "high") => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: query = *&("A B C", "a c"), foo = 10, name = "no idea", bar = "high") => + "/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c", + uri!(complex: foo = 3, name = "hi", bar = "b", + query = &User { name: "A B C".into(), nickname: "a b".into() }) => + "/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b", + uri!(complex: query = &User { name: "A B C".into(), nickname: "a b".into() }, + foo = 3, name = "hi", bar = "b") => + "/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b", + } + + // Ensure variables are correctly processed. + let user = User { name: "Robert".into(), nickname: "Bob".into() }; + assert_uri_eq! { + uri!(complex: "complex", 0, "high", &user) => + "/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob", + uri!(complex: "complex", 0, "high", &user) => + "/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob", + uri!(complex: "complex", 0, "high", user) => + "/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob", + } +} + +#[test] +fn check_location_promotion() { + struct S1(String); + struct S2 { name: String }; + + let s1 = S1("Bob".into()); + let s2 = S2 { name: "Bob".into() }; + + assert_uri_eq! { + uri!(simple2: 1, &S1("A".into()).0) => "/1/A", + uri!(simple2: 1, S1("A".into()).0) => "/1/A", + uri!(simple2: 1, &S2 { name: "A".into() }.name) => "/1/A", + uri!(simple2: 1, S2 { name: "A".into() }.name) => "/1/A", + uri!(simple2: 1, &s1.0) => "/1/Bob", + uri!(simple2: 1, &s2.name) => "/1/Bob", + uri!(simple2: 2, &s1.0) => "/2/Bob", + uri!(simple2: 2, &s2.name) => "/2/Bob", + uri!(simple2: 2, s1.0) => "/2/Bob", + uri!(simple2: 2, s2.name) => "/2/Bob", + } + + let mut s1 = S1("Bob".into()); + let mut s2 = S2 { name: "Bob".into() }; + assert_uri_eq! { + uri!(simple2: 1, &mut S1("A".into()).0) => "/1/A", + uri!(simple2: 1, S1("A".into()).0) => "/1/A", + uri!(simple2: 1, &mut S2 { name: "A".into() }.name) => "/1/A", + uri!(simple2: 1, S2 { name: "A".into() }.name) => "/1/A", + uri!(simple2: 1, &mut s1.0) => "/1/Bob", + uri!(simple2: 1, &mut s2.name) => "/1/Bob", + uri!(simple2: 2, &mut s1.0) => "/2/Bob", + uri!(simple2: 2, &mut s2.name) => "/2/Bob", + uri!(simple2: 2, s1.0) => "/2/Bob", + uri!(simple2: 2, s2.name) => "/2/Bob", + } +} + +#[test] +fn check_scoped() { + assert_uri_eq!{ + uri!(typed_uris::simple: 100) => "/typed_uris/100", + uri!(typed_uris::simple: id = 100) => "/typed_uris/100", + uri!(typed_uris::deeper::simple: 100) => "/typed_uris/deeper/100", + } +} + +mod typed_uris { + use super::assert_uri_eq; + + #[post("/typed_uris/<id>")] + fn simple(id: i32) { } + + #[test] + fn check_simple_scoped() { + assert_uri_eq! { + uri!(simple: id = 100) => "/typed_uris/100", + uri!(::simple: id = 100) => "/100", + uri!("/mount", ::simple: id = 100) => "/mount/100", + uri!(::simple2: id = 100, name = "hello") => "/100/hello", + } + } + + pub mod deeper { + use super::assert_uri_eq; + + #[post("/typed_uris/deeper/<id>")] + fn simple(id: i32) { } + + #[test] + fn check_deep_scoped() { + assert_uri_eq! { + uri!(super::simple: id = 100) => "/typed_uris/100", + uri!(::simple: id = 100) => "/100", + } + } + } +} diff --git a/core/codegen_next/tests/ui-fail/route-attribute-general-syntax.rs b/core/codegen_next/tests/ui-fail/route-attribute-general-syntax.rs @@ -0,0 +1,130 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +// Check a path is supplied, at least. + +#[get()] //~ ERROR missing expected parameter +fn a0() {} + +// Check that it only works on functions. + +#[get("/")] +struct S; +//~^ ERROR expected `fn` +//~^^ HELP on functions + +#[get("/")] +enum A { } +//~^ ERROR expected `fn` +//~^^ HELP on functions + +#[get("/")] +trait Foo { } +//~^ ERROR expected `fn` +//~^^ HELP on functions + +#[get("/")] +impl S { } +//~^ ERROR expected `fn` +//~^^ HELP on functions + +// Check that additional parameter weirdness is caught. + +#[get("/", 123)] //~ ERROR expected +fn b0() {} + +#[get("/", "/")] //~ ERROR expected +fn b1() {} + +#[get(data = "<foo>", "/")] //~ ERROR unexpected keyed parameter +fn b2(foo: usize) {} + +#[get("/", unknown = "foo")] //~ ERROR unexpected +fn b3() {} + +#[get("/", ...)] //~ ERROR malformed +//~^ HELP expected syntax +fn b4() {} + +// Check that all identifiers are named + +#[get("/")] +fn c1(_: usize) {} //~ ERROR cannot be ignored +//~^ HELP must be of the form + +// Check that the path is a string, rank is an integer. + +#[get(100)] //~ ERROR expected string +fn d0() {} + +#[get('/')] //~ ERROR expected string +fn d1() {} + +#[get("/", rank = "1")] //~ ERROR expected integer +fn d2() {} + +#[get("/", rank = '1')] //~ ERROR expected integer +fn d3() {} + +// Check that formats are valid media-type strings. + +#[get("/", format = "applicationx-custom")] //~ ERROR invalid or unknown media type +fn e0() {} + +#[get("/", format = "")] //~ ERROR invalid or unknown media type +fn e1() {} + +#[get("/", format = "//")] //~ ERROR invalid or unknown media type +fn e2() {} + +#[get("/", format = "/")] //~ ERROR invalid or unknown media type +fn e3() {} + +#[get("/", format = "a/")] //~ ERROR invalid or unknown media type +fn e4() {} + +#[get("/", format = "/a")] //~ ERROR invalid or unknown media type +fn e5() {} + +#[get("/", format = "/a/")] //~ ERROR invalid or unknown media type +fn e6() {} + +#[get("/", format = "a/b/")] //~ ERROR invalid or unknown media type +fn e7() {} + +#[get("/", format = "unknown")] //~ ERROR unknown media type +fn e8() {} + +#[get("/", format = 12)] //~ ERROR expected string +fn e9() {} + +#[get("/", format = 'j')] //~ ERROR expected string +fn e10() {} + +#[get("/", format = "text//foo")] //~ ERROR invalid or unknown media type +fn e12() {} + +// Check that route methods are validated properly. + +#[route(CONNECT, "/")] //~ ERROR invalid HTTP method for route +//~^ HELP method must be one of +fn f0() {} + +#[route(FIX, "/")] //~ ERROR invalid HTTP method +//~^ HELP method must be one of +fn f1() {} + +#[route("hi", "/")] //~ ERROR expected identifier +//~^ HELP method must be one of +fn f2() {} + +#[route("GET", "/")] //~ ERROR expected identifier +//~^ HELP method must be one of +fn f3() {} + +#[route(120, "/")] //~ ERROR expected identifier +//~^ HELP method must be one of +fn f4() {} + +fn main() {} diff --git a/core/codegen_next/tests/ui-fail/route-attribute-general-syntax.stderr b/core/codegen_next/tests/ui-fail/route-attribute-general-syntax.stderr @@ -0,0 +1,216 @@ +error: missing expected parameter: `path` + --> $DIR/route-attribute-general-syntax.rs:7:1 + | +7 | #[get()] //~ ERROR missing expected parameter + | ^^^^^^^^ + +error: expected `fn` + --> $DIR/route-attribute-general-syntax.rs:13:1 + | +13 | struct S; + | ^^^^^^ + | + = help: #[get] can only be used on functions + +error: expected `fn` + --> $DIR/route-attribute-general-syntax.rs:18:1 + | +18 | enum A { } + | ^^^^ + | + = help: #[get] can only be used on functions + +error: expected `fn` + --> $DIR/route-attribute-general-syntax.rs:23:1 + | +23 | trait Foo { } + | ^^^^^ + | + = help: #[get] can only be used on functions + +error: expected `fn` + --> $DIR/route-attribute-general-syntax.rs:28:1 + | +28 | impl S { } + | ^^^^ + | + = help: #[get] can only be used on functions + +error: expected key/value pair + --> $DIR/route-attribute-general-syntax.rs:34:12 + | +34 | #[get("/", 123)] //~ ERROR expected + | ^^^ + +error: expected key/value pair + --> $DIR/route-attribute-general-syntax.rs:37:12 + | +37 | #[get("/", "/")] //~ ERROR expected + | ^^^ + +error: unexpected keyed parameter: expected literal or identifier + --> $DIR/route-attribute-general-syntax.rs:40:7 + | +40 | #[get(data = "<foo>", "/")] //~ ERROR unexpected keyed parameter + | ^^^^^^^^^^^^^^ + +error: unexpected attribute parameter: `unknown` + --> $DIR/route-attribute-general-syntax.rs:43:12 + | +43 | #[get("/", unknown = "foo")] //~ ERROR unexpected + | ^^^^^^^^^^^^^^^ + +error: malformed attribute + --> $DIR/route-attribute-general-syntax.rs:46:1 + | +46 | #[get("/", ...)] //~ ERROR malformed + | ^^^^^^^^^^^^^^^^ + | + = help: expected syntax: #[get(key = value, ..)] + +error: handler arguments cannot be ignored + --> $DIR/route-attribute-general-syntax.rs:53:7 + | +53 | fn c1(_: usize) {} //~ ERROR cannot be ignored + | ^^^^^^^^ + | + = help: all handler arguments must be of the form: `ident: Type` + +error: invalid value: expected string literal + --> $DIR/route-attribute-general-syntax.rs:58:7 + | +58 | #[get(100)] //~ ERROR expected string + | ^^^ + +error: invalid value: expected string literal + --> $DIR/route-attribute-general-syntax.rs:61:7 + | +61 | #[get('/')] //~ ERROR expected string + | ^^^ + +error: invalid value: expected integer literal + --> $DIR/route-attribute-general-syntax.rs:64:19 + | +64 | #[get("/", rank = "1")] //~ ERROR expected integer + | ^^^ + +error: invalid value: expected integer literal + --> $DIR/route-attribute-general-syntax.rs:67:19 + | +67 | #[get("/", rank = '1')] //~ ERROR expected integer + | ^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:72:21 + | +72 | #[get("/", format = "applicationx-custom")] //~ ERROR invalid or unknown media type + | ^^^^^^^^^^^^^^^^^^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:75:21 + | +75 | #[get("/", format = "")] //~ ERROR invalid or unknown media type + | ^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:78:21 + | +78 | #[get("/", format = "//")] //~ ERROR invalid or unknown media type + | ^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:81:21 + | +81 | #[get("/", format = "/")] //~ ERROR invalid or unknown media type + | ^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:84:21 + | +84 | #[get("/", format = "a/")] //~ ERROR invalid or unknown media type + | ^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:87:21 + | +87 | #[get("/", format = "/a")] //~ ERROR invalid or unknown media type + | ^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:90:21 + | +90 | #[get("/", format = "/a/")] //~ ERROR invalid or unknown media type + | ^^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:93:21 + | +93 | #[get("/", format = "a/b/")] //~ ERROR invalid or unknown media type + | ^^^^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:96:21 + | +96 | #[get("/", format = "unknown")] //~ ERROR unknown media type + | ^^^^^^^^^ + +error: invalid value: expected string literal + --> $DIR/route-attribute-general-syntax.rs:99:21 + | +99 | #[get("/", format = 12)] //~ ERROR expected string + | ^^ + +error: invalid value: expected string literal + --> $DIR/route-attribute-general-syntax.rs:102:21 + | +102 | #[get("/", format = 'j')] //~ ERROR expected string + | ^^^ + +error: invalid or unknown media type + --> $DIR/route-attribute-general-syntax.rs:105:21 + | +105 | #[get("/", format = "text//foo")] //~ ERROR invalid or unknown media type + | ^^^^^^^^^^^ + +error: invalid HTTP method for route handlers + --> $DIR/route-attribute-general-syntax.rs:110:9 + | +110 | #[route(CONNECT, "/")] //~ ERROR invalid HTTP method for route + | ^^^^^^^ + | + = help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` + +error: invalid HTTP method + --> $DIR/route-attribute-general-syntax.rs:114:9 + | +114 | #[route(FIX, "/")] //~ ERROR invalid HTTP method + | ^^^ + | + = help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` + +error: expected identifier, found string literal + --> $DIR/route-attribute-general-syntax.rs:118:9 + | +118 | #[route("hi", "/")] //~ ERROR expected identifier + | ^^^^ + | + = help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` + +error: expected identifier, found string literal + --> $DIR/route-attribute-general-syntax.rs:122:9 + | +122 | #[route("GET", "/")] //~ ERROR expected identifier + | ^^^^^ + | + = help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` + +error: expected identifier, found integer literal + --> $DIR/route-attribute-general-syntax.rs:126:9 + | +126 | #[route(120, "/")] //~ ERROR expected identifier + | ^^^ + | + = help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS` + +error: aborting due to 32 previous errors + diff --git a/core/codegen_next/tests/ui-fail/route-path-bad-syntax.rs b/core/codegen_next/tests/ui-fail/route-path-bad-syntax.rs @@ -0,0 +1,135 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +// Check that route paths are absolute and normalized. + +#[get("a")] //~ ERROR invalid path URI +//~^ HELP expected +fn f0() {} + +#[get("")] //~ ERROR invalid path URI +//~^ HELP expected +fn f1() {} + +#[get("a/b/c")] //~ ERROR invalid path URI +//~^ HELP expected +fn f2() {} + +#[get("/a///b")] //~ ERROR empty segments +//~^ NOTE expected +fn f3() {} + +#[get("/?bat&&")] //~ ERROR empty segments +fn f4() {} + +#[get("/?bat&&")] //~ ERROR empty segments +fn f5() {} + +#[get("/a/b//")] //~ ERROR empty segments +//~^ NOTE expected +fn f6() {} + +// Check that paths contain only valid URI characters + +#[get("/?????")] //~ ERROR invalid URI characters +//~^ NOTE cannot contain +fn g0() {} + +#[get("/!@#$%^&*()")] //~ ERROR invalid path URI +//~^ HELP origin form +fn g1() {} + +#[get("/a%20b")] //~ ERROR invalid URI characters +//~^ NOTE cannot contain +fn g2() {} + +#[get("/a?a%20b")] //~ ERROR invalid URI characters +//~^ NOTE cannot contain +fn g3() {} + +#[get("/a?a+b")] //~ ERROR invalid URI characters +//~^ NOTE cannot contain +fn g4() {} + +// Check that all declared parameters are accounted for + +#[get("/<name>")] //~ ERROR unused dynamic parameter +fn h0(_name: usize) {} //~ NOTE expected argument named `name` here + +#[get("/a?<r>")] //~ ERROR unused dynamic parameter +fn h1() {} //~ NOTE expected argument named `r` here + +#[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter +fn h2() {} //~ NOTE expected argument named `test` here + +#[get("/<_r>")] //~ ERROR unused dynamic parameter +fn h3() {} //~ NOTE expected argument named `_r` here + +#[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter +//~^ ERROR unused dynamic parameter +fn h4() {} //~ NOTE expected argument named `_r` here +//~^ NOTE expected argument named `b` here + +// Check dynamic parameters are valid idents + +#[get("/<foo_.>")] //~ ERROR `foo_.` is not a valid identifier +//~^ HELP must be valid +fn i0() {} + +#[get("/<foo*>")] //~ ERROR `foo*` is not a valid identifier +//~^ HELP must be valid +fn i1() {} + +#[get("/<!>")] //~ ERROR `!` is not a valid identifier +//~^ HELP must be valid +fn i2() {} + +#[get("/<name>:<id>")] //~ ERROR `name>:<id` is not a valid identifier +//~^ HELP must be valid +fn i3() {} + +// Check that a data parameter is exactly `<param>` + +#[get("/", data = "foo")] //~ ERROR malformed parameter +//~^ HELP must be of the form +fn j0() {} + +#[get("/", data = "<foo..>")] //~ ERROR malformed parameter +//~^ HELP must be of the form +fn j1() {} + +#[get("/", data = "<foo")] //~ ERROR missing a closing bracket +//~^ HELP did you mean +fn j2() {} + +#[get("/", data = "<test >")] //~ ERROR `test ` is not a valid identifier +//~^ HELP must be valid +fn j3() {} + +// Check that all identifiers are named + +#[get("/<_>")] //~ ERROR must be named +fn k0(_: usize) {} //~^ HELP use a name such as + +// Check that strange dynamic syntax is caught. + +#[get("/<>")] //~ ERROR cannot be empty +fn m0() {} + +#[get("/<id><")] //~ ERROR malformed parameter +//~^ HELP must be of the form +//~^^ HELP identifiers cannot contain +fn m1() {} + +#[get("/<<<<id><")] //~ ERROR malformed parameter +//~^ HELP must be of the form +//~^^ HELP identifiers cannot contain +fn m2() {} + +#[get("/<>name><")] //~ ERROR malformed parameter +//~^ HELP must be of the form +//~^^ HELP identifiers cannot contain +fn m3() {} + +fn main() { } diff --git a/core/codegen_next/tests/ui-fail/route-path-bad-syntax.stderr b/core/codegen_next/tests/ui-fail/route-path-bad-syntax.stderr @@ -0,0 +1,271 @@ +error: invalid path URI: expected token '/' but found 'a' at index 0 + --> $DIR/route-path-bad-syntax.rs:7:8 + | +7 | #[get("a")] //~ ERROR invalid path URI + | ^^ + | + = help: expected path in origin form: "/path/<param>" + +error: invalid path URI: expected token '/' but none was found at index 0 + --> $DIR/route-path-bad-syntax.rs:11:8 + | +11 | #[get("")] //~ ERROR invalid path URI + | ^ + | + = help: expected path in origin form: "/path/<param>" + +error: invalid path URI: expected token '/' but found 'a' at index 0 + --> $DIR/route-path-bad-syntax.rs:15:8 + | +15 | #[get("a/b/c")] //~ ERROR invalid path URI + | ^^^^^^ + | + = help: expected path in origin form: "/path/<param>" + +error: paths cannot contain empty segments + --> $DIR/route-path-bad-syntax.rs:19:7 + | +19 | #[get("/a///b")] //~ ERROR empty segments + | ^^^^^^^^ + | + = note: expected '/a/b', found '/a///b' + +error: query cannot contain empty segments + --> $DIR/route-path-bad-syntax.rs:23:10 + | +23 | #[get("/?bat&&")] //~ ERROR empty segments + | ^^^^^ + +error: query cannot contain empty segments + --> $DIR/route-path-bad-syntax.rs:26:10 + | +26 | #[get("/?bat&&")] //~ ERROR empty segments + | ^^^^^ + +error: paths cannot contain empty segments + --> $DIR/route-path-bad-syntax.rs:29:7 + | +29 | #[get("/a/b//")] //~ ERROR empty segments + | ^^^^^^^^ + | + = note: expected '/a/b', found '/a/b//' + +error: component contains invalid URI characters + --> $DIR/route-path-bad-syntax.rs:35:10 + | +35 | #[get("/?????")] //~ ERROR invalid URI characters + | ^^^^ + | + = note: components cannot contain '%' and '+' characters + +error: invalid path URI: expected EOF but found '#' at index 3 + --> $DIR/route-path-bad-syntax.rs:39:11 + | +39 | #[get("/!@#$%^&*()")] //~ ERROR invalid path URI + | ^^^^^^^^^ + | + = help: expected path in origin form: "/path/<param>" + +error: component contains invalid URI characters + --> $DIR/route-path-bad-syntax.rs:43:9 + | +43 | #[get("/a%20b")] //~ ERROR invalid URI characters + | ^^^^^ + | + = note: components cannot contain '%' and '+' characters + +error: component contains invalid URI characters + --> $DIR/route-path-bad-syntax.rs:47:11 + | +47 | #[get("/a?a%20b")] //~ ERROR invalid URI characters + | ^^^^^ + | + = note: components cannot contain '%' and '+' characters + +error: component contains invalid URI characters + --> $DIR/route-path-bad-syntax.rs:51:11 + | +51 | #[get("/a?a+b")] //~ ERROR invalid URI characters + | ^^^ + | + = note: components cannot contain '%' and '+' characters + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:57:9 + | +57 | #[get("/<name>")] //~ ERROR unused dynamic parameter + | ^^^^^^ + | +note: expected argument named `name` here + --> $DIR/route-path-bad-syntax.rs:58:7 + | +58 | fn h0(_name: usize) {} //~ NOTE expected argument named `name` here + | ^^^^^^^^^^^^ + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:60:11 + | +60 | #[get("/a?<r>")] //~ ERROR unused dynamic parameter + | ^^^ + | +note: expected argument named `r` here + --> $DIR/route-path-bad-syntax.rs:61:1 + | +61 | fn h1() {} //~ NOTE expected argument named `r` here + | ^^^^^^^^^^ + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:63:22 + | +63 | #[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter + | ^^^^^^ + | +note: expected argument named `test` here + --> $DIR/route-path-bad-syntax.rs:64:1 + | +64 | fn h2() {} //~ NOTE expected argument named `test` here + | ^^^^^^^^^^ + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:66:9 + | +66 | #[get("/<_r>")] //~ ERROR unused dynamic parameter + | ^^^^ + | +note: expected argument named `_r` here + --> $DIR/route-path-bad-syntax.rs:67:1 + | +67 | fn h3() {} //~ NOTE expected argument named `_r` here + | ^^^^^^^^^^ + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:69:9 + | +69 | #[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter + | ^^^^ + | +note: expected argument named `_r` here + --> $DIR/route-path-bad-syntax.rs:71:1 + | +71 | fn h4() {} //~ NOTE expected argument named `_r` here + | ^^^^^^^^^^ + +error: unused dynamic parameter + --> $DIR/route-path-bad-syntax.rs:69:14 + | +69 | #[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter + | ^^^ + | +note: expected argument named `b` here + --> $DIR/route-path-bad-syntax.rs:71:1 + | +71 | fn h4() {} //~ NOTE expected argument named `_r` here + | ^^^^^^^^^^ + +error: `foo_.` is not a valid identifier + --> $DIR/route-path-bad-syntax.rs:76:9 + | +76 | #[get("/<foo_.>")] //~ ERROR `foo_.` is not a valid identifier + | ^^^^^^^ + | + = help: parameter names must be valid identifiers + +error: `foo*` is not a valid identifier + --> $DIR/route-path-bad-syntax.rs:80:9 + | +80 | #[get("/<foo*>")] //~ ERROR `foo*` is not a valid identifier + | ^^^^^^ + | + = help: parameter names must be valid identifiers + +error: `!` is not a valid identifier + --> $DIR/route-path-bad-syntax.rs:84:9 + | +84 | #[get("/<!>")] //~ ERROR `!` is not a valid identifier + | ^^^ + | + = help: parameter names must be valid identifiers + +error: `name>:<id` is not a valid identifier + --> $DIR/route-path-bad-syntax.rs:88:9 + | +88 | #[get("/<name>:<id>")] //~ ERROR `name>:<id` is not a valid identifier + | ^^^^^^^^^^^ + | + = help: parameter names must be valid identifiers + +error: malformed parameter + --> $DIR/route-path-bad-syntax.rs:94:20 + | +94 | #[get("/", data = "foo")] //~ ERROR malformed parameter + | ^^^ + | + = help: parameter must be of the form '<param>' + +error: malformed parameter + --> $DIR/route-path-bad-syntax.rs:98:20 + | +98 | #[get("/", data = "<foo..>")] //~ ERROR malformed parameter + | ^^^^^^^ + | + = help: parameter must be of the form '<param>' + +error: parameter is missing a closing bracket + --> $DIR/route-path-bad-syntax.rs:102:20 + | +102 | #[get("/", data = "<foo")] //~ ERROR missing a closing bracket + | ^^^^ + | + = help: did you mean '<foo>'? + +error: `test ` is not a valid identifier + --> $DIR/route-path-bad-syntax.rs:106:20 + | +106 | #[get("/", data = "<test >")] //~ ERROR `test ` is not a valid identifier + | ^^^^^^^ + | + = help: parameter names must be valid identifiers + +error: parameters must be named + --> $DIR/route-path-bad-syntax.rs:112:9 + | +112 | #[get("/<_>")] //~ ERROR must be named + | ^^^ + | + = help: use a name such as `_guard` or `_param` + +error: parameter names cannot be empty + --> $DIR/route-path-bad-syntax.rs:117:9 + | +117 | #[get("/<>")] //~ ERROR cannot be empty + | ^^ + +error: malformed parameter or identifier + --> $DIR/route-path-bad-syntax.rs:120:9 + | +120 | #[get("/<id><")] //~ ERROR malformed parameter + | ^^^^^ + | + = help: parameters must be of the form '<param>' + = help: identifiers cannot contain '<' or '>' + +error: malformed parameter or identifier + --> $DIR/route-path-bad-syntax.rs:125:9 + | +125 | #[get("/<<<<id><")] //~ ERROR malformed parameter + | ^^^^^^^^ + | + = help: parameters must be of the form '<param>' + = help: identifiers cannot contain '<' or '>' + +error: malformed parameter or identifier + --> $DIR/route-path-bad-syntax.rs:130:9 + | +130 | #[get("/<>name><")] //~ ERROR malformed parameter + | ^^^^^^^^ + | + = help: parameters must be of the form '<param>' + = help: identifiers cannot contain '<' or '>' + +error: aborting due to 31 previous errors + diff --git a/core/codegen_next/tests/ui-fail/route-type-errors.rs b/core/codegen_next/tests/ui-fail/route-type-errors.rs @@ -0,0 +1,33 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +struct Q; + +#[get("/<foo>")] +fn f0(foo: Q) {} //~ ERROR FromParam + +#[get("/<foo..>")] +fn f1(foo: Q) {} //~ ERROR FromSegments + +#[get("/?<foo>")] +fn f2(foo: Q) {} //~ ERROR FromFormValue + +#[get("/?<foo..>")] +fn f3(foo: Q) {} //~ ERROR FromQuery + +#[post("/", data = "<foo>")] +fn f4(foo: Q) {} //~ ERROR FromData + +#[get("/<foo>")] +fn f5(a: Q, foo: Q) {} +//~^ ERROR FromParam +//~^^ ERROR FromRequest + +#[get("/<foo>/other/<bar>/<good>/okay")] +fn f6(a: Q, foo: Q, good: usize, bar: Q) {} +//~^ ERROR FromParam +//~^^ ERROR FromParam +//~^^^ ERROR FromRequest + +fn main() { } diff --git a/core/codegen_next/tests/ui-fail/route-type-errors.stderr b/core/codegen_next/tests/ui-fail/route-type-errors.stderr @@ -0,0 +1,65 @@ +error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied + --> $DIR/route-type-errors.rs:8:7 + | +8 | fn f0(foo: Q) {} //~ ERROR FromParam + | ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromSegments<'_>` is not satisfied + --> $DIR/route-type-errors.rs:11:7 + | +11 | fn f1(foo: Q) {} //~ ERROR FromSegments + | ^^^^^^ the trait `rocket::request::FromSegments<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromFormValue<'_>` is not satisfied + --> $DIR/route-type-errors.rs:14:7 + | +14 | fn f2(foo: Q) {} //~ ERROR FromFormValue + | ^^^^^^ the trait `rocket::request::FromFormValue<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromQuery<'_>` is not satisfied + --> $DIR/route-type-errors.rs:17:7 + | +17 | fn f3(foo: Q) {} //~ ERROR FromQuery + | ^^^^^^ the trait `rocket::request::FromQuery<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::data::FromDataSimple` is not satisfied + --> $DIR/route-type-errors.rs:20:7 + | +20 | fn f4(foo: Q) {} //~ ERROR FromData + | ^^^^^^ the trait `rocket::data::FromDataSimple` is not implemented for `Q` + | + = note: required because of the requirements on the impl of `rocket::data::FromData<'_>` for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromRequest<'_, '_>` is not satisfied + --> $DIR/route-type-errors.rs:23:7 + | +23 | fn f5(a: Q, foo: Q) {} + | ^^^^ the trait `rocket::request::FromRequest<'_, '_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied + --> $DIR/route-type-errors.rs:23:13 + | +23 | fn f5(a: Q, foo: Q) {} + | ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromRequest<'_, '_>` is not satisfied + --> $DIR/route-type-errors.rs:28:7 + | +28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {} + | ^^^^ the trait `rocket::request::FromRequest<'_, '_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied + --> $DIR/route-type-errors.rs:28:13 + | +28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {} + | ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q` + +error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied + --> $DIR/route-type-errors.rs:28:34 + | +28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {} + | ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q` + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/core/codegen_next/tests/ui-fail/route-warnings.rs b/core/codegen_next/tests/ui-fail/route-warnings.rs @@ -0,0 +1,26 @@ +// must-compile-successfully + +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +// Check for unknown media types. + +#[get("/", format = "application/x-custom")] //~ WARNING not a known media type +fn f0() {} + +#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type +fn f1() {} + +#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type +fn f2() {} + +// Check if a data argument is used with a usually non-payload bearing method. + +#[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method +fn g0(_foo: rocket::Data) {} + +#[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method +fn g1(_foo: rocket::Data) {} + +fn main() { } diff --git a/core/codegen_next/tests/ui-fail/route-warnings.stderr b/core/codegen_next/tests/ui-fail/route-warnings.stderr @@ -0,0 +1,42 @@ +warning: 'application/x-custom' is not a known media type + --> $DIR/route-warnings.rs:9:21 + | +9 | #[get("/", format = "application/x-custom")] //~ WARNING not a known media type + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: 'x-custom/plain' is not a known media type + --> $DIR/route-warnings.rs:12:21 + | +12 | #[get("/", format = "x-custom/plain")] //~ WARNING not a known media type + | ^^^^^^^^^^^^^^^^ + +warning: 'x-custom/x-custom' is not a known media type + --> $DIR/route-warnings.rs:15:21 + | +15 | #[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type + | ^^^^^^^^^^^^^^^^^^^ + +warning: `data` used with non-payload-supporting method + --> $DIR/route-warnings.rs:20:12 + | +20 | #[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method + | ^^^^^^^^^^^^^^^ + | +note: 'GET' does not typically support payloads + --> $DIR/route-warnings.rs:20:3 + | +20 | #[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method + | ^^^ + +warning: `data` used with non-payload-supporting method + --> $DIR/route-warnings.rs:23:13 + | +23 | #[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method + | ^^^^^^^^^^^^^^^ + | +note: 'HEAD' does not typically support payloads + --> $DIR/route-warnings.rs:23:3 + | +23 | #[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method + | ^^^^ + diff --git a/core/codegen_next/tests/ui-fail/typed-uri-bad-type.rs b/core/codegen_next/tests/ui-fail/typed-uri-bad-type.rs @@ -0,0 +1,29 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +use rocket::http::RawStr; +use rocket::request::FromParam; + +struct S; + +impl<'a> FromParam<'a> for S { + type Error = (); + fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> { Ok(S) } +} + +#[post("/<id>")] +fn simple(id: i32) { } + +#[post("/<id>/<name>")] +fn not_uri_display(id: i32, name: S) { } + +#[post("/<id>/<name>")] +fn not_uri_display_but_unused(id: i32, name: S) { } + +fn main() { + uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str> + uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str> + uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64> + uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_> +} diff --git a/core/codegen_next/tests/ui-fail/typed-uri-bad-type.stderr b/core/codegen_next/tests/ui-fail/typed-uri-bad-type.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied + --> $DIR/typed-uri-bad-type.rs:25:23 + | +25 | uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str> + | ^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32` + | + = note: required by `rocket::http::uri::FromUriParam::from_uri_param` + +error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied + --> $DIR/typed-uri-bad-type.rs:26:18 + | +26 | uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str> + | ^^^^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32` + | + = note: required by `rocket::http::uri::FromUriParam::from_uri_param` + +error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<i64>` is not satisfied + --> $DIR/typed-uri-bad-type.rs:27:23 + | +27 | uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64> + | ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<i64>` is not implemented for `i32` + | + = note: required by `rocket::http::uri::FromUriParam::from_uri_param` + +error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<_>` is not satisfied + --> $DIR/typed-uri-bad-type.rs:28:31 + | +28 | uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_> + | ^ the trait `rocket::http::uri::FromUriParam<_>` is not implemented for `S` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/core/codegen_next/tests/ui-fail/typed-uris-bad-params.rs b/core/codegen_next/tests/ui-fail/typed-uris-bad-params.rs @@ -0,0 +1,72 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +use std::fmt; + +use rocket::http::Cookies; + +#[post("/<id>")] +fn has_one(id: i32) { } + +#[post("/<id>")] +fn has_one_guarded(cookies: Cookies, id: i32) { } + +#[post("/<id>/<name>")] +fn has_two(cookies: Cookies, id: i32, name: String) { } + +fn main() { + uri!(has_one); //~ ERROR expects 1 parameter but 0 + + uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2 + uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2 + uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2 + + uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3 + uri!(has_two: 10); //~ ERROR expects 2 parameters but 1 + + uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters + //~^ HELP unknown parameter: `name` + + uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters + //~^ HELP unknown parameter: `name` + + uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters + //~^ HELP unknown parameters: `name`, `age` + + uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters + //~^ HELP unknown parameters: `name`, `age` + //~^^ HELP duplicate parameter: `id` + + uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters + //~^ HELP duplicate parameter: `id` + + uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters + //~^ HELP duplicate parameter: `id` + + uri!(has_one: name = "hi"); //~ ERROR invalid parameters + //~^ HELP unknown parameter: `name` + //~^^ HELP missing parameter: `id` + + uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters + //~^ HELP unknown parameter: `cookies` + + uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters + //~^ HELP unknown parameter: `cookies` + + uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters + //~^ HELP duplicate parameter: `id` + //~^^ HELP missing parameter: `name` + + uri!(has_two: name = "hi"); //~ ERROR invalid parameters + //~^ HELP missing parameter: `id` + + uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters + //~^ HELP duplicate parameter: `id` + //~^^ HELP missing parameter: `name` + //~^^^ HELP unknown parameter: `cookies` + + uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters + //~^ HELP missing parameter: `name` + //~^^ HELP unknown parameter: `cookies` +} diff --git a/core/codegen_next/tests/ui-fail/typed-uris-bad-params.stderr b/core/codegen_next/tests/ui-fail/typed-uris-bad-params.stderr @@ -0,0 +1,229 @@ +error: `has_one` route uri expects 1 parameter but 0 were supplied + --> $DIR/typed-uris-bad-params.rs:19:10 + | +19 | uri!(has_one); //~ ERROR expects 1 parameter but 0 + | ^^^^^^^ + | + = note: expected parameter: id: i32 + +error: `has_one` route uri expects 1 parameter but 2 were supplied + --> $DIR/typed-uris-bad-params.rs:21:19 + | +21 | uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2 + | ^^^^^ + | + = note: expected parameter: id: i32 + +error: `has_one` route uri expects 1 parameter but 2 were supplied + --> $DIR/typed-uris-bad-params.rs:22:19 + | +22 | uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2 + | ^^^^^^^^^^^^ + | + = note: expected parameter: id: i32 + +error: `has_one_guarded` route uri expects 1 parameter but 2 were supplied + --> $DIR/typed-uris-bad-params.rs:23:27 + | +23 | uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2 + | ^^^^^^^^^ + | + = note: expected parameter: id: i32 + +error: `has_two` route uri expects 2 parameters but 3 were supplied + --> $DIR/typed-uris-bad-params.rs:25:19 + | +25 | uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3 + | ^^^^^^^^^^^^^^^^^ + | + = note: expected parameters: id: i32, name: String + +error: `has_two` route uri expects 2 parameters but 1 was supplied + --> $DIR/typed-uris-bad-params.rs:26:19 + | +26 | uri!(has_two: 10); //~ ERROR expects 2 parameters but 1 + | ^^ + | + = note: expected parameters: id: i32, name: String + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:28:19 + | +28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameter: `name` + --> $DIR/typed-uris-bad-params.rs:28:29 + | +28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters + | ^^^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:31:19 + | +31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameter: `name` + --> $DIR/typed-uris-bad-params.rs:31:19 + | +31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters + | ^^^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:34:19 + | +34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameters: `name`, `age` + --> $DIR/typed-uris-bad-params.rs:34:19 + | +34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters + | ^^^^ ^^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:37:19 + | +37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameters: `name`, `age` + --> $DIR/typed-uris-bad-params.rs:37:19 + | +37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters + | ^^^^ ^^^ +help: duplicate parameter: `id` + --> $DIR/typed-uris-bad-params.rs:37:51 + | +37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters + | ^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:41:19 + | +41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: duplicate parameter: `id` + --> $DIR/typed-uris-bad-params.rs:41:29 + | +41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters + | ^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:44:19 + | +44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: duplicate parameter: `id` + --> $DIR/typed-uris-bad-params.rs:44:29 + | +44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters + | ^^ + +error: invalid parameters for `has_one` route uri + --> $DIR/typed-uris-bad-params.rs:47:19 + | +47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters + | ^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 + = help: missing parameter: `id` +help: unknown parameter: `name` + --> $DIR/typed-uris-bad-params.rs:47:19 + | +47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters + | ^^^^ + +error: invalid parameters for `has_one_guarded` route uri + --> $DIR/typed-uris-bad-params.rs:51:27 + | +51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:51:27 + | +51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters + | ^^^^^^^ + +error: invalid parameters for `has_one_guarded` route uri + --> $DIR/typed-uris-bad-params.rs:54:27 + | +54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32 +help: unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:54:37 + | +54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters + | ^^^^^^^ + +error: invalid parameters for `has_two` route uri + --> $DIR/typed-uris-bad-params.rs:57:19 + | +57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32, name: String + = help: missing parameter: `name` +help: duplicate parameter: `id` + --> $DIR/typed-uris-bad-params.rs:57:29 + | +57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters + | ^^ + +error: invalid parameters for `has_two` route uri + --> $DIR/typed-uris-bad-params.rs:61:19 + | +61 | uri!(has_two: name = "hi"); //~ ERROR invalid parameters + | ^^^^^^^^^^^ + | + = note: uri parameters are: id: i32, name: String + = help: missing parameter: `id` + +error: invalid parameters for `has_two` route uri + --> $DIR/typed-uris-bad-params.rs:64:19 + | +64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32, name: String + = help: missing parameter: `name` +help: unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:64:19 + | +64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters + | ^^^^^^^ +help: duplicate parameter: `id` + --> $DIR/typed-uris-bad-params.rs:64:45 + | +64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters + | ^^ ^^ + +error: invalid parameters for `has_two` route uri + --> $DIR/typed-uris-bad-params.rs:69:19 + | +69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32, name: String + = help: missing parameter: `name` +help: unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:69:29 + | +69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters + | ^^^^^^^ + +error: aborting due to 19 previous errors + diff --git a/core/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.rs b/core/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.rs @@ -0,0 +1,19 @@ +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] + +#[macro_use] extern crate rocket; + +#[post("/<id>/<name>")] +fn simple(id: i32, name: String) -> &'static str { "" } + +fn main() { + uri!(simple: id = 100, "Hello"); //~ ERROR named and unnamed + uri!(simple: "Hello", id = 100); //~ ERROR named and unnamed + uri!(simple,); //~ ERROR expected `:` + uri!(simple:); //~ ERROR argument list + uri!("/mount"); //~ ERROR route path + uri!("/mount",); //~ ERROR expected identifier + uri!("mount", simple); //~ invalid mount point + uri!("/mount/<id>", simple); //~ invalid mount point + uri!(); //~ unexpected end of input + uri!(simple: id = ); //~ expected expression +} diff --git a/core/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.stderr b/core/codegen_next/tests/ui-fail/typed-uris-invalid-syntax.stderr @@ -0,0 +1,62 @@ +error: named and unnamed parameters cannot be mixed + --> $DIR/typed-uris-invalid-syntax.rs:9:18 + | +9 | uri!(simple: id = 100, "Hello"); //~ ERROR named and unnamed + | ^^^^^^^^^^^^^^^^^ + +error: named and unnamed parameters cannot be mixed + --> $DIR/typed-uris-invalid-syntax.rs:10:18 + | +10 | uri!(simple: "Hello", id = 100); //~ ERROR named and unnamed + | ^^^^^^^^^^^^^^^^^ + +error: expected `:` + --> $DIR/typed-uris-invalid-syntax.rs:11:16 + | +11 | uri!(simple,); //~ ERROR expected `:` + | ^ + +error: expected argument list after `:` + --> $DIR/typed-uris-invalid-syntax.rs:12:16 + | +12 | uri!(simple:); //~ ERROR argument list + | ^ + +error: unexpected end of input: expected ',' followed by route path + --> $DIR/typed-uris-invalid-syntax.rs:13:10 + | +13 | uri!("/mount"); //~ ERROR route path + | ^^^^^^^^ + +error: unexpected end of input, expected identifier + --> $DIR/typed-uris-invalid-syntax.rs:14:5 + | +14 | uri!("/mount",); //~ ERROR expected identifier + | ^^^^^^^^^^^^^^^^ + +error: invalid mount point; mount points must be static, absolute URIs: `/example` + --> $DIR/typed-uris-invalid-syntax.rs:15:10 + | +15 | uri!("mount", simple); //~ invalid mount point + | ^^^^^^^ + +error: invalid mount point; mount points must be static, absolute URIs: `/example` + --> $DIR/typed-uris-invalid-syntax.rs:16:10 + | +16 | uri!("/mount/<id>", simple); //~ invalid mount point + | ^^^^^^^^^^^^^ + +error: unexpected end of input, call to `uri!` cannot be empty + --> $DIR/typed-uris-invalid-syntax.rs:17:5 + | +17 | uri!(); //~ unexpected end of input + | ^^^^^^^ + +error: unexpected end of input, expected expression + --> $DIR/typed-uris-invalid-syntax.rs:18:5 + | +18 | uri!(simple: id = ); //~ expected expression + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/core/codegen_next/tests/ui-fail/update-references.sh b/core/codegen_next/tests/ui-fail/update-references.sh @@ -34,7 +34,7 @@ shift shopt -s nullglob while [[ "$1" != "" ]]; do - for EXT in "stderr" "stdout" "fixed"; do + for EXT in "stderr" "fixed"; do for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml @@ -35,4 +35,3 @@ optional = true [dev-dependencies] rocket = { version = "0.4.0-dev", path = "../lib" } -rocket_codegen = { version = "0.4.0-dev", path = "../codegen" } diff --git a/core/http/src/method.rs b/core/http/src/method.rs @@ -40,7 +40,15 @@ impl Method { } /// Returns `true` if an HTTP request with the method represented by `self` - /// supports a payload. + /// always supports a payload. + /// + /// The following methods always support payloads: + /// + /// * `PUT`, `POST`, `DELETE`, `PATCH` + /// + /// The following methods _do not_ always support payloads: + /// + /// * `GET`, `HEAD`, `CONNECT`, `TRACE`, `OPTIONS` /// /// # Example /// diff --git a/core/http/src/route.rs b/core/http/src/route.rs @@ -108,7 +108,8 @@ impl<'a> RouteSegment<'a> { return Ok(RouteSegment { string, source, name, kind, index }); } else if segment.is_empty() { return Err(Empty); - } else if segment.starts_with('<') && segment.len() > 1 { + } else if segment.starts_with('<') && segment.len() > 1 + && !segment[1..].contains('<') && !segment[1..].contains('>') { return Err(MissingClose); } else if segment.contains('>') || segment.contains('<') { return Err(Malformed); diff --git a/core/http/src/uri/uri_display.rs b/core/http/src/uri/uri_display.rs @@ -105,8 +105,7 @@ use self::priv_encode_set::PATH_ENCODE_SET; /// dynamic parameter type. /// /// ```rust -/// # #![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)] -/// # #![plugin(rocket_codegen)] +/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] /// # #[macro_use] extern crate rocket; /// # fn main() { } /// use rocket::http::RawStr; @@ -229,7 +228,9 @@ macro_rules! impl_with_display { use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; impl_with_display! { - i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize, + f32, f64, bool, IpAddr, Ipv4Addr, Ipv6Addr } diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml @@ -34,7 +34,6 @@ isatty = "0.1" [dev-dependencies] # TODO: Find a way to not depend on this. lazy_static = "1.0" -rocket_codegen = { version = "0.4.0-dev", path = "../codegen" } [build-dependencies] yansi = "0.4" diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs @@ -1,5 +1,5 @@ #![feature(specialization)] -#![feature(plugin, decl_macro)] +#![feature(decl_macro)] #![feature(try_trait)] #![feature(fnbox)] #![feature(never_type)] @@ -30,12 +30,10 @@ //! //! ## Libraries //! -//! Rocket's functionality is split into three crates: +//! Rocket's functionality is split into two crates: //! //! 1. [Core](/rocket) - The core library. Needed by every Rocket application. -//! 2. [Codegen](/rocket_codegen) - Core code generation plugin. Should always -//! be used alongside `rocket`, though it's not necessary. -//! 3. [Contrib](/rocket_contrib) - Provides useful functionality for many +//! 2. [Contrib](/rocket_contrib) - Provides useful functionality for many //! Rocket applications. Completely optional. //! //! ## Usage @@ -48,7 +46,6 @@ //! ```rust,ignore //! [dependencies] //! rocket = "*" -//! rocket_codegen = "*" //! ``` //! //! If you'll be deploying your project to [crates.io](https://crates.io), diff --git a/core/lib/src/request/form/from_form.rs b/core/lib/src/request/form/from_form.rs @@ -5,13 +5,11 @@ use request::FormItems; /// /// # Deriving /// -/// This trait can be automatically derived via the -/// [rocket_codegen](/rocket_codegen) plugin. When deriving `FromForm`, every -/// field in the structure must implement -/// [FromFormValue](trait.FromFormValue.html). Rocket validates each field in -/// the structure by calling its `FromFormValue` implementation. You may wish to -/// implement `FromFormValue` for your own types for custom, automatic -/// validation. +/// This trait can be automatically derived. When deriving `FromForm`, every +/// field in the structure must implement [`FromFormValue`]. Rocket validates +/// each field in the structure by calling its `FromFormValue` implementation. +/// You may wish to implement `FromFormValue` for your own types for custom, +/// automatic validation. /// /// ```rust /// #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs @@ -428,11 +428,12 @@ impl<'r> Request<'r> { }).as_ref() } - /// Returns the media type "format" of the request if it is present. + /// Returns the media type "format" of the request. /// /// The "format" of a request is either the Content-Type, if the request /// methods indicates support for a payload, or the preferred media type in - /// the Accept header otherwise. + /// the Accept header otherwise. If the method indicates no payload and no + /// Accept header is specified, a media type of `Any` is returned. /// /// The media type returned from this method is used to match against the /// `format` route attribute. @@ -455,13 +456,16 @@ impl<'r> Request<'r> { /// # }); /// ``` pub fn format(&self) -> Option<&MediaType> { + static ANY: MediaType = MediaType::Any; if self.method().supports_payload() { self.content_type().map(|ct| ct.media_type()) } else { // FIXME: Should we be using `accept_first` or `preferred`? Or // should we be checking neither and instead pass things through // where the client accepts the thing at all? - self.accept().map(|accept| accept.preferred().media_type()) + self.accept() + .map(|accept| accept.preferred().media_type()) + .or(Some(&ANY)) } } diff --git a/core/lib/src/router/collider.rs b/core/lib/src/router/collider.rs @@ -21,10 +21,7 @@ impl Route { self.method == other.method && self.rank == other.rank && paths_collide(self, other) - && match (self.format.as_ref(), other.format.as_ref()) { - (Some(a), Some(b)) => media_types_collide(a, b), - _ => true - } + && formats_collide(self, other) } /// Determines if this route matches against the given request. This means @@ -43,13 +40,7 @@ impl Route { self.method == req.method() && paths_match(self, req) && queries_match(self, req) - && match self.format { - Some(ref a) => match req.format() { - Some(ref b) => media_types_collide(a, b), - None => false - }, - None => true - } + && formats_match(self, req) } } @@ -116,6 +107,40 @@ fn queries_match(route: &Route, request: &Request) -> bool { true } +fn formats_collide(route: &Route, other: &Route) -> bool { + // When matching against the `Accept` header, the client can always provide + // a media type that will cause a collision through non-specificity. + if !route.method.supports_payload() { + return true; + } + + // When matching against the `Content-Type` header, we'll only consider + // requests as having a `Content-Type` if they're fully specified. If a + // route doesn't have a `format`, it accepts all `Content-Type`s. If a + // request doesn't have a format, it only matches routes without a format. + match (route.format.as_ref(), other.format.as_ref()) { + (Some(a), Some(b)) => media_types_collide(a, b), + _ => true + } +} + +fn formats_match(route: &Route, request: &Request) -> bool { + if !route.method.supports_payload() { + route.format.as_ref() + .and_then(|a| request.format().map(|b| (a, b))) + .map(|(a, b)| media_types_collide(a, b)) + .unwrap_or(true) + } else { + match route.format.as_ref() { + Some(a) => match request.format() { + Some(b) if b.specificity() == 2 => media_types_collide(a, b), + _ => false + } + None => true + } + } +} + fn media_types_collide(first: &MediaType, other: &MediaType) -> bool { let collide = |a, b| a == "*" || b == "*" || a == b; collide(first.top(), other.top()) && collide(first.sub(), other.sub()) @@ -313,15 +338,15 @@ mod tests { assert!(!mt_mt_collide("something/*", "random/else")); } - fn r_mt_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool + fn r_mt_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>> { - let mut route_a = Route::new(m1, "/", dummy_handler); + let mut route_a = Route::new(m, "/", dummy_handler); if let Some(mt_str) = mt1.into() { route_a.format = Some(mt_str.parse::<MediaType>().unwrap()); } - let mut route_b = Route::new(m2, "/", dummy_handler); + let mut route_b = Route::new(m, "/", dummy_handler); if let Some(mt_str) = mt2.into() { route_b.format = Some(mt_str.parse::<MediaType>().unwrap()); } @@ -331,23 +356,44 @@ mod tests { #[test] fn test_route_content_type_colliions() { - assert!(r_mt_mt_collide(Get, "application/json", Get, "application/json")); - assert!(r_mt_mt_collide(Get, "*/json", Get, "application/json")); - assert!(r_mt_mt_collide(Get, "*/json", Get, "application/*")); - assert!(r_mt_mt_collide(Get, "text/html", Get, "text/*")); - assert!(r_mt_mt_collide(Get, "any/thing", Get, "*/*")); - - assert!(r_mt_mt_collide(Get, None, Get, "text/*")); - assert!(r_mt_mt_collide(Get, None, Get, "text/html")); - assert!(r_mt_mt_collide(Get, None, Get, "*/*")); - assert!(r_mt_mt_collide(Get, "text/html", Get, None)); - assert!(r_mt_mt_collide(Get, "*/*", Get, None)); - assert!(r_mt_mt_collide(Get, "application/json", Get, None)); - - assert!(!r_mt_mt_collide(Get, "text/html", Get, "application/*")); - assert!(!r_mt_mt_collide(Get, "application/html", Get, "text/*")); - assert!(!r_mt_mt_collide(Get, "*/json", Get, "text/html")); - assert!(!r_mt_mt_collide(Get, "text/html", Get, "text/css")); + // non-payload bearing routes always collide + assert!(r_mt_mt_collide(Get, "application/json", "application/json")); + assert!(r_mt_mt_collide(Get, "*/json", "application/json")); + assert!(r_mt_mt_collide(Get, "*/json", "application/*")); + assert!(r_mt_mt_collide(Get, "text/html", "text/*")); + assert!(r_mt_mt_collide(Get, "any/thing", "*/*")); + + assert!(r_mt_mt_collide(Get, None, "text/*")); + assert!(r_mt_mt_collide(Get, None, "text/html")); + assert!(r_mt_mt_collide(Get, None, "*/*")); + assert!(r_mt_mt_collide(Get, "text/html", None)); + assert!(r_mt_mt_collide(Get, "*/*", None)); + assert!(r_mt_mt_collide(Get, "application/json", None)); + + assert!(r_mt_mt_collide(Get, "application/*", "text/*")); + assert!(r_mt_mt_collide(Get, "application/json", "text/*")); + assert!(r_mt_mt_collide(Get, "application/json", "text/html")); + assert!(r_mt_mt_collide(Get, "text/html", "text/html")); + + // payload bearing routes collide if the media types collide + assert!(r_mt_mt_collide(Post, "application/json", "application/json")); + assert!(r_mt_mt_collide(Post, "*/json", "application/json")); + assert!(r_mt_mt_collide(Post, "*/json", "application/*")); + assert!(r_mt_mt_collide(Post, "text/html", "text/*")); + assert!(r_mt_mt_collide(Post, "any/thing", "*/*")); + + assert!(r_mt_mt_collide(Post, None, "text/*")); + assert!(r_mt_mt_collide(Post, None, "text/html")); + assert!(r_mt_mt_collide(Post, None, "*/*")); + assert!(r_mt_mt_collide(Post, "text/html", None)); + assert!(r_mt_mt_collide(Post, "*/*", None)); + assert!(r_mt_mt_collide(Post, "application/json", None)); + + assert!(!r_mt_mt_collide(Post, "text/html", "application/*")); + assert!(!r_mt_mt_collide(Post, "application/html", "text/*")); + assert!(!r_mt_mt_collide(Post, "*/json", "text/html")); + assert!(!r_mt_mt_collide(Post, "text/html", "text/css")); + assert!(!r_mt_mt_collide(Post, "other/html", "text/html")); } fn req_route_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool @@ -381,8 +427,9 @@ mod tests { assert!(req_route_mt_collide(Get, "application/json", "application/json")); assert!(req_route_mt_collide(Get, "text/html", "text/html")); assert!(req_route_mt_collide(Get, "text/html", "*/*")); - assert!(req_route_mt_collide(Get, None, "text/html")); assert!(req_route_mt_collide(Get, None, "*/*")); + assert!(req_route_mt_collide(Get, None, "text/*")); + assert!(req_route_mt_collide(Get, None, "text/html")); assert!(req_route_mt_collide(Get, None, "application/json")); assert!(req_route_mt_collide(Post, "text/html", None)); @@ -394,10 +441,19 @@ mod tests { assert!(req_route_mt_collide(Get, "application/json", None)); assert!(req_route_mt_collide(Get, "x-custom/anything", None)); assert!(req_route_mt_collide(Get, None, None)); + assert!(req_route_mt_collide(Get, None, "text/html")); + assert!(req_route_mt_collide(Get, None, "application/json")); assert!(req_route_mt_collide(Get, "text/html, text/plain", "text/html")); assert!(req_route_mt_collide(Get, "text/html; q=0.5, text/xml", "text/xml")); + assert!(!req_route_mt_collide(Post, None, "text/html")); + assert!(!req_route_mt_collide(Post, None, "text/*")); + assert!(!req_route_mt_collide(Post, None, "*/text")); + assert!(!req_route_mt_collide(Post, None, "*/*")); + assert!(!req_route_mt_collide(Post, None, "text/html")); + assert!(!req_route_mt_collide(Post, None, "application/json")); + assert!(!req_route_mt_collide(Post, "application/json", "text/html")); assert!(!req_route_mt_collide(Post, "application/json", "text/*")); assert!(!req_route_mt_collide(Post, "application/json", "*/xml")); @@ -406,7 +462,6 @@ mod tests { assert!(!req_route_mt_collide(Get, "application/json", "*/xml")); assert!(!req_route_mt_collide(Post, None, "text/html")); - assert!(!req_route_mt_collide(Post, None, "*/*")); assert!(!req_route_mt_collide(Post, None, "application/json")); } diff --git a/examples/config/tests/development.rs b/examples/config/tests/development.rs @@ -1,5 +1,4 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; diff --git a/examples/config/tests/production.rs b/examples/config/tests/production.rs @@ -1,5 +1,4 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; diff --git a/examples/config/tests/staging.rs b/examples/config/tests/staging.rs @@ -1,5 +1,4 @@ -#![feature(plugin, decl_macro, proc_macro_non_items)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; diff --git a/examples/redirect/Cargo.toml b/examples/redirect/Cargo.toml @@ -6,4 +6,3 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } -rocket_codegen = { path = "../../core/codegen" } diff --git a/examples/redirect/src/main.rs b/examples/redirect/src/main.rs @@ -1,5 +1,4 @@ -#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; diff --git a/examples/session/Cargo.toml b/examples/session/Cargo.toml @@ -6,7 +6,6 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } -rocket_codegen = { path = "../../core/codegen" } [dependencies.rocket_contrib] path = "../../contrib/lib" diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs @@ -1,5 +1,4 @@ -#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro, never_type)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro, never_type)] #[macro_use] extern crate rocket; extern crate rocket_contrib; diff --git a/examples/tera_templates/Cargo.toml b/examples/tera_templates/Cargo.toml @@ -6,7 +6,6 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } -rocket_codegen = { path = "../../core/codegen" } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/examples/tera_templates/src/main.rs b/examples/tera_templates/src/main.rs @@ -1,5 +1,4 @@ -#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; diff --git a/scripts/config.sh b/scripts/config.sh @@ -30,7 +30,7 @@ CONTRIB_ROOT=$(relative "contrib") || exit $? # Root of project-like directories. CORE_LIB_ROOT=$(relative "core/lib") || exit $? -CORE_CODEGEN_ROOT=$(relative "core/codegen") || exit $? +# CORE_CODEGEN_ROOT=$(relative "core/codegen") || exit $? CORE_CODEGEN_NEXT_ROOT=$(relative "core/codegen_next") || exit $? CORE_HTTP_ROOT=$(relative "core/http") || exit $? CONTRIB_LIB_ROOT=$(relative "contrib/lib") || exit $? @@ -41,7 +41,7 @@ DOC_DIR=$(relative "target/doc") || exit $? ALL_PROJECT_DIRS=( "${CORE_LIB_ROOT}" - "${CORE_CODEGEN_ROOT}" + # "${CORE_CODEGEN_ROOT}" "${CORE_CODEGEN_NEXT_ROOT}" "${CORE_HTTP_ROOT}" "${CONTRIB_LIB_ROOT}" @@ -53,7 +53,7 @@ if [ "${1}" = "-p" ]; then echo "CORE_ROOT: ${CORE_ROOT}" echo "CONTRIB_ROOT: ${CONTRIB_ROOT}" echo "CORE_LIB_ROOT: ${CORE_LIB_ROOT}" - echo "CORE_CODEGEN_ROOT: ${CORE_CODEGEN_ROOT}" + # echo "CORE_CODEGEN_ROOT: ${CORE_CODEGEN_ROOT}" echo "CORE_CODEGEN_NEXT_ROOT: ${CORE_CODEGEN_NEXT_ROOT}" echo "CORE_HTTP_ROOT: ${CORE_HTTP_ROOT}" echo "CONTRIB_LIB_ROOT: ${CONTRIB_LIB_ROOT}" diff --git a/site/guide/getting-started.md b/site/guide/getting-started.md @@ -61,10 +61,9 @@ Modify `src/main.rs` so that it contains the code for the Rocket `Hello, world!` program, reproduced below: ```rust -#![feature(plugin)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] -extern crate rocket; +#[macro_use] extern crate rocket; #[get("/")] fn index() -> &'static str { diff --git a/site/guide/overview.md b/site/guide/overview.md @@ -138,10 +138,9 @@ We typically call `launch` from the `main` function. Our complete _Hello, world!_ application thus looks like: ```rust -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] -extern crate rocket; +#[macro_use] extern crate rocket; #[get("/world")] fn world() -> &'static str { @@ -153,11 +152,11 @@ fn main() { } ``` -Note that we've added the `#![feature(plugin, decl_macro)]` and -`#![plugin(rocket_codegen)]` lines to tell Rust that we'll be using Rocket's -code generation plugin. We've also imported the `rocket` crate into our -namespace via `extern crate rocket`. Finally, we call the `launch` method in the -`main` function. +Note the `#![feature]` line: this tells Rust that we're opting in to compiler +features vailable in the nightly release channel. This line must be in the crate +root, typically `main.rs`. We've also imported the `rocket` crate and all of its +macros into our namespace via `#[macro_use] extern crate rocket`. Finally, we +call the `launch` method in the `main` function. Running the application, the console shows: diff --git a/site/guide/pastebin.md b/site/guide/pastebin.md @@ -51,10 +51,9 @@ And finally, create a skeleton Rocket application to work off of in `src/main.rs`: ```rust -#![feature(plugin, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] -extern crate rocket; +#[macro_use] extern crate rocket; fn main() { rocket::ignite().launch(); diff --git a/site/index.toml b/site/index.toml @@ -44,10 +44,9 @@ margin = 9 [[sections]] title = "Hello, Rocket!" code = ''' - #![feature(plugin)] - #![plugin(rocket_codegen)] + #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] - extern crate rocket; + #[macro_use] extern crate rocket; #[get("/hello/<name>/<age>")] fn hello(name: String, age: u8) -> String {