From 7f8e397dfb26a5311c88c365a6a4447e5c495dc0 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Wed, 1 Nov 2023 17:08:51 +0000
Subject: [PATCH] Behave (or assert) when source or target are null or dirty.

---
 pcbnew/connectivity/connectivity_data.cpp     | 16 ++++++++++++++++
 pcbnew/dialogs/dialog_drc.cpp                 |  8 ++++++++
 pcbnew/drc/drc_test_provider_connectivity.cpp |  3 +++
 pcbnew/ratsnest/ratsnest_data.cpp             |  4 ++++
 pcbnew/ratsnest/ratsnest_view_item.cpp        |  2 +-
 pcbnew/router/router_tool.cpp                 |  7 +++++--
 pcbnew/tools/pcb_selection_tool.cpp           |  5 +++++
 7 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/pcbnew/connectivity/connectivity_data.cpp b/pcbnew/connectivity/connectivity_data.cpp
index 57022eb587..8a0b416e02 100644
--- a/pcbnew/connectivity/connectivity_data.cpp
+++ b/pcbnew/connectivity/connectivity_data.cpp
@@ -385,6 +385,10 @@ void CONNECTIVITY_DATA::ComputeLocalRatsnest( const std::vector<BOARD_ITEM*>& aI
     {
         const std::shared_ptr<const CN_ANCHOR>& nodeA = edge.GetSourceNode();
         const std::shared_ptr<const CN_ANCHOR>& nodeB = edge.GetTargetNode();
+
+        if( !nodeA || nodeA->Dirty() || !nodeB || nodeB->Dirty() )
+            continue;
+
         RN_DYNAMIC_LINE l;
 
         // Use the parents' positions
@@ -988,6 +992,9 @@ CONNECTIVITY_DATA::GetRatsnestForItems( const std::vector<BOARD_ITEM*>& aItems )
             std::shared_ptr<const CN_ANCHOR> srcNode = edge.GetSourceNode();
             std::shared_ptr<const CN_ANCHOR> dstNode = edge.GetTargetNode();
 
+            if( !srcNode || srcNode->Dirty() || !dstNode || dstNode->Dirty() )
+                continue;
+
             BOARD_CONNECTED_ITEM* srcParent = srcNode->Parent();
             BOARD_CONNECTED_ITEM* dstParent = dstNode->Parent();
 
@@ -1013,6 +1020,12 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForPad( const PAD* aPad
 
     for( const CN_EDGE& edge : net->GetEdges() )
     {
+        if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
+            continue;
+
+        if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
+            continue;
+
         if( edge.GetSourceNode()->Parent() == aPad || edge.GetTargetNode()->Parent() == aPad )
             edges.push_back( edge );
     }
@@ -1046,6 +1059,9 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForComponent( FOOTPRINT
             const std::shared_ptr<const CN_ANCHOR>& srcNode = edge.GetSourceNode();
             const std::shared_ptr<const CN_ANCHOR>& dstNode = edge.GetTargetNode();
 
+            if( !srcNode || srcNode->Dirty() || !dstNode || dstNode->Dirty() )
+                continue;
+
             const PAD* srcParent = static_cast<const PAD*>( srcNode->Parent() );
             const PAD* dstParent = static_cast<const PAD*>( dstNode->Parent() );
 
diff --git a/pcbnew/dialogs/dialog_drc.cpp b/pcbnew/dialogs/dialog_drc.cpp
index 6948e43c1a..b01f87387e 100644
--- a/pcbnew/dialogs/dialog_drc.cpp
+++ b/pcbnew/dialogs/dialog_drc.cpp
@@ -491,6 +491,14 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
             m_frame->GetBoard()->GetConnectivity()->RunOnUnconnectedEdges(
                     [&]( CN_EDGE& edge )
                     {
+                        // Connectivity was valid when DRC was run, but this is a modeless dialog
+                        // so it might not be now.
+                        if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
+                            return true;
+
+                        if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
+                            return true;
+
                         if( edge.GetSourceNode()->Parent() == a
                                 && edge.GetTargetNode()->Parent() == b )
                         {
diff --git a/pcbnew/drc/drc_test_provider_connectivity.cpp b/pcbnew/drc/drc_test_provider_connectivity.cpp
index afd46c5dc8..07f4927ba8 100644
--- a/pcbnew/drc/drc_test_provider_connectivity.cpp
+++ b/pcbnew/drc/drc_test_provider_connectivity.cpp
@@ -151,6 +151,9 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
                 if( !reportProgress( ii++, count, progressDelta ) )
                     return false;   // DRC cancelled
 
+                wxCHECK( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), true );
+                wxCHECK( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), true );
+
                 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNCONNECTED_ITEMS );
                 drcItem->SetItems( edge.GetSourceNode()->Parent(), edge.GetTargetNode()->Parent() );
                 reportViolation( drcItem, edge.GetSourceNode()->Pos(), UNDEFINED_LAYER );
diff --git a/pcbnew/ratsnest/ratsnest_data.cpp b/pcbnew/ratsnest/ratsnest_data.cpp
index b71c6cba5a..9133734da5 100644
--- a/pcbnew/ratsnest/ratsnest_data.cpp
+++ b/pcbnew/ratsnest/ratsnest_data.cpp
@@ -123,6 +123,8 @@ void RN_NET::kruskalMST( const std::vector<CN_EDGE> &aEdges )
         const std::shared_ptr<const CN_ANCHOR>& source = tmp.GetSourceNode();
         const std::shared_ptr<const CN_ANCHOR>& target = tmp.GetTargetNode();
 
+        wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
+
         if( dset.unite( source->GetTag(), target->GetTag() ) )
         {
             if( tmp.GetWeight() > 0 )
@@ -416,6 +418,8 @@ void RN_NET::OptimizeRNEdges()
         const std::shared_ptr<const CN_ANCHOR>& source = edge.GetSourceNode();
         const std::shared_ptr<const CN_ANCHOR>& target = edge.GetTargetNode();
 
+        wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
+
         if( source->ConnectedItemsCount() == 0 )
         {
             optimizeZoneAnchor( source->Pos(), source->Parent()->GetLayerSet(), target,
diff --git a/pcbnew/ratsnest/ratsnest_view_item.cpp b/pcbnew/ratsnest/ratsnest_view_item.cpp
index 76341be370..fcbca134d5 100644
--- a/pcbnew/ratsnest/ratsnest_view_item.cpp
+++ b/pcbnew/ratsnest/ratsnest_view_item.cpp
@@ -199,7 +199,7 @@ void RATSNEST_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
             const std::shared_ptr<const CN_ANCHOR>& sourceNode = edge.GetSourceNode();
             const std::shared_ptr<const CN_ANCHOR>& targetNode = edge.GetTargetNode();
 
-            if( !sourceNode || !sourceNode->Valid() || !targetNode || !targetNode->Valid() )
+            if( !sourceNode || sourceNode->Dirty() || !targetNode || targetNode->Dirty() )
                 continue;
 
             const VECTOR2I source( sourceNode->Pos() );
diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp
index 08174bf99d..3546a90553 100644
--- a/pcbnew/router/router_tool.cpp
+++ b/pcbnew/router/router_tool.cpp
@@ -1640,10 +1640,13 @@ int ROUTER_TOOL::RouteSelected( const TOOL_EVENT& aEvent )
             std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
             std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
 
+            if( !source || source->Dirty() || !target || target->Dirty() )
+                continue;
+
             if( source->Parent() == item )
-                anchors.push_back( edge.GetSourceNode() );
+                anchors.push_back( source );
             else if( target->Parent() == item )
-                anchors.push_back( edge.GetTargetNode() );
+                anchors.push_back( target );
         }
 
         // Route them
diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp
index bc945ef9a6..733d05f874 100644
--- a/pcbnew/tools/pcb_selection_tool.cpp
+++ b/pcbnew/tools/pcb_selection_tool.cpp
@@ -1651,6 +1651,9 @@ int PCB_SELECTION_TOOL::selectUnconnected( const TOOL_EVENT& aEvent )
     {
         for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
         {
+            wxCHECK2( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), continue );
+            wxCHECK2( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), continue );
+
             BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
             BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
 
@@ -1714,6 +1717,8 @@ int PCB_SELECTION_TOOL::grabUnconnected( const TOOL_EVENT& aEvent )
             const CN_ANCHOR* other = edge.GetSourceNode()->Parent() == pad ? edge.GetTargetNode().get()
                                                                            : edge.GetSourceNode().get();
 
+            wxCHECK2( other && !other->Dirty(), continue );
+
             // We only want to grab footprints, so the ratnest has to point to a pad
             if( other->Parent()->Type() != PCB_PAD_T )
                 continue;