Skip to content

Commit 886f475

Browse files
committed
Add new lint: ip_consant
1 parent 5dccb10 commit 886f475

13 files changed

+699
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5888,6 +5888,7 @@ Released 2018-09-13
58885888
[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
58895889
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
58905890
[`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
5891+
[`ip_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#ip_constant
58915892
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
58925893
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
58935894
[`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
379379
crate::methods::INSPECT_FOR_EACH_INFO,
380380
crate::methods::INTO_ITER_ON_REF_INFO,
381381
crate::methods::IO_OTHER_ERROR_INFO,
382+
crate::methods::IP_CONSTANT_INFO,
382383
crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
383384
crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
384385
crate::methods::ITER_CLONED_COLLECT_INFO,

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ extern crate rustc_session;
5555
extern crate rustc_span;
5656
extern crate rustc_target;
5757
extern crate rustc_trait_selection;
58+
extern crate smallvec;
5859
extern crate thin_vec;
5960

6061
#[macro_use]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use clippy_utils::consts::{ConstEvalCtxt, Constant};
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind};
5+
use rustc_lint::LateContext;
6+
use rustc_span::sym;
7+
use smallvec::SmallVec;
8+
9+
use super::IP_CONSTANT;
10+
11+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
12+
if let ExprKind::Path(QPath::TypeRelative(
13+
Ty {
14+
kind: TyKind::Path(QPath::Resolved(_, func_path)),
15+
..
16+
},
17+
p,
18+
)) = func.kind
19+
&& p.ident.name == sym::new
20+
&& let Some(func_def_id) = func_path.res.opt_def_id()
21+
&& matches!(
22+
cx.tcx.get_diagnostic_name(func_def_id),
23+
Some(sym::Ipv4Addr | sym::Ipv6Addr)
24+
)
25+
&& let Some(args) = args
26+
.iter()
27+
.map(|arg| {
28+
if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) {
29+
u8::try_from(constant).ok()
30+
} else {
31+
None
32+
}
33+
})
34+
.collect::<Option<SmallVec<[u8; 8]>>>()
35+
{
36+
let constant_name = match args.as_slice() {
37+
[0, 0, 0, 0] | [0, 0, 0, 0, 0, 0, 0, 0] => "UNSPECIFIED",
38+
[127, 0, 0, 1] | [0, 0, 0, 0, 0, 0, 0, 1] => "LOCALHOST",
39+
[255, 255, 255, 255] => "BROADCAST",
40+
_ => return,
41+
};
42+
43+
span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| {
44+
diag.span_suggestion_verbose(
45+
expr.span.with_lo(p.ident.span.lo()),
46+
"use",
47+
constant_name,
48+
Applicability::MachineApplicable,
49+
);
50+
});
51+
}
52+
}

clippy_lints/src/methods/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod inefficient_to_string;
3737
mod inspect_for_each;
3838
mod into_iter_on_ref;
3939
mod io_other_error;
40+
mod ip_constant;
4041
mod is_digit_ascii_radix;
4142
mod is_empty;
4243
mod iter_cloned_collect;
@@ -4528,6 +4529,42 @@ declare_clippy_lint! {
45284529
"detect swap with a temporary value"
45294530
}
45304531

4532+
declare_clippy_lint! {
4533+
/// ### What it does
4534+
/// Checks for IP addresses that could be replaced with predefined constants such as
4535+
/// `Ipv4Addr::new(127, 0, 0, 1)` instead of using the appropriate constants.
4536+
///
4537+
/// ### Why is this bad?
4538+
/// Using specific IP addresses like `127.0.0.1` or `::1` is less clear and less maintainable than using the
4539+
/// predefined constants `Ipv4Addr::LOCALHOST` or `Ipv6Addr::LOCALHOST`. These constants improve code
4540+
/// readability, make the intent explicit, and are less error-prone.
4541+
///
4542+
/// ### Example
4543+
/// ```no_run
4544+
/// use std::net::{Ipv4Addr, Ipv6Addr};
4545+
///
4546+
/// // IPv4 loopback
4547+
/// let addr_v4 = Ipv4Addr::new(127, 0, 0, 1);
4548+
///
4549+
/// // IPv6 loopback
4550+
/// let addr_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
4551+
/// ```
4552+
/// Use instead:
4553+
/// ```no_run
4554+
/// use std::net::{Ipv4Addr, Ipv6Addr};
4555+
///
4556+
/// // IPv4 loopback
4557+
/// let addr_v4 = Ipv4Addr::LOCALHOST;
4558+
///
4559+
/// // IPv6 loopback
4560+
/// let addr_v6 = Ipv6Addr::LOCALHOST;
4561+
/// ```
4562+
#[clippy::version = "1.89.0"]
4563+
pub IP_CONSTANT,
4564+
style,
4565+
"hardcoded localhost IP address"
4566+
}
4567+
45314568
#[expect(clippy::struct_excessive_bools)]
45324569
pub struct Methods {
45334570
avoid_breaking_exported_api: bool,
@@ -4706,6 +4743,7 @@ impl_lint_pass!(Methods => [
47064743
MANUAL_CONTAINS,
47074744
IO_OTHER_ERROR,
47084745
SWAP_WITH_TEMPORARY,
4746+
IP_CONSTANT,
47094747
]);
47104748

47114749
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4738,6 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
47384776
useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
47394777
io_other_error::check(cx, expr, func, args, self.msrv);
47404778
swap_with_temporary::check(cx, expr, func, args);
4779+
ip_constant::check(cx, expr, func, args);
47414780
},
47424781
ExprKind::MethodCall(method_call, receiver, args, _) => {
47434782
let method_span = method_call.ident.span;

tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![warn(clippy::await_holding_invalid_type)]
2+
#![allow(clippy::ip_constant)]
23
use std::net::Ipv4Addr;
34

45
async fn bad() -> u32 {

tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: holding a disallowed type across an await point `std::string::String`
2-
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
2+
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:6:9
33
|
44
LL | let _x = String::from("hello");
55
| ^^
@@ -9,13 +9,13 @@ LL | let _x = String::from("hello");
99
= help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`
1010

1111
error: holding a disallowed type across an await point `std::net::Ipv4Addr`
12-
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:11:9
12+
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:12:9
1313
|
1414
LL | let x = Ipv4Addr::new(127, 0, 0, 1);
1515
| ^
1616

1717
error: holding a disallowed type across an await point `std::string::String`
18-
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:35:13
18+
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:36:13
1919
|
2020
LL | let _x = String::from("hi!");
2121
| ^^

tests/ui/ip_constant.fixed

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#![warn(clippy::ip_constant)]
2+
#![allow(dead_code)]
3+
#![allow(clippy::identity_op)]
4+
#![allow(clippy::eq_op)]
5+
6+
fn literal_test1() {
7+
use std::net::Ipv4Addr;
8+
let _ = Ipv4Addr::LOCALHOST;
9+
//~^ ip_constant
10+
let _ = Ipv4Addr::BROADCAST;
11+
//~^ ip_constant
12+
let _ = Ipv4Addr::UNSPECIFIED;
13+
//~^ ip_constant
14+
15+
use std::net::Ipv6Addr;
16+
let _ = Ipv6Addr::LOCALHOST;
17+
//~^ ip_constant
18+
let _ = Ipv6Addr::UNSPECIFIED;
19+
//~^ ip_constant
20+
}
21+
22+
fn literal_test2() {
23+
use std::net;
24+
let _ = net::Ipv4Addr::LOCALHOST;
25+
//~^ ip_constant
26+
let _ = net::Ipv4Addr::BROADCAST;
27+
//~^ ip_constant
28+
let _ = net::Ipv4Addr::UNSPECIFIED;
29+
//~^ ip_constant
30+
31+
let _ = net::Ipv6Addr::LOCALHOST;
32+
//~^ ip_constant
33+
let _ = net::Ipv6Addr::UNSPECIFIED;
34+
//~^ ip_constant
35+
}
36+
37+
fn literal_test3() {
38+
let _ = std::net::Ipv4Addr::LOCALHOST;
39+
//~^ ip_constant
40+
let _ = std::net::Ipv4Addr::BROADCAST;
41+
//~^ ip_constant
42+
let _ = std::net::Ipv4Addr::UNSPECIFIED;
43+
//~^ ip_constant
44+
45+
let _ = std::net::Ipv6Addr::LOCALHOST;
46+
//~^ ip_constant
47+
let _ = std::net::Ipv6Addr::UNSPECIFIED;
48+
//~^ ip_constant
49+
}
50+
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;
58+
59+
fn const_test1() {
60+
use std::net::Ipv4Addr;
61+
let _ = Ipv4Addr::LOCALHOST;
62+
//~^ ip_constant
63+
let _ = Ipv4Addr::BROADCAST;
64+
//~^ ip_constant
65+
let _ = Ipv4Addr::UNSPECIFIED;
66+
//~^ ip_constant
67+
68+
use std::net::Ipv6Addr;
69+
let _ = Ipv6Addr::LOCALHOST;
70+
71+
let _ = Ipv6Addr::UNSPECIFIED;
72+
}
73+
74+
fn const_test2() {
75+
use std::net::Ipv4Addr;
76+
let _ = Ipv4Addr::LOCALHOST;
77+
//~^ ip_constant
78+
let _ = Ipv4Addr::BROADCAST;
79+
//~^ ip_constant
80+
let _ = Ipv4Addr::UNSPECIFIED;
81+
//~^ ip_constant
82+
83+
use std::net::Ipv6Addr;
84+
let _ = Ipv6Addr::LOCALHOST;
85+
//~^ ip_constant
86+
let _ = Ipv6Addr::LOCALHOST;
87+
//~^ ip_constant
88+
}
89+
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+
105+
fn main() {
106+
// UI Test
107+
}

0 commit comments

Comments
 (0)