rustrust-sqlx

Why is Clippy warning me about a function which may panic?


I am quite new to Rust and I stumbled upon this warning from Clippy in my codebase:

warning: docs for function which may panic missing `# Panics` section
    --> src/activity.rs:113:1                                                                                                                                                                                                       ▐
     |                                                                                                                                                                                                                              ▐
 113 | pub async fn running(db: &PgPool, user_id: String) -> anyhow::Result<Option<Running>> {                                                                                                                                      ▐
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                        ▐
     |
 note: first possible panic found here
    --> src/activity.rs:130:9
     |
 130 |         user_id
     |         ^^^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
     = note: `-W clippy::missing-panics-doc` implied by `-W clippy::pedantic`
     = help: to override `-W clippy::pedantic` add `#[allow(clippy::missing_panics_doc)]`

The connected code looks like this:

#[derive(Debug)]
pub struct Running {
    id: sqlx::types::Uuid,
    user_id: String,
    name: String,
    start_time: chrono::DateTime<chrono::Utc>,
    end_time: Option<chrono::DateTime<chrono::Utc>>,
    tags: Vec<Tag>,
}

pub async fn running(db: &PgPool, user_id: String) -> anyhow::Result<Option<Running>> {
    let result = sqlx::query_as!(
        Running, 
        r#"
        SELECT 
            activities.*,
            COALESCE(array_agg((tags.id, tags.user_id, tags.name)) filter (WHERE tags.id IS NOT NULL), '{}') AS "tags!: Vec<Tag>"
        FROM activities
        LEFT JOIN activities_tags
            ON activities.id = activities_tags.activity_id
        LEFT JOIN tags
            ON activities_tags.tag_id = tags.id
        WHERE activities.user_id = $1 AND end_time IS NULL
        GROUP BY activities.id
        ORDER BY activities.start_time ASC
        LIMIT 1
        "#,
        user_id
    ).fetch_optional(db).await?;
    Ok(result)
}

I dug through the internet with this warning, but could not really find an answer or solution.

I am wondering two things:

  1. Is this warning just because of the fact that sqlx 'panics' once the schema validation is wrong and thus carries this warning into runtime, which can not really happen?
  2. Is there even anything I can do about the panic or is the normal with sqlx for example?

My thought process so far in Rust was that when it compiles you are pretty much good to go, but this warning makes me wonder, if there is still some run-time magic going on which have to be aware of?


Solution

  • I would like to shed some light on your issue. I stumbled upon the same issue today and I think I know the cause.

    Cause of the problem

    The macro query_as! expands into something that contains panic. If you follow how the macro expands you will find that for each parameter ($1, user_id in your case) of the query, the expanded macro contains this piece of code (link to github here):

    // this shouldn't actually run
    if false {
        use ::sqlx::ty_match::{WrapSameExt as _, MatchBorrowExt as _};
    
        // evaluate the expression only once in case it contains moves
        let expr = ::sqlx::ty_match::dupe_value(#name);
    
         // if `expr` is `Option<T>`, get `Option<$ty>`, otherwise `$ty`
         let ty_check = ::sqlx::ty_match::WrapSame::<#param_ty, _>::new(&expr).wrap_same();
    
         // if `expr` is `&str`, convert `String` to `&str`
         let (mut _ty_check, match_borrow) = ::sqlx::ty_match::MatchBorrow::new(ty_check, &expr);
    
         _ty_check = match_borrow.match_borrow();
    
         // this causes move-analysis to effectively ignore this block
         ::std::panic!();
    }
    

    Clippy then thinks this panic can happen, when in reality it can't.

    Side note

    Interesting is that authors of the library thought about clippy. At the start of the expanded macro is #[allow(clippy::all)] (link to github here). But for some reason it doesn't allow panics.

    Solution

    Probably not needed anymore but anyway, my advice is to use #[allow(clippy::panic)] before each query. Or you can put it on module which contains your queries.