okapi: Okapi obfuscates compiler errors

Thank you for this project! I’ve been struggling with documenting an API I build, and keeping that documentation up to date. I’m trying to add okapi to my project to have the documentation be created automatically. Unfortunately, I’m running into some issues:

As it stands, Rocket is able to provide neat Rust error messages. Consider the following example:

#![feature(decl_macro, proc_macro_hygiene)]

use rocket::{get, routes};
use rocket_contrib::json::Json;

#[derive(serde::Serialize)]
struct Response {
    reply: String,
}

#[get("/")]
fn my_controller() -> Json<Response> {
    Json(Response {
        reply: 0,
    })
}


fn main() {
    rocket::ignite()
        .mount("/", routes![my_controller])
        .launch();
}

This fails with a compile error:

[me@mycomputer tmp]$ cargo check
    Checking tmp v0.1.0 (/home/me/code/rust/tmp)
error[E0308]: mismatched types
  --> src/main.rs:17:16
   |
17 |         reply: 0,
   |                ^
   |                |
   |                expected struct `std::string::String`, found integer
   |                help: try using a conversion method: `0.to_string()`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `tmp`.

However, when the code is changed to use Okapi:

#![feature(decl_macro, proc_macro_hygiene)]

use rocket::get;
use rocket_contrib::json::Json;
use rocket_okapi::{openapi, routes_with_openapi};
use schemars::JsonSchema;

#[derive(serde::Serialize, JsonSchema)]
struct Response {
    reply: String,
}

#[openapi]
#[get("/")]
fn my_controller() -> Json<Response> {
    Json(Response {
        reply: 0,
    })
}


fn main() {
    rocket::ignite()
        .mount("/", routes_with_openapi![my_controller])
        .launch();
}

The error message changes:

[me@mycomputer tmp]$ cargo check
    Checking tmp v0.1.0 (/home/me/code/rust/tmp)
error[E0308]: mismatched types

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `tmp`.

The useful information about what caused the compilation error is gone, making it really hard to debug. Is there any way to get it back?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 2
  • Comments: 15 (13 by maintainers)

Commits related to this issue

Most upvoted comments

Done - https://crates.io/crates/rocket_okapi 0.3.3 is available, hopefully this will fix everything!

This should be fixed in rocket_okapi v0.3.2, which is now published on crates.io - could you check that it works for you?

tl;dr: a fix will be out soon

So this was a weird one - I think the root cause is rust-lang/rust#43081

You can see the same problem with the following repro, where both no_op1 and no_op2 should both pass through tokens unmodified (although no_op2 does it by parsing them in then quoting them back out):

///// MACRO CRATE: /////
#[macro_use]
extern crate quote;
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn no_op1(_: TokenStream, input: TokenStream) -> TokenStream {
    input
}

#[proc_macro_attribute]
pub fn no_op2(_: TokenStream, input: TokenStream) -> TokenStream {
    let parsed_input = syn::parse::<syn::Item>(input).unwrap();
    quote!(#parsed_input).into()
}

///// APP CRATE: /////
use codegen::{no_op1, no_op2};

#[no_op1]
#[no_op1]
fn one_one() {
    let one_one: i32 = "nan";
}

#[no_op1]
#[no_op2]
fn one_two() {
    let one_two: i32 = "nan";
}

#[no_op2]
#[no_op1]
fn two_one() {
    let two_one: i32 = "nan";
}

#[no_op2]
#[no_op2]
fn two_two() {
    let two_two: i32 = "nan";
}

The output of cargo check is then:

error[E0308]: mismatched types
  |
  = note: expected type `i32`
             found type `&'static str`

error[E0308]: mismatched types
 --> app\src\main.rs:6:24
  |
6 |     let one_one: i32 = "nan";
  |                        ^^^^^ expected i32, found reference
  |
  = note: expected type `i32`
             found type `&'static str`

error[E0308]: mismatched types
  --> app\src\main.rs:18:24
   |
18 |     let two_one: i32 = "nan";
   |                        ^^^^^ expected i32, found reference
   |
   = note: expected type `i32`
              found type `&'static str`

error[E0308]: mismatched types
  --> app\src\main.rs:24:24
   |
24 |     let two_two: i32 = "nan";
   |                        ^^^^^ expected i32, found reference
   |
   = note: expected type `i32`
              found type `&'static str`

Note that one_two loses its span information, obfuscating the error. no_op1/no_op2 interact exactly the same way the openapi/get attributes do. The good news is that this also gives us a way to work-around it: by making the openapi attribute behave more like no_op2 (by passing the tokens through syn/quote), span information will be retained.

I’ll try to get the fix out imminently