diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee1..0449114b63d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1759,6 +1759,7 @@ Released 2018-09-13 [`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 +[`char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_slices [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a4..d0a785edb7c4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -901,6 +901,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::CAST_REF_TO_MUT, &types::CAST_SIGN_LOSS, &types::CHAR_LIT_AS_U8, + &types::CHAR_SLICES, &types::FN_TO_NUMERIC_CAST, &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, &types::IMPLICIT_HASHER, @@ -1336,6 +1337,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_PRECISION_LOSS), LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_SIGN_LOSS), + LintId::of(&types::CHAR_SLICES), LintId::of(&types::IMPLICIT_HASHER), LintId::of(&types::INVALID_UPCAST_COMPARISONS), LintId::of(&types::LET_UNIT_VALUE), diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index fd74783335d5..6144ab4fb268 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -66,6 +66,34 @@ declare_clippy_lint! { "usage of `Box>`, vector elements are already on the heap" } +declare_clippy_lint! { + /// **What it does:** Checks for use of `&[char]` anywhere in the code. + /// + /// **Why is this bad?** `char` represents a Unicode codepoint, not a + /// character. Usually users of `&[char]` want O(1) indexing on characters, + /// not codepoints. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// struct X { + /// chars: &[char] + /// } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// struct X { + /// chars: &[&str] + /// } + /// ``` + pub CHAR_SLICES, + pedantic, + "usage of `&[char]`, usually you want to index characters instead of codepoints" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. @@ -252,7 +280,7 @@ pub struct Types { vec_box_size_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, CHAR_SLICES, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { @@ -581,6 +609,24 @@ impl Types { "a `VecDeque` might work", ); return; // don't recurse into the type + } else if let TyKind::Slice(s) = hir_ty.kind { + if let TyKind::Path(ref qpath) = s.kind { + if !is_local { + let hir_id = hir_ty.hir_id; + let res = qpath_res(cx, qpath, hir_id); + if let Some(def_id) = res.opt_def_id() { + if Some(def_id) == cx.tcx.lang_items().char_impl() { + span_lint( + cx, + CHAR_SLICES, + hir_ty.span, + "consider using `&[&str]` instead of `&[char]`", + ); + return; // don't recurse into the type + } + } + } + } } } match *qpath { @@ -714,6 +760,25 @@ impl Types { }; self.check_ty(cx, &mut_ty.ty, is_local); }, + TyKind::Slice(ref s) => { + if let TyKind::Path(ref qpath) = s.kind { + if !is_local { + let hir_id = hir_ty.hir_id; + let res = qpath_res(cx, qpath, hir_id); + if let Some(def_id) = res.opt_def_id() { + if Some(def_id) == cx.tcx.lang_items().char_impl() { + span_lint( + cx, + CHAR_SLICES, + hir_ty.span, + "consider using `&[&str]` instead of `&[char]`", + ); + return; // don't recurse into the type + } + } + } + } + }, _ => self.check_ty(cx, &mut_ty.ty, is_local), } } diff --git a/tests/ui/char_slices.rs b/tests/ui/char_slices.rs new file mode 100644 index 000000000000..b16abb8c665d --- /dev/null +++ b/tests/ui/char_slices.rs @@ -0,0 +1,15 @@ +#![warn(clippy::char_slices)] + +fn main() { + let char_slice: &[char] = &['a', 'b']; + takes_char_slice(char_slice); + let char_slice2 = returns_char_slice(); +} + +fn takes_char_slice(char_slice: &[char]) { + println!("{:?}", char_slice) +} + +fn returns_char_slice() -> &'static [char] { + &['c', 'd'] +}