BMBM
BMBM

Reputation: 16013

Compiler warns about unused code for private functions that are actually used

I started grouping code for one of my projects into module files. I encountered a lot of warnings about unused code for functions in those modules, but the functions are actually used (privately within the module).

An example from my project:

// ...

// Line 46:

fn calc_p(word: &str, dict: &Dictionary) -> f32
{
    if !dict.contains_key(word) {
        return 0.0;
    }

    let total: u32 = dict.values().sum();
    dict[word] as f32 / total as f32
}

...

But at line 13 in the same file, same module the function is clearly used:

// ...

pub fn suggest_corrections(to_be_corrected: &str, dict: &Dictionary) -> Vec<(f32, String)>
{
    let candidates = create_candidates(to_be_corrected, dict);

    let mut weighted_candidates = candidates
        .into_iter()
        .map(|w| (calc_p(&w, dict), w)) // Line 13 - calc_p used here
        .collect::<Vec<_>>();

    weighted_candidates.sort_by(|a, b| b.partial_cmp(a).unwrap());

    weighted_candidates
}

// ...

Yet I get the compiler warning:

warning: function is never used: `calc_p`, #[warn(dead_code)] on by default
  --> src/spellcheck/corrections.rs:46:1
   |
46 |   fn calc_p(word: &str, dict: &Dictionary) -> f32
   |  _^ starting here...
47 | | {
48 | |     if !dict.contains_key(word) {
49 | |         return 0.0;
50 | |     }
51 | |
52 | |     let total: u32 = dict.values().sum();
53 | |     dict[word] as f32 / total as f32
54 | | }
   | |_^ ...ending here

I thought maybe it's because the module corrections is not public, but I also marked the module as public and still get the same warning.

I tried to reproduce the behaviour using the playground, but if the module is defined in the same file it seems to work. The warnings also happen for me during a TravisCI build.

Why is the function marked as unused although it's actually used in the same file in the same module?

I'm using Rust 1.15 / 1.16.

Upvotes: 3

Views: 2125

Answers (1)

Shepmaster
Shepmaster

Reputation: 430961

Why is the function marked as unused although it's actually used in the same file in the same module?

Because unused code is a transitive calculation, and the code that calls that function cannot be called from outside the crate. The compiler is correct here.


The first error I see is:

warning: static item is never used: `ALPHABET`, #[warn(dead_code)] on by default
 --> src/spellcheck/edits.rs:3:1

That's defined as:

src/spellcheck/edits.rs

static ALPHABET : &'static str = "abcdefghijklmnopqrstuvwxyz";

So it's not public, and can only be used from inside the module.

It's called from replaces and inserts. Both of those are used from edits1, a public function. Is the module containing it (edits) public?

src/spellcheck/mod.rs

mod edits;
pub mod corrections;
pub mod dictionary;

Nope, it is not. So where is edits1 called from? kodecheck_spellcheck::spellcheck::corrections::create_candidates, a non-public function. This is called by suggest_corrections, a public function, inside the public module corrections.

Let's look up the tree...

src/lib-spellcheck.rs

// @FIXME do we really need this here or move to sub-modules?
#[macro_use]
extern crate nom;
extern crate regex;

mod spellcheck;

Ah. For whatever reason, you've decided to introduce a completely nested private module spellcheck inside the crate (which is why the full path earlier is kodecheck_spellcheck::spellcheck::corrections::create_candidates).

From this, we can see that kodecheck_spellcheck::spellcheck cannot be accessed from outside the crate; it's not pub.


Let's create an example that uses the crate. This the the best way to see if it's true and would have been easy for you to test yourself:

examples/foo.rs

extern crate kodecheck_spellcheck;

fn main() {
    kodecheck_spellcheck::spellcheck::corrections::create_candidates();
}

Compiling this has the error:

error: module `spellcheck` is private
 --> examples/foo.rs:4:5
  |
4 |     kodecheck_spellcheck::spellcheck::corrections::create_candidates();
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

We could also create a MCVE:

mod spellcheck {
    mod edits {
        static ALPHABET: &'static str = "";

        pub fn edits1() {
            println!("{}", ALPHABET);
        }
    }

    pub mod corrections {
        pub fn create_candidates() {
            ::spellcheck::edits::edits1();
        }
    }
}

fn main() {}

Note that this differs from your example because main is empty. Your library crate doesn't have a mainmethod that calls anything. You could also explicitly specify that it's a library crate, but this is less well-known:

#![crate_type="lib"]

mod spellcheck {
    mod edits {
        static ALPHABET: &'static str = "";

        pub fn edits1() {
            println!("{}", ALPHABET);
        }
    }

    pub mod corrections {
        pub fn create_candidates() {
            ::spellcheck::edits::edits1();
        }
    }
}

Because there's a private module where all the code is, nothing outside this crate can call anything inside the crate. Therefore every method is unreachable.

To fix it, I'd suggest combining src/spellcheck/mod.rs into src/lib-spellcheck.rs. You could also just make the module public, but I see no reason to have the extra module.

I also see no reason to rename src/lib.rs to src/lib-spellcheck.rs; it's far more idiomatic to just leave it lib.rs.


Additionally, you should go ahead and become comfortable with Rust style. Braces go on the same line as the signature:

fn calc_p(word: &str, dict: &Dictionary) -> f32 {
    // ...

Tools like rustfmt will help you apply the proper style.

Upvotes: 5

Related Questions