Skip to content

Commit a45c37b

Browse files
committed
support more constants
1 parent 26af329 commit a45c37b

File tree

8 files changed

+392
-148
lines changed

8 files changed

+392
-148
lines changed

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
260260
crate::literal_representation::UNREADABLE_LITERAL_INFO,
261261
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
262262
crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO,
263-
crate::localhost_hardcode::LOCALHOST_HARDCODE_INFO,
264263
crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO,
265264
crate::loops::EMPTY_LOOP_INFO,
266265
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
@@ -398,6 +397,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
398397
crate::methods::ITER_SKIP_ZERO_INFO,
399398
crate::methods::ITER_WITH_DRAIN_INFO,
400399
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
400+
crate::methods::LOCALHOST_HARDCODE_INFO,
401401
crate::methods::MANUAL_CONTAINS_INFO,
402402
crate::methods::MANUAL_C_STR_LITERALS_INFO,
403403
crate::methods::MANUAL_FILTER_MAP_INFO,

clippy_lints/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ mod lifetimes;
199199
mod lines_filter_map_ok;
200200
mod literal_representation;
201201
mod literal_string_with_formatting_args;
202-
mod localhost_hardcode;
203202
mod loops;
204203
mod macro_metavars_in_unsafe;
205204
mod macro_use;
@@ -947,6 +946,5 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
947946
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
948947
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
949948
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
950-
store.register_late_pass(|_| Box::new(localhost_hardcode::LocalhostHardcode));
951949
// add lints here, do not remove this comment, it's used in `new_lint`
952950
}

clippy_lints/src/localhost_hardcode.rs

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use clippy_utils::consts::{ConstEvalCtxt, Constant};
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::source::snippet;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind};
6+
use rustc_lint::LateContext;
7+
use rustc_span::sym;
8+
9+
use super::LOCALHOST_HARDCODE;
10+
11+
static IPV4V6_CONSTANTS: &[(&[u128], &str)] = &[
12+
// Ipv4
13+
(&[127, 0, 0, 1], "LOCALHOST"),
14+
(&[255, 255, 255, 255], "BROADCAST"),
15+
(&[0, 0, 0, 0], "UNSPECIFIED"),
16+
// Ipv6
17+
(&[0, 0, 0, 0, 0, 0, 0, 1], "LOCALHOST"),
18+
(&[0, 0, 0, 0, 0, 0, 0, 0], "UNSPECIFIED"),
19+
];
20+
21+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
22+
if let ExprKind::Path(QPath::TypeRelative(
23+
Ty {
24+
kind: TyKind::Path(QPath::Resolved(_, func_path)),
25+
..
26+
},
27+
p,
28+
)) = func.kind
29+
&& p.ident.as_str() == "new"
30+
&& let Some(func_def_id) = func_path.res.opt_def_id()
31+
&& (cx.tcx.is_diagnostic_item(sym::Ipv4Addr, func_def_id)
32+
|| cx.tcx.is_diagnostic_item(sym::Ipv6Addr, func_def_id))
33+
&& let Some(constant_name) = is_hardcoded_ipv4v6_constant(cx, args)
34+
{
35+
let sugg_snip = format!(
36+
"{}::{}",
37+
snippet(cx, func_path.span, cx.tcx.def_path_str(func_def_id).as_str()),
38+
constant_name
39+
);
40+
41+
span_lint_and_sugg(
42+
cx,
43+
LOCALHOST_HARDCODE,
44+
expr.span,
45+
format!("use `{sugg_snip}` instead"),
46+
"try",
47+
sugg_snip,
48+
Applicability::MachineApplicable,
49+
);
50+
}
51+
}
52+
53+
fn is_hardcoded_ipv4v6_constant(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<&'static str> {
54+
if args.len() != 4 && args.len() != 8 {
55+
return None;
56+
}
57+
58+
// Extract integer constants from arguments
59+
let mut constants = Vec::new();
60+
for arg in args {
61+
if let Some(Constant::Int(constant)) = ConstEvalCtxt::new(cx).eval(arg) {
62+
constants.push(constant);
63+
} else {
64+
return None;
65+
}
66+
}
67+
// Check against known IP constants
68+
for (pattern, name) in IPV4V6_CONSTANTS {
69+
if pattern.len() == constants.len() && pattern.iter().eq(constants.iter()) {
70+
return Some(name);
71+
}
72+
}
73+
74+
None
75+
}

clippy_lints/src/methods/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ mod iter_skip_zero;
5454
mod iter_with_drain;
5555
mod iterator_step_by_zero;
5656
mod join_absolute_paths;
57+
mod localhost_hardcode;
5758
mod manual_c_str_literals;
5859
mod manual_contains;
5960
mod manual_inspect;
@@ -4528,6 +4529,30 @@ declare_clippy_lint! {
45284529
"detect swap with a temporary value"
45294530
}
45304531

4532+
declare_clippy_lint! {
4533+
/// ### What it does
4534+
/// Detects hardcoded localhost IP addresses using `Ipv4Addr::new(127, 0, 0, 1)`.
4535+
///
4536+
/// ### Why is this bad?
4537+
/// Using a hardcoded IP address `(127.0.0.1)` is less clear and maintainable than using the
4538+
/// `Ipv4Addr::LOCALHOST` constant.
4539+
///
4540+
/// ### Example
4541+
/// ```no_run
4542+
/// use std::net::Ipv4Addr;
4543+
/// let addr = Ipv4Addr::new(127, 0, 0, 1);
4544+
/// ```
4545+
/// Use instead:
4546+
/// ```no_run
4547+
/// use std::net::Ipv4Addr;
4548+
/// let addr = Ipv4Addr::LOCALHOST;
4549+
/// ```
4550+
#[clippy::version = "1.89.0"]
4551+
pub LOCALHOST_HARDCODE,
4552+
style,
4553+
"hardcoded localhost IP address"
4554+
}
4555+
45314556
#[expect(clippy::struct_excessive_bools)]
45324557
pub struct Methods {
45334558
avoid_breaking_exported_api: bool,
@@ -4706,6 +4731,7 @@ impl_lint_pass!(Methods => [
47064731
MANUAL_CONTAINS,
47074732
IO_OTHER_ERROR,
47084733
SWAP_WITH_TEMPORARY,
4734+
LOCALHOST_HARDCODE,
47094735
]);
47104736

47114737
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4738,6 +4764,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
47384764
useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
47394765
io_other_error::check(cx, expr, func, args, self.msrv);
47404766
swap_with_temporary::check(cx, expr, func, args);
4767+
localhost_hardcode::check(cx, expr, func, args);
47414768
},
47424769
ExprKind::MethodCall(method_call, receiver, args, _) => {
47434770
let method_span = method_call.ident.span;

tests/ui/localhost_hardcode.fixed

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,112 @@
11
#![warn(clippy::localhost_hardcode)]
22
#![allow(dead_code)]
3+
#![allow(clippy::identity_op)]
4+
#![allow(clippy::eq_op)]
35

46
fn literal_test1() {
57
use std::net::Ipv4Addr;
68
let _ = Ipv4Addr::LOCALHOST;
79
//~^ localhost_hardcode
10+
let _ = Ipv4Addr::BROADCAST;
11+
//~^ localhost_hardcode
12+
let _ = Ipv4Addr::UNSPECIFIED;
13+
//~^ localhost_hardcode
14+
15+
use std::net::Ipv6Addr;
16+
let _ = Ipv6Addr::LOCALHOST;
17+
//~^ localhost_hardcode
18+
let _ = Ipv6Addr::UNSPECIFIED;
19+
//~^ localhost_hardcode
820
}
921

1022
fn literal_test2() {
1123
use std::net;
1224
let _ = net::Ipv4Addr::LOCALHOST;
1325
//~^ localhost_hardcode
26+
let _ = net::Ipv4Addr::BROADCAST;
27+
//~^ localhost_hardcode
28+
let _ = net::Ipv4Addr::UNSPECIFIED;
29+
//~^ localhost_hardcode
30+
31+
let _ = net::Ipv6Addr::LOCALHOST;
32+
//~^ localhost_hardcode
33+
let _ = net::Ipv6Addr::UNSPECIFIED;
34+
//~^ localhost_hardcode
1435
}
1536

1637
fn literal_test3() {
1738
let _ = std::net::Ipv4Addr::LOCALHOST;
1839
//~^ localhost_hardcode
40+
let _ = std::net::Ipv4Addr::BROADCAST;
41+
//~^ localhost_hardcode
42+
let _ = std::net::Ipv4Addr::UNSPECIFIED;
43+
//~^ localhost_hardcode
44+
45+
let _ = std::net::Ipv6Addr::LOCALHOST;
46+
//~^ localhost_hardcode
47+
let _ = std::net::Ipv6Addr::UNSPECIFIED;
48+
//~^ localhost_hardcode
1949
}
2050

21-
const CONSTANT_1: u8 = 127;
22-
const CONSTANT_2: u8 = 0;
23-
const CONSTANT_3: u8 = 0;
24-
const CONSTANT_4: u8 = 1;
51+
const CONST_U8_0: u8 = 0;
52+
const CONST_U8_1: u8 = 1;
53+
const CONST_U8_127: u8 = 127;
54+
const CONST_U8_255: u8 = 255;
55+
56+
const CONST_U16_0: u16 = 0;
57+
const CONST_U16_1: u16 = 1;
2558

2659
fn const_test1() {
2760
use std::net::Ipv4Addr;
2861
let _ = Ipv4Addr::LOCALHOST;
2962
//~^ localhost_hardcode
30-
}
31-
32-
fn const_test2() {
33-
use std::net;
34-
let _ = net::Ipv4Addr::LOCALHOST;
63+
let _ = Ipv4Addr::BROADCAST;
3564
//~^ localhost_hardcode
36-
}
37-
38-
fn const_test3() {
39-
let _ = std::net::Ipv4Addr::LOCALHOST;
65+
let _ = Ipv4Addr::UNSPECIFIED;
4066
//~^ localhost_hardcode
67+
68+
use std::net::Ipv6Addr;
69+
let _ = Ipv6Addr::LOCALHOST;
70+
71+
let _ = Ipv6Addr::UNSPECIFIED;
4172
}
4273

43-
fn const_test4() {
74+
fn const_test2() {
4475
use std::net::Ipv4Addr;
4576
let _ = Ipv4Addr::LOCALHOST;
4677
//~^ localhost_hardcode
47-
let _ = Ipv4Addr::LOCALHOST;
78+
let _ = Ipv4Addr::BROADCAST;
4879
//~^ localhost_hardcode
49-
let _ = Ipv4Addr::LOCALHOST;
80+
let _ = Ipv4Addr::UNSPECIFIED;
5081
//~^ localhost_hardcode
51-
let _ = Ipv4Addr::LOCALHOST;
82+
83+
use std::net::Ipv6Addr;
84+
let _ = Ipv6Addr::LOCALHOST;
85+
//~^ localhost_hardcode
86+
let _ = Ipv6Addr::LOCALHOST;
5287
//~^ localhost_hardcode
5388
}
5489

90+
macro_rules! ipv4_new {
91+
($a:expr, $b:expr, $c:expr, $d:expr) => {
92+
std::net::Ipv4Addr::new($a, $b, $c, $d)
93+
};
94+
}
95+
96+
fn macro_test() {
97+
let _ = ipv4_new!(127, 0, 0, 1);
98+
// no lint
99+
let _ = ipv4_new!(255, 255, 255, 255);
100+
// no lint
101+
let _ = ipv4_new!(0, 0, 0, 0);
102+
// no lint
103+
}
104+
55105
fn main() {
56106
literal_test1();
57107
literal_test2();
58108
literal_test3();
59109
const_test1();
60110
const_test2();
61-
const_test3();
62-
const_test4();
111+
macro_test();
63112
}

0 commit comments

Comments
 (0)