lix/clang-tidy/FixIncludes.cc

91 lines
3.2 KiB
C++

#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