Rollup merge of #37049 - srinivasreddy:librustc_lint, r=nrc
run rustfmt on librustc_lint folder
This commit is contained in:
commit
4e65489e77
|
@ -24,19 +24,21 @@ use rustc::hir::intravisit::FnKind;
|
|||
pub enum MethodLateContext {
|
||||
TraitDefaultImpl,
|
||||
TraitImpl,
|
||||
PlainImpl
|
||||
PlainImpl,
|
||||
}
|
||||
|
||||
pub fn method_context(cx: &LateContext, id: ast::NodeId, span: Span) -> MethodLateContext {
|
||||
let def_id = cx.tcx.map.local_def_id(id);
|
||||
match cx.tcx.impl_or_trait_items.borrow().get(&def_id) {
|
||||
None => span_bug!(span, "missing method descriptor?!"),
|
||||
Some(item) => match item.container() {
|
||||
ty::TraitContainer(..) => MethodLateContext::TraitDefaultImpl,
|
||||
ty::ImplContainer(cid) => {
|
||||
match cx.tcx.impl_trait_ref(cid) {
|
||||
Some(_) => MethodLateContext::TraitImpl,
|
||||
None => MethodLateContext::PlainImpl
|
||||
Some(item) => {
|
||||
match item.container() {
|
||||
ty::TraitContainer(..) => MethodLateContext::TraitDefaultImpl,
|
||||
ty::ImplContainer(cid) => {
|
||||
match cx.tcx.impl_trait_ref(cid) {
|
||||
Some(_) => MethodLateContext::TraitImpl,
|
||||
None => MethodLateContext::PlainImpl,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,19 +65,20 @@ impl NonCamelCaseTypes {
|
|||
|
||||
// start with a non-lowercase letter rather than non-uppercase
|
||||
// ones (some scripts don't have a concept of upper/lowercase)
|
||||
!name.is_empty() &&
|
||||
!name.chars().next().unwrap().is_lowercase() &&
|
||||
!name.contains('_')
|
||||
!name.is_empty() && !name.chars().next().unwrap().is_lowercase() && !name.contains('_')
|
||||
}
|
||||
|
||||
fn to_camel_case(s: &str) -> String {
|
||||
s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
|
||||
if i == 0 {
|
||||
c.to_uppercase().collect::<String>()
|
||||
} else {
|
||||
c.to_lowercase().collect()
|
||||
}
|
||||
)).collect::<Vec<_>>().concat()
|
||||
s.split('_')
|
||||
.flat_map(|word| {
|
||||
word.chars().enumerate().map(|(i, c)| if i == 0 {
|
||||
c.to_uppercase().collect::<String>()
|
||||
} else {
|
||||
c.to_lowercase().collect()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.concat()
|
||||
}
|
||||
|
||||
let s = name.as_str();
|
||||
|
@ -83,9 +86,14 @@ impl NonCamelCaseTypes {
|
|||
if !is_camel_case(name) {
|
||||
let c = to_camel_case(&s);
|
||||
let m = if c.is_empty() {
|
||||
format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s)
|
||||
format!("{} `{}` should have a camel case name such as `CamelCase`",
|
||||
sort,
|
||||
s)
|
||||
} else {
|
||||
format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
|
||||
format!("{} `{}` should have a camel case name such as `{}`",
|
||||
sort,
|
||||
s,
|
||||
c)
|
||||
};
|
||||
cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]);
|
||||
}
|
||||
|
@ -100,10 +108,14 @@ impl LintPass for NonCamelCaseTypes {
|
|||
|
||||
impl LateLintPass for NonCamelCaseTypes {
|
||||
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
|
||||
let extern_repr_count = it.attrs.iter().filter(|attr| {
|
||||
attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
|
||||
.any(|r| r == &attr::ReprExtern)
|
||||
}).count();
|
||||
let extern_repr_count = it.attrs
|
||||
.iter()
|
||||
.filter(|attr| {
|
||||
attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr)
|
||||
.iter()
|
||||
.any(|r| r == &attr::ReprExtern)
|
||||
})
|
||||
.count();
|
||||
let has_extern_repr = extern_repr_count > 0;
|
||||
|
||||
if has_extern_repr {
|
||||
|
@ -111,12 +123,10 @@ impl LateLintPass for NonCamelCaseTypes {
|
|||
}
|
||||
|
||||
match it.node {
|
||||
hir::ItemTy(..) | hir::ItemStruct(..) | hir::ItemUnion(..) => {
|
||||
self.check_case(cx, "type", it.name, it.span)
|
||||
}
|
||||
hir::ItemTrait(..) => {
|
||||
self.check_case(cx, "trait", it.name, it.span)
|
||||
}
|
||||
hir::ItemTy(..) |
|
||||
hir::ItemStruct(..) |
|
||||
hir::ItemUnion(..) => self.check_case(cx, "type", it.name, it.span),
|
||||
hir::ItemTrait(..) => self.check_case(cx, "trait", it.name, it.span),
|
||||
hir::ItemEnum(ref enum_definition, _) => {
|
||||
if has_extern_repr {
|
||||
return;
|
||||
|
@ -126,7 +136,7 @@ impl LateLintPass for NonCamelCaseTypes {
|
|||
self.check_case(cx, "variant", variant.node.name, variant.span);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,9 +175,7 @@ impl NonSnakeCase {
|
|||
continue;
|
||||
}
|
||||
for ch in s.chars() {
|
||||
if !buf.is_empty() && buf != "'"
|
||||
&& ch.is_uppercase()
|
||||
&& !last_upper {
|
||||
if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
|
||||
words.push(buf);
|
||||
buf = String::new();
|
||||
}
|
||||
|
@ -205,10 +213,11 @@ impl NonSnakeCase {
|
|||
let sc = NonSnakeCase::to_snake_case(name);
|
||||
let msg = if sc != name {
|
||||
format!("{} `{}` should have a snake case name such as `{}`",
|
||||
sort, name, sc)
|
||||
sort,
|
||||
name,
|
||||
sc)
|
||||
} else {
|
||||
format!("{} `{}` should have a snake case name",
|
||||
sort, name)
|
||||
format!("{} `{}` should have a snake case name", sort, name)
|
||||
};
|
||||
match span {
|
||||
Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg),
|
||||
|
@ -226,8 +235,10 @@ impl LintPass for NonSnakeCase {
|
|||
|
||||
impl LateLintPass for NonSnakeCase {
|
||||
fn check_crate(&mut self, cx: &LateContext, cr: &hir::Crate) {
|
||||
let attr_crate_name = cr.attrs.iter().find(|at| at.check_name("crate_name"))
|
||||
.and_then(|at| at.value_str().map(|s| (at, s)));
|
||||
let attr_crate_name = cr.attrs
|
||||
.iter()
|
||||
.find(|at| at.check_name("crate_name"))
|
||||
.and_then(|at| at.value_str().map(|s| (at, s)));
|
||||
if let Some(ref name) = cx.tcx.sess.opts.crate_name {
|
||||
self.check_snake_case(cx, "crate", name, None);
|
||||
} else if let Some((attr, ref name)) = attr_crate_name {
|
||||
|
@ -235,22 +246,28 @@ impl LateLintPass for NonSnakeCase {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_fn(&mut self, cx: &LateContext,
|
||||
fk: FnKind, _: &hir::FnDecl,
|
||||
_: &hir::Block, span: Span, id: ast::NodeId) {
|
||||
fn check_fn(&mut self,
|
||||
cx: &LateContext,
|
||||
fk: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
span: Span,
|
||||
id: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::Method(name, ..) => match method_context(cx, id, span) {
|
||||
MethodLateContext::PlainImpl => {
|
||||
self.check_snake_case(cx, "method", &name.as_str(), Some(span))
|
||||
},
|
||||
MethodLateContext::TraitDefaultImpl => {
|
||||
self.check_snake_case(cx, "trait method", &name.as_str(), Some(span))
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
FnKind::Method(name, ..) => {
|
||||
match method_context(cx, id, span) {
|
||||
MethodLateContext::PlainImpl => {
|
||||
self.check_snake_case(cx, "method", &name.as_str(), Some(span))
|
||||
}
|
||||
MethodLateContext::TraitDefaultImpl => {
|
||||
self.check_snake_case(cx, "trait method", &name.as_str(), Some(span))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
FnKind::ItemFn(name, ..) => {
|
||||
self.check_snake_case(cx, "function", &name.as_str(), Some(span))
|
||||
},
|
||||
}
|
||||
FnKind::Closure(_) => (),
|
||||
}
|
||||
}
|
||||
|
@ -263,13 +280,17 @@ impl LateLintPass for NonSnakeCase {
|
|||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
|
||||
if let hir::MethodTraitItem(_, None) = trait_item.node {
|
||||
self.check_snake_case(cx, "trait method", &trait_item.name.as_str(),
|
||||
self.check_snake_case(cx,
|
||||
"trait method",
|
||||
&trait_item.name.as_str(),
|
||||
Some(trait_item.span));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) {
|
||||
self.check_snake_case(cx, "lifetime", &t.lifetime.name.as_str(),
|
||||
self.check_snake_case(cx,
|
||||
"lifetime",
|
||||
&t.lifetime.name.as_str(),
|
||||
Some(t.lifetime.span));
|
||||
}
|
||||
|
||||
|
@ -282,8 +303,12 @@ impl LateLintPass for NonSnakeCase {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_struct_def(&mut self, cx: &LateContext, s: &hir::VariantData,
|
||||
_: ast::Name, _: &hir::Generics, _: ast::NodeId) {
|
||||
fn check_struct_def(&mut self,
|
||||
cx: &LateContext,
|
||||
s: &hir::VariantData,
|
||||
_: ast::Name,
|
||||
_: &hir::Generics,
|
||||
_: ast::NodeId) {
|
||||
for sf in s.fields() {
|
||||
self.check_snake_case(cx, "structure field", &sf.name.as_str(), Some(sf.span));
|
||||
}
|
||||
|
@ -306,13 +331,16 @@ impl NonUpperCaseGlobals {
|
|||
if s.chars().any(|c| c.is_lowercase()) {
|
||||
let uc = NonSnakeCase::to_snake_case(&s).to_uppercase();
|
||||
if uc != &s[..] {
|
||||
cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
|
||||
&format!("{} `{}` should have an upper case name such as `{}`",
|
||||
sort, s, uc));
|
||||
cx.span_lint(NON_UPPER_CASE_GLOBALS,
|
||||
span,
|
||||
&format!("{} `{}` should have an upper case name such as `{}`",
|
||||
sort,
|
||||
s,
|
||||
uc));
|
||||
} else {
|
||||
cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
|
||||
&format!("{} `{}` should have an upper case name",
|
||||
sort, s));
|
||||
cx.span_lint(NON_UPPER_CASE_GLOBALS,
|
||||
span,
|
||||
&format!("{} `{}` should have an upper case name", sort, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,8 +369,7 @@ impl LateLintPass for NonUpperCaseGlobals {
|
|||
fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) {
|
||||
match ti.node {
|
||||
hir::ConstTraitItem(..) => {
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
|
||||
ti.name, ti.span);
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ti.name, ti.span);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -351,8 +378,7 @@ impl LateLintPass for NonUpperCaseGlobals {
|
|||
fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) {
|
||||
match ii.node {
|
||||
hir::ImplItemKind::Const(..) => {
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
|
||||
ii.name, ii.span);
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ii.name, ii.span);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -363,8 +389,10 @@ impl LateLintPass for NonUpperCaseGlobals {
|
|||
if let PatKind::Path(None, ref path) = p.node {
|
||||
if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() {
|
||||
if let Def::Const(..) = cx.tcx.expect_def(p.id) {
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
|
||||
path.segments[0].name, path.span);
|
||||
NonUpperCaseGlobals::check_upper_case(cx,
|
||||
"constant in pattern",
|
||||
path.segments[0].name,
|
||||
path.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,15 +37,15 @@ use rustc::ty::{self, Ty, TyCtxt};
|
|||
use rustc::ty::adjustment;
|
||||
use rustc::traits::{self, Reveal};
|
||||
use rustc::hir::map as hir_map;
|
||||
use util::nodemap::{NodeSet};
|
||||
use util::nodemap::NodeSet;
|
||||
use lint::{Level, LateContext, LintContext, LintArray, Lint};
|
||||
use lint::{LintPass, LateLintPass};
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use syntax::{ast};
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax_pos::{Span};
|
||||
use syntax_pos::Span;
|
||||
|
||||
use rustc::hir::{self, PatKind};
|
||||
use rustc::hir::intravisit::FnKind;
|
||||
|
@ -75,7 +75,8 @@ impl LateLintPass for WhileTrue {
|
|||
if let hir::ExprWhile(ref cond, ..) = e.node {
|
||||
if let hir::ExprLit(ref lit) = cond.node {
|
||||
if let ast::LitKind::Bool(true) = lit.node {
|
||||
cx.span_lint(WHILE_TRUE, e.span,
|
||||
cx.span_lint(WHILE_TRUE,
|
||||
e.span,
|
||||
"denote infinite loops with loop { ... }");
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +94,7 @@ declare_lint! {
|
|||
pub struct BoxPointers;
|
||||
|
||||
impl BoxPointers {
|
||||
fn check_heap_type<'a, 'tcx>(&self, cx: &LateContext<'a, 'tcx>,
|
||||
span: Span, ty: Ty<'tcx>) {
|
||||
fn check_heap_type<'a, 'tcx>(&self, cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) {
|
||||
for leaf_ty in ty.walk() {
|
||||
if let ty::TyBox(_) = leaf_ty.sty {
|
||||
let m = format!("type uses owned (Box type) pointers: {}", ty);
|
||||
|
@ -117,10 +117,8 @@ impl LateLintPass for BoxPointers {
|
|||
hir::ItemTy(..) |
|
||||
hir::ItemEnum(..) |
|
||||
hir::ItemStruct(..) |
|
||||
hir::ItemUnion(..) =>
|
||||
self.check_heap_type(cx, it.span,
|
||||
cx.tcx.node_id_to_type(it.id)),
|
||||
_ => ()
|
||||
hir::ItemUnion(..) => self.check_heap_type(cx, it.span, cx.tcx.node_id_to_type(it.id)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// If it's a struct, we also have to check the fields' types
|
||||
|
@ -128,11 +126,12 @@ impl LateLintPass for BoxPointers {
|
|||
hir::ItemStruct(ref struct_def, _) |
|
||||
hir::ItemUnion(ref struct_def, _) => {
|
||||
for struct_field in struct_def.fields() {
|
||||
self.check_heap_type(cx, struct_field.span,
|
||||
self.check_heap_type(cx,
|
||||
struct_field.span,
|
||||
cx.tcx.node_id_to_type(struct_field.id));
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,9 +165,11 @@ impl LateLintPass for NonShorthandFieldPatterns {
|
|||
}
|
||||
if let PatKind::Binding(_, ident, None) = fieldpat.node.pat.node {
|
||||
if ident.node == fieldpat.node.name {
|
||||
cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
|
||||
cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS,
|
||||
fieldpat.span,
|
||||
&format!("the `{}:` in this pattern is redundant and can \
|
||||
be removed", ident.node))
|
||||
be removed",
|
||||
ident.node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,27 +204,35 @@ impl LateLintPass for UnsafeCode {
|
|||
|
||||
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
|
||||
match it.node {
|
||||
hir::ItemTrait(hir::Unsafety::Unsafe, ..) =>
|
||||
cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait"),
|
||||
hir::ItemTrait(hir::Unsafety::Unsafe, ..) => {
|
||||
cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait")
|
||||
}
|
||||
|
||||
hir::ItemImpl(hir::Unsafety::Unsafe, ..) =>
|
||||
cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait"),
|
||||
hir::ItemImpl(hir::Unsafety::Unsafe, ..) => {
|
||||
cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait")
|
||||
}
|
||||
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn(&mut self, cx: &LateContext, fk: FnKind, _: &hir::FnDecl,
|
||||
_: &hir::Block, span: Span, _: ast::NodeId) {
|
||||
fn check_fn(&mut self,
|
||||
cx: &LateContext,
|
||||
fk: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
span: Span,
|
||||
_: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) =>
|
||||
cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
|
||||
FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => {
|
||||
cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function")
|
||||
}
|
||||
|
||||
FnKind::Method(_, sig, ..) => {
|
||||
if sig.unsafety == hir::Unsafety::Unsafe {
|
||||
cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
@ -232,7 +241,8 @@ impl LateLintPass for UnsafeCode {
|
|||
fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
|
||||
if let hir::MethodTraitItem(ref sig, None) = trait_item.node {
|
||||
if sig.unsafety == hir::Unsafety::Unsafe {
|
||||
cx.span_lint(UNSAFE_CODE, trait_item.span,
|
||||
cx.span_lint(UNSAFE_CODE,
|
||||
trait_item.span,
|
||||
"declaration of an `unsafe` method")
|
||||
}
|
||||
}
|
||||
|
@ -263,9 +273,9 @@ pub struct MissingDoc {
|
|||
impl MissingDoc {
|
||||
pub fn new() -> MissingDoc {
|
||||
MissingDoc {
|
||||
struct_def_stack: vec!(),
|
||||
struct_def_stack: vec![],
|
||||
in_variant: false,
|
||||
doc_hidden_stack: vec!(false),
|
||||
doc_hidden_stack: vec![false],
|
||||
private_traits: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
@ -275,11 +285,11 @@ impl MissingDoc {
|
|||
}
|
||||
|
||||
fn check_missing_docs_attrs(&self,
|
||||
cx: &LateContext,
|
||||
id: Option<ast::NodeId>,
|
||||
attrs: &[ast::Attribute],
|
||||
sp: Span,
|
||||
desc: &'static str) {
|
||||
cx: &LateContext,
|
||||
id: Option<ast::NodeId>,
|
||||
attrs: &[ast::Attribute],
|
||||
sp: Span,
|
||||
desc: &'static str) {
|
||||
// If we're building a test harness, then warning about
|
||||
// documentation is probably not really relevant right now.
|
||||
if cx.sess().opts.test {
|
||||
|
@ -302,7 +312,8 @@ impl MissingDoc {
|
|||
|
||||
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc");
|
||||
if !has_doc {
|
||||
cx.span_lint(MISSING_DOCS, sp,
|
||||
cx.span_lint(MISSING_DOCS,
|
||||
sp,
|
||||
&format!("missing documentation for {}", desc));
|
||||
}
|
||||
}
|
||||
|
@ -316,8 +327,10 @@ impl LintPass for MissingDoc {
|
|||
|
||||
impl LateLintPass for MissingDoc {
|
||||
fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
|
||||
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
|
||||
attr.check_name("doc") && match attr.meta_item_list() {
|
||||
let doc_hidden = self.doc_hidden() ||
|
||||
attrs.iter().any(|attr| {
|
||||
attr.check_name("doc") &&
|
||||
match attr.meta_item_list() {
|
||||
None => false,
|
||||
Some(l) => attr::list_contains_name(&l[..], "hidden"),
|
||||
}
|
||||
|
@ -329,13 +342,21 @@ impl LateLintPass for MissingDoc {
|
|||
self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
|
||||
}
|
||||
|
||||
fn check_struct_def(&mut self, _: &LateContext, _: &hir::VariantData,
|
||||
_: ast::Name, _: &hir::Generics, item_id: ast::NodeId) {
|
||||
fn check_struct_def(&mut self,
|
||||
_: &LateContext,
|
||||
_: &hir::VariantData,
|
||||
_: ast::Name,
|
||||
_: &hir::Generics,
|
||||
item_id: ast::NodeId) {
|
||||
self.struct_def_stack.push(item_id);
|
||||
}
|
||||
|
||||
fn check_struct_def_post(&mut self, _: &LateContext, _: &hir::VariantData,
|
||||
_: ast::Name, _: &hir::Generics, item_id: ast::NodeId) {
|
||||
fn check_struct_def_post(&mut self,
|
||||
_: &LateContext,
|
||||
_: &hir::VariantData,
|
||||
_: ast::Name,
|
||||
_: &hir::Generics,
|
||||
item_id: ast::NodeId) {
|
||||
let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
|
||||
assert!(popped == item_id);
|
||||
}
|
||||
|
@ -358,10 +379,10 @@ impl LateLintPass for MissingDoc {
|
|||
for itm in items {
|
||||
self.private_traits.insert(itm.id);
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
"a trait"
|
||||
},
|
||||
}
|
||||
hir::ItemTy(..) => "a type alias",
|
||||
hir::ItemImpl(.., Some(ref trait_ref), _, ref impl_items) => {
|
||||
// If the trait is private, add the impl items to private_traits so they don't get
|
||||
|
@ -369,26 +390,30 @@ impl LateLintPass for MissingDoc {
|
|||
let real_trait = cx.tcx.expect_def(trait_ref.ref_id).def_id();
|
||||
if let Some(node_id) = cx.tcx.map.as_local_node_id(real_trait) {
|
||||
match cx.tcx.map.find(node_id) {
|
||||
Some(hir_map::NodeItem(item)) => if item.vis == hir::Visibility::Inherited {
|
||||
for itm in impl_items {
|
||||
self.private_traits.insert(itm.id);
|
||||
Some(hir_map::NodeItem(item)) => {
|
||||
if item.vis == hir::Visibility::Inherited {
|
||||
for itm in impl_items {
|
||||
self.private_traits.insert(itm.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return
|
||||
},
|
||||
return;
|
||||
}
|
||||
hir::ItemConst(..) => "a constant",
|
||||
hir::ItemStatic(..) => "a static",
|
||||
_ => return
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
|
||||
if self.private_traits.contains(&trait_item.id) { return }
|
||||
if self.private_traits.contains(&trait_item.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let desc = match trait_item.node {
|
||||
hir::ConstTraitItem(..) => "an associated constant",
|
||||
|
@ -396,9 +421,11 @@ impl LateLintPass for MissingDoc {
|
|||
hir::TypeTraitItem(..) => "an associated type",
|
||||
};
|
||||
|
||||
self.check_missing_docs_attrs(cx, Some(trait_item.id),
|
||||
self.check_missing_docs_attrs(cx,
|
||||
Some(trait_item.id),
|
||||
&trait_item.attrs,
|
||||
trait_item.span, desc);
|
||||
trait_item.span,
|
||||
desc);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
|
||||
|
@ -412,26 +439,34 @@ impl LateLintPass for MissingDoc {
|
|||
hir::ImplItemKind::Method(..) => "a method",
|
||||
hir::ImplItemKind::Type(_) => "an associated type",
|
||||
};
|
||||
self.check_missing_docs_attrs(cx, Some(impl_item.id),
|
||||
self.check_missing_docs_attrs(cx,
|
||||
Some(impl_item.id),
|
||||
&impl_item.attrs,
|
||||
impl_item.span, desc);
|
||||
impl_item.span,
|
||||
desc);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
|
||||
if !sf.is_positional() {
|
||||
if sf.vis == hir::Public || self.in_variant {
|
||||
let cur_struct_def = *self.struct_def_stack.last()
|
||||
let cur_struct_def = *self.struct_def_stack
|
||||
.last()
|
||||
.expect("empty struct_def_stack");
|
||||
self.check_missing_docs_attrs(cx, Some(cur_struct_def),
|
||||
&sf.attrs, sf.span,
|
||||
self.check_missing_docs_attrs(cx,
|
||||
Some(cur_struct_def),
|
||||
&sf.attrs,
|
||||
sf.span,
|
||||
"a struct field")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
|
||||
self.check_missing_docs_attrs(cx, Some(v.node.data.id()),
|
||||
&v.node.attrs, v.span, "a variant");
|
||||
self.check_missing_docs_attrs(cx,
|
||||
Some(v.node.data.id()),
|
||||
&v.node.attrs,
|
||||
v.span,
|
||||
"a variant");
|
||||
assert!(!self.in_variant);
|
||||
self.in_variant = true;
|
||||
}
|
||||
|
@ -486,7 +521,9 @@ impl LateLintPass for MissingCopyImplementations {
|
|||
}
|
||||
_ => return,
|
||||
};
|
||||
if def.has_dtor() { return; }
|
||||
if def.has_dtor() {
|
||||
return;
|
||||
}
|
||||
let parameter_environment = cx.tcx.empty_parameter_environment();
|
||||
// FIXME (@jroesch) should probably inver this so that the parameter env still impls this
|
||||
// method
|
||||
|
@ -514,9 +551,7 @@ pub struct MissingDebugImplementations {
|
|||
|
||||
impl MissingDebugImplementations {
|
||||
pub fn new() -> MissingDebugImplementations {
|
||||
MissingDebugImplementations {
|
||||
impling_types: None,
|
||||
}
|
||||
MissingDebugImplementations { impling_types: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -533,7 +568,9 @@ impl LateLintPass for MissingDebugImplementations {
|
|||
}
|
||||
|
||||
match item.node {
|
||||
hir::ItemStruct(..) | hir::ItemUnion(..) | hir::ItemEnum(..) => {},
|
||||
hir::ItemStruct(..) |
|
||||
hir::ItemUnion(..) |
|
||||
hir::ItemEnum(..) => {}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
|
@ -585,12 +622,13 @@ pub struct Deprecated {
|
|||
|
||||
impl Deprecated {
|
||||
pub fn new() -> Deprecated {
|
||||
Deprecated {
|
||||
current_item: ast::CRATE_NODE_ID,
|
||||
}
|
||||
Deprecated { current_item: ast::CRATE_NODE_ID }
|
||||
}
|
||||
|
||||
fn lint(&self, cx: &LateContext, _id: DefId, span: Span,
|
||||
fn lint(&self,
|
||||
cx: &LateContext,
|
||||
_id: DefId,
|
||||
span: Span,
|
||||
stability: &Option<&attr::Stability>,
|
||||
deprecation: &Option<stability::DeprecationEntry>) {
|
||||
// Deprecated attributes apply in-crate and cross-crate.
|
||||
|
@ -641,9 +679,10 @@ impl LintPass for Deprecated {
|
|||
impl LateLintPass for Deprecated {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
|
||||
self.push_item(item.id);
|
||||
stability::check_item(cx.tcx, item, false,
|
||||
&mut |id, sp, stab, depr|
|
||||
self.lint(cx, id, sp, &stab, &depr));
|
||||
stability::check_item(cx.tcx,
|
||||
item,
|
||||
false,
|
||||
&mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, cx: &LateContext, item: &hir::Item) {
|
||||
|
@ -651,27 +690,30 @@ impl LateLintPass for Deprecated {
|
|||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
|
||||
stability::check_expr(cx.tcx, e,
|
||||
&mut |id, sp, stab, depr|
|
||||
self.lint(cx, id, sp, &stab, &depr));
|
||||
stability::check_expr(cx.tcx,
|
||||
e,
|
||||
&mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
|
||||
}
|
||||
|
||||
fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
|
||||
stability::check_path(cx.tcx, path, id,
|
||||
&mut |id, sp, stab, depr|
|
||||
self.lint(cx, id, sp, &stab, &depr));
|
||||
stability::check_path(cx.tcx,
|
||||
path,
|
||||
id,
|
||||
&mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
|
||||
}
|
||||
|
||||
fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
|
||||
stability::check_path_list_item(cx.tcx, item,
|
||||
&mut |id, sp, stab, depr|
|
||||
self.lint(cx, id, sp, &stab, &depr));
|
||||
stability::check_path_list_item(cx.tcx,
|
||||
item,
|
||||
&mut |id, sp, stab, depr| {
|
||||
self.lint(cx, id, sp, &stab, &depr)
|
||||
});
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
|
||||
stability::check_pat(cx.tcx, pat,
|
||||
&mut |id, sp, stab, depr|
|
||||
self.lint(cx, id, sp, &stab, &depr));
|
||||
stability::check_pat(cx.tcx,
|
||||
pat,
|
||||
&mut |id, sp, stab, depr| self.lint(cx, id, sp, &stab, &depr));
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, _: &LateContext, item: &hir::ImplItem) {
|
||||
|
@ -716,15 +758,20 @@ impl LintPass for UnconditionalRecursion {
|
|||
}
|
||||
|
||||
impl LateLintPass for UnconditionalRecursion {
|
||||
fn check_fn(&mut self, cx: &LateContext, fn_kind: FnKind, _: &hir::FnDecl,
|
||||
blk: &hir::Block, sp: Span, id: ast::NodeId) {
|
||||
fn check_fn(&mut self,
|
||||
cx: &LateContext,
|
||||
fn_kind: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
blk: &hir::Block,
|
||||
sp: Span,
|
||||
id: ast::NodeId) {
|
||||
let method = match fn_kind {
|
||||
FnKind::ItemFn(..) => None,
|
||||
FnKind::Method(..) => {
|
||||
cx.tcx.impl_or_trait_item(cx.tcx.map.local_def_id(id)).as_opt_method()
|
||||
}
|
||||
// closures can't recur, so they don't matter.
|
||||
FnKind::Closure(_) => return
|
||||
FnKind::Closure(_) => return,
|
||||
};
|
||||
|
||||
// Walk through this function (say `f`) looking to see if
|
||||
|
@ -779,10 +826,8 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
// is this a recursive call?
|
||||
let self_recursive = if node_id != ast::DUMMY_NODE_ID {
|
||||
match method {
|
||||
Some(ref method) => {
|
||||
expr_refers_to_this_method(cx.tcx, method, node_id)
|
||||
}
|
||||
None => expr_refers_to_this_fn(cx.tcx, id, node_id)
|
||||
Some(ref method) => expr_refers_to_this_method(cx.tcx, method, node_id),
|
||||
None => expr_refers_to_this_fn(cx.tcx, id, node_id),
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -808,7 +853,8 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
// no break */ }`) shouldn't be linted unless it actually
|
||||
// recurs.
|
||||
if !reached_exit_without_self_call && !self_call_spans.is_empty() {
|
||||
let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, sp,
|
||||
let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION,
|
||||
sp,
|
||||
"function cannot return without recurring");
|
||||
|
||||
// FIXME #19668: these could be span_lint_note's instead of this manual guard.
|
||||
|
@ -829,23 +875,21 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
// Functions for identifying if the given Expr NodeId `id`
|
||||
// represents a call to the function `fn_id`/method `method`.
|
||||
|
||||
fn expr_refers_to_this_fn(tcx: TyCtxt,
|
||||
fn_id: ast::NodeId,
|
||||
id: ast::NodeId) -> bool {
|
||||
fn expr_refers_to_this_fn(tcx: TyCtxt, fn_id: ast::NodeId, id: ast::NodeId) -> bool {
|
||||
match tcx.map.get(id) {
|
||||
hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
|
||||
tcx.expect_def_or_none(callee.id).map_or(false, |def| {
|
||||
def.def_id() == tcx.map.local_def_id(fn_id)
|
||||
})
|
||||
tcx.expect_def_or_none(callee.id)
|
||||
.map_or(false, |def| def.def_id() == tcx.map.local_def_id(fn_id))
|
||||
}
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the expression `id` performs a call to `method`.
|
||||
fn expr_refers_to_this_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
method: &ty::Method,
|
||||
id: ast::NodeId) -> bool {
|
||||
id: ast::NodeId)
|
||||
-> bool {
|
||||
// Check for method calls and overloaded operators.
|
||||
let opt_m = tcx.tables.borrow().method_map.get(&ty::MethodCall::expr(id)).cloned();
|
||||
if let Some(m) = opt_m {
|
||||
|
@ -859,9 +903,11 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
if let Some(adjustment::AdjustDerefRef(adj)) = opt_adj {
|
||||
for i in 0..adj.autoderefs {
|
||||
let method_call = ty::MethodCall::autoderef(id, i as u32);
|
||||
if let Some(m) = tcx.tables.borrow().method_map
|
||||
.get(&method_call)
|
||||
.cloned() {
|
||||
if let Some(m) = tcx.tables
|
||||
.borrow()
|
||||
.method_map
|
||||
.get(&method_call)
|
||||
.cloned() {
|
||||
if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
|
||||
return true;
|
||||
}
|
||||
|
@ -877,13 +923,16 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
match tcx.expect_def_or_none(callee.id) {
|
||||
Some(Def::Method(def_id)) => {
|
||||
let item_substs = tcx.node_id_item_substs(callee.id);
|
||||
method_call_refers_to_method(
|
||||
tcx, method, def_id, &item_substs.substs, id)
|
||||
method_call_refers_to_method(tcx,
|
||||
method,
|
||||
def_id,
|
||||
&item_substs.substs,
|
||||
id)
|
||||
}
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -893,15 +942,14 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
method: &ty::Method,
|
||||
callee_id: DefId,
|
||||
callee_substs: &Substs<'tcx>,
|
||||
expr_id: ast::NodeId) -> bool {
|
||||
expr_id: ast::NodeId)
|
||||
-> bool {
|
||||
let callee_item = tcx.impl_or_trait_item(callee_id);
|
||||
|
||||
match callee_item.container() {
|
||||
// This is an inherent method, so the `def_id` refers
|
||||
// directly to the method definition.
|
||||
ty::ImplContainer(_) => {
|
||||
callee_id == method.def_id
|
||||
}
|
||||
ty::ImplContainer(_) => callee_id == method.def_id,
|
||||
|
||||
// A trait method, from any number of possible sources.
|
||||
// Attempt to select a concrete impl before checking.
|
||||
|
@ -939,13 +987,12 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
let container = ty::ImplContainer(vtable_impl.impl_def_id);
|
||||
// It matches if it comes from the same impl,
|
||||
// and has the same method name.
|
||||
container == method.container
|
||||
&& callee_item.name() == method.name
|
||||
container == method.container && callee_item.name() == method.name
|
||||
}
|
||||
|
||||
// There's no way to know if this call is
|
||||
// recursive, so we assume it's not.
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -992,7 +1039,8 @@ impl LateLintPass for PluginAsLibrary {
|
|||
};
|
||||
|
||||
if prfn.is_some() {
|
||||
cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
|
||||
cx.span_lint(PLUGIN_AS_LIBRARY,
|
||||
it.span,
|
||||
"compiler plugin used as an ordinary library");
|
||||
}
|
||||
}
|
||||
|
@ -1050,15 +1098,15 @@ impl LateLintPass for InvalidNoMangleItems {
|
|||
"generic functions must be mangled");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
hir::ItemStatic(..) => {
|
||||
if attr::contains_name(&it.attrs, "no_mangle") &&
|
||||
!cx.access_levels.is_reachable(it.id) {
|
||||
!cx.access_levels.is_reachable(it.id) {
|
||||
let msg = format!("static {} is marked #[no_mangle], but not exported",
|
||||
it.name);
|
||||
cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
|
||||
}
|
||||
},
|
||||
}
|
||||
hir::ItemConst(..) => {
|
||||
if attr::contains_name(&it.attrs, "no_mangle") {
|
||||
// Const items do not refer to a particular location in memory, and therefore
|
||||
|
@ -1068,7 +1116,7 @@ impl LateLintPass for InvalidNoMangleItems {
|
|||
cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1096,19 +1144,21 @@ impl LateLintPass for MutableTransmutes {
|
|||
consider instead using an UnsafeCell";
|
||||
match get_transmute_from_to(cx, expr) {
|
||||
Some((&ty::TyRef(_, from_mt), &ty::TyRef(_, to_mt))) => {
|
||||
if to_mt.mutbl == hir::Mutability::MutMutable
|
||||
&& from_mt.mutbl == hir::Mutability::MutImmutable {
|
||||
if to_mt.mutbl == hir::Mutability::MutMutable &&
|
||||
from_mt.mutbl == hir::Mutability::MutImmutable {
|
||||
cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
|
||||
fn get_transmute_from_to<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr)
|
||||
-> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
|
||||
fn get_transmute_from_to<'a, 'tcx>
|
||||
(cx: &LateContext<'a, 'tcx>,
|
||||
expr: &hir::Expr)
|
||||
-> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> {
|
||||
match expr.node {
|
||||
hir::ExprPath(..) => (),
|
||||
_ => return None
|
||||
_ => return None,
|
||||
}
|
||||
if let Def::Fn(did) = cx.tcx.expect_def(expr.id) {
|
||||
if !def_id_is_transmute(cx, did) {
|
||||
|
@ -1120,8 +1170,8 @@ impl LateLintPass for MutableTransmutes {
|
|||
let from = bare_fn.sig.0.inputs[0];
|
||||
let to = bare_fn.sig.0.output;
|
||||
return Some((&from.sty, &to.sty));
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -1130,7 +1180,7 @@ impl LateLintPass for MutableTransmutes {
|
|||
fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
|
||||
match cx.tcx.lookup_item_type(def_id).ty.sty {
|
||||
ty::TyFnDef(.., ref bfty) if bfty.abi == RustIntrinsic => (),
|
||||
_ => return false
|
||||
_ => return false,
|
||||
}
|
||||
cx.tcx.item_name(def_id).as_str() == "transmute"
|
||||
}
|
||||
|
|
|
@ -48,10 +48,10 @@ extern crate rustc_back;
|
|||
extern crate rustc_const_eval;
|
||||
extern crate syntax_pos;
|
||||
|
||||
pub use rustc::lint as lint;
|
||||
pub use rustc::middle as middle;
|
||||
pub use rustc::session as session;
|
||||
pub use rustc::util as util;
|
||||
pub use rustc::lint;
|
||||
pub use rustc::middle;
|
||||
pub use rustc::session;
|
||||
pub use rustc::util;
|
||||
|
||||
use session::Session;
|
||||
use lint::LintId;
|
||||
|
@ -139,13 +139,24 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
|||
MissingDebugImplementations,
|
||||
);
|
||||
|
||||
add_lint_group!(sess, "bad_style",
|
||||
NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS);
|
||||
add_lint_group!(sess,
|
||||
"bad_style",
|
||||
NON_CAMEL_CASE_TYPES,
|
||||
NON_SNAKE_CASE,
|
||||
NON_UPPER_CASE_GLOBALS);
|
||||
|
||||
add_lint_group!(sess, "unused",
|
||||
UNUSED_IMPORTS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE,
|
||||
UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE,
|
||||
UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES);
|
||||
add_lint_group!(sess,
|
||||
"unused",
|
||||
UNUSED_IMPORTS,
|
||||
UNUSED_VARIABLES,
|
||||
UNUSED_ASSIGNMENTS,
|
||||
DEAD_CODE,
|
||||
UNUSED_MUT,
|
||||
UNREACHABLE_CODE,
|
||||
UNUSED_MUST_USE,
|
||||
UNUSED_UNSAFE,
|
||||
PATH_STATEMENTS,
|
||||
UNUSED_ATTRIBUTES);
|
||||
|
||||
// Guidelines for creating a future incompatibility lint:
|
||||
//
|
||||
|
@ -155,7 +166,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
|||
// and include the full URL.
|
||||
// - Later, change lint to error
|
||||
// - Eventually, remove lint
|
||||
store.register_future_incompatible(sess, vec![
|
||||
store.register_future_incompatible(sess,
|
||||
vec![
|
||||
FutureIncompatibleInfo {
|
||||
id: LintId::of(PRIVATE_IN_PUBLIC),
|
||||
reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
|
||||
|
@ -204,11 +216,13 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
|||
|
||||
// Register renamed and removed lints
|
||||
store.register_renamed("unknown_features", "unused_features");
|
||||
store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
|
||||
store.register_removed("unsigned_negation",
|
||||
"replaced by negate_unsigned feature gate");
|
||||
store.register_removed("negate_unsigned", "cast a signed value instead");
|
||||
store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok");
|
||||
// This was renamed to raw_pointer_derive, which was then removed,
|
||||
// so it is also considered removed
|
||||
store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok");
|
||||
store.register_removed("raw_pointer_deriving",
|
||||
"using derive with raw pointers is ok");
|
||||
store.register_removed("drop_with_repr_extern", "drop flags have been removed");
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use rustc::traits::Reveal;
|
|||
use middle::const_val::ConstVal;
|
||||
use rustc_const_eval::eval_const_expr_partial;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use util::nodemap::{FnvHashSet};
|
||||
use util::nodemap::FnvHashSet;
|
||||
use lint::{LateContext, LintContext, LintArray};
|
||||
use lint::{LintPass, LateLintPass};
|
||||
|
||||
|
@ -91,15 +91,15 @@ pub struct TypeLimits {
|
|||
|
||||
impl TypeLimits {
|
||||
pub fn new() -> TypeLimits {
|
||||
TypeLimits {
|
||||
negated_expr_id: ast::DUMMY_NODE_ID,
|
||||
}
|
||||
TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID }
|
||||
}
|
||||
}
|
||||
|
||||
impl LintPass for TypeLimits {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
|
||||
lint_array!(UNUSED_COMPARISONS,
|
||||
OVERFLOWING_LITERALS,
|
||||
EXCEEDING_BITSHIFTS)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,13 +111,13 @@ impl LateLintPass for TypeLimits {
|
|||
match lit.node {
|
||||
ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)) => {
|
||||
forbid_unsigned_negation(cx, e.span);
|
||||
},
|
||||
}
|
||||
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
|
||||
if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
|
||||
forbid_unsigned_negation(cx, e.span);
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
let t = cx.tcx.node_id_to_type(expr.id);
|
||||
|
@ -129,10 +129,11 @@ impl LateLintPass for TypeLimits {
|
|||
if self.negated_expr_id != e.id {
|
||||
self.negated_expr_id = expr.id;
|
||||
}
|
||||
},
|
||||
}
|
||||
hir::ExprBinary(binop, ref l, ref r) => {
|
||||
if is_comparison(binop) && !check_limits(cx.tcx, binop, &l, &r) {
|
||||
cx.span_lint(UNUSED_COMPARISONS, e.span,
|
||||
cx.span_lint(UNUSED_COMPARISONS,
|
||||
e.span,
|
||||
"comparison is useless due to type limits");
|
||||
}
|
||||
|
||||
|
@ -140,30 +141,35 @@ impl LateLintPass for TypeLimits {
|
|||
let opt_ty_bits = match cx.tcx.node_id_to_type(l.id).sty {
|
||||
ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
|
||||
ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
|
||||
_ => None
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(bits) = opt_ty_bits {
|
||||
let exceeding = if let hir::ExprLit(ref lit) = r.node {
|
||||
if let ast::LitKind::Int(shift, _) = lit.node { shift >= bits }
|
||||
else { false }
|
||||
if let ast::LitKind::Int(shift, _) = lit.node {
|
||||
shift >= bits
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) {
|
||||
Ok(ConstVal::Integral(i)) => {
|
||||
i.is_negative() || i.to_u64()
|
||||
.map(|i| i >= bits)
|
||||
.unwrap_or(true)
|
||||
},
|
||||
_ => { false }
|
||||
i.is_negative() ||
|
||||
i.to_u64()
|
||||
.map(|i| i >= bits)
|
||||
.unwrap_or(true)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
if exceeding {
|
||||
cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
|
||||
cx.span_lint(EXCEEDING_BITSHIFTS,
|
||||
e.span,
|
||||
"bitshift exceeds the type's number of bits");
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
hir::ExprLit(ref lit) => {
|
||||
match cx.tcx.node_id_to_type(e.id).sty {
|
||||
ty::TyInt(t) => {
|
||||
|
@ -182,14 +188,15 @@ impl LateLintPass for TypeLimits {
|
|||
// avoiding use of -min to prevent overflow/panic
|
||||
if (negative && v > max as u64 + 1) ||
|
||||
(!negative && v > max as u64) {
|
||||
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
||||
cx.span_lint(OVERFLOWING_LITERALS,
|
||||
e.span,
|
||||
&format!("literal out of range for {:?}", t));
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => bug!()
|
||||
_ => bug!(),
|
||||
};
|
||||
},
|
||||
}
|
||||
ty::TyUint(t) => {
|
||||
let uint_type = if let ast::UintTy::Us = t {
|
||||
cx.sess().target.uint_type
|
||||
|
@ -201,13 +208,14 @@ impl LateLintPass for TypeLimits {
|
|||
// _v is u8, within range by definition
|
||||
ast::LitKind::Byte(_v) => return,
|
||||
ast::LitKind::Int(v, _) => v,
|
||||
_ => bug!()
|
||||
_ => bug!(),
|
||||
};
|
||||
if lit_val < min || lit_val > max {
|
||||
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
||||
cx.span_lint(OVERFLOWING_LITERALS,
|
||||
e.span,
|
||||
&format!("literal out of range for {:?}", t));
|
||||
}
|
||||
},
|
||||
}
|
||||
ty::TyFloat(t) => {
|
||||
let (min, max) = float_ty_range(t);
|
||||
let lit_val: f64 = match lit.node {
|
||||
|
@ -215,70 +223,71 @@ impl LateLintPass for TypeLimits {
|
|||
ast::LitKind::FloatUnsuffixed(ref v) => {
|
||||
match v.parse() {
|
||||
Ok(f) => f,
|
||||
Err(_) => return
|
||||
Err(_) => return,
|
||||
}
|
||||
}
|
||||
_ => bug!()
|
||||
_ => bug!(),
|
||||
};
|
||||
if lit_val < min || lit_val > max {
|
||||
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
||||
cx.span_lint(OVERFLOWING_LITERALS,
|
||||
e.span,
|
||||
&format!("literal out of range for {:?}", t));
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
|
||||
min: T, max: T) -> bool {
|
||||
fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
|
||||
match binop.node {
|
||||
hir::BiLt => v > min && v <= max,
|
||||
hir::BiLe => v >= min && v < max,
|
||||
hir::BiGt => v >= min && v < max,
|
||||
hir::BiGe => v > min && v <= max,
|
||||
hir::BiLt => v > min && v <= max,
|
||||
hir::BiLe => v >= min && v < max,
|
||||
hir::BiGt => v >= min && v < max,
|
||||
hir::BiGe => v > min && v <= max,
|
||||
hir::BiEq | hir::BiNe => v >= min && v <= max,
|
||||
_ => bug!()
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
|
||||
codemap::respan(binop.span, match binop.node {
|
||||
hir::BiLt => hir::BiGt,
|
||||
hir::BiLe => hir::BiGe,
|
||||
hir::BiGt => hir::BiLt,
|
||||
hir::BiGe => hir::BiLe,
|
||||
_ => return binop
|
||||
})
|
||||
codemap::respan(binop.span,
|
||||
match binop.node {
|
||||
hir::BiLt => hir::BiGt,
|
||||
hir::BiLe => hir::BiGe,
|
||||
hir::BiGt => hir::BiLt,
|
||||
hir::BiGe => hir::BiLe,
|
||||
_ => return binop,
|
||||
})
|
||||
}
|
||||
|
||||
// for isize & usize, be conservative with the warnings, so that the
|
||||
// warnings are consistent between 32- and 64-bit platforms
|
||||
fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
|
||||
match int_ty {
|
||||
ast::IntTy::Is => (i64::MIN, i64::MAX),
|
||||
ast::IntTy::I8 => (i8::MIN as i64, i8::MAX as i64),
|
||||
ast::IntTy::I16 => (i16::MIN as i64, i16::MAX as i64),
|
||||
ast::IntTy::I32 => (i32::MIN as i64, i32::MAX as i64),
|
||||
ast::IntTy::I64 => (i64::MIN, i64::MAX)
|
||||
ast::IntTy::Is => (i64::MIN, i64::MAX),
|
||||
ast::IntTy::I8 => (i8::MIN as i64, i8::MAX as i64),
|
||||
ast::IntTy::I16 => (i16::MIN as i64, i16::MAX as i64),
|
||||
ast::IntTy::I32 => (i32::MIN as i64, i32::MAX as i64),
|
||||
ast::IntTy::I64 => (i64::MIN, i64::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
|
||||
match uint_ty {
|
||||
ast::UintTy::Us => (u64::MIN, u64::MAX),
|
||||
ast::UintTy::U8 => (u8::MIN as u64, u8::MAX as u64),
|
||||
ast::UintTy::U16 => (u16::MIN as u64, u16::MAX as u64),
|
||||
ast::UintTy::U32 => (u32::MIN as u64, u32::MAX as u64),
|
||||
ast::UintTy::U64 => (u64::MIN, u64::MAX)
|
||||
ast::UintTy::Us => (u64::MIN, u64::MAX),
|
||||
ast::UintTy::U8 => (u8::MIN as u64, u8::MAX as u64),
|
||||
ast::UintTy::U16 => (u16::MIN as u64, u16::MAX as u64),
|
||||
ast::UintTy::U32 => (u32::MIN as u64, u32::MAX as u64),
|
||||
ast::UintTy::U64 => (u64::MIN, u64::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
|
||||
match float_ty {
|
||||
ast::FloatTy::F32 => (f32::MIN as f64, f32::MAX as f64),
|
||||
ast::FloatTy::F64 => (f64::MIN, f64::MAX)
|
||||
ast::FloatTy::F64 => (f64::MIN, f64::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,60 +314,60 @@ impl LateLintPass for TypeLimits {
|
|||
fn check_limits<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
binop: hir::BinOp,
|
||||
l: &hir::Expr,
|
||||
r: &hir::Expr) -> bool {
|
||||
r: &hir::Expr)
|
||||
-> bool {
|
||||
let (lit, expr, swap) = match (&l.node, &r.node) {
|
||||
(&hir::ExprLit(_), _) => (l, r, true),
|
||||
(_, &hir::ExprLit(_)) => (r, l, false),
|
||||
_ => return true
|
||||
_ => return true,
|
||||
};
|
||||
// Normalize the binop so that the literal is always on the RHS in
|
||||
// the comparison
|
||||
let norm_binop = if swap {
|
||||
rev_binop(binop)
|
||||
} else {
|
||||
binop
|
||||
};
|
||||
let norm_binop = if swap { rev_binop(binop) } else { binop };
|
||||
match tcx.node_id_to_type(expr.id).sty {
|
||||
ty::TyInt(int_ty) => {
|
||||
let (min, max) = int_ty_range(int_ty);
|
||||
let lit_val: i64 = match lit.node {
|
||||
hir::ExprLit(ref li) => match li.node {
|
||||
ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
|
||||
ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i64,
|
||||
_ => return true
|
||||
},
|
||||
_ => bug!()
|
||||
hir::ExprLit(ref li) => {
|
||||
match li.node {
|
||||
ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
|
||||
ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i64,
|
||||
_ => return true,
|
||||
}
|
||||
}
|
||||
_ => bug!(),
|
||||
};
|
||||
is_valid(norm_binop, lit_val, min, max)
|
||||
}
|
||||
ty::TyUint(uint_ty) => {
|
||||
let (min, max): (u64, u64) = uint_ty_range(uint_ty);
|
||||
let lit_val: u64 = match lit.node {
|
||||
hir::ExprLit(ref li) => match li.node {
|
||||
ast::LitKind::Int(v, _) => v,
|
||||
_ => return true
|
||||
},
|
||||
_ => bug!()
|
||||
hir::ExprLit(ref li) => {
|
||||
match li.node {
|
||||
ast::LitKind::Int(v, _) => v,
|
||||
_ => return true,
|
||||
}
|
||||
}
|
||||
_ => bug!(),
|
||||
};
|
||||
is_valid(norm_binop, lit_val, min, max)
|
||||
}
|
||||
_ => true
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_comparison(binop: hir::BinOp) -> bool {
|
||||
match binop.node {
|
||||
hir::BiEq | hir::BiLt | hir::BiLe |
|
||||
hir::BiNe | hir::BiGe | hir::BiGt => true,
|
||||
_ => false
|
||||
hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn forbid_unsigned_negation(cx: &LateContext, span: Span) {
|
||||
cx.sess()
|
||||
.struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
|
||||
.span_help(span, "use a cast or the `!` operator")
|
||||
.emit();
|
||||
.struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
|
||||
.span_help(span, "use a cast or the `!` operator")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +379,7 @@ declare_lint! {
|
|||
}
|
||||
|
||||
struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
enum FfiResult {
|
||||
|
@ -403,9 +412,13 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
|
||||
if def.variants[data_idx].fields.len() == 1 {
|
||||
match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
|
||||
ty::TyFnPtr(_) => { return true; }
|
||||
ty::TyRef(..) => { return true; }
|
||||
_ => { }
|
||||
ty::TyFnPtr(_) => {
|
||||
return true;
|
||||
}
|
||||
ty::TyRef(..) => {
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,10 +428,7 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
/// Check if the given type is "ffi-safe" (has a stable, well-defined
|
||||
/// representation which can be exported to C code).
|
||||
fn check_type_for_ffi(&self,
|
||||
cache: &mut FnvHashSet<Ty<'tcx>>,
|
||||
ty: Ty<'tcx>)
|
||||
-> FfiResult {
|
||||
fn check_type_for_ffi(&self, cache: &mut FnvHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult {
|
||||
use self::FfiResult::*;
|
||||
let cx = self.cx.tcx;
|
||||
|
||||
|
@ -431,112 +441,118 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
match ty.sty {
|
||||
ty::TyAdt(def, substs) => match def.adt_kind() {
|
||||
AdtKind::Struct => {
|
||||
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
|
||||
return FfiUnsafe(
|
||||
"found struct without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to \
|
||||
the type");
|
||||
}
|
||||
|
||||
// We can't completely trust repr(C) markings; make sure the
|
||||
// fields are actually safe.
|
||||
if def.struct_variant().fields.is_empty() {
|
||||
return FfiUnsafe(
|
||||
"found zero-size struct in foreign module, consider \
|
||||
adding a member to this struct");
|
||||
}
|
||||
|
||||
for field in &def.struct_variant().fields {
|
||||
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
|
||||
FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
|
||||
ty::TyAdt(def, substs) => {
|
||||
match def.adt_kind() {
|
||||
AdtKind::Struct => {
|
||||
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
|
||||
return FfiUnsafe("found struct without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to the type");
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
AdtKind::Union => {
|
||||
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
|
||||
return FfiUnsafe(
|
||||
"found union without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to \
|
||||
the type");
|
||||
}
|
||||
|
||||
for field in &def.struct_variant().fields {
|
||||
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
|
||||
FfiUnsafe(s) => { return FfiBadUnion(def.did, s); }
|
||||
// We can't completely trust repr(C) markings; make sure the
|
||||
// fields are actually safe.
|
||||
if def.struct_variant().fields.is_empty() {
|
||||
return FfiUnsafe("found zero-size struct in foreign module, consider \
|
||||
adding a member to this struct");
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
AdtKind::Enum => {
|
||||
if def.variants.is_empty() {
|
||||
// Empty enums are okay... although sort of useless.
|
||||
return FfiSafe
|
||||
}
|
||||
|
||||
// Check for a repr() attribute to specify the size of the
|
||||
// discriminant.
|
||||
let repr_hints = cx.lookup_repr_hints(def.did);
|
||||
match &repr_hints[..] {
|
||||
&[] => {
|
||||
// Special-case types like `Option<extern fn()>`.
|
||||
if !is_repr_nullable_ptr(cx, def, substs) {
|
||||
return FfiUnsafe(
|
||||
"found enum without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(...)] attribute to \
|
||||
the type")
|
||||
}
|
||||
}
|
||||
&[ref hint] => {
|
||||
if !hint.is_ffi_safe() {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe(
|
||||
"enum has unexpected #[repr(...)] attribute")
|
||||
}
|
||||
|
||||
// Enum with an explicitly sized discriminant; either
|
||||
// a C-style enum or a discriminated union.
|
||||
|
||||
// The layout of enum variants is implicitly repr(C).
|
||||
// FIXME: Is that correct?
|
||||
}
|
||||
_ => {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe(
|
||||
"enum has too many #[repr(...)] attributes");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the contained variants.
|
||||
for variant in &def.variants {
|
||||
for field in &variant.fields {
|
||||
let arg = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, arg);
|
||||
for field in &def.struct_variant().fields {
|
||||
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
|
||||
FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
|
||||
return r;
|
||||
}
|
||||
FfiUnsafe(s) => {
|
||||
return FfiBadStruct(def.did, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
AdtKind::Union => {
|
||||
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
|
||||
return FfiUnsafe("found union without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to the type");
|
||||
}
|
||||
|
||||
for field in &def.struct_variant().fields {
|
||||
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
|
||||
return r;
|
||||
}
|
||||
FfiUnsafe(s) => {
|
||||
return FfiBadUnion(def.did, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
AdtKind::Enum => {
|
||||
if def.variants.is_empty() {
|
||||
// Empty enums are okay... although sort of useless.
|
||||
return FfiSafe;
|
||||
}
|
||||
|
||||
// Check for a repr() attribute to specify the size of the
|
||||
// discriminant.
|
||||
let repr_hints = cx.lookup_repr_hints(def.did);
|
||||
match &repr_hints[..] {
|
||||
&[] => {
|
||||
// Special-case types like `Option<extern fn()>`.
|
||||
if !is_repr_nullable_ptr(cx, def, substs) {
|
||||
return FfiUnsafe("found enum without foreign-function-safe \
|
||||
representation annotation in foreign \
|
||||
module, consider adding a #[repr(...)] \
|
||||
attribute to the type");
|
||||
}
|
||||
}
|
||||
&[ref hint] => {
|
||||
if !hint.is_ffi_safe() {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
|
||||
}
|
||||
|
||||
// Enum with an explicitly sized discriminant; either
|
||||
// a C-style enum or a discriminated union.
|
||||
|
||||
// The layout of enum variants is implicitly repr(C).
|
||||
// FIXME: Is that correct?
|
||||
}
|
||||
_ => {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe("enum has too many #[repr(...)] attributes");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the contained variants.
|
||||
for variant in &def.variants {
|
||||
for field in &variant.fields {
|
||||
let arg = cx.normalize_associated_type(&field.ty(cx, substs));
|
||||
let r = self.check_type_for_ffi(cache, arg);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
|
||||
return r;
|
||||
}
|
||||
FfiUnsafe(s) => {
|
||||
return FfiBadEnum(def.did, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
ty::TyChar => {
|
||||
FfiUnsafe("found Rust type `char` in foreign module, while \
|
||||
|
@ -544,8 +560,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Primitive types with a stable representation.
|
||||
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
|
||||
ty::TyFloat(..) | ty::TyNever => FfiSafe,
|
||||
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
|
||||
|
||||
ty::TyBox(..) => {
|
||||
FfiUnsafe("found Rust type Box<_> in foreign module, \
|
||||
|
@ -572,24 +587,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
consider using a struct instead")
|
||||
}
|
||||
|
||||
ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
|
||||
self.check_type_for_ffi(cache, m.ty)
|
||||
}
|
||||
ty::TyRawPtr(ref m) |
|
||||
ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
|
||||
|
||||
ty::TyArray(ty, _) => {
|
||||
self.check_type_for_ffi(cache, ty)
|
||||
}
|
||||
ty::TyArray(ty, _) => self.check_type_for_ffi(cache, ty),
|
||||
|
||||
ty::TyFnPtr(bare_fn) => {
|
||||
match bare_fn.abi {
|
||||
Abi::Rust |
|
||||
Abi::RustIntrinsic |
|
||||
Abi::PlatformIntrinsic |
|
||||
Abi::RustCall => {
|
||||
return FfiUnsafe(
|
||||
"found function pointer with Rust calling \
|
||||
convention in foreign module; consider using an \
|
||||
`extern` function pointer")
|
||||
Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
|
||||
return FfiUnsafe("found function pointer with Rust calling convention in \
|
||||
foreign module; consider using an `extern` function \
|
||||
pointer")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -599,24 +607,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
let r = self.check_type_for_ffi(cache, sig.output);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
_ => { return r; }
|
||||
_ => {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
for arg in sig.inputs {
|
||||
let r = self.check_type_for_ffi(cache, arg);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
_ => { return r; }
|
||||
_ => {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
|
||||
ty::TyClosure(..) | ty::TyProjection(..) | ty::TyAnon(..) |
|
||||
ty::TyFnDef(..) => {
|
||||
bug!("Unexpected type in foreign function")
|
||||
}
|
||||
ty::TyParam(..) |
|
||||
ty::TyInfer(..) |
|
||||
ty::TyError |
|
||||
ty::TyClosure(..) |
|
||||
ty::TyProjection(..) |
|
||||
ty::TyAnon(..) |
|
||||
ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,23 +647,28 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
FfiResult::FfiBadStruct(_, s) => {
|
||||
// FIXME: This diagnostic is difficult to read, and doesn't
|
||||
// point at the relevant field.
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
&format!("found non-foreign-function-safe member in \
|
||||
struct marked #[repr(C)]: {}", s));
|
||||
self.cx.span_lint(IMPROPER_CTYPES,
|
||||
sp,
|
||||
&format!("found non-foreign-function-safe member in struct \
|
||||
marked #[repr(C)]: {}",
|
||||
s));
|
||||
}
|
||||
FfiResult::FfiBadUnion(_, s) => {
|
||||
// FIXME: This diagnostic is difficult to read, and doesn't
|
||||
// point at the relevant field.
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
&format!("found non-foreign-function-safe member in \
|
||||
union marked #[repr(C)]: {}", s));
|
||||
self.cx.span_lint(IMPROPER_CTYPES,
|
||||
sp,
|
||||
&format!("found non-foreign-function-safe member in union \
|
||||
marked #[repr(C)]: {}",
|
||||
s));
|
||||
}
|
||||
FfiResult::FfiBadEnum(_, s) => {
|
||||
// FIXME: This diagnostic is difficult to read, and doesn't
|
||||
// point at the relevant variant.
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
&format!("found non-foreign-function-safe member in \
|
||||
enum: {}", s));
|
||||
self.cx.span_lint(IMPROPER_CTYPES,
|
||||
sp,
|
||||
&format!("found non-foreign-function-safe member in enum: {}",
|
||||
s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -719,13 +738,13 @@ impl LintPass for VariantSizeDifferences {
|
|||
impl LateLintPass for VariantSizeDifferences {
|
||||
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
|
||||
if let hir::ItemEnum(ref enum_definition, ref gens) = it.node {
|
||||
if gens.ty_params.is_empty() { // sizes only make sense for non-generic types
|
||||
if gens.ty_params.is_empty() {
|
||||
// sizes only make sense for non-generic types
|
||||
let t = cx.tcx.node_id_to_type(it.id);
|
||||
let layout = cx.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| {
|
||||
let ty = cx.tcx.erase_regions(&t);
|
||||
ty.layout(&infcx).unwrap_or_else(|e| {
|
||||
bug!("failed to get layout for `{}`: {}", t, e)
|
||||
})
|
||||
ty.layout(&infcx)
|
||||
.unwrap_or_else(|e| bug!("failed to get layout for `{}`: {}", t, e))
|
||||
});
|
||||
|
||||
if let Layout::General { ref variants, ref size, discr, .. } = *layout {
|
||||
|
@ -738,23 +757,21 @@ impl LateLintPass for VariantSizeDifferences {
|
|||
.zip(variants)
|
||||
.map(|(variant, variant_layout)| {
|
||||
// Subtract the size of the enum discriminant
|
||||
let bytes = variant_layout.min_size.bytes()
|
||||
.saturating_sub(discr_size);
|
||||
let bytes = variant_layout.min_size
|
||||
.bytes()
|
||||
.saturating_sub(discr_size);
|
||||
|
||||
debug!("- variant `{}` is {} bytes large", variant.node.name, bytes);
|
||||
bytes
|
||||
})
|
||||
.enumerate()
|
||||
.fold((0, 0, 0),
|
||||
|(l, s, li), (idx, size)|
|
||||
if size > l {
|
||||
(size, l, idx)
|
||||
} else if size > s {
|
||||
(l, size, li)
|
||||
} else {
|
||||
(l, s, li)
|
||||
}
|
||||
);
|
||||
.fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l {
|
||||
(size, l, idx)
|
||||
} else if size > s {
|
||||
(l, size, li)
|
||||
} else {
|
||||
(l, s, li)
|
||||
});
|
||||
|
||||
// we only warn if the largest variant is at least thrice as large as
|
||||
// the second-largest.
|
||||
|
@ -762,7 +779,8 @@ impl LateLintPass for VariantSizeDifferences {
|
|||
cx.span_lint(VARIANT_SIZE_DIFFERENCES,
|
||||
enum_definition.variants[largest_index].span,
|
||||
&format!("enum variant is more than three times larger \
|
||||
({} bytes) than the next largest", largest));
|
||||
({} bytes) than the next largest",
|
||||
largest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,8 +49,12 @@ impl UnusedMut {
|
|||
if let hir::BindByValue(hir::MutMutable) = mode {
|
||||
if !name.as_str().starts_with("_") {
|
||||
match mutables.entry(name.0 as usize) {
|
||||
Vacant(entry) => { entry.insert(vec![id]); },
|
||||
Occupied(mut entry) => { entry.get_mut().push(id); },
|
||||
Vacant(entry) => {
|
||||
entry.insert(vec![id]);
|
||||
}
|
||||
Occupied(mut entry) => {
|
||||
entry.get_mut().push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +64,8 @@ impl UnusedMut {
|
|||
let used_mutables = cx.tcx.used_mut_nodes.borrow();
|
||||
for (_, v) in &mutables {
|
||||
if !v.iter().any(|e| used_mutables.contains(e)) {
|
||||
cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]),
|
||||
cx.span_lint(UNUSED_MUT,
|
||||
cx.tcx.map.span(v[0]),
|
||||
"variable does not need to be mutable");
|
||||
}
|
||||
}
|
||||
|
@ -90,9 +95,13 @@ impl LateLintPass for UnusedMut {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_fn(&mut self, cx: &LateContext,
|
||||
_: FnKind, decl: &hir::FnDecl,
|
||||
_: &hir::Block, _: Span, _: ast::NodeId) {
|
||||
fn check_fn(&mut self,
|
||||
cx: &LateContext,
|
||||
_: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
_: Span,
|
||||
_: ast::NodeId) {
|
||||
for a in &decl.inputs {
|
||||
self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
|
||||
}
|
||||
|
@ -124,7 +133,7 @@ impl LateLintPass for UnusedResults {
|
|||
fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
|
||||
let expr = match s.node {
|
||||
hir::StmtSemi(ref expr, _) => &**expr,
|
||||
_ => return
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let hir::ExprRet(..) = expr.node {
|
||||
|
@ -184,8 +193,8 @@ impl LateLintPass for UnusedUnsafe {
|
|||
if let hir::ExprBlock(ref blk) = e.node {
|
||||
// Don't warn about generated blocks, that'll just pollute the output.
|
||||
if blk.rules == hir::UnsafeBlock(hir::UserProvided) &&
|
||||
!cx.tcx.used_unsafe.borrow().contains(&blk.id) {
|
||||
cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
|
||||
!cx.tcx.used_unsafe.borrow().contains(&blk.id) {
|
||||
cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,8 +219,7 @@ impl LateLintPass for PathStatements {
|
|||
fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
|
||||
if let hir::StmtSemi(ref expr, _) = s.node {
|
||||
if let hir::ExprPath(..) = expr.node {
|
||||
cx.span_lint(PATH_STATEMENTS, s.span,
|
||||
"path statement with no effect");
|
||||
cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,8 +250,8 @@ impl LateLintPass for UnusedAttributes {
|
|||
AttributeType::Whitelisted if attr.check_name(name) => {
|
||||
debug!("{:?} is Whitelisted", name);
|
||||
break;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,24 +267,22 @@ impl LateLintPass for UnusedAttributes {
|
|||
debug!("Emitting warning for: {:?}", attr);
|
||||
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
|
||||
// Is it a builtin attribute that must be used at the crate level?
|
||||
let known_crate = KNOWN_ATTRIBUTES.iter().find(|&&(name, ty, _)| {
|
||||
attr.name() == name &&
|
||||
ty == AttributeType::CrateLevel
|
||||
}).is_some();
|
||||
let known_crate = KNOWN_ATTRIBUTES.iter()
|
||||
.find(|&&(name, ty, _)| attr.name() == name && ty == AttributeType::CrateLevel)
|
||||
.is_some();
|
||||
|
||||
// Has a plugin registered this attribute as one which must be used at
|
||||
// the crate level?
|
||||
let plugin_crate = plugin_attributes.iter()
|
||||
.find(|&&(ref x, t)| {
|
||||
&*attr.name() == x &&
|
||||
AttributeType::CrateLevel == t
|
||||
}).is_some();
|
||||
if known_crate || plugin_crate {
|
||||
.find(|&&(ref x, t)| &*attr.name() == x && AttributeType::CrateLevel == t)
|
||||
.is_some();
|
||||
if known_crate || plugin_crate {
|
||||
let msg = match attr.node.style {
|
||||
ast::AttrStyle::Outer => "crate-level attribute should be an inner \
|
||||
attribute: add an exclamation mark: #![foo]",
|
||||
ast::AttrStyle::Inner => "crate-level attribute should be in the \
|
||||
root module",
|
||||
ast::AttrStyle::Outer => {
|
||||
"crate-level attribute should be an inner attribute: add an exclamation \
|
||||
mark: #![foo]"
|
||||
}
|
||||
ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
|
||||
};
|
||||
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
|
||||
}
|
||||
|
@ -296,12 +302,16 @@ declare_lint! {
|
|||
pub struct UnusedParens;
|
||||
|
||||
impl UnusedParens {
|
||||
fn check_unused_parens_core(&self, cx: &EarlyContext, value: &ast::Expr, msg: &str,
|
||||
fn check_unused_parens_core(&self,
|
||||
cx: &EarlyContext,
|
||||
value: &ast::Expr,
|
||||
msg: &str,
|
||||
struct_lit_needs_parens: bool) {
|
||||
if let ast::ExprKind::Paren(ref inner) = value.node {
|
||||
let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&inner);
|
||||
if !necessary {
|
||||
cx.span_lint(UNUSED_PARENS, value.span,
|
||||
cx.span_lint(UNUSED_PARENS,
|
||||
value.span,
|
||||
&format!("unnecessary parentheses around {}", msg))
|
||||
}
|
||||
}
|
||||
|
@ -319,8 +329,7 @@ impl UnusedParens {
|
|||
ast::ExprKind::AssignOp(_, ref lhs, ref rhs) |
|
||||
ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
|
||||
// X { y: 1 } + X { y: 2 }
|
||||
contains_exterior_struct_lit(&lhs) ||
|
||||
contains_exterior_struct_lit(&rhs)
|
||||
contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
|
||||
}
|
||||
ast::ExprKind::Unary(_, ref x) |
|
||||
ast::ExprKind::Cast(ref x, _) |
|
||||
|
@ -337,7 +346,7 @@ impl UnusedParens {
|
|||
contains_exterior_struct_lit(&exprs[0])
|
||||
}
|
||||
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -363,18 +372,20 @@ impl EarlyLintPass for UnusedParens {
|
|||
Assign(_, ref value) => (value, "assigned value", false),
|
||||
AssignOp(.., ref value) => (value, "assigned value", false),
|
||||
InPlace(_, ref value) => (value, "emplacement value", false),
|
||||
_ => return
|
||||
_ => return,
|
||||
};
|
||||
self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
|
||||
let (value, msg) = match s.node {
|
||||
ast::StmtKind::Local(ref local) => match local.init {
|
||||
Some(ref value) => (value, "assigned value"),
|
||||
None => return
|
||||
},
|
||||
_ => return
|
||||
ast::StmtKind::Local(ref local) => {
|
||||
match local.init {
|
||||
Some(ref value) => (value, "assigned value"),
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
self.check_unused_parens_core(cx, &value, msg, false);
|
||||
}
|
||||
|
@ -427,23 +438,24 @@ impl LateLintPass for UnusedAllocation {
|
|||
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
|
||||
match e.node {
|
||||
hir::ExprBox(_) => {}
|
||||
_ => return
|
||||
_ => return,
|
||||
}
|
||||
|
||||
if let Some(adjustment) = cx.tcx.tables.borrow().adjustments.get(&e.id) {
|
||||
if let adjustment::AdjustDerefRef(adjustment::AutoDerefRef {
|
||||
ref autoref, ..
|
||||
}) = *adjustment {
|
||||
if let adjustment::AdjustDerefRef(adjustment::AutoDerefRef { ref autoref, .. }) =
|
||||
*adjustment {
|
||||
match autoref {
|
||||
&Some(adjustment::AutoPtr(_, hir::MutImmutable)) => {
|
||||
cx.span_lint(UNUSED_ALLOCATION, e.span,
|
||||
cx.span_lint(UNUSED_ALLOCATION,
|
||||
e.span,
|
||||
"unnecessary allocation, use & instead");
|
||||
}
|
||||
&Some(adjustment::AutoPtr(_, hir::MutMutable)) => {
|
||||
cx.span_lint(UNUSED_ALLOCATION, e.span,
|
||||
cx.span_lint(UNUSED_ALLOCATION,
|
||||
e.span,
|
||||
"unnecessary allocation, use &mut instead");
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue