Use GetType() for pin alternate electrical type queries

When a pin has an alternate function selected, GetType() correctly
returns the alternate's type. However, GetCanonicalElectricalTypeName(),
GetElectricalTypeName(), and PlotPinType() were accessing m_type directly,
bypassing the alternate type lookup. This caused netlist exports to show
the wrong pin type (e.g., "no_connect" instead of "power_in") and the
NC symbol to be drawn incorrectly when alternates changed the pin type.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/22286

(cherry picked from commit 25430cfbb8)
This commit is contained in:
Seth Hillbrand
2026-01-14 11:09:15 -08:00
parent f1b0926699
commit 18575df311
7 changed files with 2979 additions and 15 deletions
+5 -15
View File
@@ -308,25 +308,15 @@ void SCH_PIN::SetType( ELECTRICAL_PINTYPE aType )
wxString SCH_PIN::GetCanonicalElectricalTypeName() const
{
if( m_type != ELECTRICAL_PINTYPE::PT_INHERIT )
return ::GetCanonicalElectricalTypeName( m_type );
if( !m_libPin )
return ::GetCanonicalElectricalTypeName( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
return m_libPin->GetCanonicalElectricalTypeName();
// Use GetType() which correctly handles alternates
return ::GetCanonicalElectricalTypeName( GetType() );
}
wxString SCH_PIN::GetElectricalTypeName() const
{
if( m_type != ELECTRICAL_PINTYPE::PT_INHERIT )
return ElectricalPinTypeGetText( m_type );
if( !m_libPin )
return ElectricalPinTypeGetText( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
return m_libPin->GetElectricalTypeName();
// Use GetType() which correctly handles alternates
return ElectricalPinTypeGetText( GetType() );
}
@@ -1186,7 +1176,7 @@ void SCH_PIN::PlotPinType( PLOTTER *aPlotter, const VECTOR2I &aPosition,
y1 + ( MapY1 + MapX1 ) * deco_size ) );
}
if( m_type == ELECTRICAL_PINTYPE::PT_NC ) // Draw a N.C. symbol
if( GetType() == ELECTRICAL_PINTYPE::PT_NC ) // Draw a N.C. symbol
{
const int deco_size = TARGET_PIN_RADIUS;
const int ex1 = aPosition.x;
@@ -0,0 +1,660 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_barcodes": false,
"apply_defaults_to_fp_dimensions": false,
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": true,
"text_position": 0,
"units_format": 0
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.8,
"height": 1.27,
"width": 2.54
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_field_mismatch": "warning",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_to_hole": "warning",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"missing_tuning_profile": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_on_edge_cuts": "error",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_on_post_machined_layer": "error",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"tuning_profile_track_geometries": "ignore",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_groove_width": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"component_class_settings": {
"assignments": [],
"meta": {
"version": 0
},
"sheet_component_classes": {
"enabled": false
}
},
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"ground_pin_not_ground": "warning",
"hier_label_mismatch": "error",
"isolated_pin_label": "warning",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"stacked_pin_name": "warning",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "bugtest.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"tuning_profile": "",
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 5
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"annotation": {
"method": 0,
"sort_order": 0
},
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "Default Editing",
"sort_asc": true,
"sort_field": "Reference"
},
"bus_aliases": {},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"hop_over_size_choice": 0,
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.25,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 0.0,
"text_offset_ratio": 0.08
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"reuse_designators": true,
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0,
"top_level_sheets": [
{
"filename": "bugtest.kicad_sch",
"name": "bugtest",
"uuid": "084ee3b1-71f6-47a6-9dc8-ecb4f5c55a9a"
}
],
"used_designators": "",
"variants": []
},
"sheets": [
[
"084ee3b1-71f6-47a6-9dc8-ecb4f5c55a9a",
"bugtest"
]
],
"text_variables": {},
"tuning_profiles": {
"meta": {
"version": 0
},
"tuning_profiles_impedance_geometric": []
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,392 @@
(kicad_symbol_lib
(version 20241209)
(generator "kicad_symbol_editor")
(generator_version "9.0")
(symbol "MOLEX_PICOBLADE_8PHOR"
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "J1"
(at 6.35 0.0001 0)
(effects
(font
(size 1.27 1.0795)
)
(justify left)
)
)
(property "Value" "MOLEX"
(at 6.35 -2.5399 0)
(effects
(font
(size 1.27 1.0795)
)
(justify left)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "1.25mm (.049\") Pitch PicoBlade Header, SMT, Vertical, Lead-free, 8 Circuits"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "LOCATION" "Front"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "FUNCTION" "Panel"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "MOLEX_PICOBLADE_8PHOR_1_0"
(polyline
(pts
(xy -2.54 12.7) (xy 5.08 12.7)
)
(stroke
(width 0.254)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy -2.54 -15.24) (xy -2.54 12.7)
)
(stroke
(width 0.254)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 5.08 12.7) (xy 5.08 -15.24)
)
(stroke
(width 0.254)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 5.08 -15.24) (xy -2.54 -15.24)
)
(stroke
(width 0.254)
(type solid)
)
(fill
(type none)
)
)
(text "${LOCATION}"
(at 2.286 -2.54 900)
(effects
(font
(size 1.27 1.0795)
)
(justify bottom)
)
)
(text "${FUNCTION}"
(at 3.81 -2.54 900)
(effects
(font
(size 1.27 1.0795)
)
(justify bottom)
)
)
(pin no_connect line
(at -5.08 7.62 0)
(length 2.54)
(name "1"
(effects
(font
(size 0 0)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "1.bidir" bidirectional line)
(alternate "1.free" free line)
(alternate "1.in" input line)
(alternate "1.oc" open_collector line)
(alternate "1.oe" open_emitter line)
(alternate "1.out" output line)
(alternate "1.pas" passive line)
(alternate "1.pow" power_in line)
(alternate "1.sup" power_out line)
(alternate "1.tristate" tri_state line)
(alternate "1.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 5.08 0)
(length 2.54)
(name "2"
(effects
(font
(size 0 0)
)
)
)
(number "2"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "2.bidir" bidirectional line)
(alternate "2.free" free line)
(alternate "2.in" input line)
(alternate "2.oc" open_collector line)
(alternate "2.oe" open_emitter line)
(alternate "2.out" output line)
(alternate "2.pas" passive line)
(alternate "2.pow" power_in line)
(alternate "2.sup" power_out line)
(alternate "2.tristate" tri_state line)
(alternate "2.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 2.54 0)
(length 2.54)
(name "3"
(effects
(font
(size 0 0)
)
)
)
(number "3"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "3.bidir" bidirectional line)
(alternate "3.free" free line)
(alternate "3.in" input line)
(alternate "3.oc" open_collector line)
(alternate "3.oe" open_emitter line)
(alternate "3.out" output line)
(alternate "3.pas" passive line)
(alternate "3.pow" power_in line)
(alternate "3.sup" power_out line)
(alternate "3.tristate" tri_state line)
(alternate "3.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 0 0)
(length 2.54)
(name "4"
(effects
(font
(size 0 0)
)
)
)
(number "4"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "4.bidir" bidirectional line)
(alternate "4.free" free line)
(alternate "4.in" input line)
(alternate "4.oc" open_collector line)
(alternate "4.oe" open_emitter line)
(alternate "4.out" output line)
(alternate "4.pas" passive line)
(alternate "4.pow" power_in line)
(alternate "4.sup" power_out line)
(alternate "4.tristate" tri_state line)
(alternate "4.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 -2.54 0)
(length 2.54)
(name "5"
(effects
(font
(size 0 0)
)
)
)
(number "5"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "5.bidir" bidirectional line)
(alternate "5.free" free line)
(alternate "5.in" input line)
(alternate "5.oc" open_collector line)
(alternate "5.oe" open_emitter line)
(alternate "5.out" output line)
(alternate "5.pas" passive line)
(alternate "5.pow" power_in line)
(alternate "5.sup" power_out line)
(alternate "5.tristate" tri_state line)
(alternate "5.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 -5.08 0)
(length 2.54)
(name "6"
(effects
(font
(size 0 0)
)
)
)
(number "6"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "6.bidir" bidirectional line)
(alternate "6.free" free line)
(alternate "6.in" input line)
(alternate "6.oc" open_collector line)
(alternate "6.oe" open_emitter line)
(alternate "6.out" output line)
(alternate "6.pas" passive line)
(alternate "6.pow" power_in line)
(alternate "6.sup" power_out line)
(alternate "6.tristate" tri_state line)
(alternate "6.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 -7.62 0)
(length 2.54)
(name "7"
(effects
(font
(size 0 0)
)
)
)
(number "7"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "7.bidir" bidirectional line)
(alternate "7.free" free line)
(alternate "7.in" input line)
(alternate "7.oc" open_collector line)
(alternate "7.oe" open_emitter line)
(alternate "7.out" output line)
(alternate "7.pas" passive line)
(alternate "7.pow" power_in line)
(alternate "7.sup" power_out line)
(alternate "7.tristate" tri_state line)
(alternate "7.unspec" unspecified line)
)
(pin no_connect line
(at -5.08 -10.16 0)
(length 2.54)
(name "8"
(effects
(font
(size 0 0)
)
)
)
(number "8"
(effects
(font
(size 1.27 1.27)
)
)
)
(alternate "8.bidir" bidirectional line)
(alternate "8.free" free line)
(alternate "8.in" input line)
(alternate "8.oc" open_collector line)
(alternate "8.oe" open_emitter line)
(alternate "8.out" output line)
(alternate "8.pas" passive line)
(alternate "8.pow" power_in line)
(alternate "8.sup" power_out line)
(alternate "8.tristate" tri_state line)
(alternate "8.unspec" unspecified line)
)
(pin passive line
(at -5.08 -15.24 0)
(length 2.54)
(name "G"
(effects
(font
(size 0 0)
)
)
)
(number "G"
(effects
(font
(size 0 0)
)
)
)
)
)
(embedded_fonts no)
)
)
+1
View File
@@ -68,6 +68,7 @@ set( QA_EESCHEMA_SRCS
test_issue18119_bus_hierarchy.cpp
test_issue18299_bus_member_rename.cpp
test_issue20173_paste_subsheet.cpp
test_issue22286_pin_alternate.cpp
test_issue16915_bus_netclass.cpp
test_nc_pin_connectivity.cpp
test_crossing_label.cpp
@@ -0,0 +1,197 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
/**
* @file
* Test for issue #22286 - Pin alternate type not being applied after schematic load.
*
* When a pin has an alternate function selected (like "8.pow" with type power_in),
* the pin TYPE should switch from the default (no_connect) to the alternate's type.
* This test verifies the fix by loading the actual reproduction schematic.
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include "eeschema_test_utils.h"
#include <lib_symbol.h>
#include <pin_type.h>
#include <sch_pin.h>
#include <sch_symbol.h>
#include <wildcards_and_files_ext.h>
class TEST_ISSUE22286_FIXTURE : public KI_TEST::SCHEMATIC_TEST_FIXTURE
{
protected:
wxFileName GetSchematicPath( const wxString& aBaseName ) override
{
wxFileName fn( aBaseName );
return fn;
}
public:
SCH_SYMBOL* FindSymbolByRef( const wxString& aRef )
{
if( !m_schematic )
return nullptr;
SCH_SCREEN* screen = m_schematic->RootScreen();
if( !screen )
return nullptr;
for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
if( symbol && symbol->GetRef( &m_schematic->Hierarchy()[0], false ) == aRef )
return symbol;
}
return nullptr;
}
SCH_PIN* FindPinByNumber( SCH_SYMBOL* aSymbol, const wxString& aNumber )
{
if( !aSymbol )
return nullptr;
for( SCH_PIN* pin : aSymbol->GetPins( &m_schematic->Hierarchy()[0] ) )
{
if( pin->GetNumber() == aNumber )
return pin;
}
return nullptr;
}
};
BOOST_FIXTURE_TEST_SUITE( Issue22286, TEST_ISSUE22286_FIXTURE )
/**
* Test that pin alternates are correctly applied after loading a schematic from disk.
*
* This test loads the actual reproduction schematic for issue #22286 and verifies that:
* 1. Pin 8 on symbol J1 has the alternate "8.pow" set
* 2. Pin 8's GetType() returns PT_POWER_IN (not PT_NC)
* 3. The library pin has the alternates map populated
*/
BOOST_AUTO_TEST_CASE( PinAlternateTypeAfterSchematicLoad )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "issue22286" ) );
fn.SetName( wxS( "bugtest" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
// Find symbol J1 (the MOLEX connector)
SCH_SYMBOL* j1 = FindSymbolByRef( wxS( "J1" ) );
BOOST_REQUIRE_MESSAGE( j1 != nullptr, "Symbol J1 not found in schematic" );
// Find pin 8
SCH_PIN* pin8 = FindPinByNumber( j1, wxS( "8" ) );
BOOST_REQUIRE_MESSAGE( pin8 != nullptr, "Pin 8 not found on symbol J1" );
// Check that the alternate is set
BOOST_CHECK_MESSAGE( pin8->GetAlt() == wxS( "8.pow" ),
"Pin 8 should have alternate '8.pow' set, but has '"
+ pin8->GetAlt() + "'" );
// Check that m_libPin is set
BOOST_CHECK_MESSAGE( pin8->GetLibPin() != nullptr,
"Pin 8 should have m_libPin set after schematic load" );
if( pin8->GetLibPin() )
{
// Check that the library pin has alternates
const auto& alternates = pin8->GetLibPin()->GetAlternates();
BOOST_CHECK_MESSAGE( !alternates.empty(),
"Library pin should have alternates populated" );
BOOST_CHECK_MESSAGE( alternates.count( wxS( "8.pow" ) ) > 0,
"Library pin should have '8.pow' in alternates map" );
// Check the alternate's type in the library pin
if( alternates.count( wxS( "8.pow" ) ) > 0 )
{
const SCH_PIN::ALT& alt = alternates.at( wxS( "8.pow" ) );
BOOST_CHECK_MESSAGE( alt.m_Type == ELECTRICAL_PINTYPE::PT_POWER_IN,
"Library pin alternate '8.pow' should have type PT_POWER_IN" );
}
}
// The critical test: GetType() should return the alternate's type, not the default
ELECTRICAL_PINTYPE pinType = pin8->GetType();
BOOST_CHECK_MESSAGE( pinType == ELECTRICAL_PINTYPE::PT_POWER_IN,
"Pin 8 GetType() should return PT_POWER_IN (7) for alternate '8.pow', "
"but returned " + std::to_string( static_cast<int>( pinType ) )
+ " (" + ElectricalPinTypeGetText( pinType ) + ")" );
// Verify the shown name is the alternate name
BOOST_CHECK_MESSAGE( pin8->GetShownName() == wxS( "8.pow" ),
"Pin 8 GetShownName() should return '8.pow', but returned '"
+ pin8->GetShownName() + "'" );
}
/**
* Verify the connection graph does NOT treat the pin as no-connect.
*
* When pin 8 has type PT_POWER_IN (from alternate), it should NOT be marked
* as a no-connect in the connection graph. It will still get a "Net-(...)" name
* because it's on a regular connector (not a power symbol), but importantly it
* should NOT be prefixed with "unconnected-".
*/
BOOST_AUTO_TEST_CASE( ConnectionGraphHandlesPinAlternateType )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "issue22286" ) );
fn.SetName( wxS( "bugtest" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
SCH_SYMBOL* j1 = FindSymbolByRef( wxS( "J1" ) );
BOOST_REQUIRE( j1 != nullptr );
SCH_PIN* pin8 = FindPinByNumber( j1, wxS( "8" ) );
BOOST_REQUIRE( pin8 != nullptr );
// Get the net name for pin 8
SCH_SHEET_PATH path = m_schematic->Hierarchy()[0];
wxString netName = pin8->GetDefaultNetName( path );
// With the fix, pin 8 should NOT get an "unconnected-(...)" name because
// it's a power pin (PT_POWER_IN), not a no-connect pin (PT_NC).
// It will still get a "Net-(...)" name because it's on a regular connector.
BOOST_CHECK_MESSAGE( !netName.StartsWith( wxS( "unconnected-(" ) ),
"Pin 8 (power_in) should not get an 'unconnected-(...)' name. "
"Got: '" + netName + "'" );
}
BOOST_AUTO_TEST_SUITE_END()
+328
View File
@@ -214,6 +214,334 @@ BOOST_AUTO_TEST_CASE( AlternatePinRenameUpdates )
}
/**
* Test for issue #22286 - GetType() should return the alternate's type when alternate is selected
*/
BOOST_AUTO_TEST_CASE( AlternatePinTypeReturnsCorrectType )
{
// Set the library pin's default type to no_connect
m_lib_pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
// Add an alternate with a different type (power_in)
SCH_PIN::ALT powerAlt;
powerAlt.m_Name = wxS( "8.pow" );
powerAlt.m_Shape = GRAPHIC_PINSHAPE::LINE;
powerAlt.m_Type = ELECTRICAL_PINTYPE::PT_POWER_IN;
m_lib_pin->GetAlternates()[ wxS( "8.pow" ) ] = powerAlt;
// Flatten and update the symbol to get a fresh schematic pin
m_parent_symbol->SetLibSymbol( m_parent_part->Flatten().release() );
m_sch_pin = m_parent_symbol->GetPins()[0];
// Before selecting alternate, type should be the default (NC)
BOOST_CHECK( m_sch_pin->GetType() == ELECTRICAL_PINTYPE::PT_NC );
// Select the alternate
m_sch_pin->SetAlt( wxS( "8.pow" ) );
// After selecting alternate, type should be the alternate's type (POWER_IN)
BOOST_CHECK_EQUAL( m_sch_pin->GetAlt(), "8.pow" );
BOOST_CHECK( m_sch_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN );
// Also verify shape changed
BOOST_CHECK( m_sch_pin->GetShape() == GRAPHIC_PINSHAPE::LINE );
}
/**
* Test for issue #22286 - simulate schematic loading scenario
* When a schematic is loaded, the pin's alternate is set BEFORE SetLibSymbol is called.
* The alternate must persist through UpdatePins().
*/
BOOST_AUTO_TEST_CASE( AlternatePinTypePersistsThroughSymbolUpdate )
{
// Set the library pin's default type to no_connect
m_lib_pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
// Add an alternate with a different type (power_in)
SCH_PIN::ALT powerAlt;
powerAlt.m_Name = wxS( "8.pow" );
powerAlt.m_Shape = GRAPHIC_PINSHAPE::LINE;
powerAlt.m_Type = ELECTRICAL_PINTYPE::PT_POWER_IN;
m_lib_pin->GetAlternates()[ wxS( "8.pow" ) ] = powerAlt;
// First flatten to set up the symbol
m_parent_symbol->SetLibSymbol( m_parent_part->Flatten().release() );
m_sch_pin = m_parent_symbol->GetPins()[0];
// Set the alternate on the schematic pin (like the parser would)
m_sch_pin->SetAlt( wxS( "8.pow" ) );
BOOST_CHECK_EQUAL( m_sch_pin->GetAlt(), "8.pow" );
BOOST_CHECK( m_sch_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN );
// Now simulate what happens when the library symbol is re-resolved
// This is similar to what happens after loading a schematic from file
m_parent_symbol->SetLibSymbol( m_parent_part->Flatten().release() );
m_sch_pin = m_parent_symbol->GetPins()[0];
// After the symbol update, the alternate should still be set
BOOST_CHECK_EQUAL( m_sch_pin->GetAlt(), "8.pow" );
// And GetType() should return the alternate's type
BOOST_CHECK( m_sch_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN );
}
/**
* Test for issue #22286 - simulate parser-created pins with alternates
* The parser creates pins with m_alt set but m_libPin=nullptr.
* Verify that after UpdatePins() the alternate type is correctly returned.
*/
BOOST_AUTO_TEST_CASE( ParserCreatedPinWithAlternate )
{
// Create a library symbol with a pin that has alternates
LIB_SYMBOL* libSymbol = new LIB_SYMBOL( "test_symbol", nullptr );
SCH_PIN* libPin = new SCH_PIN( libSymbol );
libPin->SetNumber( "8" );
libPin->SetName( "PIN8" );
libPin->SetType( ELECTRICAL_PINTYPE::PT_NC ); // Default type is NC
libPin->SetPosition( VECTOR2I( 0, 0 ) );
// Add an alternate with power_in type
SCH_PIN::ALT powerAlt;
powerAlt.m_Name = wxS( "8.pow" );
powerAlt.m_Shape = GRAPHIC_PINSHAPE::LINE;
powerAlt.m_Type = ELECTRICAL_PINTYPE::PT_POWER_IN;
libPin->GetAlternates()[ wxS( "8.pow" ) ] = powerAlt;
libSymbol->AddDrawItem( libPin );
// Create a schematic symbol
SCH_SHEET_PATH path;
SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libSymbol->GetLibId(), &path, 0, 0,
VECTOR2I( 0, 0 ) );
symbol->SetRef( &path, "J1" );
// Simulate what the parser does: create a raw pin with alternate set
// This bypasses SetAlt() validation by using the constructor directly
symbol->GetRawPins().clear(); // Remove auto-created pins
symbol->GetRawPins().emplace_back(
std::make_unique<SCH_PIN>( symbol, wxS( "8" ), wxS( "8.pow" ), KIID() ) );
SCH_PIN* parserPin = symbol->GetRawPins()[0].get();
// At this point, m_alt is set but m_libPin is nullptr
BOOST_CHECK_EQUAL( parserPin->GetAlt(), "8.pow" );
BOOST_CHECK( parserPin->GetLibPin() == nullptr );
// GetType() should return PT_UNSPECIFIED when m_libPin is null
BOOST_CHECK( parserPin->GetType() == ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
// Now simulate SetLibSymbol() which calls UpdatePins()
symbol->SetLibSymbol( libSymbol->Flatten().release() );
// Verify the flattened library symbol has pins with alternates
BOOST_CHECK( symbol->GetLibSymbolRef() != nullptr );
std::vector<SCH_PIN*> libPins = symbol->GetLibSymbolRef()->GetPins();
BOOST_CHECK_EQUAL( libPins.size(), 1 );
if( !libPins.empty() )
{
SCH_PIN* flattenedLibPin = libPins[0];
BOOST_CHECK( !flattenedLibPin->GetAlternates().empty() );
BOOST_CHECK( flattenedLibPin->GetAlternates().count( wxS( "8.pow" ) ) > 0 );
}
// Get the pin after the update
SCH_PIN* updatedPin = symbol->GetPins( &path )[0];
// The alternate should still be set
BOOST_CHECK_EQUAL( updatedPin->GetAlt(), "8.pow" );
// m_libPin should now be set
BOOST_CHECK( updatedPin->GetLibPin() != nullptr );
// GetType() should return the alternate's type (PT_POWER_IN)
BOOST_CHECK( updatedPin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN );
delete symbol;
delete libSymbol;
}
/**
* Test for issue #22286 - detailed trace of SetLibSymbol flow
* Verifies that library pin alternates are preserved through the copy
* and that SetAlt succeeds in UpdatePins().
*/
BOOST_AUTO_TEST_CASE( LibraryPinAlternatesPreservedThroughCopy )
{
// Create original library symbol with pin that has alternates
LIB_SYMBOL* origLibSymbol = new LIB_SYMBOL( "test_symbol", nullptr );
SCH_PIN* origLibPin = new SCH_PIN( origLibSymbol );
origLibPin->SetNumber( "8" );
origLibPin->SetName( "8" ); // Default name matches number
origLibPin->SetType( ELECTRICAL_PINTYPE::PT_NC ); // Default type is NC
origLibPin->SetPosition( VECTOR2I( 0, 0 ) );
// Add alternate with power_in type
SCH_PIN::ALT powerAlt;
powerAlt.m_Name = wxS( "8.pow" );
powerAlt.m_Shape = GRAPHIC_PINSHAPE::LINE;
powerAlt.m_Type = ELECTRICAL_PINTYPE::PT_POWER_IN;
origLibPin->GetAlternates()[ wxS( "8.pow" ) ] = powerAlt;
origLibSymbol->AddDrawItem( origLibPin );
// Verify original has alternates
BOOST_CHECK( !origLibPin->GetAlternates().empty() );
BOOST_CHECK( origLibPin->GetAlternates().count( wxS( "8.pow" ) ) > 0 );
// Simulate what happens during loading:
// 1. Library symbol is stored in m_libSymbols (just keep the original)
// 2. Create a COPY of the library symbol (like UpdateLocalLibSymbolLinks does)
LIB_SYMBOL* copiedLibSymbol = new LIB_SYMBOL( *origLibSymbol );
// Verify the copied library symbol has pins with alternates
std::vector<SCH_PIN*> copiedLibPins = copiedLibSymbol->GetPins();
BOOST_CHECK_EQUAL( copiedLibPins.size(), 1 );
if( !copiedLibPins.empty() )
{
SCH_PIN* copiedLibPin = copiedLibPins[0];
// This is the critical check - does the copied lib pin have alternates?
BOOST_CHECK_MESSAGE( !copiedLibPin->GetAlternates().empty(),
"Copied library pin should have alternates" );
BOOST_CHECK_MESSAGE( copiedLibPin->GetAlternates().count( wxS( "8.pow" ) ) > 0,
"Copied library pin should have '8.pow' alternate" );
// Check if the alternate has the correct type
if( copiedLibPin->GetAlternates().count( wxS( "8.pow" ) ) > 0 )
{
BOOST_CHECK( copiedLibPin->GetAlternates().at( wxS( "8.pow" ) ).m_Type
== ELECTRICAL_PINTYPE::PT_POWER_IN );
}
}
// Now create a schematic symbol with parser-created raw pins
SCH_SHEET_PATH path;
SCH_SYMBOL* symbol = new SCH_SYMBOL( *origLibSymbol, origLibSymbol->GetLibId(), &path, 0, 0,
VECTOR2I( 0, 0 ) );
symbol->SetRef( &path, "J1" );
// Clear auto-created pins and add parser-style raw pin
symbol->GetRawPins().clear();
symbol->GetRawPins().emplace_back(
std::make_unique<SCH_PIN>( symbol, wxS( "8" ), wxS( "8.pow" ), KIID() ) );
// Verify raw pin state before SetLibSymbol
SCH_PIN* rawPin = symbol->GetRawPins()[0].get();
BOOST_CHECK_EQUAL( rawPin->GetAlt(), "8.pow" );
BOOST_CHECK( rawPin->GetLibPin() == nullptr );
// Call SetLibSymbol with the COPIED library symbol (like UpdateLocalLibSymbolLinks does)
symbol->SetLibSymbol( copiedLibSymbol ); // Takes ownership
// Get the pin after UpdatePins() was called
std::vector<SCH_PIN*> schPins = symbol->GetPins( &path );
BOOST_CHECK_EQUAL( schPins.size(), 1 );
if( !schPins.empty() )
{
SCH_PIN* schPin = schPins[0];
// Check that m_libPin is set
BOOST_CHECK_MESSAGE( schPin->GetLibPin() != nullptr,
"Schematic pin should have m_libPin set after UpdatePins" );
// Check that the alternate is still set
BOOST_CHECK_MESSAGE( schPin->GetAlt() == "8.pow",
"Alternate should be preserved as '8.pow'" );
// Check that GetType() returns the alternate's type
BOOST_CHECK_MESSAGE( schPin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN,
"GetType() should return alternate's type (PT_POWER_IN)" );
// Additional diagnostic: check the library pin's alternates
if( schPin->GetLibPin() )
{
BOOST_CHECK_MESSAGE( !schPin->GetLibPin()->GetAlternates().empty(),
"Library pin pointed to by schematic pin should have alternates" );
BOOST_CHECK_MESSAGE( schPin->GetLibPin()->GetAlternates().count( wxS( "8.pow" ) ) > 0,
"Library pin should have '8.pow' in its alternates map" );
}
}
delete symbol;
delete origLibSymbol;
}
/**
* Test for issue #22286 - verify behavior when GetType() is called before
* m_libPin is set. This simulates what happens if GetType() is called
* during loading before UpdateLocalLibSymbolLinks() runs.
*/
BOOST_AUTO_TEST_CASE( GetTypeBeforeLibPinSet )
{
// Create a schematic symbol with parser-created raw pin
SCH_SHEET_PATH path;
LIB_SYMBOL* libSymbol = new LIB_SYMBOL( "test_symbol", nullptr );
SCH_PIN* libPin = new SCH_PIN( libSymbol );
libPin->SetNumber( "8" );
libPin->SetName( "8" );
libPin->SetType( ELECTRICAL_PINTYPE::PT_NC );
libPin->SetPosition( VECTOR2I( 0, 0 ) );
SCH_PIN::ALT powerAlt;
powerAlt.m_Name = wxS( "8.pow" );
powerAlt.m_Shape = GRAPHIC_PINSHAPE::LINE;
powerAlt.m_Type = ELECTRICAL_PINTYPE::PT_POWER_IN;
libPin->GetAlternates()[ wxS( "8.pow" ) ] = powerAlt;
libSymbol->AddDrawItem( libPin );
// Create symbol using default constructor (like parser does)
SCH_SYMBOL* symbol = new SCH_SYMBOL();
symbol->SetLibId( libSymbol->GetLibId() );
// Add parser-style raw pin with alternate set but no m_libPin
symbol->GetRawPins().emplace_back(
std::make_unique<SCH_PIN>( symbol, wxS( "8" ), wxS( "8.pow" ), KIID() ) );
SCH_PIN* rawPin = symbol->GetRawPins()[0].get();
// At this point: m_alt is set, m_libPin is nullptr, m_type is PT_INHERIT
BOOST_CHECK_EQUAL( rawPin->GetAlt(), "8.pow" );
BOOST_CHECK( rawPin->GetLibPin() == nullptr );
// GetType() with m_alt set but m_libPin=nullptr should return PT_UNSPECIFIED
// per the code in GetType()
BOOST_CHECK_MESSAGE( rawPin->GetType() == ELECTRICAL_PINTYPE::PT_UNSPECIFIED,
"GetType() with m_alt set but m_libPin=nullptr should return PT_UNSPECIFIED" );
// GetShownName() should still return the alternate name
BOOST_CHECK_EQUAL( rawPin->GetShownName(), "8.pow" );
// Now set the library symbol which triggers UpdatePins()
symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
// After UpdatePins(), pin should have correct type
std::vector<SCH_PIN*> schPins = symbol->GetPins( &path );
BOOST_CHECK_EQUAL( schPins.size(), 1 );
if( !schPins.empty() )
{
SCH_PIN* schPin = schPins[0];
BOOST_CHECK_EQUAL( schPin->GetAlt(), "8.pow" );
BOOST_CHECK( schPin->GetLibPin() != nullptr );
BOOST_CHECK_MESSAGE( schPin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN,
"After SetLibSymbol, GetType() should return PT_POWER_IN" );
}
delete symbol;
delete libSymbol;
}
/**
* Test for issue #22566 - changing to a symbol with fewer pins should not crash
*/