rustc_resolve: move unused import checking into its own module.
This commit is contained in:
parent
c54fc980f3
commit
1614d92f51
2 changed files with 178 additions and 141 deletions
161
src/librustc_resolve/check_unused.rs
Normal file
161
src/librustc_resolve/check_unused.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unused import checking
|
||||||
|
//
|
||||||
|
// Although this is mostly a lint pass, it lives in here because it depends on
|
||||||
|
// resolve data structures and because it finalises the privacy information for
|
||||||
|
// `use` directives.
|
||||||
|
//
|
||||||
|
|
||||||
|
use Resolver;
|
||||||
|
use Namespace::{TypeNS, ValueNS};
|
||||||
|
|
||||||
|
use rustc::lint;
|
||||||
|
use rustc::middle::privacy::{DependsOn, LastImport, Used, Unused};
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::ast::{ViewItem, ViewItemExternCrate, ViewItemUse};
|
||||||
|
use syntax::ast::{ViewPathGlob, ViewPathList, ViewPathSimple};
|
||||||
|
use syntax::codemap::{Span, DUMMY_SP};
|
||||||
|
use syntax::visit::{mod, Visitor};
|
||||||
|
|
||||||
|
struct UnusedImportCheckVisitor<'a, 'b:'a> {
|
||||||
|
resolver: &'a mut Resolver<'b>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
|
||||||
|
impl<'a, 'b> Deref<Resolver<'b>> for UnusedImportCheckVisitor<'a, 'b> {
|
||||||
|
fn deref<'c>(&'c self) -> &'c Resolver<'b> {
|
||||||
|
&*self.resolver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> DerefMut<Resolver<'b>> for UnusedImportCheckVisitor<'a, 'b> {
|
||||||
|
fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
|
||||||
|
&mut *self.resolver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
|
||||||
|
// We have information about whether `use` (import) directives are actually used now.
|
||||||
|
// If an import is not used at all, we signal a lint error. If an import is only used
|
||||||
|
// for a single namespace, we remove the other namespace from the recorded privacy
|
||||||
|
// information. That means in privacy.rs, we will only check imports and namespaces
|
||||||
|
// which are used. In particular, this means that if an import could name either a
|
||||||
|
// public or private item, we will check the correct thing, dependent on how the import
|
||||||
|
// is used.
|
||||||
|
fn finalize_import(&mut self, id: ast::NodeId, span: Span) {
|
||||||
|
debug!("finalizing import uses for {}",
|
||||||
|
self.session.codemap().span_to_snippet(span));
|
||||||
|
|
||||||
|
if !self.used_imports.contains(&(id, TypeNS)) &&
|
||||||
|
!self.used_imports.contains(&(id, ValueNS)) {
|
||||||
|
self.session.add_lint(lint::builtin::UNUSED_IMPORTS,
|
||||||
|
id,
|
||||||
|
span,
|
||||||
|
"unused import".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (v_priv, t_priv) = match self.last_private.get(&id) {
|
||||||
|
Some(&LastImport {
|
||||||
|
value_priv: v,
|
||||||
|
value_used: _,
|
||||||
|
type_priv: t,
|
||||||
|
type_used: _
|
||||||
|
}) => (v, t),
|
||||||
|
Some(_) => {
|
||||||
|
panic!("we should only have LastImport for `use` directives")
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut v_used = if self.used_imports.contains(&(id, ValueNS)) {
|
||||||
|
Used
|
||||||
|
} else {
|
||||||
|
Unused
|
||||||
|
};
|
||||||
|
let t_used = if self.used_imports.contains(&(id, TypeNS)) {
|
||||||
|
Used
|
||||||
|
} else {
|
||||||
|
Unused
|
||||||
|
};
|
||||||
|
|
||||||
|
match (v_priv, t_priv) {
|
||||||
|
// Since some items may be both in the value _and_ type namespaces (e.g., structs)
|
||||||
|
// we might have two LastPrivates pointing at the same thing. There is no point
|
||||||
|
// checking both, so lets not check the value one.
|
||||||
|
(Some(DependsOn(def_v)), Some(DependsOn(def_t))) if def_v == def_t => v_used = Unused,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_private.insert(id, LastImport{value_priv: v_priv,
|
||||||
|
value_used: v_used,
|
||||||
|
type_priv: t_priv,
|
||||||
|
type_used: t_used});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'v> Visitor<'v> for UnusedImportCheckVisitor<'a, 'b> {
|
||||||
|
fn visit_view_item(&mut self, vi: &ViewItem) {
|
||||||
|
// Ignore is_public import statements because there's no way to be sure
|
||||||
|
// whether they're used or not. Also ignore imports with a dummy span
|
||||||
|
// because this means that they were generated in some fashion by the
|
||||||
|
// compiler and we don't need to consider them.
|
||||||
|
if vi.vis == ast::Public || vi.span == DUMMY_SP {
|
||||||
|
visit::walk_view_item(self, vi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match vi.node {
|
||||||
|
ViewItemExternCrate(_, _, id) => {
|
||||||
|
if let Some(crate_num) = self.session.cstore.find_extern_mod_stmt_cnum(id) {
|
||||||
|
if !self.used_crates.contains(&crate_num) {
|
||||||
|
self.session.add_lint(lint::builtin::UNUSED_EXTERN_CRATES,
|
||||||
|
id,
|
||||||
|
vi.span,
|
||||||
|
"unused extern crate".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ViewItemUse(ref p) => {
|
||||||
|
match p.node {
|
||||||
|
ViewPathSimple(_, _, id) => {
|
||||||
|
self.finalize_import(id, p.span)
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPathList(_, ref list, _) => {
|
||||||
|
for i in list.iter() {
|
||||||
|
self.finalize_import(i.node.id(), i.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ViewPathGlob(_, id) => {
|
||||||
|
if !self.used_imports.contains(&(id, TypeNS)) &&
|
||||||
|
!self.used_imports.contains(&(id, ValueNS)) {
|
||||||
|
self.session
|
||||||
|
.add_lint(lint::builtin::UNUSED_IMPORTS,
|
||||||
|
id,
|
||||||
|
p.span,
|
||||||
|
"unused import".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit::walk_view_item(self, vi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
|
||||||
|
let mut visitor = UnusedImportCheckVisitor { resolver: resolver };
|
||||||
|
visit::walk_crate(&mut visitor, krate);
|
||||||
|
}
|
|
@ -96,6 +96,8 @@ use std::mem::replace;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::uint;
|
use std::uint;
|
||||||
|
|
||||||
|
mod check_unused;
|
||||||
|
|
||||||
#[deriving(Copy)]
|
#[deriving(Copy)]
|
||||||
struct BindingInfo {
|
struct BindingInfo {
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -935,17 +937,6 @@ impl<'a, 'b, 'v> Visitor<'v> for BuildReducedGraphVisitor<'a, 'b> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnusedImportCheckVisitor<'a, 'b:'a> {
|
|
||||||
resolver: &'a mut Resolver<'b>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'v> Visitor<'v> for UnusedImportCheckVisitor<'a, 'b> {
|
|
||||||
fn visit_view_item(&mut self, vi: &ViewItem) {
|
|
||||||
self.resolver.check_for_item_unused_imports(vi);
|
|
||||||
visit::walk_view_item(self, vi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq)]
|
||||||
enum FallbackChecks {
|
enum FallbackChecks {
|
||||||
Everything,
|
Everything,
|
||||||
|
@ -1006,22 +997,6 @@ impl<'a> Resolver<'a> {
|
||||||
emit_errors: true,
|
emit_errors: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// The main name resolution procedure.
|
|
||||||
fn resolve(&mut self, krate: &ast::Crate) {
|
|
||||||
self.build_reduced_graph(krate);
|
|
||||||
self.session.abort_if_errors();
|
|
||||||
|
|
||||||
self.resolve_imports();
|
|
||||||
self.session.abort_if_errors();
|
|
||||||
|
|
||||||
self.record_exports();
|
|
||||||
self.session.abort_if_errors();
|
|
||||||
|
|
||||||
self.resolve_crate(krate);
|
|
||||||
self.session.abort_if_errors();
|
|
||||||
|
|
||||||
self.check_for_unused_imports(krate);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Reduced graph building
|
// Reduced graph building
|
||||||
|
@ -6068,119 +6043,6 @@ impl<'a> Resolver<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Unused import checking
|
|
||||||
//
|
|
||||||
// Although this is mostly a lint pass, it lives in here because it depends on
|
|
||||||
// resolve data structures and because it finalises the privacy information for
|
|
||||||
// `use` directives.
|
|
||||||
//
|
|
||||||
|
|
||||||
fn check_for_unused_imports(&mut self, krate: &ast::Crate) {
|
|
||||||
let mut visitor = UnusedImportCheckVisitor{ resolver: self };
|
|
||||||
visit::walk_crate(&mut visitor, krate);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_for_item_unused_imports(&mut self, vi: &ViewItem) {
|
|
||||||
// Ignore is_public import statements because there's no way to be sure
|
|
||||||
// whether they're used or not. Also ignore imports with a dummy span
|
|
||||||
// because this means that they were generated in some fashion by the
|
|
||||||
// compiler and we don't need to consider them.
|
|
||||||
if vi.vis == Public { return }
|
|
||||||
if vi.span == DUMMY_SP { return }
|
|
||||||
|
|
||||||
match vi.node {
|
|
||||||
ViewItemExternCrate(_, _, id) => {
|
|
||||||
if let Some(crate_num) = self.session.cstore.find_extern_mod_stmt_cnum(id) {
|
|
||||||
if !self.used_crates.contains(&crate_num) {
|
|
||||||
self.session.add_lint(lint::builtin::UNUSED_EXTERN_CRATES,
|
|
||||||
id,
|
|
||||||
vi.span,
|
|
||||||
"unused extern crate".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ViewItemUse(ref p) => {
|
|
||||||
match p.node {
|
|
||||||
ViewPathSimple(_, _, id) => self.finalize_import(id, p.span),
|
|
||||||
|
|
||||||
ViewPathList(_, ref list, _) => {
|
|
||||||
for i in list.iter() {
|
|
||||||
self.finalize_import(i.node.id(), i.span);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ViewPathGlob(_, id) => {
|
|
||||||
if !self.used_imports.contains(&(id, TypeNS)) &&
|
|
||||||
!self.used_imports.contains(&(id, ValueNS)) {
|
|
||||||
self.session
|
|
||||||
.add_lint(lint::builtin::UNUSED_IMPORTS,
|
|
||||||
id,
|
|
||||||
p.span,
|
|
||||||
"unused import".to_string());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have information about whether `use` (import) directives are actually used now.
|
|
||||||
// If an import is not used at all, we signal a lint error. If an import is only used
|
|
||||||
// for a single namespace, we remove the other namespace from the recorded privacy
|
|
||||||
// information. That means in privacy.rs, we will only check imports and namespaces
|
|
||||||
// which are used. In particular, this means that if an import could name either a
|
|
||||||
// public or private item, we will check the correct thing, dependent on how the import
|
|
||||||
// is used.
|
|
||||||
fn finalize_import(&mut self, id: NodeId, span: Span) {
|
|
||||||
debug!("finalizing import uses for {}",
|
|
||||||
self.session.codemap().span_to_snippet(span));
|
|
||||||
|
|
||||||
if !self.used_imports.contains(&(id, TypeNS)) &&
|
|
||||||
!self.used_imports.contains(&(id, ValueNS)) {
|
|
||||||
self.session.add_lint(lint::builtin::UNUSED_IMPORTS,
|
|
||||||
id,
|
|
||||||
span,
|
|
||||||
"unused import".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let (v_priv, t_priv) = match self.last_private.get(&id) {
|
|
||||||
Some(&LastImport {
|
|
||||||
value_priv: v,
|
|
||||||
value_used: _,
|
|
||||||
type_priv: t,
|
|
||||||
type_used: _
|
|
||||||
}) => (v, t),
|
|
||||||
Some(_) => {
|
|
||||||
panic!("we should only have LastImport for `use` directives")
|
|
||||||
}
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut v_used = if self.used_imports.contains(&(id, ValueNS)) {
|
|
||||||
Used
|
|
||||||
} else {
|
|
||||||
Unused
|
|
||||||
};
|
|
||||||
let t_used = if self.used_imports.contains(&(id, TypeNS)) {
|
|
||||||
Used
|
|
||||||
} else {
|
|
||||||
Unused
|
|
||||||
};
|
|
||||||
|
|
||||||
match (v_priv, t_priv) {
|
|
||||||
// Since some items may be both in the value _and_ type namespaces (e.g., structs)
|
|
||||||
// we might have two LastPrivates pointing at the same thing. There is no point
|
|
||||||
// checking both, so lets not check the value one.
|
|
||||||
(Some(DependsOn(def_v)), Some(DependsOn(def_t))) if def_v == def_t => v_used = Unused,
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
self.last_private.insert(id, LastImport{value_priv: v_priv,
|
|
||||||
value_used: v_used,
|
|
||||||
type_priv: t_priv,
|
|
||||||
type_used: t_used});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Diagnostics
|
// Diagnostics
|
||||||
//
|
//
|
||||||
|
@ -6268,7 +6130,21 @@ pub fn resolve_crate(session: &Session,
|
||||||
krate: &Crate)
|
krate: &Crate)
|
||||||
-> CrateMap {
|
-> CrateMap {
|
||||||
let mut resolver = Resolver::new(session, krate.span);
|
let mut resolver = Resolver::new(session, krate.span);
|
||||||
resolver.resolve(krate);
|
|
||||||
|
resolver.build_reduced_graph(krate);
|
||||||
|
session.abort_if_errors();
|
||||||
|
|
||||||
|
resolver.resolve_imports();
|
||||||
|
session.abort_if_errors();
|
||||||
|
|
||||||
|
resolver.record_exports();
|
||||||
|
session.abort_if_errors();
|
||||||
|
|
||||||
|
resolver.resolve_crate(krate);
|
||||||
|
session.abort_if_errors();
|
||||||
|
|
||||||
|
check_unused::check_crate(&mut resolver, krate);
|
||||||
|
|
||||||
CrateMap {
|
CrateMap {
|
||||||
def_map: resolver.def_map,
|
def_map: resolver.def_map,
|
||||||
freevars: resolver.freevars,
|
freevars: resolver.freevars,
|
||||||
|
|
Loading…
Reference in a new issue