Skip to content

Commit be7f95f

Browse files
authored
Merge branch 'master' into master
2 parents 0c767ea + 2466a05 commit be7f95f

File tree

84 files changed

+3046
-520
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+3046
-520
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3276,6 +3276,7 @@ Released 2018-09-13
32763276
<!-- begin autogenerated links to lint list -->
32773277
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
32783278
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
3279+
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
32793280
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
32803281
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
32813282
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
@@ -3362,6 +3363,7 @@ Released 2018-09-13
33623363
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
33633364
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
33643365
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
3366+
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
33653367
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
33663368
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
33673369
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ filetime = "0.2"
4040
rustc-workspace-hack = "1.0"
4141

4242
# UI test dependencies
43+
clap = { version = "3.1", features = ["derive"] }
4344
clippy_utils = { path = "clippy_utils" }
4445
derive-new = "0.5"
4546
if_chain = "1.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::{trim_span, walk_span_to_context};
3+
use clippy_utils::{meets_msrv, msrvs};
4+
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
5+
use rustc_errors::Applicability;
6+
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
7+
use rustc_semver::RustcVersion;
8+
use rustc_session::{declare_tool_lint, impl_lint_pass};
9+
use rustc_span::Span;
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
14+
/// don't because they're a half open range.
15+
///
16+
/// ### Why is this bad?
17+
/// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// let _ = 'a'..'z';
22+
/// ```
23+
/// Use instead:
24+
/// ```rust
25+
/// let _ = 'a'..='z';
26+
/// ```
27+
#[clippy::version = "1.63.0"]
28+
pub ALMOST_COMPLETE_LETTER_RANGE,
29+
suspicious,
30+
"almost complete letter range"
31+
}
32+
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
33+
34+
pub struct AlmostCompleteLetterRange {
35+
msrv: Option<RustcVersion>,
36+
}
37+
impl AlmostCompleteLetterRange {
38+
pub fn new(msrv: Option<RustcVersion>) -> Self {
39+
Self { msrv }
40+
}
41+
}
42+
impl EarlyLintPass for AlmostCompleteLetterRange {
43+
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
44+
if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
45+
let ctxt = e.span.ctxt();
46+
let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
47+
&& let Some(end) = walk_span_to_context(end.span, ctxt)
48+
&& meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
49+
{
50+
Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
51+
} else {
52+
None
53+
};
54+
check_range(cx, e.span, start, end, sugg);
55+
}
56+
}
57+
58+
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
59+
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
60+
&& matches!(kind.node, RangeEnd::Excluded)
61+
{
62+
let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
63+
"..="
64+
} else {
65+
"..."
66+
};
67+
check_range(cx, p.span, start, end, Some((kind.span, sugg)));
68+
}
69+
}
70+
71+
extract_msrv_attr!(EarlyContext);
72+
}
73+
74+
fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
75+
if let ExprKind::Lit(start_lit) = &start.peel_parens().kind
76+
&& let ExprKind::Lit(end_lit) = &end.peel_parens().kind
77+
&& matches!(
78+
(&start_lit.kind, &end_lit.kind),
79+
(LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z'))
80+
| (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z'))
81+
)
82+
{
83+
span_lint_and_then(
84+
cx,
85+
ALMOST_COMPLETE_LETTER_RANGE,
86+
span,
87+
"almost complete ascii letter range",
88+
|diag| {
89+
if let Some((span, sugg)) = sugg {
90+
diag.span_suggestion(
91+
span,
92+
"use an inclusive range",
93+
sugg.to_owned(),
94+
Applicability::MaybeIncorrect,
95+
);
96+
}
97+
}
98+
);
99+
}
100+
}

clippy_lints/src/attrs.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -585,15 +585,21 @@ impl EarlyLintPass for EarlyAttributes {
585585
}
586586

587587
fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
588-
for attr in &item.attrs {
588+
let mut iter = item.attrs.iter().peekable();
589+
while let Some(attr) = iter.next() {
589590
if matches!(attr.kind, AttrKind::Normal(..))
590591
&& attr.style == AttrStyle::Outer
591592
&& is_present_in_source(cx, attr.span)
592593
{
593594
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
594-
let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent());
595+
let end_of_attr_to_next_attr_or_item = Span::new(
596+
attr.span.hi(),
597+
iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
598+
item.span.ctxt(),
599+
item.span.parent(),
600+
);
595601

596-
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
602+
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
597603
let lines = snippet.split('\n').collect::<Vec<_>>();
598604
let lines = without_block_comments(lines);
599605

@@ -623,8 +629,15 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti
623629
if feature_item.has_name(sym::rustfmt);
624630
// check for `rustfmt_skip` and `rustfmt::skip`
625631
if let Some(skip_item) = &items[1].meta_item();
626-
if skip_item.has_name(sym!(rustfmt_skip)) ||
627-
skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
632+
if skip_item.has_name(sym!(rustfmt_skip))
633+
|| skip_item
634+
.path
635+
.segments
636+
.last()
637+
.expect("empty path in attribute")
638+
.ident
639+
.name
640+
== sym::skip;
628641
// Only lint outer attributes, because custom inner attributes are unstable
629642
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
630643
if attr.style == AttrStyle::Outer;

clippy_lints/src/dbg_macro.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function};
55
use rustc_errors::Applicability;
66
use rustc_hir::{Expr, ExprKind};
77
use rustc_lint::{LateContext, LateLintPass};
8-
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_session::{declare_tool_lint, impl_lint_pass};
99
use rustc_span::sym;
1010

1111
declare_clippy_lint! {
@@ -30,14 +30,27 @@ declare_clippy_lint! {
3030
"`dbg!` macro is intended as a debugging tool"
3131
}
3232

33-
declare_lint_pass!(DbgMacro => [DBG_MACRO]);
33+
#[derive(Copy, Clone)]
34+
pub struct DbgMacro {
35+
allow_dbg_in_tests: bool,
36+
}
37+
38+
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
39+
40+
impl DbgMacro {
41+
pub fn new(allow_dbg_in_tests: bool) -> Self {
42+
DbgMacro { allow_dbg_in_tests }
43+
}
44+
}
3445

3546
impl LateLintPass<'_> for DbgMacro {
3647
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
3748
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
3849
if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) {
39-
// we make an exception for test code
40-
if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
50+
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
51+
if self.allow_dbg_in_tests
52+
&& (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id))
53+
{
4154
return;
4255
}
4356
let mut applicability = Applicability::MachineApplicable;
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use itertools::Itertools;
3+
use rustc_ast::{AttrKind, Attribute};
4+
use rustc_lint::{EarlyContext, EarlyLintPass};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
7+
declare_clippy_lint! {
8+
/// ### What it does
9+
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
10+
/// outside of code blocks
11+
/// ### Why is this bad?
12+
/// It is likely a typo when defining an intra-doc link
13+
///
14+
/// ### Example
15+
/// ```rust
16+
/// /// See also: ['foo']
17+
/// fn bar() {}
18+
/// ```
19+
/// Use instead:
20+
/// ```rust
21+
/// /// See also: [`foo`]
22+
/// fn bar() {}
23+
/// ```
24+
#[clippy::version = "1.60.0"]
25+
pub DOC_LINK_WITH_QUOTES,
26+
pedantic,
27+
"possible typo for an intra-doc link"
28+
}
29+
declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
30+
31+
impl EarlyLintPass for DocLinkWithQuotes {
32+
fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
33+
if let AttrKind::DocComment(_, symbol) = attr.kind {
34+
if contains_quote_link(symbol.as_str()) {
35+
span_lint(
36+
ctx,
37+
DOC_LINK_WITH_QUOTES,
38+
attr.span,
39+
"possible intra-doc link using quotes instead of backticks",
40+
);
41+
}
42+
}
43+
}
44+
}
45+
46+
fn contains_quote_link(s: &str) -> bool {
47+
let mut in_backticks = false;
48+
let mut found_opening = false;
49+
50+
for c in s.chars().tuple_windows::<(char, char)>() {
51+
match c {
52+
('`', _) => in_backticks = !in_backticks,
53+
('[', '\'') if !in_backticks => found_opening = true,
54+
('\'', ']') if !in_backticks && found_opening => return true,
55+
_ => {},
56+
}
57+
}
58+
59+
false
60+
}

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
store.register_group(true, "clippy::all", Some("clippy_all"), vec![
66
LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
7+
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
78
LintId::of(approx_const::APPROX_CONSTANT),
89
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
910
LintId::of(assign_ops::ASSIGN_OP_PATTERN),

clippy_lints/src/lib.register_lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ store.register_lints(&[
3434
#[cfg(feature = "internal")]
3535
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
3636
absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
37+
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
3738
approx_const::APPROX_CONSTANT,
3839
arithmetic::FLOAT_ARITHMETIC,
3940
arithmetic::INTEGER_ARITHMETIC,
@@ -124,6 +125,7 @@ store.register_lints(&[
124125
doc::MISSING_PANICS_DOC,
125126
doc::MISSING_SAFETY_DOC,
126127
doc::NEEDLESS_DOCTEST_MAIN,
128+
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
127129
double_comparison::DOUBLE_COMPARISONS,
128130
double_parens::DOUBLE_PARENS,
129131
drop_forget_ref::DROP_COPY,

clippy_lints/src/lib.register_pedantic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
2626
LintId::of(doc::DOC_MARKDOWN),
2727
LintId::of(doc::MISSING_ERRORS_DOC),
2828
LintId::of(doc::MISSING_PANICS_DOC),
29+
LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
2930
LintId::of(empty_enum::EMPTY_ENUM),
3031
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
3132
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),

clippy_lints/src/lib.register_suspicious.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Manual edits will be overwritten.
44

55
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
6+
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
67
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
78
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
89
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),

clippy_lints/src/lib.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ mod renamed_lints;
168168

169169
// begin lints modules, do not remove this comment, it’s used in `update_lints`
170170
mod absurd_extreme_comparisons;
171+
mod almost_complete_letter_range;
171172
mod approx_const;
172173
mod arithmetic;
173174
mod as_conversions;
@@ -208,6 +209,7 @@ mod disallowed_methods;
208209
mod disallowed_script_idents;
209210
mod disallowed_types;
210211
mod doc;
212+
mod doc_link_with_quotes;
211213
mod double_comparison;
212214
mod double_parens;
213215
mod drop_forget_ref;
@@ -887,9 +889,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
887889
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
888890
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
889891
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
892+
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
890893
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
891894
store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee));
892-
store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
895+
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
896+
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
893897
let cargo_ignore_publish = conf.cargo_ignore_publish;
894898
store.register_late_pass(move || {
895899
Box::new(cargo::Cargo {
@@ -909,6 +913,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
909913
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
910914
store.register_late_pass(|| Box::new(get_first::GetFirst));
911915
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
916+
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
912917
store.register_late_pass(|| Box::new(path_from_format::PathFromFormat));
913918
// add lints here, do not remove this comment, it's used in `new_lint`
914919
}

clippy_lints/src/needless_late_init.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,15 @@ fn assignment_suggestions<'tcx>(
194194
}))
195195
.collect::<Option<Vec<(Span, String)>>>()?;
196196

197-
let applicability = if suggestions.len() > 1 {
197+
match suggestions.len() {
198+
// All of `exprs` are never types
199+
// https://github.com/rust-lang/rust-clippy/issues/8911
200+
0 => None,
201+
1 => Some((Applicability::MachineApplicable, suggestions)),
198202
// multiple suggestions don't work with rustfix in multipart_suggest
199203
// https://github.com/rust-lang/rustfix/issues/141
200-
Applicability::Unspecified
201-
} else {
202-
Applicability::MachineApplicable
203-
};
204-
Some((applicability, suggestions))
204+
_ => Some((Applicability::Unspecified, suggestions)),
205+
}
205206
}
206207

207208
struct Usage<'tcx> {

0 commit comments

Comments
 (0)