Skip to content

Ignoring collapsible_match is extremely confusing #14281

Closed
@ZaneErebos

Description

@ZaneErebos

This lint does not include the place at which you can apply an attribute to ignore the lint, which can leave users very confused.

Take the following code

match e.downcast::<RustlsError>() {
  Ok(rustls_err) => match rustls_err {
    RustlsError::InvalidMessage(e) => match e {
      // non https request is received
      InvalidMessage::InvalidContentType => (),

      _ => eprintln!(
        "Error during tls handshake from {addr}: {rustls_err:?}",
      ),
    },

    RustlsError::AlertReceived(e) => match e {
      // self-signed certs
      AlertDescription::CertificateUnknown => (),

      _ => eprintln!(
        "Error during tls handshake from {addr}: {rustls_err:?}",
      ),
    },

    _ => eprintln!(
      "Error during tls handshake from {addr}: {rustls_err:?}",
    ),
  },

  Err(e) => match e.kind() {
    // https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#unexpected-eof
    ErrorKind::UnexpectedEof => (),

    _ => {
      eprintln!("Error during tls handshake from {addr}: {e:?}");
    }
  },
};
$ cargo clippy
warning: this `match` can be collapsed into the outer `match`
   --> server/src/main.rs:137:51
    |
137 |                   RustlsError::InvalidMessage(e) => match e {
    |  ___________________________________________________^
138 | |                   // non https request is received
139 | |                   InvalidMessage::InvalidContentType => (),
...   |
143 | |                   ),
144 | |                 },
    | |_________________^
    |
help: the outer pattern can be modified to include the inner pattern
   --> server/src/main.rs:137:45
    |
137 |                 RustlsError::InvalidMessage(e) => match e {
    |                                             ^ replace this binding
138 |                   // non https request is received
139 |                   InvalidMessage::InvalidContentType => (),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
    = note: `#[warn(clippy::collapsible_match)]` on by default

warning: this `match` can be collapsed into the outer `match`
   --> server/src/main.rs:146:50
    |
146 |                   RustlsError::AlertReceived(e) => match e {
    |  __________________________________________________^
147 | |                   // self-signed certs
148 | |                   AlertDescription::CertificateUnknown => (),
...   |
152 | |                   ),
153 | |                 },
    | |_________________^
    |
help: the outer pattern can be modified to include the inner pattern
   --> server/src/main.rs:146:44
    |
146 |                 RustlsError::AlertReceived(e) => match e {
    |                                            ^ replace this binding
147 |                   // self-signed certs
148 |                   AlertDescription::CertificateUnknown => (),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match

Ok, whatever. I like my code the way it is, I will ignore the warnings.

match e.downcast::<RustlsError>() {
  Ok(rustls_err) => match rustls_err {
    RustlsError::InvalidMessage(e) => {
+      #[expect(clippy::collapsible_match)]
      match e {
        // non https request is received
        InvalidMessage::InvalidContentType => (),
...
    }

    RustlsError::AlertReceived(e) => {
+      #[expect(clippy::collapsible_match)]
      match e {
        // self-signed certs
        AlertDescription::CertificateUnknown => (),
...

(& formatting changes)

$ cargo clippy
warning: this `match` can be collapsed into the outer `match`
   --> server/src/main.rs:139:19
    |
139 | /                   match e {
140 | |                     // non https request is received
141 | |                     InvalidMessage::InvalidContentType => (),
...   |
145 | |                     ),
146 | |                   }
    | |___________________^
    |
help: the outer pattern can be modified to include the inner pattern
   --> server/src/main.rs:137:45
    |
137 |                 RustlsError::InvalidMessage(e) => {
    |                                             ^ replace this binding
...
141 |                     InvalidMessage::InvalidContentType => (),
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
    = note: `#[warn(clippy::collapsible_match)]` on by default

warning: this `match` can be collapsed into the outer `match`
   --> server/src/main.rs:151:19
    |
151 | /                   match e {
152 | |                     // self-signed certs
153 | |                     AlertDescription::CertificateUnknown => (),
...   |
157 | |                     ),
158 | |                   }
    | |___________________^
    |
help: the outer pattern can be modified to include the inner pattern
   --> server/src/main.rs:149:44
    |
149 |                 RustlsError::AlertReceived(e) => {
    |                                            ^ replace this binding
...
153 |                     AlertDescription::CertificateUnknown => (),
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match

warning: this lint expectation is unfulfilled
   --> server/src/main.rs:138:28
    |
138 |                   #[expect(clippy::collapsible_match)]
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unfulfilled_lint_expectations)]` on by default

warning: this lint expectation is unfulfilled
   --> server/src/main.rs:150:28
    |
150 |                   #[expect(clippy::collapsible_match)]
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^

Very confused, I tried to undo my changes and reapply but the warnings still persisted. I then tried to apply the attributes to the e bindings but got syntax errors.

Now even more confused, I glanced back at the code and saw that the 2 branches looked very similar. Out of curiosity I added a character to one of the strings, and the warning for that block disappeared. Maybe clippy detected that I was doing the same thing as the parent match in both cases and thus issued the warnings? To test it, I added the same character at the same position in the string in the parent block (match rustls_err) and suddenly the warning switched from the block without the character to the one that did. I undid all changes and applied the attribute to the parent block, and the warnings disappeared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions