diff --git a/src/array_indexing.rs b/src/array_indexing.rs
index 3c6acb93284..ece66ae20a2 100644
--- a/src/array_indexing.rs
+++ b/src/array_indexing.rs
@@ -83,7 +83,7 @@ impl LateLintPass for ArrayIndexing {
eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None)).map(|v| v.ok());
if let Some((start, end)) = to_const_range(start, end, range.limits, size) {
- if start >= size || end >= size {
+ if start > size || end > size {
utils::span_lint(cx,
OUT_OF_BOUNDS_INDEXING,
e.span,
@@ -109,14 +109,11 @@ impl LateLintPass for ArrayIndexing {
}
}
-/// Returns an option containing a tuple with the start and end (exclusive) of the range
-///
-/// Note: we assume the start and the end of the range are unsigned, since array slicing
-/// works only on usize
+/// Returns an option containing a tuple with the start and end (exclusive) of the range.
fn to_const_range(start: Option>,
- end: Option >,
- limits: RangeLimits,
- array_size: ConstInt)
+ end: Option >,
+ limits: RangeLimits,
+ array_size: ConstInt)
-> Option<(ConstInt, ConstInt)> {
let start = match start {
Some(Some(ConstVal::Integral(x))) => x,
@@ -127,13 +124,13 @@ fn to_const_range(start: Option >,
let end = match end {
Some(Some(ConstVal::Integral(x))) => {
if limits == RangeLimits::Closed {
- x
+ (x + ConstInt::Infer(1)).expect("such a big array is not realistic")
} else {
- (x - ConstInt::Infer(1)).expect("x > 0")
+ x
}
}
Some(_) => return None,
- None => (array_size - ConstInt::Infer(1)).expect("array_size > 0"),
+ None => array_size
};
Some((start, end))
diff --git a/src/panic.rs b/src/panic.rs
index 7dbcf2a5b30..8b9bf9f1f19 100644
--- a/src/panic.rs
+++ b/src/panic.rs
@@ -1,7 +1,7 @@
use rustc::lint::*;
use rustc_front::hir::*;
use syntax::ast::LitKind;
-use utils::{span_lint, in_external_macro, match_path, BEGIN_UNWIND};
+use utils::{span_lint, is_direct_expn_of, match_path, BEGIN_UNWIND};
/// **What it does:** This lint checks for missing parameters in `panic!`.
///
@@ -28,7 +28,6 @@ impl LintPass for PanicPass {
impl LateLintPass for PanicPass {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
if_let_chain! {[
- in_external_macro(cx, expr.span),
let ExprBlock(ref block) = expr.node,
let Some(ref ex) = block.expr,
let ExprCall(ref fun, ref params) = ex.node,
@@ -36,16 +35,13 @@ impl LateLintPass for PanicPass {
let ExprPath(None, ref path) = fun.node,
match_path(path, &BEGIN_UNWIND),
let ExprLit(ref lit) = params[0].node,
+ is_direct_expn_of(cx, params[0].span, "panic").is_some(),
let LitKind::Str(ref string, _) = lit.node,
let Some(par) = string.find('{'),
- string[par..].contains('}'),
- let Some(sp) = cx.sess().codemap()
- .with_expn_info(expr.span.expn_id,
- |info| info.map(|i| i.call_site))
+ string[par..].contains('}')
], {
-
- span_lint(cx, PANIC_PARAMS, sp,
- "You probably are missing some parameter in your `panic!` call");
+ span_lint(cx, PANIC_PARAMS, params[0].span,
+ "you probably are missing some parameter in your format string");
}}
}
}
diff --git a/src/strings.rs b/src/strings.rs
index f4318fc261a..aa9cdcce50c 100644
--- a/src/strings.rs
+++ b/src/strings.rs
@@ -8,7 +8,7 @@ use rustc_front::hir::*;
use syntax::codemap::Spanned;
use utils::STRING_PATH;
use utils::SpanlessEq;
-use utils::{match_type, span_lint, walk_ptrs_ty, get_parent_expr};
+use utils::{match_type, span_lint, span_lint_and_then, walk_ptrs_ty, get_parent_expr};
/// **What it does:** This lint matches code of the form `x = x + y` (without `let`!).
///
@@ -140,12 +140,19 @@ impl LateLintPass for StringLitAsBytes {
if name.node.as_str() == "as_bytes" {
if let ExprLit(ref lit) = args[0].node {
if let LitKind::Str(ref lit_content, _) = lit.node {
- if lit_content.chars().all(|c| c.is_ascii()) && !in_macro(cx, e.span) {
- let msg = format!("calling `as_bytes()` on a string literal. \
- Consider using a byte string literal instead: \
- `b{}`",
- snippet(cx, args[0].span, r#""foo""#));
- span_lint(cx, STRING_LIT_AS_BYTES, e.span, &msg);
+ if lit_content.chars().all(|c| c.is_ascii()) && !in_macro(cx, args[0].span) {
+ span_lint_and_then(cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `as_bytes()` on a string literal",
+ |db| {
+ let sugg = format!("b{}",
+ snippet(cx, args[0].span, r#""foo""#));
+ db.span_suggestion(e.span,
+ "consider using a byte string literal instead",
+ sugg);
+ });
+
}
}
}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 86a5e24efc2..3fb52318b6f 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -604,6 +604,7 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &'
}
/// Return the pre-expansion span if is this comes from an expansion of the macro `name`.
+/// See also `is_direct_expn_of`.
pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option {
loop {
let span_name_span = cx.tcx
@@ -619,6 +620,25 @@ pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option
}
}
+/// Return the pre-expansion span if is this directly comes from an expansion of the macro `name`.
+/// The difference with `is_expn_of` is that in
+/// ```rust,ignore
+/// foo!(bar!(42));
+/// ```
+/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only `bar!` by
+/// `is_direct_expn_of`.
+pub fn is_direct_expn_of(cx: &LateContext, span: Span, name: &str) -> Option {
+ let span_name_span = cx.tcx
+ .sess
+ .codemap()
+ .with_expn_info(span.expn_id, |expn| expn.map(|ei| (ei.callee.name(), ei.call_site)));
+
+ match span_name_span {
+ Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span),
+ _ => None,
+ }
+}
+
/// Returns index of character after first CamelCase component of `s`
pub fn camel_case_until(s: &str) -> usize {
let mut iter = s.char_indices();
diff --git a/tests/compile-fail/array_indexing.rs b/tests/compile-fail/array_indexing.rs
index 14f3448a9f6..35fadf8c1e4 100644
--- a/tests/compile-fail/array_indexing.rs
+++ b/tests/compile-fail/array_indexing.rs
@@ -16,6 +16,8 @@ fn main() {
&x[0...4]; //~ERROR: range is out of bounds
&x[..];
&x[1..];
+ &x[4..];
+ &x[5..]; //~ERROR: range is out of bounds
&x[..4];
&x[..5]; //~ERROR: range is out of bounds
@@ -24,4 +26,16 @@ fn main() {
&y[1..2]; //~ERROR: slicing may panic
&y[..];
&y[0...4]; //~ERROR: slicing may panic
+
+ let empty: [i8; 0] = [];
+ empty[0]; //~ERROR: const index is out of bounds
+ &empty[1..5]; //~ERROR: range is out of bounds
+ &empty[0...4]; //~ERROR: range is out of bounds
+ &empty[..];
+ &empty[0..];
+ &empty[0..0];
+ &empty[0...0]; //~ERROR: range is out of bounds
+ &empty[..0];
+ &empty[1..]; //~ERROR: range is out of bounds
+ &empty[..4]; //~ERROR: range is out of bounds
}
diff --git a/tests/compile-fail/panic.rs b/tests/compile-fail/panic.rs
index 38fe5aa2c0f..7e535d69b69 100644
--- a/tests/compile-fail/panic.rs
+++ b/tests/compile-fail/panic.rs
@@ -1,13 +1,15 @@
#![feature(plugin)]
#![plugin(clippy)]
-#[deny(panic_params)]
+#![deny(panic_params)]
fn missing() {
if true {
- panic!("{}"); //~ERROR: You probably are missing some parameter
+ panic!("{}"); //~ERROR: you probably are missing some parameter
+ } else if false {
+ panic!("{:?}"); //~ERROR: you probably are missing some parameter
} else {
- panic!("{:?}"); //~ERROR: You probably are missing some parameter
+ assert!(true, "here be missing values: {}"); //~ERROR you probably are missing some parameter
}
}
@@ -15,12 +17,16 @@ fn ok_single() {
panic!("foo bar");
}
+fn ok_inner() {
+ // Test for #768
+ assert!("foo bar".contains(&format!("foo {}", "bar")));
+}
+
fn ok_multiple() {
panic!("{}", "This is {ok}");
}
fn ok_bracket() {
- // the match is just here because of #759, it serves no other purpose for the lint
match 42 {
1337 => panic!("{so is this"),
666 => panic!("so is this}"),
@@ -33,4 +39,5 @@ fn main() {
ok_single();
ok_multiple();
ok_bracket();
+ ok_inner();
}
diff --git a/tests/compile-fail/strings.rs b/tests/compile-fail/strings.rs
index 7ed93737ffa..656349ba621 100644
--- a/tests/compile-fail/strings.rs
+++ b/tests/compile-fail/strings.rs
@@ -47,9 +47,15 @@ fn both() {
#[allow(dead_code, unused_variables)]
#[deny(string_lit_as_bytes)]
fn str_lit_as_bytes() {
- let bs = "hello there".as_bytes(); //~ERROR calling `as_bytes()`
+ let bs = "hello there".as_bytes();
+ //~^ERROR calling `as_bytes()`
+ //~|HELP byte string literal
+ //~|SUGGESTION b"hello there"
+
// no warning, because this cannot be written as a byte string literal:
let ubs = "☃".as_bytes();
+
+ let strify = stringify!(foobar).as_bytes();
}
fn main() {
diff --git a/util/update_wiki.py b/util/update_wiki.py
index a10b3549a22..cf3421a8228 100755
--- a/util/update_wiki.py
+++ b/util/update_wiki.py
@@ -23,7 +23,7 @@ def parse_path(p="src"):
def parse_conf(p):
c = {}
- with open(p + '/conf.rs') as f:
+ with open(p + '/utils/conf.rs') as f:
f = f.read()
m = re.search(conf_re, f)