Rollup merge of #65105 - Mark-Simulacrum:split-librustc, r=nikomatsakis

Split out some passes from librustc

This is just moving them out to librustc_passes -- I've not measured compile time or run time. I don't expect any significant impact, but this seems prudent regardless.
This commit is contained in:
Tyler Mandry 2019-10-05 21:55:00 -07:00 committed by GitHub
commit 2c8cbcca54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 238 additions and 228 deletions

View file

@ -466,66 +466,6 @@ fn main() {
```
"##,
// This shouldn't really ever trigger since the repeated value error comes first
E0136: r##"
A binary can only have one entry point, and by default that entry point is the
function `main()`. If there are multiple such functions, please rename one.
"##,
E0137: r##"
More than one function was declared with the `#[main]` attribute.
Erroneous code example:
```compile_fail,E0137
#![feature(main)]
#[main]
fn foo() {}
#[main]
fn f() {} // error: multiple functions with a `#[main]` attribute
```
This error indicates that the compiler found multiple functions with the
`#[main]` attribute. This is an error because there must be a unique entry
point into a Rust program. Example:
```
#![feature(main)]
#[main]
fn f() {} // ok!
```
"##,
E0138: r##"
More than one function was declared with the `#[start]` attribute.
Erroneous code example:
```compile_fail,E0138
#![feature(start)]
#[start]
fn foo(argc: isize, argv: *const *const u8) -> isize {}
#[start]
fn f(argc: isize, argv: *const *const u8) -> isize {}
// error: multiple 'start' functions
```
This error indicates that the compiler found multiple functions with the
`#[start]` attribute. This is an error because there must be a unique entry
point into a Rust program. Example:
```
#![feature(start)]
#[start]
fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok!
```
"##,
E0139: r##"
#### Note: this error code is no longer emitted by the compiler.
@ -1626,33 +1566,6 @@ It is not possible to use stability attributes outside of the standard library.
Also, for now, it is not possible to write deprecation messages either.
"##,
E0512: r##"
Transmute with two differently sized types was attempted. Erroneous code
example:
```compile_fail,E0512
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0u16)); }
// error: cannot transmute between types of different sizes,
// or dependently-sized types
}
```
Please use types with same size or use the expected type directly. Example:
```
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
// or:
unsafe { takes_u8(0u8); } // ok!
}
```
"##,
E0517: r##"
This error indicates that a `#[repr(..)]` attribute was placed on an
unsupported item.
@ -1847,84 +1760,6 @@ See [RFC 1522] for more details.
[RFC 1522]: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
"##,
E0591: r##"
Per [RFC 401][rfc401], if you have a function declaration `foo`:
```
// For the purposes of this explanation, all of these
// different kinds of `fn` declarations are equivalent:
struct S;
fn foo(x: S) { /* ... */ }
# #[cfg(for_demonstration_only)]
extern "C" { fn foo(x: S); }
# #[cfg(for_demonstration_only)]
impl S { fn foo(self) { /* ... */ } }
```
the type of `foo` is **not** `fn(S)`, as one might expect.
Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`.
However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`,
so you rarely notice this:
```
# struct S;
# fn foo(_: S) {}
let x: fn(S) = foo; // OK, coerces
```
The reason that this matter is that the type `fn(S)` is not specific to
any particular function: it's a function _pointer_. So calling `x()` results
in a virtual call, whereas `foo()` is statically dispatched, because the type
of `foo` tells us precisely what function is being called.
As noted above, coercions mean that most code doesn't have to be
concerned with this distinction. However, you can tell the difference
when using **transmute** to convert a fn item into a fn pointer.
This is sometimes done as part of an FFI:
```compile_fail,E0591
extern "C" fn foo(userdata: Box<i32>) {
/* ... */
}
# fn callback(_: extern "C" fn(*mut i32)) {}
# use std::mem::transmute;
# unsafe {
let f: extern "C" fn(*mut i32) = transmute(foo);
callback(f);
# }
```
Here, transmute is being used to convert the types of the fn arguments.
This pattern is incorrect because, because the type of `foo` is a function
**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
is a function pointer, which is not zero-sized.
This pattern should be rewritten. There are a few possible ways to do this:
- change the original fn declaration to match the expected signature,
and do the cast in the fn body (the preferred option)
- cast the fn item fo a fn pointer before calling transmute, as shown here:
```
# extern "C" fn foo(_: Box<i32>) {}
# use std::mem::transmute;
# unsafe {
let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too
# }
```
The same applies to transmutes to `*mut fn()`, which were observed in practice.
Note though that use of this type is generally incorrect.
The intention is typically to describe a function pointer, but just `fn()`
alone suffices for that. `*mut fn()` is a pointer to a fn pointer.
(Since these values are typically just passed to C code, however, this rarely
makes a difference in practice.)
[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
"##,
E0593: r##"
You tried to supply an `Fn`-based type with an incorrect number of arguments
than what was expected.
@ -1941,21 +1776,6 @@ fn main() {
```
"##,
E0601: r##"
No `main` function was found in a binary crate. To fix this error, add a
`main` function. For example:
```
fn main() {
// Your program will start here.
println!("Hello world!");
}
```
If you don't know the basics of Rust, you can go look to the Rust Book to get
started: https://doc.rust-lang.org/book/
"##,
E0602: r##"
An unknown lint was used on the command line.

View file

@ -101,16 +101,12 @@ pub mod lint;
pub mod middle {
pub mod expr_use_visitor;
pub mod cstore;
pub mod dead;
pub mod dependency_format;
pub mod diagnostic_items;
pub mod entry;
pub mod exported_symbols;
pub mod free_region;
pub mod intrinsicck;
pub mod lib_features;
pub mod lang_items;
pub mod liveness;
pub mod mem_categorization;
pub mod privacy;
pub mod reachable;

View file

@ -1,676 +0,0 @@
// This implements the dead-code warning pass. It follows middle::reachable
// closely. The idea is that all reachable symbols are live, codes called
// from live codes are live, and everything else is dead.
use crate::hir::Node;
use crate::hir::{self, PatKind, TyKind};
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::hir::itemlikevisit::ItemLikeVisitor;
use crate::hir::def::{CtorOf, Res, DefKind};
use crate::hir::CodegenFnAttrFlags;
use crate::hir::def_id::{DefId, LOCAL_CRATE};
use crate::lint;
use crate::middle::privacy;
use crate::ty::{self, DefIdTree, TyCtxt};
use crate::util::nodemap::FxHashSet;
use rustc_data_structures::fx::FxHashMap;
use syntax::{ast, attr};
use syntax::symbol::sym;
use syntax_pos;
// Any local node that may call something in its body block should be
// explored. For example, if it's a live Node::Item that is a
// function, then we should explore its block to check for codes that
// may need to be marked as live.
fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
match tcx.hir().find(hir_id) {
Some(Node::Item(..)) |
Some(Node::ImplItem(..)) |
Some(Node::ForeignItem(..)) |
Some(Node::TraitItem(..)) |
Some(Node::Variant(..)) |
Some(Node::AnonConst(..)) |
Some(Node::Pat(..)) => true,
_ => false
}
}
struct MarkSymbolVisitor<'a, 'tcx> {
worklist: Vec<hir::HirId>,
tcx: TyCtxt<'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
live_symbols: FxHashSet<hir::HirId>,
repr_has_repr_c: bool,
in_pat: bool,
inherited_pub_visibility: bool,
ignore_variant_stack: Vec<DefId>,
// maps from tuple struct constructors to tuple struct items
struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
}
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
fn check_def_id(&mut self, def_id: DefId) {
if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) {
self.worklist.push(hir_id);
}
self.live_symbols.insert(hir_id);
}
}
fn insert_def_id(&mut self, def_id: DefId) {
if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
debug_assert!(!should_explore(self.tcx, hir_id));
self.live_symbols.insert(hir_id);
}
}
fn handle_res(&mut self, res: Res) {
match res {
Res::Def(DefKind::Const, _)
| Res::Def(DefKind::AssocConst, _)
| Res::Def(DefKind::TyAlias, _) => {
self.check_def_id(res.def_id());
}
_ if self.in_pat => {},
Res::PrimTy(..) | Res::SelfCtor(..) |
Res::Local(..) => {}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
let variant_id = self.tcx.parent(ctor_def_id).unwrap();
let enum_id = self.tcx.parent(variant_id).unwrap();
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&ctor_def_id) {
self.check_def_id(variant_id);
}
}
Res::Def(DefKind::Variant, variant_id) => {
let enum_id = self.tcx.parent(variant_id).unwrap();
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&variant_id) {
self.check_def_id(variant_id);
}
}
Res::SelfTy(t, i) => {
if let Some(t) = t {
self.check_def_id(t);
}
if let Some(i) = i {
self.check_def_id(i);
}
}
Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
_ => {
self.check_def_id(res.def_id());
}
}
}
fn lookup_and_handle_method(&mut self, id: hir::HirId) {
if let Some(def_id) = self.tables.type_dependent_def_id(id) {
self.check_def_id(def_id);
} else {
bug!("no type-dependent def for method");
}
}
fn handle_field_access(&mut self, lhs: &hir::Expr, hir_id: hir::HirId) {
match self.tables.expr_ty_adjusted(lhs).kind {
ty::Adt(def, _) => {
let index = self.tcx.field_index(hir_id, self.tables);
self.insert_def_id(def.non_enum_variant().fields[index].did);
}
ty::Tuple(..) => {}
_ => span_bug!(lhs.span, "named field access on non-ADT"),
}
}
fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, res: Res, pats: &[hir::FieldPat]) {
let variant = match self.tables.node_type(lhs.hir_id).kind {
ty::Adt(adt, _) => adt.variant_of_res(res),
_ => span_bug!(lhs.span, "non-ADT in struct pattern")
};
for pat in pats {
if let PatKind::Wild = pat.pat.kind {
continue;
}
let index = self.tcx.field_index(pat.hir_id, self.tables);
self.insert_def_id(variant.fields[index].did);
}
}
fn mark_live_symbols(&mut self) {
let mut scanned = FxHashSet::default();
while let Some(id) = self.worklist.pop() {
if !scanned.insert(id) {
continue
}
// in the case of tuple struct constructors we want to check the item, not the generated
// tuple struct constructor function
let id = self.struct_constructors.get(&id).cloned().unwrap_or(id);
if let Some(node) = self.tcx.hir().find(id) {
self.live_symbols.insert(id);
self.visit_node(node);
}
}
}
fn visit_node(&mut self, node: Node<'tcx>) {
let had_repr_c = self.repr_has_repr_c;
self.repr_has_repr_c = false;
let had_inherited_pub_visibility = self.inherited_pub_visibility;
self.inherited_pub_visibility = false;
match node {
Node::Item(item) => {
match item.kind {
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
let def_id = self.tcx.hir().local_def_id(item.hir_id);
let def = self.tcx.adt_def(def_id);
self.repr_has_repr_c = def.repr.c();
intravisit::walk_item(self, &item);
}
hir::ItemKind::Enum(..) => {
self.inherited_pub_visibility = item.vis.node.is_pub();
intravisit::walk_item(self, &item);
}
hir::ItemKind::ForeignMod(..) => {}
_ => {
intravisit::walk_item(self, &item);
}
}
}
Node::TraitItem(trait_item) => {
intravisit::walk_trait_item(self, trait_item);
}
Node::ImplItem(impl_item) => {
intravisit::walk_impl_item(self, impl_item);
}
Node::ForeignItem(foreign_item) => {
intravisit::walk_foreign_item(self, &foreign_item);
}
_ => {}
}
self.repr_has_repr_c = had_repr_c;
self.inherited_pub_visibility = had_inherited_pub_visibility;
}
fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &hir::HirVec<hir::Field>) {
if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
for field in fields {
let index = self.tcx.field_index(field.hir_id, self.tables);
self.insert_def_id(adt.non_enum_variant().fields[index].did);
}
}
}
}
impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body: hir::BodyId) {
let old_tables = self.tables;
self.tables = self.tcx.body_tables(body);
let body = self.tcx.hir().body(body);
self.visit_body(body);
self.tables = old_tables;
}
fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name,
_: &hir::Generics, _: hir::HirId, _: syntax_pos::Span) {
let has_repr_c = self.repr_has_repr_c;
let inherited_pub_visibility = self.inherited_pub_visibility;
let live_fields = def.fields().iter().filter(|f| {
has_repr_c || inherited_pub_visibility || f.vis.node.is_pub()
});
self.live_symbols.extend(live_fields.map(|f| f.hir_id));
intravisit::walk_struct_def(self, def);
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
match expr.kind {
hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
let res = self.tables.qpath_res(qpath, expr.hir_id);
self.handle_res(res);
}
hir::ExprKind::MethodCall(..) => {
self.lookup_and_handle_method(expr.hir_id);
}
hir::ExprKind::Field(ref lhs, ..) => {
self.handle_field_access(&lhs, expr.hir_id);
}
hir::ExprKind::Struct(_, ref fields, _) => {
if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind {
self.mark_as_used_if_union(adt, fields);
}
}
_ => ()
}
intravisit::walk_expr(self, expr);
}
fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
// Inside the body, ignore constructions of variants
// necessary for the pattern to match. Those construction sites
// can't be reached unless the variant is constructed elsewhere.
let len = self.ignore_variant_stack.len();
self.ignore_variant_stack.extend(arm.pat.necessary_variants());
intravisit::walk_arm(self, arm);
self.ignore_variant_stack.truncate(len);
}
fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
match pat.kind {
PatKind::Struct(ref path, ref fields, _) => {
let res = self.tables.qpath_res(path, pat.hir_id);
self.handle_field_pattern_match(pat, res, fields);
}
PatKind::Path(ref qpath) => {
let res = self.tables.qpath_res(qpath, pat.hir_id);
self.handle_res(res);
}
_ => ()
}
self.in_pat = true;
intravisit::walk_pat(self, pat);
self.in_pat = false;
}
fn visit_path(&mut self, path: &'tcx hir::Path, _: hir::HirId) {
self.handle_res(path.res);
intravisit::walk_path(self, path);
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
match ty.kind {
TyKind::Def(item_id, _) => {
let item = self.tcx.hir().expect_item(item_id.id);
intravisit::walk_item(self, item);
}
_ => ()
}
intravisit::walk_ty(self, ty);
}
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
self.live_symbols.insert(c.hir_id);
intravisit::walk_anon_const(self, c);
}
}
fn has_allow_dead_code_or_lang_attr(
tcx: TyCtxt<'_>,
id: hir::HirId,
attrs: &[ast::Attribute],
) -> bool {
if attr::contains_name(attrs, sym::lang) {
return true;
}
// Stable attribute for #[lang = "panic_impl"]
if attr::contains_name(attrs, sym::panic_handler) {
return true;
}
// (To be) stable attribute for #[lang = "oom"]
if attr::contains_name(attrs, sym::alloc_error_handler) {
return true;
}
let def_id = tcx.hir().local_def_id(id);
let cg_attrs = tcx.codegen_fn_attrs(def_id);
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
if cg_attrs.contains_extern_indicator() ||
cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
return true;
}
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
}
// This visitor seeds items that
// 1) We want to explicitly consider as live:
// * Item annotated with #[allow(dead_code)]
// - This is done so that if we want to suppress warnings for a
// group of dead functions, we only have to annotate the "root".
// For example, if both `f` and `g` are dead and `f` calls `g`,
// then annotating `f` with `#[allow(dead_code)]` will suppress
// warning for both `f` and `g`.
// * Item annotated with #[lang=".."]
// - This is because lang items are always callable from elsewhere.
// or
// 2) We are not sure to be live or not
// * Implementation of a trait method
struct LifeSeeder<'k, 'tcx> {
worklist: Vec<hir::HirId>,
krate: &'k hir::Crate,
tcx: TyCtxt<'tcx>,
// see `MarkSymbolVisitor::struct_constructors`
struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
}
impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) {
let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx,
item.hir_id,
&item.attrs);
if allow_dead_code {
self.worklist.push(item.hir_id);
}
match item.kind {
hir::ItemKind::Enum(ref enum_def, _) => {
if allow_dead_code {
self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id));
}
for variant in &enum_def.variants {
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
self.struct_constructors.insert(ctor_hir_id, variant.id);
}
}
}
hir::ItemKind::Trait(.., ref trait_item_refs) => {
for trait_item_ref in trait_item_refs {
let trait_item = self.krate.trait_item(trait_item_ref.id);
match trait_item.kind {
hir::TraitItemKind::Const(_, Some(_)) |
hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
if has_allow_dead_code_or_lang_attr(self.tcx,
trait_item.hir_id,
&trait_item.attrs) {
self.worklist.push(trait_item.hir_id);
}
}
_ => {}
}
}
}
hir::ItemKind::Impl(.., ref opt_trait, _, ref impl_item_refs) => {
for impl_item_ref in impl_item_refs {
let impl_item = self.krate.impl_item(impl_item_ref.id);
if opt_trait.is_some() ||
has_allow_dead_code_or_lang_attr(self.tcx,
impl_item.hir_id,
&impl_item.attrs) {
self.worklist.push(impl_item_ref.id.hir_id);
}
}
}
hir::ItemKind::Struct(ref variant_data, _) => {
if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
self.struct_constructors.insert(ctor_hir_id, item.hir_id);
}
}
_ => ()
}
}
fn visit_trait_item(&mut self, _item: &hir::TraitItem) {
// ignore: we are handling this in `visit_item` above
}
fn visit_impl_item(&mut self, _item: &hir::ImplItem) {
// ignore: we are handling this in `visit_item` above
}
}
fn create_and_seed_worklist<'tcx>(
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
krate: &hir::Crate,
) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) {
let worklist = access_levels.map.iter().filter_map(|(&id, level)| {
if level >= &privacy::AccessLevel::Reachable {
Some(id)
} else {
None
}
}).chain(
// Seed entry point
tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id).unwrap())
).collect::<Vec<_>>();
// Seed implemented trait items
let mut life_seeder = LifeSeeder {
worklist,
krate,
tcx,
struct_constructors: Default::default(),
};
krate.visit_all_item_likes(&mut life_seeder);
(life_seeder.worklist, life_seeder.struct_constructors)
}
fn find_live<'tcx>(
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
krate: &hir::Crate,
) -> FxHashSet<hir::HirId> {
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate);
let mut symbol_visitor = MarkSymbolVisitor {
worklist,
tcx,
tables: &ty::TypeckTables::empty(None),
live_symbols: Default::default(),
repr_has_repr_c: false,
in_pat: false,
inherited_pub_visibility: false,
ignore_variant_stack: vec![],
struct_constructors,
};
symbol_visitor.mark_live_symbols();
symbol_visitor.live_symbols
}
struct DeadVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
live_symbols: FxHashSet<hir::HirId>,
}
impl DeadVisitor<'tcx> {
fn should_warn_about_item(&mut self, item: &hir::Item) -> bool {
let should_warn = match item.kind {
hir::ItemKind::Static(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..) => true,
_ => false
};
should_warn && !self.symbol_is_live(item.hir_id)
}
fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool {
let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id));
!field.is_positional()
&& !self.symbol_is_live(field.hir_id)
&& !field_type.is_phantom_data()
&& !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs)
}
fn should_warn_about_variant(&mut self, variant: &hir::Variant) -> bool {
!self.symbol_is_live(variant.id)
&& !has_allow_dead_code_or_lang_attr(self.tcx,
variant.id,
&variant.attrs)
}
fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool {
!self.symbol_is_live(fi.hir_id)
&& !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id, &fi.attrs)
}
// id := HIR id of an item's definition.
fn symbol_is_live(
&mut self,
id: hir::HirId,
) -> bool {
if self.live_symbols.contains(&id) {
return true;
}
// If it's a type whose items are live, then it's live, too.
// This is done to handle the case where, for example, the static
// method of a private type is used, but the type itself is never
// called directly.
let def_id = self.tcx.hir().local_def_id(id);
let inherent_impls = self.tcx.inherent_impls(def_id);
for &impl_did in inherent_impls.iter() {
for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] {
if let Some(item_hir_id) = self.tcx.hir().as_local_hir_id(item_did) {
if self.live_symbols.contains(&item_hir_id) {
return true;
}
}
}
}
false
}
fn warn_dead_code(&mut self,
id: hir::HirId,
span: syntax_pos::Span,
name: ast::Name,
node_type: &str,
participle: &str) {
if !name.as_str().starts_with("_") {
self.tcx
.lint_hir(lint::builtin::DEAD_CODE,
id,
span,
&format!("{} is never {}: `{}`",
node_type, participle, name));
}
}
}
impl Visitor<'tcx> for DeadVisitor<'tcx> {
/// Walk nested items in place so that we don't report dead-code
/// on inner functions when the outer function is already getting
/// an error. We could do this also by checking the parents, but
/// this is how the code is setup and it seems harmless enough.
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::All(&self.tcx.hir())
}
fn visit_item(&mut self, item: &'tcx hir::Item) {
if self.should_warn_about_item(item) {
// For items that have a definition with a signature followed by a
// block, point only at the signature.
let span = match item.kind {
hir::ItemKind::Fn(..) |
hir::ItemKind::Mod(..) |
hir::ItemKind::Enum(..) |
hir::ItemKind::Struct(..) |
hir::ItemKind::Union(..) |
hir::ItemKind::Trait(..) |
hir::ItemKind::Impl(..) => self.tcx.sess.source_map().def_span(item.span),
_ => item.span,
};
let participle = match item.kind {
hir::ItemKind::Struct(..) => "constructed", // Issue #52325
_ => "used"
};
self.warn_dead_code(
item.hir_id,
span,
item.ident.name,
item.kind.descriptive_variant(),
participle,
);
} else {
// Only continue if we didn't warn
intravisit::walk_item(self, item);
}
}
fn visit_variant(&mut self,
variant: &'tcx hir::Variant,
g: &'tcx hir::Generics,
id: hir::HirId) {
if self.should_warn_about_variant(&variant) {
self.warn_dead_code(variant.id, variant.span, variant.ident.name,
"variant", "constructed");
} else {
intravisit::walk_variant(self, variant, g, id);
}
}
fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) {
if self.should_warn_about_foreign_item(fi) {
self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name,
fi.kind.descriptive_variant(), "used");
}
intravisit::walk_foreign_item(self, fi);
}
fn visit_struct_field(&mut self, field: &'tcx hir::StructField) {
if self.should_warn_about_field(&field) {
self.warn_dead_code(field.hir_id, field.span, field.ident.name, "field", "used");
}
intravisit::walk_struct_field(self, field);
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
match impl_item.kind {
hir::ImplItemKind::Const(_, body_id) => {
if !self.symbol_is_live(impl_item.hir_id) {
self.warn_dead_code(impl_item.hir_id,
impl_item.span,
impl_item.ident.name,
"associated const",
"used");
}
self.visit_nested_body(body_id)
}
hir::ImplItemKind::Method(_, body_id) => {
if !self.symbol_is_live(impl_item.hir_id) {
let span = self.tcx.sess.source_map().def_span(impl_item.span);
self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "method",
"used");
}
self.visit_nested_body(body_id)
}
hir::ImplItemKind::OpaqueTy(..) |
hir::ImplItemKind::TyAlias(..) => {}
}
}
// Overwrite so that we don't warn the trait item itself.
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
match trait_item.kind {
hir::TraitItemKind::Const(_, Some(body_id)) |
hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)) => {
self.visit_nested_body(body_id)
}
hir::TraitItemKind::Const(_, None) |
hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) |
hir::TraitItemKind::Type(..) => {}
}
}
}
pub fn check_crate(tcx: TyCtxt<'_>) {
let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
let krate = tcx.hir().krate();
let live_symbols = find_live(tcx, access_levels, krate);
let mut visitor = DeadVisitor {
tcx,
live_symbols,
};
intravisit::walk_crate(&mut visitor, krate);
}

View file

@ -1,202 +0,0 @@
use crate::hir::map as hir_map;
use crate::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
use crate::session::{config, Session};
use crate::session::config::EntryFnType;
use syntax::attr;
use syntax::entry::EntryPointType;
use syntax::symbol::sym;
use syntax_pos::Span;
use crate::hir::{HirId, Item, ItemKind, ImplItem, TraitItem};
use crate::hir::itemlikevisit::ItemLikeVisitor;
use crate::ty::TyCtxt;
use crate::ty::query::Providers;
struct EntryContext<'a, 'tcx> {
session: &'a Session,
map: &'a hir_map::Map<'tcx>,
/// The top-level function called `main`.
main_fn: Option<(HirId, Span)>,
/// The function that has attribute named `main`.
attr_main_fn: Option<(HirId, Span)>,
/// The function that has the attribute 'start' on it.
start_fn: Option<(HirId, Span)>,
/// The functions that one might think are `main` but aren't, e.g.
/// main functions not defined at the top level. For diagnostics.
non_main_fns: Vec<(HirId, Span)> ,
}
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx Item) {
let def_id = self.map.local_def_id(item.hir_id);
let def_key = self.map.def_key(def_id);
let at_root = def_key.parent == Some(CRATE_DEF_INDEX);
find_item(item, self, at_root);
}
fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem) {
// Entry fn is never a trait item.
}
fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem) {
// Entry fn is never a trait item.
}
}
fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> {
assert_eq!(cnum, LOCAL_CRATE);
let any_exe = tcx.sess.crate_types.borrow().iter().any(|ty| {
*ty == config::CrateType::Executable
});
if !any_exe {
// No need to find a main function.
return None;
}
// If the user wants no main function at all, then stop here.
if attr::contains_name(&tcx.hir().krate().attrs, sym::no_main) {
return None;
}
let mut ctxt = EntryContext {
session: tcx.sess,
map: tcx.hir(),
main_fn: None,
attr_main_fn: None,
start_fn: None,
non_main_fns: Vec::new(),
};
tcx.hir().krate().visit_all_item_likes(&mut ctxt);
configure_main(tcx, &ctxt)
}
// Beware, this is duplicated in `libsyntax/entry.rs`, so make sure to keep
// them in sync.
fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType {
match item.kind {
ItemKind::Fn(..) => {
if attr::contains_name(&item.attrs, sym::start) {
EntryPointType::Start
} else if attr::contains_name(&item.attrs, sym::main) {
EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if at_root {
// This is a top-level function so can be `main`.
EntryPointType::MainNamed
} else {
EntryPointType::OtherMain
}
} else {
EntryPointType::None
}
}
_ => EntryPointType::None,
}
}
fn find_item(item: &Item, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
match entry_point_type(item, at_root) {
EntryPointType::MainNamed => {
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.hir_id, item.span));
} else {
span_err!(ctxt.session, item.span, E0136,
"multiple `main` functions");
}
},
EntryPointType::OtherMain => {
ctxt.non_main_fns.push((item.hir_id, item.span));
},
EntryPointType::MainAttr => {
if ctxt.attr_main_fn.is_none() {
ctxt.attr_main_fn = Some((item.hir_id, item.span));
} else {
struct_span_err!(ctxt.session, item.span, E0137,
"multiple functions with a `#[main]` attribute")
.span_label(item.span, "additional `#[main]` function")
.span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function")
.emit();
}
},
EntryPointType::Start => {
if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((item.hir_id, item.span));
} else {
struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions")
.span_label(ctxt.start_fn.unwrap().1, "previous `start` function here")
.span_label(item.span, "multiple `start` functions")
.emit();
}
}
EntryPointType::None => (),
}
}
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> {
if let Some((hir_id, _)) = visitor.start_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start))
} else if let Some((hir_id, _)) = visitor.attr_main_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
} else if let Some((hir_id, _)) = visitor.main_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
} else {
no_main_err(tcx, visitor);
None
}
}
fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
// There is no main function.
let mut err = struct_err!(tcx.sess, E0601,
"`main` function not found in crate `{}`", tcx.crate_name(LOCAL_CRATE));
let filename = &tcx.sess.local_crate_source_file;
let note = if !visitor.non_main_fns.is_empty() {
for &(_, span) in &visitor.non_main_fns {
err.span_note(span, "here is a function named `main`");
}
err.note("you have one or more functions named `main` not defined at the crate level");
err.help("either move the `main` function definitions or attach the `#[main]` attribute \
to one of them");
// There were some functions named `main` though. Try to give the user a hint.
format!("the main function must be defined at the crate level{}",
filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default())
} else if let Some(filename) = filename {
format!("consider adding a `main` function to `{}`", filename.display())
} else {
String::from("consider adding a `main` function at the crate level")
};
let sp = tcx.hir().krate().span;
// The file may be empty, which leads to the diagnostic machinery not emitting this
// note. This is a relatively simple way to detect that case and emit a span-less
// note instead.
if let Ok(_) = tcx.sess.source_map().lookup_line(sp.lo()) {
err.set_span(sp);
err.span_label(sp, &note);
} else {
err.note(&note);
}
if tcx.sess.teach(&err.get_code().unwrap()) {
err.note("If you don't know the basics of Rust, you can go look to the Rust Book \
to get started: https://doc.rust-lang.org/book/");
}
err.emit();
}
pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> {
tcx.entry_fn(LOCAL_CRATE)
}
pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
entry_fn,
..*providers
};
}

View file

@ -1,170 +0,0 @@
use crate::hir::def::{Res, DefKind};
use crate::hir::def_id::DefId;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx};
use crate::ty::query::Providers;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use rustc_index::vec::Idx;
use syntax_pos::{Span, sym};
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::hir;
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) {
tcx.hir().visit_item_likes_in_module(
module_def_id,
&mut ItemVisitor { tcx }.as_deep_visitor()
);
}
pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
check_mod_intrinsics,
..*providers
};
}
struct ItemVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
}
struct ExprVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
tables: &'tcx ty::TypeckTables<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
/// If the type is `Option<T>`, it will return `T`, otherwise
/// the type itself. Works on most `Option`-like types.
fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
let (def, substs) = match ty.kind {
ty::Adt(def, substs) => (def, substs),
_ => return ty
};
if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() {
let data_idx;
let one = VariantIdx::new(1);
let zero = VariantIdx::new(0);
if def.variants[zero].fields.is_empty() {
data_idx = one;
} else if def.variants[one].fields.is_empty() {
data_idx = zero;
} else {
return ty;
}
if def.variants[data_idx].fields.len() == 1 {
return def.variants[data_idx].fields[0].ty(tcx, substs);
}
}
ty
}
impl ExprVisitor<'tcx> {
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
self.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
self.tcx.item_name(def_id) == sym::transmute
}
fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
// Check for same size using the skeletons.
if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
if sk_from.same_size(sk_to) {
return;
}
// Special-case transmutting from `typeof(function)` and
// `Option<typeof(function)>` to present a clearer error.
let from = unpack_option_like(self.tcx, from);
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.kind, sk_to) {
if size_to == Pointer.size(&self.tcx) {
struct_span_err!(self.tcx.sess, span, E0591,
"can't transmute zero-sized type")
.note(&format!("source type: {}", from))
.note(&format!("target type: {}", to))
.help("cast with `as` to a pointer instead")
.emit();
return;
}
}
}
// Try to display a sensible error with as much information as possible.
let skeleton_string = |ty: Ty<'tcx>, sk| {
match sk {
Ok(SizeSkeleton::Known(size)) => {
format!("{} bits", size.bits())
}
Ok(SizeSkeleton::Pointer { tail, .. }) => {
format!("pointer to `{}`", tail)
}
Err(LayoutError::Unknown(bad)) => {
if bad == ty {
"this type does not have a fixed size".to_owned()
} else {
format!("size can vary because of {}", bad)
}
}
Err(err) => err.to_string()
}
};
let mut err = struct_span_err!(self.tcx.sess, span, E0512,
"cannot transmute between types of different sizes, \
or dependently-sized types");
if from == to {
err.note(&format!("`{}` does not have a fixed size", from));
} else {
err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
.note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
}
err.emit()
}
}
impl Visitor<'tcx> for ItemVisitor<'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
let owner_def_id = self.tcx.hir().body_owner_def_id(body_id);
let body = self.tcx.hir().body(body_id);
let param_env = self.tcx.param_env(owner_def_id);
let tables = self.tcx.typeck_tables_of(owner_def_id);
ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body);
self.visit_body(body);
}
}
impl Visitor<'tcx> for ExprVisitor<'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
let res = if let hir::ExprKind::Path(ref qpath) = expr.kind {
self.tables.qpath_res(qpath, expr.hir_id)
} else {
Res::Err
};
if let Res::Def(DefKind::Fn, did) = res {
if self.def_id_is_transmute(did) {
let typ = self.tables.node_type(expr.hir_id);
let sig = typ.fn_sig(self.tcx);
let from = sig.inputs().skip_binder()[0];
let to = *sig.output().skip_binder();
self.check_transmute(expr.span, from, to);
}
}
intravisit::walk_expr(self, expr);
}
}

File diff suppressed because it is too large Load diff