This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Make expression lookup find only the first variable in the closest module
Needs ReviewPublic

Authored by teemperor on May 29 2020, 6:57 AM.

Details

Summary

Usually when searching for variables we stop after we find the first one (see for example
ClangExpressionDeclMap::LookupLocalVariable or
ClangExpressionDeclMap::FindGlobalVariable), as there are legitimate reasons for multiple
variables with the same name being defined in the ASTs that LLDB searches. For example,
a header with at static global variable that is included by multiple shared libraries will
produce such duplicate variables.

However, when resolving lookups in namespaces, LLDB currently iterates over all lldb_private::Modules
that are known to contain the given namespace and then searches in all of these modules for
the given lookup. This causes that when a static global variable from a header is included in
different shared libraries, LLDB will add all these variables to the lookup results. The results
will then cause Clang to emit a diagnostic about an ambiguous lookup and the expression
will fail to compile.

This patch solves this by only adding the first variable that is found to the lookup and ignoring
the others. It also changes the search order to first look in the current module of the
execution context and then iterate over all other modules, so that LLDB will always find the
'closest' definition of the given variable.

Also adds a test for the case where all these variables are in one module, but solving that issue
is yet another patch.

Just to state the obvious: Picking the first variable won't work for variable templates, but we anyway
don't really support those yet (beside pretending they are just normal variables). Once we properly
model those this logic just needs minor tweaking to make them work as expected.

Fixes rdar://63222982

Diff Detail

Event Timeline

teemperor created this revision.May 29 2020, 6:57 AM
shafik added inline comments.May 29 2020, 9:34 AM
lldb/test/API/lang/cpp/namespace_search/TestNamespaceSearch.py
93

It have read this a few times, I don't quite understand why this case and the ones below don't work.

teemperor marked an inline comment as done.May 29 2020, 10:11 AM
teemperor added inline comments.
lldb/test/API/lang/cpp/namespace_search/TestNamespaceSearch.py
93

It's the test compiled into one executable (=module), so searching the closest module first isn't enough to find the right variable. And the current code for searching one module just picks the first VarDecl it finds in an AST (there is a better logic for non-namespace globals apparently, but it doesn't work for namespaces yet).

Better illustrated:

AST dump of test cases above:

Module 1: <- we'll first search this one with this patch
TranslationUnitDecl <<invalid sloc>> <invalid sloc>
|-NamespaceDecl <<invalid sloc>> <invalid sloc> Foo
| |-VarDecl <<invalid sloc>> <invalid sloc> VarNotInMain 'const char *' <- right variable
Module 2:
TranslationUnitDecl <<invalid sloc>> <invalid sloc>
|-NamespaceDecl <<invalid sloc>> <invalid sloc> Foo
| `-VarDecl <<invalid sloc>> <invalid sloc> VarNotInMain 'const char *'

AST dump of test cases below:

Module 1: <- we'll first search this one with this patch
TranslationUnitDecl <<invalid sloc>> <invalid sloc>
|-NamespaceDecl <<invalid sloc>> <invalid sloc> Foo
| |-VarDecl <<invalid sloc>> <invalid sloc> VarNotInMain 'const char *' <- tu0's variable
| `-VarDecl <<invalid sloc>> <invalid sloc> VarNotInMain 'const char *' <- tu1's variable (looks identical to the one above for LLDB).
labath added a comment.Jun 1 2020, 6:27 AM

I can't say I'm super familiar with this stuff (e.g., I never understood why we need to handle the global scope specially everywhere), but the general idea seem pretty simple and straight-forward enough. Some minor comments inline.

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
708

I believe current_namespace == n should be enough. Or maybe use ClangASTImporter::NamespaceMapItem *current_namespace instead of the optional (compare with `current_namespace == &n) and avoid the copies and value comparisons...

lldb/test/API/lang/cpp/namespace_search/TestNamespaceSearch.py
22

what does the ld stand for ?

24

If you use self.registerSharedLibrariesWithTarget to get the environment, then the test will work correctly in remote setups too.

lldb/test/API/lang/cpp/namespace_search/tu0.cpp
12

I'm pretty sure that defining multiple functions like this (regardless of whether they are inline or not) is an ODR violation. If you did not want to test anything specific to odr violations, then I suggest making the function static instead. (And if you did, then I'd like to know what it is.)