Variable shadowing

Hi all,

I’m using lldb 3.9 through the C++ API and I’m trying to determine if a local variable is shadowed or not with no luck.

For the code below:

  1. Is there an API call, that I somehow missed, that can tell me that (v = 2) shadows (v = 1)?

  2. Can I rely on their order in the SBValueList object?

  3. Would you guys think it would be worth adding bool SBValue::IsShadowed() const ?

1 void foo()

2 {

3 int v = 1;

4 {

5 int v = 2;

→ 6 ++v;

7 }

8 }

Thank,

Bogdan

You can currently do this by checking for other variables to see if any names match.

In python when stopped in the function below you can do use the API:

(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.

frame_vars = lldb.frame.GetVariables(True, True, True, True)
print frame_vars

(int) v = 1
(int) v = 2

frame_vars is a list of all variables in the current stack frame. They are ordered correctly so you see your first "v" first and the second one next.

Lets grab the exact lexical block from the current frame:

block = lldb.frame.GetBlock()

This is the block from line 4 - 7 in your source code.

print block.GetVariables(lldb.frame, True, True, True, 0)

(int) v = 2

Note that if we ask the block for its variables, it will only have the variables contained in that block. Now you can ask "block" for its parent block:

print block.GetParent().GetVariables(lldb.frame, True, True, True, 0)

(int) v = 1

So with all of this you could create a new python command:

#!/usr/bin/python

import lldb
import shlex

@lldb.command("check-shadow")
def check_shadow_command(debugger, command, result, dict):
    target = debugger.GetSelectedTarget()
    if not target:
        print >>result, "invalid target"
        return
    process = target.GetProcess()
    if not process:
        print >>result, "invalid process"
        return
    thread = process.GetSelectedThread()
    if not thread:
        print >>result, "invalid thread"
        return
    frame = thread.GetSelectedFrame()
    if not frame:
        print >>result, "invalid frame"
        return
    # Parse command line args
    command_args = shlex.split(command)
    # TODO: add support for using arguments that are passed to this command...
    
    # Make a dictionary of variable name to "SBBlock and SBValue"
    var_dict = {}
    
    # Get the deepest most block from the current frame
    block = frame.GetBlock()
    # Iterate through the block and all of its parents
    while block.IsValid():
        # Get block variables from the current block only
        block_vars = block.GetVariables(frame, True, True, True, 0)
        # Iterate through all variables in the current block
        for block_var in block_vars:
            # Get the variable name and see if we already have a variable by this name?
            block_var_name = block_var.GetName()
            if block_var_name in var_dict:
                # We already have seen a variable with this name, so it is shadowed
                print block, block_var
                print 'is shadowed by:'
                shadow_block_and_vars = var_dict[block_var_name]
                for shadow_block_and_var in shadow_block_and_vars:
                    print shadow_block_and_var[0], shadow_block_and_var[1]
            # Since we can have multiple shadowed variables, we our variable
            # name dictionary to have an array or "block + variable" pairs so
            # We can correctly print out all shadowed variables and whow which
            # blocks they come from
            if block_var_name in var_dict:
                var_dict[block_var_name].append([block, block_var])
            else:
                var_dict[block_var_name] = [[block, block_var]]
        # Get the parent block and continue
        block = block.GetParent()
    
I have attached this as a script that you can import:

lldbshadow.py (2.27 KB)

The logic is a bit wrong in my script, it should first print out the variables we have already found, followed by the one we are currently processing. The fixed script is attached:

lldbshadow.py (2.27 KB)

Thanks!
Using the blocks I can get all the information I need: locals, function statics and arguments plus shadowing. Now I only need globals which I can get using "target.FindGlobalVariables('.', 1000, eMatchTypeRegex)" and search for variable names in the dictionary.

Your script seems pretty useful. Maybe you can add it to the python examples.

% svn commit shadow.py
Adding shadow.py
Transmitting file data .done
Committing transaction...
Committed revision 273604.

It is now in examples/python/shadow.py and the command is called "shadow".