This is an archive of the discontinued LLVM Phabricator instance.

[clangd] locateMacroAt handles patched macros
ClosedPublic

Authored by kadircet on May 19 2020, 2:23 AM.

Details

Summary

Depends on D79992.

This patch changes locateMacroAt to perform #line directive substitution
for macro identifier locations.

We first check whether a location is inside a file included through
built-in header. If so we check whether line directive maps it back to
the main file, and afterwards use TokenBuffers to find exact location of
the identifier on the line.

Instead of performing the mapping in locateMacroAt, we could also store
a mapping inside the ParsedAST whenever we use a patched preamble. But
that would imply adding more responsibility to ParsedAST and paying for
the mapping even when it is not going to be used.

====

Go-To-Definition:

Later on these locations are used for serving go-to-definition requests,
this enables jumping to definition inside the preamble section in
presence of patched macros.

=====

Go-To-Refs:

Macro references in main file are collected separetely and stored as a
map from macro's symbol id to reference ranges. Those ranges are
computed inside PPCallbacks, hence we don't have access to TokenBuffer.

In presence of preamble patch, any reference to a macro inside the
preamble section will unfortunately have the wrong range. They'll point
into the patch rather than the main file. Hence during findReferences,
we won't get any ranges reported for those.

Fixing those requires:
- Lexing the preamble section to figure out "real range" of a patched
  macro definition
- Postponing range/location calculations until a later step in which we
  have access to tokenbuffers.

This patch trades some accuracy in favor of code complexity. We don't do
any patching for references inside the preamble patch but get any
reference inside the main file for free.

Diff Detail

Event Timeline

kadircet created this revision.May 19 2020, 2:23 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 19 2020, 2:23 AM
kadircet edited the summary of this revision. (Show Details)May 20 2020, 2:38 AM
kadircet edited the summary of this revision. (Show Details)May 20 2020, 3:15 AM
sammccall added inline comments.May 20 2020, 9:10 AM
clang-tools-extra/clangd/SourceCode.cpp
974

OK, this is fairly horrible...

I want to say the preamble patch location translation should be a separate function rather than coupled to macro-specific stuff.
(Then we don't even need the extra struct member, but we can still keep it to "remind" people the translation is needed, if we like).

But of course we didn't preserve the formatting in the preamble patch, so getPresumedLoc() doesn't give us the right location, it just gives us something on the right line that we then need to re-parse...

This really isn't looking like a great tradeoff to me anymore (and I know I suggested it).
How much work do you think it is (compared to say, the logic here plus doing it in ~2 more places) to give the preamblepatch the invariant that the PresumedLoc has a usable col as well.
The original implementation did padding with spaces, but I guess we could as well have the preamble patching stuff splice the actual source code...

clang-tools-extra/clangd/SourceCode.h
295

It's not obvious to me that this comment describes a special case but the field is valid in general.
Maybe

/// Location of the identifier that names the macro.
/// Unlike Info->Location, this translates preamble-patch locations to main-file locations.
297

nit: call this NameLoc or NameLocation?
ident is *slightly* vague

kadircet updated this revision to Diff 266047.May 25 2020, 10:26 AM
kadircet marked 3 inline comments as done.

Instead of re-lexing at use time, put macro identifier on correct column while
patching and make use of presumed locations at use time.

sammccall added inline comments.May 25 2020, 11:41 AM
clang-tools-extra/clangd/Preamble.cpp
124

Spells directive in \p DirectiveRange while prepending it with \p Prefix

I wouldn't understand what this meant if I didn't already know :-) Maybe:

// Formats a PP directive consisting of Prefix (e.g. "#define ") and Body ("X 10").
// The formatting is copied so that the tokens in Body have PresumedLocs with
// correct columns and lines.
128

nit: move out-param to end?

134

This is only going to give you something useful for file IDs, not macro expansions. Whose responsibility is it to make sure there's no macro IDs here?

135

this name seems a bit confusing to me, since it only covers one case.
TargetColumn?
if (TargetColumn < Prefix.size()) reads a lot celarer I think.

137

again this comment only applyes to one branch

140

Rather than "pad from the start of a new line" it might be clearer to give an example here:

... We produce e.g.:
#line N-1
#define \
   X 10
150

this asserts isValidFileRange, so we need this to be true :-)

clang-tools-extra/clangd/SourceCode.cpp
975

I think this block can now be lifted to a function in SourceCode or Preamble or so?

(BTW this looks miles better to me, thanks for persistence!)

kadircet updated this revision to Diff 266111.May 26 2020, 12:17 AM
kadircet marked 10 inline comments as done.
  • Extract preamble-patch location translation to a helper:
    • Migrate usage in include collector
    • Make preamble patch header name part of the check.
  • Handle macro ids in spellDirective
  • Tweak comments/names
kadircet added inline comments.May 26 2020, 12:20 AM
clang-tools-extra/clangd/Preamble.cpp
134

For define directives this is already true as MacroInfo's definition range is a file range(as macro bodies are lexed without macro expansions).

For any other directive with macro expansions, we only care about the expansion locations as we want to spell out the directive as written. So we can just map back to expansion locs here.

137

why ? we pad in both cases, starting from the beginning of the line in first branch and starting after Prefix in the second.

clang-tools-extra/clangd/SourceCode.cpp
974

as discussed offline, changed patching logic to put directive in correct column in addition to line. Now we can just make use of presumedloc.

sammccall accepted this revision.May 26 2020, 1:24 AM

Looks great, ship it!

clang-tools-extra/clangd/Preamble.cpp
154

nit: not "before the directive", just before the DirectiveRange.begin()

157

nit: I think it'd be easier to understand the other case if this one came first, as it's the fallback

clang-tools-extra/clangd/Preamble.h
136

Can we have a unit-test for this function?
I think the minimal thing (least entangled with PreamblePatch construction) is to manually write a snippet that looks like a preamble patch like

# line 3 main.cpp
int foo();

map it into a TestTU with AdditionalFiles, set ExtraArgs = {"-include","patch.h"} and then get the location with findDecl

This revision is now accepted and ready to land.May 26 2020, 1:24 AM
kadircet updated this revision to Diff 266128.May 26 2020, 2:10 AM
kadircet marked 4 inline comments as done.
  • Add tests for preamble-patch translation logic
This revision was automatically updated to reflect the committed changes.