1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 #include "connection_graph.h"
27 #include <common.h>     // for ExpandEnvVarSubstitutions
28 #include <erc.h>
29 #include <string_utils.h>
30 #include <lib_pin.h>
31 #include <sch_edit_frame.h>
32 #include <sch_marker.h>
33 #include <sch_reference_list.h>
34 #include <sch_sheet.h>
35 #include <sch_sheet_pin.h>
36 #include <schematic.h>
37 #include <drawing_sheet/ds_draw_item.h>
38 #include <drawing_sheet/ds_proxy_view_item.h>
39 #include <wx/ffile.h>
40 
41 
42 /* ERC tests :
43  *  1 - conflicts between connected pins ( example: 2 connected outputs )
44  *  2 - minimal connections requirements ( 1 input *must* be connected to an
45  * output, or a passive pin )
46  */
47 
48 /*
49  *  Minimal ERC requirements:
50  *  All pins *must* be connected (except ELECTRICAL_PINTYPE::PT_NC).
51  *  When a pin is not connected in schematic, the user must place a "non
52  * connected" symbol to this pin.
53  *  This ensures a forgotten connection will be detected.
54  */
55 
56 // Messages for matrix rows:
57 const wxString CommentERC_H[] =
58 {
59     _( "Input Pin" ),
60     _( "Output Pin" ),
61     _( "Bidirectional Pin" ),
62     _( "Tri-State Pin" ),
63     _( "Passive Pin" ),
64     _( "Free Pin" ),
65     _( "Unspecified Pin" ),
66     _( "Power Input Pin" ),
67     _( "Power Output Pin" ),
68     _( "Open Collector" ),
69     _( "Open Emitter" ),
70     _( "No Connection" )
71 };
72 
73 // Messages for matrix columns
74 const wxString CommentERC_V[] =
75 {
76     _( "Input Pin" ),
77     _( "Output Pin" ),
78     _( "Bidirectional Pin" ),
79     _( "Tri-State Pin" ),
80     _( "Passive Pin" ),
81     _( "Free Pin" ),
82     _( "Unspecified Pin" ),
83     _( "Power Input Pin" ),
84     _( "Power Output Pin" ),
85     _( "Open Collector" ),
86     _( "Open Emitter" ),
87     _( "No Connection" )
88 };
89 
90 
91 // List of pin types that are considered drivers for usual input pins
92 // i.e. pin type = ELECTRICAL_PINTYPE::PT_INPUT, but not PT_POWER_IN
93 // that need only a PT_POWER_OUT pin type to be driven
94 const std::set<ELECTRICAL_PINTYPE> DrivingPinTypes =
95         {
96             ELECTRICAL_PINTYPE::PT_OUTPUT,
97             ELECTRICAL_PINTYPE::PT_POWER_OUT,
98             ELECTRICAL_PINTYPE::PT_PASSIVE,
99             ELECTRICAL_PINTYPE::PT_TRISTATE,
100             ELECTRICAL_PINTYPE::PT_BIDI
101         };
102 
103 // List of pin types that are considered drivers for power pins
104 // In fact only a ELECTRICAL_PINTYPE::PT_POWER_OUT pin type can drive
105 // power input pins
106 const std::set<ELECTRICAL_PINTYPE> DrivingPowerPinTypes =
107         {
108             ELECTRICAL_PINTYPE::PT_POWER_OUT
109         };
110 
111 // List of pin types that require a driver elsewhere on the net
112 const std::set<ELECTRICAL_PINTYPE> DrivenPinTypes =
113         {
114             ELECTRICAL_PINTYPE::PT_INPUT,
115             ELECTRICAL_PINTYPE::PT_POWER_IN
116         };
117 
TestDuplicateSheetNames(bool aCreateMarker)118 int ERC_TESTER::TestDuplicateSheetNames( bool aCreateMarker )
119 {
120     SCH_SCREEN* screen;
121     int         err_count = 0;
122 
123     SCH_SCREENS screenList( m_schematic->Root() );
124 
125     for( screen = screenList.GetFirst(); screen != nullptr; screen = screenList.GetNext() )
126     {
127         std::vector<SCH_SHEET*> list;
128 
129         for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
130             list.push_back( static_cast<SCH_SHEET*>( item ) );
131 
132         for( size_t i = 0; i < list.size(); i++ )
133         {
134             SCH_SHEET* sheet = list[i];
135 
136             for( size_t j = i + 1; j < list.size(); j++ )
137             {
138                 SCH_SHEET* test_item = list[j];
139 
140                 // We have found a second sheet: compare names
141                 // we are using case insensitive comparison to avoid mistakes between
142                 // similar names like Mysheet and mysheet
143                 if( sheet->GetName().CmpNoCase( test_item->GetName() ) == 0 )
144                 {
145                     if( aCreateMarker )
146                     {
147                         std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DUPLICATE_SHEET_NAME );
148                         ercItem->SetItems( sheet, test_item );
149 
150                         SCH_MARKER* marker = new SCH_MARKER( ercItem, sheet->GetPosition() );
151                         screen->Append( marker );
152                     }
153 
154                     err_count++;
155                 }
156             }
157         }
158     }
159 
160     return err_count;
161 }
162 
163 
TestTextVars(DS_PROXY_VIEW_ITEM * aDrawingSheet)164 void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
165 {
166     DS_DRAW_ITEM_LIST wsItems;
167 
168     auto unresolved = [this]( wxString str )
169     {
170         str = ExpandEnvVarSubstitutions( str, &m_schematic->Prj() );
171         return str.Matches( wxT( "*${*}*" ) );
172     };
173 
174     if( aDrawingSheet )
175     {
176         wsItems.SetMilsToIUfactor( IU_PER_MILS );
177         wsItems.SetPageNumber( "1" );
178         wsItems.SetSheetCount( 1 );
179         wsItems.SetFileName( "dummyFilename" );
180         wsItems.SetSheetName( "dummySheet" );
181         wsItems.SetSheetLayer( "dummyLayer" );
182         wsItems.SetProject( &m_schematic->Prj() );
183         wsItems.BuildDrawItemsList( aDrawingSheet->GetPageInfo(), aDrawingSheet->GetTitleBlock());
184     }
185 
186     SCH_SHEET_PATH  savedCurrentSheet = m_schematic->CurrentSheet();
187     SCH_SHEET_LIST  sheets = m_schematic->GetSheets();
188 
189     for( SCH_SHEET_PATH& sheet : sheets )
190     {
191         m_schematic->SetCurrentSheet( sheet );
192         SCH_SCREEN* screen = sheet.LastScreen();
193 
194         for( SCH_ITEM* item : screen->Items().OfType( SCH_LOCATE_ANY_T ) )
195         {
196             if( item->Type() == SCH_SYMBOL_T )
197             {
198                 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
199 
200                 for( SCH_FIELD& field : symbol->GetFields() )
201                 {
202                     if( unresolved( field.GetShownText() ) )
203                     {
204                         wxPoint pos = field.GetPosition() - symbol->GetPosition();
205                         pos = symbol->GetTransform().TransformCoordinate( pos );
206                         pos += symbol->GetPosition();
207 
208                         std::shared_ptr<ERC_ITEM> ercItem =
209                                 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
210                         ercItem->SetItems( &field );
211 
212                         SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
213                         screen->Append( marker );
214                     }
215                 }
216             }
217             else if( item->Type() == SCH_SHEET_T )
218             {
219                 SCH_SHEET* subSheet = static_cast<SCH_SHEET*>( item );
220 
221                 for( SCH_FIELD& field : subSheet->GetFields() )
222                 {
223                     if( unresolved( field.GetShownText() ) )
224                     {
225                         std::shared_ptr<ERC_ITEM> ercItem =
226                                 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
227                         ercItem->SetItems( &field );
228 
229                         SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
230                         screen->Append( marker );
231                     }
232                 }
233 
234                 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
235                 {
236                     if( pin->GetShownText().Matches( wxT( "*${*}*" ) ) )
237                     {
238                         std::shared_ptr<ERC_ITEM> ercItem =
239                                 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
240                         ercItem->SetItems( pin );
241 
242                         SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
243                         screen->Append( marker );
244                     }
245                 }
246             }
247             else if( SCH_TEXT* text = dynamic_cast<SCH_TEXT*>( item ) )
248             {
249                 if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
250                 {
251                     std::shared_ptr<ERC_ITEM> ercItem =
252                             ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
253                     ercItem->SetItems( text );
254 
255                     SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
256                     screen->Append( marker );
257                 }
258             }
259         }
260 
261         for( DS_DRAW_ITEM_BASE* item = wsItems.GetFirst(); item; item = wsItems.GetNext() )
262         {
263             if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) )
264             {
265                 if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
266                 {
267                     std::shared_ptr<ERC_ITEM> ercItem =
268                             ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
269                     ercItem->SetErrorMessage( _( "Unresolved text variable in drawing sheet." ) );
270 
271                     SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
272                     screen->Append( marker );
273                 }
274             }
275         }
276     }
277 
278     m_schematic->SetCurrentSheet( savedCurrentSheet );
279 }
280 
281 
TestConflictingBusAliases()282 int ERC_TESTER::TestConflictingBusAliases()
283 {
284     wxString    msg;
285     int         err_count = 0;
286 
287     SCH_SCREENS screens( m_schematic->Root() );
288     std::vector< std::shared_ptr<BUS_ALIAS> > aliases;
289 
290     for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
291     {
292         std::unordered_set< std::shared_ptr<BUS_ALIAS> > screen_aliases = screen->GetBusAliases();
293 
294         for( const std::shared_ptr<BUS_ALIAS>& alias : screen_aliases )
295         {
296             for( const std::shared_ptr<BUS_ALIAS>& test : aliases )
297             {
298                 if( alias->GetName() == test->GetName() && alias->Members() != test->Members() )
299                 {
300                     msg.Printf( _( "Bus alias %s has conflicting definitions on %s and %s" ),
301                                 alias->GetName(),
302                                 alias->GetParent()->GetFileName(),
303                                 test->GetParent()->GetFileName() );
304 
305                     std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ALIAS_CONFLICT );
306                     ercItem->SetErrorMessage( msg );
307 
308                     SCH_MARKER* marker = new SCH_MARKER( ercItem, wxPoint() );
309                     test->GetParent()->Append( marker );
310 
311                     ++err_count;
312                 }
313             }
314         }
315 
316         aliases.insert( aliases.end(), screen_aliases.begin(), screen_aliases.end() );
317     }
318 
319     return err_count;
320 }
321 
322 
TestMultiunitFootprints()323 int ERC_TESTER::TestMultiunitFootprints()
324 {
325     SCH_SHEET_LIST sheets = m_schematic->GetSheets();
326 
327     int errors = 0;
328     std::map<wxString, LIB_ID> footprints;
329     SCH_MULTI_UNIT_REFERENCE_MAP refMap;
330     sheets.GetMultiUnitSymbols( refMap, true );
331 
332     for( std::pair<const wxString, SCH_REFERENCE_LIST>& symbol : refMap )
333     {
334         SCH_REFERENCE_LIST& refList = symbol.second;
335 
336         if( refList.GetCount() == 0 )
337         {
338             wxFAIL;   // it should not happen
339             continue;
340         }
341 
342         // Reference footprint
343         SCH_SYMBOL* unit = nullptr;
344         wxString    unitName;
345         wxString    unitFP;
346 
347         for( unsigned i = 0; i < refList.GetCount(); ++i )
348         {
349             SCH_SHEET_PATH sheetPath = refList.GetItem( i ).GetSheetPath();
350             unitFP = refList.GetItem( i ).GetFootprint();
351 
352             if( !unitFP.IsEmpty() )
353             {
354                 unit = refList.GetItem( i ).GetSymbol();
355                 unitName = unit->GetRef( &sheetPath, true );
356                 break;
357             }
358         }
359 
360         for( unsigned i = 0; i < refList.GetCount(); ++i )
361         {
362             SCH_REFERENCE& secondRef = refList.GetItem( i );
363             SCH_SYMBOL*    secondUnit = secondRef.GetSymbol();
364             wxString       secondName = secondUnit->GetRef( &secondRef.GetSheetPath(), true );
365             const wxString secondFp = secondRef.GetFootprint();
366             wxString       msg;
367 
368             if( unit && !secondFp.IsEmpty() && unitFP != secondFp )
369             {
370                 msg.Printf( _( "Different footprints assigned to %s and %s" ),
371                             unitName, secondName );
372 
373                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_FP );
374                 ercItem->SetErrorMessage( msg );
375                 ercItem->SetItems( unit, secondUnit );
376 
377                 SCH_MARKER* marker = new SCH_MARKER( ercItem, secondUnit->GetPosition() );
378                 secondRef.GetSheetPath().LastScreen()->Append( marker );
379 
380                 ++errors;
381             }
382         }
383     }
384 
385     return errors;
386 }
387 
388 
TestNoConnectPins()389 int ERC_TESTER::TestNoConnectPins()
390 {
391     int err_count = 0;
392 
393     for( const SCH_SHEET_PATH& sheet : m_schematic->GetSheets() )
394     {
395         std::map<wxPoint, std::vector<SCH_PIN*>> pinMap;
396 
397         for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
398         {
399             SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
400 
401             for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
402             {
403                 if( pin->GetLibPin()->GetType() == ELECTRICAL_PINTYPE::PT_NC )
404                     pinMap[pin->GetPosition()].emplace_back( pin );
405             }
406         }
407 
408         for( auto& pair : pinMap )
409         {
410             if( pair.second.size() > 1 )
411             {
412                 err_count++;
413 
414                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
415 
416                 ercItem->SetItems( pair.second[0], pair.second[1],
417                                    pair.second.size() > 2 ? pair.second[2] : nullptr,
418                                    pair.second.size() > 3 ? pair.second[3] : nullptr );
419                 ercItem->SetErrorMessage( _( "Pins with \"no connection\" type are connected" ) );
420 
421                 SCH_MARKER* marker = new SCH_MARKER( ercItem, pair.first );
422                 sheet.LastScreen()->Append( marker );
423             }
424         }
425     }
426 
427     return err_count;
428 }
429 
430 
TestPinToPin()431 int ERC_TESTER::TestPinToPin()
432 {
433     ERC_SETTINGS&  settings = m_schematic->ErcSettings();
434     const NET_MAP& nets     = m_schematic->ConnectionGraph()->GetNetMap();
435 
436     int errors = 0;
437 
438     for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
439     {
440         std::vector<SCH_PIN*> pins;
441         std::unordered_map<EDA_ITEM*, SCH_SCREEN*> pinToScreenMap;
442 
443         for( CONNECTION_SUBGRAPH* subgraph: net.second )
444         {
445             for( EDA_ITEM* item : subgraph->m_items )
446             {
447                 if( item->Type() == SCH_PIN_T )
448                 {
449                     pins.emplace_back( static_cast<SCH_PIN*>( item ) );
450                     pinToScreenMap[item] = subgraph->m_sheet.LastScreen();
451                 }
452             }
453         }
454 
455         // Single-pin nets are handled elsewhere
456         if( pins.size() < 2 )
457             continue;
458 
459         std::set<std::pair<SCH_PIN*, SCH_PIN*>> tested;
460 
461         SCH_PIN* needsDriver = nullptr;
462         bool     hasDriver   = false;
463 
464         // We need different drivers for power nets and normal nets.
465         // A power net has at least one pin having the ELECTRICAL_PINTYPE::PT_POWER_IN
466         // and power nets can be driven only by ELECTRICAL_PINTYPE::PT_POWER_OUT pins
467         bool     ispowerNet  = false;
468 
469         for( SCH_PIN* refPin : pins )
470         {
471             if( refPin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
472             {
473                 ispowerNet = true;
474                 break;
475             }
476         }
477 
478         for( SCH_PIN* refPin : pins )
479         {
480             ELECTRICAL_PINTYPE refType = refPin->GetType();
481 
482             if( DrivenPinTypes.count( refType ) )
483             {
484                 // needsDriver will be the pin shown in the error report eventually, so try to
485                 // upgrade to a "better" pin if possible: something visible and only a power symbol
486                 // if this net needs a power driver
487                 if( !needsDriver ||
488                     ( !needsDriver->IsVisible() && refPin->IsVisible() ) ||
489                     ( ispowerNet != ( needsDriver->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN ) &&
490                       ispowerNet == ( refType == ELECTRICAL_PINTYPE::PT_POWER_IN ) ) )
491                 {
492                     needsDriver = refPin;
493                 }
494             }
495 
496             if( ispowerNet )
497                 hasDriver |= ( DrivingPowerPinTypes.count( refType ) != 0 );
498             else
499                 hasDriver |= ( DrivingPinTypes.count( refType ) != 0 );
500 
501             for( SCH_PIN* testPin : pins )
502             {
503                 if( testPin == refPin )
504                     continue;
505 
506                 std::pair<SCH_PIN*, SCH_PIN*> pair1 = std::make_pair( refPin, testPin );
507                 std::pair<SCH_PIN*, SCH_PIN*> pair2 = std::make_pair( testPin, refPin );
508 
509                 if( tested.count( pair1 ) || tested.count( pair2 ) )
510                     continue;
511 
512                 tested.insert( pair1 );
513                 tested.insert( pair2 );
514 
515                 ELECTRICAL_PINTYPE testType = testPin->GetType();
516 
517                 if( ispowerNet )
518                     hasDriver |= ( DrivingPowerPinTypes.count( testType ) != 0 );
519                 else
520                     hasDriver |= ( DrivingPinTypes.count( testType ) != 0 );
521 
522                 PIN_ERROR erc = settings.GetPinMapValue( refType, testType );
523 
524                 if( erc != PIN_ERROR::OK && settings.IsTestEnabled( ERCE_PIN_TO_PIN_WARNING ) )
525                 {
526                     std::shared_ptr<ERC_ITEM> ercItem =
527                             ERC_ITEM::Create( erc == PIN_ERROR::WARNING ? ERCE_PIN_TO_PIN_WARNING :
528                                                                           ERCE_PIN_TO_PIN_ERROR );
529                     ercItem->SetItems( refPin, testPin );
530                     ercItem->SetIsSheetSpecific();
531 
532                     ercItem->SetErrorMessage(
533                             wxString::Format( _( "Pins of type %s and %s are connected" ),
534                                               ElectricalPinTypeGetText( refType ),
535                                               ElectricalPinTypeGetText( testType ) ) );
536 
537                     SCH_MARKER* marker = new SCH_MARKER( ercItem,
538                                                          refPin->GetTransformedPosition() );
539                     pinToScreenMap[refPin]->Append( marker );
540                     errors++;
541                 }
542             }
543         }
544 
545         if( needsDriver && !hasDriver )
546         {
547             int err_code = ispowerNet ? ERCE_POWERPIN_NOT_DRIVEN : ERCE_PIN_NOT_DRIVEN;
548 
549             if( settings.IsTestEnabled( err_code ) )
550             {
551                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( err_code );
552 
553                 ercItem->SetItems( needsDriver );
554 
555                 SCH_MARKER* marker = new SCH_MARKER( ercItem,
556                                                      needsDriver->GetTransformedPosition() );
557                 pinToScreenMap[needsDriver]->Append( marker );
558                 errors++;
559             }
560         }
561     }
562 
563     return errors;
564 }
565 
566 
TestMultUnitPinConflicts()567 int ERC_TESTER::TestMultUnitPinConflicts()
568 {
569     const NET_MAP& nets = m_schematic->ConnectionGraph()->GetNetMap();
570 
571     int errors = 0;
572 
573     std::unordered_map<wxString, std::pair<wxString, SCH_PIN*>> pinToNetMap;
574 
575     for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
576     {
577         const wxString& netName = net.first.first;
578         std::vector<SCH_PIN*> pins;
579 
580         for( CONNECTION_SUBGRAPH* subgraph : net.second )
581         {
582             for( EDA_ITEM* item : subgraph->m_items )
583             {
584                 if( item->Type() == SCH_PIN_T )
585                 {
586                     SCH_PIN* pin = static_cast<SCH_PIN*>( item );
587 
588                     if( !pin->GetLibPin()->GetParent()->IsMulti() )
589                         continue;
590 
591                     wxString name = pin->GetParentSymbol()->GetRef( &subgraph->m_sheet ) +
592                                       + ":" + pin->GetShownNumber();
593 
594                     if( !pinToNetMap.count( name ) )
595                     {
596                         pinToNetMap[name] = std::make_pair( netName, pin );
597                     }
598                     else if( pinToNetMap[name].first != netName )
599                     {
600                         std::shared_ptr<ERC_ITEM> ercItem =
601                                 ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_NET );
602 
603                         ercItem->SetErrorMessage( wxString::Format(
604                                 _( "Pin %s is connected to both %s and %s" ),
605                                 pin->GetShownNumber(),
606                                 netName,
607                                 pinToNetMap[name].first ) );
608 
609                         ercItem->SetItems( pin, pinToNetMap[name].second );
610                         ercItem->SetIsSheetSpecific();
611 
612                         SCH_MARKER* marker = new SCH_MARKER( ercItem,
613                                                              pin->GetTransformedPosition() );
614                         subgraph->m_sheet.LastScreen()->Append( marker );
615                         errors += 1;
616                     }
617                 }
618             }
619         }
620     }
621 
622     return errors;
623 }
624 
625 
TestSimilarLabels()626 int ERC_TESTER::TestSimilarLabels()
627 {
628     const NET_MAP& nets = m_schematic->ConnectionGraph()->GetNetMap();
629 
630     int errors = 0;
631 
632     std::unordered_map<wxString, SCH_TEXT*> labelMap;
633 
634     for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
635     {
636         std::vector<SCH_PIN*> pins;
637 
638         for( CONNECTION_SUBGRAPH* subgraph : net.second )
639         {
640             for( EDA_ITEM* item : subgraph->m_items )
641             {
642                 switch( item->Type() )
643                 {
644                 case SCH_LABEL_T:
645                 case SCH_HIER_LABEL_T:
646                 case SCH_GLOBAL_LABEL_T:
647                 {
648                     SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
649 
650                     wxString normalized = text->GetShownText().Lower();
651 
652                     if( !labelMap.count( normalized ) )
653                     {
654                         labelMap[normalized] = text;
655                     }
656                     else if( labelMap.at( normalized )->GetShownText() != text->GetShownText() )
657                     {
658                         std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SIMILAR_LABELS );
659                         ercItem->SetItems( text, labelMap.at( normalized ) );
660 
661                         SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
662                         subgraph->m_sheet.LastScreen()->Append( marker );
663                         errors += 1;
664                     }
665 
666                     break;
667                 }
668 
669                 default:
670                     break;
671                 }
672             }
673         }
674     }
675 
676     return errors;
677 }
678 
679 
TestLibSymbolIssues()680 int ERC_TESTER::TestLibSymbolIssues()
681 {
682     wxCHECK( m_schematic, 0 );
683 
684     SYMBOL_LIB_TABLE* libTable = m_schematic->Prj().SchSymbolLibTable();
685     wxString          msg;
686     int               err_count = 0;
687 
688     SCH_SCREENS screens( m_schematic->Root() );
689 
690     for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
691     {
692         std::vector<SCH_MARKER*> markers;
693 
694         for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
695         {
696             SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
697 
698             wxCHECK2( symbol, continue );
699 
700             LIB_SYMBOL* libSymbolInSchematic = symbol->GetLibSymbolRef().get();
701 
702             wxCHECK2( libSymbolInSchematic, continue );
703 
704             wxString       libName = symbol->GetLibId().GetLibNickname();
705             LIB_TABLE_ROW* libTableRow = libTable->FindRow( libName, true );
706 
707             if( !libTableRow )
708             {
709                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
710                 ercItem->SetItems( symbol );
711                 msg.Printf( _( "The current configuration does not include the library '%s'." ),
712                             UnescapeString( libName ) );
713                 ercItem->SetErrorMessage( msg );
714 
715                 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
716                 continue;
717             }
718             else if( !libTable->HasLibrary( libName, true ) )
719             {
720                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
721                 ercItem->SetItems( symbol );
722                 msg.Printf( _( "The library '%s' is not enabled in the current configuration." ),
723                             UnescapeString( libName ) );
724                 ercItem->SetErrorMessage( msg );
725 
726                 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
727                 continue;
728             }
729 
730             wxString    symbolName = symbol->GetLibId().GetLibItemName();
731             LIB_SYMBOL* libSymbol = SchGetLibSymbol( symbol->GetLibId(), libTable );
732 
733             if( libSymbol == nullptr )
734             {
735                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
736                 ercItem->SetItems( symbol );
737                 msg.Printf( "Symbol '%s' not found in symbol library '%s'.",
738                             UnescapeString( symbolName ),
739                             UnescapeString( libName ) );
740                 ercItem->SetErrorMessage( msg );
741 
742                 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
743                 continue;
744             }
745 
746             std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
747 
748             if( *flattenedSymbol != *libSymbolInSchematic )
749             {
750                 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
751                 ercItem->SetItems( symbol );
752                 msg.Printf( "Symbol '%s' has been modified in library '%s'.",
753                             UnescapeString( symbolName ),
754                             UnescapeString( libName ) );
755                 ercItem->SetErrorMessage( msg );
756 
757                 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
758             }
759         }
760 
761         for( SCH_MARKER* marker : markers )
762         {
763             screen->Append( marker );
764             err_count += 1;
765         }
766     }
767 
768     return err_count;
769 }
770