1//===--- DeprecatedHeadersCheck.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 "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"
16
17#include <algorithm>
18#include <vector>
19
20using IncludeMarker =
21 clang::tidy::modernize::DeprecatedHeadersCheck::IncludeMarker;
22namespace clang::tidy::modernize {
23namespace {
24
25class IncludeModernizePPCallbacks : public PPCallbacks {
26public:
27 explicit IncludeModernizePPCallbacks(
28 std::vector<IncludeMarker> &IncludesToBeProcessed, LangOptions LangOpts,
29 const SourceManager &SM, bool CheckHeaderFile);
30
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,
36 bool ModuleImported,
37 SrcMgr::CharacteristicKind FileType) override;
38
39private:
40 std::vector<IncludeMarker> &IncludesToBeProcessed;
41 LangOptions LangOpts;
42 llvm::StringMap<std::string> CStyledHeaderToCxx;
43 llvm::StringSet<> DeleteHeaders;
44 const SourceManager &SM;
45 bool CheckHeaderFile;
46};
47
48class ExternCRefutationVisitor
49 : public RecursiveASTVisitor<ExternCRefutationVisitor> {
50 std::vector<IncludeMarker> &IncludesToBeProcessed;
51 const SourceManager &SM;
52
53public:
54 ExternCRefutationVisitor(std::vector<IncludeMarker> &IncludesToBeProcessed,
55 SourceManager &SM)
56 : IncludesToBeProcessed(IncludesToBeProcessed), SM(SM) {}
57 bool shouldWalkTypesOfTypeLocs() const { return false; }
58 bool shouldVisitLambdaBody() const { return false; }
59
60 bool VisitLinkageSpecDecl(LinkageSpecDecl *LinkSpecDecl) const {
61 if (LinkSpecDecl->getLanguage() != LinkageSpecLanguageIDs::C ||
62 !LinkSpecDecl->hasBraces())
63 return true;
64
65 auto ExternCBlockBegin = LinkSpecDecl->getBeginLoc();
66 auto ExternCBlockEnd = LinkSpecDecl->getEndLoc();
67 auto IsWrapped = [=, &SM = SM](const IncludeMarker &Marker) -> bool {
68 return SM.isBeforeInTranslationUnit(LHS: ExternCBlockBegin, RHS: Marker.DiagLoc) &&
69 SM.isBeforeInTranslationUnit(LHS: Marker.DiagLoc, RHS: ExternCBlockEnd);
70 };
71
72 llvm::erase_if(C&: IncludesToBeProcessed, P: IsWrapped);
73 return true;
74 }
75};
76} // namespace
77
78DeprecatedHeadersCheck::DeprecatedHeadersCheck(StringRef Name,
79 ClangTidyContext *Context)
80 : ClangTidyCheck(Name, Context),
81 CheckHeaderFile(Options.get(LocalName: "CheckHeaderFile", Default: false)) {}
82
83void DeprecatedHeadersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
84 Options.store(Options&: Opts, LocalName: "CheckHeaderFile", Value: CheckHeaderFile);
85}
86
87void DeprecatedHeadersCheck::registerPPCallbacks(
88 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
89 PP->addPPCallbacks(C: std::make_unique<IncludeModernizePPCallbacks>(
90 args&: IncludesToBeProcessed, args: getLangOpts(), args&: PP->getSourceManager(),
91 args&: CheckHeaderFile));
92}
93void 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(NodeMatch: ast_matchers::translationUnitDecl().bind(ID: "TU"), Action: this);
102}
103
104void DeprecatedHeadersCheck::onEndOfTranslationUnit() {
105 IncludesToBeProcessed.clear();
106}
107
108void DeprecatedHeadersCheck::check(
109 const ast_matchers::MatchFinder::MatchResult &Result) {
110 SourceManager &SM = Result.Context->getSourceManager();
111
112 // Suppress includes wrapped by `extern "C" { ... }` blocks.
113 ExternCRefutationVisitor Visitor(IncludesToBeProcessed, SM);
114 Visitor.TraverseAST(AST&: *Result.Context);
115
116 // Emit all the remaining reports.
117 for (const IncludeMarker &Marker : IncludesToBeProcessed) {
118 if (Marker.Replacement.empty()) {
119 diag(Loc: Marker.DiagLoc,
120 Description: "including '%0' has no effect in C++; consider removing it")
121 << Marker.FileName
122 << FixItHint::CreateRemoval(RemoveRange: Marker.ReplacementRange);
123 } else {
124 diag(Loc: Marker.DiagLoc, Description: "inclusion of deprecated C++ header "
125 "'%0'; consider using '%1' instead")
126 << Marker.FileName << Marker.Replacement
127 << FixItHint::CreateReplacement(
128 RemoveRange: Marker.ReplacementRange,
129 Code: (llvm::Twine("<") + Marker.Replacement + ">").str());
130 }
131 }
132}
133
134IncludeModernizePPCallbacks::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"},
148 {"math.h", "cmath"},
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"},
156 {"time.h", "ctime"},
157 {"wchar.h", "cwchar"},
158 {"wctype.h", "cwctype"}})) {
159 CStyledHeaderToCxx.insert(KV: KeyValue);
160 }
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(KV: KeyValue);
171 }
172 }
173 for (const auto &Key :
174 std::vector<std::string>({"stdalign.h", "stdbool.h", "iso646.h"})) {
175 DeleteHeaders.insert(key: Key);
176 }
177}
178
179void 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) {
184
185 // If we don't want to warn for non-main file reports and this is one, skip
186 // it.
187 if (!CheckHeaderFile && !SM.isInMainFile(Loc: HashLoc))
188 return;
189
190 // Ignore system headers.
191 if (SM.isInSystemHeader(Loc: HashLoc))
192 return;
193
194 // FIXME: Take care of library symbols from the global namespace.
195 //
196 // Reasonable options for the check:
197 //
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(Key: FileName) != 0) {
203 IncludesToBeProcessed.emplace_back(
204 args: IncludeMarker{.Replacement: CStyledHeaderToCxx[FileName], .FileName: FileName,
205 .ReplacementRange: FilenameRange.getAsRange(), .DiagLoc: DiagLoc});
206 } else if (DeleteHeaders.count(Key: FileName) != 0) {
207 IncludesToBeProcessed.emplace_back(
208 // NOLINTNEXTLINE(modernize-use-emplace) - false-positive
209 args: IncludeMarker{.Replacement: std::string{}, .FileName: FileName,
210 .ReplacementRange: SourceRange{HashLoc, FilenameRange.getEnd()}, .DiagLoc: DiagLoc});
211 }
212}
213
214} // namespace clang::tidy::modernize
215

source code of clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp