This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Make order of completions for expressions deterministic and sorted by Clang's priority values.
ClosedPublic

Authored by teemperor on May 20 2020, 5:09 AM.

Details

Summary

It turns out that the order in which we provide completions for expressions is nondeterministic. This leads to confusing user experience and also breaks the reproducer tests (as two LLDB tests can go out of sync due to the non-determinism in the completion lists)

The reason for the non-determinism is that the CompletionConsumer informs us about decls in the order in which it finds declarations in the lookup store of the DeclContexts it visits (mainly this snippet in SemaLookup.cpp):

// Enumerate all of the results in this context.
for (DeclContextLookupResult R :
     Load ? Ctx->lookups()
          : Ctx->noload_lookups(/*PreserveInternalState=*/false)) {
   [...]

This storage of the lookup is sorted by pointer values (see the hash of DeclarationName) and can therefore be non-deterministic. The LLDB code completion consumer that receives these calls originally expected that the order of declarations is defined by Clang, but it seems the API expects the client to provide an order to the completions.

This patch fixes the issue as follows:

  • We sort the completions we get from Clang alphabetically and also by the priority value we get from Clang (with priority value sorting having precedence over the alphabetical sorting)
  • We make all the functions/variables that touch a completion before the sorting const-qualified. The idea is that this should prevent that we never have observable side-effect from touching these declarations in a non-deterministic order (e.g., we don't try to complete the type by accident).

This way we behave like the other parts of Clang which also sort the results by some deterministic value (usually the name or something computed from a name, e.g., edit distance to a given string).

We most likely also need to fix the Clang code to make the loop I listed above deterministic to prevent these issues in the future (tracked in rdar://63442513 ). This wouldn't replace the functionality provided in this patch though as we would still need the priority and overall alphabetical sorting.

Note: I had to increase the lldb-vscode completion limit to 100 as the tests look for strings that aren't in the first 50 results anymore due to variable names starting with letters like 'v' (which are now always shown much further down in the list due to the alphabetical sorting).

Fixes rdar://63200995

Diff Detail

Event Timeline

teemperor created this revision.May 20 2020, 5:09 AM
teemperor edited the summary of this revision. (Show Details)May 20 2020, 5:14 AM
teemperor edited the summary of this revision. (Show Details)May 20 2020, 5:18 AM
teemperor added a reviewer: clayborg.
teemperor marked an inline comment as done.
teemperor added inline comments.
lldb/tools/lldb-vscode/lldb-vscode.cpp
970

@clayborg Can you check that this doesn't break any VSCode functionality? I guess the frontend should be able to handle lists of 100 items.

JDevlieghere added inline comments.May 20 2020, 9:40 AM
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
759

Any reason you didn't make this the regular comparison operator for CompletionWithPriority?

787

Doing this stuff in the destructor seems a bit magical. Is there no better place for this?

900

Can this be a range-based for loop?

  • Moved code out of destructor. Also moved the CompletionRequest parameter to the place where we actually need it.
  • Moved completion sorting code to operator<.
teemperor marked an inline comment as done.
  • Removed some unrelated whitespace change.
JDevlieghere accepted this revision.May 27 2020, 10:20 AM
This revision is now accepted and ready to land.May 27 2020, 10:20 AM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptMay 27 2020, 10:50 AM