//===--- SuspiciousIncludeCheck.cpp - clang-tidy --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SuspiciousIncludeCheck.h" #include "../utils/FileExtensionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/Lex/Preprocessor.h" #include namespace clang::tidy::bugprone { namespace { class SuspiciousIncludePPCallbacks : public PPCallbacks { public: explicit SuspiciousIncludePPCallbacks(SuspiciousIncludeCheck &Check, const SourceManager &SM, Preprocessor *PP) : Check(Check), PP(PP) {} void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported, SrcMgr::CharacteristicKind FileType) override; private: SuspiciousIncludeCheck &Check; Preprocessor *PP; }; } // namespace SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), HeaderFileExtensions(Context->getHeaderFileExtensions()), ImplementationFileExtensions(Context->getImplementationFileExtensions()) { } void SuspiciousIncludeCheck::registerPPCallbacks( const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { PP->addPPCallbacks( ::std::make_unique(*this, SM, PP)); } void SuspiciousIncludePPCallbacks::InclusionDirective( SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported, SrcMgr::CharacteristicKind FileType) { if (IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import) return; SourceLocation DiagLoc = FilenameRange.getBegin().getLocWithOffset(1); const std::optional IFE = utils::getFileExtension(FileName, Check.ImplementationFileExtensions); if (!IFE) return; Check.diag(DiagLoc, "suspicious #%0 of file with '%1' extension") << IncludeTok.getIdentifierInfo()->getName() << *IFE; for (const auto &HFE : Check.HeaderFileExtensions) { SmallString<128> GuessedFileName(FileName); llvm::sys::path::replace_extension(GuessedFileName, (!HFE.empty() ? "." : "") + HFE); OptionalFileEntryRef File = PP->LookupFile(DiagLoc, GuessedFileName, IsAngled, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); if (File) { Check.diag(DiagLoc, "did you mean to include '%0'?", DiagnosticIDs::Note) << GuessedFileName; } } } } // namespace clang::tidy::bugprone