Handle ERC/DRC assertions in drawing sheet.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19132
This commit is contained in:
Jeff Young 2024-12-02 20:12:53 +00:00
parent 55ca13e7cf
commit 4883c27972
8 changed files with 122 additions and 76 deletions

View File

@ -56,7 +56,7 @@ enum Bracket
}; };
wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject ) wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject, int aFlags )
{ {
std::function<bool( wxString* )> projectResolver = std::function<bool( wxString* )> projectResolver =
[&]( wxString* token ) -> bool [&]( wxString* token ) -> bool
@ -64,12 +64,12 @@ wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject )
return aProject->TextVarResolver( token ); return aProject->TextVarResolver( token );
}; };
return ExpandTextVars( aSource, &projectResolver ); return ExpandTextVars( aSource, &projectResolver, aFlags );
} }
wxString ExpandTextVars( const wxString& aSource, wxString ExpandTextVars( const wxString& aSource,
const std::function<bool( wxString* )>* aResolver ) const std::function<bool( wxString* )>* aResolver, int aFlags )
{ {
static wxRegEx userDefinedWarningError( wxS( "^(ERC|DRC)_(WARNING|ERROR).*$" ) ); static wxRegEx userDefinedWarningError( wxS( "^(ERC|DRC)_(WARNING|ERROR).*$" ) );
wxString newbuf; wxString newbuf;
@ -94,7 +94,7 @@ wxString ExpandTextVars( const wxString& aSource,
if( token.IsEmpty() ) if( token.IsEmpty() )
continue; continue;
if( userDefinedWarningError.Matches( token ) ) if( ( aFlags & FOR_ERC_DRC ) == 0 && userDefinedWarningError.Matches( token ) )
{ {
// Only show user-defined warnings/errors during ERC/DRC // Only show user-defined warnings/errors during ERC/DRC
} }

View File

@ -173,7 +173,7 @@ wxString DS_DRAW_ITEM_LIST::BuildFullText( const wxString& aTextbase )
} }
else if( m_titleBlock ) else if( m_titleBlock )
{ {
if( m_titleBlock->TextVarResolver( token, m_project ) ) if( m_titleBlock->TextVarResolver( token, m_project, m_flags ) )
{ {
// No need for tokenUpdated; TITLE_BLOCK::TextVarResolver() already goes // No need for tokenUpdated; TITLE_BLOCK::TextVarResolver() already goes
// up to the project. // up to the project.
@ -185,7 +185,7 @@ wxString DS_DRAW_ITEM_LIST::BuildFullText( const wxString& aTextbase )
m_titleBlock = nullptr; m_titleBlock = nullptr;
{ {
*token = ExpandTextVars( *token, &wsResolver ); *token = ExpandTextVars( *token, &wsResolver, m_flags );
} }
m_titleBlock = savedTitleBlock; m_titleBlock = savedTitleBlock;
@ -200,7 +200,7 @@ wxString DS_DRAW_ITEM_LIST::BuildFullText( const wxString& aTextbase )
if( tokenUpdated ) if( tokenUpdated )
{ {
*token = ExpandTextVars( *token, m_project ); *token = ExpandTextVars( *token, m_project, m_flags );
return true; return true;
} }
@ -210,7 +210,7 @@ wxString DS_DRAW_ITEM_LIST::BuildFullText( const wxString& aTextbase )
return false; return false;
}; };
return ExpandTextVars( aTextbase, &wsResolver ); return ExpandTextVars( aTextbase, &wsResolver, m_flags );
} }

View File

@ -92,7 +92,7 @@ void TITLE_BLOCK::GetContextualTextVars( wxArrayString* aVars )
} }
bool TITLE_BLOCK::TextVarResolver( wxString* aToken, const PROJECT* aProject ) const bool TITLE_BLOCK::TextVarResolver( wxString* aToken, const PROJECT* aProject, int aFlags ) const
{ {
bool tokenUpdated = false; bool tokenUpdated = false;
wxString originalToken = *aToken; wxString originalToken = *aToken;
@ -159,7 +159,7 @@ bool TITLE_BLOCK::TextVarResolver( wxString* aToken, const PROJECT* aProject ) c
if( aToken->IsSameAs( wxT( "CURRENT_DATE" ) ) ) if( aToken->IsSameAs( wxT( "CURRENT_DATE" ) ) )
*aToken = getCurrentDate(); *aToken = getCurrentDate();
else if( aProject ) else if( aProject )
*aToken = ExpandTextVars( *aToken, aProject ); *aToken = ExpandTextVars( *aToken, aProject, aFlags );
// This is the default fallback, so don't claim we resolved it // This is the default fallback, so don't claim we resolved it
if( *aToken == wxT( "${" ) + originalToken + wxT( "}" ) ) if( *aToken == wxT( "${" ) + originalToken + wxT( "}" ) )

View File

@ -182,7 +182,7 @@ int ERC_TESTER::TestDuplicateSheetNames( bool aCreateMarker )
void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet ) void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
{ {
DS_DRAW_ITEM_LIST wsItems( schIUScale ); DS_DRAW_ITEM_LIST wsItems( schIUScale, FOR_ERC_DRC );
auto unresolved = auto unresolved =
[this]( wxString str ) [this]( wxString str )
@ -193,7 +193,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
auto testAssertion = auto testAssertion =
[]( const SCH_ITEM* item, const SCH_SHEET_PATH& sheet, SCH_SCREEN* screen, []( const SCH_ITEM* item, const SCH_SHEET_PATH& sheet, SCH_SCREEN* screen,
const wxString& text ) const wxString& text, const VECTOR2I& pos )
{ {
static wxRegEx warningExpr( wxS( "^\\$\\{ERC_WARNING\\s*([^}]*)\\}(.*)$" ) ); static wxRegEx warningExpr( wxS( "^\\$\\{ERC_WARNING\\s*([^}]*)\\}(.*)$" ) );
static wxRegEx errorExpr( wxS( "^\\$\\{ERC_ERROR\\s*([^}]*)\\}(.*)$" ) ); static wxRegEx errorExpr( wxS( "^\\$\\{ERC_ERROR\\s*([^}]*)\\}(.*)$" ) );
@ -201,26 +201,42 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
if( warningExpr.Matches( text ) ) if( warningExpr.Matches( text ) )
{ {
std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_WARNING ); std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_WARNING );
wxString ercText = warningExpr.GetMatch( text, 1 );
if( item )
ercItem->SetItems( item );
else
ercText += _( " (in drawing sheet)" );
ercItem->SetItems( item );
ercItem->SetSheetSpecificPath( sheet ); ercItem->SetSheetSpecificPath( sheet );
ercItem->SetErrorMessage( warningExpr.GetMatch( text, 1 ) ); ercItem->SetErrorMessage( ercText );
SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() ); SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
screen->Append( marker ); screen->Append( marker );
return true;
} }
if( errorExpr.Matches( text ) ) if( errorExpr.Matches( text ) )
{ {
std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_ERROR ); std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_ERROR );
wxString ercText = errorExpr.GetMatch( text, 1 );
if( item )
ercItem->SetItems( item );
else
ercText += _( " (in drawing sheet)" );
ercItem->SetItems( item );
ercItem->SetSheetSpecificPath( sheet ); ercItem->SetSheetSpecificPath( sheet );
ercItem->SetErrorMessage( errorExpr.GetMatch( text, 1 ) ); ercItem->SetErrorMessage( ercText );
SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() ); SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
screen->Append( marker ); screen->Append( marker );
return true;
} }
return false;
}; };
if( aDrawingSheet ) if( aDrawingSheet )
@ -256,7 +272,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( &field, sheet, screen, field.GetText() ); testAssertion( &field, sheet, screen, field.GetText(), field.GetPosition() );
} }
symbol->GetLibSymbolRef()->RunOnChildren( symbol->GetLibSymbolRef()->RunOnChildren(
@ -284,7 +300,8 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( symbol, sheet, screen, textItem->GetText() ); testAssertion( symbol, sheet, screen, textItem->GetText(),
textItem->GetPosition() );
} }
else if( child->Type() == SCH_TEXTBOX_T ) else if( child->Type() == SCH_TEXTBOX_T )
{ {
@ -304,7 +321,8 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( symbol, sheet, screen, textboxItem->GetText() ); testAssertion( symbol, sheet, screen, textboxItem->GetText(),
textboxItem->GetPosition() );
} }
} ); } );
} }
@ -322,7 +340,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( &field, sheet, screen, field.GetText() ); testAssertion( &field, sheet, screen, field.GetText(), field.GetPosition() );
} }
} }
else if( item->Type() == SCH_SHEET_T ) else if( item->Type() == SCH_SHEET_T )
@ -341,7 +359,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( &field, sheet, screen, field.GetText() ); testAssertion( &field, sheet, screen, field.GetText(), field.GetPosition() );
} }
SCH_SHEET_PATH subSheetPath = sheet; SCH_SHEET_PATH subSheetPath = sheet;
@ -372,7 +390,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( text, sheet, screen, text->GetText() ); testAssertion( text, sheet, screen, text->GetText(), text->GetPosition() );
} }
else if( SCH_TEXTBOX* textBox = dynamic_cast<SCH_TEXTBOX*>( item ) ) else if( SCH_TEXTBOX* textBox = dynamic_cast<SCH_TEXTBOX*>( item ) )
{ {
@ -386,7 +404,7 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
screen->Append( marker ); screen->Append( marker );
} }
testAssertion( textBox, sheet, screen, textBox->GetText() ); testAssertion( textBox, sheet, screen, textBox->GetText(), textBox->GetPosition() );
} }
} }
@ -394,7 +412,11 @@ void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
{ {
if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) ) if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) )
{ {
if( text->GetShownText( true ).Matches( wxS( "*${*}*" ) ) ) if( testAssertion( nullptr, sheet, screen, text->GetText(), text->GetPosition() ) )
{
// Don't run unresolved test
}
else if( text->GetShownText( true ).Matches( wxS( "*${*}*" ) ) )
{ {
std::shared_ptr<ERC_ITEM> erc = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE ); std::shared_ptr<ERC_ITEM> erc = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
erc->SetErrorMessage( _( "Unresolved text variable in drawing sheet" ) ); erc->SetErrorMessage( _( "Unresolved text variable in drawing sheet" ) );

View File

@ -88,10 +88,14 @@ KICOMMON_API const wxString ExpandEnvVarSubstitutions( const wxString& aString,
/** /**
* Expand '${var-name}' templates in text. * Expand '${var-name}' templates in text.
*/ */
KICOMMON_API wxString ExpandTextVars( const wxString& aSource, #define FOR_ERC_DRC 1
const std::function<bool( wxString* )>* aResolver );
KICOMMON_API wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject ); KICOMMON_API wxString ExpandTextVars( const wxString& aSource,
const std::function<bool( wxString* )>* aResolver,
int aFlags = 0 );
KICOMMON_API wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject,
int aFlags = 0 );
/** /**
* Returns any variables unexpanded, e.g. ${VAR} -> VAR * Returns any variables unexpanded, e.g. ${VAR} -> VAR

View File

@ -400,7 +400,7 @@ private:
class DS_DRAW_ITEM_LIST class DS_DRAW_ITEM_LIST
{ {
public: public:
DS_DRAW_ITEM_LIST( const EDA_IU_SCALE& aIuScale ) : DS_DRAW_ITEM_LIST( const EDA_IU_SCALE& aIuScale, int aFlags = 0 ) :
m_iuScale( aIuScale ) m_iuScale( aIuScale )
{ {
m_idx = 0; m_idx = 0;
@ -411,6 +411,7 @@ public:
m_titleBlock = nullptr; m_titleBlock = nullptr;
m_project = nullptr; m_project = nullptr;
m_isFirstPage = true; m_isFirstPage = true;
m_flags = aFlags;
m_properties = nullptr; m_properties = nullptr;
} }
@ -578,6 +579,7 @@ protected:
wxString m_pageNumber; ///< The actual page number displayed in the title block. wxString m_pageNumber; ///< The actual page number displayed in the title block.
wxString m_sheetLayer; // for text variable references wxString m_sheetLayer; // for text variable references
const PROJECT* m_project; // for project-based text variable references const PROJECT* m_project; // for project-based text variable references
int m_flags;
const std::map<wxString, wxString>* m_properties; // for text variable references const std::map<wxString, wxString>* m_properties; // for text variable references
}; };

View File

@ -116,7 +116,7 @@ public:
} }
static void GetContextualTextVars( wxArrayString* aVars ); static void GetContextualTextVars( wxArrayString* aVars );
bool TextVarResolver( wxString* aToken, const PROJECT* aProject ) const; bool TextVarResolver( wxString* aToken, const PROJECT* aProject, int aFlags = 0 ) const;
/** /**
* Output the object to \a aFormatter in s-expression form. * Output the object to \a aFormatter in s-expression form.

View File

@ -280,44 +280,6 @@ void DRC_TEST_PROVIDER_MISC::testAssertions()
} ); } );
} }
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_GENERIC_WARNING ) )
{
if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( item ) )
{
static wxRegEx warningExpr( wxS( "^\\$\\{DRC_WARNING\\s*([^}]*)\\}(.*)$" ) );
wxString text = textItem->GetText();
if( warningExpr.Matches( text ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_GENERIC_WARNING );
drcItem->SetItems( item );
drcItem->SetErrorMessage( warningExpr.GetMatch( text, 1 ) );
reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
}
}
}
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_GENERIC_ERROR ) )
{
if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( item ) )
{
static wxRegEx errorExpr( wxS( "^\\$\\{DRC_ERROR\\s*([^}]*)\\}(.*)$" ) );
wxString text = textItem->GetText();
if( errorExpr.Matches( text ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_GENERIC_ERROR );
drcItem->SetItems( item );
drcItem->SetErrorMessage( errorExpr.GetMatch( text, 1 ) );
reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
}
}
}
return true; return true;
}; };
@ -339,6 +301,55 @@ void DRC_TEST_PROVIDER_MISC::testTextVars()
PCB_DIMENSION_T PCB_DIMENSION_T
}; };
auto testAssertion =
[&]( BOARD_ITEM* item, const wxString& text, const VECTOR2I& pos, int layer )
{
static wxRegEx warningExpr( wxS( "^\\$\\{DRC_WARNING\\s*([^}]*)\\}(.*)$" ) );
static wxRegEx errorExpr( wxS( "^\\$\\{DRC_ERROR\\s*([^}]*)\\}(.*)$" ) );
if( warningExpr.Matches( text ) )
{
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_GENERIC_WARNING ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_GENERIC_WARNING );
wxString drcText = warningExpr.GetMatch( text, 1 );
if( item )
drcItem->SetItems( item );
else
drcText += _( " (in drawing sheet)" );
drcItem->SetErrorMessage( drcText );
reportViolation( drcItem, pos, layer );
}
return true;
}
if( errorExpr.Matches( text ) )
{
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_GENERIC_ERROR ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_GENERIC_ERROR );
wxString drcText = errorExpr.GetMatch( text, 1 );
if( item )
drcItem->SetItems( item );
else
drcText += _( " (in drawing sheet)" );
drcItem->SetErrorMessage( drcText );
reportViolation( drcItem, pos, layer );
}
return true;
}
return false;
};
forEachGeometryItem( itemTypes, LSET::AllLayersMask(), forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
{ {
@ -367,13 +378,15 @@ void DRC_TEST_PROVIDER_MISC::testTextVars()
reportViolation( drcItem, item->GetPosition(), item->GetLayer() ); reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
} }
testAssertion( item, textItem->GetText(), item->GetPosition(), item->GetLayer() );
} }
return true; return true;
} ); } );
DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet(); DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet();
DS_DRAW_ITEM_LIST drawItems( pcbIUScale ); DS_DRAW_ITEM_LIST drawItems( pcbIUScale, FOR_ERC_DRC );
if( !drawingSheet || m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) ) if( !drawingSheet || m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
return; return;
@ -394,14 +407,19 @@ void DRC_TEST_PROVIDER_MISC::testTextVars()
if( m_drcEngine->IsCancelled() ) if( m_drcEngine->IsCancelled() )
return; return;
DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ); if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) )
if( text && text->GetShownText( true ).Matches( wxT( "*${*}*" ) ) )
{ {
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNRESOLVED_VARIABLE ); if( testAssertion( nullptr, text->GetText(), text->GetPosition(), LAYER_DRAWINGSHEET ) )
drcItem->SetItems( drawingSheet ); {
// Don't run unresolved test
}
else if( text->GetShownText( true ).Matches( wxT( "*${*}*" ) ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNRESOLVED_VARIABLE );
drcItem->SetItems( drawingSheet );
reportViolation( drcItem, text->GetPosition(), LAYER_DRAWINGSHEET ); reportViolation( drcItem, text->GetPosition(), LAYER_DRAWINGSHEET );
}
} }
} }
} }