diff options
Diffstat (limited to 'subprojects/lix-clang-tidy/FixIncludes.cc')
-rw-r--r-- | subprojects/lix-clang-tidy/FixIncludes.cc | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/subprojects/lix-clang-tidy/FixIncludes.cc b/subprojects/lix-clang-tidy/FixIncludes.cc new file mode 100644 index 000000000..602d3d355 --- /dev/null +++ b/subprojects/lix-clang-tidy/FixIncludes.cc @@ -0,0 +1,90 @@ +#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) { + const bool IsAlreadyFixed = FileName.starts_with("lix/lib"); + 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 |