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 "../utils/FileExtensionsUtils.h" |
11 | #include "clang/AST/ASTContext.h" |
12 | #include "clang/Lex/Preprocessor.h" |
13 | #include <optional> |
14 | |
15 | namespace clang::tidy::bugprone { |
16 | |
17 | namespace { |
18 | class SuspiciousIncludePPCallbacks : public PPCallbacks { |
19 | public: |
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 | |
33 | private: |
34 | SuspiciousIncludeCheck &Check; |
35 | Preprocessor *PP; |
36 | }; |
37 | } // namespace |
38 | |
39 | SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name, |
40 | ClangTidyContext *Context) |
41 | : ClangTidyCheck(Name, Context), |
42 | HeaderFileExtensions(Context->getHeaderFileExtensions()), |
43 | ImplementationFileExtensions(Context->getImplementationFileExtensions()) { |
44 | } |
45 | |
46 | void SuspiciousIncludeCheck::registerPPCallbacks( |
47 | const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { |
48 | PP->addPPCallbacks( |
49 | C: ::std::make_unique<SuspiciousIncludePPCallbacks>(args&: *this, args: SM, args&: PP)); |
50 | } |
51 | |
52 | void SuspiciousIncludePPCallbacks::InclusionDirective( |
53 | SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, |
54 | bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, |
55 | StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, |
56 | bool ModuleImported, SrcMgr::CharacteristicKind FileType) { |
57 | if (IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import) |
58 | return; |
59 | |
60 | SourceLocation DiagLoc = FilenameRange.getBegin().getLocWithOffset(Offset: 1); |
61 | |
62 | const std::optional<StringRef> IFE = |
63 | utils::getFileExtension(FileName, FileExtensions: Check.ImplementationFileExtensions); |
64 | if (!IFE) |
65 | return; |
66 | |
67 | Check.diag(Loc: DiagLoc, Description: "suspicious #%0 of file with '%1' extension" ) |
68 | << IncludeTok.getIdentifierInfo()->getName() << *IFE; |
69 | |
70 | for (const auto &HFE : Check.HeaderFileExtensions) { |
71 | SmallString<128> GuessedFileName(FileName); |
72 | llvm::sys::path::replace_extension(path&: GuessedFileName, |
73 | extension: (!HFE.empty() ? "." : "" ) + HFE); |
74 | |
75 | OptionalFileEntryRef File = |
76 | PP->LookupFile(FilenameLoc: DiagLoc, Filename: GuessedFileName, isAngled: IsAngled, FromDir: nullptr, FromFile: nullptr, |
77 | CurDir: nullptr, SearchPath: nullptr, RelativePath: nullptr, SuggestedModule: nullptr, IsMapped: nullptr, IsFrameworkFound: nullptr); |
78 | if (File) { |
79 | Check.diag(Loc: DiagLoc, Description: "did you mean to include '%0'?" , Level: DiagnosticIDs::Note) |
80 | << GuessedFileName; |
81 | } |
82 | } |
83 | } |
84 | |
85 | } // namespace clang::tidy::bugprone |
86 | |