1 //===--- DeprecatedHeadersCheck.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 "DeprecatedHeadersCheck.h"
10 #include "clang/AST/RecursiveASTVisitor.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "llvm/ADT/StringMap.h"
15 #include "llvm/ADT/StringSet.h"
21 clang::tidy::modernize::DeprecatedHeadersCheck::IncludeMarker
;
22 namespace clang::tidy::modernize
{
25 class IncludeModernizePPCallbacks
: public PPCallbacks
{
27 explicit IncludeModernizePPCallbacks(
28 std::vector
<IncludeMarker
> &IncludesToBeProcessed
, LangOptions LangOpts
,
29 const SourceManager
&SM
, bool CheckHeaderFile
);
31 void InclusionDirective(SourceLocation HashLoc
, const Token
&IncludeTok
,
32 StringRef FileName
, bool IsAngled
,
33 CharSourceRange FilenameRange
,
34 OptionalFileEntryRef File
, StringRef SearchPath
,
35 StringRef RelativePath
, const Module
*SuggestedModule
,
37 SrcMgr::CharacteristicKind FileType
) override
;
40 std::vector
<IncludeMarker
> &IncludesToBeProcessed
;
42 llvm::StringMap
<std::string
> CStyledHeaderToCxx
;
43 llvm::StringSet
<> DeleteHeaders
;
44 const SourceManager
&SM
;
48 class ExternCRefutationVisitor
49 : public RecursiveASTVisitor
<ExternCRefutationVisitor
> {
50 std::vector
<IncludeMarker
> &IncludesToBeProcessed
;
51 const SourceManager
&SM
;
54 ExternCRefutationVisitor(std::vector
<IncludeMarker
> &IncludesToBeProcessed
,
56 : IncludesToBeProcessed(IncludesToBeProcessed
), SM(SM
) {}
57 bool shouldWalkTypesOfTypeLocs() const { return false; }
58 bool shouldVisitLambdaBody() const { return false; }
60 bool VisitLinkageSpecDecl(LinkageSpecDecl
*LinkSpecDecl
) const {
61 if (LinkSpecDecl
->getLanguage() != LinkageSpecLanguageIDs::C
||
62 !LinkSpecDecl
->hasBraces())
65 auto ExternCBlockBegin
= LinkSpecDecl
->getBeginLoc();
66 auto ExternCBlockEnd
= LinkSpecDecl
->getEndLoc();
67 auto IsWrapped
= [=, &SM
= SM
](const IncludeMarker
&Marker
) -> bool {
68 return SM
.isBeforeInTranslationUnit(ExternCBlockBegin
, Marker
.DiagLoc
) &&
69 SM
.isBeforeInTranslationUnit(Marker
.DiagLoc
, ExternCBlockEnd
);
72 llvm::erase_if(IncludesToBeProcessed
, IsWrapped
);
78 DeprecatedHeadersCheck::DeprecatedHeadersCheck(StringRef Name
,
79 ClangTidyContext
*Context
)
80 : ClangTidyCheck(Name
, Context
),
81 CheckHeaderFile(Options
.get("CheckHeaderFile", false)) {}
83 void DeprecatedHeadersCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
84 Options
.store(Opts
, "CheckHeaderFile", CheckHeaderFile
);
87 void DeprecatedHeadersCheck::registerPPCallbacks(
88 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
89 PP
->addPPCallbacks(std::make_unique
<IncludeModernizePPCallbacks
>(
90 IncludesToBeProcessed
, getLangOpts(), PP
->getSourceManager(),
93 void DeprecatedHeadersCheck::registerMatchers(
94 ast_matchers::MatchFinder
*Finder
) {
95 // Even though the checker operates on a "preprocessor" level, we still need
96 // to act on a "TranslationUnit" to acquire the AST where we can walk each
97 // Decl and look for `extern "C"` blocks where we will suppress the report we
98 // collected during the preprocessing phase.
99 // The `onStartOfTranslationUnit()` won't suffice, since we need some handle
100 // to the `ASTContext`.
101 Finder
->addMatcher(ast_matchers::translationUnitDecl().bind("TU"), this);
104 void DeprecatedHeadersCheck::onEndOfTranslationUnit() {
105 IncludesToBeProcessed
.clear();
108 void DeprecatedHeadersCheck::check(
109 const ast_matchers::MatchFinder::MatchResult
&Result
) {
110 SourceManager
&SM
= Result
.Context
->getSourceManager();
112 // Suppress includes wrapped by `extern "C" { ... }` blocks.
113 ExternCRefutationVisitor
Visitor(IncludesToBeProcessed
, SM
);
114 Visitor
.TraverseAST(*Result
.Context
);
116 // Emit all the remaining reports.
117 for (const IncludeMarker
&Marker
: IncludesToBeProcessed
) {
118 if (Marker
.Replacement
.empty()) {
120 "including '%0' has no effect in C++; consider removing it")
122 << FixItHint::CreateRemoval(Marker
.ReplacementRange
);
124 diag(Marker
.DiagLoc
, "inclusion of deprecated C++ header "
125 "'%0'; consider using '%1' instead")
126 << Marker
.FileName
<< Marker
.Replacement
127 << FixItHint::CreateReplacement(
128 Marker
.ReplacementRange
,
129 (llvm::Twine("<") + Marker
.Replacement
+ ">").str());
134 IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
135 std::vector
<IncludeMarker
> &IncludesToBeProcessed
, LangOptions LangOpts
,
136 const SourceManager
&SM
, bool CheckHeaderFile
)
137 : IncludesToBeProcessed(IncludesToBeProcessed
), LangOpts(LangOpts
), SM(SM
),
138 CheckHeaderFile(CheckHeaderFile
) {
139 for (const auto &KeyValue
:
140 std::vector
<std::pair
<llvm::StringRef
, std::string
>>(
141 {{"assert.h", "cassert"},
142 {"complex.h", "complex"},
143 {"ctype.h", "cctype"},
144 {"errno.h", "cerrno"},
145 {"float.h", "cfloat"},
146 {"limits.h", "climits"},
147 {"locale.h", "clocale"},
149 {"setjmp.h", "csetjmp"},
150 {"signal.h", "csignal"},
151 {"stdarg.h", "cstdarg"},
152 {"stddef.h", "cstddef"},
153 {"stdio.h", "cstdio"},
154 {"stdlib.h", "cstdlib"},
155 {"string.h", "cstring"},
157 {"wchar.h", "cwchar"},
158 {"wctype.h", "cwctype"}})) {
159 CStyledHeaderToCxx
.insert(KeyValue
);
161 // Add C++11 headers.
162 if (LangOpts
.CPlusPlus11
) {
163 for (const auto &KeyValue
:
164 std::vector
<std::pair
<llvm::StringRef
, std::string
>>(
165 {{"fenv.h", "cfenv"},
166 {"stdint.h", "cstdint"},
167 {"inttypes.h", "cinttypes"},
168 {"tgmath.h", "ctgmath"},
169 {"uchar.h", "cuchar"}})) {
170 CStyledHeaderToCxx
.insert(KeyValue
);
173 for (const auto &Key
:
174 std::vector
<std::string
>({"stdalign.h", "stdbool.h", "iso646.h"})) {
175 DeleteHeaders
.insert(Key
);
179 void IncludeModernizePPCallbacks::InclusionDirective(
180 SourceLocation HashLoc
, const Token
&IncludeTok
, StringRef FileName
,
181 bool IsAngled
, CharSourceRange FilenameRange
, OptionalFileEntryRef File
,
182 StringRef SearchPath
, StringRef RelativePath
, const Module
*SuggestedModule
,
183 bool ModuleImported
, SrcMgr::CharacteristicKind FileType
) {
185 // If we don't want to warn for non-main file reports and this is one, skip
187 if (!CheckHeaderFile
&& !SM
.isInMainFile(HashLoc
))
190 // Ignore system headers.
191 if (SM
.isInSystemHeader(HashLoc
))
194 // FIXME: Take care of library symbols from the global namespace.
196 // Reasonable options for the check:
198 // 1. Insert std prefix for every such symbol occurrence.
199 // 2. Insert `using namespace std;` to the beginning of TU.
200 // 3. Do nothing and let the user deal with the migration himself.
201 SourceLocation DiagLoc
= FilenameRange
.getBegin();
202 if (CStyledHeaderToCxx
.count(FileName
) != 0) {
203 IncludesToBeProcessed
.emplace_back(
204 IncludeMarker
{CStyledHeaderToCxx
[FileName
], FileName
,
205 FilenameRange
.getAsRange(), DiagLoc
});
206 } else if (DeleteHeaders
.count(FileName
) != 0) {
207 IncludesToBeProcessed
.emplace_back(
208 // NOLINTNEXTLINE(modernize-use-emplace) - false-positive
209 IncludeMarker
{std::string
{}, FileName
,
210 SourceRange
{HashLoc
, FilenameRange
.getEnd()}, DiagLoc
});
214 } // namespace clang::tidy::modernize