diff --git a/common/drc_rules.keywords b/common/drc_rules.keywords
index 7b21a6ba61..c490def6d6 100644
--- a/common/drc_rules.keywords
+++ b/common/drc_rules.keywords
@@ -19,6 +19,8 @@ inner
 layer
 length
 max
+mechanical_clearance
+mechanical_hole_clearance
 micro_via
 min
 min_resolved_spokes
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 54bed23ee1..3ab6e269cb 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -234,6 +234,7 @@ set( PCBNEW_DRC_SRCS
     drc/drc_test_provider_disallow.cpp
     drc/drc_test_provider_connectivity.cpp
     drc/drc_test_provider_copper_clearance.cpp
+    drc/drc_test_provider_mechanical_clearance.cpp
     drc/drc_test_provider_courtyard_clearance.cpp
     drc/drc_test_provider_edge_clearance.cpp
     drc/drc_test_provider_hole_to_hole.cpp
diff --git a/pcbnew/dialogs/panel_setup_rules.cpp b/pcbnew/dialogs/panel_setup_rules.cpp
index c7b1c4fe14..2d7f94aec5 100644
--- a/pcbnew/dialogs/panel_setup_rules.cpp
+++ b/pcbnew/dialogs/panel_setup_rules.cpp
@@ -301,6 +301,8 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
                      "hole_clearance|"
                      "hole_size|"
                      "hole_to_hole|"
+                     "mechanical_clearance|"
+                     "mechanical_hole_clearance|"
                      "min_resolved_spokes|"
                      "silk_clearance|"
                      "skew|"
diff --git a/pcbnew/dialogs/panel_setup_rules_help.md b/pcbnew/dialogs/panel_setup_rules_help.md
index 69cd1af36e..4774b71c9a 100644
--- a/pcbnew/dialogs/panel_setup_rules_help.md
+++ b/pcbnew/dialogs/panel_setup_rules_help.md
@@ -30,6 +30,8 @@
  * length
  * hole\_clearance
  * hole\_size
+ * mechanical\_clearance
+ * mechanical\_hole\_clearance
  * min\_resolved\_spokes
  * silk\_clearance
  * skew
@@ -42,7 +44,7 @@
  * via\_diameter
  * zone\_connection
 
-
+Note: `clearance` and `hole_clearance` rules are not run against items of the same net; `mechanical_clearance` and `mechanical_hole_clearance` rules are.
 <br>
 
 ### Item Types
@@ -229,4 +231,9 @@ For the latter use a `(layer "layer_name")` clause in the rule.
 
     # Require all four thermal relief spokes to connect to parent zone
     (rule fully_spoked_pads
-        (constraint min_resolved_spokes 4))
\ No newline at end of file
+        (constraint min_resolved_spokes 4))
+
+    # Prevent solder wicking from SMD pads
+    (rule holes_in_pads
+        (constraint mechanical_hole_clearance (min 0.2mm))
+        (condition "B.Pad_Type == 'SMD'"))
\ No newline at end of file
diff --git a/pcbnew/drc/drc_rule.h b/pcbnew/drc/drc_rule.h
index e05b933922..5f569d176a 100644
--- a/pcbnew/drc/drc_rule.h
+++ b/pcbnew/drc/drc_rule.h
@@ -64,7 +64,9 @@ enum DRC_CONSTRAINT_T
     DIFF_PAIR_GAP_CONSTRAINT,
     DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT,
     DIFF_PAIR_INTRA_SKEW_CONSTRAINT,
-    VIA_COUNT_CONSTRAINT
+    VIA_COUNT_CONSTRAINT,
+    MECHANICAL_CLEARANCE_CONSTRAINT,
+    MECHANICAL_HOLE_CLEARANCE_CONSTRAINT
 };
 
 
diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp
index f82d9a37b9..d829d9ff1b 100644
--- a/pcbnew/drc/drc_rule_parser.cpp
+++ b/pcbnew/drc/drc_rule_parser.cpp
@@ -256,9 +256,11 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
     if( (int) token == DSN_RIGHT || token == T_EOF )
     {
         msg.Printf( _( "Missing constraint type.|  Expected %s." ),
-                    "clearance, hole_clearance, edge_clearance, hole, hole_to_hole, "
-                    "courtyard_clearance, silk_clearance, track_width, annular_width, via_diameter, "
-                    "disallow, length, skew, via_count, diff_pair_gap or diff_pair_uncoupled" );
+                    "clearance, hole_clearance, edge_clearance, mechanical_clearance, "
+                    "mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
+                    "hole_to_hole, track_width, annular_width, via_diameter, disallow, "
+                    "zone_connection, thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, "
+                    "length, skew, via_count, diff_pair_gap or diff_pair_uncoupled" );
         reportError( msg );
         return;
     }
@@ -288,13 +290,15 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
     case T_via_count:                 c.m_Type = VIA_COUNT_CONSTRAINT;                 break;
     case T_diff_pair_gap:             c.m_Type = DIFF_PAIR_GAP_CONSTRAINT;             break;
     case T_diff_pair_uncoupled:       c.m_Type = DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT;   break;
+    case T_mechanical_clearance:      c.m_Type = MECHANICAL_CLEARANCE_CONSTRAINT;      break;
+    case T_mechanical_hole_clearance: c.m_Type = MECHANICAL_HOLE_CLEARANCE_CONSTRAINT; break;
     default:
         msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
-                    "clearance, hole_clearance, edge_clearance, hole_size, hole_to_hole, "
-                    "courtyard_clearance, silk_clearance, text_height, text_thickness, "
-                    "track_width, annular_width, via_diameter, zone_connection, "
-                    "thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, "
-                    "disallow, length, skew, diff_pair_gap or diff_pair_uncoupled." );
+                    "clearance, hole_clearance, edge_clearance, mechanical_clearance, "
+                    "mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
+                    "hole_to_hole, track_width, annular_width, disallow, zone_connection, "
+                    "thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, length, skew, "
+                    "via_count, via_diameter, diff_pair_gap or diff_pair_uncoupled" );
         reportError( msg );
     }
 
@@ -362,7 +366,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
 
         default:
             msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
-                        "'solid', 'thermal_reliefs' or 'none'." );
+                        "solid, thermal_reliefs or none." );
             reportError( msg );
             break;
         }
diff --git a/pcbnew/drc/drc_test_provider_mechanical_clearance.cpp b/pcbnew/drc/drc_test_provider_mechanical_clearance.cpp
new file mode 100644
index 0000000000..e773b9fcdf
--- /dev/null
+++ b/pcbnew/drc/drc_test_provider_mechanical_clearance.cpp
@@ -0,0 +1,503 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2021 KiCad Developers.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <common.h>
+#include <board_design_settings.h>
+#include <footprint.h>
+#include <pad.h>
+#include <pcb_track.h>
+#include <zone.h>
+
+#include <geometry/seg.h>
+#include <geometry/shape_segment.h>
+
+#include <drc/drc_engine.h>
+#include <drc/drc_rtree.h>
+#include <drc/drc_item.h>
+#include <drc/drc_rule.h>
+#include <drc/drc_test_provider_clearance_base.h>
+
+/*
+    Mechanical clearance test.
+
+    Errors generated:
+    - DRCE_MECHANICAL_CLEARANCE
+    - DRCE_MECHANICAL_HOLE_CLEARANCE
+*/
+
+class DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
+{
+public:
+    DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE () :
+            DRC_TEST_PROVIDER_CLEARANCE_BASE()
+    {
+    }
+
+    virtual ~DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE()
+    {
+    }
+
+    virtual bool Run() override;
+
+    virtual const wxString GetName() const override
+    {
+        return "mechanical_clearance";
+    };
+
+    virtual const wxString GetDescription() const override
+    {
+        return "Tests item clearances irrespective of nets";
+    }
+
+    virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
+
+private:
+    bool testItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer,
+                              BOARD_ITEM* other );
+
+    void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
+
+private:
+    DRC_RTREE                m_itemTree;
+    std::vector<BOARD_ITEM*> m_items;
+    std::vector<ZONE*>       m_zones;
+};
+
+
+bool DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE::Run()
+{
+    m_board = m_drcEngine->GetBoard();
+    m_itemTree.clear();
+    m_zones.clear();
+    m_items.clear();
+
+    DRC_CONSTRAINT worstConstraint;
+
+    if( m_drcEngine->QueryWorstConstraint( MECHANICAL_CLEARANCE_CONSTRAINT, worstConstraint ) )
+        m_largestClearance = worstConstraint.GetValue().Min();
+
+    if( m_drcEngine->QueryWorstConstraint( MECHANICAL_HOLE_CLEARANCE_CONSTRAINT, worstConstraint ) )
+        m_largestClearance = std::max( m_largestClearance, worstConstraint.GetValue().Min() );
+
+    if( m_largestClearance <= 0 )
+    {
+        reportAux( "No Clearance constraints found. Tests not run." );
+        return true;   // continue with other tests
+    }
+
+    for( ZONE* zone : m_board->Zones() )
+    {
+        if( !zone->GetIsRuleArea() )
+        {
+            m_zones.push_back( zone );
+            m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
+        }
+    }
+
+    for( FOOTPRINT* footprint : m_board->Footprints() )
+    {
+        for( PAD* pad : footprint->Pads() )
+            m_largestClearance = std::max( m_largestClearance, pad->GetLocalClearance() );
+
+        for( ZONE* zone : footprint->Zones() )
+        {
+            if( !zone->GetIsRuleArea() )
+            {
+                m_zones.push_back( zone );
+                m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
+            }
+        }
+    }
+
+    reportAux( "Worst clearance : %d nm", m_largestClearance );
+
+    // This is the number of tests between 2 calls to the progress bar
+    size_t delta = 50;
+    size_t count = 0;
+    size_t ii = 0;
+
+    auto countItems =
+            [&]( BOARD_ITEM* item ) -> bool
+            {
+                ++count;
+                return true;
+            };
+
+    auto addToItemTree =
+            [&]( BOARD_ITEM* item ) -> bool
+            {
+                if( !reportProgress( ii++, count, delta ) )
+                    return false;
+
+                m_items.push_back( item );
+
+                LSET layers = item->GetLayerSet();
+
+                // Special-case pad holes which pierce all the copper layers
+                if( item->Type() == PCB_PAD_T )
+                {
+                    PAD* pad = static_cast<PAD*>( item );
+
+                    if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 )
+                        layers |= LSET::AllCuMask();
+                }
+
+                for( PCB_LAYER_ID layer : layers.Seq() )
+                    m_itemTree.Insert( item, layer, m_largestClearance );
+
+                return true;
+            };
+
+    if( !reportPhase( _( "Gathering items..." ) ) )
+        return false;   // DRC cancelled
+
+    static const std::vector<KICAD_T> itemTypes = {
+        PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_SHAPE_T, PCB_FP_SHAPE_T,
+        PCB_TEXT_T, PCB_FP_TEXT_T, PCB_DIMENSION_T, PCB_DIM_ALIGNED_T, PCB_DIM_LEADER_T,
+        PCB_DIM_CENTER_T,  PCB_DIM_RADIAL_T, PCB_DIM_ORTHOGONAL_T
+    };
+
+    forEachGeometryItem( itemTypes, LSET::AllLayersMask(), countItems );
+    forEachGeometryItem( itemTypes, LSET::AllLayersMask(), addToItemTree );
+
+    std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> checkedPairs;
+
+    auto testItem =
+            [&]( BOARD_ITEM* item, PCB_LAYER_ID layer )
+            {
+                std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
+
+                m_itemTree.QueryColliding( item, layer, layer,
+                        // Filter:
+                        [&]( BOARD_ITEM* other ) -> bool
+                        {
+                            BOARD_ITEM* a = item;
+                            BOARD_ITEM* b = other;
+
+                            // store canonical order so we don't collide in both directions
+                            // (a:b and b:a)
+                            if( static_cast<void*>( a ) > static_cast<void*>( b ) )
+                                std::swap( a, b );
+
+                            if( checkedPairs.count( { a, b } ) )
+                            {
+                                return false;
+                            }
+                            else
+                            {
+                                checkedPairs[ { a, b } ] = 1;
+                                return true;
+                            }
+                        },
+                        // Visitor:
+                        [&]( BOARD_ITEM* other ) -> bool
+                        {
+                            return testItemAgainstItem( item, itemShape.get(), layer, other );
+                        },
+                        m_largestClearance );
+
+                testItemAgainstZones( item, layer );
+            };
+
+    if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
+            || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
+    {
+        if( !reportPhase( _( "Checking mechanical clearances..." ) ) )
+            return false;   // DRC cancelled
+
+        ii = 0;
+
+        for( BOARD_ITEM* item : m_items )
+        {
+            if( !reportProgress( ii++, m_board->Tracks().size(), delta ) )
+                break;
+
+            for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
+                testItem( item, layer );
+        }
+    }
+
+    reportRuleStatistics();
+
+    return true;
+}
+
+
+bool DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item,
+                                                                  SHAPE* itemShape,
+                                                                  PCB_LAYER_ID layer,
+                                                                  BOARD_ITEM* other )
+{
+    bool           testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
+    bool           testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
+    DRC_CONSTRAINT constraint;
+    int            clearance = 0;
+    int            actual;
+    VECTOR2I       pos;
+
+    std::shared_ptr<SHAPE> otherShape = DRC_ENGINE::GetShape( other, layer );
+
+    if( testClearance )
+    {
+        constraint = m_drcEngine->EvalRules( MECHANICAL_CLEARANCE_CONSTRAINT, item, other, layer );
+        clearance = constraint.GetValue().Min();
+    }
+
+    if( clearance > 0 )
+    {
+        if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
+        {
+            std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
+
+            m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
+                          constraint.GetName(),
+                          MessageTextFromValue( userUnits(), clearance ),
+                          MessageTextFromValue( userUnits(), actual ) );
+
+            drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
+            drce->SetItems( item, other );
+            drce->SetViolatingRule( constraint.GetParentRule() );
+
+            reportViolation( drce, (wxPoint) pos );
+        }
+    }
+
+    if( testHoles )
+    {
+        std::unique_ptr<SHAPE_SEGMENT> itemHoleShape;
+        std::unique_ptr<SHAPE_SEGMENT> otherHoleShape;
+        clearance = 0;
+
+        if( item->Type() == PCB_VIA_T )
+        {
+            PCB_VIA* via = static_cast<PCB_VIA*>( item );
+            pos = via->GetPosition();
+
+            if( via->GetLayerSet().Contains( layer ) )
+                itemHoleShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
+        }
+        else if( item->Type() == PCB_PAD_T )
+        {
+            PAD* pad = static_cast<PAD*>( item );
+
+            if( pad->GetDrillSize().x )
+                itemHoleShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
+        }
+
+        if( other->Type() == PCB_VIA_T )
+        {
+            PCB_VIA* via = static_cast<PCB_VIA*>( other );
+            pos = via->GetPosition();
+
+            if( via->GetLayerSet().Contains( layer ) )
+                otherHoleShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
+        }
+        else if( other->Type() == PCB_PAD_T )
+        {
+            PAD* pad = static_cast<PAD*>( other );
+
+            if( pad->GetDrillSize().x )
+                otherHoleShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
+        }
+
+        if( itemHoleShape || otherHoleShape )
+        {
+            constraint = m_drcEngine->EvalRules( MECHANICAL_HOLE_CLEARANCE_CONSTRAINT, other, item,
+                                                 layer );
+            clearance = constraint.GetValue().Min();
+        }
+
+        if( clearance > 0 && itemHoleShape && itemHoleShape->Collide( otherShape.get(), clearance,
+                                                                      &actual, &pos ) )
+        {
+            std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
+
+            m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
+                          constraint.GetName(),
+                          MessageTextFromValue( userUnits(), clearance ),
+                          MessageTextFromValue( userUnits(), actual ) );
+
+            drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
+            drce->SetItems( item, other );
+            drce->SetViolatingRule( constraint.GetParentRule() );
+
+            reportViolation( drce, (wxPoint) pos );
+        }
+
+        if( clearance > 0 && otherHoleShape && otherHoleShape->Collide( itemShape, clearance,
+                                                                        &actual, &pos ) )
+        {
+            std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
+
+            m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
+                          constraint.GetName(),
+                          MessageTextFromValue( userUnits(), clearance ),
+                          MessageTextFromValue( userUnits(), actual ) );
+
+            drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
+            drce->SetItems( item, other );
+            drce->SetViolatingRule( constraint.GetParentRule() );
+
+            reportViolation( drce, (wxPoint) pos );
+        }
+    }
+
+    return true;
+}
+
+
+void DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem,
+                                                                   PCB_LAYER_ID aLayer )
+{
+    for( ZONE* zone : m_zones )
+    {
+        if( !zone->GetLayerSet().test( aLayer ) )
+            continue;
+
+        if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
+        {
+            bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
+            bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
+
+            if( !testClearance && !testHoles )
+                return;
+
+            DRC_RTREE*     zoneTree = m_board->m_CopperZoneRTrees[ zone ].get();
+            EDA_RECT       itemBBox = aItem->GetBoundingBox();
+            DRC_CONSTRAINT constraint;
+            int            clearance = -1;
+            int            actual;
+            VECTOR2I       pos;
+
+            if( testClearance )
+            {
+                constraint = m_drcEngine->EvalRules( MECHANICAL_CLEARANCE_CONSTRAINT, aItem, zone,
+                                                     aLayer );
+                clearance = constraint.GetValue().Min();
+            }
+
+            if( clearance > 0 )
+            {
+                std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
+
+                if( aItem->Type() == PCB_PAD_T )
+                {
+                    PAD* pad = static_cast<PAD*>( aItem );
+
+                    if( !pad->FlashLayer( aLayer ) )
+                    {
+                        if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
+                            continue;
+
+                        const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
+                        int                  size = hole->GetWidth();
+
+                        // Note: drill size represents finish size, which means the actual hole
+                        // size is the plating thickness larger.
+                        if( pad->GetAttribute() == PAD_ATTRIB::PTH )
+                            size += m_board->GetDesignSettings().GetHolePlatingThickness();
+
+                        itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
+                    }
+                }
+
+                if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
+                                              &actual, &pos ) )
+                {
+                    std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
+
+                    m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
+                                  constraint.GetName(),
+                                  MessageTextFromValue( userUnits(), clearance ),
+                                  MessageTextFromValue( userUnits(), actual ) );
+
+                    drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
+                    drce->SetItems( aItem, zone );
+                    drce->SetViolatingRule( constraint.GetParentRule() );
+
+                    reportViolation( drce, (wxPoint) pos );
+                }
+            }
+
+            if( testHoles && ( aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_PAD_T ) )
+            {
+                std::unique_ptr<SHAPE_SEGMENT> holeShape;
+
+                if( aItem->Type() == PCB_VIA_T )
+                {
+                    PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
+                    pos = via->GetPosition();
+
+                    if( via->GetLayerSet().Contains( aLayer ) )
+                        holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
+                }
+                else if( aItem->Type() == PCB_PAD_T )
+                {
+                    PAD* pad = static_cast<PAD*>( aItem );
+
+                    if( pad->GetDrillSize().x )
+                        holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
+                }
+
+                if( holeShape )
+                {
+                    constraint = m_drcEngine->EvalRules( MECHANICAL_HOLE_CLEARANCE_CONSTRAINT,
+                                                         aItem, zone, aLayer );
+                    clearance = constraint.GetValue().Min();
+
+                    if( clearance > 0 && zoneTree->QueryColliding( itemBBox, holeShape.get(),
+                                                                   aLayer, clearance, &actual,
+                                                                   &pos ) )
+                    {
+                        std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
+
+                        m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
+                                      constraint.GetName(),
+                                      MessageTextFromValue( userUnits(), clearance ),
+                                      MessageTextFromValue( userUnits(), actual ) );
+
+                        drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
+                        drce->SetItems( aItem, zone );
+                        drce->SetViolatingRule( constraint.GetParentRule() );
+
+                        reportViolation( drce, (wxPoint) pos );
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+
+
+std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE::GetConstraintTypes() const
+{
+    return { MECHANICAL_CLEARANCE_CONSTRAINT, MECHANICAL_HOLE_CLEARANCE_CONSTRAINT };
+}
+
+
+namespace detail
+{
+    static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_MECHANICAL_CLEARANCE> dummy;
+}