mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-16 22:18:40 +00:00
Merge remote-tracking branch 'origin/master' into 1537-drop_copy
This commit is contained in:
commit
8ae82eb4ab
52 changed files with 717 additions and 568 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,6 +1,24 @@
|
|||
# Change Log
|
||||
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
|
||||
* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
|
||||
* 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`]
|
||||
|
||||
## 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
|
||||
* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
|
||||
|
|
|
@ -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
|
||||
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
|
||||
contains some questionable code itself! Also before making a pull request, please run
|
||||
`util/update_lints.py`, which will update `lib.rs` and `README.md` with the lint declarations. Our
|
||||
travis build actually checks for this.
|
||||
Compiling clippy can take almost a minute or more depending on your machine.
|
||||
You can set the environment flag `CARGO_INCREMENTAL=1` to cut down that time to
|
||||
almost a third on average, depending on the influence your change has.
|
||||
|
||||
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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.0.115"
|
||||
version = "0.0.121"
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Andre Bogus <bogusandre@gmail.com>",
|
||||
|
@ -30,7 +30,7 @@ test = false
|
|||
|
||||
[dependencies]
|
||||
# begin automatic update
|
||||
clippy_lints = { version = "0.0.115", path = "clippy_lints" }
|
||||
clippy_lints = { version = "0.0.121", path = "clippy_lints" }
|
||||
# end automatic update
|
||||
cargo_metadata = "0.1.1"
|
||||
|
||||
|
|
16
README.md
16
README.md
|
@ -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:
|
||||
`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).
|
||||
|
||||
### 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
|
||||
[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
|
||||
[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 } ...`
|
||||
[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
|
||||
[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
|
||||
[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_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_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_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`
|
||||
[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
|
||||
[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
|
||||
[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_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`
|
||||
[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()`
|
||||
|
@ -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
|
||||
[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
|
||||
[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_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 `..`
|
||||
|
|
|
@ -4,16 +4,15 @@ environment:
|
|||
matrix:
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
MSYS2_BITS: 32
|
||||
RUN_CARGO_CLIPPY: true
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
MSYS2_BITS: 32
|
||||
RUN_CARGO_CLIPPY: true
|
||||
- TARGET: x86_64-pc-windows-gnu
|
||||
MSYS2_BITS: 64
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
MSYS2_BITS: 64
|
||||
|
||||
install:
|
||||
- set PATH=C:\Program Files\Git\mingw64\bin;%PATH%
|
||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
|
||||
- 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
|
||||
|
@ -29,7 +28,7 @@ test_script:
|
|||
- cargo test --features debugging
|
||||
- copy target\debug\cargo-clippy.exe C:\Users\appveyor\.cargo\bin\
|
||||
- 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:
|
||||
- provider: Email
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin automatic update
|
||||
version = "0.0.115"
|
||||
version = "0.0.121"
|
||||
# end automatic update
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use rustc_const_math::ConstInt;
|
||||
use rustc_const_math::{ConstUsize, ConstIsize, ConstInt};
|
||||
use rustc::hir;
|
||||
use syntax::ast::RangeLimits;
|
||||
use utils::{self, higher};
|
||||
|
@ -61,11 +60,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
|
|||
// Array with known size can be checked statically
|
||||
let ty = cx.tables.expr_ty(array);
|
||||
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);
|
||||
|
||||
// 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 size <= const_index {
|
||||
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
|
||||
if let Some(range) = higher::range(index) {
|
||||
let start = range.start
|
||||
.map(|start| constcx.eval(start, ExprTypeChecked))
|
||||
.map(|start| constcx.eval(start))
|
||||
.map(|v| v.ok());
|
||||
let end = range.end
|
||||
.map(|end| constcx.eval(end, ExprTypeChecked))
|
||||
.map(|end| constcx.eval(end))
|
||||
.map(|v| v.ok());
|
||||
|
||||
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 {
|
||||
Some(Some(ConstVal::Integral(x))) => x,
|
||||
Some(_) => return None,
|
||||
None => ConstInt::Infer(0),
|
||||
None => ConstInt::U8(0),
|
||||
};
|
||||
|
||||
let end = match *end {
|
||||
Some(Some(ConstVal::Integral(x))) => {
|
||||
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 {
|
||||
x
|
||||
}
|
||||
|
|
|
@ -86,8 +86,8 @@ impl LintPass for AttrPass {
|
|||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
|
||||
fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
|
||||
if let MetaItemKind::List(ref items) = attr.value.node {
|
||||
if items.is_empty() || attr.name() != "deprecated" {
|
||||
if let Some(ref items) = attr.meta_item_list() {
|
||||
if items.is_empty() || attr.name().map_or(true, |n| n != "deprecated") {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
|
@ -110,31 +110,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
|
|||
ItemExternCrate(_) |
|
||||
ItemUse(_, _) => {
|
||||
for attr in &item.attrs {
|
||||
if let MetaItemKind::List(ref lint_list) = attr.value.node {
|
||||
match &*attr.name().as_str() {
|
||||
"allow" | "warn" | "deny" | "forbid" => {
|
||||
// whitelist `unused_imports` and `deprecated`
|
||||
for lint in lint_list {
|
||||
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
|
||||
if let ItemUse(_, _) = item.node {
|
||||
return;
|
||||
if let Some(ref lint_list) = attr.meta_item_list() {
|
||||
if let Some(name) = attr.name() {
|
||||
match &*name.as_str() {
|
||||
"allow" | "warn" | "deny" | "forbid" => {
|
||||
// whitelist `unused_imports` and `deprecated`
|
||||
for lint in lint_list {
|
||||
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
|
||||
if let ItemUse(_, _) = item.node {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
|
||||
if sugg.len() > 1 {
|
||||
span_lint_and_then(cx,
|
||||
USELESS_ATTRIBUTE,
|
||||
attr.span,
|
||||
"useless lint attribute",
|
||||
|db| {
|
||||
sugg.insert(1, '!');
|
||||
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
|
||||
});
|
||||
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
|
||||
if sugg.len() > 1 {
|
||||
span_lint_and_then(cx,
|
||||
USELESS_ATTRIBUTE,
|
||||
attr.span,
|
||||
"useless lint attribute",
|
||||
|db| {
|
||||
sugg.insert(1, '!');
|
||||
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 {
|
||||
if let MetaItemKind::List(ref values) = attr.value.node {
|
||||
if values.len() != 1 || attr.name() != "inline" {
|
||||
if let Some(ref values) = attr.meta_item_list() {
|
||||
if values.len() != 1 || attr.name().map_or(true, |n| n != "inline") {
|
||||
continue;
|
||||
}
|
||||
if is_word(&values[0], "always") {
|
||||
|
|
|
@ -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> {
|
||||
use rustc::ty::subst::Substs;
|
||||
match lit.node {
|
||||
ExprLit(ref lit_ptr) => {
|
||||
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) => {
|
||||
let def = cx.tables.qpath_def(qpath, lit.id);
|
||||
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 {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use utils::*;
|
|||
declare_lint! {
|
||||
pub BLOCK_IN_IF_CONDITION_EXPR,
|
||||
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
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
use rustc::lint::LateContext;
|
||||
use rustc::hir::def::Def;
|
||||
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::ty::{self, TyCtxt};
|
||||
use std::cmp::Ordering::{self, Equal};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
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;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
@ -52,21 +53,6 @@ pub enum 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 {
|
||||
fn eq(&self, other: &Constant) -> bool {
|
||||
match (self, other) {
|
||||
|
@ -174,28 +160,33 @@ impl PartialOrd for Constant {
|
|||
|
||||
/// parse a `LitKind` to a `Constant`
|
||||
#[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 {
|
||||
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
|
||||
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
|
||||
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
|
||||
LitKind::Char(c) => Constant::Char(c),
|
||||
LitKind::Int(value, LitIntType::Unsuffixed) => Constant::Int(ConstInt::Infer(value)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U8)) => Constant::Int(ConstInt::U8(value as u8)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U16)) => Constant::Int(ConstInt::U16(value as u16)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U32)) => Constant::Int(ConstInt::U32(value as u32)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U128)) => Constant::Int(ConstInt::U128(value as u128)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => {
|
||||
Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32)))
|
||||
},
|
||||
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)),
|
||||
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::Int(n, hint) => {
|
||||
match (&ty.sty, hint) {
|
||||
(&ty::TyInt(ity), _) |
|
||||
(_, Signed(ity)) => {
|
||||
Constant::Int(ConstInt::new_signed_truncating(n as i128, ity, tcx.sess.target.int_type))
|
||||
},
|
||||
(&ty::TyUint(uty), _) |
|
||||
(_, Unsigned(uty)) => {
|
||||
Constant::Int(ConstInt::new_unsigned_truncating(n as u128, uty, tcx.sess.target.uint_type))
|
||||
},
|
||||
_ => bug!(),
|
||||
}
|
||||
},
|
||||
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||
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)> {
|
||||
let mut cx = ConstEvalLateContext {
|
||||
lcx: Some(lcx),
|
||||
tcx: lcx.tcx,
|
||||
tables: lcx.tables,
|
||||
needed_resolution: false,
|
||||
};
|
||||
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
|
||||
}
|
||||
|
||||
pub fn constant_simple(e: &Expr) -> Option<Constant> {
|
||||
let mut cx = ConstEvalLateContext {
|
||||
lcx: None,
|
||||
needed_resolution: false,
|
||||
};
|
||||
cx.expr(e)
|
||||
pub fn constant_simple(lcx: &LateContext, e: &Expr) -> Option<Constant> {
|
||||
constant(lcx, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
|
||||
}
|
||||
|
||||
struct ConstEvalLateContext<'c, 'cc: 'c> {
|
||||
lcx: Option<&'c LateContext<'c, 'cc>>,
|
||||
struct ConstEvalLateContext<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
needed_resolution: bool,
|
||||
}
|
||||
|
||||
|
@ -257,17 +246,15 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
ExprPath(ref qpath) => self.fetch_path(qpath, e.id),
|
||||
ExprBlock(ref block) => self.block(block),
|
||||
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),
|
||||
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
|
||||
ExprRepeat(ref value, number_id) => {
|
||||
if let Some(lcx) = self.lcx {
|
||||
self.binop_apply(value,
|
||||
&lcx.tcx.hir.body(number_id).value,
|
||||
|v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ExprRepeat(ref value, _) => {
|
||||
let n = match self.tables.expr_ty(e).sty {
|
||||
ty::TyArray(_, n) => n,
|
||||
_ => span_bug!(e.span, "typeck error"),
|
||||
};
|
||||
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
|
||||
},
|
||||
ExprUnary(op, ref operand) => {
|
||||
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
|
||||
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
|
||||
if let Some(lcx) = self.lcx {
|
||||
let def = lcx.tables.qpath_def(qpath, id);
|
||||
match def {
|
||||
Def::Const(def_id) |
|
||||
Def::AssociatedConst(def_id) => {
|
||||
let substs = Some(lcx.tables
|
||||
.node_id_item_substs(id)
|
||||
.unwrap_or_else(|| lcx.tcx.intern_substs(&[])));
|
||||
if let Some((const_expr, _tab, _ty)) = lookup_const_by_id(lcx.tcx, def_id, substs) {
|
||||
let ret = self.expr(const_expr);
|
||||
if ret.is_some() {
|
||||
self.needed_resolution = true;
|
||||
}
|
||||
return ret;
|
||||
let def = self.tables.qpath_def(qpath, id);
|
||||
match def {
|
||||
Def::Const(def_id) |
|
||||
Def::AssociatedConst(def_id) => {
|
||||
let substs = self.tables
|
||||
.node_id_item_substs(id)
|
||||
.unwrap_or_else(|| self.tcx.intern_substs(&[]));
|
||||
if let Some((const_expr, tables)) = lookup_const_by_id(self.tcx, def_id, substs) {
|
||||
let mut cx = ConstEvalLateContext {
|
||||
tcx: self.tcx,
|
||||
tables: tables,
|
||||
needed_resolution: false,
|
||||
};
|
||||
let ret = cx.expr(const_expr);
|
||||
if ret.is_some() {
|
||||
self.needed_resolution = true;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -369,15 +359,4 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
_ => 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,13 @@ impl LintPass for 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) {
|
||||
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 e = cfg.graph.len_edges() as u64;
|
||||
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);
|
||||
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);
|
||||
match ty.sty {
|
||||
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;
|
||||
},
|
||||
_ => (),
|
||||
|
|
|
@ -89,11 +89,9 @@ pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [a
|
|||
|
||||
for attr in attrs {
|
||||
if attr.is_sugared_doc {
|
||||
if let ast::MetaItemKind::NameValue(ref doc) = attr.value.node {
|
||||
if let ast::LitKind::Str(ref doc, _) = doc.node {
|
||||
let doc = (*doc.as_str()).to_owned();
|
||||
docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
|
||||
}
|
||||
if let Some(ref doc) = attr.value_str() {
|
||||
let doc = (*doc.as_str()).to_owned();
|
||||
docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
|
|||
if let Some(body_id) = variant.disr_expr {
|
||||
use rustc_const_eval::*;
|
||||
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(Isize(Is64(i)))) => i as i32 as i64 != i,
|
||||
_ => false,
|
||||
|
|
|
@ -66,7 +66,8 @@ fn check_closure(cx: &LateContext, expr: &Expr) {
|
|||
// Is it an unsafe function? They don't implement the closure traits
|
||||
ty::TyFnDef(_, _, 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;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -129,7 +129,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
|
|||
match self.cx.tables.expr_ty(func).sty {
|
||||
ty::TyFnDef(_, _, 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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc::lint::*;
|
|||
use rustc::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
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`.
|
||||
///
|
||||
|
@ -60,11 +60,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
|
|||
|
||||
|
||||
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 {
|
||||
0 => v == Constant::Int(ConstInt::Infer(0)),
|
||||
-1 => v == Constant::Int(ConstInt::InferSigned(-1)),
|
||||
1 => v == Constant::Int(ConstInt::Infer(1)),
|
||||
0 => v.to_u128_unchecked() == 0,
|
||||
-1 => match v.int_type() {
|
||||
SignedInt(_) => #[allow(cast_possible_wrap)] (v.to_u128_unchecked() as i128 == -1),
|
||||
UnsignedInt(_) => false
|
||||
},
|
||||
1 => v.to_u128_unchecked() == 1,
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
span_lint(cx,
|
||||
|
|
|
@ -58,6 +58,10 @@ impl EarlyLintPass for ItemsAfterStatements {
|
|||
if in_macro(cx, it.span) {
|
||||
return;
|
||||
}
|
||||
if let ItemKind::MacroDef(..) = it.node {
|
||||
// do not lint `macro_rules`, but continue processing further statements
|
||||
continue;
|
||||
}
|
||||
span_lint(cx,
|
||||
ITEMS_AFTER_STATEMENTS,
|
||||
it.span,
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{span_lint_and_then, snippet_opt};
|
||||
use rustc::ty::layout::TargetDataLayout;
|
||||
use utils::{span_lint_and_then, snippet_opt, type_size};
|
||||
use rustc::ty::TypeFoldable;
|
||||
use rustc::traits::Reveal;
|
||||
|
||||
/// **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;
|
||||
|
||||
for (i, variant) in adt.variants.iter().enumerate() {
|
||||
let data_layout = TargetDataLayout::parse(cx.sess());
|
||||
cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
|
||||
let size: u64 = variant.fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let ty = cx.tcx.item_type(f.did);
|
||||
if ty.needs_subst() {
|
||||
0 // we can't reason about generics, so we treat them as zero sized
|
||||
} else {
|
||||
ty.layout(&infcx)
|
||||
.expect("layout should be computable for concrete type")
|
||||
.size(&data_layout)
|
||||
.bytes()
|
||||
}
|
||||
})
|
||||
.sum();
|
||||
let size: u64 = variant.fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let ty = cx.tcx.item_type(f.did);
|
||||
if ty.needs_subst() {
|
||||
0 // we can't reason about generics, so we treat them as zero sized
|
||||
} else {
|
||||
type_size(cx, ty).expect("size should be computable for concrete type")
|
||||
}
|
||||
})
|
||||
.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 largest_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);
|
||||
}
|
||||
|
||||
if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) {
|
||||
|
|
|
@ -186,7 +186,8 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
|
|||
fn is_is_empty(cx: &LateContext, item: &ty::AssociatedItem) -> bool {
|
||||
if let ty::AssociatedKind::Method = item.kind {
|
||||
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
|
||||
} else {
|
||||
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.
|
||||
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)))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#![feature(slice_patterns)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
#![feature(collections_bound)]
|
||||
|
||||
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
|
||||
#![allow(needless_lifetimes)]
|
||||
|
@ -447,6 +446,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
misc::REDUNDANT_PATTERN,
|
||||
misc::SHORT_CIRCUIT_STATEMENT,
|
||||
misc::TOPLEVEL_REF_ARG,
|
||||
misc::ZERO_PTR,
|
||||
misc_early::BUILTIN_TYPE_SHADOW,
|
||||
misc_early::DOUBLE_NEG,
|
||||
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::UNNEEDED_FIELD_PATTERN,
|
||||
misc_early::ZERO_PREFIXED_LITERAL,
|
||||
misc_early::ZERO_PTR,
|
||||
mut_reference::UNNECESSARY_MUT_PASSED,
|
||||
mutex_atomic::MUTEX_ATOMIC,
|
||||
needless_bool::BOOL_COMPARISON,
|
||||
|
|
|
@ -257,7 +257,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
}
|
||||
},
|
||||
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 {
|
||||
self.record(&None);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use rustc::lint::*;
|
|||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::middle::region::CodeExtent;
|
||||
use rustc::ty;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use std::collections::HashMap;
|
||||
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) {
|
||||
// ...and both sides are compile-time constant integers...
|
||||
let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
|
||||
if let Ok(start_idx) = constcx.eval(start, ExprTypeChecked) {
|
||||
if let Ok(end_idx) = constcx.eval(end, ExprTypeChecked) {
|
||||
if let Ok(start_idx) = constcx.eval(start) {
|
||||
if let Ok(end_idx) = constcx.eval(end) {
|
||||
// ...and the start index is greater than the end index,
|
||||
// this loop will never run. This is often confusing for developers
|
||||
// who think that this will iterate from the larger value to the
|
||||
|
|
|
@ -2,7 +2,6 @@ use rustc::hir::*;
|
|||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use rustc_const_math::ConstInt;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -11,7 +10,7 @@ use syntax::ast::LitKind;
|
|||
use syntax::codemap::Span;
|
||||
use utils::paths;
|
||||
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;
|
||||
|
||||
/// **What it does:** Checks for matches with a single arm where an `if let`
|
||||
|
@ -31,7 +30,7 @@ use utils::sugg::Sugg;
|
|||
declare_lint! {
|
||||
pub SINGLE_MATCH,
|
||||
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`"
|
||||
}
|
||||
|
||||
|
@ -180,11 +179,12 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
|||
if arms.len() == 2 &&
|
||||
arms[0].pats.len() == 1 && arms[0].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
|
||||
} 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`
|
||||
Some(&*arms[1].body)
|
||||
Some(els)
|
||||
} else {
|
||||
// allow match arms with just expressions
|
||||
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>) {
|
||||
if arms[1].pats[0].node == PatKind::Wild {
|
||||
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));
|
||||
});
|
||||
report_single_match_single_pattern(cx, ex, arms, expr, els);
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
cx: &LateContext,
|
||||
ex: &Expr,
|
||||
|
@ -254,26 +258,7 @@ fn check_single_match_opt_like(
|
|||
|
||||
for &(ty_path, pat_path) in candidates {
|
||||
if &path == pat_path && match_type(cx, ty, ty_path) {
|
||||
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));
|
||||
});
|
||||
report_single_match_single_pattern(cx, ex, arms, expr, els);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
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);
|
||||
arms.iter()
|
||||
.flat_map(|arm| {
|
||||
|
@ -427,8 +412,8 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
|
|||
.filter_map(|pat| {
|
||||
if_let_chain! {[
|
||||
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
|
||||
let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked),
|
||||
let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked)
|
||||
let Ok(lhs) = constcx.eval(lhs),
|
||||
let Ok(rhs) = constcx.eval(rhs)
|
||||
], {
|
||||
let rhs = match *range_end {
|
||||
RangeEnd::Included => Bound::Included(rhs),
|
||||
|
@ -439,7 +424,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
|
|||
|
||||
if_let_chain! {[
|
||||
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)) });
|
||||
}}
|
||||
|
|
|
@ -36,7 +36,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
|
|||
let forgot_ty = cx.tables.expr_ty(&args[0]);
|
||||
|
||||
if match forgot_ty.ty_adt_def() {
|
||||
Some(def) => def.has_dtor(),
|
||||
Some(def) => def.has_dtor(cx.tcx),
|
||||
_ => false,
|
||||
} {
|
||||
span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type");
|
||||
|
|
|
@ -3,7 +3,6 @@ use rustc::lint::*;
|
|||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use std::borrow::Cow;
|
||||
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`
|
||||
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 {
|
||||
let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r));
|
||||
span_lint_and_then(cx,
|
||||
|
|
|
@ -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();
|
||||
|
||||
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) {
|
||||
fetch_const(args, MinMax::Max)
|
||||
fetch_const(cx, args, MinMax::Max)
|
||||
} else {
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
if let Some(c) = constant_simple(&args[0]) {
|
||||
if constant_simple(&args[1]).is_none() {
|
||||
if let Some(c) = constant_simple(cx, &args[0]) {
|
||||
if constant_simple(cx, &args[1]).is_none() {
|
||||
// otherwise ignore
|
||||
Some((m, c, &args[1]))
|
||||
} else {
|
||||
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]))
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -4,13 +4,13 @@ use rustc::hir::intravisit::FnKind;
|
|||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use rustc_const_math::ConstFloat;
|
||||
use syntax::codemap::{Span, Spanned, ExpnFormat};
|
||||
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 syntax::ast::LitKind;
|
||||
|
||||
/// **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"
|
||||
}
|
||||
|
||||
/// **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)]
|
||||
pub struct Pass;
|
||||
|
||||
|
@ -185,7 +203,8 @@ impl LintPass for Pass {
|
|||
MODULO_ONE,
|
||||
REDUNDANT_PATTERN,
|
||||
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) {
|
||||
if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
if let ExprPath(QPath::Resolved(_, ref path)) = left.node {
|
||||
check_nan(cx, path, expr.span);
|
||||
match expr.node {
|
||||
ExprCast(ref e, ref ty) => {
|
||||
check_cast(cx, expr.span, e, ty);
|
||||
return;
|
||||
},
|
||||
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 {
|
||||
check_nan(cx, path, expr.span);
|
||||
}
|
||||
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") {
|
||||
if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
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, "..");
|
||||
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;
|
||||
}
|
||||
}
|
||||
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,
|
||||
"consider comparing them within some error",
|
||||
format!("({}).abs() < error", lhs - rhs));
|
||||
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
|
||||
});
|
||||
} else if op == BiRem && is_integer_literal(right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
db.span_suggestion(expr.span,
|
||||
"consider comparing them within some error",
|
||||
format!("({}).abs() < error", lhs - rhs));
|
||||
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
|
||||
});
|
||||
} else if op == BiRem && is_integer_literal(right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if in_attributes_expansion(cx, expr) {
|
||||
// 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) {
|
||||
path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" {
|
||||
span_lint(cx,
|
||||
CMP_NAN,
|
||||
span,
|
||||
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
|
||||
});
|
||||
fn check_nan(cx: &LateContext, path: &Path, expr: &Expr) {
|
||||
if !in_constant(cx, expr.id) {
|
||||
path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" {
|
||||
span_lint(cx,
|
||||
CMP_NAN,
|
||||
expr.span,
|
||||
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
use std::cmp::Ordering;
|
||||
match val {
|
||||
val @ ConstFloat::F32(_) => {
|
||||
let zero = ConstFloat::F32(0.0);
|
||||
|
||||
let zero = ConstFloat::FInfer {
|
||||
f32: 0.0,
|
||||
f64: 0.0,
|
||||
};
|
||||
let infinity = ConstFloat::F32(::std::f32::INFINITY);
|
||||
|
||||
let infinity = ConstFloat::FInfer {
|
||||
f32: ::std::f32::INFINITY,
|
||||
f64: ::std::f64::INFINITY,
|
||||
};
|
||||
let neg_infinity = ConstFloat::F32(::std::f32::NEG_INFINITY);
|
||||
|
||||
let neg_infinity = ConstFloat::FInfer {
|
||||
f32: ::std::f32::NEG_INFINITY,
|
||||
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)
|
||||
},
|
||||
val @ ConstFloat::F64(_) => {
|
||||
let zero = ConstFloat::F64(0.0);
|
||||
|
||||
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
|
||||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
|
||||
let infinity = ConstFloat::F64(::std::f64::INFINITY);
|
||||
|
||||
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 {
|
||||
false
|
||||
}
|
||||
|
@ -486,3 +518,19 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
|
|||
_ => 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);
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -162,24 +162,6 @@ declare_lint! {
|
|||
"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)]
|
||||
pub struct MiscEarly;
|
||||
|
||||
|
@ -192,8 +174,7 @@ impl LintPass for MiscEarly {
|
|||
MIXED_CASE_HEX_LITERALS,
|
||||
UNSEPARATED_LITERAL_SUFFIX,
|
||||
ZERO_PREFIXED_LITERAL,
|
||||
BUILTIN_TYPE_SHADOW,
|
||||
ZERO_PTR)
|
||||
BUILTIN_TYPE_SHADOW)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ impl MissingDoc {
|
|||
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 {
|
||||
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
sp,
|
||||
|
|
|
@ -62,7 +62,7 @@ fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS,
|
|||
match type_definition.sty {
|
||||
TypeVariants::TyFnDef(_, _, 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()) {
|
||||
match parameter.sty {
|
||||
TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |
|
||||
|
|
|
@ -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 {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
@ -55,14 +59,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
return;
|
||||
}
|
||||
|
||||
if !matches!(kind, FnKind::ItemFn(..)) {
|
||||
return;
|
||||
match kind {
|
||||
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.
|
||||
let fn_trait = cx.tcx.lang_items.fn_trait().expect("failed to find `Fn` trait");
|
||||
let asref_trait = get_trait_def_id(cx, &paths::ASREF_TRAIT).expect("failed to find `AsRef` trait");
|
||||
let borrow_trait = get_trait_def_id(cx, &paths::BORROW_TRAIT).expect("failed to find `Borrow` trait");
|
||||
let fn_trait = need!(cx.tcx.lang_items.fn_trait());
|
||||
let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT));
|
||||
let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
|
||||
|
||||
let preds: Vec<ty::Predicate> = {
|
||||
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 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.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) {
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
|
|||
fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
|
||||
if_let_chain!([
|
||||
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(),
|
||||
val == 1,
|
||||
cx.tables.expr_ty(exp).is_integral()
|
||||
|
|
|
@ -113,7 +113,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
|
|||
|
||||
fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
||||
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()) {
|
||||
if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {
|
||||
|
|
|
@ -2,7 +2,6 @@ use regex_syntax;
|
|||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use std::collections::HashSet;
|
||||
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> {
|
||||
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),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -150,9 +150,5 @@ impl EarlyLintPass for ReturnPass {
|
|||
}
|
||||
|
||||
fn attr_is_cfg(attr: &ast::Attribute) -> bool {
|
||||
if let ast::MetaItemKind::List(_) = attr.value.node {
|
||||
attr.name() == "cfg"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
attr.meta_item_list().is_some() && attr.name().map_or(false, |n| n == "cfg")
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@ use rustc::lint::*;
|
|||
use rustc::ty;
|
||||
use std::cmp::Ordering;
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
use syntax::attr::IntType;
|
||||
use syntax::codemap::Span;
|
||||
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;
|
||||
|
||||
/// Handles all the linting of funky types
|
||||
|
@ -310,7 +311,7 @@ pub struct CastPass;
|
|||
declare_lint! {
|
||||
pub CAST_PRECISION_LOSS,
|
||||
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
|
||||
|
@ -331,7 +332,7 @@ declare_lint! {
|
|||
declare_lint! {
|
||||
pub CAST_SIGN_LOSS,
|
||||
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
|
||||
|
@ -351,7 +352,7 @@ declare_lint! {
|
|||
declare_lint! {
|
||||
pub CAST_POSSIBLE_TRUNCATION,
|
||||
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`"
|
||||
}
|
||||
|
||||
|
@ -375,7 +376,7 @@ declare_lint! {
|
|||
declare_lint! {
|
||||
pub CAST_POSSIBLE_WRAP,
|
||||
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`"
|
||||
}
|
||||
|
||||
|
@ -392,7 +393,7 @@ declare_lint! {
|
|||
declare_lint! {
|
||||
pub UNNECESSARY_CAST,
|
||||
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.
|
||||
|
@ -907,7 +908,6 @@ fn detect_absurd_comparison<'a>(
|
|||
fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeExpr<'a>> {
|
||||
use rustc::middle::const_val::ConstVal::*;
|
||||
use rustc_const_math::*;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::*;
|
||||
use types::ExtremeType::*;
|
||||
|
||||
|
@ -918,7 +918,7 @@ fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeEx
|
|||
_ => 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,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
@ -1077,7 +1077,13 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
|
|||
use std::*;
|
||||
|
||||
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) => {
|
||||
Some(match int_ty {
|
||||
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> {
|
||||
use rustc::middle::const_val::ConstVal::*;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
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) => {
|
||||
if let Integral(const_int) = val {
|
||||
Some(match const_int.erase_type() {
|
||||
ConstInt::InferSigned(x) => FullInt::S(x as i128),
|
||||
ConstInt::Infer(x) => FullInt::U(x as u128),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
match const_int.int_type() {
|
||||
IntType::SignedInt(_) => #[allow(cast_possible_wrap)] Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
|
||||
IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -166,6 +166,7 @@ define_Conf! {
|
|||
("doc-valid-idents", doc_valid_idents, [
|
||||
"MiB", "GiB", "TiB", "PiB", "EiB",
|
||||
"DirectX",
|
||||
"ECMAScript",
|
||||
"GPLv2", "GPLv3",
|
||||
"GitHub",
|
||||
"IPv4", "IPv6",
|
||||
|
|
|
@ -9,6 +9,8 @@ use rustc::traits::Reveal;
|
|||
use rustc::traits;
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::ty;
|
||||
use rustc::ty::layout::TargetDataLayout;
|
||||
use rustc::mir::transform::MirSource;
|
||||
use rustc_errors;
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
|
@ -97,6 +99,17 @@ pub mod higher;
|
|||
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
|
||||
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.
|
||||
pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
|
||||
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, "..")
|
||||
/// ```
|
||||
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.
|
||||
|
@ -665,17 +678,13 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
if attr.is_sugared_doc {
|
||||
continue;
|
||||
}
|
||||
if let ast::MetaItemKind::NameValue(ref value) = attr.value.node {
|
||||
if attr.name() == name {
|
||||
if let LitKind::Str(ref s, _) = value.node {
|
||||
if let Ok(value) = FromStr::from_str(&*s.as_str()) {
|
||||
attr::mark_used(attr);
|
||||
f(value)
|
||||
} else {
|
||||
sess.span_err(value.span, "not a number");
|
||||
}
|
||||
if let Some(ref value) = attr.value_str() {
|
||||
if attr.name().map_or(false, |n| n == name) {
|
||||
if let Ok(value) = FromStr::from_str(&*value.as_str()) {
|
||||
attr::mark_used(attr);
|
||||
f(value)
|
||||
} 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 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.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()
|
||||
}
|
||||
|
||||
|
@ -806,7 +815,7 @@ pub fn same_tys<'a, 'tcx>(
|
|||
pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyFnDef(_, _, f) |
|
||||
ty::TyFnPtr(f) => f.unsafety == Unsafety::Unsafe,
|
||||
ty::TyFnPtr(f) => f.unsafety() == Unsafety::Unsafe,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -972,3 +981,9 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
|
|||
|
||||
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()))
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ impl<'a> Sugg<'a> {
|
|||
ast::ExprKind::Block(..) |
|
||||
ast::ExprKind::Break(..) |
|
||||
ast::ExprKind::Call(..) |
|
||||
ast::ExprKind::Catch(..) |
|
||||
ast::ExprKind::Continue(..) |
|
||||
ast::ExprKind::Field(..) |
|
||||
ast::ExprKind::ForLoop(..) |
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use syntax::codemap::Span;
|
||||
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) {
|
||||
let snippet = match *vec_args {
|
||||
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()
|
||||
} else {
|
||||
return;
|
||||
|
|
|
@ -36,8 +36,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
// TODO - constant_simple does not fold many operations involving floats.
|
||||
// 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.
|
||||
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(left),
|
||||
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(right),
|
||||
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left),
|
||||
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right),
|
||||
let Ok(0.0) = lhs_value.parse(),
|
||||
let Ok(0.0) = rhs_value.parse()
|
||||
], {
|
||||
|
|
46
src/main.rs
46
src/main.rs
|
@ -89,7 +89,6 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
|||
lint_groups,
|
||||
llvm_passes,
|
||||
attributes,
|
||||
mir_passes,
|
||||
.. } = registry;
|
||||
let sess = &state.session;
|
||||
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.mir_passes.borrow_mut().extend(mir_passes);
|
||||
sess.plugin_attributes.borrow_mut().extend(attributes);
|
||||
}
|
||||
old(state);
|
||||
|
@ -244,30 +242,32 @@ pub fn main() {
|
|||
.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
|
||||
// without having to pass --sysroot or anything
|
||||
let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
|
||||
env::args().collect()
|
||||
} else {
|
||||
env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
|
||||
};
|
||||
rustc_driver::in_rustc_thread(|| {
|
||||
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
|
||||
// without having to pass --sysroot or anything
|
||||
let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
|
||||
env::args().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
|
||||
// linted but not built
|
||||
let clippy_enabled = env::args().any(|s| s == "-Zno-trans");
|
||||
// this check ensures that dependencies are built but not linted and the final crate is
|
||||
// linted but not built
|
||||
let clippy_enabled = env::args().any(|s| s == "-Zno-trans");
|
||||
|
||||
if clippy_enabled {
|
||||
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
|
||||
}
|
||||
if clippy_enabled {
|
||||
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
|
||||
}
|
||||
|
||||
let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
|
||||
let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);
|
||||
|
||||
if let Err(err_count) = result {
|
||||
if err_count > 0 {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
|
||||
let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);
|
||||
if let Err(err_count) = result {
|
||||
if err_count > 0 {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
.expect("rustc_thread failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(rustc_private)]
|
||||
#![feature(collections_bound)]
|
||||
|
||||
extern crate clippy_lints;
|
||||
extern crate syntax;
|
||||
|
|
13
tests/run-pass/ice-1588.rs
Normal file
13
tests/run-pass/ice-1588.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
#![allow(clippy)]
|
||||
|
||||
fn main() {
|
||||
match 1 {
|
||||
1 => {}
|
||||
2 => {
|
||||
[0; 1];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
#![deny(mut_mut, zero_ptr, cmp_nan)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
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)]
|
||||
fn main() {
|
||||
lazy_static! {
|
||||
|
@ -19,4 +27,6 @@ fn main() {
|
|||
static ref MUT_COUNT : usize = MUT_MAP.len();
|
||||
}
|
||||
assert!(*MUT_COUNT == 1);
|
||||
// FIXME: don't lint in array length, requires `check_body`
|
||||
//let _ = [""; (42.0 < std::f32::NAN) as usize];
|
||||
}
|
||||
|
|
11
tests/run-pass/single-match-else.rs
Normal file
11
tests/run-pass/single-match-else.rs
Normal 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);
|
||||
}
|
|
@ -3,33 +3,79 @@
|
|||
|
||||
#![deny(invalid_upcast_comparisons)]
|
||||
#![allow(unused, eq_op, no_effect, unnecessary_operation)]
|
||||
|
||||
fn mk_value<T>() -> T { unimplemented!() }
|
||||
|
||||
fn main() {
|
||||
let zero: u32 = 0;
|
||||
let u8_max: u8 = 255;
|
||||
let u32: u32 = mk_value();
|
||||
let u8: u8 = mk_value();
|
||||
let i32: i32 = mk_value();
|
||||
let i8: i8 = mk_value();
|
||||
|
||||
(u8_max as u32) > 300;
|
||||
(u8_max as u32) > 20;
|
||||
// always false, since no u8 can be > 300
|
||||
(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;
|
||||
(zero as i32) < 10;
|
||||
// always false, since u8 -> i32 doesn't wrap
|
||||
(u8 as i32) < 0;
|
||||
-5 != (u8 as i32);
|
||||
// inverted of the above
|
||||
(u8 as i32) >= 0;
|
||||
-5 == (u8 as i32);
|
||||
|
||||
-5 < (zero as i32);
|
||||
0 <= (zero as i32);
|
||||
0 < (zero as i32);
|
||||
// always false, since no u8 can be 1337
|
||||
1337 == (u8 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:
|
||||
42 == (u8_max as i32);
|
||||
42 != (u8_max as i32);
|
||||
42 > (u8_max as i32);
|
||||
(u8_max as i32) == 42;
|
||||
(u8_max as i32) != 42;
|
||||
(u8_max as i32) > 42;
|
||||
(u8_max as i32) < 42;
|
||||
(u8 as u32) > 20;
|
||||
42 == (u8 as i32);
|
||||
42 != (u8 as i32);
|
||||
42 > (u8 as i32);
|
||||
(u8 as i32) == 42;
|
||||
(u8 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);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false
|
||||
--> $DIR/invalid_upcast_comparisons.rs:10:5
|
||||
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
|
||||
--> $DIR/invalid_upcast_comparisons.rs:16:5
|
||||
|
|
||||
10 | (u8_max as u32) > 300;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
16 | (u8 as u32) > 300;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/invalid_upcast_comparisons.rs:4:9
|
||||
|
@ -10,53 +10,161 @@ note: lint level defined here
|
|||
4 | #![deny(invalid_upcast_comparisons)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: because of the numeric bounds on `zero` 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
|
||||
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
|
||||
--> $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
|
||||
|
|
||||
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
|
||||
|
|
||||
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
|
||||
|
|
||||
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
|
||||
--> $DIR/invalid_upcast_comparisons.rs:24:5
|
||||
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
|
||||
--> $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
|
||||
|
|
||||
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
|
||||
|
||||
|
|
|
@ -17,3 +17,14 @@ fn main() {
|
|||
fn foo() { println!("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);
|
||||
}
|
||||
|
|
11
tests/ui/needless_pass_by_value_proc_macro.rs
Normal file
11
tests/ui/needless_pass_by_value_proc_macro.rs
Normal 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!() }
|
Loading…
Add table
Reference in a new issue