POC statically check if paths are panic free

POC statically check if paths are panic free

Hacky way to statically check if there are panics in the function call tree within the blocks labelled with
'deny-panic' that (almost) works.

Intro

I needed something with the following poperties:

  • Check at compile time if code paths are panic free
  • If a path end up calling something in another crate check the other crate
  • Use cargo to solve the dependencies so that I don't have to do it manually

I started experimenting with the approach used by dtolnay/no-panic. But I ended with big issues
here the full post. I also experimented a little bit with clippy but at the end I decided to
go with the below approach.

Approach

The main idea is to use the rustc_* libraries to parse the code of a crate and look for block labelled
deny-panic. For each founded block recursively check all the function's call. When a function end up
calling std::begin_panic emit an error.
The above is achieved by a binary called unpanic that can be used with cargo with
RUSTC_WRAPPER=unpanic TARGET_CRATE=[name] cargo +nightly build

How it works

When executed, unpanic verifies whether the crate being checked is the target crate, i.e., the one we want to analyze. If the crate is not the target, it builds it using rustc and appends a line to ./target/no-panic/deps containing the crate name and all rustc arguments.
If the crate is the target crate it will build it with rustc, then it will get the deps build info from
./target/no-panic/deps.
Then unpanic will collect all the blocks in the target crate labelled with deny-panic and check
if they are panic free. If a block labelled allow-panic is found that block it is skipped. If the skipped
block is not in the target crate an warning is emitted. Right now nested
deny-panic are not supported: allow-panic { deny-panic {} } will be ignored.

Test

To see unpanic in action clone the repo go in the workspace root and run:
cargo clean && cargo +nightly build -p unpanic && RUSTC_WRAPPER=./target/debug/unpanic TARGET_CRATE=test1_bin cargo +nightly build -p test1_bin

Below an extract of the output:

OMG A PANIC
    test_if_see_panics_in_imported_functions in tests/test1_bin/src/main.rs:13:1: 18:2 (#0)

    function_test in tests/test1_bin/src/main.rs:16:9: 16:22 (#0)

    it_panic in tests/test1_lib/src/function_test.rs:2:5: 2:20 (#0)

    begin_panic in /rustc/371994e0d8380600ddda78ca1be937c7fb179b49/library/std/src/panic.rs:19:9: 19:32 (#7)

./run_test.sh will run the tests.

Std lib

Right now unpanic is not able to build the std lib so if a panic is invoked by std, unpanic
will not emit an error. This is not a big issue for my particular use case cause I can assume
infinite memory.
The best solution in my opinion is to introduce another label deny-alloc that check for
allocations.

Main issues

Given that this is a POC everything is very hacky and there are a lot of issues.
These 2 particular things blocks me and I'm not sure how to solve them: extern issue and assoc fn
solver
.

Extern issue (1)

If extern crates are not declared with extern crate [crate-name] in the anlyzed source code, unpanic do not works. I do not have idea of how I could solve this. I already put edition=2018 in the rustc_interface::run_compiler configs.
Here is where I build the configs and here is where I invoke run_compiler.

Assoc fn solver (2)

When I have to solve the path for extrern assoc functions I do I thing that seems very wrong.
Here the thing. And here a post with more details.

Other issues

Another big limitations is that right now if we have any loop in the call tree the checker panics,
I left it as a todo cause before spend time on it I wanted to have some feedback on the approach and
on the solvibility of 1 and 2.

6 Likes

Finally found some time to work on it. I pushed a patch that solve the extern issue and now the 2018 edition is no more mandatory.

Next I will add a patch to allow loops in the call tree so it will be usable also to check real projects, not only toy examples.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.