jeremiah-c-leary / vhdl-style-guide

Style guide enforcement for VHDL
GNU General Public License v3.0
175 stars 38 forks source link

IndexError: pop from empty list when having lots of parenthesis in a package body #1168

Closed SittingDuc closed 2 months ago

SittingDuc commented 2 months ago

Hello again, Obviously I have been off using vsg all over my work projects, and I have a new issue that is tripping me up.

Environment vsg-3.19.0, or vsg-3.23.0, or git as of 29 April 2024. Ubuntu Linux 22.04.04, Python 3.10.12

Demonstrating File

package body demo_pkg is
  procedure demo(any:natural) is 
  begin
    func0(a, (proc0(b,c)) = (1 downto 0 => '0'), d, e);
  end procedure;
end package body;

Results in

$ vsg -f demobest.vhd -ap
multiprocessing.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/lib/python3.10/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/home/gsharp/.local/lib/python3.10/site-packages/vsg/apply_rules.py", line 83, in apply_rules
    oVhdlFile = vhdlFile.vhdlFile(lFileContent, commandLineArguments, sFileName, eError, oConfig)
  File "/home/gsharp/.local/lib/python3.10/site-packages/vsg/vhdlFile/vhdlFile.py", line 85, in __init__
    self._processFile()
  File "/home/gsharp/.local/lib/python3.10/site-packages/vsg/vhdlFile/vhdlFile.py", line 128, in _processFile
    set_aggregate_tokens(self.lAllObjects)
  File "/home/gsharp/.local/lib/python3.10/site-packages/vsg/vhdlFile/vhdlFile.py", line 713, in set_aggregate_tokens
    iIndex = lOpenParens.pop()
IndexError: pop from empty list
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/gsharp/.local/bin/vsg", line 8, in <module>
    sys.exit(main())
  File "/home/gsharp/.local/lib/python3.10/site-packages/vsg/__main__.py", line 154, in main
    for tResult in pool.imap(f, enumerate(commandLineArguments.filename)):
  File "/usr/lib/python3.10/multiprocessing/pool.py", line 873, in next
    raise value
IndexError: pop from empty list

Non-Demonstrating File Changing the input file by removing the parenthesis

package body demo_pkg is
  procedure demo(any:natural) is 
  begin
    func0(a, proc0(b,c) = (1 downto 0 => '0'), d, e);
  end procedure;
end package body;

And it runs as expected

$ vsg -f otherbest.vhd -ap
================================================================================
File:  otherbest.vhd
================================================================================
Phase 7 of 7... Reporting
Total Rules Checked: 736
Total Violations:    11
  Error   :    11
  Warning :     0
...

It seems to require the (foo) = (bar) evaluation to boolean, it seems to require most of the nesting steps. Sometimes I can remove arguments 'a', 'd', and 'e' and still demo, but other times not. I have not yet poked the source to see if I can find where the list is popping...

SittingDuc commented 2 months ago

https://github.com/jeremiah-c-leary/vhdl-style-guide/blob/a972c42c4cb61bab1d659b95a2d78d59eea4569c/vsg/vhdlFile/vhdlFile.py#L717

Is the line that is throwing the IndexError exception. But I don't know why :(

jeremiah-c-leary commented 2 months ago

Morning @SittingDuc ,

I suspect (proc0(b,c)) = (1 downto 0 => '0') is being treated as an aggregate.

The line

func0(a, (proc0(b,c)) = (1 downto 0 => '0'), d, e);

is parsed into the following tokens:

1 <vsg.parser.whitespace object at 0x7fe5b0a04d60>
2 <vsg.token.procedure_call.procedure_name object at 0x7fe5b0a046a0>
3 <vsg.token.procedure_call.open_parenthesis object at 0x7fe5b0a04610>
4 <vsg.token.association_element.actual_part object at 0x7fe5b0a04520>
5 <vsg.token.association_list.comma object at 0x7fe5b0a04940>
6 <vsg.parser.whitespace object at 0x7fe5b0a041c0>
7 <vsg.token.association_element.formal_part object at 0x7fe5b0a04160>
8 <vsg.token.todo.name object at 0x7fe5b0a05510>
9 <vsg.token.todo.open_parenthesis object at 0x7fe5b0a054e0>
10 <vsg.parser.todo object at 0x7fe5b0a04730>
11 <vsg.parser.comma object at 0x7fe5b0a04820>
12 <vsg.parser.todo object at 0x7fe5b0a04ac0>
13 <vsg.token.todo.close_parenthesis object at 0x7fe5b0a053f0>
14 <vsg.parser.close_parenthesis object at 0x7fe5b0a04970>
15 <vsg.parser.whitespace object at 0x7fe5b0a04850>
16 <vsg.token.relational_operator.equal object at 0x7fe5b0a048e0>
17 <vsg.parser.whitespace object at 0x7fe5b0a04be0>
18 <vsg.parser.open_parenthesis object at 0x7fe5b0a049d0>
19 <vsg.parser.todo object at 0x7fe5b0a043a0>
20 <vsg.parser.whitespace object at 0x7fe5b0a04a00>
21 <vsg.token.direction.downto object at 0x7fe5b0a04bb0>
22 <vsg.parser.whitespace object at 0x7fe5b0a04b50>
23 <vsg.parser.todo object at 0x7fe5b0a048b0>
24 <vsg.parser.whitespace object at 0x7fe5b0a04760>
25 <vsg.token.association_element.assignment object at 0x7fe5b0a04b20>
26 <vsg.parser.whitespace object at 0x7fe5b0a04c40>
27 <vsg.token.association_element.actual_part object at 0x7fe5b0a04a90>
28 <vsg.token.association_element.actual_part object at 0x7fe5b0a04d30>
29 <vsg.token.association_list.comma object at 0x7fe5b0a04a60>
30 <vsg.parser.whitespace object at 0x7fe5b0a04d00>
31 <vsg.token.association_element.actual_part object at 0x7fe5b0a04910>
32 <vsg.token.association_list.comma object at 0x7fe5b0a04c10>
33 <vsg.parser.whitespace object at 0x7fe5b0a04dc0>
34 <vsg.token.association_element.actual_part object at 0x7fe5b0a04df0>
35 <vsg.token.procedure_call.close_parenthesis object at 0x7fe5b0a04ca0>
36 <vsg.token.procedure_call_statement.semicolon object at 0x7fe5b0a04e50>
37 <vsg.parser.carriage_return object at 0x7fe5b0a047f0>

Line 7 corresponds with the open parenthesis before proc0 and is not being classified as a parenthesis. So the number of open and close parenthesis are not equal and it will error out when attempting to match open and close parenthesis.

If you remove the => from the line:

func0(a, (proc0(b,c)) = (1 downto 0), d, e);

it will not error out.

I believe line 41 in vsg/vhdlFile/classify/association_element.py:

 38 def classify(iStart, iEnd, lObjects, sEnd):
 39     iCurrent = iStart
 40     # Classify formal part if it exists
 41     if utils.find_in_index_range("=>", iStart, iEnd, lObjects):
 42         iCurrent = formal_part.classify(token.formal_part, iCurrent, lObjects)
 43         iCurrent = utils.assign_next_token_required("=>", token.assignment, iCurrent, lObjects)

should be updated to understand the assignment operator must exist at the correct parenthesis depth.

--Jeremy

SittingDuc commented 2 months ago

Okay, I can confirm that, replacing the (range => '0') in my production code with a constant defined as zeroes upscope, causes the issue to go away.

My code is not as pretty, so a full fix is still desirable, but I at least have a workaround to lint this one file and carry on

jeremiah-c-leary commented 2 months ago

Morning @SittingDuc ,

I pushed an update for this to the issue-1168 branch. When you get a chance could you check it out on your end and validate it is working and let me know?

Thanks,

--Jeremy

SittingDuc commented 2 months ago

Hello. I can confirm on my production file the branch fixes the issue: no more python stack dump. Thank you

jeremiah-c-leary commented 2 months ago

Awesome, I will merge this into master.