1 //===--- SuspiciousIncludeCheck.cpp - clang-tidy --------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "SuspiciousIncludeCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/Lex/Preprocessor.h"
12
13 namespace clang {
14 namespace tidy {
15 namespace bugprone {
16
17 namespace {
18 class SuspiciousIncludePPCallbacks : public PPCallbacks {
19 public:
SuspiciousIncludePPCallbacks(SuspiciousIncludeCheck & Check,const SourceManager & SM,Preprocessor * PP)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, const FileEntry *File,
28 StringRef SearchPath, StringRef RelativePath,
29 const Module *Imported,
30 SrcMgr::CharacteristicKind FileType) override;
31
32 private:
33 SuspiciousIncludeCheck &Check;
34 Preprocessor *PP;
35 };
36 } // namespace
37
SuspiciousIncludeCheck(StringRef Name,ClangTidyContext * Context)38 SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name,
39 ClangTidyContext *Context)
40 : ClangTidyCheck(Name, Context),
41 RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
42 "HeaderFileExtensions", utils::defaultHeaderFileExtensions())),
43 RawStringImplementationFileExtensions(Options.getLocalOrGlobal(
44 "ImplementationFileExtensions",
45 utils::defaultImplementationFileExtensions())) {
46 if (!utils::parseFileExtensions(RawStringImplementationFileExtensions,
47 ImplementationFileExtensions,
48 utils::defaultFileExtensionDelimiters())) {
49 this->configurationDiag("Invalid implementation file extension: '%0'")
50 << RawStringImplementationFileExtensions;
51 }
52
53 if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
54 HeaderFileExtensions,
55 utils::defaultFileExtensionDelimiters())) {
56 this->configurationDiag("Invalid header file extension: '%0'")
57 << RawStringHeaderFileExtensions;
58 }
59 }
60
storeOptions(ClangTidyOptions::OptionMap & Opts)61 void SuspiciousIncludeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
62 Options.store(Opts, "ImplementationFileExtensions",
63 RawStringImplementationFileExtensions);
64 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
65 }
66
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)67 void SuspiciousIncludeCheck::registerPPCallbacks(
68 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
69 PP->addPPCallbacks(
70 ::std::make_unique<SuspiciousIncludePPCallbacks>(*this, SM, PP));
71 }
72
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,const FileEntry * File,StringRef SearchPath,StringRef RelativePath,const Module * Imported,SrcMgr::CharacteristicKind FileType)73 void SuspiciousIncludePPCallbacks::InclusionDirective(
74 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
75 bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
76 StringRef SearchPath, StringRef RelativePath, const Module *Imported,
77 SrcMgr::CharacteristicKind FileType) {
78 if (IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import)
79 return;
80
81 SourceLocation DiagLoc = FilenameRange.getBegin().getLocWithOffset(1);
82
83 const Optional<StringRef> IFE =
84 utils::getFileExtension(FileName, Check.ImplementationFileExtensions);
85 if (!IFE)
86 return;
87
88 Check.diag(DiagLoc, "suspicious #%0 of file with '%1' extension")
89 << IncludeTok.getIdentifierInfo()->getName() << *IFE;
90
91 for (const auto &HFE : Check.HeaderFileExtensions) {
92 SmallString<128> GuessedFileName(FileName);
93 llvm::sys::path::replace_extension(GuessedFileName,
94 (HFE.size() ? "." : "") + HFE);
95
96 const DirectoryLookup *CurDir;
97 Optional<FileEntryRef> File =
98 PP->LookupFile(DiagLoc, GuessedFileName, IsAngled, nullptr, nullptr,
99 CurDir, nullptr, nullptr, nullptr, nullptr, nullptr);
100 if (File) {
101 Check.diag(DiagLoc, "did you mean to include '%0'?", DiagnosticIDs::Note)
102 << GuessedFileName;
103 }
104 }
105 }
106
107 } // namespace bugprone
108 } // namespace tidy
109 } // namespace clang
110