This is an archive of the discontinued LLVM Phabricator instance.

[clangd] prototype: Implement unused include warnings with include-cleaner library.
ClosedPublic

Authored by hokein on Jan 3 2023, 1:19 AM.

Details

Summary

A prototype of using include-cleaner library in clangd:

  • (re)implement clangd's "unused include" warnings with the library
  • the new implementation is hidden under a flag Config::UnusedIncludesPolicy::Experiment

Diff Detail

Event Timeline

hokein created this revision.Jan 3 2023, 1:19 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 3 2023, 1:19 AM
hokein requested review of this revision.Jan 3 2023, 1:19 AM
hokein updated this revision to Diff 489959.Jan 17 2023, 3:30 PM

rebase and update

regarding testing, i think updating all tests that we have in IncludeCleanerTests that call computeUnusedIncludes to also call the new function that'll use include-cleaner library should be enough.

clang-tools-extra/clangd/CMakeLists.txt
5

can you move this near the include_directories for clang-tidy below (near line 63)

clang-tools-extra/clangd/Config.h
91

can you also add a comment saying that "Experiment tries to provide the same behaviour as Strict but using include-cleaner"

clang-tools-extra/clangd/IncludeCleaner.cpp
470

agreed, let's change StdlibHeaders in IncludeStructure to actually be a BySpelling mapping, it should be no-op for existing implementation as we're only checking for stdlib headers already (and there's no other usage)

478

this might behave slightly different than what we have in RecordedPP, and rest of the applications of include-cleaner will be using that. can we expose the pieces in RecordedPP that collects MacroRefs as a separate handler and attach that collector (combined with the skipped range collection inside CollectMainFileMacros and also still converting to MainFileMacros in the end (as we can't store sourcelocations/identifierinfos from preamble)?

afterwards we can use the names stored in there to get back to IdentifierInfos and Ranges stored to get back to SourceLocations. WDYT?

486

you can get the IdentifierInfo using Name inside Macro and PP.

511

let's use getUnused directly here, with an empty set of PublicHeaders.

528

can you keep computeUnusedIncludes the same and introduce a new function that'll call include_cleaner? afterwards we can just do a switch on policy here and call the relevant function.

clang-tools-extra/clangd/ParsedAST.cpp
594

why do we need to collect pragmas in main file? i think we already have necessary information available via IncludeStructure (it stores keeps and exports, and we don't care about anything else in the main file AFAICT). so let's just use the PragmaIncludes we're getting from the Preamble directly? without even making a copy and returning a reference from the Preamble instead in ParsedAST::getPragmaIncludes

hokein updated this revision to Diff 490137.Jan 18 2023, 6:53 AM
hokein marked 4 inline comments as done.

address comments.

clang-tools-extra/clangd/IncludeCleaner.cpp
470

sure, I will address it in a followup patch.

478

Yeah, this is a better solution, but I'm not sure whether we should do this before the release cut, it has a side effect of changing the find-macro-reference behavior in clangd. It requires some design/implement work.

I agree that the current solution is hacky, and eventually will be replaced, but it follows the existing findReferencedMacros, so it is not that bad. I tend to land this version before the release cut. What do you think?

486

oh, right. Done.

clang-tools-extra/clangd/ParsedAST.cpp
594

i think we already have necessary information available via IncludeStructure (it stores keeps and exports, and we don't care about anything else in the main file AFAICT)

The IncludeStructure doesn't have a full support for IWYU export pragma, it only tracks the headers that have the export pragma.

My understand of the end goal is to use the PragmaInclude to handle every IWYU-related things, and we can remove all these IWYU bits in the IncludeStructure, clangd IWYU pragma handling code.

hokein updated this revision to Diff 490287.Jan 18 2023, 2:10 PM

update the tests.

kadircet added inline comments.Jan 19 2023, 12:48 AM
clang-tools-extra/clangd/IncludeCleaner.cpp
478

Yeah, this is a better solution, but I'm not sure whether we should do this before the release cut, it has a side effect of changing the find-macro-reference behavior in clangd.

OK, i think you're right about possibly changing semantic highlighting&refs for clangd. Let's push this as a follow-up after the branch cut then.

It requires some design/implement work.

No action needed here just wanted to layout some ideas;
We just need to change the implementation details of CollectMainFileMacros to wrap the RecordedPP and add skipped ranges support to it. We might need to do a little plumbing to convert between types (as RecordedPP stores SourceLocations/MacroInfos that can't be used across preamble and AST) but that should be trivial to marshal (we can do the conversion inside EndOfMainFile). I haven't fully tried this though, so if you tried and faced certain incompatibilities PLMK.

I agree that the current solution is hacky, and eventually will be replaced, but it follows the existing findReferencedMacros, so it is not that bad. I tend to land this version before the release cut. What do you think?

I am worried that landing this version and letting people use it for 6 months might result in us getting bug reports that we can't address until we converge (or even worse get bug reports due to behavior change after we converge). But changes here are somewhat more justified as we put this as an experimental feature, compared to regressions in existing clangd behavior as you pointed out.

clang-tools-extra/clangd/IncludeCleaner.h
100 ↗(On Diff #490287)

s/computeUnusedIncludesNew/computeUnusedIncludesExperimental/

can you also add a comment saying that this uses include-cleaner library to perform usage analysis?

clang-tools-extra/clangd/ParsedAST.cpp
594

The IncludeStructure doesn't have a full support for IWYU export pragma, it only tracks the headers that have the export pragma.

And I think that's all we need for IWYU pragmas inside the main file (as main file is a leaf and exporting headers from it doesn't change anything apart from making sure we keep them around)

My understand of the end goal is to use the PragmaInclude to handle every IWYU-related things, and we can remove all these IWYU bits in the IncludeStructure, clangd IWYU pragma handling code.

Yes, I agree with some version of that, but until then this is introducing some extra changes (+ copying around more information in every AST build). So can we leave this piece out of the initial patch and just use the PragmaInclude we have from Preamble without copying it around?

clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
345 ↗(On Diff #490287)

can you inline the call to computeUnusedIncludes into the EXPECT statement here?

382 ↗(On Diff #490287)

can you also update the test here to have same structure as below?

hokein updated this revision to Diff 490452.Jan 19 2023, 4:05 AM
hokein marked 3 inline comments as done.

address comments:

  • rename to computeUnusedIncludesExperimental
  • use PragmaInclude from preamble, limit the scope of the patch
clang-tools-extra/clangd/ParsedAST.cpp
594

Yes, I agree with some version of that, but until then this is introducing some extra changes (+ copying around more information in every AST build). So can we leave this piece out of the initial patch and just use the PragmaInclude we have from Preamble without copying it around?

Sure, that sounds a good plan, and have a better narrowed scope of the patch.

clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
345 ↗(On Diff #490287)

sure, since this is not directly related to this patch, addressed in ccb67491f0dd55c5bd8ed5f71cb802422bfaa969.

kadircet accepted this revision.Jan 19 2023, 4:44 AM

thanks!

clang-tools-extra/clangd/ParsedAST.h
110

can you add a comment saying might be null if AST is built without a preamble?

This revision is now accepted and ready to land.Jan 19 2023, 4:44 AM
This revision was landed with ongoing or failed builds.Jan 19 2023, 5:31 AM
This revision was automatically updated to reflect the committed changes.
hokein marked an inline comment as done.

With this, I now get:
FAILED: bin/clangd-fuzzer
: && /usr/lib/icecream/libexec/icecc/bin/c++ -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O2 -g -DNDEBUG -fuse-ld=lld -Wl,--color-diagnostics -Wl,--gc-sections tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/FuzzerClangdMain.cpp.o tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/clangd-fuzzer.cpp.o -o bin/clangd-fuzzer -Wl,-rpath,"\$ORIGIN/../lib" lib/libclangDaemon.a lib/libclangdSupport.a lib/libclangPseudo.a lib/libclangPseudoGrammar.a lib/libclangTidyAndroidModule.a lib/libclangTidyAbseilModule.a lib/libclangTidyAlteraModule.a lib/libclangTidyBoostModule.a lib/libclangTidyCERTModule.a lib/libclangTidyConcurrencyModule.a lib/libclangTidyDarwinModule.a lib/libclangTidyFuchsiaModule.a lib/libclangTidyHICPPModule.a lib/libclangTidyBugproneModule.a lib/libclangTidyCppCoreGuidelinesModule.a lib/libclangTidyGoogleModule.a lib/libclangTidyLinuxKernelModule.a lib/libclangTidyLLVMModule.a lib/libclangTidyLLVMLibcModule.a lib/libclangTidyMiscModule.a lib/libclangAnalysis.a lib/libclangASTMatchers.a lib/libclangAST.a lib/libclangLex.a lib/libclangBasic.a lib/libclangTidyModernizeModule.a lib/libclangTidyObjCModule.a lib/libclangTidyOpenMPModule.a lib/libclangTidyPerformanceModule.a lib/libclangTidyPortabilityModule.a lib/libclangTidyReadabilityModule.a lib/libclangTidyZirconModule.a lib/libclangTidyMPIModule.a lib/libclangTidyUtils.a lib/libclangTidy.a lib/libclang-cpp.so.16git lib/libLLVM-16git.so && :
ld.lld: error: undefined symbol: clang::include_cleaner::walkUsed(llvm::ArrayRef<clang::Decl*>, llvm::ArrayRef<clang::include_cleaner::SymbolReference>, clang::include_cleaner::PragmaIncludes const*, clang::SourceManager const&, llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)>)

referenced by IncludeCleaner.cpp:504 (/sda/home/christian/dev/llvm/clang-tools-extra/clangd/IncludeCleaner.cpp:504)

IncludeCleaner.cpp.o:(clang::clangd::computeUnusedIncludesExperimental(clang::clangd::ParsedAST&) (.localalias)) in archive lib/libclangDaemon.a

With this, I now get:
FAILED: bin/clangd-fuzzer
: && /usr/lib/icecream/libexec/icecc/bin/c++ -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O2 -g -DNDEBUG -fuse-ld=lld -Wl,--color-diagnostics -Wl,--gc-sections tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/FuzzerClangdMain.cpp.o tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/clangd-fuzzer.cpp.o -o bin/clangd-fuzzer -Wl,-rpath,"\$ORIGIN/../lib" lib/libclangDaemon.a lib/libclangdSupport.a lib/libclangPseudo.a lib/libclangPseudoGrammar.a lib/libclangTidyAndroidModule.a lib/libclangTidyAbseilModule.a lib/libclangTidyAlteraModule.a lib/libclangTidyBoostModule.a lib/libclangTidyCERTModule.a lib/libclangTidyConcurrencyModule.a lib/libclangTidyDarwinModule.a lib/libclangTidyFuchsiaModule.a lib/libclangTidyHICPPModule.a lib/libclangTidyBugproneModule.a lib/libclangTidyCppCoreGuidelinesModule.a lib/libclangTidyGoogleModule.a lib/libclangTidyLinuxKernelModule.a lib/libclangTidyLLVMModule.a lib/libclangTidyLLVMLibcModule.a lib/libclangTidyMiscModule.a lib/libclangAnalysis.a lib/libclangASTMatchers.a lib/libclangAST.a lib/libclangLex.a lib/libclangBasic.a lib/libclangTidyModernizeModule.a lib/libclangTidyObjCModule.a lib/libclangTidyOpenMPModule.a lib/libclangTidyPerformanceModule.a lib/libclangTidyPortabilityModule.a lib/libclangTidyReadabilityModule.a lib/libclangTidyZirconModule.a lib/libclangTidyMPIModule.a lib/libclangTidyUtils.a lib/libclangTidy.a lib/libclang-cpp.so.16git lib/libLLVM-16git.so && :
ld.lld: error: undefined symbol: clang::include_cleaner::walkUsed(llvm::ArrayRef<clang::Decl*>, llvm::ArrayRef<clang::include_cleaner::SymbolReference>, clang::include_cleaner::PragmaIncludes const*, clang::SourceManager const&, llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)>)

referenced by IncludeCleaner.cpp:504 (/sda/home/christian/dev/llvm/clang-tools-extra/clangd/IncludeCleaner.cpp:504)

IncludeCleaner.cpp.o:(clang::clangd::computeUnusedIncludesExperimental(clang::clangd::ParsedAST&) (.localalias)) in archive lib/libclangDaemon.a

sorry, should be fixed in https://github.com/llvm/llvm-project/commit/e84d69f52d9a9fab9162128d8fe8ebec99ea60da.

With this, I now get:
FAILED: bin/clangd-fuzzer
: && /usr/lib/icecream/libexec/icecc/bin/c++ -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O2 -g -DNDEBUG -fuse-ld=lld -Wl,--color-diagnostics -Wl,--gc-sections tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/FuzzerClangdMain.cpp.o tools/clang/tools/extra/clangd/fuzzer/CMakeFiles/clangd-fuzzer.dir/clangd-fuzzer.cpp.o -o bin/clangd-fuzzer -Wl,-rpath,"\$ORIGIN/../lib" lib/libclangDaemon.a lib/libclangdSupport.a lib/libclangPseudo.a lib/libclangPseudoGrammar.a lib/libclangTidyAndroidModule.a lib/libclangTidyAbseilModule.a lib/libclangTidyAlteraModule.a lib/libclangTidyBoostModule.a lib/libclangTidyCERTModule.a lib/libclangTidyConcurrencyModule.a lib/libclangTidyDarwinModule.a lib/libclangTidyFuchsiaModule.a lib/libclangTidyHICPPModule.a lib/libclangTidyBugproneModule.a lib/libclangTidyCppCoreGuidelinesModule.a lib/libclangTidyGoogleModule.a lib/libclangTidyLinuxKernelModule.a lib/libclangTidyLLVMModule.a lib/libclangTidyLLVMLibcModule.a lib/libclangTidyMiscModule.a lib/libclangAnalysis.a lib/libclangASTMatchers.a lib/libclangAST.a lib/libclangLex.a lib/libclangBasic.a lib/libclangTidyModernizeModule.a lib/libclangTidyObjCModule.a lib/libclangTidyOpenMPModule.a lib/libclangTidyPerformanceModule.a lib/libclangTidyPortabilityModule.a lib/libclangTidyReadabilityModule.a lib/libclangTidyZirconModule.a lib/libclangTidyMPIModule.a lib/libclangTidyUtils.a lib/libclangTidy.a lib/libclang-cpp.so.16git lib/libLLVM-16git.so && :
ld.lld: error: undefined symbol: clang::include_cleaner::walkUsed(llvm::ArrayRef<clang::Decl*>, llvm::ArrayRef<clang::include_cleaner::SymbolReference>, clang::include_cleaner::PragmaIncludes const*, clang::SourceManager const&, llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)>)

referenced by IncludeCleaner.cpp:504 (/sda/home/christian/dev/llvm/clang-tools-extra/clangd/IncludeCleaner.cpp:504)

IncludeCleaner.cpp.o:(clang::clangd::computeUnusedIncludesExperimental(clang::clangd::ParsedAST&) (.localalias)) in archive lib/libclangDaemon.a

sorry, should be fixed in https://github.com/llvm/llvm-project/commit/e84d69f52d9a9fab9162128d8fe8ebec99ea60da.

this seems to be not enough, I am also seeing clangd build failiures

/mnt/b/yoe/master/build/tmp/hosttools/ld: lib/libclangDaemon.a(Preamble.cpp.o): in function `clang::clangd::(anonymous namespace)::CppFilePreambleCallbacks::BeforeExecute(clang::CompilerInstance&)':
Preamble.cpp:(.text._ZN5clang6clangd12_GLOBAL__N_124CppFilePreambleCallbacks13BeforeExecuteERNS_16CompilerInstanceE+0x8b): undefined reference to `clang::include_cleaner::PragmaIncludes::record(clang::CompilerInsta
nce const&)'
/mnt/b/yoe/master/build/tmp/hosttools/ld: lib/libclangDaemon.a(IncludeCleaner.cpp.o): in function `clang::clangd::computeUnusedIncludesExperimental(clang::clangd::ParsedAST&) [clone .localalias]':
IncludeCleaner.cpp:(.text._ZN5clang6clangd33computeUnusedIncludesExperimentalERNS0_9ParsedASTE+0x3a1): undefined reference to `clang::include_cleaner::walkUsed(llvm::ArrayRef<clang::Decl*>, llvm::ArrayRef<clang::in
clude_cleaner::SymbolReference>, clang::include_cleaner::PragmaIncludes const*, clang::SourceManager const&, llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cl
eaner::Header>)>)'
collect2: error: ld returned 1 exit status
[193/193] Linking CXX executable bin/clangd
FAILED: bin/clangd

I'm still seeing the build error on clangd-fuzzer on a commit that clearly has e84d69f52d9a9fab9162128d8fe8ebec99ea60da in its history.

with -DLLVM_LINK_LLVM_DYLIB=ON, in case it matters.

oops, sorry for the trouble it caused, and thanks for @kadircet fixing it.