95 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "SuspiciousIncludeCheck.h"10#include "../utils/FileExtensionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/Lex/Preprocessor.h"13#include <optional>14 15namespace clang::tidy::bugprone {16 17namespace {18class SuspiciousIncludePPCallbacks : public PPCallbacks {19public:20 explicit SuspiciousIncludePPCallbacks(SuspiciousIncludeCheck &Check,21 const SourceManager &SM,22 Preprocessor *PP)23 : Check(Check), PP(PP) {}24 25 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,26 StringRef FileName, bool IsAngled,27 CharSourceRange FilenameRange,28 OptionalFileEntryRef File, StringRef SearchPath,29 StringRef RelativePath, const Module *SuggestedModule,30 bool ModuleImported,31 SrcMgr::CharacteristicKind FileType) override;32 33private:34 SuspiciousIncludeCheck &Check;35 Preprocessor *PP;36};37} // namespace38 39SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name,40 ClangTidyContext *Context)41 : ClangTidyCheck(Name, Context),42 HeaderFileExtensions(Context->getHeaderFileExtensions()),43 ImplementationFileExtensions(Context->getImplementationFileExtensions()),44 IgnoredRegexString(Options.get("IgnoredRegex").value_or(StringRef{})),45 IgnoredRegex(IgnoredRegexString) {}46 47void SuspiciousIncludeCheck::registerPPCallbacks(48 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {49 PP->addPPCallbacks(50 ::std::make_unique<SuspiciousIncludePPCallbacks>(*this, SM, PP));51}52 53void SuspiciousIncludeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {54 if (!IgnoredRegexString.empty())55 Options.store(Opts, "IgnoredRegex", IgnoredRegexString);56}57 58void SuspiciousIncludePPCallbacks::InclusionDirective(59 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,60 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,61 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,62 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {63 if (IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import)64 return;65 66 if (!Check.IgnoredRegexString.empty() && Check.IgnoredRegex.match(FileName))67 return;68 69 const SourceLocation DiagLoc = FilenameRange.getBegin().getLocWithOffset(1);70 71 const std::optional<StringRef> IFE =72 utils::getFileExtension(FileName, Check.ImplementationFileExtensions);73 if (!IFE)74 return;75 76 Check.diag(DiagLoc, "suspicious #%0 of file with '%1' extension")77 << IncludeTok.getIdentifierInfo()->getName() << *IFE;78 79 for (const auto &HFE : Check.HeaderFileExtensions) {80 SmallString<128> GuessedFileName(FileName);81 llvm::sys::path::replace_extension(GuessedFileName,82 (!HFE.empty() ? "." : "") + HFE);83 84 const OptionalFileEntryRef File =85 PP->LookupFile(DiagLoc, GuessedFileName, IsAngled, nullptr, nullptr,86 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);87 if (File) {88 Check.diag(DiagLoc, "did you mean to include '%0'?", DiagnosticIDs::Note)89 << GuessedFileName;90 }91 }92}93 94} // namespace clang::tidy::bugprone95