lix/subprojects/lix-clang-tidy/FixIncludes.cc

93 lines
3.4 KiB
C++
Raw Normal View History

#include "FixIncludes.hh"
#include <clang-tidy/ClangTidyCheck.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/Lex/Preprocessor.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Debug.h>
#include <memory>
#include <set>
#include <string>
namespace nix::clang_tidy {
using namespace clang;
using namespace clang::tidy;
class FixIncludesCallbacks : public PPCallbacks {
public:
ClangTidyCheck &Check;
Preprocessor &PP;
FixIncludesCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
: Check(Check), PP(PP) {}
private:
bool Ignore = false;
virtual void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID, SourceLocation Loc) override;
virtual void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange,
OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported,
SrcMgr::CharacteristicKind FileType) override;
};
void FixIncludesCallbacks::LexedFileChanged(FileID, LexedFileChangeReason,
SrcMgr::CharacteristicKind FileType,
FileID, SourceLocation) {
Ignore = FileType != SrcMgr::C_User;
}
void FixIncludesCallbacks::InclusionDirective(
SourceLocation, const Token &, StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef,
StringRef, const Module *, SrcMgr::CharacteristicKind) {
if (Ignore)
return;
// FIXME: this is kinda evil, but this is a one-time fixup
const std::vector<std::string> SourceDirs = {"src/", "include/lix/"};
const auto Bracketize = [IsAngled](StringRef s) {
return IsAngled ? ("<" + s + ">").str() : ("\"" + s + "\"").str();
};
for (const auto &SourceDir : SourceDirs) {
meson: prepare for include rearrangement Context: we have include paths that are "types.hh" and similarly common names. We currently have these compatibly available as "lix/libutil/types.hh" externally but *not yet internally*. This is because we don't have any way for the src directory to appear as `"lix/"` from inside of Lix: the lix/ include directory is created by the install process. The goal of this whole thing is to make it clearer which component of Lix that files are a part of, which should hopefully help at least a little bit to new developers. One disadvantage of un-mixing these is that it will cause some API changes if we ever move a file between libraries, but that is not very common, and we don't care that much about external API users. This was planned for a while and is why we have a FixIncludes check to begin with. Personally I don't see a great benefit in rearranging our source code, and in fact, it would probably be counterproductive: - Moving the includes into a separate `include/` directory would just make developers have to deal with more directories, when we can already generate the desired layout through the build process. - This would also decouple the .cc and .hh files which currently conventionally have each others' definitions and declarations respectively, right next to each other, making it easier for them to feel decoupled and diverge. Content: Add ../include as an include directory so that lix/ in include paths will resolve to src/ within Lix itself, just as it does externally today. This prepares for a further series of commits applying the actual change to each library one-by-one by accepting both include versions at once. This could have been done with ../ and a symlink called lix, but we would like to not accept libexpr/foo.hh internally for it would be broken externally, so we need an otherwise empty directory for the include. Change-Id: Ideac17faadae2bcea2dffbab34eb27c582ede399
2024-11-12 22:50:57 +00:00
// Ignore generated files, since they are often only used internally within
// a library anyhow and they are not in the normal source dir.
const bool IsAlreadyFixed = FileName.starts_with("lix/lib") || FileName.contains(".gen.") || FileName.ends_with(".md");
if (File && File->getNameAsRequested().contains(SourceDir) &&
!IsAlreadyFixed) {
StringRef Name = File->getNameAsRequested();
auto Idx = Name.find(SourceDir);
assert(Idx != std::string::npos);
std::string Suffix = Name.drop_front(Idx + SourceDir.length()).str();
if (!Suffix.starts_with("lib")) {
llvm::dbgs() << "ignored: " << Suffix << "\n";
return;
}
Suffix = "lix/" + Suffix;
auto Diag = Check.diag(FilenameRange.getBegin(),
"include needs to specify the source subdir");
Diag << FilenameRange
<< FixItHint::CreateReplacement(FilenameRange, Bracketize(Suffix));
}
}
}
void FixIncludesCheck::registerPPCallbacks(const SourceManager &,
Preprocessor *PP, Preprocessor *) {
PP->addPPCallbacks(std::make_unique<FixIncludesCallbacks>(*this, *PP));
}
}; // namespace nix::clang_tidy