1 //===--- SuspiciousIncludeCheck.cpp - clang-tidy --------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "SuspiciousIncludeCheck.h"
10 #include "../utils/FileExtensionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Lex/Preprocessor.h"
15 namespace clang::tidy::bugprone
{
18 class SuspiciousIncludePPCallbacks
: public PPCallbacks
{
20 explicit SuspiciousIncludePPCallbacks(SuspiciousIncludeCheck
&Check
,
21 const SourceManager
&SM
,
23 : Check(Check
), PP(PP
) {}
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
*Imported
,
30 SrcMgr::CharacteristicKind FileType
) override
;
33 SuspiciousIncludeCheck
&Check
;
38 SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name
,
39 ClangTidyContext
*Context
)
40 : ClangTidyCheck(Name
, Context
) {
41 std::optional
<StringRef
> ImplementationFileExtensionsOption
=
42 Options
.get("ImplementationFileExtensions");
43 RawStringImplementationFileExtensions
=
44 ImplementationFileExtensionsOption
.value_or(
45 utils::defaultImplementationFileExtensions());
46 if (ImplementationFileExtensionsOption
) {
47 if (!utils::parseFileExtensions(RawStringImplementationFileExtensions
,
48 ImplementationFileExtensions
,
49 utils::defaultFileExtensionDelimiters())) {
50 this->configurationDiag("Invalid implementation file extension: '%0'")
51 << RawStringImplementationFileExtensions
;
54 ImplementationFileExtensions
= Context
->getImplementationFileExtensions();
56 std::optional
<StringRef
> HeaderFileExtensionsOption
=
57 Options
.get("HeaderFileExtensions");
58 RawStringHeaderFileExtensions
=
59 HeaderFileExtensionsOption
.value_or(utils::defaultHeaderFileExtensions());
60 if (HeaderFileExtensionsOption
) {
61 if (!utils::parseFileExtensions(RawStringHeaderFileExtensions
,
63 utils::defaultFileExtensionDelimiters())) {
64 this->configurationDiag("Invalid header file extension: '%0'")
65 << RawStringHeaderFileExtensions
;
68 HeaderFileExtensions
= Context
->getHeaderFileExtensions();
71 void SuspiciousIncludeCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
72 Options
.store(Opts
, "ImplementationFileExtensions",
73 RawStringImplementationFileExtensions
);
74 Options
.store(Opts
, "HeaderFileExtensions", RawStringHeaderFileExtensions
);
77 void SuspiciousIncludeCheck::registerPPCallbacks(
78 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
80 ::std::make_unique
<SuspiciousIncludePPCallbacks
>(*this, SM
, PP
));
83 void SuspiciousIncludePPCallbacks::InclusionDirective(
84 SourceLocation HashLoc
, const Token
&IncludeTok
, StringRef FileName
,
85 bool IsAngled
, CharSourceRange FilenameRange
, OptionalFileEntryRef File
,
86 StringRef SearchPath
, StringRef RelativePath
, const Module
*Imported
,
87 SrcMgr::CharacteristicKind FileType
) {
88 if (IncludeTok
.getIdentifierInfo()->getPPKeywordID() == tok::pp_import
)
91 SourceLocation DiagLoc
= FilenameRange
.getBegin().getLocWithOffset(1);
93 const std::optional
<StringRef
> IFE
=
94 utils::getFileExtension(FileName
, Check
.ImplementationFileExtensions
);
98 Check
.diag(DiagLoc
, "suspicious #%0 of file with '%1' extension")
99 << IncludeTok
.getIdentifierInfo()->getName() << *IFE
;
101 for (const auto &HFE
: Check
.HeaderFileExtensions
) {
102 SmallString
<128> GuessedFileName(FileName
);
103 llvm::sys::path::replace_extension(GuessedFileName
,
104 (!HFE
.empty() ? "." : "") + HFE
);
106 OptionalFileEntryRef File
=
107 PP
->LookupFile(DiagLoc
, GuessedFileName
, IsAngled
, nullptr, nullptr,
108 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
110 Check
.diag(DiagLoc
, "did you mean to include '%0'?", DiagnosticIDs::Note
)
116 } // namespace clang::tidy::bugprone