This is an archive of the discontinued LLVM Phabricator instance.

[include-cleaner] Follow `IWYU pragma: export` links transitively.
ClosedPublic

Authored by VitaNuo on Aug 8 2023, 6:58 AM.

Diff Detail

Event Timeline

VitaNuo created this revision.Aug 8 2023, 6:58 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 8 2023, 6:58 AM
Herald added a subscriber: kadircet. · View Herald Transcript
VitaNuo requested review of this revision.Aug 8 2023, 6:58 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 8 2023, 6:58 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
VitaNuo updated this revision to Diff 548204.Aug 8 2023, 7:02 AM

Remove accidental comment.

VitaNuo updated this revision to Diff 548217.Aug 8 2023, 7:36 AM

Remove the preferred header hint for exporters.

i think we should also have some unittests in FindHeadersTest, some sample scenarios:

foo.h

#pragma once
void foo();

export1.h

#pragma once
// IWYU pragma: private, include "public1.h"
#include "foo.h" // IWYU pragma: export

export2.h

#pragma once
// IWYU pragma: private, include "public2.h"
#include "export2.h" // IWYU pragma: export

export3.h

#pragma once
// IWYU pragma: private, include "public3.h"
#include "export3.h" // IWYU pragma: export
void foo(); // we also have a declaration here, hence this is another provider root.

user.cc

#include "export3.h"
void bar() { foo(); }

headersForFoo() should look like foo.h, public3.h, export1.h, export2.h, export3.h

clang-tools-extra/include-cleaner/lib/FindHeaders.cpp
195

what about writing this as:

std::queue<const FileEntry*> Exporters;
while (FE) {
     Results.emplace_back(FE,
                           isPublicHeader(FE, *PI) |
                               (IsOrigin ? Hints::OriginHeader : Hints::None));
      for (const auto *Export : PI->getExporters(FE, SM.getFileManager()))
        Exporters.push(Export);

      if (auto Verbatim = PI->getPublic(FE); !Verbatim.empty()) {
        Results.emplace_back(Verbatim,
                             Hints::PublicHeader | Hints::PreferredHeader);
        break;
      }
      if (PI->isSelfContained(FE) || FID == SM.getMainFileID())
        break;

      // Walkup the include stack for non self-contained headers.
      FID = SM.getDecomposedIncludedLoc(FID).first;
      FE = SM.getFileEntryForID(FID);
      IsOrigin = false;
}
// Now traverse provider trees rooted at exporters.
// Note that we only traverse export edges, and ignore private -> public mappings,
// as those pragmas apply to exporter, and not the main provider being exported in
// this header.
std::set<const FileEntry*> SeenExports;
while (!Exporters.empty()) {
  auto *Export = Exporters.front();
  Exporters.pop();
  if (!SeenExports.insert(Export).second) // In case of cyclic exports
     continue;
  Results.emplace_back(Export, isPublicHeader(Export, *PI));
  for (const auto *Export : PI->getExporters(FE, SM.getFileManager()))
     Exporters.push(Export);
}

That way we don't need to change the contracts around getExporter in PragmaIncludes

204

FE here is always a "provider" for the symbol in question, so we should actually respect the private pragmas in them. we should only ignore those pragmas if they're discovered inside a header, that we found because we followed an export edge.

207

verbatim spellings are relative to an include search path (or even worse, relative to the file containing the include directive) so we can't rely on being able to resolve it directly through FileManager (it can only resolve either absolute paths nor paths relative to CWD).

VitaNuo updated this revision to Diff 548540.Aug 9 2023, 3:05 AM

Address the comments.

VitaNuo marked 3 inline comments as done.Aug 9 2023, 3:05 AM
VitaNuo updated this revision to Diff 548546.Aug 9 2023, 3:24 AM

Add a test with private headers involved. Order of exporters is from outermost to innermost.

kadircet accepted this revision.Aug 9 2023, 4:15 AM

Order of exporters is from outermost to innermost.

FWIW, that's mostly a coincidence. all 3 exporters are private headers, hence they're penalized for it. Afterwards "export1.h" is actually boosted among other exporters as it is also an "origin" provider. "export2.h" and "export3.h" are pretty much equal hence they're ranked based on their name. (discovery order of includes doesn't determine the ranking).

This revision is now accepted and ready to land.Aug 9 2023, 4:15 AM
This revision was landed with ongoing or failed builds.Aug 9 2023, 5:10 AM
This revision was automatically updated to reflect the committed changes.