From 1f620e9cca1d4df10c98779b6ca7ba8b2842adf6 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Sun, 9 Mar 2025 10:46:31 +0000
Subject: [PATCH] forAllGeometryItems must account for PCB_GROUP descendants.

---
 pcbnew/drc/drc_test_provider.cpp | 187 ++++++++++++++-----------------
 1 file changed, 87 insertions(+), 100 deletions(-)

diff --git a/pcbnew/drc/drc_test_provider.cpp b/pcbnew/drc/drc_test_provider.cpp
index ce54e7a540..6302d2c3f4 100644
--- a/pcbnew/drc/drc_test_provider.cpp
+++ b/pcbnew/drc/drc_test_provider.cpp
@@ -28,6 +28,7 @@
 #include <pad.h>
 #include <zone.h>
 #include <pcb_text.h>
+#include <pcb_group.h>
 
 
 // A list of all basic (ie: non-compound) board geometry items
@@ -153,9 +154,11 @@ void DRC_TEST_PROVIDER::reportRuleStatistics()
 int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes, LSET aLayers,
                                             const std::function<bool( BOARD_ITEM*)>& aFunc )
 {
-    BOARD *brd = m_drcEngine->GetBoard();
+    BOARD* brd = m_drcEngine->GetBoard();
+    bool   cancelled = false;
+    int    n = 0;
+
     std::bitset<MAX_STRUCT_TYPE_ID> typeMask;
-    int n = 0;
 
     if( aTypes.size() == 0 )
     {
@@ -168,68 +171,77 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
             typeMask[ aType ] = true;
     }
 
+    auto processItem =
+            [&]( BOARD_ITEM* item )
+            {
+                if( ( item->GetLayerSet() & aLayers ).any() )
+                {
+                    if( typeMask[ PCB_TRACE_T ] && item->Type() == PCB_TRACE_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_VIA_T ] && item->Type() == PCB_VIA_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_ARC_T ] && item->Type() == PCB_ARC_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_DIMENSION_T ] && BaseType( item->Type() ) == PCB_DIMENSION_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_SHAPE_T ] && item->Type() == PCB_SHAPE_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_TEXT_T ] && item->Type() == PCB_TEXT_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_TEXTBOX_T ] && item->Type() == PCB_TEXTBOX_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                    else if( typeMask[ PCB_TARGET_T ] && item->Type() == PCB_TARGET_T )
+                    {
+                        cancelled |= !aFunc( item );
+                        n++;
+                    }
+                }
+            };
+
     for( PCB_TRACK* item : brd->Tracks() )
     {
-        if( (item->GetLayerSet() & aLayers).any() )
-        {
-            if( typeMask[ PCB_TRACE_T ] && item->Type() == PCB_TRACE_T )
-            {
-                aFunc( item );
-                n++;
-            }
-            else if( typeMask[ PCB_VIA_T ] && item->Type() == PCB_VIA_T )
-            {
-                aFunc( item );
-                n++;
-            }
-            else if( typeMask[ PCB_ARC_T ] && item->Type() == PCB_ARC_T )
-            {
-                aFunc( item );
-                n++;
-            }
-        }
+        processItem( item );
+
+        if( cancelled )
+            return n;
     }
 
     for( BOARD_ITEM* item : brd->Drawings() )
     {
-        if( (item->GetLayerSet() & aLayers).any() )
+        processItem( item );
+
+        if( item->Type() == PCB_GROUP_T )
         {
-            if( typeMask[ PCB_DIMENSION_T ] && BaseType( item->Type() ) == PCB_DIMENSION_T )
-            {
-                if( !aFunc( item ) )
-                    return n;
-
-                n++;
-            }
-            else if( typeMask[ PCB_SHAPE_T ] && item->Type() == PCB_SHAPE_T )
-            {
-                if( !aFunc( item ) )
-                    return n;
-
-                n++;
-            }
-            else if( typeMask[ PCB_TEXT_T ] && item->Type() == PCB_TEXT_T )
-            {
-                if( !aFunc( item ) )
-                    return n;
-
-                n++;
-            }
-            else if( typeMask[ PCB_TEXTBOX_T ] && item->Type() == PCB_TEXTBOX_T )
-            {
-                if( !aFunc( item ) )
-                    return n;
-
-                n++;
-            }
-            else if( typeMask[ PCB_TARGET_T ] && item->Type() == PCB_TARGET_T )
-            {
-                if( !aFunc( item ) )
-                    return n;
-
-                n++;
-            }
+            static_cast<PCB_GROUP*>( item )->RunOnDescendants(
+                    [&]( BOARD_ITEM* child )
+                    {
+                        processItem( child );
+                    } );
         }
+
+        if( cancelled )
+            return n;
     }
 
     if( typeMask[ PCB_ZONE_T ] )
@@ -238,11 +250,12 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
         {
             if( ( item->GetLayerSet() & aLayers ).any() )
             {
-                if( !aFunc( item ) )
-                    return n;
-
+                cancelled |= !aFunc( item );
                 n++;
             }
+
+            if( cancelled )
+                return n;
         }
     }
 
@@ -254,9 +267,7 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
             {
                 if( ( field->GetLayerSet() & aLayers ).any() )
                 {
-                    if( !aFunc( field ) )
-                        return n;
-
+                    cancelled |= !aFunc( field );
                     n++;
                 }
             }
@@ -269,9 +280,7 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
                 // Careful: if a pad has a hole then it pierces all layers
                 if( pad->HasHole() || ( pad->GetLayerSet() & aLayers ).any() )
                 {
-                    if( !aFunc( pad ) )
-                        return n;
-
+                    cancelled |= !aFunc( pad );
                     n++;
                 }
             }
@@ -279,36 +288,15 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
 
         for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
         {
-            if( (dwg->GetLayerSet() & aLayers).any() )
+            processItem( dwg );
+
+            if( dwg->Type() == PCB_GROUP_T )
             {
-                if( typeMask[ PCB_DIMENSION_T ] && BaseType( dwg->Type() ) == PCB_DIMENSION_T )
-                {
-                    if( !aFunc( dwg ) )
-                        return n;
-
-                    n++;
-                }
-                else if( typeMask[ PCB_TEXT_T ] && dwg->Type() == PCB_TEXT_T )
-                {
-                    if( !aFunc( dwg ) )
-                        return n;
-
-                    n++;
-                }
-                else if( typeMask[ PCB_TEXTBOX_T ] && dwg->Type() == PCB_TEXTBOX_T )
-                {
-                    if( !aFunc( dwg ) )
-                        return n;
-
-                    n++;
-                }
-                else if( typeMask[ PCB_SHAPE_T ] && dwg->Type() == PCB_SHAPE_T )
-                {
-                    if( !aFunc( dwg ) )
-                        return n;
-
-                    n++;
-                }
+                static_cast<PCB_GROUP*>( dwg )->RunOnDescendants(
+                        [&]( BOARD_ITEM* child )
+                        {
+                            processItem( child );
+                        } );
             }
         }
 
@@ -318,9 +306,7 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
             {
                 if( (zone->GetLayerSet() & aLayers).any() )
                 {
-                    if( !aFunc( zone ) )
-                        return n;
-
+                    cancelled |= !aFunc( zone );
                     n++;
                 }
             }
@@ -328,11 +314,12 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
 
         if( typeMask[ PCB_FOOTPRINT_T ] )
         {
-            if( !aFunc( footprint ) )
-                return n;
-
+            cancelled |= !aFunc( footprint );
             n++;
         }
+
+        if( cancelled )
+            return n;
     }
 
     return n;