Skip to content

Commit 665bfbb

Browse files
author
Balazs Benics
committed
Reland "[clang-tidy] modernize-deprecated-headers check should respect extern "C" blocks""
This partially reverts commit e8cae48. Changes since that commit: - Use `SourceManager::isBeforeInTranslationUnit` instead of the fancy decomposed decl logarithmic search. - Add a test for including a system header containing a deprecated include. - Add `REQUIRES: system-linux` clause to the test. Reviewed By: LegalizeAdulthood, whisperity Differential Revision: https://reviews.llvm.org/D125209
1 parent 9886046 commit 665bfbb

File tree

6 files changed

+180
-22
lines changed

6 files changed

+180
-22
lines changed

clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,27 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "DeprecatedHeadersCheck.h"
10+
#include "clang/AST/RecursiveASTVisitor.h"
1011
#include "clang/Frontend/CompilerInstance.h"
1112
#include "clang/Lex/PPCallbacks.h"
1213
#include "clang/Lex/Preprocessor.h"
1314
#include "llvm/ADT/StringMap.h"
1415
#include "llvm/ADT/StringSet.h"
1516

17+
#include <algorithm>
1618
#include <vector>
1719

20+
using IncludeMarker =
21+
clang::tidy::modernize::DeprecatedHeadersCheck::IncludeMarker;
1822
namespace clang {
1923
namespace tidy {
2024
namespace modernize {
21-
2225
namespace {
26+
2327
class IncludeModernizePPCallbacks : public PPCallbacks {
2428
public:
25-
explicit IncludeModernizePPCallbacks(ClangTidyCheck &Check,
26-
LangOptions LangOpts);
29+
explicit IncludeModernizePPCallbacks(
30+
std::vector<IncludeMarker> &IncludesToBeProcessed, LangOptions LangOpts);
2731

2832
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
2933
StringRef FileName, bool IsAngled,
@@ -33,22 +37,95 @@ class IncludeModernizePPCallbacks : public PPCallbacks {
3337
SrcMgr::CharacteristicKind FileType) override;
3438

3539
private:
36-
ClangTidyCheck &Check;
40+
std::vector<IncludeMarker> &IncludesToBeProcessed;
3741
LangOptions LangOpts;
3842
llvm::StringMap<std::string> CStyledHeaderToCxx;
3943
llvm::StringSet<> DeleteHeaders;
4044
};
45+
46+
class ExternCRefutationVisitor
47+
: public RecursiveASTVisitor<ExternCRefutationVisitor> {
48+
std::vector<IncludeMarker> &IncludesToBeProcessed;
49+
const SourceManager &SM;
50+
51+
public:
52+
ExternCRefutationVisitor(std::vector<IncludeMarker> &IncludesToBeProcessed,
53+
SourceManager &SM)
54+
: IncludesToBeProcessed(IncludesToBeProcessed), SM(SM) {}
55+
bool shouldWalkTypesOfTypeLocs() const { return false; }
56+
bool shouldVisitLambdaBody() const { return false; }
57+
58+
bool VisitLinkageSpecDecl(LinkageSpecDecl *LinkSpecDecl) const {
59+
if (LinkSpecDecl->getLanguage() != LinkageSpecDecl::lang_c ||
60+
!LinkSpecDecl->hasBraces())
61+
return true;
62+
63+
auto ExternCBlockBegin = LinkSpecDecl->getBeginLoc();
64+
auto ExternCBlockEnd = LinkSpecDecl->getEndLoc();
65+
auto IsWrapped = [=, &SM = SM](const IncludeMarker &Marker) -> bool {
66+
return SM.isBeforeInTranslationUnit(ExternCBlockBegin, Marker.DiagLoc) &&
67+
SM.isBeforeInTranslationUnit(Marker.DiagLoc, ExternCBlockEnd);
68+
};
69+
70+
llvm::erase_if(IncludesToBeProcessed, IsWrapped);
71+
return true;
72+
}
73+
};
4174
} // namespace
4275

76+
DeprecatedHeadersCheck::DeprecatedHeadersCheck(StringRef Name,
77+
ClangTidyContext *Context)
78+
: ClangTidyCheck(Name, Context) {}
79+
4380
void DeprecatedHeadersCheck::registerPPCallbacks(
4481
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
45-
PP->addPPCallbacks(
46-
::std::make_unique<IncludeModernizePPCallbacks>(*this, getLangOpts()));
82+
PP->addPPCallbacks(std::make_unique<IncludeModernizePPCallbacks>(
83+
IncludesToBeProcessed, getLangOpts()));
84+
}
85+
void DeprecatedHeadersCheck::registerMatchers(
86+
ast_matchers::MatchFinder *Finder) {
87+
// Even though the checker operates on a "preprocessor" level, we still need
88+
// to act on a "TranslationUnit" to acquire the AST where we can walk each
89+
// Decl and look for `extern "C"` blocks where we will suppress the report we
90+
// collected during the preprocessing phase.
91+
// The `onStartOfTranslationUnit()` won't suffice, since we need some handle
92+
// to the `ASTContext`.
93+
Finder->addMatcher(ast_matchers::translationUnitDecl().bind("TU"), this);
94+
}
95+
96+
void DeprecatedHeadersCheck::onEndOfTranslationUnit() {
97+
IncludesToBeProcessed.clear();
98+
}
99+
100+
void DeprecatedHeadersCheck::check(
101+
const ast_matchers::MatchFinder::MatchResult &Result) {
102+
SourceManager &SM = Result.Context->getSourceManager();
103+
104+
// Suppress includes wrapped by `extern "C" { ... }` blocks.
105+
ExternCRefutationVisitor Visitor(IncludesToBeProcessed, SM);
106+
Visitor.TraverseAST(*Result.Context);
107+
108+
// Emit all the remaining reports.
109+
for (const IncludeMarker &Marker : IncludesToBeProcessed) {
110+
if (Marker.Replacement.empty()) {
111+
diag(Marker.DiagLoc,
112+
"including '%0' has no effect in C++; consider removing it")
113+
<< Marker.FileName
114+
<< FixItHint::CreateRemoval(Marker.ReplacementRange);
115+
} else {
116+
diag(Marker.DiagLoc, "inclusion of deprecated C++ header "
117+
"'%0'; consider using '%1' instead")
118+
<< Marker.FileName << Marker.Replacement
119+
<< FixItHint::CreateReplacement(
120+
Marker.ReplacementRange,
121+
(llvm::Twine("<") + Marker.Replacement + ">").str());
122+
}
123+
}
47124
}
48125

49-
IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(ClangTidyCheck &Check,
50-
LangOptions LangOpts)
51-
: Check(Check), LangOpts(LangOpts) {
126+
IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
127+
std::vector<IncludeMarker> &IncludesToBeProcessed, LangOptions LangOpts)
128+
: IncludesToBeProcessed(IncludesToBeProcessed), LangOpts(LangOpts) {
52129
for (const auto &KeyValue :
53130
std::vector<std::pair<llvm::StringRef, std::string>>(
54131
{{"assert.h", "cassert"},
@@ -101,19 +178,15 @@ void IncludeModernizePPCallbacks::InclusionDirective(
101178
// 1. Insert std prefix for every such symbol occurrence.
102179
// 2. Insert `using namespace std;` to the beginning of TU.
103180
// 3. Do nothing and let the user deal with the migration himself.
181+
SourceLocation DiagLoc = FilenameRange.getBegin();
104182
if (CStyledHeaderToCxx.count(FileName) != 0) {
105-
std::string Replacement =
106-
(llvm::Twine("<") + CStyledHeaderToCxx[FileName] + ">").str();
107-
Check.diag(FilenameRange.getBegin(), "inclusion of deprecated C++ header "
108-
"'%0'; consider using '%1' instead")
109-
<< FileName << CStyledHeaderToCxx[FileName]
110-
<< FixItHint::CreateReplacement(FilenameRange.getAsRange(),
111-
Replacement);
183+
IncludesToBeProcessed.push_back(
184+
IncludeMarker{CStyledHeaderToCxx[FileName], FileName,
185+
FilenameRange.getAsRange(), DiagLoc});
112186
} else if (DeleteHeaders.count(FileName) != 0) {
113-
Check.diag(FilenameRange.getBegin(),
114-
"including '%0' has no effect in C++; consider removing it")
115-
<< FileName << FixItHint::CreateRemoval(
116-
SourceRange(HashLoc, FilenameRange.getEnd()));
187+
IncludesToBeProcessed.push_back(
188+
IncludeMarker{std::string{}, FileName,
189+
SourceRange{HashLoc, FilenameRange.getEnd()}, DiagLoc});
117190
}
118191
}
119192

clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,25 @@ namespace modernize {
3434
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-headers.html
3535
class DeprecatedHeadersCheck : public ClangTidyCheck {
3636
public:
37-
DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context)
38-
: ClangTidyCheck(Name, Context) {}
37+
DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context);
3938
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
4039
return LangOpts.CPlusPlus;
4140
}
4241
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
4342
Preprocessor *ModuleExpanderPP) override;
43+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
44+
void onEndOfTranslationUnit() override;
45+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
46+
47+
struct IncludeMarker {
48+
std::string Replacement;
49+
StringRef FileName;
50+
SourceRange ReplacementRange;
51+
SourceLocation DiagLoc;
52+
};
53+
54+
private:
55+
std::vector<IncludeMarker> IncludesToBeProcessed;
4456
};
4557

4658
} // namespace modernize

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ Changes in existing checks
170170
<clang-tidy/checks/misc-redundant-expression>` involving assignments in
171171
conditions. This fixes `Issue 35853 <https://github.com/llvm/llvm-project/issues/35853>`_.
172172

173+
- Fixed a false positive in :doc:`modernize-deprecated-headers
174+
<clang-tidy/checks/modernize-deprecated-headers>` involving including
175+
C header files from C++ files wrapped by ``extern "C" { ... }`` blocks.
176+
Such includes will be ignored by now.
177+
173178
- Improved :doc:`performance-inefficient-vector-operation
174179
<clang-tidy/checks/performance-inefficient-vector-operation>` to work when
175180
the vector is a member of a structure.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <assert.h>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <assert.h>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
// Copy the 'mylib.h' to a directory under the build directory. This is
3+
// required, since the relative order of the emitted diagnostics depends on the
4+
// absolute file paths which is sorted by clang-tidy prior emitting.
5+
//
6+
// RUN: mkdir -p %t/sys && mkdir -p %t/usr \
7+
// RUN: && cp %S/Inputs/modernize-deprecated-headers/mysystemlib.h %t/sys/mysystemlib.h \
8+
// RUN: && cp %S/Inputs/modernize-deprecated-headers/mylib.h %t/usr/mylib.h
9+
10+
// RUN: %check_clang_tidy -std=c++11 %s modernize-deprecated-headers %t \
11+
// RUN: --header-filter='.*' --system-headers \
12+
// RUN: -- -I %t/usr -isystem %t/sys -isystem %S/Inputs/modernize-deprecated-headers
13+
14+
// REQUIRES: system-linux
15+
16+
#define EXTERN_C extern "C"
17+
18+
extern "C++" {
19+
// We should still have the warnings here.
20+
#include <stdbool.h>
21+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it [modernize-deprecated-headers]
22+
}
23+
24+
#include <assert.h>
25+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
26+
27+
#include <stdbool.h>
28+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it [modernize-deprecated-headers]
29+
30+
#include <mysystemlib.h> // FIXME: We should have no warning into system headers.
31+
// CHECK-MESSAGES: mysystemlib.h:1:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
32+
33+
#include <mylib.h>
34+
// CHECK-MESSAGES: mylib.h:1:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
35+
36+
namespace wrapping {
37+
extern "C" {
38+
#include <assert.h> // no-warning
39+
#include <mylib.h> // no-warning
40+
#include <stdbool.h> // no-warning
41+
}
42+
} // namespace wrapping
43+
44+
extern "C" {
45+
namespace wrapped {
46+
#include <assert.h> // no-warning
47+
#include <mylib.h> // no-warning
48+
#include <stdbool.h> // no-warning
49+
} // namespace wrapped
50+
}
51+
52+
namespace wrapping {
53+
extern "C" {
54+
namespace wrapped {
55+
#include <assert.h> // no-warning
56+
#include <mylib.h> // no-warning
57+
#include <stdbool.h> // no-warning
58+
} // namespace wrapped
59+
}
60+
} // namespace wrapping
61+
62+
EXTERN_C {
63+
#include <assert.h> // no-warning
64+
#include <mylib.h> // no-warning
65+
#include <stdbool.h> // no-warning
66+
}

0 commit comments

Comments
 (0)