2021-06-03 01:41:37 -05:00
|
|
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
|
|
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
|
|
|
use clippy_utils::sugg::Sugg;
|
|
|
|
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
|
|
|
|
use clippy_utils::{is_expr_path_def_path, paths};
|
|
|
|
use if_chain::if_chain;
|
|
|
|
use rustc_ast::LitKind;
|
|
|
|
use rustc_errors::Applicability;
|
|
|
|
use rustc_hir::{Expr, ExprKind, LangItem};
|
|
|
|
use rustc_lint::LateContext;
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-24 21:13:38 -06:00
|
|
|
use rustc_middle::ty::{self, Ty};
|
2021-06-03 01:41:37 -05:00
|
|
|
use rustc_span::symbol::sym;
|
|
|
|
use std::borrow::Cow;
|
|
|
|
|
|
|
|
use super::MANUAL_STR_REPEAT;
|
|
|
|
|
|
|
|
enum RepeatKind {
|
|
|
|
String,
|
|
|
|
Char(char),
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_ty_param(ty: Ty<'_>) -> Option<Ty<'_>> {
|
|
|
|
if let ty::Adt(_, subs) = ty.kind() {
|
|
|
|
subs.types().next()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
|
|
|
|
if let ExprKind::Lit(lit) = &e.kind {
|
|
|
|
match lit.node {
|
|
|
|
LitKind::Str(..) => Some(RepeatKind::String),
|
|
|
|
LitKind::Char(c) => Some(RepeatKind::Char(c)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let ty = cx.typeck_results().expr_ty(e);
|
2021-10-02 18:51:01 -05:00
|
|
|
if is_type_diagnostic_item(cx, ty, sym::String)
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-24 21:13:38 -06:00
|
|
|
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
|
|
|
|
|| (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str))
|
2021-06-03 01:41:37 -05:00
|
|
|
{
|
|
|
|
Some(RepeatKind::String)
|
|
|
|
} else {
|
|
|
|
let ty = ty.peel_refs();
|
2021-10-02 18:51:01 -05:00
|
|
|
(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then(|| RepeatKind::String)
|
2021-06-03 01:41:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn check(
|
|
|
|
cx: &LateContext<'_>,
|
|
|
|
collect_expr: &Expr<'_>,
|
|
|
|
take_expr: &Expr<'_>,
|
|
|
|
take_self_arg: &Expr<'_>,
|
|
|
|
take_arg: &Expr<'_>,
|
|
|
|
) {
|
|
|
|
if_chain! {
|
|
|
|
if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
|
|
|
|
if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT);
|
2021-10-02 18:51:01 -05:00
|
|
|
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String);
|
2021-06-03 01:41:37 -05:00
|
|
|
if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
|
|
|
|
if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
|
|
|
|
if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
|
|
|
if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id);
|
|
|
|
if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id);
|
|
|
|
if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg);
|
|
|
|
let ctxt = collect_expr.span.ctxt();
|
|
|
|
if ctxt == take_expr.span.ctxt();
|
|
|
|
if ctxt == take_self_arg.span.ctxt();
|
|
|
|
then {
|
|
|
|
let mut app = Applicability::MachineApplicable;
|
|
|
|
let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0;
|
|
|
|
|
|
|
|
let val_str = match repeat_kind {
|
|
|
|
RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return,
|
|
|
|
RepeatKind::Char('\'') => r#""'""#.into(),
|
|
|
|
RepeatKind::Char('"') => r#""\"""#.into(),
|
|
|
|
RepeatKind::Char(_) =>
|
|
|
|
match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) {
|
|
|
|
Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])),
|
|
|
|
s @ Cow::Borrowed(_) => s,
|
|
|
|
},
|
|
|
|
RepeatKind::String =>
|
|
|
|
Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(),
|
|
|
|
};
|
|
|
|
|
|
|
|
span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
MANUAL_STR_REPEAT,
|
|
|
|
collect_expr.span,
|
|
|
|
"manual implementation of `str::repeat` using iterators",
|
|
|
|
"try this",
|
|
|
|
format!("{}.repeat({})", val_str, count_snip),
|
|
|
|
app
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|