clang-tools 18.0.0git
SuspiciousIncludeCheck.cpp
Go to the documentation of this file.
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
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 *Imported,
30 SrcMgr::CharacteristicKind FileType) override;
31
32private:
33 SuspiciousIncludeCheck &Check;
34 Preprocessor *PP;
35};
36} // namespace
37
39 ClangTidyContext *Context)
40 : ClangTidyCheck(Name, Context) {
41 std::optional<StringRef> ImplementationFileExtensionsOption =
42 Options.get("ImplementationFileExtensions");
43 RawStringImplementationFileExtensions =
44 ImplementationFileExtensionsOption.value_or(
46 if (ImplementationFileExtensionsOption) {
47 if (!utils::parseFileExtensions(RawStringImplementationFileExtensions,
50 this->configurationDiag("Invalid implementation file extension: '%0'")
51 << RawStringImplementationFileExtensions;
52 }
53 } else
54 ImplementationFileExtensions = Context->getImplementationFileExtensions();
55
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,
64 this->configurationDiag("Invalid header file extension: '%0'")
65 << RawStringHeaderFileExtensions;
66 }
67 } else
68 HeaderFileExtensions = Context->getHeaderFileExtensions();
69}
70
72 Options.store(Opts, "ImplementationFileExtensions",
73 RawStringImplementationFileExtensions);
74 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
75}
76
78 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
79 PP->addPPCallbacks(
80 ::std::make_unique<SuspiciousIncludePPCallbacks>(*this, SM, PP));
81}
82
83void 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)
89 return;
90
91 SourceLocation DiagLoc = FilenameRange.getBegin().getLocWithOffset(1);
92
93 const std::optional<StringRef> IFE =
94 utils::getFileExtension(FileName, Check.ImplementationFileExtensions);
95 if (!IFE)
96 return;
97
98 Check.diag(DiagLoc, "suspicious #%0 of file with '%1' extension")
99 << IncludeTok.getIdentifierInfo()->getName() << *IFE;
100
101 for (const auto &HFE : Check.HeaderFileExtensions) {
102 SmallString<128> GuessedFileName(FileName);
103 llvm::sys::path::replace_extension(GuessedFileName,
104 (!HFE.empty() ? "." : "") + HFE);
105
106 OptionalFileEntryRef File =
107 PP->LookupFile(DiagLoc, GuessedFileName, IsAngled, nullptr, nullptr,
108 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
109 if (File) {
110 Check.diag(DiagLoc, "did you mean to include '%0'?", DiagnosticIDs::Note)
111 << GuessedFileName;
112 }
113 }
114}
115
116} // namespace clang::tidy::bugprone
llvm::StringRef Name
bool IsAngled
true if this was an include with angle brackets
StringRef FileName
std::optional< StringRef > get(StringRef LocalName) const
Read a named option from the Context.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
SuspiciousIncludeCheck(StringRef Name, ClangTidyContext *Context)
std::optional< StringRef > getFileExtension(StringRef FileName, const FileExtensionsSet &FileExtensions)
Decides whether a file has a header file extension.
StringRef defaultHeaderFileExtensions()
Returns recommended default value for the list of header file extensions.
StringRef defaultFileExtensionDelimiters()
Returns recommended default value for the list of file extension delimiters.
bool parseFileExtensions(StringRef AllFileExtensions, FileExtensionsSet &FileExtensions, StringRef Delimiters)
Parses header file extensions from a semicolon-separated list.
StringRef defaultImplementationFileExtensions()
Returns recommended default value for the list of implementation file extensions.
llvm::StringMap< ClangTidyValue > OptionMap