Skip to content

add new lint: ip_constant #14878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5888,6 +5888,7 @@ Released 2018-09-13
[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
[`ip_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#ip_constant
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::INSPECT_FOR_EACH_INFO,
crate::methods::INTO_ITER_ON_REF_INFO,
crate::methods::IO_OTHER_ERROR_INFO,
crate::methods::IP_CONSTANT_INFO,
crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
crate::methods::ITER_CLONED_COLLECT_INFO,
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
extern crate rustc_trait_selection;
extern crate smallvec;
extern crate thin_vec;

#[macro_use]
Expand Down
52 changes: 52 additions & 0 deletions clippy_lints/src/methods/ip_constant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use smallvec::SmallVec;

use super::IP_CONSTANT;

pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
if let ExprKind::Path(QPath::TypeRelative(
Ty {
kind: TyKind::Path(QPath::Resolved(_, func_path)),
..
},
p,
)) = func.kind
&& p.ident.name == sym::new
&& let Some(func_def_id) = func_path.res.opt_def_id()
&& matches!(
cx.tcx.get_diagnostic_name(func_def_id),
Some(sym::Ipv4Addr | sym::Ipv6Addr)
)
&& let Some(args) = args
.iter()
.map(|arg| {
if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) {
u8::try_from(constant).ok()
} else {
None
}
})
.collect::<Option<SmallVec<[u8; 8]>>>()
{
let constant_name = match args.as_slice() {
[0, 0, 0, 0] | [0, 0, 0, 0, 0, 0, 0, 0] => "UNSPECIFIED",
[127, 0, 0, 1] | [0, 0, 0, 0, 0, 0, 0, 1] => "LOCALHOST",
[255, 255, 255, 255] => "BROADCAST",
_ => return,
};

span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| {
diag.span_suggestion_verbose(
expr.span.with_lo(p.ident.span.lo()),
"use",
constant_name,
Applicability::MachineApplicable,
);
});
}
}
39 changes: 39 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod inefficient_to_string;
mod inspect_for_each;
mod into_iter_on_ref;
mod io_other_error;
mod ip_constant;
mod is_digit_ascii_radix;
mod is_empty;
mod iter_cloned_collect;
Expand Down Expand Up @@ -4528,6 +4529,42 @@ declare_clippy_lint! {
"detect swap with a temporary value"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for IP addresses that could be replaced with predefined constants such as
/// `Ipv4Addr::new(127, 0, 0, 1)` instead of using the appropriate constants.
///
/// ### Why is this bad?
/// Using specific IP addresses like `127.0.0.1` or `::1` is less clear and less maintainable than using the
/// predefined constants `Ipv4Addr::LOCALHOST` or `Ipv6Addr::LOCALHOST`. These constants improve code
/// readability, make the intent explicit, and are less error-prone.
///
/// ### Example
/// ```no_run
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// // IPv4 loopback
/// let addr_v4 = Ipv4Addr::new(127, 0, 0, 1);
///
/// // IPv6 loopback
/// let addr_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
/// ```
/// Use instead:
/// ```no_run
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// // IPv4 loopback
/// let addr_v4 = Ipv4Addr::LOCALHOST;
///
/// // IPv6 loopback
/// let addr_v6 = Ipv6Addr::LOCALHOST;
/// ```
#[clippy::version = "1.89.0"]
pub IP_CONSTANT,
pedantic,
"hardcoded localhost IP address"
}

#[expect(clippy::struct_excessive_bools)]
pub struct Methods {
avoid_breaking_exported_api: bool,
Expand Down Expand Up @@ -4706,6 +4743,7 @@ impl_lint_pass!(Methods => [
MANUAL_CONTAINS,
IO_OTHER_ERROR,
SWAP_WITH_TEMPORARY,
IP_CONSTANT,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -4738,6 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
io_other_error::check(cx, expr, func, args, self.msrv);
swap_with_temporary::check(cx, expr, func, args);
ip_constant::check(cx, expr, func, args);
},
ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(clippy::await_holding_invalid_type)]
#![allow(clippy::ip_constant)]
use std::net::Ipv4Addr;

async fn bad() -> u32 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: holding a disallowed type across an await point `std::string::String`
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:6:9
|
LL | let _x = String::from("hello");
| ^^
Expand All @@ -9,13 +9,13 @@ LL | let _x = String::from("hello");
= help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`

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

error: holding a disallowed type across an await point `std::string::String`
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:35:13
--> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:36:13
|
LL | let _x = String::from("hi!");
| ^^
Expand Down
107 changes: 107 additions & 0 deletions tests/ui/ip_constant.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#![warn(clippy::ip_constant)]
#![allow(dead_code)]
#![allow(clippy::identity_op)]
#![allow(clippy::eq_op)]

fn literal_test1() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = Ipv4Addr::UNSPECIFIED;
//~^ ip_constant

use std::net::Ipv6Addr;
let _ = Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv6Addr::UNSPECIFIED;
//~^ ip_constant
}

fn literal_test2() {
use std::net;
let _ = net::Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = net::Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = net::Ipv4Addr::UNSPECIFIED;
//~^ ip_constant

let _ = net::Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = net::Ipv6Addr::UNSPECIFIED;
//~^ ip_constant
}

fn literal_test3() {
let _ = std::net::Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = std::net::Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = std::net::Ipv4Addr::UNSPECIFIED;
//~^ ip_constant

let _ = std::net::Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = std::net::Ipv6Addr::UNSPECIFIED;
//~^ ip_constant
}

const CONST_U8_0: u8 = 0;
const CONST_U8_1: u8 = 1;
const CONST_U8_127: u8 = 127;
const CONST_U8_255: u8 = 255;

const CONST_U16_0: u16 = 0;
const CONST_U16_1: u16 = 1;

fn const_test1() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = Ipv4Addr::UNSPECIFIED;
//~^ ip_constant

use std::net::Ipv6Addr;
let _ = Ipv6Addr::LOCALHOST;

let _ = Ipv6Addr::UNSPECIFIED;
}

fn const_test2() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = Ipv4Addr::UNSPECIFIED;
//~^ ip_constant

use std::net::Ipv6Addr;
let _ = Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv6Addr::LOCALHOST;
//~^ ip_constant
}

macro_rules! ipv4_new {
($a:expr, $b:expr, $c:expr, $d:expr) => {
std::net::Ipv4Addr::new($a, $b, $c, $d)
};
}

fn macro_test() {
let _ = ipv4_new!(127, 0, 0, 1);
// no lint
let _ = ipv4_new!(255, 255, 255, 255);
// no lint
let _ = ipv4_new!(0, 0, 0, 0);
// no lint
}

fn main() {
// UI Test
}
Loading