From e1bd7488d0fce970278a59b670154e850b3df99d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 25 Feb 2024 07:57:11 -0800 Subject: [PATCH] all: remove LenIter, use Go 1.22 range-over-int instead Updates #11058 Updates golang/go#65685 Change-Id: Ibb216b346e511d486271ab3d84e4546c521e4e22 Signed-off-by: Brad Fitzpatrick --- cmd/tsconnect/wasm/wasm_js.go | 4 ++-- control/controlclient/map.go | 2 +- ipn/ipnlocal/local.go | 34 ++++++++++++++++----------------- ipn/ipnlocal/network-lock.go | 2 +- ipn/ipnlocal/serve.go | 2 +- ipn/ipnlocal/web_client.go | 2 +- net/tsaddr/tsaddr.go | 8 ++++---- net/tsdial/dnsmap.go | 4 ++-- tsnet/tsnet.go | 2 +- types/views/views.go | 15 --------------- types/views/views_test.go | 21 -------------------- wgengine/magicsock/debughttp.go | 2 +- wgengine/magicsock/endpoint.go | 4 ++-- wgengine/magicsock/magicsock.go | 6 +++--- wgengine/netstack/netstack.go | 4 ++-- wgengine/pendopen.go | 2 +- wgengine/userspace.go | 8 ++++---- wgengine/wgcfg/nmcfg/nmcfg.go | 4 ++-- 18 files changed, 45 insertions(+), 81 deletions(-) diff --git a/cmd/tsconnect/wasm/wasm_js.go b/cmd/tsconnect/wasm/wasm_js.go index 93e8d9798..5c826d1a3 100644 --- a/cmd/tsconnect/wasm/wasm_js.go +++ b/cmd/tsconnect/wasm/wasm_js.go @@ -264,7 +264,7 @@ func (i *jsIPN) run(jsCallbacks js.Value) { name = p.Hostinfo().Hostname() } addrs := make([]string, p.Addresses().Len()) - for i := range p.Addresses().LenIter() { + for i := range p.Addresses().Len() { addrs[i] = p.Addresses().At(i).Addr().String() } return jsNetMapPeerNode{ @@ -582,7 +582,7 @@ func mapSlice[T any, M any](a []T, f func(T) M) []M { func mapSliceView[T any, M any](a views.Slice[T], f func(T) M) []M { n := make([]M, a.Len()) - for i := range a.LenIter() { + for i := range a.Len() { n[i] = f(a.At(i)) } return n diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 6231d4b64..d44310028 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -728,7 +728,7 @@ func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChang return nil, false } - for i := range va.LenIter() { + for i := range va.Len() { if !va.At(i).Equal(vb.At(i)) { return nil, false } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index b8aa769a1..d99ce189c 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -792,7 +792,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) { var tailscaleIPs []netip.Addr if b.netMap != nil { addrs := b.netMap.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { if addr := addrs.At(i); addr.IsSingleIP() { sb.AddTailscaleIP(addr.Addr()) tailscaleIPs = append(tailscaleIPs, addr.Addr()) @@ -856,7 +856,7 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) { lastSeen = *p.LastSeen() } tailscaleIPs := make([]netip.Addr, 0, p.Addresses().Len()) - for i := range p.Addresses().LenIter() { + for i := range p.Addresses().Len() { addr := p.Addresses().At(i) if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) { tailscaleIPs = append(tailscaleIPs, addr.Addr()) @@ -977,7 +977,7 @@ func (b *LocalBackend) peerCapsLocked(src netip.Addr) tailcfg.PeerCapMap { return nil } addrs := b.netMap.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { a := addrs.At(i) if !a.IsSingleIP() { continue @@ -1433,7 +1433,7 @@ func setExitNodeID(prefs *ipn.Prefs, nm *netmap.NetworkMap) (prefsChanged bool) } for _, peer := range nm.Peers { - for i := range peer.Addresses().LenIter() { + for i := range peer.Addresses().Len() { addr := peer.Addresses().At(i) if !addr.IsSingleIP() || addr.Addr() != prefs.ExitNodeIP { continue @@ -1877,7 +1877,7 @@ func (b *LocalBackend) updateFilterLocked(netMap *netmap.NetworkMap, prefs ipn.P logNetsB.RemovePrefix(tsaddr.ChromeOSVMRange()) if haveNetmap { addrs = netMap.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { localNetsB.AddPrefix(addrs.At(i)) } packetFilter = netMap.PacketFilter @@ -1987,7 +1987,7 @@ func packetFilterPermitsUnlockedNodes(peers map[tailcfg.NodeID]tailcfg.NodeView, continue } numUnlocked++ - for i := range p.AllowedIPs().LenIter() { // not only addresses! + for i := range p.AllowedIPs().Len() { // not only addresses! b.AddPrefix(p.AllowedIPs().At(i)) } } @@ -3640,14 +3640,14 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg. return // TODO: propagate error? } var have4 bool - for i := range addrs.LenIter() { + for i := range addrs.Len() { if addrs.At(i).Addr().Is4() { have4 = true break } } var ips []netip.Addr - for i := range addrs.LenIter() { + for i := range addrs.Len() { addr := addrs.At(i) if selfV6Only { if addr.Addr().Is6() { @@ -3936,7 +3936,7 @@ func (b *LocalBackend) initPeerAPIListener() { b.peerAPIServer = ps isNetstack := b.sys.IsNetstack() - for i := range addrs.LenIter() { + for i := range addrs.Len() { a := addrs.At(i) var ln net.Listener var err error @@ -4250,7 +4250,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State) { case ipn.Running: var addrStrs []string addrs := netMap.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { addrStrs = append(addrStrs, addrs.At(i).Addr().String()) } systemd.Status("Connected; %s; %s", activeLogin, strings.Join(addrStrs, " ")) @@ -4626,7 +4626,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) { b.nodeByAddr[k] = 0 } addNode := func(n tailcfg.NodeView) { - for i := range n.Addresses().LenIter() { + for i := range n.Addresses().Len() { if ipp := n.Addresses().At(i); ipp.IsSingleIP() { b.nodeByAddr[ipp.Addr()] = n.ID() } @@ -5062,7 +5062,7 @@ func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error { func peerAPIPorts(peer tailcfg.NodeView) (p4, p6 uint16) { svcs := peer.Hostinfo().Services() - for i := range svcs.LenIter() { + for i := range svcs.Len() { s := svcs.At(i) switch s.Proto { case tailcfg.PeerAPI4: @@ -5095,7 +5095,7 @@ func peerAPIBase(nm *netmap.NetworkMap, peer tailcfg.NodeView) string { var have4, have6 bool addrs := nm.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { a := addrs.At(i) if !a.IsSingleIP() { continue @@ -5118,7 +5118,7 @@ func peerAPIBase(nm *netmap.NetworkMap, peer tailcfg.NodeView) string { } func nodeIP(n tailcfg.NodeView, pred func(netip.Addr) bool) netip.Addr { - for i := range n.Addresses().LenIter() { + for i := range n.Addresses().Len() { a := n.Addresses().At(i) if a.IsSingleIP() && pred(a.Addr()) { return a.Addr() @@ -5296,7 +5296,7 @@ func wireguardExitNodeDNSResolvers(nm *netmap.NetworkMap, peers map[tailcfg.Node resolvers := p.ExitNodeDNSResolvers() if !resolvers.IsNil() && resolvers.Len() > 0 { copies := make([]*dnstype.Resolver, resolvers.Len()) - for i := range resolvers.LenIter() { + for i := range resolvers.Len() { copies[i] = resolvers.At(i).AsStruct() } return copies, true @@ -5319,7 +5319,7 @@ func peerCanProxyDNS(p tailcfg.NodeView) bool { // If p.Cap is not populated (e.g. older control server), then do the old // thing of searching through services. services := p.Hostinfo().Services() - for i := range services.LenIter() { + for i := range services.Len() { if s := services.At(i); s.Proto == tailcfg.PeerAPIDNS && s.Port >= 1 { return true } @@ -5495,7 +5495,7 @@ func (b *LocalBackend) handleQuad100Port80Conn(w http.ResponseWriter, r *http.Re return } io.WriteString(w, "

Local addresses:

\n") diff --git a/ipn/ipnlocal/network-lock.go b/ipn/ipnlocal/network-lock.go index cfe33147b..7f158e81d 100644 --- a/ipn/ipnlocal/network-lock.go +++ b/ipn/ipnlocal/network-lock.go @@ -100,7 +100,7 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) { TailscaleIPs: make([]netip.Addr, p.Addresses().Len()), NodeKey: p.Key(), } - for i := range p.Addresses().LenIter() { + for i := range p.Addresses().Len() { addr := p.Addresses().At(i) if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) { fp.TailscaleIPs[i] = addr.Addr() diff --git a/ipn/ipnlocal/serve.go b/ipn/ipnlocal/serve.go index d1ed1ef8a..55f1b02ec 100644 --- a/ipn/ipnlocal/serve.go +++ b/ipn/ipnlocal/serve.go @@ -222,7 +222,7 @@ func (b *LocalBackend) updateServeTCPPortNetMapAddrListenersLocked(ports []uint1 } addrs := nm.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { a := addrs.At(i) for _, p := range ports { addrPort := netip.AddrPortFrom(a.Addr(), p) diff --git a/ipn/ipnlocal/web_client.go b/ipn/ipnlocal/web_client.go index ee919d976..ccde9f01d 100644 --- a/ipn/ipnlocal/web_client.go +++ b/ipn/ipnlocal/web_client.go @@ -121,7 +121,7 @@ func (b *LocalBackend) updateWebClientListenersLocked() { } addrs := b.netMap.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { addrPort := netip.AddrPortFrom(addrs.At(i).Addr(), webClientPort) if _, ok := b.webClientListeners[addrPort]; ok { continue // already listening diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go index 93a720b01..126f6302a 100644 --- a/net/tsaddr/tsaddr.go +++ b/net/tsaddr/tsaddr.go @@ -203,7 +203,7 @@ func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool } // General case: m := map[netip.Addr]bool{} - for i := range addrs.LenIter() { + for i := range addrs.Len() { m[addrs.At(i).Addr()] = true } return func(ip netip.Addr) bool { return m[ip] } @@ -229,7 +229,7 @@ func PrefixIs6(p netip.Prefix) bool { return p.Addr().Is6() } // IPv6 /0 route. func ContainsExitRoutes(rr views.Slice[netip.Prefix]) bool { var v4, v6 bool - for i := range rr.LenIter() { + for i := range rr.Len() { r := rr.At(i) if r == allIPv4 { v4 = true @@ -243,7 +243,7 @@ func ContainsExitRoutes(rr views.Slice[netip.Prefix]) bool { // ContainsNonExitSubnetRoutes reports whether v contains Subnet // Routes other than ExitNode Routes. func ContainsNonExitSubnetRoutes(rr views.Slice[netip.Prefix]) bool { - for i := range rr.LenIter() { + for i := range rr.Len() { if rr.At(i).Bits() != 0 { return true } @@ -274,7 +274,7 @@ func SortPrefixes(p []netip.Prefix) { // in that match f. func FilterPrefixesCopy(in views.Slice[netip.Prefix], f func(netip.Prefix) bool) []netip.Prefix { var out []netip.Prefix - for i := range in.LenIter() { + for i := range in.Len() { if v := in.At(i); f(v) { out = append(out, v) } diff --git a/net/tsdial/dnsmap.go b/net/tsdial/dnsmap.go index a549b5203..f5d13861b 100644 --- a/net/tsdial/dnsmap.go +++ b/net/tsdial/dnsmap.go @@ -42,7 +42,7 @@ func dnsMapFromNetworkMap(nm *netmap.NetworkMap) dnsMap { if dnsname.HasSuffix(nm.Name, suffix) { ret[canonMapKey(dnsname.TrimSuffix(nm.Name, suffix))] = ip } - for i := range addrs.LenIter() { + for i := range addrs.Len() { if addrs.At(i).Addr().Is4() { have4 = true } @@ -52,7 +52,7 @@ func dnsMapFromNetworkMap(nm *netmap.NetworkMap) dnsMap { if p.Name() == "" { continue } - for i := range p.Addresses().LenIter() { + for i := range p.Addresses().Len() { a := p.Addresses().At(i) ip := a.Addr() if ip.Is4() && !have4 { diff --git a/tsnet/tsnet.go b/tsnet/tsnet.go index 6edbc5ec9..e51e07a51 100644 --- a/tsnet/tsnet.go +++ b/tsnet/tsnet.go @@ -428,7 +428,7 @@ func (s *Server) TailscaleIPs() (ip4, ip6 netip.Addr) { return } addrs := nm.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { addr := addrs.At(i) ip := addr.Addr() if ip.Is6() { diff --git a/types/views/views.go b/types/views/views.go index be267c275..f0c093488 100644 --- a/types/views/views.go +++ b/types/views/views.go @@ -77,11 +77,6 @@ func (v ByteSlice[T]) AppendTo(dst T) T { return append(dst, v.ж...) } -// LenIter returns a slice the same length as the v.Len(). -// The caller can then range over it to get the valid indexes. -// It does not allocate. -func (v ByteSlice[T]) LenIter() []struct{} { return make([]struct{}, len(v.ж)) } - // At returns the byte at index `i` of the slice. func (v ByteSlice[T]) At(i int) byte { return v.ж[i] } @@ -154,11 +149,6 @@ func (v SliceView[T, V]) IsNil() bool { return v.ж == nil } // Len returns the length of the slice. func (v SliceView[T, V]) Len() int { return len(v.ж) } -// LenIter returns a slice the same length as the v.Len(). -// The caller can then range over it to get the valid indexes. -// It does not allocate. -func (v SliceView[T, V]) LenIter() []struct{} { return make([]struct{}, len(v.ж)) } - // At returns a View of the element at index `i` of the slice. func (v SliceView[T, V]) At(i int) V { return v.ж[i].View() } @@ -245,11 +235,6 @@ func (v Slice[T]) IsNil() bool { return v.ж == nil } // Len returns the length of the slice. func (v Slice[T]) Len() int { return len(v.ж) } -// LenIter returns a slice the same length as the v.Len(). -// The caller can then range over it to get the valid indexes. -// It does not allocate. -func (v Slice[T]) LenIter() []struct{} { return make([]struct{}, len(v.ж)) } - // At returns the element at index `i` of the slice. func (v Slice[T]) At(i int) T { return v.ж[i] } diff --git a/types/views/views_test.go b/types/views/views_test.go index e3d5cbda9..933d72395 100644 --- a/types/views/views_test.go +++ b/types/views/views_test.go @@ -141,27 +141,6 @@ func TestViewUtils(t *testing.T) { qt.Equals, true) } -func TestLenIter(t *testing.T) { - orig := []string{"foo", "bar"} - var got []string - v := SliceOf(orig) - for i := range v.LenIter() { - got = append(got, v.At(i)) - } - if !reflect.DeepEqual(orig, got) { - t.Errorf("got %q; want %q", got, orig) - } - x := 0 - n := testing.AllocsPerRun(10000, func() { - for range v.LenIter() { - x++ - } - }) - if n > 0 { - t.Errorf("allocs = %v; want 0", n) - } -} - func TestSliceEqual(t *testing.T) { a := SliceOf([]string{"foo", "bar"}) b := SliceOf([]string{"foo", "bar"}) diff --git a/wgengine/magicsock/debughttp.go b/wgengine/magicsock/debughttp.go index 4a5531fe0..6c07b0d5e 100644 --- a/wgengine/magicsock/debughttp.go +++ b/wgengine/magicsock/debughttp.go @@ -102,7 +102,7 @@ func (c *Conn) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) { sort.Slice(ent, func(i, j int) bool { return ent[i].pub.Less(ent[j].pub) }) peers := map[key.NodePublic]tailcfg.NodeView{} - for i := range c.peers.LenIter() { + for i := range c.peers.Len() { p := c.peers.At(i) peers[p.Key()] = p } diff --git a/wgengine/magicsock/endpoint.go b/wgengine/magicsock/endpoint.go index 3986f718d..16036f638 100644 --- a/wgengine/magicsock/endpoint.go +++ b/wgengine/magicsock/endpoint.go @@ -1366,7 +1366,7 @@ func (de *endpoint) updateFromNode(n tailcfg.NodeView, heartbeatDisabled bool, p } func (de *endpoint) setEndpointsLocked(eps interface { - LenIter() []struct{} + Len() int At(i int) netip.AddrPort }) { for _, st := range de.endpointState { @@ -1374,7 +1374,7 @@ func (de *endpoint) setEndpointsLocked(eps interface { } var newIpps []netip.AddrPort - for i := range eps.LenIter() { + for i := range eps.Len() { if i > math.MaxInt16 { // Seems unlikely. break diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index e42688602..42ffae2e1 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -1793,7 +1793,7 @@ func nodesEqual(x, y views.Slice[tailcfg.NodeView]) bool { if x.Len() != y.Len() { return false } - for i := range x.LenIter() { + for i := range x.Len() { if !x.At(i).Equal(y.At(i)) { return false } @@ -2056,7 +2056,7 @@ func (c *Conn) logEndpointCreated(n tailcfg.NodeView) { fmt.Fprintf(w, "derp=%v%s ", regionID, code) } - for i := range n.AllowedIPs().LenIter() { + for i := range n.AllowedIPs().Len() { a := n.AllowedIPs().At(i) if a.IsSingleIP() { fmt.Fprintf(w, "aip=%v ", a.Addr()) @@ -2064,7 +2064,7 @@ func (c *Conn) logEndpointCreated(n tailcfg.NodeView) { fmt.Fprintf(w, "aip=%v ", a) } } - for i := range n.Endpoints().LenIter() { + for i := range n.Endpoints().Len() { ep := n.Endpoints().At(i) fmt.Fprintf(w, "ep=%v ", ep) } diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go index c58f11023..ae2513b33 100644 --- a/wgengine/netstack/netstack.go +++ b/wgengine/netstack/netstack.go @@ -366,12 +366,12 @@ func (ns *Impl) UpdateNetstackIPs(nm *netmap.NetworkMap) { newPfx := make(map[netip.Prefix]bool) if selfNode.Valid() { - for i := range selfNode.Addresses().LenIter() { + for i := range selfNode.Addresses().Len() { p := selfNode.Addresses().At(i) newPfx[p] = true } if ns.ProcessSubnets { - for i := range selfNode.AllowedIPs().LenIter() { + for i := range selfNode.AllowedIPs().Len() { p := selfNode.AllowedIPs().At(i) newPfx[p] = true } diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go index af5d3d8a7..0b34be8ab 100644 --- a/wgengine/pendopen.go +++ b/wgengine/pendopen.go @@ -160,7 +160,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) { ps, found := e.getPeerStatusLite(n.Key()) if !found { onlyZeroRoute := true // whether peerForIP returned n only because its /0 route matched - for i := range n.AllowedIPs().LenIter() { + for i := range n.AllowedIPs().Len() { r := n.AllowedIPs().At(i) if r.Bits() != 0 && r.Contains(flow.Dst.Addr()) { onlyZeroRoute = false diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 525af32a9..a8b2719f3 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -780,7 +780,7 @@ func (e *userspaceEngine) updateActivityMapsLocked(trackNodes []key.NodePublic, // hasOverlap checks if there is a IPPrefix which is common amongst the two // provided slices. func hasOverlap(aips, rips views.Slice[netip.Prefix]) bool { - for i := range aips.LenIter() { + for i := range aips.Len() { aip := aips.At(i) if views.SliceContains(rips, aip) { return true @@ -1233,7 +1233,7 @@ func (e *userspaceEngine) mySelfIPMatchingFamily(dst netip.Addr) (src netip.Addr if addrs.Len() == 0 { return zero, errors.New("no self address in netmap") } - for i := range addrs.LenIter() { + for i := range addrs.Len() { if a := addrs.At(i); a.IsSingleIP() && a.Addr().BitLen() == dst.BitLen() { return a.Addr(), nil } @@ -1379,7 +1379,7 @@ func (e *userspaceEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) { // Check for exact matches before looking for subnet matches. // TODO(bradfitz): add maps for these. on NetworkMap? for _, p := range nm.Peers { - for i := range p.Addresses().LenIter() { + for i := range p.Addresses().Len() { a := p.Addresses().At(i) if a.Addr() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) { return PeerForIP{Node: p, Route: a}, true @@ -1387,7 +1387,7 @@ func (e *userspaceEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) { } } addrs := nm.GetAddresses() - for i := range addrs.LenIter() { + for i := range addrs.Len() { if a := addrs.At(i); a.Addr() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) { return PeerForIP{Node: nm.SelfNode, IsSelf: true, Route: a}, true } diff --git a/wgengine/wgcfg/nmcfg/nmcfg.go b/wgengine/wgcfg/nmcfg/nmcfg.go index 885d507fa..229512d4c 100644 --- a/wgengine/wgcfg/nmcfg/nmcfg.go +++ b/wgengine/wgcfg/nmcfg/nmcfg.go @@ -41,7 +41,7 @@ func cidrIsSubnet(node tailcfg.NodeView, cidr netip.Prefix) bool { if !cidr.IsSingleIP() { return true } - for i := range node.Addresses().LenIter() { + for i := range node.Addresses().Len() { selfCIDR := node.Addresses().At(i) if cidr == selfCIDR { return false @@ -100,7 +100,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, didExitNodeWarn := false cpeer.V4MasqAddr = peer.SelfNodeV4MasqAddrForThisPeer() cpeer.V6MasqAddr = peer.SelfNodeV6MasqAddrForThisPeer() - for i := range peer.AllowedIPs().LenIter() { + for i := range peer.AllowedIPs().Len() { allowedIP := peer.AllowedIPs().At(i) if allowedIP.Bits() == 0 && peer.StableID() != exitNode { if didExitNodeWarn {