11use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
2- use clippy_utils:: diagnostics:: span_lint_and_sugg;
3- use clippy_utils:: source:: snippet;
2+ use clippy_utils:: diagnostics:: span_lint_and_then;
43use rustc_errors:: Applicability ;
54use rustc_hir:: { Expr , ExprKind , QPath , Ty , TyKind } ;
65use rustc_lint:: LateContext ;
76use rustc_span:: sym;
7+ use smallvec:: SmallVec ;
88
99use super :: IP_CONSTANT ;
1010
@@ -16,109 +16,37 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
1616 } ,
1717 p,
1818 ) ) = func. kind
19- && p. ident . as_str ( ) == " new"
19+ && p. ident . name == sym :: new
2020 && let Some ( func_def_id) = func_path. res . opt_def_id ( )
21- && ( cx. tcx . is_diagnostic_item ( sym:: Ipv4Addr , func_def_id)
22- || cx. tcx . is_diagnostic_item ( sym:: Ipv6Addr , func_def_id) )
23- && let Some ( constant_name) = is_ipaddr_constants ( cx, args)
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 ] > > > ( )
2435 {
25- let sugg_snip = format ! (
26- "{}::{} ",
27- snippet ( cx , func_path . span , cx . tcx . def_path_str ( func_def_id ) . as_str ( ) ) ,
28- constant_name
29- ) ;
30-
31- span_lint_and_sugg (
32- cx ,
33- IP_CONSTANT ,
34- expr. span ,
35- format ! ( "use `{sugg_snip}` instead" ) ,
36- "try" ,
37- sugg_snip ,
38- Applicability :: MachineApplicable ,
39- ) ;
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+ } ) ;
4051 }
4152}
42-
43- struct Node {
44- children : & ' static [ ( u128 , Node ) ] ,
45- constant_name : Option < & ' static str > ,
46- }
47-
48- impl Node {
49- const fn new ( children : & ' static [ ( u128 , Node ) ] , constant_name : Option < & ' static str > ) -> Self {
50- Self {
51- children,
52- constant_name,
53- }
54- }
55-
56- const fn leaf ( constant_name : & ' static str ) -> Self {
57- Self {
58- children : & [ ] ,
59- constant_name : Some ( constant_name) ,
60- }
61- }
62- }
63-
64- // Tree structure for IP constants
65- #[ rustfmt:: skip]
66- static IPADDR_CONSTANTS_TREE : Node = Node :: new ( & [
67- ( 127 , Node :: new ( & [
68- ( 0 , Node :: new ( & [
69- ( 0 , Node :: new ( & [
70- ( 1 , Node :: leaf ( "LOCALHOST" ) ) // IPv4 127.0.0.1
71- ] , None ) )
72- ] , None ) )
73- ] , None ) ) ,
74- ( 255 , Node :: new ( & [
75- ( 255 , Node :: new ( & [
76- ( 255 , Node :: new ( & [
77- ( 255 , Node :: leaf ( "BROADCAST" ) ) // IPv4 255.255.255.255
78- ] , None ) )
79- ] , None ) )
80- ] , None ) ) ,
81- ( 0 , Node :: new ( & [
82- ( 0 , Node :: new ( & [
83- ( 0 , Node :: new ( & [
84- ( 0 , Node :: new ( & [
85- ( 0 , Node :: new ( & [
86- ( 0 , Node :: new ( & [
87- ( 0 , Node :: new ( & [
88- ( 0 , Node :: leaf ( "UNSPECIFIED" ) ) , // IPv6 ::
89- ( 1 , Node :: leaf ( "LOCALHOST" ) ) // IPv6 ::1
90- ] , None ) )
91- ] , None ) )
92- ] , None ) )
93- ] , Some ( "UNSPECIFIED" ) ) ) // IPv4 0.0.0.0
94- ] , None ) )
95- ] , None ) )
96- ] , None ) ) ,
97- ] , None ) ;
98-
99- fn is_ipaddr_constants ( cx : & LateContext < ' _ > , args : & [ Expr < ' _ > ] ) -> Option < & ' static str > {
100- if args. len ( ) != 4 && args. len ( ) != 8 {
101- return None ;
102- }
103-
104- // Extract integer constants from arguments
105- let mut constants = Vec :: new ( ) ;
106- for arg in args {
107- if let Some ( Constant :: Int ( constant) ) = ConstEvalCtxt :: new ( cx) . eval ( arg) {
108- constants. push ( constant) ;
109- } else {
110- return None ;
111- }
112- }
113-
114- let mut current_node = & IPADDR_CONSTANTS_TREE ;
115- for value in constants {
116- if let Some ( ( _, next_node) ) = current_node. children . iter ( ) . find ( |( val, _) | * val == value) {
117- current_node = next_node;
118- } else {
119- return None ; // Early termination on mismatch
120- }
121- }
122-
123- current_node. constant_name
124- }
0 commit comments