zixuw created this revision. zixuw added reviewers: ributzka, dang, cishida. Herald added a project: All. zixuw requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Proof-of-concept patch. Needs more work. Transform input headers to relative (angle) includes. `getRelativeIncludeName` walks `HeaderSearchOptions::UserEntries` to try to find a relative include stem. Problems: - do we want the exact logic of `HeaderSearch::suggestPathToFileForDiagnostics`? - in `ExtractAPIAction::PrepareToExecuteAction` where the includes are transformed, we don't have a pre-processor yet, so we don't have access to a lot of useful information like `HeaderSearch`, headermaps, DirectoryLookup etc. - we might not always want to transform an absolute path because the resulting relative include name might get remapped in a headermap, for example in test `known_files_only_hmap.c`. But how does it work with modules where we need relative includes? Is the setup in `known_files_only_hmap` even valid? - reverse lookup in headermap when deciding if the symbol is coming from a knwon file: I think my current implementation is correct and necessary to solve the headermap problem, but might have problems due to the above point: an unknown file might be mapped to the transformed relative name of a known input. I think the key problem is that, by passing in an input `/absolute/path/header.h`, do we mean to process the actual content of that particular header, or do we mean to use it just as a "lable" and the thing we really want to process is whatever this lable points to with the given search paths and headermaps? In the example of frameworks, I think the latter is true: we want to use DSTROOT to pass in the headers because DSTROOT has a defined framework layout so that we can transform the paths to framework-style relative includes by matching the layout pattern `*.framework/Header/` and rely on headermaps/VFS/module/search paths to find the intended headers. In that sense, the `known_files_only_hmap` test is a really strange setup with ambiguious intention and context. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D123831 Files: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp =================================================================== --- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -38,7 +38,10 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" #include <memory> #include <utility> @@ -55,10 +58,113 @@ return {}; } +Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, + StringRef File) { + using namespace llvm::sys; + // Matches framework include patterns + const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)"); + StringRef WorkingDir = CI.getFileSystemOpts().WorkingDir; + + SmallString<128> FilePath(File.begin(), File.end()); + if (!WorkingDir.empty() && !path::is_absolute(FilePath)) + fs::make_absolute(WorkingDir, FilePath); + path::remove_dots(FilePath, true); + File = FilePath; + + // FIXME: The following is adapted from + // HeaderSearch::suggestPathToFileForDiagnostics. Need to figure out if + // everything here is actually needed. + unsigned BestPrefixLength = 0; + // Checks whether `Dir` is a strict path prefix of `File`. If so and that's + // the longest prefix we've seen so for it, returns true and updates the + // `BestPrefixLength` accordingly. + auto CheckDir = [&](llvm::StringRef Dir) -> bool { + llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); + if (!WorkingDir.empty() && !path::is_absolute(DirPath)) + fs::make_absolute(WorkingDir, DirPath); + path::remove_dots(DirPath, true); + Dir = DirPath; + for (auto NI = path::begin(File), NE = path::end(File), + DI = path::begin(Dir), DE = path::end(Dir); + /*termination condition in loop*/; ++NI, ++DI) { + // '.' components in File are ignored. + while (NI != NE && *NI == ".") + ++NI; + if (NI == NE) + break; + + // '.' components in Dir are ignored. + while (DI != DE && *DI == ".") + ++DI; + if (DI == DE) { + // Dir is a prefix of File, up to '.' components and choice of path + // separators. + unsigned PrefixLength = NI - path::begin(File); + if (PrefixLength > BestPrefixLength) { + BestPrefixLength = PrefixLength; + return true; + } + break; + } + + // Consider all path separators equal. + if (NI->size() == 1 && DI->size() == 1 && + path::is_separator(NI->front()) && path::is_separator(DI->front())) + continue; + + // Special case Apple .sdk folders since the search path is typically a + // symlink like `iPhoneSimulator14.5.sdk` while the file is instead + // located in `iPhoneSimulator.sdk` (the real folder). + if (NI->endswith(".sdk") && DI->endswith(".sdk")) { + StringRef NBasename = path::stem(*NI); + StringRef DBasename = path::stem(*DI); + if (DBasename.startswith(NBasename)) + continue; + } + + if (*NI != *DI) + break; + } + return false; + }; + + bool BestPrefixIsFramework = false; + + for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { + if (CheckDir(Entry.Path)) { + if (Entry.IsFramework) + BestPrefixIsFramework = true; + else + BestPrefixIsFramework = false; + } + } + + if (!BestPrefixLength && CheckDir(CI.getFileSystemOpts().WorkingDir)) + BestPrefixIsFramework = false; + + if (!BestPrefixLength) + return None; + + if (BestPrefixIsFramework) { + SmallVector<StringRef, 4> Matches; + Rule.match(File, &Matches); + // Returned matches are always in stable order. + if (Matches.size() != 4) + return None; + + return path::convert_to_slash( + (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + Matches[3]) + .str()); + } + + return path::convert_to_slash(File.drop_front(BestPrefixLength)); +} + struct LocationFileChecker { bool isLocationInKnownFile(SourceLocation Loc) { // If the loc refers to a macro expansion we need to first get the file // location of the expansion. + auto &SM = CI.getSourceManager(); auto FileLoc = SM.getFileLoc(Loc); FileID FID = SM.getFileID(FileLoc); if (FID.isInvalid()) @@ -71,20 +177,44 @@ if (KnownFileEntries.count(File)) return true; + if (ExternalFileEntries.count(File)) + return false; + + for (const DirectoryLookup &DL : + CI.getPreprocessor().getHeaderSearchInfo().search_dir_range()) { + if (!DL.isHeaderMap()) + continue; + + StringRef SpelledFilename = + DL.getHeaderMap()->reverseLookupFilename(File->getName()); + if (!SpelledFilename.empty() && + llvm::find(KnownFiles, SpelledFilename) != KnownFiles.end()) { + // Record that the file was found to avoid future reverse lookup for the + // same file. + KnownFileEntries.insert(File); + return true; + } + } + + // Record that the file was not found to avoid future reverse lookup for + // the same file. + ExternalFileEntries.insert(File); return false; } - LocationFileChecker(const SourceManager &SM, - const std::vector<std::string> &KnownFiles) - : SM(SM) { + LocationFileChecker(const CompilerInstance &CI, + std::vector<std::string> &KnownFiles) + : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() { for (const auto &KnownFilePath : KnownFiles) - if (auto FileEntry = SM.getFileManager().getFile(KnownFilePath)) + if (auto FileEntry = CI.getFileManager().getFile(KnownFilePath)) KnownFileEntries.insert(*FileEntry); } private: - const SourceManager &SM; + const CompilerInstance &CI; + std::vector<std::string> &KnownFiles; llvm::DenseSet<const FileEntry *> KnownFileEntries; + llvm::DenseSet<const FileEntry *> ExternalFileEntries; }; /// The RecursiveASTVisitor to traverse symbol declarations and collect API @@ -743,8 +873,7 @@ CI.getTarget().getTriple(), CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); - auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(), - KnownInputFiles); + auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( CI.getSourceManager(), *LCF, *API, CI.getPreprocessor())); @@ -767,11 +896,19 @@ HeaderContents += "#import"; else HeaderContents += "#include"; - HeaderContents += " \""; - HeaderContents += FIF.getFile(); - HeaderContents += "\"\n"; - KnownInputFiles.emplace_back(FIF.getFile()); + StringRef FilePath = FIF.getFile(); + if (auto RelativeName = getRelativeIncludeName(CI, FilePath)) { + HeaderContents += " <"; + HeaderContents += *RelativeName; + HeaderContents += ">\n"; + KnownInputFiles.emplace_back(*RelativeName); + } else { + HeaderContents += " \""; + HeaderContents += FilePath; + HeaderContents += "\"\n"; + KnownInputFiles.emplace_back(FilePath); + } } Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits