Merge remote-tracking branch 'origin/master' into 1537-drop_copy

This commit is contained in:
Oliver Schneider
2017-03-24 10:11:46 +01:00
52 changed files with 717 additions and 568 deletions

View File

@@ -1,6 +1,24 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 0.0.121 — 2017-03-21
* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
## 0.0.120 — 2017-03-17
* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
## 0.0.119 — 2017-03-13
* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
## 0.0.118 — 2017-03-05
* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
## 0.0.117 — 2017-03-01
* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
## 0.0.116 — 2017-02-28
* Fix `cargo clippy` on 64 bit windows systems
## 0.0.115 — 2017-02-27 ## 0.0.115 — 2017-02-27
* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* * Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] * New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
@@ -65,7 +83,7 @@ All notable changes to this project will be documented in this file.
* New lint: [`get_unwrap`] * New lint: [`get_unwrap`]
## 0.0.98 — 2016-11-08 ## 0.0.98 — 2016-11-08
* Fixes a an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` * Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
## 0.0.97 — 2016-11-03 ## 0.0.97 — 2016-11-03
* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was * For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was

View File

@@ -35,10 +35,9 @@ T-middle issues can be more involved and require verifying types. The
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
Should you add a lint, try it on clippy itself using `util/dogfood.sh`. You may find that clippy Compiling clippy can take almost a minute or more depending on your machine.
contains some questionable code itself! Also before making a pull request, please run You can set the environment flag `CARGO_INCREMENTAL=1` to cut down that time to
`util/update_lints.py`, which will update `lib.rs` and `README.md` with the lint declarations. Our almost a third on average, depending on the influence your change has.
travis build actually checks for this.
Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected. Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected.
Of course there's little sense in writing the output yourself or copying it around. Of course there's little sense in writing the output yourself or copying it around.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.0.115" version = "0.0.121"
authors = [ authors = [
"Manish Goregaokar <manishsmail@gmail.com>", "Manish Goregaokar <manishsmail@gmail.com>",
"Andre Bogus <bogusandre@gmail.com>", "Andre Bogus <bogusandre@gmail.com>",
@@ -30,7 +30,7 @@ test = false
[dependencies] [dependencies]
# begin automatic update # begin automatic update
clippy_lints = { version = "0.0.115", path = "clippy_lints" } clippy_lints = { version = "0.0.121", path = "clippy_lints" }
# end automatic update # end automatic update
cargo_metadata = "0.1.1" cargo_metadata = "0.1.1"

View File

@@ -50,7 +50,7 @@ Then build by enabling the feature: `cargo build --features "clippy"`
Instead of adding the `cfg_attr` attributes you can also run clippy on demand: Instead of adding the `cfg_attr` attributes you can also run clippy on demand:
`cargo rustc --features clippy -- -Z no-trans -Z extra-plugins=clippy` `cargo rustc --features clippy -- -Z no-trans -Z extra-plugins=clippy`
(the `-Z no trans`, while not neccessary, will stop the compilation process after (the `-Z no trans`, while not necessary, will stop the compilation process after
typechecking (and lints) have completed, which can significantly reduce the runtime). typechecking (and lints) have completed, which can significantly reduce the runtime).
### As a cargo subcommand (`cargo clippy`) ### As a cargo subcommand (`cargo clippy`)
@@ -191,16 +191,16 @@ name
[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops) | allow | any compound assignment operation [assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops) | allow | any compound assignment operation
[bad_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#bad_bit_mask) | warn | expressions of the form `_ & mask == select` that will only ever return `true` or `false` [bad_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#bad_bit_mask) | warn | expressions of the form `_ & mask == select` that will only ever return `true` or `false`
[blacklisted_name](https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name) | warn | usage of a blacklisted/placeholder name [blacklisted_name](https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name) | warn | usage of a blacklisted/placeholder name
[block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces that can be eliminated in conditions, e.g `if { true } ...` [block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces that can be eliminated in conditions, e.g. `if { true } ...`
[block_in_if_condition_stmt](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_stmt) | warn | complex blocks in conditions, e.g. `if { let x = true; x } ...` [block_in_if_condition_stmt](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_stmt) | warn | complex blocks in conditions, e.g. `if { let x = true; x } ...`
[bool_comparison](https://github.com/Manishearth/rust-clippy/wiki#bool_comparison) | warn | comparing a variable to a boolean, e.g. `if x == true` [bool_comparison](https://github.com/Manishearth/rust-clippy/wiki#bool_comparison) | warn | comparing a variable to a boolean, e.g. `if x == true`
[box_vec](https://github.com/Manishearth/rust-clippy/wiki#box_vec) | warn | usage of `Box<Vec<T>>`, vector elements are already on the heap [box_vec](https://github.com/Manishearth/rust-clippy/wiki#box_vec) | warn | usage of `Box<Vec<T>>`, vector elements are already on the heap
[boxed_local](https://github.com/Manishearth/rust-clippy/wiki#boxed_local) | warn | using `Box<T>` where unnecessary [boxed_local](https://github.com/Manishearth/rust-clippy/wiki#boxed_local) | warn | using `Box<T>` where unnecessary
[builtin_type_shadow](https://github.com/Manishearth/rust-clippy/wiki#builtin_type_shadow) | warn | shadowing a builtin type [builtin_type_shadow](https://github.com/Manishearth/rust-clippy/wiki#builtin_type_shadow) | warn | shadowing a builtin type
[cast_possible_truncation](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_truncation) | allow | casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, or `x as i32` where `x: f32` [cast_possible_truncation](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_truncation) | allow | casts that may cause truncation of the value, e.g. `x as u8` where `x: u32`, or `x as i32` where `x: f32`
[cast_possible_wrap](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_wrap) | allow | casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` and `x > i32::MAX` [cast_possible_wrap](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_wrap) | allow | casts that may cause wrapping around the value, e.g. `x as i32` where `x: u32` and `x > i32::MAX`
[cast_precision_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_precision_loss) | allow | casts that cause loss of precision, e.g `x as f32` where `x: u64` [cast_precision_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_precision_loss) | allow | casts that cause loss of precision, e.g. `x as f32` where `x: u64`
[cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g `x as u32` where `x: i32` [cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g. `x as u32` where `x: i32`
[char_lit_as_u8](https://github.com/Manishearth/rust-clippy/wiki#char_lit_as_u8) | warn | casting a character literal to u8 [char_lit_as_u8](https://github.com/Manishearth/rust-clippy/wiki#char_lit_as_u8) | warn | casting a character literal to u8
[chars_next_cmp](https://github.com/Manishearth/rust-clippy/wiki#chars_next_cmp) | warn | using `.chars().next()` to check if a string starts with a char [chars_next_cmp](https://github.com/Manishearth/rust-clippy/wiki#chars_next_cmp) | warn | using `.chars().next()` to check if a string starts with a char
[clone_double_ref](https://github.com/Manishearth/rust-clippy/wiki#clone_double_ref) | warn | using `clone` on `&&T` [clone_double_ref](https://github.com/Manishearth/rust-clippy/wiki#clone_double_ref) | warn | using `clone` on `&&T`
@@ -336,7 +336,7 @@ name
[should_implement_trait](https://github.com/Manishearth/rust-clippy/wiki#should_implement_trait) | warn | defining a method that should be implementing a std trait [should_implement_trait](https://github.com/Manishearth/rust-clippy/wiki#should_implement_trait) | warn | defining a method that should be implementing a std trait
[similar_names](https://github.com/Manishearth/rust-clippy/wiki#similar_names) | allow | similarly named items and bindings [similar_names](https://github.com/Manishearth/rust-clippy/wiki#similar_names) | allow | similarly named items and bindings
[single_char_pattern](https://github.com/Manishearth/rust-clippy/wiki#single_char_pattern) | warn | using a single-character str where a char could be used, e.g. `_.split("x")` [single_char_pattern](https://github.com/Manishearth/rust-clippy/wiki#single_char_pattern) | warn | using a single-character str where a char could be used, e.g. `_.split("x")`
[single_match](https://github.com/Manishearth/rust-clippy/wiki#single_match) | warn | a match statement with a single nontrivial arm (i.e, where the other arm is `_ => {}`) instead of `if let` [single_match](https://github.com/Manishearth/rust-clippy/wiki#single_match) | warn | a match statement with a single nontrivial arm (i.e. where the other arm is `_ => {}`) instead of `if let`
[single_match_else](https://github.com/Manishearth/rust-clippy/wiki#single_match_else) | allow | a match statement with a two arms where the second arm's pattern is a wildcard instead of `if let` [single_match_else](https://github.com/Manishearth/rust-clippy/wiki#single_match_else) | allow | a match statement with a two arms where the second arm's pattern is a wildcard instead of `if let`
[string_add](https://github.com/Manishearth/rust-clippy/wiki#string_add) | allow | using `x + ..` where x is a `String` instead of `push_str()` [string_add](https://github.com/Manishearth/rust-clippy/wiki#string_add) | allow | using `x + ..` where x is a `String` instead of `push_str()`
[string_add_assign](https://github.com/Manishearth/rust-clippy/wiki#string_add_assign) | allow | using `x = x + ..` where x is a `String` instead of `push_str()` [string_add_assign](https://github.com/Manishearth/rust-clippy/wiki#string_add_assign) | allow | using `x = x + ..` where x is a `String` instead of `push_str()`
@@ -354,7 +354,7 @@ name
[type_complexity](https://github.com/Manishearth/rust-clippy/wiki#type_complexity) | warn | usage of very complex types that might be better factored into `type` definitions [type_complexity](https://github.com/Manishearth/rust-clippy/wiki#type_complexity) | warn | usage of very complex types that might be better factored into `type` definitions
[unicode_not_nfc](https://github.com/Manishearth/rust-clippy/wiki#unicode_not_nfc) | allow | using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information) [unicode_not_nfc](https://github.com/Manishearth/rust-clippy/wiki#unicode_not_nfc) | allow | using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information)
[unit_cmp](https://github.com/Manishearth/rust-clippy/wiki#unit_cmp) | warn | comparing unit values [unit_cmp](https://github.com/Manishearth/rust-clippy/wiki#unit_cmp) | warn | comparing unit values
[unnecessary_cast](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_cast) | warn | cast to the same type, e.g `x as i32` where `x: i32` [unnecessary_cast](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_cast) | warn | cast to the same type, e.g. `x as i32` where `x: i32`
[unnecessary_mut_passed](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_mut_passed) | warn | an argument passed as a mutable reference although the callee only demands an immutable reference [unnecessary_mut_passed](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_mut_passed) | warn | an argument passed as a mutable reference although the callee only demands an immutable reference
[unnecessary_operation](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation) | warn | outer expressions with no effect [unnecessary_operation](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation) | warn | outer expressions with no effect
[unneeded_field_pattern](https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern) | warn | struct fields bound to a wildcard instead of using `..` [unneeded_field_pattern](https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern) | warn | struct fields bound to a wildcard instead of using `..`

View File

@@ -4,16 +4,15 @@ environment:
matrix: matrix:
- TARGET: i686-pc-windows-gnu - TARGET: i686-pc-windows-gnu
MSYS2_BITS: 32 MSYS2_BITS: 32
RUN_CARGO_CLIPPY: true
- TARGET: i686-pc-windows-msvc - TARGET: i686-pc-windows-msvc
MSYS2_BITS: 32 MSYS2_BITS: 32
RUN_CARGO_CLIPPY: true
- TARGET: x86_64-pc-windows-gnu - TARGET: x86_64-pc-windows-gnu
MSYS2_BITS: 64 MSYS2_BITS: 64
- TARGET: x86_64-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc
MSYS2_BITS: 64 MSYS2_BITS: 64
install: install:
- set PATH=C:\Program Files\Git\mingw64\bin;%PATH%
- curl -sSf -o rustup-init.exe https://win.rustup.rs/ - curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly - rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin;C:\Users\appveyor\.rustup\toolchains\nightly-%TARGET%\bin - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin;C:\Users\appveyor\.rustup\toolchains\nightly-%TARGET%\bin
@@ -29,7 +28,7 @@ test_script:
- cargo test --features debugging - cargo test --features debugging
- copy target\debug\cargo-clippy.exe C:\Users\appveyor\.cargo\bin\ - copy target\debug\cargo-clippy.exe C:\Users\appveyor\.cargo\bin\
- cargo clippy -- -D clippy - cargo clippy -- -D clippy
- if defined RUN_CARGO_CLIPPY cd clippy_lints && cargo clippy -- -D clippy && cd .. - cd clippy_lints && cargo clippy -- -D clippy && cd ..
notifications: notifications:
- provider: Email - provider: Email

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
# begin automatic update # begin automatic update
version = "0.0.115" version = "0.0.121"
# end automatic update # end automatic update
authors = [ authors = [
"Manish Goregaokar <manishsmail@gmail.com>", "Manish Goregaokar <manishsmail@gmail.com>",

View File

@@ -1,9 +1,8 @@
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt; use rustc_const_math::{ConstUsize, ConstIsize, ConstInt};
use rustc::hir; use rustc::hir;
use syntax::ast::RangeLimits; use syntax::ast::RangeLimits;
use utils::{self, higher}; use utils::{self, higher};
@@ -61,11 +60,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Array with known size can be checked statically // Array with known size can be checked statically
let ty = cx.tables.expr_ty(array); let ty = cx.tables.expr_ty(array);
if let ty::TyArray(_, size) = ty.sty { if let ty::TyArray(_, size) = ty.sty {
let size = ConstInt::Infer(size as u128); let size = ConstInt::Usize(ConstUsize::new(size as u64, cx.sess().target.uint_type)
.expect("array size is invalid"));
let constcx = ConstContext::with_tables(cx.tcx, cx.tables); let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
// Index is a constant uint // Index is a constant uint
let const_index = constcx.eval(index, ExprTypeChecked); let const_index = constcx.eval(index);
if let Ok(ConstVal::Integral(const_index)) = const_index { if let Ok(ConstVal::Integral(const_index)) = const_index {
if size <= const_index { if size <= const_index {
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds"); utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
@@ -77,10 +77,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Index is a constant range // Index is a constant range
if let Some(range) = higher::range(index) { if let Some(range) = higher::range(index) {
let start = range.start let start = range.start
.map(|start| constcx.eval(start, ExprTypeChecked)) .map(|start| constcx.eval(start))
.map(|v| v.ok()); .map(|v| v.ok());
let end = range.end let end = range.end
.map(|end| constcx.eval(end, ExprTypeChecked)) .map(|end| constcx.eval(end))
.map(|v| v.ok()); .map(|v| v.ok());
if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) { if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
@@ -117,13 +117,31 @@ fn to_const_range(
let start = match *start { let start = match *start {
Some(Some(ConstVal::Integral(x))) => x, Some(Some(ConstVal::Integral(x))) => x,
Some(_) => return None, Some(_) => return None,
None => ConstInt::Infer(0), None => ConstInt::U8(0),
}; };
let end = match *end { let end = match *end {
Some(Some(ConstVal::Integral(x))) => { Some(Some(ConstVal::Integral(x))) => {
if limits == RangeLimits::Closed { if limits == RangeLimits::Closed {
(x + ConstInt::Infer(1)).expect("such a big array is not realistic") match x {
ConstInt::U8(_) => (x + ConstInt::U8(1)),
ConstInt::U16(_) => (x + ConstInt::U16(1)),
ConstInt::U32(_) => (x + ConstInt::U32(1)),
ConstInt::U64(_) => (x + ConstInt::U64(1)),
ConstInt::U128(_) => (x + ConstInt::U128(1)),
ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
ConstInt::I8(_) => (x + ConstInt::I8(1)),
ConstInt::I16(_) => (x + ConstInt::I16(1)),
ConstInt::I32(_) => (x + ConstInt::I32(1)),
ConstInt::I64(_) => (x + ConstInt::I64(1)),
ConstInt::I128(_) => (x + ConstInt::I128(1)),
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}
.expect("such a big array is not realistic")
} else { } else {
x x
} }

View File

@@ -86,8 +86,8 @@ impl LintPass for AttrPass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) { fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
if let MetaItemKind::List(ref items) = attr.value.node { if let Some(ref items) = attr.meta_item_list() {
if items.is_empty() || attr.name() != "deprecated" { if items.is_empty() || attr.name().map_or(true, |n| n != "deprecated") {
return; return;
} }
for item in items { for item in items {
@@ -110,31 +110,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
ItemExternCrate(_) | ItemExternCrate(_) |
ItemUse(_, _) => { ItemUse(_, _) => {
for attr in &item.attrs { for attr in &item.attrs {
if let MetaItemKind::List(ref lint_list) = attr.value.node { if let Some(ref lint_list) = attr.meta_item_list() {
match &*attr.name().as_str() { if let Some(name) = attr.name() {
"allow" | "warn" | "deny" | "forbid" => { match &*name.as_str() {
// whitelist `unused_imports` and `deprecated` "allow" | "warn" | "deny" | "forbid" => {
for lint in lint_list { // whitelist `unused_imports` and `deprecated`
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") { for lint in lint_list {
if let ItemUse(_, _) = item.node { if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
return; if let ItemUse(_, _) = item.node {
return;
}
} }
} }
} if let Some(mut sugg) = snippet_opt(cx, attr.span) {
if let Some(mut sugg) = snippet_opt(cx, attr.span) { if sugg.len() > 1 {
if sugg.len() > 1 { span_lint_and_then(cx,
span_lint_and_then(cx, USELESS_ATTRIBUTE,
USELESS_ATTRIBUTE, attr.span,
attr.span, "useless lint attribute",
"useless lint attribute", |db| {
|db| { sugg.insert(1, '!');
sugg.insert(1, '!'); db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg); });
}); }
} }
} },
}, _ => {},
_ => {}, }
} }
} }
} }
@@ -218,8 +220,8 @@ fn check_attrs(cx: &LateContext, span: Span, name: &Name, attrs: &[Attribute]) {
} }
for attr in attrs { for attr in attrs {
if let MetaItemKind::List(ref values) = attr.value.node { if let Some(ref values) = attr.meta_item_list() {
if values.len() != 1 || attr.name() != "inline" { if values.len() != 1 || attr.name().map_or(true, |n| n != "inline") {
continue; continue;
} }
if is_word(&values[0], "always") { if is_word(&values[0], "always") {

View File

@@ -237,6 +237,7 @@ fn check_ineffective_gt(cx: &LateContext, span: Span, m: u128, c: u128, op: &str
} }
fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> { fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
use rustc::ty::subst::Substs;
match lit.node { match lit.node {
ExprLit(ref lit_ptr) => { ExprLit(ref lit_ptr) => {
if let LitKind::Int(value, _) = lit_ptr.node { if let LitKind::Int(value, _) = lit_ptr.node {
@@ -248,7 +249,7 @@ fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
ExprPath(ref qpath) => { ExprPath(ref qpath) => {
let def = cx.tables.qpath_def(qpath, lit.id); let def = cx.tables.qpath_def(qpath, lit.id);
if let Def::Const(def_id) = def { if let Def::Const(def_id) = def {
lookup_const_by_id(cx.tcx, def_id, None).and_then(|(l, _tab, _ty)| fetch_int_literal(cx, l)) lookup_const_by_id(cx.tcx, def_id, Substs::empty()).and_then(|(l, _ty)| fetch_int_literal(cx, l))
} else { } else {
None None
} }

View File

@@ -18,7 +18,7 @@ use utils::*;
declare_lint! { declare_lint! {
pub BLOCK_IN_IF_CONDITION_EXPR, pub BLOCK_IN_IF_CONDITION_EXPR,
Warn, Warn,
"braces that can be eliminated in conditions, e.g `if { true } ...`" "braces that can be eliminated in conditions, e.g. `if { true } ...`"
} }
/// **What it does:** Checks for `if` conditions that use blocks containing /// **What it does:** Checks for `if` conditions that use blocks containing

View File

@@ -3,14 +3,15 @@
use rustc::lint::LateContext; use rustc::lint::LateContext;
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc_const_eval::lookup_const_by_id; use rustc_const_eval::lookup_const_by_id;
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize}; use rustc_const_math::ConstInt;
use rustc::hir::*; use rustc::hir::*;
use rustc::ty::{self, TyCtxt};
use std::cmp::Ordering::{self, Equal}; use std::cmp::Ordering::{self, Equal};
use std::cmp::PartialOrd; use std::cmp::PartialOrd;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy, IntTy, NodeId}; use syntax::ast::{FloatTy, LitKind, StrStyle, NodeId};
use syntax::ptr::P; use syntax::ptr::P;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -52,21 +53,6 @@ pub enum Constant {
Tuple(Vec<Constant>), Tuple(Vec<Constant>),
} }
impl Constant {
/// Convert to `u64` if possible.
///
/// # panics
///
/// If the constant could not be converted to `u64` losslessly.
fn as_u64(&self) -> u64 {
if let Constant::Int(val) = *self {
val.to_u64().expect("negative constant can't be casted to `u64`")
} else {
panic!("Could not convert a `{:?}` to `u64`", self);
}
}
}
impl PartialEq for Constant { impl PartialEq for Constant {
fn eq(&self, other: &Constant) -> bool { fn eq(&self, other: &Constant) -> bool {
match (self, other) { match (self, other) {
@@ -174,28 +160,33 @@ impl PartialOrd for Constant {
/// parse a `LitKind` to a `Constant` /// parse a `LitKind` to a `Constant`
#[allow(cast_possible_wrap)] #[allow(cast_possible_wrap)]
pub fn lit_to_constant(lit: &LitKind) -> Constant { pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut ty: ty::Ty<'tcx>) -> Constant {
use syntax::ast::*;
use syntax::ast::LitIntType::*;
use rustc::ty::util::IntTypeExt;
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(tcx)
}
}
match *lit { match *lit {
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style), LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)), LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()), LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
LitKind::Char(c) => Constant::Char(c), LitKind::Char(c) => Constant::Char(c),
LitKind::Int(value, LitIntType::Unsuffixed) => Constant::Int(ConstInt::Infer(value)), LitKind::Int(n, hint) => {
LitKind::Int(value, LitIntType::Unsigned(UintTy::U8)) => Constant::Int(ConstInt::U8(value as u8)), match (&ty.sty, hint) {
LitKind::Int(value, LitIntType::Unsigned(UintTy::U16)) => Constant::Int(ConstInt::U16(value as u16)), (&ty::TyInt(ity), _) |
LitKind::Int(value, LitIntType::Unsigned(UintTy::U32)) => Constant::Int(ConstInt::U32(value as u32)), (_, Signed(ity)) => {
LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)), Constant::Int(ConstInt::new_signed_truncating(n as i128, ity, tcx.sess.target.int_type))
LitKind::Int(value, LitIntType::Unsigned(UintTy::U128)) => Constant::Int(ConstInt::U128(value as u128)), },
LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => { (&ty::TyUint(uty), _) |
Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32))) (_, Unsigned(uty)) => {
}, Constant::Int(ConstInt::new_unsigned_truncating(n as u128, uty, tcx.sess.target.uint_type))
LitKind::Int(value, LitIntType::Signed(IntTy::I8)) => Constant::Int(ConstInt::I8(value as i8)), },
LitKind::Int(value, LitIntType::Signed(IntTy::I16)) => Constant::Int(ConstInt::I16(value as i16)), _ => bug!(),
LitKind::Int(value, LitIntType::Signed(IntTy::I32)) => Constant::Int(ConstInt::I32(value as i32)), }
LitKind::Int(value, LitIntType::Signed(IntTy::I64)) => Constant::Int(ConstInt::I64(value as i64)),
LitKind::Int(value, LitIntType::Signed(IntTy::I128)) => Constant::Int(ConstInt::I128(value as i128)),
LitKind::Int(value, LitIntType::Signed(IntTy::Is)) => {
Constant::Int(ConstInt::Isize(ConstIsize::Is32(value as i32)))
}, },
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()), LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any), LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
@@ -231,22 +222,20 @@ fn neg_float_str(s: &str) -> String {
pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> { pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext { let mut cx = ConstEvalLateContext {
lcx: Some(lcx), tcx: lcx.tcx,
tables: lcx.tables,
needed_resolution: false, needed_resolution: false,
}; };
cx.expr(e).map(|cst| (cst, cx.needed_resolution)) cx.expr(e).map(|cst| (cst, cx.needed_resolution))
} }
pub fn constant_simple(e: &Expr) -> Option<Constant> { pub fn constant_simple(lcx: &LateContext, e: &Expr) -> Option<Constant> {
let mut cx = ConstEvalLateContext { constant(lcx, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
lcx: None,
needed_resolution: false,
};
cx.expr(e)
} }
struct ConstEvalLateContext<'c, 'cc: 'c> { struct ConstEvalLateContext<'a, 'tcx: 'a> {
lcx: Option<&'c LateContext<'c, 'cc>>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
needed_resolution: bool, needed_resolution: bool,
} }
@@ -257,17 +246,15 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
ExprPath(ref qpath) => self.fetch_path(qpath, e.id), ExprPath(ref qpath) => self.fetch_path(qpath, e.id),
ExprBlock(ref block) => self.block(block), ExprBlock(ref block) => self.block(block),
ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise), ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise),
ExprLit(ref lit) => Some(lit_to_constant(&lit.node)), ExprLit(ref lit) => Some(lit_to_constant(&lit.node, self.tcx, self.tables.expr_ty(e))),
ExprArray(ref vec) => self.multi(vec).map(Constant::Vec), ExprArray(ref vec) => self.multi(vec).map(Constant::Vec),
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple), ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprRepeat(ref value, number_id) => { ExprRepeat(ref value, _) => {
if let Some(lcx) = self.lcx { let n = match self.tables.expr_ty(e).sty {
self.binop_apply(value, ty::TyArray(_, n) => n,
&lcx.tcx.hir.body(number_id).value, _ => span_bug!(e.span, "typeck error"),
|v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize))) };
} else { self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
None
}
}, },
ExprUnary(op, ref operand) => { ExprUnary(op, ref operand) => {
self.expr(operand).and_then(|o| match op { self.expr(operand).and_then(|o| match op {
@@ -292,24 +279,27 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
/// lookup a possibly constant expression from a ExprPath /// lookup a possibly constant expression from a ExprPath
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> { fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
if let Some(lcx) = self.lcx { let def = self.tables.qpath_def(qpath, id);
let def = lcx.tables.qpath_def(qpath, id); match def {
match def { Def::Const(def_id) |
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
Def::AssociatedConst(def_id) => { let substs = self.tables
let substs = Some(lcx.tables .node_id_item_substs(id)
.node_id_item_substs(id) .unwrap_or_else(|| self.tcx.intern_substs(&[]));
.unwrap_or_else(|| lcx.tcx.intern_substs(&[]))); if let Some((const_expr, tables)) = lookup_const_by_id(self.tcx, def_id, substs) {
if let Some((const_expr, _tab, _ty)) = lookup_const_by_id(lcx.tcx, def_id, substs) { let mut cx = ConstEvalLateContext {
let ret = self.expr(const_expr); tcx: self.tcx,
if ret.is_some() { tables: tables,
self.needed_resolution = true; needed_resolution: false,
} };
return ret; let ret = cx.expr(const_expr);
if ret.is_some() {
self.needed_resolution = true;
} }
}, return ret;
_ => {}, }
} },
_ => {},
} }
None None
} }
@@ -369,15 +359,4 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
_ => None, _ => None,
} }
} }
fn binop_apply<F>(&mut self, left: &Expr, right: &Expr, op: F) -> Option<Constant>
where F: Fn(Constant, Constant) -> Option<Constant>
{
if let (Some(lc), Some(rc)) = (self.expr(left), self.expr(right)) {
op(lc, rc)
} else {
None
}
}
} }

View File

@@ -41,12 +41,13 @@ impl LintPass for CyclomaticComplexity {
} }
impl CyclomaticComplexity { impl CyclomaticComplexity {
fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr, span: Span) { fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, body: &'tcx Body, span: Span) {
if in_macro(cx, span) { if in_macro(cx, span) {
return; return;
} }
let cfg = CFG::new(cx.tcx, expr); let cfg = CFG::new(cx.tcx, body);
let expr = &body.value;
let n = cfg.graph.len_nodes() as u64; let n = cfg.graph.len_nodes() as u64;
let e = cfg.graph.len_edges() as u64; let e = cfg.graph.len_edges() as u64;
if e + 2 < n { if e + 2 < n {
@@ -101,7 +102,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
) { ) {
let def_id = cx.tcx.hir.local_def_id(node_id); let def_id = cx.tcx.hir.local_def_id(node_id);
if !cx.tcx.has_attr(def_id, "test") { if !cx.tcx.has_attr(def_id, "test") {
self.check(cx, &body.value, span); self.check(cx, body, span);
} }
} }
@@ -136,7 +137,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CCHelper<'a, 'tcx> {
let ty = self.cx.tables.node_id_to_type(callee.id); let ty = self.cx.tables.node_id_to_type(callee.id);
match ty.sty { match ty.sty {
ty::TyFnDef(_, _, ty) | ty::TyFnDef(_, _, ty) |
ty::TyFnPtr(ty) if ty.sig.skip_binder().output().sty == ty::TyNever => { ty::TyFnPtr(ty) if ty.skip_binder().output().sty == ty::TyNever => {
self.divergence += 1; self.divergence += 1;
}, },
_ => (), _ => (),

View File

@@ -89,11 +89,9 @@ pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [a
for attr in attrs { for attr in attrs {
if attr.is_sugared_doc { if attr.is_sugared_doc {
if let ast::MetaItemKind::NameValue(ref doc) = attr.value.node { if let Some(ref doc) = attr.value_str() {
if let ast::LitKind::Str(ref doc, _) = doc.node { let doc = (*doc.as_str()).to_owned();
let doc = (*doc.as_str()).to_owned(); docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
}
} }
} }
} }

View File

@@ -45,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
if let Some(body_id) = variant.disr_expr { if let Some(body_id) = variant.disr_expr {
use rustc_const_eval::*; use rustc_const_eval::*;
let constcx = ConstContext::new(cx.tcx, body_id); let constcx = ConstContext::new(cx.tcx, body_id);
let bad = match constcx.eval(&cx.tcx.hir.body(body_id).value, EvalHint::ExprTypeChecked) { let bad = match constcx.eval(&cx.tcx.hir.body(body_id).value) {
Ok(ConstVal::Integral(Usize(Us64(i)))) => i as u32 as u64 != i, Ok(ConstVal::Integral(Usize(Us64(i)))) => i as u32 as u64 != i,
Ok(ConstVal::Integral(Isize(Is64(i)))) => i as i32 as i64 != i, Ok(ConstVal::Integral(Isize(Is64(i)))) => i as i32 as i64 != i,
_ => false, _ => false,

View File

@@ -66,7 +66,8 @@ fn check_closure(cx: &LateContext, expr: &Expr) {
// Is it an unsafe function? They don't implement the closure traits // Is it an unsafe function? They don't implement the closure traits
ty::TyFnDef(_, _, fn_ty) | ty::TyFnDef(_, _, fn_ty) |
ty::TyFnPtr(fn_ty) => { ty::TyFnPtr(fn_ty) => {
if fn_ty.unsafety == Unsafety::Unsafe || fn_ty.sig.skip_binder().output().sty == ty::TyNever { if fn_ty.skip_binder().unsafety == Unsafety::Unsafe ||
fn_ty.skip_binder().output().sty == ty::TyNever {
return; return;
} }
}, },

View File

@@ -129,7 +129,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
match self.cx.tables.expr_ty(func).sty { match self.cx.tables.expr_ty(func).sty {
ty::TyFnDef(_, _, fn_ty) | ty::TyFnDef(_, _, fn_ty) |
ty::TyFnPtr(fn_ty) => { ty::TyFnPtr(fn_ty) => {
if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&fn_ty.sig).output().sty { if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&fn_ty).output().sty {
self.report_diverging_sub_expr(e); self.report_diverging_sub_expr(e);
} }
}, },

View File

@@ -3,7 +3,7 @@ use rustc::lint::*;
use rustc::hir::*; use rustc::hir::*;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{span_lint, snippet, in_macro}; use utils::{span_lint, snippet, in_macro};
use rustc_const_math::ConstInt; use syntax::attr::IntType::{SignedInt, UnsignedInt};
/// **What it does:** Checks for identity operations, e.g. `x + 0`. /// **What it does:** Checks for identity operations, e.g. `x + 0`.
/// ///
@@ -60,11 +60,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) { fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
if let Some(v @ Constant::Int(_)) = constant_simple(e) { if let Some(Constant::Int(v)) = constant_simple(cx, e) {
if match m { if match m {
0 => v == Constant::Int(ConstInt::Infer(0)), 0 => v.to_u128_unchecked() == 0,
-1 => v == Constant::Int(ConstInt::InferSigned(-1)), -1 => match v.int_type() {
1 => v == Constant::Int(ConstInt::Infer(1)), SignedInt(_) => #[allow(cast_possible_wrap)] (v.to_u128_unchecked() as i128 == -1),
UnsignedInt(_) => false
},
1 => v.to_u128_unchecked() == 1,
_ => unreachable!(), _ => unreachable!(),
} { } {
span_lint(cx, span_lint(cx,

View File

@@ -58,6 +58,10 @@ impl EarlyLintPass for ItemsAfterStatements {
if in_macro(cx, it.span) { if in_macro(cx, it.span) {
return; return;
} }
if let ItemKind::MacroDef(..) = it.node {
// do not lint `macro_rules`, but continue processing further statements
continue;
}
span_lint(cx, span_lint(cx,
ITEMS_AFTER_STATEMENTS, ITEMS_AFTER_STATEMENTS,
it.span, it.span,

View File

@@ -2,10 +2,8 @@
use rustc::lint::*; use rustc::lint::*;
use rustc::hir::*; use rustc::hir::*;
use utils::{span_lint_and_then, snippet_opt}; use utils::{span_lint_and_then, snippet_opt, type_size};
use rustc::ty::layout::TargetDataLayout;
use rustc::ty::TypeFoldable; use rustc::ty::TypeFoldable;
use rustc::traits::Reveal;
/// **What it does:** Checks for large size differences between variants on `enum`s. /// **What it does:** Checks for large size differences between variants on `enum`s.
/// ///
@@ -55,28 +53,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
let mut largest_variant: Option<(_, _)> = None; let mut largest_variant: Option<(_, _)> = None;
for (i, variant) in adt.variants.iter().enumerate() { for (i, variant) in adt.variants.iter().enumerate() {
let data_layout = TargetDataLayout::parse(cx.sess()); let size: u64 = variant.fields
cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { .iter()
let size: u64 = variant.fields .map(|f| {
.iter() let ty = cx.tcx.item_type(f.did);
.map(|f| { if ty.needs_subst() {
let ty = cx.tcx.item_type(f.did); 0 // we can't reason about generics, so we treat them as zero sized
if ty.needs_subst() { } else {
0 // we can't reason about generics, so we treat them as zero sized type_size(cx, ty).expect("size should be computable for concrete type")
} else { }
ty.layout(&infcx) })
.expect("layout should be computable for concrete type") .sum();
.size(&data_layout)
.bytes()
}
})
.sum();
let grouped = (size, (i, variant)); let grouped = (size, (i, variant));
update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0); update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0);
update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0); update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0);
});
} }
if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) { if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) {

View File

@@ -186,7 +186,8 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
fn is_is_empty(cx: &LateContext, item: &ty::AssociatedItem) -> bool { fn is_is_empty(cx: &LateContext, item: &ty::AssociatedItem) -> bool {
if let ty::AssociatedKind::Method = item.kind { if let ty::AssociatedKind::Method = item.kind {
if &*item.name.as_str() == "is_empty" { if &*item.name.as_str() == "is_empty" {
let ty = cx.tcx.item_type(item.def_id).fn_sig().skip_binder(); let sig = cx.tcx.item_type(item.def_id).fn_sig();
let ty = sig.skip_binder();
ty.inputs().len() == 1 ty.inputs().len() == 1
} else { } else {
false false
@@ -198,7 +199,7 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
/// Check the inherent impl's items for an `is_empty(self)` method. /// Check the inherent impl's items for an `is_empty(self)` method.
fn has_is_empty_impl(cx: &LateContext, id: DefId) -> bool { fn has_is_empty_impl(cx: &LateContext, id: DefId) -> bool {
cx.tcx.inherent_impls.borrow().get(&id).map_or(false, |impls| { cx.tcx.maps.inherent_impls.borrow().get(&id).map_or(false, |impls| {
impls.iter().any(|imp| cx.tcx.associated_items(*imp).any(|item| is_is_empty(cx, &item))) impls.iter().any(|imp| cx.tcx.associated_items(*imp).any(|item| is_is_empty(cx, &item)))
}) })
} }

View File

@@ -9,7 +9,6 @@
#![feature(slice_patterns)] #![feature(slice_patterns)]
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
#![feature(conservative_impl_trait)] #![feature(conservative_impl_trait)]
#![feature(collections_bound)]
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)] #![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
#![allow(needless_lifetimes)] #![allow(needless_lifetimes)]
@@ -447,6 +446,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
misc::REDUNDANT_PATTERN, misc::REDUNDANT_PATTERN,
misc::SHORT_CIRCUIT_STATEMENT, misc::SHORT_CIRCUIT_STATEMENT,
misc::TOPLEVEL_REF_ARG, misc::TOPLEVEL_REF_ARG,
misc::ZERO_PTR,
misc_early::BUILTIN_TYPE_SHADOW, misc_early::BUILTIN_TYPE_SHADOW,
misc_early::DOUBLE_NEG, misc_early::DOUBLE_NEG,
misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -454,7 +454,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
misc_early::REDUNDANT_CLOSURE_CALL, misc_early::REDUNDANT_CLOSURE_CALL,
misc_early::UNNEEDED_FIELD_PATTERN, misc_early::UNNEEDED_FIELD_PATTERN,
misc_early::ZERO_PREFIXED_LITERAL, misc_early::ZERO_PREFIXED_LITERAL,
misc_early::ZERO_PTR,
mut_reference::UNNECESSARY_MUT_PASSED, mut_reference::UNNECESSARY_MUT_PASSED,
mutex_atomic::MUTEX_ATOMIC, mutex_atomic::MUTEX_ATOMIC,
needless_bool::BOOL_COMPARISON, needless_bool::BOOL_COMPARISON,

View File

@@ -257,7 +257,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
} }
}, },
Def::Trait(def_id) => { Def::Trait(def_id) => {
let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id]; let trait_def = self.cx.tcx.maps.trait_def.borrow()[&def_id];
for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions { for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions {
self.record(&None); self.record(&None);
} }

View File

@@ -8,7 +8,6 @@ use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::middle::region::CodeExtent; use rustc::middle::region::CodeExtent;
use rustc::ty; use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use std::collections::HashMap; use std::collections::HashMap;
use syntax::ast; use syntax::ast;
@@ -596,8 +595,8 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(arg) { if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(arg) {
// ...and both sides are compile-time constant integers... // ...and both sides are compile-time constant integers...
let constcx = ConstContext::with_tables(cx.tcx, cx.tables); let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
if let Ok(start_idx) = constcx.eval(start, ExprTypeChecked) { if let Ok(start_idx) = constcx.eval(start) {
if let Ok(end_idx) = constcx.eval(end, ExprTypeChecked) { if let Ok(end_idx) = constcx.eval(end) {
// ...and the start index is greater than the end index, // ...and the start index is greater than the end index,
// this loop will never run. This is often confusing for developers // this loop will never run. This is often confusing for developers
// who think that this will iterate from the larger value to the // who think that this will iterate from the larger value to the

View File

@@ -2,7 +2,6 @@ use rustc::hir::*;
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt; use rustc_const_math::ConstInt;
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -11,7 +10,7 @@ use syntax::ast::LitKind;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::paths; use utils::paths;
use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, in_external_macro, expr_block, walk_ptrs_ty, use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, in_external_macro, expr_block, walk_ptrs_ty,
is_expn_of}; is_expn_of, remove_blocks};
use utils::sugg::Sugg; use utils::sugg::Sugg;
/// **What it does:** Checks for matches with a single arm where an `if let` /// **What it does:** Checks for matches with a single arm where an `if let`
@@ -31,7 +30,7 @@ use utils::sugg::Sugg;
declare_lint! { declare_lint! {
pub SINGLE_MATCH, pub SINGLE_MATCH,
Warn, Warn,
"a match statement with a single nontrivial arm (i.e, where the other arm \ "a match statement with a single nontrivial arm (i.e. where the other arm \
is `_ => {}`) instead of `if let`" is `_ => {}`) instead of `if let`"
} }
@@ -180,11 +179,12 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
if arms.len() == 2 && if arms.len() == 2 &&
arms[0].pats.len() == 1 && arms[0].guard.is_none() && arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
arms[1].pats.len() == 1 && arms[1].guard.is_none() { arms[1].pats.len() == 1 && arms[1].guard.is_none() {
let els = if is_unit_expr(&arms[1].body) { let els = remove_blocks(&arms[1].body);
let els = if is_unit_expr(els) {
None None
} else if let ExprBlock(_) = arms[1].body.node { } else if let ExprBlock(_) = els.node {
// matches with blocks that contain statements are prettier as `if let + else` // matches with blocks that contain statements are prettier as `if let + else`
Some(&*arms[1].body) Some(els)
} else { } else {
// allow match arms with just expressions // allow match arms with just expressions
return; return;
@@ -199,29 +199,33 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
fn check_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) { fn check_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
if arms[1].pats[0].node == PatKind::Wild { if arms[1].pats[0].node == PatKind::Wild {
let lint = if els.is_some() { report_single_match_single_pattern(cx, ex, arms, expr, els);
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. \
Consider using `if let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
} }
} }
fn report_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
let lint = if els.is_some() {
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
}
fn check_single_match_opt_like( fn check_single_match_opt_like(
cx: &LateContext, cx: &LateContext,
ex: &Expr, ex: &Expr,
@@ -254,26 +258,7 @@ fn check_single_match_opt_like(
for &(ty_path, pat_path) in candidates { for &(ty_path, pat_path) in candidates {
if &path == pat_path && match_type(cx, ty, ty_path) { if &path == pat_path && match_type(cx, ty, ty_path) {
let lint = if els.is_some() { report_single_match_single_pattern(cx, ex, arms, expr, els);
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider \
using `if let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
} }
} }
} }
@@ -415,7 +400,7 @@ fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: Match
} }
/// Get all arms that are unbounded `PatRange`s. /// Get all arms that are unbounded `PatRange`s.
fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> { fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &[Arm]) -> Vec<SpannedRange<ConstVal<'tcx>>> {
let constcx = ConstContext::with_tables(cx.tcx, cx.tables); let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
arms.iter() arms.iter()
.flat_map(|arm| { .flat_map(|arm| {
@@ -427,8 +412,8 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
.filter_map(|pat| { .filter_map(|pat| {
if_let_chain! {[ if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node, let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked), let Ok(lhs) = constcx.eval(lhs),
let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked) let Ok(rhs) = constcx.eval(rhs)
], { ], {
let rhs = match *range_end { let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs), RangeEnd::Included => Bound::Included(rhs),
@@ -439,7 +424,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
if_let_chain! {[ if_let_chain! {[
let PatKind::Lit(ref value) = pat.node, let PatKind::Lit(ref value) = pat.node,
let Ok(value) = constcx.eval(value, ExprTypeChecked) let Ok(value) = constcx.eval(value)
], { ], {
return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) }); return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
}} }}

View File

@@ -36,7 +36,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
let forgot_ty = cx.tables.expr_ty(&args[0]); let forgot_ty = cx.tables.expr_ty(&args[0]);
if match forgot_ty.ty_adt_def() { if match forgot_ty.ty_adt_def() {
Some(def) => def.has_dtor(), Some(def) => def.has_dtor(cx.tcx),
_ => false, _ => false,
} { } {
span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type"); span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type");

View File

@@ -3,7 +3,6 @@ use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
@@ -1240,7 +1239,7 @@ fn lint_chars_next(cx: &LateContext, expr: &hir::Expr, chain: &hir::Expr, other:
/// lint for length-1 `str`s for methods in `PATTERN_METHODS` /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr) { fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr) {
if let Ok(ConstVal::Str(r)) = ConstContext::with_tables(cx.tcx, cx.tables).eval(arg, ExprTypeChecked) { if let Ok(ConstVal::Str(r)) = ConstContext::with_tables(cx.tcx, cx.tables).eval(arg) {
if r.len() == 1 { if r.len() == 1 {
let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r)); let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r));
span_lint_and_then(cx, span_lint_and_then(cx,

View File

@@ -65,9 +65,9 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
let def_id = cx.tables.qpath_def(qpath, path.id).def_id(); let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) { if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) {
fetch_const(args, MinMax::Min) fetch_const(cx, args, MinMax::Min)
} else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) { } else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) {
fetch_const(args, MinMax::Max) fetch_const(cx, args, MinMax::Max)
} else { } else {
None None
} }
@@ -79,18 +79,18 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
} }
} }
fn fetch_const(args: &[Expr], m: MinMax) -> Option<(MinMax, Constant, &Expr)> { fn fetch_const<'a>(cx: &LateContext, args: &'a [Expr], m: MinMax) -> Option<(MinMax, Constant, &'a Expr)> {
if args.len() != 2 { if args.len() != 2 {
return None; return None;
} }
if let Some(c) = constant_simple(&args[0]) { if let Some(c) = constant_simple(cx, &args[0]) {
if constant_simple(&args[1]).is_none() { if constant_simple(cx, &args[1]).is_none() {
// otherwise ignore // otherwise ignore
Some((m, c, &args[1])) Some((m, c, &args[1]))
} else { } else {
None None
} }
} else if let Some(c) = constant_simple(&args[1]) { } else if let Some(c) = constant_simple(cx, &args[1]) {
Some((m, c, &args[0])) Some((m, c, &args[0]))
} else { } else {
None None

View File

@@ -4,13 +4,13 @@ use rustc::hir::intravisit::FnKind;
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use rustc_const_math::ConstFloat; use rustc_const_math::ConstFloat;
use syntax::codemap::{Span, Spanned, ExpnFormat}; use syntax::codemap::{Span, Spanned, ExpnFormat};
use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path, snippet, use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path, snippet,
span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats}; span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats, in_constant};
use utils::sugg::Sugg; use utils::sugg::Sugg;
use syntax::ast::LitKind;
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`. /// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
/// ///
@@ -173,6 +173,24 @@ declare_lint! {
"using a short circuit boolean condition as a statement" "using a short circuit boolean condition as a statement"
} }
/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 0 as *const u32
/// ```
declare_lint! {
pub ZERO_PTR,
Warn,
"using 0 as *{const, mut} T"
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Pass; pub struct Pass;
@@ -185,7 +203,8 @@ impl LintPass for Pass {
MODULO_ONE, MODULO_ONE,
REDUNDANT_PATTERN, REDUNDANT_PATTERN,
USED_UNDERSCORE_BINDING, USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT) SHORT_CIRCUIT_STATEMENT,
ZERO_PTR)
} }
} }
@@ -264,41 +283,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
} }
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if let ExprBinary(ref cmp, ref left, ref right) = expr.node { match expr.node {
let op = cmp.node; ExprCast(ref e, ref ty) => {
if op.is_comparison() { check_cast(cx, expr.span, e, ty);
if let ExprPath(QPath::Resolved(_, ref path)) = left.node { return;
check_nan(cx, path, expr.span); },
ExprBinary(ref cmp, ref left, ref right) => {
let op = cmp.node;
if op.is_comparison() {
if let ExprPath(QPath::Resolved(_, ref path)) = left.node {
check_nan(cx, path, expr);
}
if let ExprPath(QPath::Resolved(_, ref path)) = right.node {
check_nan(cx, path, expr);
}
check_to_owned(cx, left, right, true, cmp.span);
check_to_owned(cx, right, left, false, cmp.span)
} }
if let ExprPath(QPath::Resolved(_, ref path)) = right.node { if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
check_nan(cx, path, expr.span); if is_allowed(cx, left) || is_allowed(cx, right) {
}
check_to_owned(cx, left, right, true, cmp.span);
check_to_owned(cx, right, left, false, cmp.span)
}
if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
if is_allowed(cx, left) || is_allowed(cx, right) {
return;
}
if let Some(name) = get_item_name(cx, expr) {
let name = &*name.as_str();
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
name.ends_with("_eq") {
return; return;
} }
} if let Some(name) = get_item_name(cx, expr) {
span_lint_and_then(cx, FLOAT_CMP, expr.span, "strict comparison of f32 or f64", |db| { let name = &*name.as_str();
let lhs = Sugg::hir(cx, left, ".."); if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
let rhs = Sugg::hir(cx, right, ".."); name.ends_with("_eq") {
return;
}
}
span_lint_and_then(cx, FLOAT_CMP, expr.span, "strict comparison of f32 or f64", |db| {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
db.span_suggestion(expr.span, db.span_suggestion(expr.span,
"consider comparing them within some error", "consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs)); format!("({}).abs() < error", lhs - rhs));
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available."); db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
}); });
} else if op == BiRem && is_integer_literal(right, 1) { } else if op == BiRem && is_integer_literal(right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
} }
},
_ => {},
} }
if in_attributes_expansion(cx, expr) { if in_attributes_expansion(cx, expr) {
// Don't lint things expanded by #[derive(...)], etc // Don't lint things expanded by #[derive(...)], etc
@@ -350,37 +376,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
} }
} }
fn check_nan(cx: &LateContext, path: &Path, span: Span) { fn check_nan(cx: &LateContext, path: &Path, expr: &Expr) {
path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" { if !in_constant(cx, expr.id) {
span_lint(cx, path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" {
CMP_NAN, span_lint(cx,
span, CMP_NAN,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead"); expr.span,
}); "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
});
}
} }
fn is_allowed(cx: &LateContext, expr: &Expr) -> bool { fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
let res = ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked); let res = ConstContext::with_tables(cx.tcx, cx.tables).eval(expr);
if let Ok(ConstVal::Float(val)) = res { if let Ok(ConstVal::Float(val)) = res {
use std::cmp::Ordering; use std::cmp::Ordering;
match val {
val @ ConstFloat::F32(_) => {
let zero = ConstFloat::F32(0.0);
let zero = ConstFloat::FInfer { let infinity = ConstFloat::F32(::std::f32::INFINITY);
f32: 0.0,
f64: 0.0,
};
let infinity = ConstFloat::FInfer { let neg_infinity = ConstFloat::F32(::std::f32::NEG_INFINITY);
f32: ::std::f32::INFINITY,
f64: ::std::f64::INFINITY,
};
let neg_infinity = ConstFloat::FInfer { val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
f32: ::std::f32::NEG_INFINITY, val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
f64: ::std::f64::NEG_INFINITY, },
}; val @ ConstFloat::F64(_) => {
let zero = ConstFloat::F64(0.0);
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) || let infinity = ConstFloat::F64(::std::f64::INFINITY);
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
let neg_infinity = ConstFloat::F64(::std::f64::NEG_INFINITY);
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
}
} else { } else {
false false
} }
@@ -486,3 +518,19 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
_ => false, _ => false,
} }
} }
fn check_cast(cx: &LateContext, span: Span, e: &Expr, ty: &Ty) {
if_let_chain! {[
let TyPtr(MutTy { mutbl, .. }) = ty.node,
let ExprLit(ref lit) = e.node,
let LitKind::Int(value, ..) = lit.node,
value == 0,
!in_constant(cx, e.id)
], {
let msg = match mutbl {
Mutability::MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::MutImmutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}}
}

View File

@@ -162,24 +162,6 @@ declare_lint! {
"shadowing a builtin type" "shadowing a builtin type"
} }
/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 0 as *const u32
/// ```
declare_lint! {
pub ZERO_PTR,
Warn,
"using 0 as *{const, mut} T"
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct MiscEarly; pub struct MiscEarly;
@@ -192,8 +174,7 @@ impl LintPass for MiscEarly {
MIXED_CASE_HEX_LITERALS, MIXED_CASE_HEX_LITERALS,
UNSEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX,
ZERO_PREFIXED_LITERAL, ZERO_PREFIXED_LITERAL,
BUILTIN_TYPE_SHADOW, BUILTIN_TYPE_SHADOW)
ZERO_PTR)
} }
} }
@@ -381,9 +362,6 @@ impl EarlyLintPass for MiscEarly {
} }
}} }}
}, },
ExprKind::Cast(ref e, ref ty) => {
check_cast(cx, expr.span, e, ty);
},
_ => (), _ => (),
} }
} }
@@ -412,18 +390,3 @@ impl EarlyLintPass for MiscEarly {
} }
} }
} }
fn check_cast(cx: &EarlyContext, span: Span, e: &Expr, ty: &Ty) {
if_let_chain! {[
let TyKind::Ptr(MutTy { mutbl, .. }) = ty.node,
let ExprKind::Lit(ref lit) = e.node,
let LitKind::Int(value, ..) = lit.node,
value == 0
], {
let msg = match mutbl {
Mutability::Mutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::Immutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}}
}

View File

@@ -77,7 +77,7 @@ impl MissingDoc {
return; return;
} }
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc"); let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name().map_or(false, |n| n == "doc"));
if !has_doc { if !has_doc {
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS, cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS,
sp, sp,

View File

@@ -62,7 +62,7 @@ fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS,
match type_definition.sty { match type_definition.sty {
TypeVariants::TyFnDef(_, _, fn_type) | TypeVariants::TyFnDef(_, _, fn_type) |
TypeVariants::TyFnPtr(fn_type) => { TypeVariants::TyFnPtr(fn_type) => {
let parameters = fn_type.sig.skip_binder().inputs(); let parameters = fn_type.skip_binder().inputs();
for (argument, parameter) in arguments.iter().zip(parameters.iter()) { for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
match parameter.sty { match parameter.sty {
TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) | TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |

View File

@@ -41,6 +41,10 @@ impl LintPass for NeedlessPassByValue {
} }
} }
macro_rules! need {
($e: expr) => { if let Some(x) = $e { x } else { return; } };
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
fn check_fn( fn check_fn(
&mut self, &mut self,
@@ -55,14 +59,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
return; return;
} }
if !matches!(kind, FnKind::ItemFn(..)) { match kind {
return; FnKind::ItemFn(.., attrs) => {
for a in attrs {
if_let_chain!{[
a.meta_item_list().is_some(),
let Some(name) = a.name(),
&*name.as_str() == "proc_macro_derive",
], {
return;
}}
}
},
_ => return,
} }
// Allows these to be passed by value. // Allows these to be passed by value.
let fn_trait = cx.tcx.lang_items.fn_trait().expect("failed to find `Fn` trait"); let fn_trait = need!(cx.tcx.lang_items.fn_trait());
let asref_trait = get_trait_def_id(cx, &paths::ASREF_TRAIT).expect("failed to find `AsRef` trait"); let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT));
let borrow_trait = get_trait_def_id(cx, &paths::BORROW_TRAIT).expect("failed to find `Borrow` trait"); let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
let preds: Vec<ty::Predicate> = { let preds: Vec<ty::Predicate> = {
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id); let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
@@ -85,7 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
let fn_def_id = cx.tcx.hir.local_def_id(node_id); let fn_def_id = cx.tcx.hir.local_def_id(node_id);
let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id); let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig(); let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, fn_sig); let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, &fn_sig);
for ((input, ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) { for ((input, ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

View File

@@ -47,7 +47,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) { fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
if_let_chain!([ if_let_chain!([
let ExprLit(ref l) = lit.node, let ExprLit(ref l) = lit.node,
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node), let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit)),
let Some(val) = ci.to_u64(), let Some(val) = ci.to_u64(),
val == 1, val == 1,
cx.tables.expr_ty(exp).is_integral() cx.tables.expr_ty(exp).is_integral()

View File

@@ -113,7 +113,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) { fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
let fn_def_id = cx.tcx.hir.local_def_id(fn_id); let fn_def_id = cx.tcx.hir.local_def_id(fn_id);
let fn_ty = cx.tcx.item_type(fn_def_id).fn_sig().skip_binder(); let sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_ty = sig.skip_binder();
for (arg, ty) in decl.inputs.iter().zip(fn_ty.inputs()) { for (arg, ty) in decl.inputs.iter().zip(fn_ty.inputs()) {
if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty { if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {

View File

@@ -2,7 +2,6 @@ use regex_syntax;
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
@@ -151,7 +150,7 @@ fn str_span(base: Span, s: &str, c: usize) -> Span {
} }
fn const_str(cx: &LateContext, e: &Expr) -> Option<InternedString> { fn const_str(cx: &LateContext, e: &Expr) -> Option<InternedString> {
match ConstContext::with_tables(cx.tcx, cx.tables).eval(e, ExprTypeChecked) { match ConstContext::with_tables(cx.tcx, cx.tables).eval(e) {
Ok(ConstVal::Str(r)) => Some(r), Ok(ConstVal::Str(r)) => Some(r),
_ => None, _ => None,
} }

View File

@@ -150,9 +150,5 @@ impl EarlyLintPass for ReturnPass {
} }
fn attr_is_cfg(attr: &ast::Attribute) -> bool { fn attr_is_cfg(attr: &ast::Attribute) -> bool {
if let ast::MetaItemKind::List(_) = attr.value.node { attr.meta_item_list().is_some() && attr.name().map_or(false, |n| n == "cfg")
attr.name() == "cfg"
} else {
false
}
} }

View File

@@ -5,9 +5,10 @@ use rustc::lint::*;
use rustc::ty; use rustc::ty;
use std::cmp::Ordering; use std::cmp::Ordering;
use syntax::ast::{IntTy, UintTy, FloatTy}; use syntax::ast::{IntTy, UintTy, FloatTy};
use syntax::attr::IntType;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint, use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint,
opt_def_id, last_path_segment}; opt_def_id, last_path_segment, type_size};
use utils::paths; use utils::paths;
/// Handles all the linting of funky types /// Handles all the linting of funky types
@@ -310,7 +311,7 @@ pub struct CastPass;
declare_lint! { declare_lint! {
pub CAST_PRECISION_LOSS, pub CAST_PRECISION_LOSS,
Allow, Allow,
"casts that cause loss of precision, e.g `x as f32` where `x: u64`" "casts that cause loss of precision, e.g. `x as f32` where `x: u64`"
} }
/// **What it does:** Checks for casts from a signed to an unsigned numerical /// **What it does:** Checks for casts from a signed to an unsigned numerical
@@ -331,7 +332,7 @@ declare_lint! {
declare_lint! { declare_lint! {
pub CAST_SIGN_LOSS, pub CAST_SIGN_LOSS,
Allow, Allow,
"casts from signed types to unsigned types, e.g `x as u32` where `x: i32`" "casts from signed types to unsigned types, e.g. `x as u32` where `x: i32`"
} }
/// **What it does:** Checks for on casts between numerical types that may /// **What it does:** Checks for on casts between numerical types that may
@@ -351,7 +352,7 @@ declare_lint! {
declare_lint! { declare_lint! {
pub CAST_POSSIBLE_TRUNCATION, pub CAST_POSSIBLE_TRUNCATION,
Allow, Allow,
"casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, \ "casts that may cause truncation of the value, e.g. `x as u8` where `x: u32`, \
or `x as i32` where `x: f32`" or `x as i32` where `x: f32`"
} }
@@ -375,7 +376,7 @@ declare_lint! {
declare_lint! { declare_lint! {
pub CAST_POSSIBLE_WRAP, pub CAST_POSSIBLE_WRAP,
Allow, Allow,
"casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` \ "casts that may cause wrapping around the value, e.g. `x as i32` where `x: u32` \
and `x > i32::MAX`" and `x > i32::MAX`"
} }
@@ -392,7 +393,7 @@ declare_lint! {
declare_lint! { declare_lint! {
pub UNNECESSARY_CAST, pub UNNECESSARY_CAST,
Warn, Warn,
"cast to the same type, e.g `x as i32` where `x: i32`" "cast to the same type, e.g. `x as i32` where `x: i32`"
} }
/// Returns the size in bits of an integral type. /// Returns the size in bits of an integral type.
@@ -907,7 +908,6 @@ fn detect_absurd_comparison<'a>(
fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeExpr<'a>> { fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeExpr<'a>> {
use rustc::middle::const_val::ConstVal::*; use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::*; use rustc_const_math::*;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::*; use rustc_const_eval::*;
use types::ExtremeType::*; use types::ExtremeType::*;
@@ -918,7 +918,7 @@ fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeEx
_ => return None, _ => return None,
}; };
let cv = match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked) { let cv = match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr) {
Ok(val) => val, Ok(val) => val,
Err(_) => return None, Err(_) => return None,
}; };
@@ -1077,7 +1077,13 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
use std::*; use std::*;
if let ExprCast(ref cast_exp, _) = expr.node { if let ExprCast(ref cast_exp, _) = expr.node {
match cx.tables.expr_ty(cast_exp).sty { let pre_cast_ty = cx.tables.expr_ty(cast_exp);
let cast_ty = cx.tables.expr_ty(expr);
// if it's a cast from i32 to u32 wrapping will invalidate all these checks
if type_size(cx, pre_cast_ty) == type_size(cx, cast_ty) {
return None;
}
match pre_cast_ty.sty {
TyInt(int_ty) => { TyInt(int_ty) => {
Some(match int_ty { Some(match int_ty {
IntTy::I8 => (FullInt::S(i8::min_value() as i128), FullInt::S(i8::max_value() as i128)), IntTy::I8 => (FullInt::S(i8::min_value() as i128), FullInt::S(i8::max_value() as i128)),
@@ -1107,18 +1113,15 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> { fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> {
use rustc::middle::const_val::ConstVal::*; use rustc::middle::const_val::ConstVal::*;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked) { match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr) {
Ok(val) => { Ok(val) => {
if let Integral(const_int) = val { if let Integral(const_int) = val {
Some(match const_int.erase_type() { match const_int.int_type() {
ConstInt::InferSigned(x) => FullInt::S(x as i128), IntType::SignedInt(_) => #[allow(cast_possible_wrap)] Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
ConstInt::Infer(x) => FullInt::U(x as u128), IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
_ => unreachable!(), }
})
} else { } else {
None None
} }

View File

@@ -166,6 +166,7 @@ define_Conf! {
("doc-valid-idents", doc_valid_idents, [ ("doc-valid-idents", doc_valid_idents, [
"MiB", "GiB", "TiB", "PiB", "EiB", "MiB", "GiB", "TiB", "PiB", "EiB",
"DirectX", "DirectX",
"ECMAScript",
"GPLv2", "GPLv3", "GPLv2", "GPLv3",
"GitHub", "GitHub",
"IPv4", "IPv6", "IPv4", "IPv6",

View File

@@ -9,6 +9,8 @@ use rustc::traits::Reveal;
use rustc::traits; use rustc::traits;
use rustc::ty::subst::Subst; use rustc::ty::subst::Subst;
use rustc::ty; use rustc::ty;
use rustc::ty::layout::TargetDataLayout;
use rustc::mir::transform::MirSource;
use rustc_errors; use rustc_errors;
use std::borrow::Cow; use std::borrow::Cow;
use std::env; use std::env;
@@ -97,6 +99,17 @@ pub mod higher;
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
rhs.expn_id != lhs.expn_id rhs.expn_id != lhs.expn_id
} }
pub fn in_constant(cx: &LateContext, id: NodeId) -> bool {
let parent_id = cx.tcx.hir.get_parent(id);
match MirSource::from_node(cx.tcx, parent_id) {
MirSource::Fn(_) => false,
MirSource::Const(_) |
MirSource::Static(..) |
MirSource::Promoted(..) => true,
}
}
/// Returns true if this `expn_info` was expanded by any macro. /// Returns true if this `expn_info` was expanded by any macro.
pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool { pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
cx.sess().codemap().with_expn_info(span.expn_id, |info| { cx.sess().codemap().with_expn_info(span.expn_id, |info| {
@@ -384,7 +397,7 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
/// snippet(cx, expr.span, "..") /// snippet(cx, expr.span, "..")
/// ``` /// ```
pub fn snippet<'a, 'b, T: LintContext<'b>>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { pub fn snippet<'a, 'b, T: LintContext<'b>>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or_else(|_| Cow::Borrowed(default)) snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
} }
/// Convert a span to a code snippet. Returns `None` if not available. /// Convert a span to a code snippet. Returns `None` if not available.
@@ -665,17 +678,13 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
if attr.is_sugared_doc { if attr.is_sugared_doc {
continue; continue;
} }
if let ast::MetaItemKind::NameValue(ref value) = attr.value.node { if let Some(ref value) = attr.value_str() {
if attr.name() == name { if attr.name().map_or(false, |n| n == name) {
if let LitKind::Str(ref s, _) = value.node { if let Ok(value) = FromStr::from_str(&*value.as_str()) {
if let Ok(value) = FromStr::from_str(&*s.as_str()) { attr::mark_used(attr);
attr::mark_used(attr); f(value)
f(value)
} else {
sess.span_err(value.span, "not a number");
}
} else { } else {
unreachable!() sess.span_err(attr.span, "not a number");
} }
} }
} }
@@ -781,7 +790,7 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> ty::T
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item); let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item);
let fn_def_id = cx.tcx.hir.local_def_id(fn_item); let fn_def_id = cx.tcx.hir.local_def_id(fn_item);
let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig(); let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, fn_sig); let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, &fn_sig);
fn_sig.output() fn_sig.output()
} }
@@ -806,7 +815,7 @@ pub fn same_tys<'a, 'tcx>(
pub fn type_is_unsafe_function(ty: ty::Ty) -> bool { pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
match ty.sty { match ty.sty {
ty::TyFnDef(_, _, f) | ty::TyFnDef(_, _, f) |
ty::TyFnPtr(f) => f.unsafety == Unsafety::Unsafe, ty::TyFnPtr(f) => f.unsafety() == Unsafety::Unsafe,
_ => false, _ => false,
} }
} }
@@ -972,3 +981,9 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
None None
} }
pub fn type_size<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>) -> Option<u64> {
cx.tcx
.infer_ctxt((), Reveal::All)
.enter(|infcx| ty.layout(&infcx).ok().map(|lay| lay.size(&TargetDataLayout::parse(cx.sess())).bytes()))
}

View File

@@ -99,6 +99,7 @@ impl<'a> Sugg<'a> {
ast::ExprKind::Block(..) | ast::ExprKind::Block(..) |
ast::ExprKind::Break(..) | ast::ExprKind::Break(..) |
ast::ExprKind::Call(..) | ast::ExprKind::Call(..) |
ast::ExprKind::Catch(..) |
ast::ExprKind::Continue(..) | ast::ExprKind::Continue(..) |
ast::ExprKind::Field(..) | ast::ExprKind::Field(..) |
ast::ExprKind::ForLoop(..) | ast::ExprKind::ForLoop(..) |

View File

@@ -1,7 +1,6 @@
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::*; use rustc::lint::*;
use rustc::ty; use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{higher, is_copy, snippet, span_lint_and_then}; use utils::{higher, is_copy, snippet, span_lint_and_then};
@@ -60,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) { fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
let snippet = match *vec_args { let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => { higher::VecArgs::Repeat(elem, len) => {
if ConstContext::with_tables(cx.tcx, cx.tables).eval(len, ExprTypeChecked).is_ok() { if ConstContext::with_tables(cx.tcx, cx.tables).eval(len).is_ok() {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into() format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
} else { } else {
return; return;

View File

@@ -36,8 +36,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// TODO - constant_simple does not fold many operations involving floats. // TODO - constant_simple does not fold many operations involving floats.
// That's probably fine for this lint - it's pretty unlikely that someone would // That's probably fine for this lint - it's pretty unlikely that someone would
// do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(left), let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left),
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(right), let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right),
let Ok(0.0) = lhs_value.parse(), let Ok(0.0) = lhs_value.parse(),
let Ok(0.0) = rhs_value.parse() let Ok(0.0) = rhs_value.parse()
], { ], {

View File

@@ -89,7 +89,6 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
lint_groups, lint_groups,
llvm_passes, llvm_passes,
attributes, attributes,
mir_passes,
.. } = registry; .. } = registry;
let sess = &state.session; let sess = &state.session;
let mut ls = sess.lint_store.borrow_mut(); let mut ls = sess.lint_store.borrow_mut();
@@ -105,7 +104,6 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
} }
sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes); sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
sess.mir_passes.borrow_mut().extend(mir_passes);
sess.plugin_attributes.borrow_mut().extend(attributes); sess.plugin_attributes.borrow_mut().extend(attributes);
} }
old(state); old(state);
@@ -244,30 +242,32 @@ pub fn main() {
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust") .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
}; };
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly rustc_driver::in_rustc_thread(|| {
// without having to pass --sysroot or anything // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") { // without having to pass --sysroot or anything
env::args().collect() let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
} else { env::args().collect()
env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect() } else {
}; env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
};
// this check ensures that dependencies are built but not linted and the final crate is // this check ensures that dependencies are built but not linted and the final crate is
// linted but not built // linted but not built
let clippy_enabled = env::args().any(|s| s == "-Zno-trans"); let clippy_enabled = env::args().any(|s| s == "-Zno-trans");
if clippy_enabled { if clippy_enabled {
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]); args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
} }
let mut ccc = ClippyCompilerCalls::new(clippy_enabled); let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None); let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);
if let Err(err_count) = result {
if let Err(err_count) = result { if err_count > 0 {
if err_count > 0 { std::process::exit(1);
std::process::exit(1); }
} }
} })
.expect("rustc_thread failed");
} }
} }

View File

@@ -1,99 +0,0 @@
#![feature(rustc_private)]
extern crate clippy_lints;
extern crate rustc;
extern crate rustc_const_eval;
extern crate rustc_const_math;
extern crate syntax;
use clippy_lints::consts::{constant_simple, Constant, FloatWidth};
use rustc_const_math::ConstInt;
use rustc::hir::*;
use syntax::ast::{LitIntType, LitKind, NodeId, StrStyle};
use syntax::codemap::{Spanned, COMMAND_LINE_SP};
use syntax::symbol::Symbol;
use syntax::ptr::P;
use syntax::util::ThinVec;
fn spanned<T>(t: T) -> Spanned<T> {
Spanned {
node: t,
span: COMMAND_LINE_SP,
}
}
fn expr(n: Expr_) -> Expr {
Expr {
id: NodeId::new(1),
node: n,
span: COMMAND_LINE_SP,
attrs: ThinVec::new(),
}
}
fn lit(l: LitKind) -> Expr {
expr(ExprLit(P(spanned(l))))
}
fn binop(op: BinOp_, l: Expr, r: Expr) -> Expr {
expr(ExprBinary(spanned(op), P(l), P(r)))
}
fn check(expect: Constant, expr: &Expr) {
assert_eq!(Some(expect), constant_simple(expr))
}
const TRUE: Constant = Constant::Bool(true);
const FALSE: Constant = Constant::Bool(false);
const ZERO: Constant = Constant::Int(ConstInt::Infer(0));
const ONE: Constant = Constant::Int(ConstInt::Infer(1));
const TWO: Constant = Constant::Int(ConstInt::Infer(2));
#[test]
fn test_lit() {
check(TRUE, &lit(LitKind::Bool(true)));
check(FALSE, &lit(LitKind::Bool(false)));
check(ZERO, &lit(LitKind::Int(0, LitIntType::Unsuffixed)));
check(Constant::Str("cool!".into(), StrStyle::Cooked),
&lit(LitKind::Str(Symbol::intern("cool!"), StrStyle::Cooked)));
}
#[test]
fn test_ops() {
check(TRUE, &binop(BiOr, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
check(FALSE, &binop(BiAnd, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
let litzero = lit(LitKind::Int(0, LitIntType::Unsuffixed));
let litone = lit(LitKind::Int(1, LitIntType::Unsuffixed));
check(TRUE, &binop(BiEq, litzero.clone(), litzero.clone()));
check(TRUE, &binop(BiGe, litzero.clone(), litzero.clone()));
check(TRUE, &binop(BiLe, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiNe, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiGt, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiLt, litzero.clone(), litzero.clone()));
check(ZERO, &binop(BiAdd, litzero.clone(), litzero.clone()));
check(TWO, &binop(BiAdd, litone.clone(), litone.clone()));
check(ONE, &binop(BiSub, litone.clone(), litzero.clone()));
check(ONE, &binop(BiMul, litone.clone(), litone.clone()));
check(ONE, &binop(BiDiv, litone.clone(), litone.clone()));
let half_any = Constant::Float("0.5".into(), FloatWidth::Any);
let half32 = Constant::Float("0.5".into(), FloatWidth::F32);
let half64 = Constant::Float("0.5".into(), FloatWidth::F64);
let pos_zero = Constant::Float("0.0".into(), FloatWidth::F64);
let neg_zero = Constant::Float("-0.0".into(), FloatWidth::F64);
assert_eq!(pos_zero, pos_zero);
assert_eq!(neg_zero, neg_zero);
assert_eq!(None, pos_zero.partial_cmp(&neg_zero));
assert_eq!(half_any, half32);
assert_eq!(half_any, half64);
// for transitivity
assert_eq!(half32, half64);
assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::U8(0)));
assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::I8(0)));
assert_eq!(Constant::Int(ConstInt::InferSigned(-1)), Constant::Int(ConstInt::I8(-1)));
}

View File

@@ -1,5 +1,4 @@
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(collections_bound)]
extern crate clippy_lints; extern crate clippy_lints;
extern crate syntax; extern crate syntax;

View File

@@ -0,0 +1,13 @@
#![feature(plugin)]
#![plugin(clippy)]
#![allow(clippy)]
fn main() {
match 1 {
1 => {}
2 => {
[0; 1];
}
_ => {}
}
}

View File

@@ -1,12 +1,20 @@
#![feature(plugin)] #![feature(plugin)]
#![plugin(clippy)] #![plugin(clippy)]
#![deny(mut_mut, zero_ptr, cmp_nan)]
#![allow(dead_code)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
use std::collections::HashMap; use std::collections::HashMap;
#[deny(mut_mut)] // ensure that we don't suggest `is_nan` and `is_null` inside constants
// FIXME: once const fn is stable, suggest these functions again in constants
const BAA: *const i32 = 0 as *const i32;
static mut BAR: *const i32 = BAA;
static mut FOO: *const i32 = 0 as *const i32;
static mut BUH: bool = 42.0 < std::f32::NAN;
#[allow(unused_variables, unused_mut)] #[allow(unused_variables, unused_mut)]
fn main() { fn main() {
lazy_static! { lazy_static! {
@@ -19,4 +27,6 @@ fn main() {
static ref MUT_COUNT : usize = MUT_MAP.len(); static ref MUT_COUNT : usize = MUT_MAP.len();
} }
assert!(*MUT_COUNT == 1); assert!(*MUT_COUNT == 1);
// FIXME: don't lint in array length, requires `check_body`
//let _ = [""; (42.0 < std::f32::NAN) as usize];
} }

View File

@@ -0,0 +1,11 @@
#![feature(plugin)]
#![plugin(clippy)]
#![warn(single_match_else)]
fn main() {
let n = match (42, 43) {
(42, n) => n,
_ => panic!("typeck error"),
};
assert_eq!(n, 43);
}

View File

@@ -3,33 +3,79 @@
#![deny(invalid_upcast_comparisons)] #![deny(invalid_upcast_comparisons)]
#![allow(unused, eq_op, no_effect, unnecessary_operation)] #![allow(unused, eq_op, no_effect, unnecessary_operation)]
fn mk_value<T>() -> T { unimplemented!() }
fn main() { fn main() {
let zero: u32 = 0; let u32: u32 = mk_value();
let u8_max: u8 = 255; let u8: u8 = mk_value();
let i32: i32 = mk_value();
let i8: i8 = mk_value();
(u8_max as u32) > 300; // always false, since no u8 can be > 300
(u8_max as u32) > 20; (u8 as u32) > 300;
(u8 as i32) > 300;
(u8 as u32) == 300;
(u8 as i32) == 300;
300 < (u8 as u32);
300 < (u8 as i32);
300 == (u8 as u32);
300 == (u8 as i32);
// inverted of the above
(u8 as u32) <= 300;
(u8 as i32) <= 300;
(u8 as u32) != 300;
(u8 as i32) != 300;
300 >= (u8 as u32);
300 >= (u8 as i32);
300 != (u8 as u32);
300 != (u8 as i32);
(zero as i32) < -5; // always false, since u8 -> i32 doesn't wrap
(zero as i32) < 10; (u8 as i32) < 0;
-5 != (u8 as i32);
// inverted of the above
(u8 as i32) >= 0;
-5 == (u8 as i32);
-5 < (zero as i32); // always false, since no u8 can be 1337
0 <= (zero as i32); 1337 == (u8 as i32);
0 < (zero as i32); 1337 == (u8 as u32);
// inverted of the above
1337 != (u8 as i32);
1337 != (u8 as u32);
-5 > (zero as i32);
-5 >= (u8_max as i32);
1337 == (u8_max as i32);
-5 == (zero as i32);
-5 != (u8_max as i32);
// Those are Ok: // Those are Ok:
42 == (u8_max as i32); (u8 as u32) > 20;
42 != (u8_max as i32); 42 == (u8 as i32);
42 > (u8_max as i32); 42 != (u8 as i32);
(u8_max as i32) == 42; 42 > (u8 as i32);
(u8_max as i32) != 42; (u8 as i32) == 42;
(u8_max as i32) > 42; (u8 as i32) != 42;
(u8_max as i32) < 42; (u8 as i32) > 42;
(u8 as i32) < 42;
(u8 as i8) == -1;
(u8 as i8) != -1;
(u8 as i32) > -1;
(u8 as i32) < -1;
(u32 as i32) < -5;
(u32 as i32) < 10;
(i8 as u8) == 1;
(i8 as u8) != 1;
(i8 as u8) < 1;
(i8 as u8) > 1;
(i32 as u32) < 5;
(i32 as u32) < 10;
-5 < (u32 as i32);
0 <= (u32 as i32);
0 < (u32 as i32);
-5 > (u32 as i32);
-5 >= (u8 as i32);
-5 == (u32 as i32);
} }

View File

@@ -1,8 +1,8 @@
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:10:5 --> $DIR/invalid_upcast_comparisons.rs:16:5
| |
10 | (u8_max as u32) > 300; 16 | (u8 as u32) > 300;
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
note: lint level defined here note: lint level defined here
--> $DIR/invalid_upcast_comparisons.rs:4:9 --> $DIR/invalid_upcast_comparisons.rs:4:9
@@ -10,53 +10,161 @@ note: lint level defined here
4 | #![deny(invalid_upcast_comparisons)] 4 | #![deny(invalid_upcast_comparisons)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:13:5
|
13 | (zero as i32) < -5;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:16:5
|
16 | -5 < (zero as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:17:5 --> $DIR/invalid_upcast_comparisons.rs:17:5
| |
17 | 0 <= (zero as i32); 17 | (u8 as i32) > 300;
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:18:5
|
18 | (u8 as u32) == 300;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:19:5
|
19 | (u8 as i32) == 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:20:5 --> $DIR/invalid_upcast_comparisons.rs:20:5
| |
20 | -5 > (zero as i32); 20 | 300 < (u8 as u32);
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:21:5 --> $DIR/invalid_upcast_comparisons.rs:21:5
| |
21 | -5 >= (u8_max as i32); 21 | 300 < (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:22:5 --> $DIR/invalid_upcast_comparisons.rs:22:5
| |
22 | 1337 == (u8_max as i32); 22 | 300 == (u8 as u32);
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:24:5 --> $DIR/invalid_upcast_comparisons.rs:23:5
| |
24 | -5 == (zero as i32); 23 | 300 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always true error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:25:5 --> $DIR/invalid_upcast_comparisons.rs:25:5
| |
25 | -5 != (u8_max as i32); 25 | (u8 as u32) <= 300;
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:26:5
|
26 | (u8 as i32) <= 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:27:5
|
27 | (u8 as u32) != 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:28:5
|
28 | (u8 as i32) != 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:29:5
|
29 | 300 >= (u8 as u32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:30:5
|
30 | 300 >= (u8 as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:31:5
|
31 | 300 != (u8 as u32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:32:5
|
32 | 300 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:35:5
|
35 | (u8 as i32) < 0;
| ^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:36:5
|
36 | -5 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:38:5
|
38 | (u8 as i32) >= 0;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:39:5
|
39 | -5 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:42:5
|
42 | 1337 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:43:5
|
43 | 1337 == (u8 as u32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:45:5
|
45 | 1337 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:46:5
|
46 | 1337 != (u8 as u32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:61:5
|
61 | (u8 as i32) > -1;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:62:5
|
62 | (u8 as i32) < -1;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:78:5
|
78 | -5 >= (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: aborting due to 27 previous errors

View File

@@ -17,3 +17,14 @@ fn main() {
fn foo() { println!("foo"); } fn foo() { println!("foo"); }
foo(); foo();
} }
fn mac() {
let mut a = 5;
println!("{}", a);
// do not lint this, because it needs to be after `a`
macro_rules! b {
() => {{ a = 6 }}
}
b!();
println!("{}", a);
}

View File

@@ -0,0 +1,11 @@
#![feature(plugin)]
#![plugin(clippy)]
#![crate_type = "proc-macro"]
#![deny(needless_pass_by_value)]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Foo)]
pub fn foo(_input: TokenStream) -> TokenStream { unimplemented!() }