Skip to content

Commit 9e2aab0

Browse files
committed
Merge pull request #767 from mcarton/fix-ice
Fix #765 & #763 & #768
2 parents 893d6e8 + bd5af32 commit 9e2aab0

File tree

8 files changed

+80
-33
lines changed

8 files changed

+80
-33
lines changed

src/array_indexing.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl LateLintPass for ArrayIndexing {
8383
eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None)).map(|v| v.ok());
8484

8585
if let Some((start, end)) = to_const_range(start, end, range.limits, size) {
86-
if start >= size || end >= size {
86+
if start > size || end > size {
8787
utils::span_lint(cx,
8888
OUT_OF_BOUNDS_INDEXING,
8989
e.span,
@@ -109,14 +109,11 @@ impl LateLintPass for ArrayIndexing {
109109
}
110110
}
111111

112-
/// Returns an option containing a tuple with the start and end (exclusive) of the range
113-
///
114-
/// Note: we assume the start and the end of the range are unsigned, since array slicing
115-
/// works only on usize
112+
/// Returns an option containing a tuple with the start and end (exclusive) of the range.
116113
fn to_const_range(start: Option<Option<ConstVal>>,
117-
end: Option<Option<ConstVal>>,
118-
limits: RangeLimits,
119-
array_size: ConstInt)
114+
end: Option<Option<ConstVal>>,
115+
limits: RangeLimits,
116+
array_size: ConstInt)
120117
-> Option<(ConstInt, ConstInt)> {
121118
let start = match start {
122119
Some(Some(ConstVal::Integral(x))) => x,
@@ -127,13 +124,13 @@ fn to_const_range(start: Option<Option<ConstVal>>,
127124
let end = match end {
128125
Some(Some(ConstVal::Integral(x))) => {
129126
if limits == RangeLimits::Closed {
130-
x
127+
(x + ConstInt::Infer(1)).expect("such a big array is not realistic")
131128
} else {
132-
(x - ConstInt::Infer(1)).expect("x > 0")
129+
x
133130
}
134131
}
135132
Some(_) => return None,
136-
None => (array_size - ConstInt::Infer(1)).expect("array_size > 0"),
133+
None => array_size
137134
};
138135

139136
Some((start, end))

src/panic.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc::lint::*;
22
use rustc_front::hir::*;
33
use syntax::ast::LitKind;
4-
use utils::{span_lint, in_external_macro, match_path, BEGIN_UNWIND};
4+
use utils::{span_lint, is_direct_expn_of, match_path, BEGIN_UNWIND};
55

66
/// **What it does:** This lint checks for missing parameters in `panic!`.
77
///
@@ -28,24 +28,20 @@ impl LintPass for PanicPass {
2828
impl LateLintPass for PanicPass {
2929
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
3030
if_let_chain! {[
31-
in_external_macro(cx, expr.span),
3231
let ExprBlock(ref block) = expr.node,
3332
let Some(ref ex) = block.expr,
3433
let ExprCall(ref fun, ref params) = ex.node,
3534
params.len() == 2,
3635
let ExprPath(None, ref path) = fun.node,
3736
match_path(path, &BEGIN_UNWIND),
3837
let ExprLit(ref lit) = params[0].node,
38+
is_direct_expn_of(cx, params[0].span, "panic").is_some(),
3939
let LitKind::Str(ref string, _) = lit.node,
4040
let Some(par) = string.find('{'),
41-
string[par..].contains('}'),
42-
let Some(sp) = cx.sess().codemap()
43-
.with_expn_info(expr.span.expn_id,
44-
|info| info.map(|i| i.call_site))
41+
string[par..].contains('}')
4542
], {
46-
47-
span_lint(cx, PANIC_PARAMS, sp,
48-
"You probably are missing some parameter in your `panic!` call");
43+
span_lint(cx, PANIC_PARAMS, params[0].span,
44+
"you probably are missing some parameter in your format string");
4945
}}
5046
}
5147
}

src/strings.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_front::hir::*;
88
use syntax::codemap::Spanned;
99
use utils::STRING_PATH;
1010
use utils::SpanlessEq;
11-
use utils::{match_type, span_lint, walk_ptrs_ty, get_parent_expr};
11+
use utils::{match_type, span_lint, span_lint_and_then, walk_ptrs_ty, get_parent_expr};
1212

1313
/// **What it does:** This lint matches code of the form `x = x + y` (without `let`!).
1414
///
@@ -140,12 +140,19 @@ impl LateLintPass for StringLitAsBytes {
140140
if name.node.as_str() == "as_bytes" {
141141
if let ExprLit(ref lit) = args[0].node {
142142
if let LitKind::Str(ref lit_content, _) = lit.node {
143-
if lit_content.chars().all(|c| c.is_ascii()) && !in_macro(cx, e.span) {
144-
let msg = format!("calling `as_bytes()` on a string literal. \
145-
Consider using a byte string literal instead: \
146-
`b{}`",
147-
snippet(cx, args[0].span, r#""foo""#));
148-
span_lint(cx, STRING_LIT_AS_BYTES, e.span, &msg);
143+
if lit_content.chars().all(|c| c.is_ascii()) && !in_macro(cx, args[0].span) {
144+
span_lint_and_then(cx,
145+
STRING_LIT_AS_BYTES,
146+
e.span,
147+
"calling `as_bytes()` on a string literal",
148+
|db| {
149+
let sugg = format!("b{}",
150+
snippet(cx, args[0].span, r#""foo""#));
151+
db.span_suggestion(e.span,
152+
"consider using a byte string literal instead",
153+
sugg);
154+
});
155+
149156
}
150157
}
151158
}

src/utils/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
604604
}
605605

606606
/// Return the pre-expansion span if is this comes from an expansion of the macro `name`.
607+
/// See also `is_direct_expn_of`.
607608
pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span> {
608609
loop {
609610
let span_name_span = cx.tcx
@@ -619,6 +620,25 @@ pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span>
619620
}
620621
}
621622

623+
/// Return the pre-expansion span if is this directly comes from an expansion of the macro `name`.
624+
/// The difference with `is_expn_of` is that in
625+
/// ```rust,ignore
626+
/// foo!(bar!(42));
627+
/// ```
628+
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only `bar!` by
629+
/// `is_direct_expn_of`.
630+
pub fn is_direct_expn_of(cx: &LateContext, span: Span, name: &str) -> Option<Span> {
631+
let span_name_span = cx.tcx
632+
.sess
633+
.codemap()
634+
.with_expn_info(span.expn_id, |expn| expn.map(|ei| (ei.callee.name(), ei.call_site)));
635+
636+
match span_name_span {
637+
Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span),
638+
_ => None,
639+
}
640+
}
641+
622642
/// Returns index of character after first CamelCase component of `s`
623643
pub fn camel_case_until(s: &str) -> usize {
624644
let mut iter = s.char_indices();

tests/compile-fail/array_indexing.rs

+14
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ fn main() {
1616
&x[0...4]; //~ERROR: range is out of bounds
1717
&x[..];
1818
&x[1..];
19+
&x[4..];
20+
&x[5..]; //~ERROR: range is out of bounds
1921
&x[..4];
2022
&x[..5]; //~ERROR: range is out of bounds
2123

@@ -24,4 +26,16 @@ fn main() {
2426
&y[1..2]; //~ERROR: slicing may panic
2527
&y[..];
2628
&y[0...4]; //~ERROR: slicing may panic
29+
30+
let empty: [i8; 0] = [];
31+
empty[0]; //~ERROR: const index is out of bounds
32+
&empty[1..5]; //~ERROR: range is out of bounds
33+
&empty[0...4]; //~ERROR: range is out of bounds
34+
&empty[..];
35+
&empty[0..];
36+
&empty[0..0];
37+
&empty[0...0]; //~ERROR: range is out of bounds
38+
&empty[..0];
39+
&empty[1..]; //~ERROR: range is out of bounds
40+
&empty[..4]; //~ERROR: range is out of bounds
2741
}

tests/compile-fail/panic.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
33

4-
#[deny(panic_params)]
4+
#![deny(panic_params)]
55

66
fn missing() {
77
if true {
8-
panic!("{}"); //~ERROR: You probably are missing some parameter
8+
panic!("{}"); //~ERROR: you probably are missing some parameter
9+
} else if false {
10+
panic!("{:?}"); //~ERROR: you probably are missing some parameter
911
} else {
10-
panic!("{:?}"); //~ERROR: You probably are missing some parameter
12+
assert!(true, "here be missing values: {}"); //~ERROR you probably are missing some parameter
1113
}
1214
}
1315

1416
fn ok_single() {
1517
panic!("foo bar");
1618
}
1719

20+
fn ok_inner() {
21+
// Test for #768
22+
assert!("foo bar".contains(&format!("foo {}", "bar")));
23+
}
24+
1825
fn ok_multiple() {
1926
panic!("{}", "This is {ok}");
2027
}
2128

2229
fn ok_bracket() {
23-
// the match is just here because of #759, it serves no other purpose for the lint
2430
match 42 {
2531
1337 => panic!("{so is this"),
2632
666 => panic!("so is this}"),
@@ -33,4 +39,5 @@ fn main() {
3339
ok_single();
3440
ok_multiple();
3541
ok_bracket();
42+
ok_inner();
3643
}

tests/compile-fail/strings.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ fn both() {
4747
#[allow(dead_code, unused_variables)]
4848
#[deny(string_lit_as_bytes)]
4949
fn str_lit_as_bytes() {
50-
let bs = "hello there".as_bytes(); //~ERROR calling `as_bytes()`
50+
let bs = "hello there".as_bytes();
51+
//~^ERROR calling `as_bytes()`
52+
//~|HELP byte string literal
53+
//~|SUGGESTION b"hello there"
54+
5155
// no warning, because this cannot be written as a byte string literal:
5256
let ubs = "☃".as_bytes();
57+
58+
let strify = stringify!(foobar).as_bytes();
5359
}
5460

5561
fn main() {

util/update_wiki.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def parse_path(p="src"):
2323

2424
def parse_conf(p):
2525
c = {}
26-
with open(p + '/conf.rs') as f:
26+
with open(p + '/utils/conf.rs') as f:
2727
f = f.read()
2828

2929
m = re.search(conf_re, f)

0 commit comments

Comments
 (0)