Uh oh!
There was an error while loading. Please reload this page.
Add dead-code warning pass - #10477
Conversation
catamorphism
commented
Nov 14, 2013
@ktt3ja Great! I'll take a look at this in the morning. |
There was a problem hiding this comment.
What's the criterion for exploring? (could we have a comment about what it is checking?)
alexcrichton
commented
Nov 14, 2013
This seems like it copies a fair bit of code from |
huonw
commented
Nov 14, 2013
Some test-case ideas:
|
ktt3ja
commented
Nov 14, 2013
@alexcrichton: I wanted to see how the final code would take shape before thinking of refactoring. If it turns out that the propagation logic does not need to change at all, then I think it would be as simple as modifying reachable::ReachableContext to take in a visitor and then pass in my own visitor. (that would require making reachable::ReachableContext pub, though, which I'm not sure would be fine) |
alexcrichton
commented
Nov 14, 2013
Certainly! That sounds like a good strategy to me (thanks!) |
ktt3ja
commented
Nov 15, 2013
@alexcrichton: It looks like I have to change ReachableContext a bit more to make dead-code warning pass works. In particular, I need to replace the lines for method in methods.iter(){self.worklist.push(method.id);}in reachable with for method in methods.iter(){match method.vis{
ast::public => {self.worklist.push(method.id);}
_ => (),}}However, I'm not sure if it's going to break something, so I would like you to give comment on that (I tried to run pubstructFoo;implFoo{fnbaz();}the method |
alexcrichton
commented
Nov 16, 2013
I'm fairly confident that it's possible to do that without breaking things, but I would rather not have anything look at privacy after the privacy pass. That should be possible by checking if the method is within the |
huonw
commented
Nov 16, 2013
I was thinking test6 would be #[crate_type="lib"];pubuse foo::Bar;mod foo {pubstructBar;}and so it shouldn't warn. (Although, the one you have there should warn, I would think.) BTW, you should add these test cases to |
ktt3ja
commented
Nov 16, 2013
Ah, I'm sorry. I deleted the comment since I was going to fix something first before notifying you. Here's the comment that huonw responded to (that I modified a bit at the end): @huonw: With the latest commit, here's my current progress: the code from original post produces the same error message. Here are test2.rs and its warning: fndead_fn(){}fnused_fn(){}#[main]fnthis_is_main(){used_fn();}// this is not mainfnmain(){dead_fn();}/home/ktt3ja/dead-code-test/test2.rs:1:0:2:1 warning: code is never used: `dead_fn`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test2.rs:1fn dead_fn(){
/home/ktt3ja/dead-code-test/test2.rs:2}
/home/ktt3ja/dead-code-test/test2.rs:13:0:1:0 warning: code is never used: `main`,#[warn(dead_code)] on by defaulttest3.rs: fndead_fn(){}#[main]fndead_fn2(){}fnused_fn(){}#[start]fnstart(_:int, _:**u8) -> int{used_fn();0}// this is not mainfnmain(){dead_fn();dead_fn2();}/home/ktt3ja/dead-code-test/test3.rs:1:0:2:1 warning: code is never used: `dead_fn`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test3.rs:1fn dead_fn(){
/home/ktt3ja/dead-code-test/test3.rs:2}
/home/ktt3ja/dead-code-test/test3.rs:5:0:6:1 warning: code is never used: `dead_fn2`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test3.rs:5fn dead_fn2(){
/home/ktt3ja/dead-code-test/test3.rs:6} /home/ktt3ja/dead-code-test/test3.rs:18:0:1:0 warning: code is never used: `main`,#[warn(dead_code)] on by defaulttest4.rs structFoo;implFoo{fnfoo(&self){bar()}}fnbar(){fnbaz(){}Foo.foo();baz();}fnmain(){}structFoo2;implFoo2{fnfoo2(&self){bar2()}}fnbar2(){fnbaz2(){}Foo2.foo2();baz2();}pubfnpub_fn(){let foo2_struct = Foo2;
foo2_struct.foo2();}/home/ktt3ja/dead-code-test/test4.rs:1:0:1:11 warning: code is never used: `Foo`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test4.rs:1structFoo;
^~~~~~~~~~~
/home/ktt3ja/dead-code-test/test4.rs:2:11:2:34 warning: code is never used: `foo`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test4.rs:2implFoo{fnfoo(&self){bar()}}
^~~~~~~~~~~~~~~~~~~~~~~
/home/ktt3ja/dead-code-test/test4.rs:3:0:8:1 warning: code is never used: `bar`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test4.rs:3fn bar(){
/home/ktt3ja/dead-code-test/test4.rs:4fnbaz(){}
/home/ktt3ja/dead-code-test/test4.rs:5 /home/ktt3ja/dead-code-test/test4.rs:6Foo.foo();
/home/ktt3ja/dead-code-test/test4.rs:7baz();
/home/ktt3ja/dead-code-test/test4.rs:8}
/home/ktt3ja/dead-code-test/test4.rs:4:3:4:14 warning: code is never used: `baz`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test4.rs:4fnbaz(){}
^~~~~~~~~~~test5.rs structFoo;implFoo{pubfnfoo1(){}fnfoo2(){}}fnmain(){}/home/ktt3ja/dead-code-test/test5.rs:1:0:1:11 warning: code is never used: `Foo`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test5.rs:1structFoo;
^~~~~~~~~~~
/home/ktt3ja/dead-code-test/test5.rs:5:1:5:13 warning: code is never used: `foo2`,#[warn(dead_code)] on by default
/home/ktt3ja/dead-code-test/test5.rs:5fnfoo2(){}
^~~~~~~~~~~~and test6.rs #[crate_type="lib"];mod foo {pubstructBar;// not externally visible without the `pub use`}produces no dead-code warning. I think that covers almost everything. Perhaps the |
There was a problem hiding this comment.
I'm not 100% sure these are meant to be public, although all their methods are pub. (In any case, they shouldn't be warning, since they are used in the BitvVariant enum, which is used in the public Bitv struct.)
There was a problem hiding this comment.
There was actually no warning on the SmallBitv and BigBitv struct; it was just that the negate method of BigBitv was never used. I have reverted the change on the visibility of those two structs, and modified Bitv's negate method to call BigBitv's negate. I think that's what was intended.
ktt3ja
commented
Dec 1, 2013
r? @alexcrichton and @huonw A few things that need mentioning:
|
huonw
commented
Dec 1, 2013
It seems ok to me, and the tests look good; but I don't have enough familiarity with these parts of rustc to r+. |
ktt3ja
commented
Dec 1, 2013
Forgot to mention: it's still missing a test with |
huonw
commented
Dec 1, 2013
Something like extern{fnabort() -> !;}which imports a symbol from libc should be ok, or, if it's not, using a compiler-internal extern"rust-intrinsic"{fntransmute<A,B>(x:A) -> B;} |
alexcrichton
commented
Dec 2, 2013
Two more high-level comments:
|
huonw
commented
Dec 2, 2013
I think this is mostly just because making the decisions about whether something is supposed to be removed or not is hard for someone who's just starting out with the Rust code base (i.e. @ktt3ja), and it gets fairly tiresome. (This is me voting for warn by default.) (That is, people who are familiar with the various areas can go through and progressively remove the dead code, and adjust the code that's not supposed to be dead.) |
ktt3ja
commented
Dec 2, 2013
huonw is right about why there are so many
The dead-code pass is already a standalone pass. The differences between them are:
|
ktt3ja
commented
Dec 3, 2013
@alexcrichton: hm, it looks like github removed all the comments you made on my old commits and my responses to them. Anyway, I just updated my commits to reduce the amount of code duplication. Unfortunately, it involves littering |
ktt3ja
commented
Dec 7, 2013
alexcrichton's comments on an older commit (that got deleted by github):
|
ktt3ja
commented
Dec 7, 2013
@alexcrichton: r? I just updated my commit to separate out |
alexcrichton
commented
Dec 7, 2013
Awesome work! Just ping me when my comments have been addressed, and I think this is good to go! |
ktt3ja
commented
Dec 7, 2013
I'm going to repost one of your comments and respond to it here in case it gets deleted the next time I force push:
It just didn't occur to me that you could implement trait inside function, that's why I only traversed module. I mark all trait methods as live because it seems to be the easiest course of action. I don't have sufficient understanding of static/dynamic dispatch (and how they are implemented), nor do I a solid enough model in my head to tackle this problem in a more detailed-oriented manner. Perhaps a stricter analysis would be something like this:
However, I think a justification to the current behavior is that traits define an interface, and defining an interface is ultimately a matter of design. I think usually someone would remove a required method from a trait definition if they are convinced that it wouldn't be useful, and conversely they would want to keep it if they feel that the method is fitting and will be used eventually. It would be awkward to warn dead code on a trait method implementation, then for someone to remove it they would have to remove the method from the trait definition and all the other trait implementations. |
alexcrichton
commented
Dec 7, 2013
I think that assuming all trait implementations are "live code" is certainly ok for now. I'd just recommend recursing into inner functions as well. |
ktt3ja
commented
Dec 8, 2013
@alexcrichton: r+? My previous changes (re-)introduced some problems that made bors fail to build. I have fixed them by:
Here's the diff between my last commit and this one: diff --git a/src/etc/extract-tests.py b/src/etc/extract-tests.py
index 5904e10..ab2556f 100644
--- a/src/etc/extract-tests.py+++ b/src/etc/extract-tests.py@@ -64,6 +64,7 @@ while cur < len(lines):
#[ allow(dead_assignment) ];\n
#[ allow(unused_mut) ];\n
#[ allow(attribute_usage) ];\n
+#[ allow(dead_code) ];\n
#[ feature(macro_rules, globs, struct_variant, managed_boxes) ];\n
""" + block
if xfail:
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index f07887c..e08726a 100644
--- a/src/librustc/driver/driver.rs+++ b/src/librustc/driver/driver.rs@@ -312,7 +312,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
time(time_passes, "death checking", (), |_|
middle::dead::check_crate(ty_cx, method_map,
- reachable_map, crate));+ &exported_items, reachable_map, crate));
time(time_passes, "lint checking", (), |_|
lint::check_crate(ty_cx, &exported_items, crate));
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 88b0a35..332b706 100644
--- a/src/librustc/middle/dead.rs+++ b/src/librustc/middle/dead.rs@@ -196,31 +196,46 @@ impl Visitor<()> for TraitMethodSeeder {
}
fn create_and_seed_worklist(tcx: ty::ctxt,
+ exported_items: &privacy::ExportedItems,
reachable_symbols: &HashSet<ast::NodeId>,
crate: &ast::Crate) -> ~[ast::NodeId] {
let mut worklist = ~[];
+ // Preferably, we would only need to seed the worklist with reachable+ // symbols. However, since the set of reachable symbols differs+ // depending on whether a crate is built as bin or lib, and we want+ // the warning to be consistent, we also seed the worklist with+ // exported symbols.+ for &id in exported_items.iter() {+ worklist.push(id);+ }
for &id in reachable_symbols.iter() {
worklist.push(id);
}
++ // Seed entry point
match *tcx.sess.entry_fn {
Some((id, _)) => worklist.push(id),
None => ()
}
- let mut trait_method_visitor = TraitMethodSeeder {++ // Seed implemeneted trait methods+ let mut trait_method_seeder = TraitMethodSeeder {
worklist: worklist
};
- visit::walk_crate(&mut trait_method_visitor, crate, ());+ visit::walk_crate(&mut trait_method_seeder, crate, ());- return trait_method_visitor.worklist;+ return trait_method_seeder.worklist;
}
fn find_live(tcx: ty::ctxt,
method_map: typeck::method_map,
+ exported_items: &privacy::ExportedItems,
reachable_symbols: &HashSet<ast::NodeId>,
crate: &ast::Crate)
-> ~HashSet<ast::NodeId> {
- let worklist = create_and_seed_worklist(tcx, reachable_symbols, crate);+ let worklist = create_and_seed_worklist(tcx, exported_items,+ reachable_symbols, crate);
let mut symbol_visitor = MarkSymbolVisitor::new(tcx, method_map, worklist);
symbol_visitor.mark_live_symbols();
symbol_visitor.live_symbols
@@ -315,13 +330,19 @@ impl Visitor<()> for DeadVisitor {
}
visit::walk_block(self, block, ());
}
++ fn visit_trait_method(&mut self, _ :&ast::trait_method, _: ()) {+ // Don't warn on trait method+ }
}
pub fn check_crate(tcx: ty::ctxt,
method_map: typeck::method_map,
exported_items: &privacy::ExportedItems,
+ reachable_symbols: &HashSet<ast::NodeId>,
crate: &ast::Crate) {
- let live_symbols = find_live(tcx, method_map, exported_items, crate);+ let live_symbols = find_live(tcx, method_map, exported_items,+ reachable_symbols, crate);
let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols };
visit::walk_crate(&mut visitor, crate, ());
} |
ktt3ja
commented
Dec 8, 2013
@alexcrichton: sorry, I keep missing these small details. :( Now I have it walk the body of a trait method. Is it okay now? |
PR for issue #1749 mainly to get some feedback and suggestion. This adds a pass that warns if a function, struct, enum, or static item is never used. For the following code, ```rust pub static pub_static: int = 0; static priv_static: int = 0; static used_static: int = 0; pub fn pub_fn() { used_fn(); } fn priv_fn() { let unused_struct = PrivStruct; } fn used_fn() {} pub struct PubStruct(); struct PrivStruct(); struct UsedStruct1 { x: int } struct UsedStruct2(int); struct UsedStruct3(); pub enum pub_enum { foo1, bar1 } enum priv_enum { foo2, bar2 } enum used_enum { foo3, bar3 } fn foo() { bar(); let unused_enum = foo2; } fn bar() { foo(); } fn main() { let used_struct1 = UsedStruct1 { x: 1 }; let used_struct2 = UsedStruct2(1); let used_struct3 = UsedStruct3; let t = used_static; let e = foo3; } ``` it would add the following warnings: ```rust /home/ktt3ja/test.rs:2:0: 2:28 warning: code is never used: `priv_static`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:2 static priv_static: int = 0; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/ktt3ja/test.rs:6:0: 6:48 warning: code is never used: `priv_fn`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:6 fn priv_fn() { let unused_struct = PrivStruct; } ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/ktt3ja/test.rs:10:0: 10:20 warning: code is never used: `PrivStruct`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:10 struct PrivStruct(); ^~~~~~~~~~~~~~~~~~~~ /home/ktt3ja/test.rs:16:0: 16:29 warning: code is never used: `priv_enum`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:16 enum priv_enum { foo2, bar2 } ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/ktt3ja/test.rs:19:0: 22:1 warning: code is never used: `foo`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:19 fn foo() { /home/ktt3ja/test.rs:20 bar(); /home/ktt3ja/test.rs:21 let unused_enum = foo2; /home/ktt3ja/test.rs:22 } /home/ktt3ja/test.rs:24:0: 26:1 warning: code is never used: `bar`, #[warn(dead_code)] on by default /home/ktt3ja/test.rs:24 fn bar() { /home/ktt3ja/test.rs:25 foo(); /home/ktt3ja/test.rs:26 } ``` Furthermore, I would like to solicit some test cases since I haven't tested extensively and I'm still unclear about some of the things in here. For example, I'm not sure how reexports would affect this and just assumed that LiveContext (which is a copy of reachable::ReachableContext) does enough work to handle it. Also, the test case above doesn't include any impl or methods, etc.
huonw
commented
Dec 8, 2013
\o/ |
10477: parser: fix parsing of macro call inside generic args r=Veykril a=cynecx 10482: fix: fix `inline_call` trying to use an uncached syntax node in Semantics r=Veykril a=Veykril Fixesrust-lang/rust-analyzer#10475 bors r+ Co-authored-by: cynecx <me@cynecx.net> Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
PR for issue #1749 mainly to get some feedback and suggestion. This adds a pass that warns if a function, struct, enum, or static item is never used. For the following code,
it would add the following warnings:
Furthermore, I would like to solicit some test cases since I haven't tested extensively and I'm still unclear about some of the things in here. For example, I'm not sure how reexports would affect this and just assumed that LiveContext (which is a copy of reachable::ReachableContext) does enough work to handle it. Also, the test case above doesn't include any impl or methods, etc.