This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Use expansion location for missing include diagnostics.
ClosedPublic

Authored by VitaNuo on Mar 23 2023, 8:15 AM.

Diff Detail

Event Timeline

VitaNuo created this revision.Mar 23 2023, 8:15 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 23 2023, 8:15 AM
VitaNuo requested review of this revision.Mar 23 2023, 8:15 AM
VitaNuo updated this revision to Diff 507748.Mar 23 2023, 8:17 AM

Remove unrelated diff.

kadircet added inline comments.Mar 24 2023, 1:46 AM
clang-tools-extra/clangd/IncludeCleaner.cpp
391–392

can you actually get the spelling location first and use expansion location only when it isn't inside the main file?
that way we get to preserve the behaviour for macro arguments, which warrants a test like:

#define FOO(X) const X x

FOO([[Foo]]); // we should have a missing include for symbol Foo here

and can we have a comment about reasons and implications, e.g. something like:

Spelling locations can point into preamble section at times. We don't want to emit diagnostics there as the offsets might be off when preamble is stale.
In such cases we fallback to expansion locations, as they're always in the main file. This means we fail to attach diagnostics to spelling of symbol names inside macro bodies.
// FIXME: Use presumed locations to make sure we can correctly generate diagnostics even in such cases.
clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
242

can you also put a #define BAR(x) Foo *x here and a usage like BAR(b); inside the main file and check that we only get include for symbol for BAR inside the main file at that location?

clang-tools-extra/include-cleaner/lib/Analysis.cpp
39–40

you can directly use expansion location instead of checking both and mentioning preamble file id here, eg: !SM.isWrittenInMainFile(SM.getExpansionLoc(Loc))

VitaNuo updated this revision to Diff 508586.Mar 27 2023, 5:06 AM
VitaNuo marked 2 inline comments as done.

Address review comments. Use spelling locations for macro arguments, and expansion locations for everything else.

VitaNuo marked an inline comment as done.Mar 27 2023, 5:06 AM

Thanks for the comments!

clang-tools-extra/clangd/IncludeCleaner.cpp
391–392

Sam suggested a magic solution for this, which is SM.getFileLoc(Ref.RefLocation). Its documentation says:

/// Given \p Loc, if it is a macro location return the expansion
/// location or the spelling location, depending on if it comes from a
/// macro argument or not.

Sounds like what we need, right?

clang-tools-extra/include-cleaner/lib/Analysis.cpp
39–40

This actually leads to both BAR and Foo in #define BAR(x) Foo *x in foo.h being recognized as missing and attaches 2 diagnostics to [[BAR]](b) in the main file. AFAIU, this is exactly what you're asking me to prevent in the previous comment.

kadircet added inline comments.Mar 27 2023, 5:59 AM
clang-tools-extra/clangd/IncludeCleaner.cpp
391–392

Rather than doing spelling & falling back to expansion, just doing getFileLoc definitely makes sense.
But what we actually want here is always map to spelling (e.g. we want Foo in macro body, rather than macro invocation to be highlighted).
It's just we can't have it all the time because accessing offsets coming from stale preamble might result in crashes. Hence can you also add the comment i mentioned above so that we don't forget? maybe with a rewording like:

// We actually always want to map usages to their spellings, but spelling locations can point into preamble section.
// Using these offsets could lead into crashes in presence of stale preambles. Hence we use `getFileLoc` instead to make sure it always points into main file.
// FIXME: Use presumed locations to map such usages back to patched locations safely.
392

I don't think you need to re-lex the token here, as Loc is already a non-macro location in main file, you can still use Tokens.spelledTokenAt(Loc). Am i missing something?

clang-tools-extra/include-cleaner/lib/Analysis.cpp
39–40

no you're right, i think i was just confused here.

still might be nicer to clean it up a little:

auto FID = SM.getFileID(SM.getSpellingLoc(Loc));
if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID())
  return;
VitaNuo marked an inline comment as done.Mar 27 2023, 6:09 AM
VitaNuo added inline comments.
clang-tools-extra/clangd/IncludeCleaner.cpp
392

It's for consistency with clangd diagnostic generation here (according to Sam), but both should work.
Would you rather prefer Tokens.spelledTokenAt(Loc)?

kadircet added inline comments.Mar 27 2023, 6:16 AM
clang-tools-extra/clangd/IncludeCleaner.cpp
392

I'd rather go with spelledTokenAt here, diagnostics code deals with locations outside of the main file, hence it makes sense for that logic to re-run the lexer. but i feel like re-running lexer here will actually create more confusion, as we tend to re-use results in token buffers rather than using raw lexer in rest of the codebase, as it confused me above.

VitaNuo updated this revision to Diff 508645.Mar 27 2023, 7:17 AM
VitaNuo marked 3 inline comments as done.

Address review comments.

thanks for the comments!

clang-tools-extra/clangd/IncludeCleaner.cpp
391–392

thanks!

392

thank you!

kadircet accepted this revision.Mar 27 2023, 7:22 AM

thanks!

This revision is now accepted and ready to land.Mar 27 2023, 7:22 AM
This revision was landed with ongoing or failed builds.Mar 27 2023, 7:27 AM
This revision was automatically updated to reflect the committed changes.