IncludeCleaner suggests to remove #include "test.h" in the following example:
test.h
namespace ns {}
test.cpp
include "test.h" using namespace ns;
This patch fixes this behavior by handling using-directive declarations
Differential D134379
[clangd] IncludeCleaner: handle using namespace ArcsinX on Sep 21 2022, 11:48 AM. Authored by
Details
IncludeCleaner suggests to remove #include "test.h" in the following example: test.h namespace ns {} test.cpp include "test.h" using namespace ns; This patch fixes this behavior by handling using-directive declarations
Diff Detail
Event TimelineComment Actions Is test.h meaningfully used in that example? My concern about marking it used is that namespaces are typically redeclared in *every* header, and this effectively disables the feature on large swaths of code: // foo.h namespace myproj { void foo(); } // bar.h namespace myproj { void bar(); } // main.cc #include "foo.h" #include "bar.h" // not meaningfully used using namespace myproj; int main() { foo(); } Comment Actions Yes, in this example both should be removed, but from IDE user point of view, I expect warning about unused using namespace ..., and after it will be removed, warning about unused include can appear. Also:
In this case I don't see a difference with other declaration types. E.g. function // foo.h void foo(); // bar.h void foo(); // main.cc #include "foo.h" #include "bar.h" int main() { foo(); } both foo.h and bar.h contains prototype of foo(), but bar.h can be removed Comment Actions Anyway if this is the only concern, we can handle namespace declaration as a special case inside ReferencedLocationCrawler::add(). something like this: diff - for (const Decl *Redecl : D->redecls()) - Result.User.insert(Redecl->getLocation()); + if (llvm::isa<NamespaceDecl>(D)) { + Result.User.insert(D->getCanonicalDecl()->getLocation()); + } else { + for (const Decl *Redecl : D->redecls()) + Result.User.insert(Redecl->getLocation()); + } And in the above example #include bar.h will be suggested to remove Could this be a suitable solution? Comment Actions I don't think picking the canonical declaration would be helpful in general case; it'll likely preserve the first header all the time, which might be unused otherwise. I feel like the best option here is, diagnosing the unused using directive as well. but I am not sure if changing the lookup semantics would always be "right". This is not easy in the current implementation of clangd, as we preserve symbols provided by headers, but the new include-cleaner library we're working on is more accommodating for such logic. Hence I'd rather leave this implementation as-is today, and see if such a behaviour would be desired in general (not only for namespace-decls, but pretty much any declaration for which we can only see "forward declarations" without a clear "canonical declaration" Comment Actions Diagnosing the unused using directive is a good option, but it's not related with IncludeCleaner. For now IncludeCleaner suggests to remove a header, but removing it leads to compile errors, which looks wrong.
This looks like a general problem, declaration of a used function in every header leads to "every header is used", maybe it's ok as far as it's not a common case. So, yes, with the proposed solution we will get rid of some "false positive" cases, but we will get some "false negative" cases. |