Compare commits
252 Commits
llvmorg-5.
...
llvmorg-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed7ec907af | ||
|
|
62364efb42 | ||
|
|
ab7cbc9afc | ||
|
|
d251e94e80 | ||
|
|
ed6aea839d | ||
|
|
2c879f4060 | ||
|
|
d07c41fa3e | ||
|
|
4adb2ddb69 | ||
|
|
f51dd7e30e | ||
|
|
39cb0a1ea9 | ||
|
|
e850a1b83c | ||
|
|
4cff10f193 | ||
|
|
68b9f14fa4 | ||
|
|
a20b011d5c | ||
|
|
5a1a5cc8e2 | ||
|
|
3fde7cb012 | ||
|
|
71de8646ae | ||
|
|
cda0841fa5 | ||
|
|
3b6421a28a | ||
|
|
416c5877f9 | ||
|
|
061f469882 | ||
|
|
2a03288383 | ||
|
|
1df636a4f2 | ||
|
|
6a9cef28f2 | ||
|
|
7e19e2d677 | ||
|
|
6d574b622e | ||
|
|
c77d26c972 | ||
|
|
b283ed6bc3 | ||
|
|
b0465c9cdc | ||
|
|
1f7b90c813 | ||
|
|
e6df079e92 | ||
|
|
e6ba2e0c90 | ||
|
|
ad64c684f0 | ||
|
|
8e1284a292 | ||
|
|
4cd2bf3c82 | ||
|
|
7f6e1fc69c | ||
|
|
94fdaca663 | ||
|
|
2fc968b740 | ||
|
|
d63cfd6596 | ||
|
|
1a5584f8e5 | ||
|
|
6aa5628ba8 | ||
|
|
2fb894962b | ||
|
|
d05e1bb960 | ||
|
|
78c8b20d1b | ||
|
|
e1a150a824 | ||
|
|
fed41450da | ||
|
|
7ee1a6c010 | ||
|
|
55dd7c0720 | ||
|
|
92842cac21 | ||
|
|
8585b8b2e9 | ||
|
|
f22ee64548 | ||
|
|
58fb9a0f93 | ||
|
|
4ef9a35c38 | ||
|
|
23de435531 | ||
|
|
e85e175ad4 | ||
|
|
6ed89d2703 | ||
|
|
d3a5999fee | ||
|
|
f80119723d | ||
|
|
1191d42eb1 | ||
|
|
8a9e6cd196 | ||
|
|
8048852215 | ||
|
|
aed1f61fe2 | ||
|
|
77df9b8b22 | ||
|
|
bfc8113a74 | ||
|
|
41bc86e79a | ||
|
|
17a0408a39 | ||
|
|
0b0176ce4f | ||
|
|
79c14f4133 | ||
|
|
de06b1bf9d | ||
|
|
234ff406d2 | ||
|
|
16744f7cf3 | ||
|
|
f5820e511c | ||
|
|
da493bb956 | ||
|
|
5fc99d2679 | ||
|
|
5935de89ba | ||
|
|
0023824740 | ||
|
|
923a5b34d9 | ||
|
|
4bee246aee | ||
|
|
53ca7d6cba | ||
|
|
9162f0c09b | ||
|
|
23abc38d36 | ||
|
|
982b3b4adf | ||
|
|
02637b33ca | ||
|
|
2f000b80c6 | ||
|
|
df01d54011 | ||
|
|
eb22dc0105 | ||
|
|
c227fac4cd | ||
|
|
1193838a48 | ||
|
|
e54e2f2f9d | ||
|
|
2e90d3afe5 | ||
|
|
914f2d0d28 | ||
|
|
0655e6d88c | ||
|
|
f3b5cd166e | ||
|
|
fc2173cc0d | ||
|
|
409a1aa043 | ||
|
|
8f87030580 | ||
|
|
16f21230c5 | ||
|
|
f07e3e8691 | ||
|
|
95582bf245 | ||
|
|
644f5a99e7 | ||
|
|
0f6491a13f | ||
|
|
addec11899 | ||
|
|
b2ebcccb49 | ||
|
|
a65b909eb7 | ||
|
|
f5184c7bb6 | ||
|
|
0b1f180ff5 | ||
|
|
fb895f1bc6 | ||
|
|
d7b75d62a0 | ||
|
|
6fc60ab46b | ||
|
|
b52e7e1091 | ||
|
|
7c5b3e87a1 | ||
|
|
1484a723be | ||
|
|
26f923df50 | ||
|
|
9babc4d687 | ||
|
|
35514e8588 | ||
|
|
2c0d496735 | ||
|
|
8e9a4a8168 | ||
|
|
4bf43dd3dc | ||
|
|
46b2df1b39 | ||
|
|
e87c6f41e3 | ||
|
|
14948450ff | ||
|
|
e986c1479c | ||
|
|
f76e64eddf | ||
|
|
ef9d113ae9 | ||
|
|
0daccddcee | ||
|
|
98fac4776f | ||
|
|
148dbe6143 | ||
|
|
a6cdac02a3 | ||
|
|
4989996dd6 | ||
|
|
cb7645093f | ||
|
|
c7af7a06f8 | ||
|
|
8facb9c510 | ||
|
|
42a16cb20d | ||
|
|
125ee05005 | ||
|
|
486b977073 | ||
|
|
b450b3fb54 | ||
|
|
3e156b37a5 | ||
|
|
6e7efc606d | ||
|
|
ca356cdf32 | ||
|
|
a617c74b04 | ||
|
|
54c90c26f1 | ||
|
|
f6a398a4b0 | ||
|
|
e7f9ae42bd | ||
|
|
79bfb19177 | ||
|
|
0a051746b1 | ||
|
|
d31514d884 | ||
|
|
082c529196 | ||
|
|
7bb9251e16 | ||
|
|
849e38e42e | ||
|
|
11077cf7d9 | ||
|
|
1106495b9c | ||
|
|
0b6e976e71 | ||
|
|
f71ea20b69 | ||
|
|
59913f7bdf | ||
|
|
672fd5906f | ||
|
|
7c71653b25 | ||
|
|
345e75945d | ||
|
|
94bba4c367 | ||
|
|
87ba102f58 | ||
|
|
ee55104010 | ||
|
|
cfa120d2cf | ||
|
|
806b2bf92f | ||
|
|
21f71c38d1 | ||
|
|
ecea256a09 | ||
|
|
2d8d46c951 | ||
|
|
b328f90bb0 | ||
|
|
030d435aba | ||
|
|
ccd3c5f205 | ||
|
|
5bc64df31f | ||
|
|
27eda48a9b | ||
|
|
ee10bd510c | ||
|
|
287cff3451 | ||
|
|
79c5e5c618 | ||
|
|
82156583ba | ||
|
|
3c66179f3e | ||
|
|
92cca6eadc | ||
|
|
1fd2a397de | ||
|
|
1ff0096c9c | ||
|
|
4f3c87e181 | ||
|
|
3f93c63924 | ||
|
|
1b49743c23 | ||
|
|
d4f58435ae | ||
|
|
3b3171ba92 | ||
|
|
4a242dbced | ||
|
|
47548bc4ae | ||
|
|
63f24abef6 | ||
|
|
5f0f07e1cc | ||
|
|
41c8d20c82 | ||
|
|
78a501493e | ||
|
|
fa88fdb8bc | ||
|
|
4bd9bdb3d5 | ||
|
|
5bebaaeb56 | ||
|
|
9f1ab8e3b9 | ||
|
|
63abf924b4 | ||
|
|
1caad7ce21 | ||
|
|
0233b48ba8 | ||
|
|
9b10e0808a | ||
|
|
1055f67308 | ||
|
|
d2f0a38dec | ||
|
|
f172e5f1d1 | ||
|
|
3fe39cdeea | ||
|
|
4c898ba628 | ||
|
|
78b36cd282 | ||
|
|
0cdb18a0ff | ||
|
|
776831911f | ||
|
|
b98695ecbd | ||
|
|
1fc8b50777 | ||
|
|
72cd787e22 | ||
|
|
5476b86ed3 | ||
|
|
f581e2cd50 | ||
|
|
51d42e77e9 | ||
|
|
7dc1f85818 | ||
|
|
4fe1712e62 | ||
|
|
481bb24909 | ||
|
|
57f962dfeb | ||
|
|
4fae7917b7 | ||
|
|
759bf08715 | ||
|
|
c011d30be2 | ||
|
|
59b7122580 | ||
|
|
8819de8819 | ||
|
|
ac7da5564c | ||
|
|
b60a8c7d6a | ||
|
|
6e97d9762d | ||
|
|
2c585068b7 | ||
|
|
80e29a8301 | ||
|
|
475150bd71 | ||
|
|
4cd57bf3a2 | ||
|
|
17becd082c | ||
|
|
9a809b2316 | ||
|
|
74ffad2922 | ||
|
|
aa8d52cd37 | ||
|
|
d7cde544c8 | ||
|
|
33799a65b3 | ||
|
|
43c1ed14b8 | ||
|
|
111f0956ca | ||
|
|
45181d08a0 | ||
|
|
8343e2ddeb | ||
|
|
c18d03105b | ||
|
|
3294e2a1a3 | ||
|
|
e9b065b5e7 | ||
|
|
86db6dfb80 | ||
|
|
7ad09ec2f2 | ||
|
|
7cd09801c4 | ||
|
|
28e9f914ed | ||
|
|
b3e36f2704 | ||
|
|
49075460d9 | ||
|
|
72f564730f | ||
|
|
8b187d55b8 | ||
|
|
71ca167b9e | ||
|
|
2402860537 | ||
|
|
ca21025169 | ||
|
|
bfe45360b1 |
@@ -1,4 +1,5 @@
|
||||
add_subdirectory(clang-apply-replacements)
|
||||
add_subdirectory(clang-rename)
|
||||
add_subdirectory(clang-reorder-fields)
|
||||
add_subdirectory(modularize)
|
||||
if(CLANG_ENABLE_STATIC_ANALYZER)
|
||||
@@ -9,7 +10,6 @@ endif()
|
||||
add_subdirectory(change-namespace)
|
||||
add_subdirectory(clang-query)
|
||||
add_subdirectory(clang-move)
|
||||
add_subdirectory(clangd)
|
||||
add_subdirectory(include-fixer)
|
||||
add_subdirectory(pp-trace)
|
||||
add_subdirectory(tool-template)
|
||||
|
||||
@@ -60,4 +60,3 @@ licenses, and/or restrictions:
|
||||
Program Directory
|
||||
------- ---------
|
||||
clang-tidy clang-tidy/cert
|
||||
clang-tidy clang-tidy/hicpp
|
||||
|
||||
@@ -28,14 +28,6 @@ joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Given "a::b::c", returns {"a", "b", "c"}.
|
||||
llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
|
||||
llvm::SmallVector<llvm::StringRef, 4> Splitted;
|
||||
Name.split(Splitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
return Splitted;
|
||||
}
|
||||
|
||||
SourceLocation startLocationForType(TypeLoc TLoc) {
|
||||
// For elaborated types (e.g. `struct a::A`) we want the portion after the
|
||||
// `struct` but including the namespace qualifier, `a::`.
|
||||
@@ -76,7 +68,9 @@ const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
|
||||
return nullptr;
|
||||
const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
|
||||
const auto *CurrentNs = InnerNs;
|
||||
auto PartialNsNameSplitted = splitSymbolName(PartialNsName);
|
||||
llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
|
||||
PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
while (!PartialNsNameSplitted.empty()) {
|
||||
// Get the inner-most namespace in CurrentContext.
|
||||
while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
|
||||
@@ -202,8 +196,6 @@ tooling::Replacement createInsertion(SourceLocation Loc,
|
||||
// Returns the shortest qualified name for declaration `DeclName` in the
|
||||
// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
|
||||
// is "a::c::d", then "b::X" will be returned.
|
||||
// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns
|
||||
// "::b::X" instead of "b::X" since there will be a name conflict otherwise.
|
||||
// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
|
||||
// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
|
||||
// will have empty name.
|
||||
@@ -214,44 +206,22 @@ std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
|
||||
if (DeclName.find(':') == llvm::StringRef::npos)
|
||||
return DeclName;
|
||||
|
||||
auto NsNameSplitted = splitSymbolName(NsName);
|
||||
auto DeclNsSplitted = splitSymbolName(DeclName);
|
||||
llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val();
|
||||
// If the Decl is in global namespace, there is no need to shorten it.
|
||||
if (DeclNsSplitted.empty())
|
||||
return UnqualifiedDeclName;
|
||||
// If NsName is the global namespace, we can simply use the DeclName sans
|
||||
// leading "::".
|
||||
if (NsNameSplitted.empty())
|
||||
return DeclName;
|
||||
|
||||
if (NsNameSplitted.front() != DeclNsSplitted.front()) {
|
||||
// The DeclName must be fully-qualified, but we still need to decide if a
|
||||
// leading "::" is necessary. For example, if `NsName` is "a::b::c" and the
|
||||
// `DeclName` is "b::X", then the reference must be qualified as "::b::X"
|
||||
// to avoid conflict.
|
||||
if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front()))
|
||||
return ("::" + DeclName).str();
|
||||
return DeclName;
|
||||
while (!DeclName.consume_front((NsName + "::").str())) {
|
||||
const auto Pos = NsName.find_last_of(':');
|
||||
if (Pos == llvm::StringRef::npos)
|
||||
return DeclName;
|
||||
assert(Pos > 0);
|
||||
NsName = NsName.substr(0, Pos - 1);
|
||||
}
|
||||
// Since there is already an overlap namespace, we know that `DeclName` can be
|
||||
// shortened, so we reduce the longest common prefix.
|
||||
auto DeclI = DeclNsSplitted.begin();
|
||||
auto DeclE = DeclNsSplitted.end();
|
||||
auto NsI = NsNameSplitted.begin();
|
||||
auto NsE = NsNameSplitted.end();
|
||||
for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) {
|
||||
}
|
||||
return (DeclI == DeclE)
|
||||
? UnqualifiedDeclName.str()
|
||||
: (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName)
|
||||
.str();
|
||||
return DeclName;
|
||||
}
|
||||
|
||||
std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
|
||||
if (Code.back() != '\n')
|
||||
Code += "\n";
|
||||
auto NsSplitted = splitSymbolName(NestedNs);
|
||||
llvm::SmallVector<StringRef, 4> NsSplitted;
|
||||
NestedNs.split(NsSplitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
while (!NsSplitted.empty()) {
|
||||
// FIXME: consider code style for comments.
|
||||
Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
|
||||
@@ -275,61 +245,27 @@ bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
|
||||
// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
|
||||
bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
|
||||
const DeclContext *DeclCtx, SourceLocation Loc) {
|
||||
SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocStart());
|
||||
SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
|
||||
Loc = SM.getSpellingLoc(Loc);
|
||||
return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
|
||||
(SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
|
||||
isNestedDeclContext(DeclCtx, D->getDeclContext()));
|
||||
}
|
||||
|
||||
// Given a qualified symbol name, returns true if the symbol will be
|
||||
// incorrectly qualified without leading "::".
|
||||
bool conflictInNamespace(llvm::StringRef QualifiedSymbol,
|
||||
llvm::StringRef Namespace) {
|
||||
auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":"));
|
||||
assert(!SymbolSplitted.empty());
|
||||
SymbolSplitted.pop_back(); // We are only interested in namespaces.
|
||||
|
||||
if (SymbolSplitted.size() > 1 && !Namespace.empty()) {
|
||||
auto NsSplitted = splitSymbolName(Namespace.trim(":"));
|
||||
assert(!NsSplitted.empty());
|
||||
// We do not check the outermost namespace since it would not be a conflict
|
||||
// if it equals to the symbol's outermost namespace and the symbol name
|
||||
// would have been shortened.
|
||||
for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) {
|
||||
if (*I == SymbolSplitted.front())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AST_MATCHER(EnumDecl, isScoped) {
|
||||
return Node.isScoped();
|
||||
}
|
||||
|
||||
bool isTemplateParameter(TypeLoc Type) {
|
||||
while (!Type.isNull()) {
|
||||
if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
|
||||
return true;
|
||||
Type = Type.getNextTypeLoc();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ChangeNamespaceTool::ChangeNamespaceTool(
|
||||
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
|
||||
llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
|
||||
std::map<std::string, tooling::Replacements> *FileToReplacements,
|
||||
llvm::StringRef FallbackStyle)
|
||||
: FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
|
||||
OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
|
||||
FilePattern(FilePattern), FilePatternRE(FilePattern) {
|
||||
FileToReplacements->clear();
|
||||
auto OldNsSplitted = splitSymbolName(OldNamespace);
|
||||
auto NewNsSplitted = splitSymbolName(NewNamespace);
|
||||
llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
|
||||
llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
|
||||
llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
|
||||
llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
|
||||
// Calculates `DiffOldNamespace` and `DiffNewNamespace`.
|
||||
while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
|
||||
OldNsSplitted.front() == NewNsSplitted.front()) {
|
||||
@@ -338,9 +274,6 @@ ChangeNamespaceTool::ChangeNamespaceTool(
|
||||
}
|
||||
DiffOldNamespace = joinNamespaces(OldNsSplitted);
|
||||
DiffNewNamespace = joinNamespaces(NewNsSplitted);
|
||||
|
||||
for (const auto &Pattern : WhiteListedSymbolPatterns)
|
||||
WhiteListedSymbolRegexes.emplace_back(Pattern);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
@@ -491,17 +424,6 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
to(GlobalVarMatcher.bind("var_decl")))
|
||||
.bind("var_ref"),
|
||||
this);
|
||||
|
||||
// Handle unscoped enum constant.
|
||||
auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl(
|
||||
hasParent(namespaceDecl()),
|
||||
unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()),
|
||||
hasAncestor(namespaceDecl(isAnonymous())))))));
|
||||
Finder->addMatcher(
|
||||
declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
|
||||
to(UnscopedEnumMatcher.bind("enum_const_decl")))
|
||||
.bind("enum_const_ref"),
|
||||
this);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::run(
|
||||
@@ -566,23 +488,6 @@ void ChangeNamespaceTool::run(
|
||||
assert(Context && "Empty decl context.");
|
||||
fixDeclRefExpr(Result, Context->getDeclContext(),
|
||||
llvm::cast<NamedDecl>(Var), VarRef);
|
||||
} else if (const auto *EnumConstRef =
|
||||
Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
|
||||
// Do not rename the reference if it is already scoped by the EnumDecl name.
|
||||
if (EnumConstRef->hasQualifier() &&
|
||||
EnumConstRef->getQualifier()->getKind() ==
|
||||
NestedNameSpecifier::SpecifierKind::TypeSpec &&
|
||||
EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
|
||||
return;
|
||||
const auto *EnumConstDecl =
|
||||
Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
|
||||
assert(EnumConstDecl);
|
||||
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(Context && "Empty decl context.");
|
||||
// FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it
|
||||
// if it turns out to be an issue.
|
||||
fixDeclRefExpr(Result, Context->getDeclContext(),
|
||||
llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);
|
||||
} else if (const auto *FuncRef =
|
||||
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
|
||||
// If this reference has been processed as a function call, we do not
|
||||
@@ -705,18 +610,19 @@ void ChangeNamespaceTool::moveClassForwardDeclaration(
|
||||
const NamedDecl *FwdDecl) {
|
||||
SourceLocation Start = FwdDecl->getLocStart();
|
||||
SourceLocation End = FwdDecl->getLocEnd();
|
||||
const SourceManager &SM = *Result.SourceManager;
|
||||
SourceLocation AfterSemi = Lexer::findLocationAfterToken(
|
||||
End, tok::semi, SM, Result.Context->getLangOpts(),
|
||||
End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
|
||||
/*SkipTrailingWhitespaceAndNewLine=*/true);
|
||||
if (AfterSemi.isValid())
|
||||
End = AfterSemi.getLocWithOffset(-1);
|
||||
// Delete the forward declaration from the code to be moved.
|
||||
addReplacementOrDie(Start, End, "", SM, &FileToReplacements);
|
||||
addReplacementOrDie(Start, End, "", *Result.SourceManager,
|
||||
&FileToReplacements);
|
||||
llvm::StringRef Code = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
|
||||
SM.getSpellingLoc(End)),
|
||||
SM, Result.Context->getLangOpts());
|
||||
CharSourceRange::getTokenRange(
|
||||
Result.SourceManager->getSpellingLoc(Start),
|
||||
Result.SourceManager->getSpellingLoc(End)),
|
||||
*Result.SourceManager, Result.Context->getLangOpts());
|
||||
// Insert the forward declaration back into the old namespace after moving the
|
||||
// code from old namespace to new namespace.
|
||||
// Insertion information is stored in `InsertFwdDecls` and actual
|
||||
@@ -725,9 +631,8 @@ void ChangeNamespaceTool::moveClassForwardDeclaration(
|
||||
const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
|
||||
// The namespace contains the forward declaration, so it must not be empty.
|
||||
assert(!NsDecl->decls_empty());
|
||||
const auto Insertion = createInsertion(
|
||||
getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),
|
||||
Code, SM);
|
||||
const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
|
||||
Code, *Result.SourceManager);
|
||||
InsertForwardDeclaration InsertFwd;
|
||||
InsertFwd.InsertionOffset = Insertion.getOffset();
|
||||
InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
|
||||
@@ -768,10 +673,10 @@ void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
|
||||
Result.SourceManager->getSpellingLoc(Start),
|
||||
Result.SourceManager->getSpellingLoc(End)),
|
||||
*Result.SourceManager, Result.Context->getLangOpts());
|
||||
// If the symbol is already fully qualified, no change needs to be make.
|
||||
if (NestedName.startswith("::"))
|
||||
return;
|
||||
std::string FromDeclName = FromDecl->getQualifiedNameAsString();
|
||||
for (llvm::Regex &RE : WhiteListedSymbolRegexes)
|
||||
if (RE.match(FromDeclName))
|
||||
return;
|
||||
std::string ReplaceName =
|
||||
getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
|
||||
// Checks if there is any using namespace declarations that can shorten the
|
||||
@@ -829,8 +734,7 @@ void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
|
||||
if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
|
||||
for (const auto *UsingShadow : Using->shadows()) {
|
||||
const auto *TargetDecl = UsingShadow->getTargetDecl();
|
||||
if (TargetDecl->getQualifiedNameAsString() ==
|
||||
FromDecl->getQualifiedNameAsString()) {
|
||||
if (TargetDecl == FromDecl) {
|
||||
ReplaceName = FromDecl->getNameAsString();
|
||||
Matched = true;
|
||||
break;
|
||||
@@ -840,13 +744,11 @@ void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
|
||||
}
|
||||
// If the new nested name in the new namespace is the same as it was in the
|
||||
// old namespace, we don't create replacement.
|
||||
if (NestedName == ReplaceName ||
|
||||
(NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName))
|
||||
if (NestedName == ReplaceName)
|
||||
return;
|
||||
// If the reference need to be fully-qualified, add a leading "::" unless
|
||||
// NewNamespace is the global namespace.
|
||||
if (ReplaceName == FromDeclName && !NewNamespace.empty() &&
|
||||
conflictInNamespace(ReplaceName, NewNamespace))
|
||||
if (ReplaceName == FromDeclName && !NewNamespace.empty())
|
||||
ReplaceName = "::" + ReplaceName;
|
||||
addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
|
||||
&FileToReplacements);
|
||||
@@ -863,8 +765,6 @@ void ChangeNamespaceTool::fixTypeLoc(
|
||||
// Types of CXXCtorInitializers do not need to be fixed.
|
||||
if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
|
||||
return;
|
||||
if (isTemplateParameter(Type))
|
||||
return;
|
||||
// The declaration which this TypeLoc refers to.
|
||||
const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
|
||||
// `hasDeclaration` gives underlying declaration, but if the type is
|
||||
@@ -983,14 +883,11 @@ void ChangeNamespaceTool::onEndOfTranslationUnit() {
|
||||
// Add replacements referring to the changed code to existing replacements,
|
||||
// which refers to the original code.
|
||||
Replaces = Replaces.merge(NewReplacements);
|
||||
auto Style = format::getStyle("file", FilePath, FallbackStyle);
|
||||
if (!Style) {
|
||||
llvm::errs() << llvm::toString(Style.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
format::FormatStyle Style =
|
||||
format::getStyle("file", FilePath, FallbackStyle);
|
||||
// Clean up old namespaces if there is nothing in it after moving.
|
||||
auto CleanReplacements =
|
||||
format::cleanupAroundReplacements(Code, Replaces, *Style);
|
||||
format::cleanupAroundReplacements(Code, Replaces, Style);
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
continue;
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
// files matching `FilePattern`.
|
||||
ChangeNamespaceTool(
|
||||
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
|
||||
llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
|
||||
std::map<std::string, tooling::Replacements> *FileToReplacements,
|
||||
llvm::StringRef FallbackStyle = "LLVM");
|
||||
|
||||
@@ -165,9 +164,6 @@ private:
|
||||
// CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
|
||||
// been processed so that we don't handle them twice.
|
||||
llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
|
||||
// Patterns of symbol names whose references are not expected to be updated
|
||||
// when changing namespaces around them.
|
||||
std::vector<llvm::Regex> WhiteListedSymbolRegexes;
|
||||
};
|
||||
|
||||
} // namespace change_namespace
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
@@ -64,38 +63,10 @@ cl::opt<std::string> FilePattern(
|
||||
cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<bool>
|
||||
DumpYAML("dump_result",
|
||||
cl::desc("Dump new file contents in YAML, if specified."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<std::string> Style("style",
|
||||
cl::desc("The style name used for reformatting."),
|
||||
cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<std::string> WhiteListFile(
|
||||
"whitelist_file",
|
||||
cl::desc("A file containing regexes of symbol names that are not expected "
|
||||
"to be updated when changing namespaces around them."),
|
||||
cl::init(""), cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() {
|
||||
std::vector<std::string> Patterns;
|
||||
if (WhiteListFile.empty())
|
||||
return Patterns;
|
||||
|
||||
llvm::SmallVector<StringRef, 8> Lines;
|
||||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
|
||||
llvm::MemoryBuffer::getFile(WhiteListFile);
|
||||
if (!File)
|
||||
return File.getError();
|
||||
llvm::StringRef Content = File.get()->getBuffer();
|
||||
Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
|
||||
for (auto Line : Lines)
|
||||
Patterns.push_back(Line.trim());
|
||||
return Patterns;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
@@ -104,16 +75,8 @@ int main(int argc, const char **argv) {
|
||||
ChangeNamespaceCategory);
|
||||
const auto &Files = OptionsParser.getSourcePathList();
|
||||
tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
|
||||
llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns =
|
||||
GetWhiteListedSymbolPatterns();
|
||||
if (!WhiteListPatterns) {
|
||||
llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". "
|
||||
<< WhiteListPatterns.getError().message() << "\n";
|
||||
return 1;
|
||||
}
|
||||
change_namespace::ChangeNamespaceTool NamespaceTool(
|
||||
OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns,
|
||||
&Tool.getReplacements(), Style);
|
||||
OldNamespace, NewNamespace, FilePattern, &Tool.getReplacements(), Style);
|
||||
ast_matchers::MatchFinder Finder;
|
||||
NamespaceTool.registerMatchers(&Finder);
|
||||
std::unique_ptr<tooling::FrontendActionFactory> Factory =
|
||||
@@ -138,41 +101,14 @@ int main(int argc, const char **argv) {
|
||||
if (Inplace)
|
||||
return Rewrite.overwriteChangedFiles();
|
||||
|
||||
std::set<llvm::StringRef> ChangedFiles;
|
||||
for (const auto &it : Tool.getReplacements())
|
||||
ChangedFiles.insert(it.first);
|
||||
|
||||
if (DumpYAML) {
|
||||
auto WriteToYAML = [&](llvm::raw_ostream &OS) {
|
||||
OS << "[\n";
|
||||
for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
|
||||
OS << " {\n";
|
||||
OS << " \"FilePath\": \"" << *I << "\",\n";
|
||||
const auto *Entry = FileMgr.getFile(*I);
|
||||
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
|
||||
std::string Content;
|
||||
llvm::raw_string_ostream ContentStream(Content);
|
||||
Rewrite.getEditBuffer(ID).write(ContentStream);
|
||||
OS << " \"SourceText\": \""
|
||||
<< llvm::yaml::escape(ContentStream.str()) << "\"\n";
|
||||
OS << " }";
|
||||
if (I != std::prev(E))
|
||||
OS << ",\n";
|
||||
}
|
||||
OS << "\n]\n";
|
||||
};
|
||||
WriteToYAML(llvm::outs());
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto &File : ChangedFiles) {
|
||||
for (const auto &File : Files) {
|
||||
const auto *Entry = FileMgr.getFile(File);
|
||||
|
||||
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
|
||||
// FIXME: print results in parsable format, e.g. JSON.
|
||||
outs() << "============== " << File << " ==============\n";
|
||||
Rewrite.getEditBuffer(ID).write(llvm::outs());
|
||||
outs() << "\n============================================\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -208,15 +208,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Determine a formatting style from options.
|
||||
format::FormatStyle FormatStyle;
|
||||
if (DoFormat) {
|
||||
auto FormatStyleOrError =
|
||||
format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
|
||||
if (!FormatStyleOrError) {
|
||||
llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
|
||||
return 1;
|
||||
}
|
||||
FormatStyle = *FormatStyleOrError;
|
||||
}
|
||||
if (DoFormat)
|
||||
FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
|
||||
|
||||
TUReplacements TURs;
|
||||
TUReplacementFiles TUFiles;
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace {
|
||||
// FIXME: Move to ASTMatchers.
|
||||
AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
|
||||
|
||||
AST_MATCHER(NamedDecl, notInMacro) { return !Node.getLocation().isMacroID(); }
|
||||
|
||||
AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
|
||||
ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
|
||||
const auto *Context = Node.getDeclContext();
|
||||
@@ -168,21 +166,6 @@ private:
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
class VarDeclarationMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit VarDeclarationMatch(ClangMoveTool *MoveTool)
|
||||
: MoveTool(MoveTool) {}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>("var");
|
||||
assert(VD);
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, VD);
|
||||
}
|
||||
|
||||
private:
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
class TypeAliasMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit TypeAliasMatch(ClangMoveTool *MoveTool)
|
||||
@@ -504,11 +487,8 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
|
||||
auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
|
||||
auto InOldFiles = anyOf(InOldHeader, InOldCC);
|
||||
auto classTemplateForwardDecls =
|
||||
classTemplateDecl(unless(has(cxxRecordDecl(isDefinition()))));
|
||||
auto ForwardClassDecls = namedDecl(
|
||||
anyOf(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition()))),
|
||||
classTemplateForwardDecls));
|
||||
auto ForwardDecls =
|
||||
cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())));
|
||||
auto TopLevelDecl =
|
||||
hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
|
||||
|
||||
@@ -521,8 +501,9 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
// We consider declarations inside a class belongs to the class. So these
|
||||
// declarations will be ignored.
|
||||
auto AllDeclsInHeader = namedDecl(
|
||||
unless(ForwardClassDecls), unless(namespaceDecl()),
|
||||
unless(usingDirectiveDecl()), // using namespace decl.
|
||||
unless(ForwardDecls), unless(namespaceDecl()),
|
||||
unless(usingDirectiveDecl()), // using namespace decl.
|
||||
unless(classTemplateDecl(has(ForwardDecls))), // template forward decl.
|
||||
InOldHeader,
|
||||
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
|
||||
hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
|
||||
@@ -533,7 +514,7 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
return;
|
||||
|
||||
// Match forward declarations in old header.
|
||||
Finder->addMatcher(namedDecl(ForwardClassDecls, InOldHeader).bind("fwd_decl"),
|
||||
Finder->addMatcher(namedDecl(ForwardDecls, InOldHeader).bind("fwd_decl"),
|
||||
this);
|
||||
|
||||
//============================================================================
|
||||
@@ -544,12 +525,12 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
// Matching using decls/type alias decls which are in named/anonymous/global
|
||||
// namespace, these decls are always copied to new.h/cc. Those in classes,
|
||||
// functions are covered in other matchers.
|
||||
Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
|
||||
usingDirectiveDecl(IsOldCCTopLevelDecl),
|
||||
typeAliasDecl(IsOldCCTopLevelDecl)),
|
||||
notInMacro())
|
||||
.bind("using_decl"),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
|
||||
usingDirectiveDecl(IsOldCCTopLevelDecl),
|
||||
typeAliasDecl(IsOldCCTopLevelDecl)))
|
||||
.bind("using_decl"),
|
||||
this);
|
||||
|
||||
// Match static functions/variable definitions which are defined in named
|
||||
// namespaces.
|
||||
@@ -570,22 +551,14 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
|
||||
// Matchers for helper declarations in old.cc.
|
||||
auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
|
||||
auto NotInMovedClass= allOf(unless(InMovedClass), InOldCC);
|
||||
auto IsOldCCHelper =
|
||||
allOf(NotInMovedClass, anyOf(isStaticStorageClass(), InAnonymousNS));
|
||||
auto DefinitionInOldCC = allOf(isDefinition(), unless(InMovedClass), InOldCC);
|
||||
auto IsOldCCHelperDefinition =
|
||||
allOf(DefinitionInOldCC, anyOf(isStaticStorageClass(), InAnonymousNS));
|
||||
// Match helper classes separately with helper functions/variables since we
|
||||
// want to reuse these matchers in finding helpers usage below.
|
||||
//
|
||||
// There could be forward declarations usage for helpers, especially for
|
||||
// classes and functions. We need include these forward declarations.
|
||||
//
|
||||
// Forward declarations for variable helpers will be excluded as these
|
||||
// declarations (with "extern") are not supposed in cpp file.
|
||||
auto HelperFuncOrVar =
|
||||
namedDecl(notInMacro(), anyOf(functionDecl(IsOldCCHelper),
|
||||
varDecl(isDefinition(), IsOldCCHelper)));
|
||||
auto HelperClasses =
|
||||
cxxRecordDecl(notInMacro(), NotInMovedClass, InAnonymousNS);
|
||||
auto HelperFuncOrVar = namedDecl(anyOf(functionDecl(IsOldCCHelperDefinition),
|
||||
varDecl(IsOldCCHelperDefinition)));
|
||||
auto HelperClasses = cxxRecordDecl(DefinitionInOldCC, InAnonymousNS);
|
||||
// Save all helper declarations in old.cc.
|
||||
Finder->addMatcher(
|
||||
namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
|
||||
@@ -635,11 +608,6 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
.bind("function"),
|
||||
MatchCallbacks.back().get());
|
||||
|
||||
MatchCallbacks.push_back(llvm::make_unique<VarDeclarationMatch>(this));
|
||||
Finder->addMatcher(
|
||||
varDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl).bind("var"),
|
||||
MatchCallbacks.back().get());
|
||||
|
||||
// Match enum definition in old.h. Enum helpers (which are defined in old.cc)
|
||||
// will not be moved for now no matter whether they are used or not.
|
||||
MatchCallbacks.push_back(llvm::make_unique<EnumDeclarationMatch>(this));
|
||||
@@ -671,12 +639,13 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
else
|
||||
MovedDecls.push_back(FWD);
|
||||
}
|
||||
} else if (const auto *ND =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("static_decls")) {
|
||||
MovedDecls.push_back(ND);
|
||||
} else if (const auto *ND =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
|
||||
MovedDecls.push_back(ND);
|
||||
HelperDeclarations.push_back(ND);
|
||||
DEBUG(llvm::dbgs() << "Add helper : "
|
||||
<< ND->getNameAsString() << " (" << ND << ")\n");
|
||||
} else if (const auto *UD =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
|
||||
MovedDecls.push_back(UD);
|
||||
@@ -730,12 +699,9 @@ void ClangMoveTool::removeDeclsInOldFiles() {
|
||||
// We remove the helper declarations which are not used in the old.cc after
|
||||
// moving the given declarations.
|
||||
for (const auto *D : HelperDeclarations) {
|
||||
DEBUG(llvm::dbgs() << "Check helper is used: "
|
||||
<< D->getNameAsString() << " (" << D << ")\n");
|
||||
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
|
||||
D->getCanonicalDecl()))) {
|
||||
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D))) {
|
||||
DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
|
||||
<< D->getNameAsString() << " (" << D << ")\n");
|
||||
<< D->getNameAsString() << " " << D << "\n");
|
||||
RemovedDecls.push_back(D);
|
||||
}
|
||||
}
|
||||
@@ -776,13 +742,10 @@ void ClangMoveTool::removeDeclsInOldFiles() {
|
||||
// Ignore replacements for new.h/cc.
|
||||
if (SI == FilePathToFileID.end()) continue;
|
||||
llvm::StringRef Code = SM.getBufferData(SI->second);
|
||||
auto Style = format::getStyle("file", FilePath, Context->FallbackStyle);
|
||||
if (!Style) {
|
||||
llvm::errs() << llvm::toString(Style.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
format::FormatStyle Style =
|
||||
format::getStyle("file", FilePath, Context->FallbackStyle);
|
||||
auto CleanReplacements = format::cleanupAroundReplacements(
|
||||
Code, Context->FileToReplacements[FilePath], *Style);
|
||||
Code, Context->FileToReplacements[FilePath], Style);
|
||||
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
@@ -811,8 +774,7 @@ void ClangMoveTool::moveDeclsToNewFiles() {
|
||||
// given symbols being moved.
|
||||
for (const auto *D : NewCCDecls) {
|
||||
if (llvm::is_contained(HelperDeclarations, D) &&
|
||||
!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
|
||||
D->getCanonicalDecl())))
|
||||
!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D)))
|
||||
continue;
|
||||
|
||||
DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
|
||||
@@ -874,20 +836,11 @@ void ClangMoveTool::onEndOfTranslationUnit() {
|
||||
for (const auto *Decl : UnremovedDeclsInOldHeader) {
|
||||
auto Kind = Decl->getKind();
|
||||
const std::string QualifiedName = Decl->getQualifiedNameAsString();
|
||||
if (Kind == Decl::Kind::Var)
|
||||
Reporter->reportDeclaration(QualifiedName, "Variable");
|
||||
else if (Kind == Decl::Kind::Function ||
|
||||
Kind == Decl::Kind::FunctionTemplate)
|
||||
if (Kind == Decl::Kind::Function || Kind == Decl::Kind::FunctionTemplate)
|
||||
Reporter->reportDeclaration(QualifiedName, "Function");
|
||||
else if (Kind == Decl::Kind::ClassTemplate ||
|
||||
Kind == Decl::Kind::CXXRecord)
|
||||
Reporter->reportDeclaration(QualifiedName, "Class");
|
||||
else if (Kind == Decl::Kind::Enum)
|
||||
Reporter->reportDeclaration(QualifiedName, "Enum");
|
||||
else if (Kind == Decl::Kind::Typedef ||
|
||||
Kind == Decl::Kind::TypeAlias ||
|
||||
Kind == Decl::Kind::TypeAliasTemplate)
|
||||
Reporter->reportDeclaration(QualifiedName, "TypeAlias");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -908,7 +861,6 @@ void ClangMoveTool::onEndOfTranslationUnit() {
|
||||
case Decl::Kind::Typedef:
|
||||
case Decl::Kind::TypeAlias:
|
||||
case Decl::Kind::TypeAliasTemplate:
|
||||
case Decl::Kind::Var:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -10,11 +10,8 @@
|
||||
#include "HelperDeclRefGraph.h"
|
||||
#include "ClangMove.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include <vector>
|
||||
|
||||
#define DEBUG_TYPE "clang-move"
|
||||
|
||||
namespace clang {
|
||||
namespace move {
|
||||
|
||||
@@ -116,19 +113,13 @@ void HelperDeclRGBuilder::run(
|
||||
if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
|
||||
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(DC);
|
||||
DEBUG(llvm::dbgs() << "Find helper function usage: "
|
||||
<< FuncRef->getDecl()->getNameAsString() << " ("
|
||||
<< FuncRef->getDecl() << ")\n");
|
||||
RG->addEdge(
|
||||
getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
|
||||
getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
|
||||
|
||||
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
|
||||
getOutmostClassOrFunDecl(FuncRef->getDecl()));
|
||||
} else if (const auto *UsedClass =
|
||||
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
|
||||
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(DC);
|
||||
DEBUG(llvm::dbgs() << "Find helper class usage: "
|
||||
<< UsedClass->getNameAsString() << " (" << UsedClass
|
||||
<< ")\n");
|
||||
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +127,10 @@ int main(int argc, const char **argv) {
|
||||
move::ClangMoveContext Context{Spec, Tool.getReplacements(),
|
||||
InitialDirectory.str(), Style, DumpDecls};
|
||||
move::DeclarationReporter Reporter;
|
||||
move::ClangMoveActionFactory Factory(&Context, &Reporter);
|
||||
|
||||
int CodeStatus = Tool.run(&Factory);
|
||||
auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
|
||||
&Context, &Reporter);
|
||||
|
||||
int CodeStatus = Tool.run(Factory.get());
|
||||
if (CodeStatus)
|
||||
return CodeStatus;
|
||||
|
||||
|
||||
@@ -97,10 +97,10 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
|
||||
if (R.isValid()) {
|
||||
TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
|
||||
&AST->getDiagnostics().getDiagnosticOptions());
|
||||
TD.emitDiagnostic(
|
||||
FullSourceLoc(R.getBegin(), AST->getSourceManager()),
|
||||
DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
|
||||
CharSourceRange::getTokenRange(R), None);
|
||||
TD.emitDiagnostic(R.getBegin(), DiagnosticsEngine::Note,
|
||||
"\"" + BI->first + "\" binds here",
|
||||
CharSourceRange::getTokenRange(R), None,
|
||||
&AST->getSourceManager());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
18
clang-tools-extra/clang-rename/CMakeLists.txt
Normal file
18
clang-tools-extra/clang-rename/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangRename
|
||||
USRFinder.cpp
|
||||
USRFindingAction.cpp
|
||||
USRLocFinder.cpp
|
||||
RenamingAction.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangIndex
|
||||
clangLex
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
93
clang-tools-extra/clang-rename/RenamingAction.cpp
Normal file
93
clang-tools-extra/clang-rename/RenamingAction.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//===--- tools/extra/clang-rename/RenamingAction.cpp - Clang rename tool --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Provides an action to rename every symbol at a point.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "RenamingAction.h"
|
||||
#include "USRLocFinder.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace clang {
|
||||
namespace rename {
|
||||
|
||||
class RenamingASTConsumer : public ASTConsumer {
|
||||
public:
|
||||
RenamingASTConsumer(
|
||||
const std::vector<std::string> &NewNames,
|
||||
const std::vector<std::string> &PrevNames,
|
||||
const std::vector<std::vector<std::string>> &USRList,
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces,
|
||||
bool PrintLocations)
|
||||
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
|
||||
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
|
||||
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
for (unsigned I = 0; I < NewNames.size(); ++I)
|
||||
HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
|
||||
}
|
||||
|
||||
void HandleOneRename(ASTContext &Context, const std::string &NewName,
|
||||
const std::string &PrevName,
|
||||
const std::vector<std::string> &USRs) {
|
||||
const SourceManager &SourceMgr = Context.getSourceManager();
|
||||
std::vector<SourceLocation> RenamingCandidates;
|
||||
std::vector<SourceLocation> NewCandidates;
|
||||
|
||||
NewCandidates =
|
||||
getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl());
|
||||
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
|
||||
NewCandidates.end());
|
||||
|
||||
unsigned PrevNameLen = PrevName.length();
|
||||
for (const auto &Loc : RenamingCandidates) {
|
||||
if (PrintLocations) {
|
||||
FullSourceLoc FullLoc(Loc, SourceMgr);
|
||||
errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
|
||||
<< ":" << FullLoc.getSpellingLineNumber() << ":"
|
||||
<< FullLoc.getSpellingColumnNumber() << "\n";
|
||||
}
|
||||
// FIXME: better error handling.
|
||||
tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
|
||||
llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
|
||||
if (Err)
|
||||
llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
|
||||
<< llvm::toString(std::move(Err)) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::string> &NewNames, &PrevNames;
|
||||
const std::vector<std::vector<std::string>> &USRList;
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces;
|
||||
bool PrintLocations;
|
||||
};
|
||||
|
||||
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
|
||||
return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
|
||||
FileToReplaces, PrintLocations);
|
||||
}
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
48
clang-tools-extra/clang-rename/RenamingAction.h
Normal file
48
clang-tools-extra/clang-rename/RenamingAction.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//===--- tools/extra/clang-rename/RenamingAction.h - Clang rename tool ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Provides an action to rename every symbol at a point.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
namespace clang {
|
||||
class ASTConsumer;
|
||||
class CompilerInstance;
|
||||
|
||||
namespace rename {
|
||||
|
||||
class RenamingAction {
|
||||
public:
|
||||
RenamingAction(const std::vector<std::string> &NewNames,
|
||||
const std::vector<std::string> &PrevNames,
|
||||
const std::vector<std::vector<std::string>> &USRList,
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces,
|
||||
bool PrintLocations = false)
|
||||
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
|
||||
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
|
||||
|
||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
||||
|
||||
private:
|
||||
const std::vector<std::string> &NewNames, &PrevNames;
|
||||
const std::vector<std::vector<std::string>> &USRList;
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces;
|
||||
bool PrintLocations;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
212
clang-tools-extra/clang-rename/USRFinder.cpp
Normal file
212
clang-tools-extra/clang-rename/USRFinder.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
//===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
|
||||
/// point.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "USRFinder.h"
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace clang {
|
||||
namespace rename {
|
||||
|
||||
// NamedDeclFindingASTVisitor recursively visits each AST node to find the
|
||||
// symbol underneath the cursor.
|
||||
// FIXME: move to seperate .h/.cc file if this gets too large.
|
||||
namespace {
|
||||
class NamedDeclFindingASTVisitor
|
||||
: public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
|
||||
public:
|
||||
// \brief Finds the NamedDecl at a point in the source.
|
||||
// \param Point the location in the source to search for the NamedDecl.
|
||||
explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
|
||||
const ASTContext &Context)
|
||||
: Result(nullptr), Point(Point), Context(Context) {}
|
||||
|
||||
// \brief Finds the NamedDecl for a name in the source.
|
||||
// \param Name the fully qualified name.
|
||||
explicit NamedDeclFindingASTVisitor(const std::string &Name,
|
||||
const ASTContext &Context)
|
||||
: Result(nullptr), Name(Name), Context(Context) {}
|
||||
|
||||
// Declaration visitors:
|
||||
|
||||
// \brief Checks if the point falls within the NameDecl. This covers every
|
||||
// declaration of a named entity that we may come across. Usually, just
|
||||
// checking if the point lies within the length of the name of the declaration
|
||||
// and the start location is sufficient.
|
||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
||||
return dyn_cast<CXXConversionDecl>(Decl)
|
||||
? true
|
||||
: setResult(Decl, Decl->getLocation(),
|
||||
Decl->getNameAsString().length());
|
||||
}
|
||||
|
||||
// Expression visitors:
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl();
|
||||
return setResult(Decl, Expr->getLocation(),
|
||||
Decl->getNameAsString().length());
|
||||
}
|
||||
|
||||
bool VisitMemberExpr(const MemberExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
|
||||
return setResult(Decl, Expr->getMemberLoc(),
|
||||
Decl->getNameAsString().length());
|
||||
}
|
||||
|
||||
// Other visitors:
|
||||
|
||||
bool VisitTypeLoc(const TypeLoc Loc) {
|
||||
const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
|
||||
const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
|
||||
TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
|
||||
if (const auto *TemplateTypeParm =
|
||||
dyn_cast<TemplateTypeParmType>(Loc.getType()))
|
||||
return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
|
||||
if (const auto *TemplateSpecType =
|
||||
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
|
||||
return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
|
||||
TypeBeginLoc, TypeEndLoc);
|
||||
}
|
||||
return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
|
||||
TypeEndLoc);
|
||||
}
|
||||
|
||||
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
|
||||
for (const auto *Initializer : ConstructorDecl->inits()) {
|
||||
// Ignore implicit initializers.
|
||||
if (!Initializer->isWritten())
|
||||
continue;
|
||||
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
|
||||
const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
|
||||
InitEndLoc = Lexer::getLocForEndOfToken(
|
||||
InitBeginLoc, 0, Context.getSourceManager(),
|
||||
Context.getLangOpts());
|
||||
if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Other:
|
||||
|
||||
const NamedDecl *getNamedDecl() { return Result; }
|
||||
|
||||
// \brief Determines if a namespace qualifier contains the point.
|
||||
// \returns false on success and sets Result.
|
||||
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
||||
while (NameLoc) {
|
||||
const NamespaceDecl *Decl =
|
||||
NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
||||
setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
|
||||
NameLoc = NameLoc.getPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// \brief Sets Result to Decl if the Point is within Start and End.
|
||||
// \returns false on success.
|
||||
bool setResult(const NamedDecl *Decl, SourceLocation Start,
|
||||
SourceLocation End) {
|
||||
if (!Decl)
|
||||
return true;
|
||||
if (Name.empty()) {
|
||||
// Offset is used to find the declaration.
|
||||
if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
|
||||
!End.isFileID() || !isPointWithin(Start, End))
|
||||
return true;
|
||||
} else {
|
||||
// Fully qualified name is used to find the declaration.
|
||||
if (Name != Decl->getQualifiedNameAsString())
|
||||
return true;
|
||||
}
|
||||
Result = Decl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
|
||||
// \returns false on success.
|
||||
bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
|
||||
// FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
|
||||
return Offset == 0 ||
|
||||
setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
|
||||
}
|
||||
|
||||
// \brief Determines if the Point is within Start and End.
|
||||
bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
|
||||
// FIXME: Add tests for Point == End.
|
||||
return Point == Start || Point == End ||
|
||||
(Context.getSourceManager().isBeforeInTranslationUnit(Start,
|
||||
Point) &&
|
||||
Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
|
||||
}
|
||||
|
||||
const NamedDecl *Result;
|
||||
const SourceLocation Point; // The location to find the NamedDecl.
|
||||
const std::string Name;
|
||||
const ASTContext &Context;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
|
||||
const SourceLocation Point) {
|
||||
const SourceManager &SM = Context.getSourceManager();
|
||||
NamedDeclFindingASTVisitor Visitor(Point, Context);
|
||||
|
||||
// Try to be clever about pruning down the number of top-level declarations we
|
||||
// see. If both start and end is either before or after the point we're
|
||||
// looking for the point cannot be inside of this decl. Don't even look at it.
|
||||
for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
|
||||
SourceLocation StartLoc = CurrDecl->getLocStart();
|
||||
SourceLocation EndLoc = CurrDecl->getLocEnd();
|
||||
if (StartLoc.isValid() && EndLoc.isValid() &&
|
||||
SM.isBeforeInTranslationUnit(StartLoc, Point) !=
|
||||
SM.isBeforeInTranslationUnit(EndLoc, Point))
|
||||
Visitor.TraverseDecl(CurrDecl);
|
||||
}
|
||||
|
||||
NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
|
||||
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
|
||||
Visitor.handleNestedNameSpecifierLoc(Location);
|
||||
|
||||
return Visitor.getNamedDecl();
|
||||
}
|
||||
|
||||
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
|
||||
const std::string &Name) {
|
||||
NamedDeclFindingASTVisitor Visitor(Name, Context);
|
||||
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
||||
|
||||
return Visitor.getNamedDecl();
|
||||
}
|
||||
|
||||
std::string getUSRForDecl(const Decl *Decl) {
|
||||
llvm::SmallVector<char, 128> Buff;
|
||||
|
||||
// FIXME: Add test for the nullptr case.
|
||||
if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
|
||||
return "";
|
||||
|
||||
return std::string(Buff.data(), Buff.size());
|
||||
}
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
84
clang-tools-extra/clang-rename/USRFinder.h
Normal file
84
clang-tools-extra/clang-rename/USRFinder.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//===--- tools/extra/clang-rename/USRFinder.h - Clang rename tool ---------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Methods for determining the USR of a symbol at a location in source
|
||||
/// code.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class Decl;
|
||||
class SourceLocation;
|
||||
class NamedDecl;
|
||||
|
||||
namespace rename {
|
||||
|
||||
// Given an AST context and a point, returns a NamedDecl identifying the symbol
|
||||
// at the point. Returns null if nothing is found at the point.
|
||||
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
|
||||
const SourceLocation Point);
|
||||
|
||||
// Given an AST context and a fully qualified name, returns a NamedDecl
|
||||
// identifying the symbol with a matching name. Returns null if nothing is
|
||||
// found for the name.
|
||||
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
|
||||
const std::string &Name);
|
||||
|
||||
// Converts a Decl into a USR.
|
||||
std::string getUSRForDecl(const Decl *Decl);
|
||||
|
||||
// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
|
||||
class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit NestedNameSpecifierLocFinder(ASTContext &Context)
|
||||
: Context(Context) {}
|
||||
|
||||
std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
|
||||
addMatchers();
|
||||
Finder.matchAST(Context);
|
||||
return Locations;
|
||||
}
|
||||
|
||||
private:
|
||||
void addMatchers() {
|
||||
const auto NestedNameSpecifierLocMatcher =
|
||||
nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
|
||||
Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
|
||||
}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
|
||||
"nestedNameSpecifierLoc");
|
||||
Locations.push_back(*NNS);
|
||||
}
|
||||
|
||||
ASTContext &Context;
|
||||
std::vector<NestedNameSpecifierLoc> Locations;
|
||||
MatchFinder Finder;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
@@ -1,4 +1,4 @@
|
||||
//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
|
||||
//===--- tools/extra/clang-rename/USRFindingAction.cpp - Clang rename tool ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@@ -13,7 +13,8 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
|
||||
#include "USRFindingAction.h"
|
||||
#include "USRFinder.h"
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
@@ -26,7 +27,6 @@
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -37,7 +37,7 @@
|
||||
using namespace llvm;
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
namespace rename {
|
||||
|
||||
namespace {
|
||||
// \brief NamedDeclFindingConsumer should delegate finding USRs of given Decl to
|
||||
@@ -104,10 +104,6 @@ private:
|
||||
void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
|
||||
RecordDecl = RecordDecl->getDefinition();
|
||||
|
||||
// Skip if the CXXRecordDecl doesn't have definition.
|
||||
if (!RecordDecl)
|
||||
return;
|
||||
|
||||
for (const auto *CtorDecl : RecordDecl->ctors())
|
||||
USRSet.insert(getUSRForDecl(CtorDecl));
|
||||
|
||||
@@ -145,9 +141,9 @@ public:
|
||||
ArrayRef<std::string> QualifiedNames,
|
||||
std::vector<std::string> &SpellingNames,
|
||||
std::vector<std::vector<std::string>> &USRList,
|
||||
bool Force, bool &ErrorOccurred)
|
||||
bool &ErrorOccurred)
|
||||
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
|
||||
SpellingNames(SpellingNames), USRList(USRList), Force(Force),
|
||||
SpellingNames(SpellingNames), USRList(USRList),
|
||||
ErrorOccurred(ErrorOccurred) {}
|
||||
|
||||
private:
|
||||
@@ -182,10 +178,6 @@ private:
|
||||
ErrorOccurred = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Force)
|
||||
return true;
|
||||
|
||||
unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
|
||||
DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
|
||||
Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
|
||||
@@ -222,15 +214,13 @@ private:
|
||||
ArrayRef<std::string> QualifiedNames;
|
||||
std::vector<std::string> &SpellingNames;
|
||||
std::vector<std::vector<std::string>> &USRList;
|
||||
bool Force;
|
||||
bool &ErrorOccurred;
|
||||
};
|
||||
|
||||
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
|
||||
return llvm::make_unique<NamedDeclFindingConsumer>(
|
||||
SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
|
||||
ErrorOccurred);
|
||||
SymbolOffsets, QualifiedNames, SpellingNames, USRList, ErrorOccurred);
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
53
clang-tools-extra/clang-rename/USRFindingAction.h
Normal file
53
clang-tools-extra/clang-rename/USRFindingAction.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===--- tools/extra/clang-rename/USRFindingAction.h - Clang rename tool --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Provides an action to find all relevant USRs at a point.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
class ASTConsumer;
|
||||
class CompilerInstance;
|
||||
class NamedDecl;
|
||||
|
||||
namespace rename {
|
||||
|
||||
struct USRFindingAction {
|
||||
USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
|
||||
ArrayRef<std::string> QualifiedNames)
|
||||
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
|
||||
ErrorOccurred(false) {}
|
||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
||||
|
||||
ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
|
||||
ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
|
||||
bool errorOccurred() { return ErrorOccurred; }
|
||||
|
||||
private:
|
||||
std::vector<unsigned> SymbolOffsets;
|
||||
std::vector<std::string> QualifiedNames;
|
||||
std::vector<std::string> SpellingNames;
|
||||
std::vector<std::vector<std::string>> USRList;
|
||||
bool ErrorOccurred;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
167
clang-tools-extra/clang-rename/USRLocFinder.cpp
Normal file
167
clang-tools-extra/clang-rename/USRLocFinder.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
//===--- tools/extra/clang-rename/USRLocFinder.cpp - Clang rename tool ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Mehtods for finding all instances of a USR. Our strategy is very
|
||||
/// simple; we just compare the USR at every relevant AST node with the one
|
||||
/// provided.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "USRLocFinder.h"
|
||||
#include "USRFinder.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <cstddef>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace clang {
|
||||
namespace rename {
|
||||
|
||||
namespace {
|
||||
|
||||
// \brief This visitor recursively searches for all instances of a USR in a
|
||||
// translation unit and stores them for later usage.
|
||||
class USRLocFindingASTVisitor
|
||||
: public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
|
||||
public:
|
||||
explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
|
||||
StringRef PrevName,
|
||||
const ASTContext &Context)
|
||||
: USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
|
||||
}
|
||||
|
||||
// Declaration visitors:
|
||||
|
||||
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
|
||||
for (const auto *Initializer : ConstructorDecl->inits()) {
|
||||
// Ignore implicit initializers.
|
||||
if (!Initializer->isWritten())
|
||||
continue;
|
||||
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
|
||||
if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
|
||||
LocationsFound.push_back(Initializer->getSourceLocation());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
|
||||
checkAndAddLocation(Decl->getLocation());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expression visitors:
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl();
|
||||
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
|
||||
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
|
||||
SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
|
||||
checkAndAddLocation(Location);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitMemberExpr(const MemberExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
|
||||
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
|
||||
SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
|
||||
checkAndAddLocation(Location);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Other visitors:
|
||||
|
||||
bool VisitTypeLoc(const TypeLoc Loc) {
|
||||
if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
|
||||
USRSet.end())
|
||||
checkAndAddLocation(Loc.getBeginLoc());
|
||||
if (const auto *TemplateTypeParm =
|
||||
dyn_cast<TemplateTypeParmType>(Loc.getType())) {
|
||||
if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
|
||||
USRSet.end())
|
||||
checkAndAddLocation(Loc.getBeginLoc());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-visitors:
|
||||
|
||||
// \brief Returns a list of unique locations. Duplicate or overlapping
|
||||
// locations are erroneous and should be reported!
|
||||
const std::vector<clang::SourceLocation> &getLocationsFound() const {
|
||||
return LocationsFound;
|
||||
}
|
||||
|
||||
// Namespace traversal:
|
||||
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
||||
while (NameLoc) {
|
||||
const NamespaceDecl *Decl =
|
||||
NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
||||
if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
|
||||
checkAndAddLocation(NameLoc.getLocalBeginLoc());
|
||||
NameLoc = NameLoc.getPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void checkAndAddLocation(SourceLocation Loc) {
|
||||
const SourceLocation BeginLoc = Loc;
|
||||
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
|
||||
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
|
||||
StringRef TokenName =
|
||||
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
|
||||
Context.getSourceManager(), Context.getLangOpts());
|
||||
size_t Offset = TokenName.find(PrevName);
|
||||
|
||||
// The token of the source location we find actually has the old
|
||||
// name.
|
||||
if (Offset != StringRef::npos)
|
||||
LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
|
||||
}
|
||||
|
||||
const std::set<std::string> USRSet;
|
||||
const std::string PrevName;
|
||||
std::vector<clang::SourceLocation> LocationsFound;
|
||||
const ASTContext &Context;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<SourceLocation>
|
||||
getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
|
||||
Decl *Decl) {
|
||||
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
|
||||
Visitor.TraverseDecl(Decl);
|
||||
NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
|
||||
|
||||
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
|
||||
Visitor.handleNestedNameSpecifierLoc(Location);
|
||||
|
||||
return Visitor.getLocationsFound();
|
||||
}
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
35
clang-tools-extra/clang-rename/USRLocFinder.h
Normal file
35
clang-tools-extra/clang-rename/USRLocFinder.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//===--- tools/extra/clang-rename/USRLocFinder.h - Clang rename tool ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Provides functionality for finding all instances of a USR in a given
|
||||
/// AST.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
|
||||
|
||||
#include "clang/AST/AST.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace rename {
|
||||
|
||||
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
|
||||
std::vector<SourceLocation>
|
||||
getLocationsOfUSRs(const std::vector<std::string> &USRs,
|
||||
llvm::StringRef PrevName, Decl *Decl);
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
|
||||
19
clang-tools-extra/clang-rename/tool/CMakeLists.txt
Normal file
19
clang-tools-extra/clang-rename/tool/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
add_clang_executable(clang-rename ClangRename.cpp)
|
||||
|
||||
target_link_libraries(clang-rename
|
||||
clangBasic
|
||||
clangFrontend
|
||||
clangRename
|
||||
clangRewrite
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
install(TARGETS clang-rename RUNTIME DESTINATION bin)
|
||||
|
||||
install(PROGRAMS clang-rename.py
|
||||
DESTINATION share/clang
|
||||
COMPONENT clang-rename)
|
||||
install(PROGRAMS clang-rename.el
|
||||
DESTINATION share/clang
|
||||
COMPONENT clang-rename)
|
||||
@@ -13,6 +13,8 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../RenamingAction.h"
|
||||
#include "../USRFindingAction.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
@@ -24,8 +26,6 @@
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
|
||||
#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
|
||||
#include "clang/Tooling/ReplacementsYaml.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
@@ -94,9 +95,6 @@ static cl::opt<std::string>
|
||||
static cl::opt<std::string>
|
||||
Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
|
||||
cl::Optional, cl::cat(ClangRenameOptions));
|
||||
static cl::opt<bool> Force("force",
|
||||
cl::desc("Ignore nonexistent qualified names."),
|
||||
cl::cat(ClangRenameOptions));
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
|
||||
@@ -126,13 +124,13 @@ int main(int argc, const char **argv) {
|
||||
// Check the arguments for correctness.
|
||||
if (NewNames.empty()) {
|
||||
errs() << "clang-rename: -new-name must be specified.\n\n";
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (SymbolOffsets.empty() == QualifiedNames.empty()) {
|
||||
errs() << "clang-rename: -offset and -qualified-name can't be present at "
|
||||
"the same time.\n";
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Check if NewNames is a valid identifier in C++17.
|
||||
@@ -144,7 +142,7 @@ int main(int argc, const char **argv) {
|
||||
auto NewNameTokKind = Table.get(NewName).getTokenID();
|
||||
if (!tok::isAnyIdentifier(NewNameTokKind)) {
|
||||
errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,12 +152,12 @@ int main(int argc, const char **argv) {
|
||||
<< ") must be equal to number of new names(" << NewNames.size()
|
||||
<< ").\n\n";
|
||||
cl::PrintHelpMessage();
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto Files = OP.getSourcePathList();
|
||||
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
|
||||
tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
|
||||
rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames);
|
||||
Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
|
||||
const std::vector<std::vector<std::string>> &USRList =
|
||||
FindingAction.getUSRList();
|
||||
@@ -172,18 +170,12 @@ int main(int argc, const char **argv) {
|
||||
|
||||
if (FindingAction.errorOccurred()) {
|
||||
// Diagnostics are already issued at this point.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Force && PrevNames.size() < NewNames.size()) {
|
||||
// No matching PrevName for all NewNames. Without Force this is an error
|
||||
// above already.
|
||||
return 0;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Perform the renaming.
|
||||
tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList,
|
||||
Tool.getReplacements(), PrintLocations);
|
||||
rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
|
||||
Tool.getReplacements(), PrintLocations);
|
||||
std::unique_ptr<tooling::FrontendActionFactory> Factory =
|
||||
tooling::newFrontendActionFactory(&RenameAction);
|
||||
int ExitCode;
|
||||
@@ -198,7 +190,7 @@ int main(int argc, const char **argv) {
|
||||
llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
|
||||
if (EC) {
|
||||
llvm::errs() << "Error opening output file: " << EC.message() << '\n';
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Export replacements.
|
||||
@@ -211,7 +203,7 @@ int main(int argc, const char **argv) {
|
||||
yaml::Output YAML(OS);
|
||||
YAML << TUR;
|
||||
OS.close();
|
||||
return 0;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Write every file to stdout. Right now we just barf the files without any
|
||||
@@ -235,5 +227,5 @@ int main(int argc, const char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
return ExitCode;
|
||||
exit(ExitCode);
|
||||
}
|
||||
@@ -26,19 +26,16 @@ add_clang_library(clangTidy
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
add_subdirectory(android)
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(plugin)
|
||||
add_subdirectory(boost)
|
||||
add_subdirectory(bugprone)
|
||||
add_subdirectory(cert)
|
||||
add_subdirectory(llvm)
|
||||
add_subdirectory(cppcoreguidelines)
|
||||
add_subdirectory(google)
|
||||
add_subdirectory(hicpp)
|
||||
add_subdirectory(llvm)
|
||||
add_subdirectory(misc)
|
||||
add_subdirectory(modernize)
|
||||
add_subdirectory(mpi)
|
||||
add_subdirectory(performance)
|
||||
add_subdirectory(plugin)
|
||||
add_subdirectory(readability)
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(utils)
|
||||
|
||||
@@ -89,13 +89,13 @@ private:
|
||||
|
||||
class ErrorReporter {
|
||||
public:
|
||||
ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
|
||||
ErrorReporter(bool ApplyFixes, StringRef FormatStyle)
|
||||
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
|
||||
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
|
||||
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
|
||||
DiagPrinter),
|
||||
SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
|
||||
TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
|
||||
SourceMgr(Diags, Files), ApplyFixes(ApplyFixes), TotalFixes(0),
|
||||
AppliedFixes(0), WarningsAsErrors(0), FormatStyle(FormatStyle) {
|
||||
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
|
||||
DiagPrinter->BeginSourceFile(LangOpts);
|
||||
}
|
||||
@@ -183,6 +183,7 @@ public:
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
// FIXME: Run clang-format on changes.
|
||||
if (ApplyFixes && TotalFixes > 0) {
|
||||
Rewriter Rewrite(SourceMgr, LangOpts);
|
||||
for (const auto &FileAndReplacements : FileReplacements) {
|
||||
@@ -196,29 +197,15 @@ public:
|
||||
continue;
|
||||
}
|
||||
StringRef Code = Buffer.get()->getBuffer();
|
||||
auto Style = format::getStyle(
|
||||
*Context.getOptionsForFile(File).FormatStyle, File, "none");
|
||||
if (!Style) {
|
||||
llvm::errs() << llvm::toString(Style.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
llvm::Expected<tooling::Replacements> Replacements =
|
||||
format::FormatStyle Style = format::getStyle("file", File, FormatStyle);
|
||||
llvm::Expected<Replacements> CleanReplacements =
|
||||
format::cleanupAroundReplacements(Code, FileAndReplacements.second,
|
||||
*Style);
|
||||
if (!Replacements) {
|
||||
llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
|
||||
Style);
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
if (llvm::Expected<tooling::Replacements> FormattedReplacements =
|
||||
format::formatReplacements(Code, *Replacements, *Style)) {
|
||||
Replacements = std::move(FormattedReplacements);
|
||||
if (!Replacements)
|
||||
llvm_unreachable("!Replacements");
|
||||
} else {
|
||||
llvm::errs() << llvm::toString(FormattedReplacements.takeError())
|
||||
<< ". Skipping formatting.\n";
|
||||
}
|
||||
if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
|
||||
if (!tooling::applyAllReplacements(CleanReplacements.get(), Rewrite)) {
|
||||
llvm::errs() << "Can't apply replacements for file " << File << "\n";
|
||||
}
|
||||
}
|
||||
@@ -239,7 +226,7 @@ private:
|
||||
return SourceLocation();
|
||||
|
||||
const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
|
||||
FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
|
||||
FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
|
||||
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
|
||||
}
|
||||
|
||||
@@ -256,11 +243,11 @@ private:
|
||||
DiagnosticsEngine Diags;
|
||||
SourceManager SourceMgr;
|
||||
llvm::StringMap<Replacements> FileReplacements;
|
||||
ClangTidyContext &Context;
|
||||
bool ApplyFixes;
|
||||
unsigned TotalFixes;
|
||||
unsigned AppliedFixes;
|
||||
unsigned WarningsAsErrors;
|
||||
StringRef FormatStyle;
|
||||
};
|
||||
|
||||
class ClangTidyASTConsumer : public MultiplexConsumer {
|
||||
@@ -302,7 +289,7 @@ static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
|
||||
|
||||
typedef std::vector<std::pair<std::string, bool>> CheckersList;
|
||||
|
||||
static CheckersList getCheckersControlList(ClangTidyContext &Context) {
|
||||
static CheckersList getCheckersControlList(GlobList &Filter) {
|
||||
CheckersList List;
|
||||
|
||||
const auto &RegisteredCheckers =
|
||||
@@ -310,7 +297,7 @@ static CheckersList getCheckersControlList(ClangTidyContext &Context) {
|
||||
bool AnalyzerChecksEnabled = false;
|
||||
for (StringRef CheckName : RegisteredCheckers) {
|
||||
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
|
||||
AnalyzerChecksEnabled |= Filter.contains(ClangTidyCheckName);
|
||||
}
|
||||
|
||||
if (!AnalyzerChecksEnabled)
|
||||
@@ -324,10 +311,8 @@ static CheckersList getCheckersControlList(ClangTidyContext &Context) {
|
||||
for (StringRef CheckName : RegisteredCheckers) {
|
||||
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
|
||||
if (CheckName.startswith("core") ||
|
||||
Context.isCheckEnabled(ClangTidyCheckName)) {
|
||||
if (CheckName.startswith("core") || Filter.contains(ClangTidyCheckName))
|
||||
List.emplace_back(CheckName, true);
|
||||
}
|
||||
}
|
||||
return List;
|
||||
}
|
||||
@@ -373,7 +358,8 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
|
||||
AnalyzerOptions->Config["cfg-temporary-dtors"] =
|
||||
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
|
||||
|
||||
AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
|
||||
GlobList &Filter = Context.getChecksFilter();
|
||||
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
|
||||
if (!AnalyzerOptions->CheckersControlList.empty()) {
|
||||
setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
|
||||
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
|
||||
@@ -392,12 +378,13 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
|
||||
|
||||
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
|
||||
std::vector<std::string> CheckNames;
|
||||
GlobList &Filter = Context.getChecksFilter();
|
||||
for (const auto &CheckFactory : *CheckFactories) {
|
||||
if (Context.isCheckEnabled(CheckFactory.first))
|
||||
if (Filter.contains(CheckFactory.first))
|
||||
CheckNames.push_back(CheckFactory.first);
|
||||
}
|
||||
|
||||
for (const auto &AnalyzerCheck : getCheckersControlList(Context))
|
||||
for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
|
||||
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
|
||||
|
||||
std::sort(CheckNames.begin(), CheckNames.end());
|
||||
@@ -472,10 +459,13 @@ ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
|
||||
return Factory.getCheckOptions();
|
||||
}
|
||||
|
||||
void runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
const CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles, ProfileData *Profile) {
|
||||
ClangTidyStats
|
||||
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
const CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
|
||||
ClangTool Tool(Compilations, InputFiles);
|
||||
clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
|
||||
|
||||
// Add extra arguments passed by the clang-tidy command-line.
|
||||
ArgumentsAdjuster PerFileExtraArgumentsInserter =
|
||||
@@ -497,7 +487,7 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
|
||||
// Remove plugins arguments.
|
||||
ArgumentsAdjuster PluginArgumentsRemover =
|
||||
[](const CommandLineArguments &Args, StringRef Filename) {
|
||||
[&Context](const CommandLineArguments &Args, StringRef Filename) {
|
||||
CommandLineArguments AdjustedArgs;
|
||||
for (size_t I = 0, E = Args.size(); I < E; ++I) {
|
||||
if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
|
||||
@@ -543,18 +533,20 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
|
||||
ActionFactory Factory(Context);
|
||||
Tool.run(&Factory);
|
||||
*Errors = Context.getErrors();
|
||||
return Context.getStats();
|
||||
}
|
||||
|
||||
void handleErrors(ClangTidyContext &Context, bool Fix,
|
||||
unsigned &WarningsAsErrorsCount) {
|
||||
ErrorReporter Reporter(Context, Fix);
|
||||
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
|
||||
StringRef FormatStyle, unsigned &WarningsAsErrorsCount) {
|
||||
ErrorReporter Reporter(Fix, FormatStyle);
|
||||
vfs::FileSystem &FileSystem =
|
||||
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
|
||||
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
|
||||
if (!InitialWorkingDir)
|
||||
llvm::report_fatal_error("Cannot get current working path.");
|
||||
|
||||
for (const ClangTidyError &Error : Context.getErrors()) {
|
||||
for (const ClangTidyError &Error : Errors) {
|
||||
if (!Error.BuildDirectory.empty()) {
|
||||
// By default, the working directory of file system is the current
|
||||
// clang-tidy running directory.
|
||||
|
||||
@@ -224,10 +224,12 @@ ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
|
||||
///
|
||||
/// \param Profile if provided, it enables check profile collection in
|
||||
/// MatchFinder, and will contain the result of the profile.
|
||||
void runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
ProfileData *Profile = nullptr);
|
||||
ClangTidyStats
|
||||
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors,
|
||||
ProfileData *Profile = nullptr);
|
||||
|
||||
// FIXME: This interface will need to be significantly extended to be useful.
|
||||
// FIXME: Implement confidence levels for displaying/fixing errors.
|
||||
@@ -235,8 +237,8 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
|
||||
/// Errors containing fixes are automatically applied and reformatted. If no
|
||||
/// clang-format configuration file is found, the given \P FormatStyle is used.
|
||||
void handleErrors(ClangTidyContext &Context, bool Fix,
|
||||
unsigned &WarningsAsErrorsCount);
|
||||
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
|
||||
StringRef FormatStyle, unsigned &WarningsAsErrorsCount);
|
||||
|
||||
/// \brief Serializes replacements into YAML and writes them to the specified
|
||||
/// output stream.
|
||||
|
||||
@@ -36,9 +36,10 @@ public:
|
||||
: DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
|
||||
|
||||
protected:
|
||||
void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
|
||||
DiagnosticsEngine::Level Level, StringRef Message,
|
||||
ArrayRef<CharSourceRange> Ranges,
|
||||
const SourceManager *SM,
|
||||
DiagOrStoredDiag Info) override {
|
||||
// Remove check name from the message.
|
||||
// FIXME: Remove this once there's a better way to pass check names than
|
||||
@@ -48,10 +49,9 @@ protected:
|
||||
if (Message.endswith(CheckNameInMessage))
|
||||
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
|
||||
|
||||
auto TidyMessage =
|
||||
Loc.isValid()
|
||||
? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
|
||||
: tooling::DiagnosticMessage(Message);
|
||||
auto TidyMessage = Loc.isValid()
|
||||
? tooling::DiagnosticMessage(Message, *SM, Loc)
|
||||
: tooling::DiagnosticMessage(Message);
|
||||
if (Level == DiagnosticsEngine::Note) {
|
||||
Error.Notes.push_back(TidyMessage);
|
||||
return;
|
||||
@@ -60,13 +60,15 @@ protected:
|
||||
Error.Message = TidyMessage;
|
||||
}
|
||||
|
||||
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
|
||||
DiagnosticsEngine::Level Level,
|
||||
ArrayRef<CharSourceRange> Ranges) override {}
|
||||
ArrayRef<CharSourceRange> Ranges,
|
||||
const SourceManager &SM) override {}
|
||||
|
||||
void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
|
||||
void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,
|
||||
SmallVectorImpl<CharSourceRange> &Ranges,
|
||||
ArrayRef<FixItHint> Hints) override {
|
||||
ArrayRef<FixItHint> Hints,
|
||||
const SourceManager &SM) override {
|
||||
assert(Loc.isValid());
|
||||
for (const auto &FixIt : Hints) {
|
||||
CharSourceRange Range = FixIt.RemoveRange;
|
||||
@@ -75,8 +77,7 @@ protected:
|
||||
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
|
||||
"Only file locations supported in fix-it hints.");
|
||||
|
||||
tooling::Replacement Replacement(Loc.getManager(), Range,
|
||||
FixIt.CodeToInsert);
|
||||
tooling::Replacement Replacement(SM, Range, FixIt.CodeToInsert);
|
||||
llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
|
||||
// FIXME: better error handling (at least, don't let other replacements be
|
||||
// applied).
|
||||
@@ -88,13 +89,16 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
|
||||
void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
|
||||
const SourceManager &SM) override {}
|
||||
|
||||
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName) override {}
|
||||
void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName,
|
||||
const SourceManager &SM) override {}
|
||||
|
||||
void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName) override {}
|
||||
void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName,
|
||||
const SourceManager &SM) override {}
|
||||
|
||||
void endDiagnostic(DiagOrStoredDiag D,
|
||||
DiagnosticsEngine::Level Level) override {
|
||||
@@ -115,7 +119,6 @@ ClangTidyError::ClangTidyError(StringRef CheckName,
|
||||
// Returns true if GlobList starts with the negative indicator ('-'), removes it
|
||||
// from the GlobList.
|
||||
static bool ConsumeNegativeIndicator(StringRef &GlobList) {
|
||||
GlobList = GlobList.trim(' ');
|
||||
if (GlobList.startswith("-")) {
|
||||
GlobList = GlobList.substr(1);
|
||||
return true;
|
||||
@@ -125,9 +128,8 @@ static bool ConsumeNegativeIndicator(StringRef &GlobList) {
|
||||
// Converts first glob from the comma-separated list of globs to Regex and
|
||||
// removes it and the trailing comma from the GlobList.
|
||||
static llvm::Regex ConsumeGlob(StringRef &GlobList) {
|
||||
StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
|
||||
StringRef Glob = UntrimmedGlob.trim(' ');
|
||||
GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
|
||||
StringRef Glob = GlobList.substr(0, GlobList.find(',')).trim();
|
||||
GlobList = GlobList.substr(Glob.size() + 1);
|
||||
SmallString<128> RegexText("^");
|
||||
StringRef MetaChars("()^$|*+?.[]\\{}");
|
||||
for (char C : Glob) {
|
||||
@@ -154,27 +156,6 @@ bool GlobList::contains(StringRef S, bool Contains) {
|
||||
return Contains;
|
||||
}
|
||||
|
||||
class ClangTidyContext::CachedGlobList {
|
||||
public:
|
||||
CachedGlobList(StringRef Globs) : Globs(Globs) {}
|
||||
|
||||
bool contains(StringRef S) {
|
||||
switch (auto &Result = Cache[S]) {
|
||||
case Yes: return true;
|
||||
case No: return false;
|
||||
case None:
|
||||
Result = Globs.contains(S) ? Yes : No;
|
||||
return Result == Yes;
|
||||
}
|
||||
llvm_unreachable("invalid enum");
|
||||
}
|
||||
|
||||
private:
|
||||
GlobList Globs;
|
||||
enum Tristate { None, Yes, No };
|
||||
llvm::StringMap<Tristate> Cache;
|
||||
};
|
||||
|
||||
ClangTidyContext::ClangTidyContext(
|
||||
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
|
||||
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
|
||||
@@ -184,8 +165,6 @@ ClangTidyContext::ClangTidyContext(
|
||||
setCurrentFile("");
|
||||
}
|
||||
|
||||
ClangTidyContext::~ClangTidyContext() = default;
|
||||
|
||||
DiagnosticBuilder ClangTidyContext::diag(
|
||||
StringRef CheckName, SourceLocation Loc, StringRef Description,
|
||||
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
|
||||
@@ -207,9 +186,8 @@ void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
|
||||
void ClangTidyContext::setCurrentFile(StringRef File) {
|
||||
CurrentFile = File;
|
||||
CurrentOptions = getOptionsForFile(CurrentFile);
|
||||
CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
|
||||
WarningAsErrorFilter =
|
||||
llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
|
||||
CheckFilter.reset(new GlobList(*getOptions().Checks));
|
||||
WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors));
|
||||
}
|
||||
|
||||
void ClangTidyContext::setASTContext(ASTContext *Context) {
|
||||
@@ -234,14 +212,14 @@ ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
|
||||
|
||||
void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
|
||||
|
||||
bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
|
||||
GlobList &ClangTidyContext::getChecksFilter() {
|
||||
assert(CheckFilter != nullptr);
|
||||
return CheckFilter->contains(CheckName);
|
||||
return *CheckFilter;
|
||||
}
|
||||
|
||||
bool ClangTidyContext::treatAsError(StringRef CheckName) const {
|
||||
GlobList &ClangTidyContext::getWarningAsErrorFilter() {
|
||||
assert(WarningAsErrorFilter != nullptr);
|
||||
return WarningAsErrorFilter->contains(CheckName);
|
||||
return *WarningAsErrorFilter;
|
||||
}
|
||||
|
||||
/// \brief Store a \c ClangTidyError.
|
||||
@@ -257,22 +235,20 @@ StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
|
||||
ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
|
||||
: Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
|
||||
LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
|
||||
LastErrorWasIgnored(false) {
|
||||
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
|
||||
: Context(Ctx), LastErrorRelatesToUserCode(false),
|
||||
LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
Diags = llvm::make_unique<DiagnosticsEngine>(
|
||||
Diags.reset(new DiagnosticsEngine(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
|
||||
/*ShouldOwnClient=*/false);
|
||||
/*ShouldOwnClient=*/false));
|
||||
Context.setDiagnosticsEngine(Diags.get());
|
||||
}
|
||||
|
||||
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
||||
if (!Errors.empty()) {
|
||||
ClangTidyError &Error = Errors.back();
|
||||
if (!Context.isCheckEnabled(Error.DiagnosticName) &&
|
||||
if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
|
||||
Error.DiagLevel != ClangTidyError::Error) {
|
||||
++Context.Stats.ErrorsIgnoredCheckFilter;
|
||||
Errors.pop_back();
|
||||
@@ -293,45 +269,16 @@ void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
||||
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
|
||||
bool Invalid;
|
||||
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
|
||||
if (Invalid)
|
||||
return false;
|
||||
|
||||
// Check if there's a NOLINT on this line.
|
||||
const char *P = CharacterData;
|
||||
while (*P != '\0' && *P != '\r' && *P != '\n')
|
||||
++P;
|
||||
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
|
||||
// FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
|
||||
if (RestOfLine.find("NOLINT") != StringRef::npos)
|
||||
return true;
|
||||
|
||||
// Check if there's a NOLINTNEXTLINE on the previous line.
|
||||
const char *BufBegin =
|
||||
SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
|
||||
if (Invalid || P == BufBegin)
|
||||
return false;
|
||||
|
||||
// Scan backwards over the current line.
|
||||
P = CharacterData;
|
||||
while (P != BufBegin && *P != '\n')
|
||||
--P;
|
||||
|
||||
// If we reached the begin of the file there is no line before it.
|
||||
if (P == BufBegin)
|
||||
return false;
|
||||
|
||||
// Skip over the newline.
|
||||
--P;
|
||||
const char *LineEnd = P;
|
||||
|
||||
// Now we're on the previous line. Skip to the beginning of it.
|
||||
while (P != BufBegin && *P != '\n')
|
||||
--P;
|
||||
|
||||
RestOfLine = StringRef(P, LineEnd - P + 1);
|
||||
if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)
|
||||
return true;
|
||||
|
||||
if (!Invalid) {
|
||||
const char *P = CharacterData;
|
||||
while (*P != '\0' && *P != '\r' && *P != '\n')
|
||||
++P;
|
||||
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
|
||||
// FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
|
||||
if (RestOfLine.find("NOLINT") != StringRef::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -404,8 +351,9 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
||||
LastErrorRelatesToUserCode = true;
|
||||
LastErrorPassesLineFilter = true;
|
||||
}
|
||||
bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
|
||||
Context.treatAsError(CheckName);
|
||||
bool IsWarningAsError =
|
||||
DiagLevel == DiagnosticsEngine::Warning &&
|
||||
Context.getWarningAsErrorFilter().contains(CheckName);
|
||||
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
|
||||
IsWarningAsError);
|
||||
}
|
||||
@@ -415,12 +363,11 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
||||
Errors.back());
|
||||
SmallString<100> Message;
|
||||
Info.FormatDiagnostic(Message);
|
||||
FullSourceLoc Loc =
|
||||
(Info.getLocation().isInvalid())
|
||||
? FullSourceLoc()
|
||||
: FullSourceLoc(Info.getLocation(), Info.getSourceManager());
|
||||
Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
|
||||
Info.getFixItHints());
|
||||
SourceManager *Sources = nullptr;
|
||||
if (Info.hasSourceManager())
|
||||
Sources = &Info.getSourceManager();
|
||||
Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
|
||||
Info.getRanges(), Info.getFixItHints(), Sources);
|
||||
|
||||
checkFilters(Info.getLocation());
|
||||
}
|
||||
@@ -482,8 +429,8 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
|
||||
|
||||
llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
|
||||
if (!HeaderFilter)
|
||||
HeaderFilter =
|
||||
llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
|
||||
HeaderFilter.reset(
|
||||
new llvm::Regex(*Context.getOptions().HeaderFilterRegex));
|
||||
return HeaderFilter.get();
|
||||
}
|
||||
|
||||
@@ -633,9 +580,7 @@ void ClangTidyDiagnosticConsumer::finish() {
|
||||
std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
|
||||
Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
|
||||
Errors.end());
|
||||
|
||||
if (RemoveIncompatibleErrors)
|
||||
removeIncompatibleErrors(Errors);
|
||||
removeIncompatibleErrors(Errors);
|
||||
|
||||
for (const ClangTidyError &Error : Errors)
|
||||
Context.storeError(Error);
|
||||
|
||||
@@ -106,8 +106,6 @@ public:
|
||||
/// \brief Initializes \c ClangTidyContext instance.
|
||||
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
|
||||
|
||||
~ClangTidyContext();
|
||||
|
||||
/// \brief Report any errors detected using this method.
|
||||
///
|
||||
/// This is still under heavy development and will likely change towards using
|
||||
@@ -138,14 +136,14 @@ public:
|
||||
/// diagnostic ID.
|
||||
StringRef getCheckName(unsigned DiagnosticID) const;
|
||||
|
||||
/// \brief Returns \c true if the check is enabled for the \c CurrentFile.
|
||||
/// \brief Returns check filter for the \c CurrentFile.
|
||||
///
|
||||
/// The \c CurrentFile can be changed using \c setCurrentFile.
|
||||
bool isCheckEnabled(StringRef CheckName) const;
|
||||
GlobList &getChecksFilter();
|
||||
|
||||
/// \brief Returns \c true if the check should be upgraded to error for the
|
||||
/// \c CurrentFile.
|
||||
bool treatAsError(StringRef CheckName) const;
|
||||
/// \brief Returns check filter for the \c CurrentFile which
|
||||
/// selects checks for upgrade to error.
|
||||
GlobList &getWarningAsErrorFilter();
|
||||
|
||||
/// \brief Returns global options.
|
||||
const ClangTidyGlobalOptions &getGlobalOptions() const;
|
||||
@@ -164,7 +162,7 @@ public:
|
||||
const ClangTidyStats &getStats() const { return Stats; }
|
||||
|
||||
/// \brief Returns all collected errors.
|
||||
ArrayRef<ClangTidyError> getErrors() const { return Errors; }
|
||||
const std::vector<ClangTidyError> &getErrors() const { return Errors; }
|
||||
|
||||
/// \brief Clears collected errors.
|
||||
void clearErrors() { Errors.clear(); }
|
||||
@@ -204,9 +202,8 @@ private:
|
||||
|
||||
std::string CurrentFile;
|
||||
ClangTidyOptions CurrentOptions;
|
||||
class CachedGlobList;
|
||||
std::unique_ptr<CachedGlobList> CheckFilter;
|
||||
std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
|
||||
std::unique_ptr<GlobList> CheckFilter;
|
||||
std::unique_ptr<GlobList> WarningAsErrorFilter;
|
||||
|
||||
LangOptions LangOpts;
|
||||
|
||||
@@ -226,8 +223,7 @@ private:
|
||||
// implementation file.
|
||||
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
|
||||
public:
|
||||
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
|
||||
bool RemoveIncompatibleErrors = true);
|
||||
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx);
|
||||
|
||||
// FIXME: The concept of converting between FixItHints and Replacements is
|
||||
// more generic and should be pulled out into a more useful Diagnostics
|
||||
@@ -253,7 +249,6 @@ private:
|
||||
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
|
||||
|
||||
ClangTidyContext &Context;
|
||||
bool RemoveIncompatibleErrors;
|
||||
std::unique_ptr<DiagnosticsEngine> Diags;
|
||||
SmallVector<ClangTidyError, 8> Errors;
|
||||
std::unique_ptr<llvm::Regex> HeaderFilter;
|
||||
|
||||
@@ -24,8 +24,9 @@ void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
|
||||
void ClangTidyCheckFactories::createChecks(
|
||||
ClangTidyContext *Context,
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
|
||||
GlobList &Filter = Context->getChecksFilter();
|
||||
for (const auto &Factory : Factories) {
|
||||
if (Context->isCheckEnabled(Factory.first))
|
||||
if (Filter.contains(Factory.first))
|
||||
Checks.emplace_back(Factory.second(Factory.first, Context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
|
||||
|
||||
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
|
||||
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
@@ -87,7 +89,6 @@ template <> struct MappingTraits<ClangTidyOptions> {
|
||||
IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
|
||||
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
|
||||
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
|
||||
IO.mapOptional("FormatStyle", Options.FormatStyle);
|
||||
IO.mapOptional("User", Options.User);
|
||||
IO.mapOptional("CheckOptions", NOpts->Options);
|
||||
IO.mapOptional("ExtraArgs", Options.ExtraArgs);
|
||||
@@ -108,7 +109,6 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
|
||||
Options.HeaderFilterRegex = "";
|
||||
Options.SystemHeaders = false;
|
||||
Options.AnalyzeTemporaryDtors = false;
|
||||
Options.FormatStyle = "none";
|
||||
Options.User = llvm::None;
|
||||
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
|
||||
E = ClangTidyModuleRegistry::end();
|
||||
@@ -148,7 +148,6 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
|
||||
overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
|
||||
overrideValue(Result.SystemHeaders, Other.SystemHeaders);
|
||||
overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
|
||||
overrideValue(Result.FormatStyle, Other.FormatStyle);
|
||||
overrideValue(Result.User, Other.User);
|
||||
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
|
||||
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
|
||||
|
||||
@@ -75,20 +75,6 @@ struct ClangTidyOptions {
|
||||
/// \brief Turns on temporary destructor-based analysis.
|
||||
llvm::Optional<bool> AnalyzeTemporaryDtors;
|
||||
|
||||
/// \brief Format code around applied fixes with clang-format using this
|
||||
/// style.
|
||||
///
|
||||
/// Can be one of:
|
||||
/// * 'none' - don't format code around applied fixes;
|
||||
/// * 'llvm', 'google', 'mozilla' or other predefined clang-format style
|
||||
/// names;
|
||||
/// * 'file' - use the .clang-format file in the closest parent directory of
|
||||
/// each source file;
|
||||
/// * '{inline-formatting-style-in-yaml-format}'.
|
||||
///
|
||||
/// See clang-format documentation for more about configuring format style.
|
||||
llvm::Optional<std::string> FormatStyle;
|
||||
|
||||
/// \brief Specifies the name or e-mail of the user running clang-tidy.
|
||||
///
|
||||
/// This option is used, for example, to place the correct user name in TODO()
|
||||
|
||||
@@ -189,37 +189,6 @@ def adapt_module(module_path, module, check_name, check_name_camel):
|
||||
f.write(line)
|
||||
|
||||
|
||||
# Adds a release notes entry.
|
||||
def add_release_notes(module_path, module, check_name):
|
||||
check_name_dashes = module + '-' + check_name
|
||||
filename = os.path.normpath(os.path.join(module_path,
|
||||
'../../docs/ReleaseNotes.rst'))
|
||||
with open(filename, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
print('Updating %s...' % filename)
|
||||
with open(filename, 'wb') as f:
|
||||
note_added = False
|
||||
header_found = False
|
||||
|
||||
for line in lines:
|
||||
if not note_added:
|
||||
match = re.search('Improvements to clang-tidy', line)
|
||||
if match:
|
||||
header_found = True
|
||||
elif header_found:
|
||||
if not line.startswith('----'):
|
||||
f.write("""
|
||||
- New `%s
|
||||
<http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_ check
|
||||
|
||||
FIXME: add release notes.
|
||||
""" % (check_name_dashes, check_name_dashes))
|
||||
note_added = True
|
||||
|
||||
f.write(line)
|
||||
|
||||
|
||||
# Adds a test for the check.
|
||||
def write_test(module_path, module, check_name):
|
||||
check_name_dashes = module + '-' + check_name
|
||||
@@ -331,7 +300,6 @@ documentation files."""
|
||||
write_header(module_path, module, check_name, check_name_camel)
|
||||
write_implementation(module_path, module, check_name_camel)
|
||||
adapt_module(module_path, module, check_name, check_name_camel)
|
||||
add_release_notes(module_path, module, check_name)
|
||||
write_test(module_path, module, check_name)
|
||||
write_docs(module_path, module, check_name)
|
||||
update_checks_list(clang_tidy_path)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
//===--- AndroidTidyModule.cpp - clang-tidy--------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "CloexecCreatCheck.h"
|
||||
#include "CloexecFopenCheck.h"
|
||||
#include "CloexecOpenCheck.h"
|
||||
#include "CloexecSocketCheck.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
/// This module is for Android specific checks.
|
||||
class AndroidModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<CloexecCreatCheck>("android-cloexec-creat");
|
||||
CheckFactories.registerCheck<CloexecFopenCheck>("android-cloexec-fopen");
|
||||
CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
|
||||
CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
|
||||
}
|
||||
};
|
||||
|
||||
// Register the AndroidTidyModule using this statically initialized variable.
|
||||
static ClangTidyModuleRegistry::Add<AndroidModule>
|
||||
X("android-module", "Adds Android platform checks.");
|
||||
|
||||
} // namespace android
|
||||
|
||||
// This anchor is used to force the linker to link in the generated object file
|
||||
// and thus register the AndroidModule.
|
||||
volatile int AndroidModuleAnchorSource = 0;
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,17 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyAndroidModule
|
||||
AndroidTidyModule.cpp
|
||||
CloexecCreatCheck.cpp
|
||||
CloexecFopenCheck.cpp
|
||||
CloexecOpenCheck.cpp
|
||||
CloexecSocketCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangLex
|
||||
clangTidy
|
||||
clangTidyUtils
|
||||
)
|
||||
@@ -1,59 +0,0 @@
|
||||
//===--- CloexecCreatCheck.cpp - clang-tidy--------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CloexecCreatCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
void CloexecCreatCheck::registerMatchers(MatchFinder *Finder) {
|
||||
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
|
||||
auto MODETType = hasType(namedDecl(hasName("mode_t")));
|
||||
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
|
||||
hasName("creat"),
|
||||
hasParameter(0, CharPointerType),
|
||||
hasParameter(1, MODETType))
|
||||
.bind("funcDecl")))
|
||||
.bind("creatFn"),
|
||||
this);
|
||||
}
|
||||
|
||||
void CloexecCreatCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("creatFn");
|
||||
const SourceManager &SM = *Result.SourceManager;
|
||||
|
||||
const std::string &ReplacementText =
|
||||
(Twine("open (") +
|
||||
Lexer::getSourceText(CharSourceRange::getTokenRange(
|
||||
MatchedCall->getArg(0)->getSourceRange()),
|
||||
SM, Result.Context->getLangOpts()) +
|
||||
", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, " +
|
||||
Lexer::getSourceText(CharSourceRange::getTokenRange(
|
||||
MatchedCall->getArg(1)->getSourceRange()),
|
||||
SM, Result.Context->getLangOpts()) +
|
||||
")")
|
||||
.str();
|
||||
|
||||
diag(MatchedCall->getLocStart(),
|
||||
"prefer open() to creat() because open() allows O_CLOEXEC")
|
||||
<< FixItHint::CreateReplacement(MatchedCall->getSourceRange(),
|
||||
ReplacementText);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,35 +0,0 @@
|
||||
//===--- CloexecCreatCheck.h - clang-tidy------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
/// creat() is better to be replaced by open().
|
||||
/// Find the usage of creat() and redirect user to use open().
|
||||
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html
|
||||
class CloexecCreatCheck : public ClangTidyCheck {
|
||||
public:
|
||||
CloexecCreatCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
|
||||
@@ -1,74 +0,0 @@
|
||||
//===--- CloexecFopenCheck.cpp - clang-tidy--------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details. //
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CloexecFopenCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
namespace {
|
||||
static const char MODE = 'e';
|
||||
|
||||
// Build the replace text. If it's string constant, add 'e' directly in the end
|
||||
// of the string. Else, add "e".
|
||||
std::string BuildReplaceText(const Expr *Arg, const SourceManager &SM,
|
||||
const LangOptions &LangOpts) {
|
||||
if (Arg->getLocStart().isMacroID())
|
||||
return (Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
|
||||
LangOpts) +
|
||||
" \"" + Twine(MODE) + "\"")
|
||||
.str();
|
||||
|
||||
StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
|
||||
return ("\"" + SR + Twine(MODE) + "\"").str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void CloexecFopenCheck::registerMatchers(MatchFinder *Finder) {
|
||||
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
|
||||
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(isExternC(), returns(asString("FILE *")),
|
||||
hasName("fopen"),
|
||||
hasParameter(0, CharPointerType),
|
||||
hasParameter(1, CharPointerType))
|
||||
.bind("funcDecl")))
|
||||
.bind("fopenFn"),
|
||||
this);
|
||||
}
|
||||
|
||||
void CloexecFopenCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("fopenFn");
|
||||
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
|
||||
const Expr *ModeArg = MatchedCall->getArg(1);
|
||||
|
||||
// Check if the 'e' may be in the mode string.
|
||||
const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
|
||||
if (!ModeStr || (ModeStr->getString().find(MODE) != StringRef::npos))
|
||||
return;
|
||||
|
||||
const std::string &ReplacementText = BuildReplaceText(
|
||||
ModeArg, *Result.SourceManager, Result.Context->getLangOpts());
|
||||
|
||||
diag(ModeArg->getLocStart(), "use %0 mode 'e' to set O_CLOEXEC")
|
||||
<< FD
|
||||
<< FixItHint::CreateReplacement(ModeArg->getSourceRange(),
|
||||
ReplacementText);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,38 +0,0 @@
|
||||
//===--- CloexecFopenCheck.h - clang-tidy------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source //
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
/// fopen() is suggested to include "e" in their mode string; like "re" would be
|
||||
/// better than "r".
|
||||
///
|
||||
/// This check only works when corresponding argument is StringLiteral. No
|
||||
/// constant propagation.
|
||||
///
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html
|
||||
class CloexecFopenCheck : public ClangTidyCheck {
|
||||
public:
|
||||
CloexecFopenCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
|
||||
@@ -1,74 +0,0 @@
|
||||
//===--- CloexecOpenCheck.cpp - clang-tidy---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CloexecOpenCheck.h"
|
||||
#include "../utils/ASTUtils.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
static constexpr const char *O_CLOEXEC = "O_CLOEXEC";
|
||||
|
||||
void CloexecOpenCheck::registerMatchers(MatchFinder *Finder) {
|
||||
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
|
||||
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
|
||||
hasAnyName("open", "open64"),
|
||||
hasParameter(0, CharPointerType),
|
||||
hasParameter(1, hasType(isInteger())))
|
||||
.bind("funcDecl")))
|
||||
.bind("openFn"),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
|
||||
hasName("openat"),
|
||||
hasParameter(0, hasType(isInteger())),
|
||||
hasParameter(1, CharPointerType),
|
||||
hasParameter(2, hasType(isInteger())))
|
||||
.bind("funcDecl")))
|
||||
.bind("openatFn"),
|
||||
this);
|
||||
}
|
||||
|
||||
void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const Expr *FlagArg = nullptr;
|
||||
if (const auto *OpenFnCall = Result.Nodes.getNodeAs<CallExpr>("openFn"))
|
||||
FlagArg = OpenFnCall->getArg(1);
|
||||
else if (const auto *OpenFnCall =
|
||||
Result.Nodes.getNodeAs<CallExpr>("openatFn"))
|
||||
FlagArg = OpenFnCall->getArg(2);
|
||||
assert(FlagArg);
|
||||
|
||||
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
|
||||
|
||||
// Check the required flag.
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
|
||||
Result.Context->getLangOpts(), O_CLOEXEC))
|
||||
return;
|
||||
|
||||
SourceLocation EndLoc =
|
||||
Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
|
||||
Result.Context->getLangOpts());
|
||||
|
||||
diag(EndLoc, "%0 should use %1 where possible")
|
||||
<< FD << O_CLOEXEC
|
||||
<< FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + O_CLOEXEC).str());
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,40 +0,0 @@
|
||||
//===--- CloexecOpenCheck.h - clang-tidy-----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
/// Finds code that opens file without using the O_CLOEXEC flag.
|
||||
///
|
||||
/// open(), openat(), and open64() had better to include O_CLOEXEC in their
|
||||
/// flags argument. Only consider simple cases that the corresponding argument
|
||||
/// is constant or binary operation OR among constants like 'O_CLOEXEC' or
|
||||
/// 'O_CLOEXEC | O_RDONLY'. No constant propagation is performed.
|
||||
///
|
||||
/// Only the symbolic 'O_CLOEXEC' macro definition is checked, not the concrete
|
||||
/// value.
|
||||
class CloexecOpenCheck : public ClangTidyCheck {
|
||||
public:
|
||||
CloexecOpenCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
|
||||
@@ -1,57 +0,0 @@
|
||||
//===--- CloexecSocketCheck.cpp - clang-tidy-------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CloexecSocketCheck.h"
|
||||
#include "../utils/ASTUtils.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
static constexpr const char *SOCK_CLOEXEC = "SOCK_CLOEXEC";
|
||||
|
||||
void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
|
||||
hasName("socket"),
|
||||
hasParameter(0, hasType(isInteger())),
|
||||
hasParameter(1, hasType(isInteger())),
|
||||
hasParameter(2, hasType(isInteger())))
|
||||
.bind("funcDecl")))
|
||||
.bind("socketFn"),
|
||||
this);
|
||||
}
|
||||
|
||||
void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("socketFn");
|
||||
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
|
||||
const Expr *FlagArg = MatchedCall->getArg(1);
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
|
||||
if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
|
||||
Result.Context->getLangOpts(), SOCK_CLOEXEC))
|
||||
return;
|
||||
|
||||
SourceLocation EndLoc =
|
||||
Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
|
||||
Result.Context->getLangOpts());
|
||||
|
||||
diag(EndLoc, "%0 should use %1 where possible")
|
||||
<< FD << SOCK_CLOEXEC
|
||||
<< FixItHint::CreateInsertion(EndLoc,
|
||||
(Twine(" | ") + SOCK_CLOEXEC).str());
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,35 +0,0 @@
|
||||
//===--- CloexecSocketCheck.h - clang-tidy-----------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace android {
|
||||
|
||||
/// Finds code that uses socket() without using the SOCK_CLOEXEC flag.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html
|
||||
class CloexecSocketCheck : public ClangTidyCheck {
|
||||
public:
|
||||
CloexecSocketCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
|
||||
@@ -1,41 +0,0 @@
|
||||
//===--- BugproneTidyModule.cpp - clang-tidy ------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "SuspiciousMemsetUsageCheck.h"
|
||||
#include "UndefinedMemoryManipulationCheck.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
class BugproneModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
|
||||
"bugprone-suspicious-memset-usage");
|
||||
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
|
||||
"bugprone-undefined-memory-manipulation");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bugprone
|
||||
|
||||
// Register the BugproneTidyModule using this statically initialized variable.
|
||||
static ClangTidyModuleRegistry::Add<bugprone::BugproneModule>
|
||||
X("bugprone-module", "Adds checks for bugprone code constructs.");
|
||||
|
||||
// This anchor is used to force the linker to link in the generated object file
|
||||
// and thus register the BugproneModule.
|
||||
volatile int BugproneModuleAnchorSource = 0;
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,17 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyBugproneModule
|
||||
BugproneTidyModule.cpp
|
||||
SuspiciousMemsetUsageCheck.cpp
|
||||
UndefinedMemoryManipulationCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAnalysis
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangLex
|
||||
clangTidy
|
||||
clangTidyUtils
|
||||
clangTooling
|
||||
)
|
||||
@@ -1,127 +0,0 @@
|
||||
//===--- SuspiciousMemsetUsageCheck.cpp - clang-tidy-----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SuspiciousMemsetUsageCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Tooling/FixIt.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
void SuspiciousMemsetUsageCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// Note: void *memset(void *buffer, int fill_char, size_t byte_count);
|
||||
// Look for memset(x, '0', z). Probably memset(x, 0, z) was intended.
|
||||
Finder->addMatcher(
|
||||
callExpr(
|
||||
callee(functionDecl(hasName("::memset"))),
|
||||
hasArgument(1, characterLiteral(equals(static_cast<unsigned>('0')))
|
||||
.bind("char-zero-fill")),
|
||||
unless(
|
||||
eachOf(hasArgument(0, anyOf(hasType(pointsTo(isAnyCharacter())),
|
||||
hasType(arrayType(hasElementType(
|
||||
isAnyCharacter()))))),
|
||||
isInTemplateInstantiation()))),
|
||||
this);
|
||||
|
||||
// Look for memset with an integer literal in its fill_char argument.
|
||||
// Will check if it gets truncated.
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(hasName("::memset"))),
|
||||
hasArgument(1, integerLiteral().bind("num-fill")),
|
||||
unless(isInTemplateInstantiation())),
|
||||
this);
|
||||
|
||||
// Look for memset(x, y, 0) as that is most likely an argument swap.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasName("::memset"))),
|
||||
unless(hasArgument(1, anyOf(characterLiteral(equals(
|
||||
static_cast<unsigned>('0'))),
|
||||
integerLiteral()))),
|
||||
unless(isInTemplateInstantiation()))
|
||||
.bind("call"),
|
||||
this);
|
||||
}
|
||||
|
||||
void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (const auto *CharZeroFill =
|
||||
Result.Nodes.getNodeAs<CharacterLiteral>("char-zero-fill")) {
|
||||
// Case 1: fill_char of memset() is a character '0'. Probably an
|
||||
// integer zero was intended.
|
||||
|
||||
SourceRange CharRange = CharZeroFill->getSourceRange();
|
||||
auto Diag =
|
||||
diag(CharZeroFill->getLocStart(), "memset fill value is char '0', "
|
||||
"potentially mistaken for int 0");
|
||||
|
||||
// Only suggest a fix if no macros are involved.
|
||||
if (CharRange.getBegin().isMacroID())
|
||||
return;
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
CharSourceRange::getTokenRange(CharRange), "0");
|
||||
}
|
||||
|
||||
else if (const auto *NumFill =
|
||||
Result.Nodes.getNodeAs<IntegerLiteral>("num-fill")) {
|
||||
// Case 2: fill_char of memset() is larger in size than an unsigned char
|
||||
// so it gets truncated during conversion.
|
||||
|
||||
llvm::APSInt NumValue;
|
||||
const auto UCharMax = (1 << Result.Context->getCharWidth()) - 1;
|
||||
if (!NumFill->EvaluateAsInt(NumValue, *Result.Context) ||
|
||||
(NumValue >= 0 && NumValue <= UCharMax))
|
||||
return;
|
||||
|
||||
diag(NumFill->getLocStart(), "memset fill value is out of unsigned "
|
||||
"character range, gets truncated");
|
||||
}
|
||||
|
||||
else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
|
||||
// Case 3: byte_count of memset() is zero. This is most likely an
|
||||
// argument swap.
|
||||
|
||||
const Expr *FillChar = Call->getArg(1);
|
||||
const Expr *ByteCount = Call->getArg(2);
|
||||
|
||||
// Return if `byte_count` is not zero at compile time.
|
||||
llvm::APSInt Value1, Value2;
|
||||
if (ByteCount->isValueDependent() ||
|
||||
!ByteCount->EvaluateAsInt(Value2, *Result.Context) || Value2 != 0)
|
||||
return;
|
||||
|
||||
// Return if `fill_char` is known to be zero or negative at compile
|
||||
// time. In these cases, swapping the args would be a nop, or
|
||||
// introduce a definite bug. The code is likely correct.
|
||||
if (!FillChar->isValueDependent() &&
|
||||
FillChar->EvaluateAsInt(Value1, *Result.Context) &&
|
||||
(Value1 == 0 || Value1.isNegative()))
|
||||
return;
|
||||
|
||||
// `byte_count` is known to be zero at compile time, and `fill_char` is
|
||||
// either not known or known to be a positive integer. Emit a warning
|
||||
// and fix-its to swap the arguments.
|
||||
auto D = diag(Call->getLocStart(),
|
||||
"memset of size zero, potentially swapped arguments");
|
||||
StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context);
|
||||
StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context);
|
||||
if (LHSString.empty() || RHSString.empty())
|
||||
return;
|
||||
|
||||
D << tooling::fixit::createReplacement(*FillChar, RHSString)
|
||||
<< tooling::fixit::createReplacement(*ByteCount, LHSString);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,35 +0,0 @@
|
||||
//===--- SuspiciousMemsetUsageCheck.h - clang-tidy---------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
/// Finds memset calls with potential mistakes in their arguments.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memset-usage.html
|
||||
class SuspiciousMemsetUsageCheck : public ClangTidyCheck {
|
||||
public:
|
||||
SuspiciousMemsetUsageCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
|
||||
@@ -1,61 +0,0 @@
|
||||
//===--- UndefinedMemoryManipulationCheck.cpp - clang-tidy-----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UndefinedMemoryManipulationCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
namespace {
|
||||
AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) {
|
||||
return !Node.isTriviallyCopyable();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) {
|
||||
const auto NotTriviallyCopyableObject =
|
||||
hasType(pointsTo(cxxRecordDecl(isNotTriviallyCopyable())));
|
||||
|
||||
// Check whether destination object is not TriviallyCopyable.
|
||||
// Applicable to all three memory manipulation functions.
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName(
|
||||
"::memset", "::memcpy", "::memmove"))),
|
||||
hasArgument(0, NotTriviallyCopyableObject))
|
||||
.bind("dest"),
|
||||
this);
|
||||
|
||||
// Check whether source object is not TriviallyCopyable.
|
||||
// Only applicable to memcpy() and memmove().
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasAnyName("::memcpy", "::memmove"))),
|
||||
hasArgument(1, NotTriviallyCopyableObject))
|
||||
.bind("src"),
|
||||
this);
|
||||
}
|
||||
|
||||
void UndefinedMemoryManipulationCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
if (const auto *Destination = Result.Nodes.getNodeAs<CallExpr>("dest")) {
|
||||
diag(Destination->getLocStart(), "undefined behavior, destination "
|
||||
"object is not TriviallyCopyable");
|
||||
}
|
||||
if (const auto *Source = Result.Nodes.getNodeAs<CallExpr>("src")) {
|
||||
diag(Source->getLocStart(), "undefined behavior, source object is not "
|
||||
"TriviallyCopyable");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,37 +0,0 @@
|
||||
//===--- UndefinedMemoryManipulationCheck.h - clang-tidy---------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
/// Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
|
||||
/// ``memmove()`` on not TriviallyCopyable objects resulting in undefined
|
||||
/// behavior.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-undefined-memory-manipulation.html
|
||||
class UndefinedMemoryManipulationCheck : public ClangTidyCheck {
|
||||
public:
|
||||
UndefinedMemoryManipulationCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
|
||||
@@ -17,10 +17,8 @@
|
||||
#include "../misc/StaticAssertCheck.h"
|
||||
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
|
||||
#include "CommandProcessorCheck.h"
|
||||
#include "DontModifyStdNamespaceCheck.h"
|
||||
#include "FloatLoopCounter.h"
|
||||
#include "LimitedRandomnessCheck.h"
|
||||
#include "PostfixOperatorCheck.h"
|
||||
#include "SetLongJmpCheck.h"
|
||||
#include "StaticObjectExceptionCheck.h"
|
||||
#include "StrToNumCheck.h"
|
||||
@@ -36,13 +34,9 @@ public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
// C++ checkers
|
||||
// DCL
|
||||
CheckFactories.registerCheck<PostfixOperatorCheck>(
|
||||
"cert-dcl21-cpp");
|
||||
CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
|
||||
CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
|
||||
"cert-dcl54-cpp");
|
||||
CheckFactories.registerCheck<DontModifyStdNamespaceCheck>(
|
||||
"cert-dcl58-cpp");
|
||||
CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
|
||||
"cert-dcl59-cpp");
|
||||
// OOP
|
||||
|
||||
@@ -3,10 +3,8 @@ set(LLVM_LINK_COMPONENTS support)
|
||||
add_clang_library(clangTidyCERTModule
|
||||
CERTTidyModule.cpp
|
||||
CommandProcessorCheck.cpp
|
||||
DontModifyStdNamespaceCheck.cpp
|
||||
FloatLoopCounter.cpp
|
||||
LimitedRandomnessCheck.cpp
|
||||
PostfixOperatorCheck.cpp
|
||||
SetLongJmpCheck.cpp
|
||||
StaticObjectExceptionCheck.cpp
|
||||
StrToNumCheck.cpp
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
//===--- DontModifyStdNamespaceCheck.cpp - clang-tidy----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DontModifyStdNamespaceCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
Finder->addMatcher(
|
||||
namespaceDecl(unless(isExpansionInSystemHeader()),
|
||||
anyOf(hasName("std"), hasName("posix")),
|
||||
has(decl(unless(anyOf(
|
||||
functionDecl(isExplicitTemplateSpecialization()),
|
||||
cxxRecordDecl(isExplicitTemplateSpecialization()))))))
|
||||
.bind("nmspc"),
|
||||
this);
|
||||
}
|
||||
|
||||
void DontModifyStdNamespaceCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
|
||||
|
||||
// Only consider top level namespaces.
|
||||
if (N->getParent() != Result.Context->getTranslationUnitDecl())
|
||||
return;
|
||||
|
||||
diag(N->getLocation(),
|
||||
"modification of %0 namespace can result in undefined behavior")
|
||||
<< N;
|
||||
}
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,36 +0,0 @@
|
||||
//===--- DontModifyStdNamespaceCheck.h - clang-tidy--------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
/// Modification of the std or posix namespace can result in undefined behavior.
|
||||
/// This check warns for such modifications.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc53-cpp.html
|
||||
class DontModifyStdNamespaceCheck : public ClangTidyCheck {
|
||||
public:
|
||||
DontModifyStdNamespaceCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
|
||||
@@ -1,88 +0,0 @@
|
||||
//===--- PostfixOperatorCheck.cpp - clang-tidy-----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PostfixOperatorCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
void PostfixOperatorCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("++"),
|
||||
hasOverloadedOperatorName("--")))
|
||||
.bind("decl"),
|
||||
this);
|
||||
}
|
||||
|
||||
void PostfixOperatorCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
|
||||
|
||||
bool HasThis = false;
|
||||
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
|
||||
HasThis = MethodDecl->isInstance();
|
||||
|
||||
// Check if the operator is a postfix one.
|
||||
if (FuncDecl->getNumParams() != (HasThis ? 1 : 2))
|
||||
return;
|
||||
|
||||
SourceRange ReturnRange = FuncDecl->getReturnTypeSourceRange();
|
||||
SourceLocation Location = ReturnRange.getBegin();
|
||||
if (!Location.isValid())
|
||||
return;
|
||||
|
||||
QualType ReturnType = FuncDecl->getReturnType();
|
||||
|
||||
// Warn when the operators return a reference.
|
||||
if (const auto *RefType = ReturnType->getAs<ReferenceType>()) {
|
||||
auto Diag = diag(Location, "overloaded %0 returns a reference instead of a "
|
||||
"constant object type")
|
||||
<< FuncDecl;
|
||||
|
||||
if (Location.isMacroID() || ReturnType->getAs<TypedefType>() ||
|
||||
RefType->getPointeeTypeAsWritten()->getAs<TypedefType>())
|
||||
return;
|
||||
|
||||
QualType ReplaceType =
|
||||
ReturnType.getNonReferenceType().getLocalUnqualifiedType();
|
||||
// The getReturnTypeSourceRange omits the qualifiers. We do not want to
|
||||
// duplicate the const.
|
||||
if (!ReturnType->getPointeeType().isConstQualified())
|
||||
ReplaceType.addConst();
|
||||
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
ReturnRange,
|
||||
ReplaceType.getAsString(Result.Context->getPrintingPolicy()) + " ");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReturnType.isConstQualified() || ReturnType->isBuiltinType() ||
|
||||
ReturnType->isPointerType())
|
||||
return;
|
||||
|
||||
auto Diag =
|
||||
diag(Location, "overloaded %0 returns a non-constant object instead of a "
|
||||
"constant object type")
|
||||
<< FuncDecl;
|
||||
|
||||
if (!Location.isMacroID() && !ReturnType->getAs<TypedefType>())
|
||||
Diag << FixItHint::CreateInsertion(Location, "const ");
|
||||
}
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,36 +0,0 @@
|
||||
//===--- PostfixOperatorCheck.h - clang-tidy---------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
/// Checks if the overloaded postfix ++ and -- operator return a constant
|
||||
/// object.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-postfix-operator.html
|
||||
class PostfixOperatorCheck : public ClangTidyCheck {
|
||||
public:
|
||||
PostfixOperatorCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
|
||||
@@ -19,7 +19,7 @@ namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions))
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
// Match any static or thread_local variable declaration that has an
|
||||
|
||||
@@ -8,63 +8,43 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "NoMallocCheck.h"
|
||||
#include "../utils/Matchers.h"
|
||||
#include "../utils/OptionsUtils.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang::ast_matchers::internal;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cppcoreguidelines {
|
||||
|
||||
namespace {
|
||||
Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
|
||||
const std::vector<std::string> NameList =
|
||||
utils::options::parseStringList(FunctionNames);
|
||||
return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "Allocations", AllocList);
|
||||
Options.store(Opts, "Reallocations", ReallocList);
|
||||
Options.store(Opts, "Deallocations", DeallocList);
|
||||
}
|
||||
|
||||
void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// C-style memory management is only problematic in C++.
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
// Registering malloc, will suggest RAII.
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(hasAnyListedName(AllocList))))
|
||||
.bind("allocation"),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc"))))
|
||||
.bind("aquisition"),
|
||||
this);
|
||||
|
||||
// Registering realloc calls, suggest std::vector or std::string.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasAnyListedName(ReallocList))))
|
||||
.bind("realloc"),
|
||||
callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"),
|
||||
this);
|
||||
|
||||
// Registering free calls, will suggest RAII instead.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasAnyListedName(DeallocList))))
|
||||
.bind("free"),
|
||||
this);
|
||||
callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this);
|
||||
}
|
||||
|
||||
void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const CallExpr *Call = nullptr;
|
||||
StringRef Recommendation;
|
||||
|
||||
if ((Call = Result.Nodes.getNodeAs<CallExpr>("allocation")))
|
||||
if ((Call = Result.Nodes.getNodeAs<CallExpr>("aquisition")))
|
||||
Recommendation = "consider a container or a smart pointer";
|
||||
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
|
||||
Recommendation = "consider std::vector or std::string";
|
||||
|
||||
@@ -27,32 +27,14 @@ namespace cppcoreguidelines {
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
|
||||
class NoMallocCheck : public ClangTidyCheck {
|
||||
public:
|
||||
/// Construct Checker and read in configuration for function names.
|
||||
NoMallocCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
AllocList(Options.get("Allocations", "::malloc;::calloc")),
|
||||
ReallocList(Options.get("Reallocations", "::realloc")),
|
||||
DeallocList(Options.get("Deallocations", "::free")) {}
|
||||
|
||||
/// Make configuration of checker discoverable.
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
|
||||
/// Registering for malloc, calloc, realloc and free calls.
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
|
||||
/// Checks matched function calls and gives suggestion to modernize the code.
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
/// Semicolon-separated list of fully qualified names of memory allocation
|
||||
/// functions the check warns about. Defaults to `::malloc;::calloc`.
|
||||
const std::string AllocList;
|
||||
/// Semicolon-separated list of fully qualified names of memory reallocation
|
||||
/// functions the check warns about. Defaults to `::realloc`.
|
||||
const std::string ReallocList;
|
||||
/// Semicolon-separated list of fully qualified names of memory deallocation
|
||||
/// functions the check warns about. Defaults to `::free`.
|
||||
const std::string DeallocList;
|
||||
};
|
||||
|
||||
} // namespace cppcoreguidelines
|
||||
|
||||
@@ -125,12 +125,12 @@ struct IntializerInsertion {
|
||||
SourceLocation Location;
|
||||
switch (Placement) {
|
||||
case InitializerPlacement::New:
|
||||
Location = utils::lexer::getPreviousToken(
|
||||
Location = utils::lexer::getPreviousNonCommentToken(
|
||||
Context, Constructor.getBody()->getLocStart())
|
||||
.getLocation();
|
||||
break;
|
||||
case InitializerPlacement::Before:
|
||||
Location = utils::lexer::getPreviousToken(
|
||||
Location = utils::lexer::getPreviousNonCommentToken(
|
||||
Context, Where->getSourceRange().getBegin())
|
||||
.getLocation();
|
||||
break;
|
||||
@@ -449,9 +449,6 @@ void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
|
||||
|
||||
// Remove any bases that were explicitly written in the initializer list.
|
||||
if (Ctor) {
|
||||
if (Ctor->isImplicit())
|
||||
return;
|
||||
|
||||
for (const CXXCtorInitializer *Init : Ctor->inits()) {
|
||||
if (Init->isBaseInitializer() && Init->isWritten())
|
||||
BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
|
||||
|
||||
@@ -22,18 +22,6 @@ namespace clang {
|
||||
namespace tidy {
|
||||
namespace cppcoreguidelines {
|
||||
|
||||
SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
|
||||
AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
|
||||
|
||||
void SpecialMemberFunctionsCheck::storeOptions(
|
||||
ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
|
||||
Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
|
||||
}
|
||||
|
||||
void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
@@ -60,12 +48,6 @@ toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
|
||||
switch (K) {
|
||||
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
|
||||
return "a destructor";
|
||||
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
|
||||
DefaultDestructor:
|
||||
return "a default destructor";
|
||||
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
|
||||
NonDefaultDestructor:
|
||||
return "a non-default destructor";
|
||||
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
|
||||
return "a copy constructor";
|
||||
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
|
||||
@@ -106,86 +88,51 @@ void SpecialMemberFunctionsCheck::check(
|
||||
|
||||
ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
|
||||
|
||||
auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) {
|
||||
llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
|
||||
ClassWithSpecialMembers[ID];
|
||||
if (!llvm::is_contained(Members, Kind))
|
||||
Members.push_back(Kind);
|
||||
};
|
||||
|
||||
if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {
|
||||
StoreMember(Dtor->isDefaulted()
|
||||
? SpecialMemberFunctionKind::DefaultDestructor
|
||||
: SpecialMemberFunctionKind::NonDefaultDestructor);
|
||||
}
|
||||
|
||||
std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
|
||||
Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
|
||||
Matchers = {{"dtor", SpecialMemberFunctionKind::Destructor},
|
||||
{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
|
||||
{"copy-assign", SpecialMemberFunctionKind::CopyAssignment},
|
||||
{"move-ctor", SpecialMemberFunctionKind::MoveConstructor},
|
||||
{"move-assign", SpecialMemberFunctionKind::MoveAssignment}};
|
||||
|
||||
for (const auto &KV : Matchers)
|
||||
if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
|
||||
StoreMember(KV.second);
|
||||
SpecialMemberFunctionKind Kind = KV.second;
|
||||
llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
|
||||
ClassWithSpecialMembers[ID];
|
||||
if (find(Members, Kind) == Members.end())
|
||||
Members.push_back(Kind);
|
||||
}
|
||||
}
|
||||
|
||||
void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
|
||||
llvm::SmallVector<SpecialMemberFunctionKind, 5> AllSpecialMembers = {
|
||||
SpecialMemberFunctionKind::Destructor,
|
||||
SpecialMemberFunctionKind::CopyConstructor,
|
||||
SpecialMemberFunctionKind::CopyAssignment};
|
||||
|
||||
if (getLangOpts().CPlusPlus11) {
|
||||
AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveConstructor);
|
||||
AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveAssignment);
|
||||
}
|
||||
|
||||
for (const auto &C : ClassWithSpecialMembers) {
|
||||
checkForMissingMembers(C.first, C.second);
|
||||
const auto &DefinedSpecialMembers = C.second;
|
||||
|
||||
if (DefinedSpecialMembers.size() == AllSpecialMembers.size())
|
||||
continue;
|
||||
|
||||
llvm::SmallVector<SpecialMemberFunctionKind, 5> UndefinedSpecialMembers;
|
||||
std::set_difference(AllSpecialMembers.begin(), AllSpecialMembers.end(),
|
||||
DefinedSpecialMembers.begin(),
|
||||
DefinedSpecialMembers.end(),
|
||||
std::back_inserter(UndefinedSpecialMembers));
|
||||
|
||||
diag(C.first.first, "class '%0' defines %1 but does not define %2")
|
||||
<< C.first.second << join(DefinedSpecialMembers, " and ")
|
||||
<< join(UndefinedSpecialMembers, " or ");
|
||||
}
|
||||
}
|
||||
|
||||
void SpecialMemberFunctionsCheck::checkForMissingMembers(
|
||||
const ClassDefId &ID,
|
||||
llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) {
|
||||
llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
|
||||
|
||||
auto HasMember = [&](SpecialMemberFunctionKind Kind) {
|
||||
return llvm::is_contained(DefinedMembers, Kind);
|
||||
};
|
||||
|
||||
auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
|
||||
if (!HasMember(Kind))
|
||||
MissingMembers.push_back(Kind);
|
||||
};
|
||||
|
||||
bool RequireThree =
|
||||
HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) ||
|
||||
(!AllowSoleDefaultDtor &&
|
||||
HasMember(SpecialMemberFunctionKind::DefaultDestructor)) ||
|
||||
HasMember(SpecialMemberFunctionKind::CopyConstructor) ||
|
||||
HasMember(SpecialMemberFunctionKind::CopyAssignment) ||
|
||||
HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
|
||||
HasMember(SpecialMemberFunctionKind::MoveAssignment);
|
||||
|
||||
bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
|
||||
getLangOpts().CPlusPlus11) ||
|
||||
HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
|
||||
HasMember(SpecialMemberFunctionKind::MoveAssignment);
|
||||
|
||||
if (RequireThree) {
|
||||
if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) &&
|
||||
!HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))
|
||||
MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
|
||||
|
||||
RequireMember(SpecialMemberFunctionKind::CopyConstructor);
|
||||
RequireMember(SpecialMemberFunctionKind::CopyAssignment);
|
||||
}
|
||||
|
||||
if (RequireFive) {
|
||||
assert(RequireThree);
|
||||
RequireMember(SpecialMemberFunctionKind::MoveConstructor);
|
||||
RequireMember(SpecialMemberFunctionKind::MoveAssignment);
|
||||
}
|
||||
|
||||
if (!MissingMembers.empty())
|
||||
diag(ID.first, "class '%0' defines %1 but does not define %2")
|
||||
<< ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
|
||||
<< cppcoreguidelines::join(MissingMembers, " or ");
|
||||
}
|
||||
|
||||
} // namespace cppcoreguidelines
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
@@ -25,16 +25,14 @@ namespace cppcoreguidelines {
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-special-member-functions.html
|
||||
class SpecialMemberFunctionsCheck : public ClangTidyCheck {
|
||||
public:
|
||||
SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context);
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
void onEndOfTranslationUnit() override;
|
||||
|
||||
enum class SpecialMemberFunctionKind : uint8_t {
|
||||
Destructor,
|
||||
DefaultDestructor,
|
||||
NonDefaultDestructor,
|
||||
CopyConstructor,
|
||||
CopyAssignment,
|
||||
MoveConstructor,
|
||||
@@ -48,12 +46,6 @@ public:
|
||||
llvm::SmallVector<SpecialMemberFunctionKind, 5>>;
|
||||
|
||||
private:
|
||||
void checkForMissingMembers(
|
||||
const ClassDefId &ID,
|
||||
llvm::ArrayRef<SpecialMemberFunctionKind> DefinedSpecialMembers);
|
||||
|
||||
const bool AllowMissingMoveFunctions;
|
||||
const bool AllowSoleDefaultDtor;
|
||||
ClassDefiningSpecialMembersMap ClassWithSpecialMembers;
|
||||
};
|
||||
|
||||
@@ -91,7 +83,7 @@ struct DenseMapInfo<
|
||||
return Val.first.getRawEncoding() + SecondHash(Val.second);
|
||||
}
|
||||
|
||||
static bool isEqual(const ClassDefId &LHS, const ClassDefId &RHS) {
|
||||
static bool isEqual(ClassDefId LHS, ClassDefId RHS) {
|
||||
if (RHS == getEmptyKey())
|
||||
return LHS == getEmptyKey();
|
||||
if (RHS == getTombstoneKey())
|
||||
|
||||
@@ -35,68 +35,55 @@ void AvoidCStyleCastsCheck::registerMatchers(
|
||||
}
|
||||
|
||||
static bool needsConstCast(QualType SourceType, QualType DestType) {
|
||||
while ((SourceType->isPointerType() && DestType->isPointerType()) ||
|
||||
(SourceType->isReferenceType() && DestType->isReferenceType())) {
|
||||
SourceType = SourceType.getNonReferenceType();
|
||||
DestType = DestType.getNonReferenceType();
|
||||
while (SourceType->isPointerType() && DestType->isPointerType()) {
|
||||
SourceType = SourceType->getPointeeType();
|
||||
DestType = DestType->getPointeeType();
|
||||
if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
|
||||
return (SourceType->isPointerType() == DestType->isPointerType()) &&
|
||||
(SourceType->isReferenceType() == DestType->isReferenceType());
|
||||
}
|
||||
if (SourceType.isConstQualified() && !DestType.isConstQualified())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
|
||||
while ((T1->isPointerType() && T2->isPointerType()) ||
|
||||
(T1->isReferenceType() && T2->isReferenceType())) {
|
||||
T1 = T1->getPointeeType();
|
||||
T2 = T2->getPointeeType();
|
||||
static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
|
||||
SourceType = SourceType.getNonReferenceType();
|
||||
DestType = DestType.getNonReferenceType();
|
||||
while (SourceType->isPointerType() && DestType->isPointerType()) {
|
||||
SourceType = SourceType->getPointeeType();
|
||||
DestType = DestType->getPointeeType();
|
||||
}
|
||||
return T1.getUnqualifiedType() == T2.getUnqualifiedType();
|
||||
return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType();
|
||||
}
|
||||
|
||||
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
|
||||
|
||||
auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
|
||||
CastExpr->getRParenLoc());
|
||||
// Ignore casts in macros.
|
||||
if (CastExpr->getExprLoc().isMacroID())
|
||||
if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
|
||||
return;
|
||||
|
||||
// Casting to void is an idiomatic way to mute "unused variable" and similar
|
||||
// warnings.
|
||||
if (CastExpr->getCastKind() == CK_ToVoid)
|
||||
if (CastExpr->getTypeAsWritten()->isVoidType())
|
||||
return;
|
||||
|
||||
auto isFunction = [](QualType T) {
|
||||
T = T.getCanonicalType().getNonReferenceType();
|
||||
return T->isFunctionType() || T->isFunctionPointerType() ||
|
||||
T->isMemberFunctionPointerType();
|
||||
};
|
||||
QualType SourceType = CastExpr->getSubExprAsWritten()->getType();
|
||||
QualType DestType = CastExpr->getTypeAsWritten();
|
||||
|
||||
const QualType DestTypeAsWritten =
|
||||
CastExpr->getTypeAsWritten().getUnqualifiedType();
|
||||
const QualType SourceTypeAsWritten =
|
||||
CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
|
||||
const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
|
||||
const QualType DestType = DestTypeAsWritten.getCanonicalType();
|
||||
|
||||
auto ReplaceRange = CharSourceRange::getCharRange(
|
||||
CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getLocStart());
|
||||
|
||||
bool FnToFnCast =
|
||||
isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
|
||||
|
||||
if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
|
||||
// Function pointer/reference casts may be needed to resolve ambiguities in
|
||||
// case of overloaded functions, so detection of redundant casts is trickier
|
||||
// in this case. Don't emit "redundant cast" warnings for function
|
||||
// pointer/reference types.
|
||||
if (SourceTypeAsWritten == DestTypeAsWritten) {
|
||||
diag(CastExpr->getLocStart(), "redundant cast to the same type")
|
||||
<< FixItHint::CreateRemoval(ReplaceRange);
|
||||
return;
|
||||
}
|
||||
if (SourceType == DestType) {
|
||||
diag(CastExpr->getLocStart(), "redundant cast to the same type")
|
||||
<< FixItHint::CreateRemoval(ParenRange);
|
||||
return;
|
||||
}
|
||||
SourceType = SourceType.getCanonicalType();
|
||||
DestType = DestType.getCanonicalType();
|
||||
if (SourceType == DestType) {
|
||||
diag(CastExpr->getLocStart(),
|
||||
"possibly redundant cast between typedefs of the same type");
|
||||
return;
|
||||
}
|
||||
|
||||
// The rest of this check is only relevant to C++.
|
||||
@@ -110,11 +97,9 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
// compiled as C++.
|
||||
if (getCurrentMainFile().endswith(".c"))
|
||||
return;
|
||||
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
|
||||
// Ignore code in .c files #included in other files (which shouldn't be done,
|
||||
// but people still do this for test and other purposes).
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
|
||||
return;
|
||||
|
||||
@@ -126,65 +111,38 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
CastExpr->getRParenLoc().getLocWithOffset(-1)),
|
||||
SM, getLangOpts());
|
||||
|
||||
auto Diag =
|
||||
auto diag_builder =
|
||||
diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
|
||||
|
||||
auto ReplaceWithCast = [&](std::string CastText) {
|
||||
auto ReplaceWithCast = [&](StringRef CastType) {
|
||||
diag_builder << CastType;
|
||||
|
||||
const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
|
||||
std::string CastText = (CastType + "<" + DestTypeString + ">").str();
|
||||
if (!isa<ParenExpr>(SubExpr)) {
|
||||
CastText.push_back('(');
|
||||
Diag << FixItHint::CreateInsertion(
|
||||
diag_builder << FixItHint::CreateInsertion(
|
||||
Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
|
||||
getLangOpts()),
|
||||
")");
|
||||
}
|
||||
Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
|
||||
};
|
||||
auto ReplaceWithNamedCast = [&](StringRef CastType) {
|
||||
Diag << CastType;
|
||||
ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
|
||||
diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
|
||||
};
|
||||
|
||||
// Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
|
||||
switch (CastExpr->getCastKind()) {
|
||||
case CK_FunctionToPointerDecay:
|
||||
ReplaceWithNamedCast("static_cast");
|
||||
return;
|
||||
case CK_ConstructorConversion:
|
||||
if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
|
||||
DestTypeAsWritten->isRecordType() &&
|
||||
!DestTypeAsWritten->isElaboratedTypeSpecifier()) {
|
||||
Diag << "constructor call syntax";
|
||||
// FIXME: Validate DestTypeString, maybe.
|
||||
ReplaceWithCast(DestTypeString.str());
|
||||
} else {
|
||||
ReplaceWithNamedCast("static_cast");
|
||||
}
|
||||
return;
|
||||
case CK_NoOp:
|
||||
if (FnToFnCast) {
|
||||
ReplaceWithNamedCast("static_cast");
|
||||
return;
|
||||
}
|
||||
if (SourceType == DestType) {
|
||||
Diag << "static_cast (if needed, the cast may be redundant)";
|
||||
ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
|
||||
return;
|
||||
}
|
||||
if (needsConstCast(SourceType, DestType) &&
|
||||
pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
|
||||
ReplaceWithNamedCast("const_cast");
|
||||
pointedTypesAreEqual(SourceType, DestType)) {
|
||||
ReplaceWithCast("const_cast");
|
||||
return;
|
||||
}
|
||||
if (DestType->isReferenceType()) {
|
||||
QualType Dest = DestType.getNonReferenceType();
|
||||
QualType Source = SourceType.getNonReferenceType();
|
||||
if (Source == Dest.withConst() ||
|
||||
SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
|
||||
ReplaceWithNamedCast("const_cast");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
if (DestType->isReferenceType() &&
|
||||
(SourceType.getNonReferenceType() ==
|
||||
DestType.getNonReferenceType().withConst() ||
|
||||
SourceType.getNonReferenceType() == DestType.getNonReferenceType())) {
|
||||
ReplaceWithCast("const_cast");
|
||||
return;
|
||||
}
|
||||
// FALLTHROUGH
|
||||
case clang::CK_IntegralCast:
|
||||
@@ -193,17 +151,14 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
// still retained.
|
||||
if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
|
||||
(DestType->isBuiltinType() || DestType->isEnumeralType())) {
|
||||
ReplaceWithNamedCast("static_cast");
|
||||
ReplaceWithCast("static_cast");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CK_BitCast:
|
||||
// FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
|
||||
if (!needsConstCast(SourceType, DestType)) {
|
||||
if (SourceType->isVoidPointerType())
|
||||
ReplaceWithNamedCast("static_cast");
|
||||
else
|
||||
ReplaceWithNamedCast("reinterpret_cast");
|
||||
ReplaceWithCast("reinterpret_cast");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -211,7 +166,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
break;
|
||||
}
|
||||
|
||||
Diag << "static_cast/const_cast/reinterpret_cast";
|
||||
diag_builder << "static_cast/const_cast/reinterpret_cast";
|
||||
}
|
||||
|
||||
} // namespace readability
|
||||
|
||||
@@ -8,6 +8,7 @@ add_clang_library(clangTidyGoogleModule
|
||||
GlobalNamesInHeadersCheck.cpp
|
||||
GoogleTidyModule.cpp
|
||||
IntegerTypesCheck.cpp
|
||||
MemsetZeroLengthCheck.cpp
|
||||
NonConstReferences.cpp
|
||||
OverloadedUnaryAndCheck.cpp
|
||||
StringReferenceMemberCheck.cpp
|
||||
|
||||
@@ -24,15 +24,12 @@ void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// provide any benefit to other languages, despite being benign.
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
Finder->addMatcher(
|
||||
cxxConstructorDecl(unless(anyOf(isImplicit(), // Compiler-generated.
|
||||
isDeleted(), isInstantiated())))
|
||||
.bind("ctor"),
|
||||
this);
|
||||
Finder->addMatcher(cxxConstructorDecl(unless(isInstantiated())).bind("ctor"),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
cxxConversionDecl(unless(anyOf(isExplicit(), // Already marked explicit.
|
||||
isImplicit(), // Compiler-generated.
|
||||
isDeleted(), isInstantiated())))
|
||||
isInstantiated())))
|
||||
|
||||
.bind("conversion"),
|
||||
this);
|
||||
@@ -91,8 +88,6 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
|
||||
if (const auto *Conversion =
|
||||
Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) {
|
||||
if (Conversion->isOutOfLine())
|
||||
return;
|
||||
SourceLocation Loc = Conversion->getLocation();
|
||||
// Ignore all macros until we learn to ignore specific ones (e.g. used in
|
||||
// gmock to define matchers).
|
||||
@@ -104,8 +99,10 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
}
|
||||
|
||||
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
|
||||
if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 ||
|
||||
Ctor->getMinRequiredArguments() > 1)
|
||||
// Do not be confused: isExplicit means 'explicit' keyword is present,
|
||||
// isImplicit means that it's a compiler-generated constructor.
|
||||
if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
|
||||
Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
|
||||
return;
|
||||
|
||||
bool takesInitializerList = isStdInitializerList(
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ExplicitMakePairCheck.h"
|
||||
#include "GlobalNamesInHeadersCheck.h"
|
||||
#include "IntegerTypesCheck.h"
|
||||
#include "MemsetZeroLengthCheck.h"
|
||||
#include "NonConstReferences.h"
|
||||
#include "OverloadedUnaryAndCheck.h"
|
||||
#include "StringReferenceMemberCheck.h"
|
||||
@@ -54,6 +55,8 @@ public:
|
||||
"google-runtime-references");
|
||||
CheckFactories.registerCheck<runtime::StringReferenceMemberCheck>(
|
||||
"google-runtime-member-string-references");
|
||||
CheckFactories.registerCheck<runtime::MemsetZeroLengthCheck>(
|
||||
"google-runtime-memset");
|
||||
CheckFactories.registerCheck<readability::AvoidCStyleCastsCheck>(
|
||||
"google-readability-casting");
|
||||
CheckFactories.registerCheck<readability::TodoCommentCheck>(
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//===--- MemsetZeroLengthCheck.cpp - clang-tidy -------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MemsetZeroLengthCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace google {
|
||||
namespace runtime {
|
||||
|
||||
void MemsetZeroLengthCheck::registerMatchers(
|
||||
ast_matchers::MatchFinder *Finder) {
|
||||
// Look for memset(x, y, 0) as those is most likely an argument swap.
|
||||
// TODO: Also handle other standard functions that suffer from the same
|
||||
// problem, e.g. memchr.
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(hasName("::memset"))),
|
||||
argumentCountIs(3),
|
||||
unless(isInTemplateInstantiation()))
|
||||
.bind("decl"),
|
||||
this);
|
||||
}
|
||||
|
||||
/// \brief Get a StringRef representing a SourceRange.
|
||||
static StringRef getAsString(const MatchFinder::MatchResult &Result,
|
||||
SourceRange R) {
|
||||
const SourceManager &SM = *Result.SourceManager;
|
||||
// Don't even try to resolve macro or include contraptions. Not worth emitting
|
||||
// a fixit for.
|
||||
if (R.getBegin().isMacroID() ||
|
||||
!SM.isWrittenInSameFile(R.getBegin(), R.getEnd()))
|
||||
return StringRef();
|
||||
|
||||
const char *Begin = SM.getCharacterData(R.getBegin());
|
||||
const char *End = SM.getCharacterData(Lexer::getLocForEndOfToken(
|
||||
R.getEnd(), 0, SM, Result.Context->getLangOpts()));
|
||||
|
||||
return StringRef(Begin, End - Begin);
|
||||
}
|
||||
|
||||
void MemsetZeroLengthCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("decl");
|
||||
|
||||
// Note, this is:
|
||||
// void *memset(void *buffer, int fill_char, size_t byte_count);
|
||||
// Arg1 is fill_char, Arg2 is byte_count.
|
||||
const Expr *Arg1 = Call->getArg(1);
|
||||
const Expr *Arg2 = Call->getArg(2);
|
||||
|
||||
// Return if `byte_count` is not zero at compile time.
|
||||
llvm::APSInt Value1, Value2;
|
||||
if (Arg2->isValueDependent() ||
|
||||
!Arg2->EvaluateAsInt(Value2, *Result.Context) || Value2 != 0)
|
||||
return;
|
||||
|
||||
// Return if `fill_char` is known to be zero or negative at compile
|
||||
// time. In these cases, swapping the args would be a nop, or
|
||||
// introduce a definite bug. The code is likely correct.
|
||||
if (!Arg1->isValueDependent() &&
|
||||
Arg1->EvaluateAsInt(Value1, *Result.Context) &&
|
||||
(Value1 == 0 || Value1.isNegative()))
|
||||
return;
|
||||
|
||||
// `byte_count` is known to be zero at compile time, and `fill_char` is
|
||||
// either not known or known to be a positive integer. Emit a warning
|
||||
// and fix-its to swap the arguments.
|
||||
auto D = diag(Call->getLocStart(),
|
||||
"memset of size zero, potentially swapped arguments");
|
||||
SourceRange LHSRange = Arg1->getSourceRange();
|
||||
SourceRange RHSRange = Arg2->getSourceRange();
|
||||
StringRef RHSString = getAsString(Result, RHSRange);
|
||||
StringRef LHSString = getAsString(Result, LHSRange);
|
||||
if (LHSString.empty() || RHSString.empty())
|
||||
return;
|
||||
|
||||
D << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(LHSRange),
|
||||
RHSString)
|
||||
<< FixItHint::CreateReplacement(CharSourceRange::getTokenRange(RHSRange),
|
||||
LHSString);
|
||||
}
|
||||
|
||||
} // namespace runtime
|
||||
} // namespace google
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
39
clang-tools-extra/clang-tidy/google/MemsetZeroLengthCheck.h
Normal file
39
clang-tools-extra/clang-tidy/google/MemsetZeroLengthCheck.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//===--- MemsetZeroLengthCheck.h - clang-tidy ---------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_MEMSETZEROLENGTHCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_MEMSETZEROLENGTHCHECK_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace google {
|
||||
namespace runtime {
|
||||
|
||||
/// Finds calls to memset with a literal zero in the length argument.
|
||||
///
|
||||
/// This is most likely unintended and the length and value arguments are
|
||||
/// swapped.
|
||||
///
|
||||
/// Corresponding cpplint.py check name: 'runtime/memset'.
|
||||
class MemsetZeroLengthCheck : public ClangTidyCheck {
|
||||
public:
|
||||
MemsetZeroLengthCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace runtime
|
||||
} // namespace google
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_MEMSETZEROLENGTHCHECK_H
|
||||
@@ -34,32 +34,12 @@ void UsingNamespaceDirectiveCheck::check(
|
||||
if (U->isImplicit() || !Loc.isValid())
|
||||
return;
|
||||
|
||||
// Do not warn if namespace is a std namespace with user-defined literals. The
|
||||
// user-defined literals can only be used with a using directive.
|
||||
if (isStdLiteralsNamespace(U->getNominatedNamespace()))
|
||||
return;
|
||||
|
||||
diag(Loc, "do not use namespace using-directives; "
|
||||
"use using-declarations instead");
|
||||
// TODO: We could suggest a list of using directives replacing the using
|
||||
// namespace directive.
|
||||
}
|
||||
|
||||
bool UsingNamespaceDirectiveCheck::isStdLiteralsNamespace(
|
||||
const NamespaceDecl *NS) {
|
||||
if (!NS->getName().endswith("literals"))
|
||||
return false;
|
||||
|
||||
const auto *Parent = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
|
||||
if (!Parent)
|
||||
return false;
|
||||
|
||||
if (Parent->isStdNamespace())
|
||||
return true;
|
||||
|
||||
return Parent->getName() == "literals" && Parent->getParent() &&
|
||||
Parent->getParent()->isStdNamespace();
|
||||
}
|
||||
} // namespace build
|
||||
} // namespace google
|
||||
} // namespace tidy
|
||||
|
||||
@@ -38,9 +38,6 @@ public:
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
static bool isStdLiteralsNamespace(const NamespaceDecl *NS);
|
||||
};
|
||||
|
||||
} // namespace build
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyHICPPModule
|
||||
NoAssemblerCheck.cpp
|
||||
HICPPTidyModule.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangLex
|
||||
clangTidy
|
||||
clangTidyCppCoreGuidelinesModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyUtils
|
||||
)
|
||||
@@ -1,75 +0,0 @@
|
||||
//===------- HICPPTidyModule.cpp - clang-tidy -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "../cppcoreguidelines/ProTypeMemberInitCheck.h"
|
||||
#include "../cppcoreguidelines/SpecialMemberFunctionsCheck.h"
|
||||
#include "../google/DefaultArgumentsCheck.h"
|
||||
#include "../google/ExplicitConstructorCheck.h"
|
||||
#include "../misc/NewDeleteOverloadsCheck.h"
|
||||
#include "../misc/NoexceptMoveConstructorCheck.h"
|
||||
#include "../misc/UndelegatedConstructor.h"
|
||||
#include "../misc/UseAfterMoveCheck.h"
|
||||
#include "../modernize/UseEqualsDefaultCheck.h"
|
||||
#include "../modernize/UseEqualsDeleteCheck.h"
|
||||
#include "../modernize/UseOverrideCheck.h"
|
||||
#include "../readability/FunctionSizeCheck.h"
|
||||
#include "../readability/IdentifierNamingCheck.h"
|
||||
#include "NoAssemblerCheck.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace hicpp {
|
||||
|
||||
class HICPPModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<google::ExplicitConstructorCheck>(
|
||||
"hicpp-explicit-conversions");
|
||||
CheckFactories.registerCheck<readability::FunctionSizeCheck>(
|
||||
"hicpp-function-size");
|
||||
CheckFactories.registerCheck<readability::IdentifierNamingCheck>(
|
||||
"hicpp-named-parameter");
|
||||
CheckFactories.registerCheck<misc::UseAfterMoveCheck>(
|
||||
"hicpp-invalid-access-moved");
|
||||
CheckFactories.registerCheck<cppcoreguidelines::ProTypeMemberInitCheck>(
|
||||
"hicpp-member-init");
|
||||
CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
|
||||
"hicpp-new-delete-operators");
|
||||
CheckFactories.registerCheck<misc::NoexceptMoveConstructorCheck>(
|
||||
"hicpp-noexcept-move");
|
||||
CheckFactories.registerCheck<NoAssemblerCheck>("hicpp-no-assembler");
|
||||
CheckFactories
|
||||
.registerCheck<cppcoreguidelines::SpecialMemberFunctionsCheck>(
|
||||
"hicpp-special-member-functions");
|
||||
CheckFactories.registerCheck<misc::UndelegatedConstructorCheck>(
|
||||
"hicpp-undelegated-constructor");
|
||||
CheckFactories.registerCheck<modernize::UseEqualsDefaultCheck>(
|
||||
"hicpp-use-equals-default");
|
||||
CheckFactories.registerCheck<modernize::UseEqualsDeleteCheck>(
|
||||
"hicpp-use-equals-delete");
|
||||
CheckFactories.registerCheck<modernize::UseOverrideCheck>(
|
||||
"hicpp-use-override");
|
||||
}
|
||||
};
|
||||
|
||||
// Register the HICPPModule using this statically initialized variable.
|
||||
static ClangTidyModuleRegistry::Add<HICPPModule>
|
||||
X("hicpp-module", "Adds High-Integrity C++ checks.");
|
||||
|
||||
} // namespace hicpp
|
||||
|
||||
// This anchor is used to force the linker to link in the generated object file
|
||||
// and thus register the HICPPModule.
|
||||
volatile int HICPPModuleAnchorSource = 0;
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,12 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
clang-tidy High-Integrity C++ Files
|
||||
------------------------------------------------------------------------------
|
||||
All clang-tidy files are licensed under the LLVM license with the following
|
||||
additions:
|
||||
|
||||
Any file referencing a High-Integrity C++ Coding guideline:
|
||||
|
||||
HIC++ Coding Standard as created by PRQA.
|
||||
|
||||
Please see http://www.codingstandard.com/section/conditions-of-use/ for more
|
||||
information.
|
||||
@@ -1,51 +0,0 @@
|
||||
//===--- NoAssemblerCheck.cpp - clang-tidy---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "NoAssemblerCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace ast_matchers {
|
||||
AST_MATCHER(VarDecl, isAsm) { return Node.hasAttr<clang::AsmLabelAttr>(); }
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, FileScopeAsmDecl>
|
||||
fileScopeAsmDecl;
|
||||
}
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace hicpp {
|
||||
|
||||
void NoAssemblerCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(asmStmt().bind("asm-stmt"), this);
|
||||
Finder->addMatcher(fileScopeAsmDecl().bind("asm-file-scope"), this);
|
||||
Finder->addMatcher(varDecl(isAsm()).bind("asm-var"), this);
|
||||
}
|
||||
|
||||
void NoAssemblerCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
SourceLocation ASMLocation;
|
||||
if (const auto *ASM = Result.Nodes.getNodeAs<AsmStmt>("asm-stmt"))
|
||||
ASMLocation = ASM->getAsmLoc();
|
||||
else if (const auto *ASM =
|
||||
Result.Nodes.getNodeAs<FileScopeAsmDecl>("asm-file-scope"))
|
||||
ASMLocation = ASM->getAsmLoc();
|
||||
else if (const auto *ASM = Result.Nodes.getNodeAs<VarDecl>("asm-var"))
|
||||
ASMLocation = ASM->getLocation();
|
||||
else
|
||||
llvm_unreachable("Unhandled case in matcher.");
|
||||
|
||||
diag(ASMLocation, "do not use inline assembler in safety-critical code");
|
||||
}
|
||||
|
||||
} // namespace hicpp
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,35 +0,0 @@
|
||||
//===--- NoAssemblerCheck.h - clang-tidy-------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace hicpp {
|
||||
|
||||
/// Find assembler statements. No fix is offered.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp-no-assembler.html
|
||||
class NoAssemblerCheck : public ClangTidyCheck {
|
||||
public:
|
||||
NoAssemblerCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace hicpp
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
|
||||
@@ -35,11 +35,8 @@ void TwineLocalCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
// of the initializer.
|
||||
const Expr *C = VD->getInit()->IgnoreImplicit();
|
||||
|
||||
while (isa<CXXConstructExpr>(C)) {
|
||||
if (cast<CXXConstructExpr>(C)->getNumArgs() == 0)
|
||||
break;
|
||||
while (isa<CXXConstructExpr>(C))
|
||||
C = cast<CXXConstructExpr>(C)->getArg(0)->IgnoreParenImpCasts();
|
||||
}
|
||||
|
||||
SourceRange TypeRange =
|
||||
VD->getTypeSourceInfo()->getTypeLoc().getSourceRange();
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Lex/Token.h"
|
||||
#include "../utils/LexerUtils.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
@@ -68,45 +67,24 @@ getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) {
|
||||
Token Tok;
|
||||
if (TheLexer.LexFromRawLexer(Tok))
|
||||
break;
|
||||
if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
|
||||
if (Tok.getLocation() == Range.getEnd() || Tok.getKind() == tok::eof)
|
||||
break;
|
||||
|
||||
if (Tok.is(tok::comment)) {
|
||||
if (Tok.getKind() == tok::comment) {
|
||||
std::pair<FileID, unsigned> CommentLoc =
|
||||
SM.getDecomposedLoc(Tok.getLocation());
|
||||
assert(CommentLoc.first == BeginLoc.first);
|
||||
Comments.emplace_back(
|
||||
Tok.getLocation(),
|
||||
StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
|
||||
} else {
|
||||
// Clear comments found before the different token, e.g. comma.
|
||||
Comments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return Comments;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<SourceLocation, StringRef>>
|
||||
getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
|
||||
std::vector<std::pair<SourceLocation, StringRef>> Comments;
|
||||
while (Loc.isValid()) {
|
||||
clang::Token Tok =
|
||||
utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false);
|
||||
if (Tok.isNot(tok::comment))
|
||||
break;
|
||||
Loc = Tok.getLocation();
|
||||
Comments.emplace_back(
|
||||
Loc,
|
||||
Lexer::getSourceText(CharSourceRange::getCharRange(
|
||||
Loc, Loc.getLocWithOffset(Tok.getLength())),
|
||||
Ctx->getSourceManager(), Ctx->getLangOpts()));
|
||||
}
|
||||
return Comments;
|
||||
}
|
||||
|
||||
static bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
|
||||
StringRef ArgName, unsigned ArgIndex) {
|
||||
bool ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
|
||||
StringRef ArgName, unsigned ArgIndex) {
|
||||
std::string ArgNameLowerStr = ArgName.lower();
|
||||
StringRef ArgNameLower = ArgNameLowerStr;
|
||||
// The threshold is arbitrary.
|
||||
@@ -147,133 +125,51 @@ static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode) {
|
||||
return InComment.compare_lower(InDecl) == 0;
|
||||
}
|
||||
|
||||
static bool looksLikeExpectMethod(const CXXMethodDecl *Expect) {
|
||||
return Expect != nullptr && Expect->getLocation().isMacroID() &&
|
||||
Expect->getNameInfo().getName().isIdentifier() &&
|
||||
Expect->getName().startswith("gmock_");
|
||||
}
|
||||
static bool areMockAndExpectMethods(const CXXMethodDecl *Mock,
|
||||
const CXXMethodDecl *Expect) {
|
||||
assert(looksLikeExpectMethod(Expect));
|
||||
return Mock != nullptr && Mock->getNextDeclInContext() == Expect &&
|
||||
Mock->getNumParams() == Expect->getNumParams() &&
|
||||
Mock->getLocation().isMacroID() &&
|
||||
Mock->getNameInfo().getName().isIdentifier() &&
|
||||
Mock->getName() == Expect->getName().substr(strlen("gmock_"));
|
||||
}
|
||||
|
||||
// This uses implementation details of MOCK_METHODx_ macros: for each mocked
|
||||
// method M it defines M() with appropriate signature and a method used to set
|
||||
// up expectations - gmock_M() - with each argument's type changed the
|
||||
// corresponding matcher. This function returns M when given either M or
|
||||
// gmock_M.
|
||||
static const CXXMethodDecl *findMockedMethod(const CXXMethodDecl *Method) {
|
||||
if (looksLikeExpectMethod(Method)) {
|
||||
const DeclContext *Ctx = Method->getDeclContext();
|
||||
if (Ctx == nullptr || !Ctx->isRecord())
|
||||
return nullptr;
|
||||
for (const auto *D : Ctx->decls()) {
|
||||
if (D->getNextDeclInContext() == Method) {
|
||||
const auto *Previous = dyn_cast<CXXMethodDecl>(D);
|
||||
return areMockAndExpectMethods(Previous, Method) ? Previous : nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (const auto *Next = dyn_cast_or_null<CXXMethodDecl>(
|
||||
Method->getNextDeclInContext())) {
|
||||
if (looksLikeExpectMethod(Next) && areMockAndExpectMethods(Method, Next))
|
||||
return Method;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// For gmock expectation builder method (the target of the call generated by
|
||||
// `EXPECT_CALL(obj, Method(...))`) tries to find the real method being mocked
|
||||
// (returns nullptr, if the mock method doesn't override anything). For other
|
||||
// functions returns the function itself.
|
||||
static const FunctionDecl *resolveMocks(const FunctionDecl *Func) {
|
||||
if (const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
|
||||
if (const auto *MockedMethod = findMockedMethod(Method)) {
|
||||
// If mocked method overrides the real one, we can use its parameter
|
||||
// names, otherwise we're out of luck.
|
||||
if (MockedMethod->size_overridden_methods() > 0) {
|
||||
return *MockedMethod->begin_overridden_methods();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return Func;
|
||||
}
|
||||
|
||||
void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
|
||||
const FunctionDecl *OriginalCallee,
|
||||
const FunctionDecl *Callee,
|
||||
SourceLocation ArgBeginLoc,
|
||||
llvm::ArrayRef<const Expr *> Args) {
|
||||
const FunctionDecl *Callee = resolveMocks(OriginalCallee);
|
||||
if (!Callee)
|
||||
return;
|
||||
|
||||
Callee = Callee->getFirstDecl();
|
||||
unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
|
||||
if (NumArgs == 0)
|
||||
return;
|
||||
|
||||
auto makeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
|
||||
return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
|
||||
Ctx->getSourceManager(),
|
||||
Ctx->getLangOpts());
|
||||
};
|
||||
|
||||
for (unsigned I = 0; I < NumArgs; ++I) {
|
||||
for (unsigned I = 0,
|
||||
E = std::min<unsigned>(Args.size(), Callee->getNumParams());
|
||||
I != E; ++I) {
|
||||
const ParmVarDecl *PVD = Callee->getParamDecl(I);
|
||||
IdentifierInfo *II = PVD->getIdentifier();
|
||||
if (!II)
|
||||
continue;
|
||||
if (auto Template = Callee->getTemplateInstantiationPattern()) {
|
||||
// Don't warn on arguments for parameters instantiated from template
|
||||
// parameter packs. If we find more arguments than the template
|
||||
// definition has, it also means that they correspond to a parameter
|
||||
// pack.
|
||||
// parameter packs. If we find more arguments than the template definition
|
||||
// has, it also means that they correspond to a parameter pack.
|
||||
if (Template->getNumParams() <= I ||
|
||||
Template->getParamDecl(I)->isParameterPack()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CharSourceRange BeforeArgument =
|
||||
makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart());
|
||||
ArgBeginLoc = Args[I]->getLocEnd();
|
||||
CharSourceRange BeforeArgument = CharSourceRange::getCharRange(
|
||||
I == 0 ? ArgBeginLoc : Args[I - 1]->getLocEnd(),
|
||||
Args[I]->getLocStart());
|
||||
BeforeArgument = Lexer::makeFileCharRange(
|
||||
BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
|
||||
|
||||
std::vector<std::pair<SourceLocation, StringRef>> Comments;
|
||||
if (BeforeArgument.isValid()) {
|
||||
Comments = getCommentsInRange(Ctx, BeforeArgument);
|
||||
} else {
|
||||
// Fall back to parsing back from the start of the argument.
|
||||
CharSourceRange ArgsRange = makeFileCharRange(
|
||||
Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd());
|
||||
Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin());
|
||||
}
|
||||
|
||||
for (auto Comment : Comments) {
|
||||
for (auto Comment : getCommentsInRange(Ctx, BeforeArgument)) {
|
||||
llvm::SmallVector<StringRef, 2> Matches;
|
||||
if (IdentRE.match(Comment.second, &Matches) &&
|
||||
!sameName(Matches[2], II->getName(), StrictMode)) {
|
||||
{
|
||||
DiagnosticBuilder Diag =
|
||||
diag(Comment.first, "argument name '%0' in comment does not "
|
||||
"match parameter name %1")
|
||||
<< Matches[2] << II;
|
||||
if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
|
||||
if (IdentRE.match(Comment.second, &Matches)) {
|
||||
if (!sameName(Matches[2], II->getName(), StrictMode)) {
|
||||
{
|
||||
DiagnosticBuilder Diag =
|
||||
diag(Comment.first, "argument name '%0' in comment does not "
|
||||
"match parameter name %1")
|
||||
<< Matches[2] << II;
|
||||
if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
Comment.first,
|
||||
(Matches[1] + II->getName() + Matches[3]).str());
|
||||
}
|
||||
}
|
||||
}
|
||||
diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II;
|
||||
if (OriginalCallee != Callee) {
|
||||
diag(OriginalCallee->getLocation(),
|
||||
"actual callee (%0) is declared here", DiagnosticIDs::Note)
|
||||
<< OriginalCallee;
|
||||
diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note)
|
||||
<< II;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,11 +187,6 @@ void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
|
||||
} else {
|
||||
const auto *Construct = cast<CXXConstructExpr>(E);
|
||||
if (Construct->getNumArgs() == 1 &&
|
||||
Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
|
||||
// Ignore implicit construction.
|
||||
return;
|
||||
}
|
||||
checkCallArgs(
|
||||
Result.Context, Construct->getConstructor(),
|
||||
Construct->getParenOrBraceRange().getBegin(),
|
||||
|
||||
@@ -43,6 +43,8 @@ private:
|
||||
const bool StrictMode;
|
||||
llvm::Regex IdentRE;
|
||||
|
||||
bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params, StringRef ArgName,
|
||||
unsigned ArgIndex);
|
||||
void checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee,
|
||||
SourceLocation ArgBeginLoc,
|
||||
llvm::ArrayRef<const Expr *> Args);
|
||||
|
||||
@@ -3,8 +3,6 @@ set(LLVM_LINK_COMPONENTS support)
|
||||
add_clang_library(clangTidyMiscModule
|
||||
ArgumentCommentCheck.cpp
|
||||
AssertSideEffectCheck.cpp
|
||||
ForwardingReferenceOverloadCheck.cpp
|
||||
LambdaFunctionNameCheck.cpp
|
||||
MisplacedConstCheck.cpp
|
||||
UnconventionalAssignOperatorCheck.cpp
|
||||
BoolPointerImplicitConversionCheck.cpp
|
||||
|
||||
@@ -72,7 +72,7 @@ void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
|
||||
|
||||
void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
// Don't run the check in failing TUs.
|
||||
if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
|
||||
if (Result.Context->getDiagnostics().hasErrorOccurred())
|
||||
return;
|
||||
|
||||
// C++ [basic.def.odr] p6:
|
||||
@@ -104,8 +104,8 @@ void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
// Function templates are allowed.
|
||||
if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
|
||||
return;
|
||||
// Ignore instantiated functions.
|
||||
if (FD->isTemplateInstantiation())
|
||||
// Function template full specialization is prohibited in header file.
|
||||
if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
|
||||
return;
|
||||
// Member function of a class template and member function of a nested class
|
||||
// in a class template are allowed.
|
||||
@@ -122,26 +122,20 @@ void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
}
|
||||
}
|
||||
|
||||
bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
|
||||
diag(FD->getLocation(),
|
||||
"%select{function|full function template specialization}0 %1 defined "
|
||||
"in a header file; function definitions in header files can lead to "
|
||||
"ODR violations")
|
||||
<< is_full_spec << FD << FixItHint::CreateInsertion(
|
||||
"function %0 defined in a header file; "
|
||||
"function definitions in header files can lead to ODR violations")
|
||||
<< FD << FixItHint::CreateInsertion(
|
||||
FD->getReturnTypeSourceRange().getBegin(), "inline ");
|
||||
} else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
|
||||
// Static data members of a class template are allowed.
|
||||
if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
|
||||
return;
|
||||
// Ignore instantiated static data members of classes.
|
||||
if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
|
||||
if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
|
||||
return;
|
||||
// Ignore variable definition within function scope.
|
||||
if (VD->hasLocalStorage() || VD->isStaticLocal())
|
||||
return;
|
||||
// Ignore inline variables.
|
||||
if (VD->isInline())
|
||||
return;
|
||||
|
||||
diag(VD->getLocation(),
|
||||
"variable %0 defined in a header file; "
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ForwardingReferenceOverloadCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace misc {
|
||||
|
||||
namespace {
|
||||
// Check if the given type is related to std::enable_if.
|
||||
AST_MATCHER(QualType, isEnableIf) {
|
||||
auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
|
||||
if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
|
||||
return false;
|
||||
}
|
||||
const NamedDecl *TypeDecl =
|
||||
Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
|
||||
return TypeDecl->isInStdNamespace() &&
|
||||
(TypeDecl->getName().equals("enable_if") ||
|
||||
TypeDecl->getName().equals("enable_if_t"));
|
||||
};
|
||||
const Type *BaseType = Node.getTypePtr();
|
||||
// Case: pointer or reference to enable_if.
|
||||
while (BaseType->isPointerType() || BaseType->isReferenceType()) {
|
||||
BaseType = BaseType->getPointeeType().getTypePtr();
|
||||
}
|
||||
// Case: type parameter dependent (enable_if<is_integral<T>>).
|
||||
if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
|
||||
BaseType = Dependent->getQualifier()->getAsType();
|
||||
}
|
||||
if (!BaseType)
|
||||
return false;
|
||||
if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
|
||||
return true; // Case: enable_if_t< >.
|
||||
} else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
|
||||
if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
|
||||
if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
|
||||
return true; // Case: enable_if< >::type.
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
|
||||
clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
|
||||
return Node.hasDefaultArgument() &&
|
||||
TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// Forwarding references require C++11 or later.
|
||||
if (!getLangOpts().CPlusPlus11)
|
||||
return;
|
||||
|
||||
auto ForwardingRefParm =
|
||||
parmVarDecl(
|
||||
hasType(qualType(rValueReferenceType(),
|
||||
references(templateTypeParmType(hasDeclaration(
|
||||
templateTypeParmDecl().bind("type-parm-decl")))),
|
||||
unless(references(isConstQualified())))))
|
||||
.bind("parm-var");
|
||||
|
||||
DeclarationMatcher findOverload =
|
||||
cxxConstructorDecl(
|
||||
hasParameter(0, ForwardingRefParm),
|
||||
unless(hasAnyParameter(
|
||||
// No warning: enable_if as constructor parameter.
|
||||
parmVarDecl(hasType(isEnableIf())))),
|
||||
unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
|
||||
// No warning: enable_if as type parameter.
|
||||
hasDefaultArgument(isEnableIf())))))))
|
||||
.bind("ctor");
|
||||
Finder->addMatcher(findOverload, this);
|
||||
}
|
||||
|
||||
void ForwardingReferenceOverloadCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
|
||||
const auto *TypeParmDecl =
|
||||
Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
|
||||
|
||||
// Get the FunctionDecl and FunctionTemplateDecl containing the function
|
||||
// parameter.
|
||||
const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
|
||||
if (!FuncForParam)
|
||||
return;
|
||||
const FunctionTemplateDecl *FuncTemplate =
|
||||
FuncForParam->getDescribedFunctionTemplate();
|
||||
if (!FuncTemplate)
|
||||
return;
|
||||
|
||||
// Check that the template type parameter belongs to the same function
|
||||
// template as the function parameter of that type. (This implies that type
|
||||
// deduction will happen on the type.)
|
||||
const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
|
||||
if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
|
||||
return;
|
||||
|
||||
// Every parameter after the first must have a default value.
|
||||
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
|
||||
for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
|
||||
if (!(*Iter)->hasDefaultArg())
|
||||
return;
|
||||
}
|
||||
bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
|
||||
DisabledMove = false;
|
||||
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
|
||||
if (OtherCtor->isCopyOrMoveConstructor()) {
|
||||
if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
|
||||
(OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
|
||||
else
|
||||
(OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
|
||||
}
|
||||
}
|
||||
bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
|
||||
bool Move = !DisabledMove || EnabledMove;
|
||||
if (!Copy && !Move)
|
||||
return;
|
||||
diag(Ctor->getLocation(),
|
||||
"constructor accepting a forwarding reference can "
|
||||
"hide the %select{copy|move|copy and move}0 constructor%s1")
|
||||
<< (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
|
||||
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
|
||||
if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
|
||||
OtherCtor->getAccess() != AS_private) {
|
||||
diag(OtherCtor->getLocation(),
|
||||
"%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
|
||||
<< OtherCtor->isMoveConstructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace misc
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,42 +0,0 @@
|
||||
//===--- ForwardingReferenceOverloadCheck.h - clang-tidy---------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace misc {
|
||||
|
||||
/// The checker looks for constructors that can act as copy or move constructors
|
||||
/// through their forwarding reference parameters. If a non const lvalue
|
||||
/// reference is passed to the constructor, the forwarding reference parameter
|
||||
/// can be a perfect match while the const reference parameter of the copy
|
||||
/// constructor can't. The forwarding reference constructor will be called,
|
||||
/// which can lead to confusion.
|
||||
/// For detailed description of this problem see: Scott Meyers, Effective Modern
|
||||
/// C++ Design, item 26.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html
|
||||
class ForwardingReferenceOverloadCheck : public ClangTidyCheck {
|
||||
public:
|
||||
ForwardingReferenceOverloadCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace misc
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
|
||||
@@ -18,50 +18,43 @@ namespace clang {
|
||||
namespace tidy {
|
||||
namespace misc {
|
||||
|
||||
namespace {
|
||||
AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
|
||||
}
|
||||
|
||||
void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// Only register the matchers for C++; the functionality currently does not
|
||||
// provide any benefit to other languages, despite being benign.
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
const auto EndCall =
|
||||
callExpr(
|
||||
callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))),
|
||||
hasArgument(
|
||||
1,
|
||||
anyOf(cxxConstructExpr(has(ignoringImplicit(
|
||||
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
|
||||
.bind("end")))),
|
||||
anything())))
|
||||
.bind("alg");
|
||||
const auto CheckForEndCall = hasArgument(
|
||||
1, anyOf(cxxConstructExpr(has(ignoringParenImpCasts(
|
||||
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
|
||||
.bind("InaccEndCall")))),
|
||||
anything()));
|
||||
|
||||
const auto DeclInStd = decl(isInStdNamespace());
|
||||
Finder->addMatcher(
|
||||
cxxMemberCallExpr(
|
||||
on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))),
|
||||
on(hasType(namedDecl(matchesName("^::std::")))),
|
||||
callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
|
||||
hasArgument(0, has(ignoringImplicit(
|
||||
anyOf(EndCall, has(ignoringImplicit(EndCall)))))),
|
||||
hasArgument(0, has(ignoringParenImpCasts(
|
||||
callExpr(callee(functionDecl(matchesName(
|
||||
"^::std::(remove(_if)?|unique)$"))),
|
||||
CheckForEndCall)
|
||||
.bind("InaccAlgCall")))),
|
||||
unless(isInTemplateInstantiation()))
|
||||
.bind("erase"),
|
||||
.bind("InaccErase"),
|
||||
this);
|
||||
}
|
||||
|
||||
void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *MemberCall =
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("InaccErase");
|
||||
const auto *EndExpr =
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("end");
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("InaccEndCall");
|
||||
const SourceLocation Loc = MemberCall->getLocStart();
|
||||
|
||||
FixItHint Hint;
|
||||
|
||||
if (!Loc.isMacroID() && EndExpr) {
|
||||
const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("alg");
|
||||
const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("InaccAlgCall");
|
||||
std::string ReplacementText = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
|
||||
*Result.SourceManager, getLangOpts());
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
//===--- LambdaFunctionNameCheck.cpp - clang-tidy--------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LambdaFunctionNameCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Lex/MacroInfo.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace misc {
|
||||
|
||||
namespace {
|
||||
|
||||
// Keep track of macro expansions that contain both __FILE__ and __LINE__. If
|
||||
// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
|
||||
// warning because __FILE__ and __LINE__ may be useful even if __func__ or
|
||||
// __FUNCTION__ is not, especially if the macro could be used in the context of
|
||||
// either a function body or a lambda body.
|
||||
class MacroExpansionsWithFileAndLine : public PPCallbacks {
|
||||
public:
|
||||
explicit MacroExpansionsWithFileAndLine(
|
||||
LambdaFunctionNameCheck::SourceRangeSet *SME)
|
||||
: SuppressMacroExpansions(SME) {}
|
||||
|
||||
void MacroExpands(const Token &MacroNameTok,
|
||||
const MacroDefinition &MD, SourceRange Range,
|
||||
const MacroArgs *Args) override {
|
||||
bool has_file = false;
|
||||
bool has_line = false;
|
||||
for (const auto& T : MD.getMacroInfo()->tokens()) {
|
||||
if (T.is(tok::identifier)) {
|
||||
StringRef IdentName = T.getIdentifierInfo()->getName();
|
||||
if (IdentName == "__FILE__") {
|
||||
has_file = true;
|
||||
} else if (IdentName == "__LINE__") {
|
||||
has_line = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_file && has_line) {
|
||||
SuppressMacroExpansions->insert(Range);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// Match on PredefinedExprs inside a lambda.
|
||||
Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
|
||||
this);
|
||||
}
|
||||
|
||||
void LambdaFunctionNameCheck::registerPPCallbacks(CompilerInstance &Compiler) {
|
||||
Compiler.getPreprocessor().addPPCallbacks(
|
||||
llvm::make_unique<MacroExpansionsWithFileAndLine>(
|
||||
&SuppressMacroExpansions));
|
||||
}
|
||||
|
||||
void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");
|
||||
if (E->getIdentType() != PredefinedExpr::Func &&
|
||||
E->getIdentType() != PredefinedExpr::Function) {
|
||||
// We don't care about other PredefinedExprs.
|
||||
return;
|
||||
}
|
||||
if (E->getLocation().isMacroID()) {
|
||||
auto ER =
|
||||
Result.SourceManager->getImmediateExpansionRange(E->getLocation());
|
||||
if (SuppressMacroExpansions.find(SourceRange(ER.first, ER.second)) !=
|
||||
SuppressMacroExpansions.end()) {
|
||||
// This is a macro expansion for which we should not warn.
|
||||
return;
|
||||
}
|
||||
}
|
||||
diag(E->getLocation(),
|
||||
"inside a lambda, '%0' expands to the name of the function call "
|
||||
"operator; consider capturing the name of the enclosing function "
|
||||
"explicitly")
|
||||
<< PredefinedExpr::getIdentTypeName(E->getIdentType());
|
||||
}
|
||||
|
||||
} // namespace misc
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,51 +0,0 @@
|
||||
//===--- LambdaFunctionNameCheck.h - clang-tidy------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace misc {
|
||||
|
||||
/// Detect when __func__ or __FUNCTION__ is being used from within a lambda. In
|
||||
/// that context, those expressions expand to the name of the call operator
|
||||
/// (i.e., `operator()`).
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html
|
||||
class LambdaFunctionNameCheck : public ClangTidyCheck {
|
||||
public:
|
||||
struct SourceRangeLessThan {
|
||||
bool operator()(const SourceRange &L, const SourceRange &R) const {
|
||||
if (L.getBegin() == R.getBegin()) {
|
||||
return L.getEnd() < R.getEnd();
|
||||
}
|
||||
return L.getBegin() < R.getBegin();
|
||||
}
|
||||
};
|
||||
using SourceRangeSet = std::set<SourceRange, SourceRangeLessThan>;
|
||||
|
||||
LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void registerPPCallbacks(CompilerInstance &Compiler) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
SourceRangeSet SuppressMacroExpansions;
|
||||
};
|
||||
|
||||
} // namespace misc
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
|
||||
@@ -185,7 +185,7 @@ void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
|
||||
continue;
|
||||
|
||||
// Only interested in macro arguments.
|
||||
if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
|
||||
if (MI->getArgumentNum(Tok.getIdentifierInfo()) < 0)
|
||||
continue;
|
||||
|
||||
// Argument is surrounded with parentheses/squares/braces/commas.
|
||||
|
||||
@@ -58,8 +58,8 @@ void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,
|
||||
}) != MI->tokens().end())
|
||||
return;
|
||||
|
||||
for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
|
||||
const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
|
||||
for (unsigned ArgNo = 0U; ArgNo < MI->getNumArgs(); ++ArgNo) {
|
||||
const IdentifierInfo *Arg = *(MI->arg_begin() + ArgNo);
|
||||
const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
|
||||
|
||||
if (hasSideEffects(ResultArgToks) &&
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
#include "DefinitionsInHeadersCheck.h"
|
||||
#include "FoldInitTypeCheck.h"
|
||||
#include "ForwardDeclarationNamespaceCheck.h"
|
||||
#include "ForwardingReferenceOverloadCheck.h"
|
||||
#include "InaccurateEraseCheck.h"
|
||||
#include "IncorrectRoundings.h"
|
||||
#include "InefficientAlgorithmCheck.h"
|
||||
#include "LambdaFunctionNameCheck.h"
|
||||
#include "MacroParenthesesCheck.h"
|
||||
#include "MacroRepeatedSideEffectsCheck.h"
|
||||
#include "MisplacedConstCheck.h"
|
||||
@@ -67,10 +65,6 @@ public:
|
||||
CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
|
||||
CheckFactories.registerCheck<AssertSideEffectCheck>(
|
||||
"misc-assert-side-effect");
|
||||
CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
|
||||
"misc-forwarding-reference-overload");
|
||||
CheckFactories.registerCheck<LambdaFunctionNameCheck>(
|
||||
"misc-lambda-function-name");
|
||||
CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
|
||||
CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
|
||||
"misc-unconventional-assign-operator");
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace misc {
|
||||
MisplacedWideningCastCheck::MisplacedWideningCastCheck(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
|
||||
CheckImplicitCasts(Options.get("CheckImplicitCasts", true)) {}
|
||||
|
||||
void MisplacedWideningCastCheck::storeOptions(
|
||||
ClangTidyOptions::OptionMap &Opts) {
|
||||
|
||||
@@ -73,18 +73,6 @@ void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
Arg->getType().isTriviallyCopyableType(*Result.Context);
|
||||
|
||||
if (IsConstArg || IsTriviallyCopyable) {
|
||||
if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
|
||||
// According to [expr.prim.lambda]p3, "whether the closure type is
|
||||
// trivially copyable" property can be changed by the implementation of
|
||||
// the language, so we shouldn't rely on it when issuing diagnostics.
|
||||
if (R->isLambda())
|
||||
return;
|
||||
// Don't warn when the type is not copyable.
|
||||
for (const auto *Ctor : R->ctors()) {
|
||||
if (Ctor->isCopyConstructor() && Ctor->isDeleted())
|
||||
return;
|
||||
}
|
||||
}
|
||||
bool IsVariable = isa<DeclRefExpr>(Arg);
|
||||
const auto *Var =
|
||||
IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
|
||||
|
||||
@@ -57,9 +57,6 @@ void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (QT.isTriviallyCopyableType(*Result.Context))
|
||||
return;
|
||||
|
||||
if (QT.isConstQualified())
|
||||
return;
|
||||
|
||||
const auto *RD = QT->getAsCXXRecordDecl();
|
||||
if (RD && RD->isTriviallyCopyable())
|
||||
return;
|
||||
|
||||
@@ -43,10 +43,6 @@ void NoexceptMoveConstructorCheck::check(
|
||||
}
|
||||
|
||||
const auto *ProtoType = Decl->getType()->getAs<FunctionProtoType>();
|
||||
|
||||
if (isUnresolvedExceptionSpec(ProtoType->getExceptionSpecType()))
|
||||
return;
|
||||
|
||||
switch (ProtoType->getNoexceptSpec(*Result.Context)) {
|
||||
case FunctionProtoType::NR_NoNoexcept:
|
||||
diag(Decl->getLocation(), "move %0s should be marked noexcept")
|
||||
|
||||
@@ -239,11 +239,6 @@ static bool rangesFullyCoverDomain(BinaryOperatorKind OpcodeLHS,
|
||||
(OpcodeRHS == BO_LT || OpcodeRHS == BO_LE))
|
||||
return true;
|
||||
|
||||
// Handle cases where constants are different but both ops are !=, like:
|
||||
// x != 5 || x != 10
|
||||
if (OpcodeLHS == BO_NE && OpcodeRHS == BO_NE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,9 @@ void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11))
|
||||
return;
|
||||
|
||||
auto NegatedString = unaryOperator(
|
||||
hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));
|
||||
auto IsAlwaysFalse =
|
||||
expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
|
||||
cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))
|
||||
cxxNullPtrLiteralExpr(), gnuNullExpr()))
|
||||
.bind("isAlwaysFalse");
|
||||
auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
|
||||
IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))
|
||||
|
||||
@@ -108,8 +108,7 @@ static bool isPossiblyBitMask(const EnumDecl *EnumDec) {
|
||||
|
||||
SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
|
||||
: ClangTidyCheck(Name, Context), StrictMode(Options.get("StrictMode", 0)) {}
|
||||
|
||||
void SuspiciousEnumUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "StrictMode", StrictMode);
|
||||
|
||||
@@ -30,7 +30,7 @@ void SuspiciousSemicolonCheck::registerMatchers(MatchFinder *Finder) {
|
||||
}
|
||||
|
||||
void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
|
||||
if (Result.Context->getDiagnostics().hasErrorOccurred())
|
||||
return;
|
||||
|
||||
const auto *Semicolon = Result.Nodes.getNodeAs<NullStmt>("semi");
|
||||
@@ -40,7 +40,7 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
return;
|
||||
|
||||
ASTContext &Ctxt = *Result.Context;
|
||||
auto Token = utils::lexer::getPreviousToken(Ctxt, LocStart);
|
||||
auto Token = utils::lexer::getPreviousNonCommentToken(Ctxt, LocStart);
|
||||
auto &SM = *Result.SourceManager;
|
||||
unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart);
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
|
||||
|
||||
void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
|
||||
const CXXCatchStmt *catchStmt, ASTContext &context) {
|
||||
const char *diagMsgCatchReference = "catch handler catches a pointer value; "
|
||||
"should throw a non-pointer value and "
|
||||
"catch by reference instead";
|
||||
if (!catchStmt)
|
||||
return;
|
||||
auto caughtType = catchStmt->getCaughtType();
|
||||
@@ -138,17 +141,12 @@ void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
|
||||
return;
|
||||
auto *varDecl = catchStmt->getExceptionDecl();
|
||||
if (const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
|
||||
const char *diagMsgCatchReference = "catch handler catches a pointer value; "
|
||||
"should throw a non-pointer value and "
|
||||
"catch by reference instead";
|
||||
// We do not diagnose when catching pointer to strings since we also allow
|
||||
// throwing string literals.
|
||||
if (!PT->getPointeeType()->isAnyCharacterType())
|
||||
diag(varDecl->getLocStart(), diagMsgCatchReference);
|
||||
} else if (!caughtType->isReferenceType()) {
|
||||
const char *diagMsgCatchReference = "catch handler catches by value; "
|
||||
"should catch by reference instead";
|
||||
// If it's not a pointer and not a reference then it must be caught "by
|
||||
// If it's not a pointer and not a reference then it must be thrown "by
|
||||
// value". In this case we should emit a diagnosis message unless the type
|
||||
// is trivial.
|
||||
if (!caughtType.isTrivialType(context))
|
||||
|
||||
@@ -58,10 +58,7 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
|
||||
this);
|
||||
|
||||
const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts(
|
||||
anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())),
|
||||
cxxOperatorCallExpr(argumentCountIs(1),
|
||||
callee(unresolvedLookupExpr()),
|
||||
hasArgument(0, cxxThisExpr())))))));
|
||||
unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))));
|
||||
const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType);
|
||||
|
||||
Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign))
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
|
||||
#include "UnusedParametersCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
@@ -29,10 +27,7 @@ bool isOverrideMethod(const FunctionDecl *Function) {
|
||||
} // namespace
|
||||
|
||||
void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
functionDecl(isDefinition(), hasBody(stmt()), hasAnyParameter(decl()))
|
||||
.bind("function"),
|
||||
this);
|
||||
Finder->addMatcher(functionDecl().bind("function"), this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -70,74 +65,21 @@ static FixItHint removeArgument(const MatchFinder::MatchResult &Result,
|
||||
Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));
|
||||
}
|
||||
|
||||
class UnusedParametersCheck::IndexerVisitor
|
||||
: public RecursiveASTVisitor<IndexerVisitor> {
|
||||
public:
|
||||
IndexerVisitor(TranslationUnitDecl *Top) { TraverseDecl(Top); }
|
||||
|
||||
const std::unordered_set<const CallExpr *> &
|
||||
getFnCalls(const FunctionDecl *Fn) {
|
||||
return Index[Fn->getCanonicalDecl()].Calls;
|
||||
}
|
||||
|
||||
const std::unordered_set<const DeclRefExpr *> &
|
||||
getOtherRefs(const FunctionDecl *Fn) {
|
||||
return Index[Fn->getCanonicalDecl()].OtherRefs;
|
||||
}
|
||||
|
||||
bool shouldTraversePostOrder() const { return true; }
|
||||
|
||||
bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {
|
||||
if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
|
||||
Fn = Fn->getCanonicalDecl();
|
||||
Index[Fn].OtherRefs.insert(DeclRef);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalkUpFromCallExpr(CallExpr *Call) {
|
||||
if (const auto *Fn =
|
||||
dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
|
||||
Fn = Fn->getCanonicalDecl();
|
||||
if (const auto *Ref =
|
||||
dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {
|
||||
Index[Fn].OtherRefs.erase(Ref);
|
||||
}
|
||||
Index[Fn].Calls.insert(Call);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct IndexEntry {
|
||||
std::unordered_set<const CallExpr *> Calls;
|
||||
std::unordered_set<const DeclRefExpr *> OtherRefs;
|
||||
};
|
||||
|
||||
std::unordered_map<const FunctionDecl *, IndexEntry> Index;
|
||||
};
|
||||
|
||||
UnusedParametersCheck::~UnusedParametersCheck() = default;
|
||||
|
||||
UnusedParametersCheck::UnusedParametersCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
|
||||
void UnusedParametersCheck::warnOnUnusedParameter(
|
||||
const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
|
||||
unsigned ParamIndex) {
|
||||
const auto *Param = Function->getParamDecl(ParamIndex);
|
||||
auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;
|
||||
|
||||
if (!Indexer) {
|
||||
Indexer = llvm::make_unique<IndexerVisitor>(
|
||||
Result.Context->getTranslationUnitDecl());
|
||||
}
|
||||
auto DeclRefExpr =
|
||||
declRefExpr(to(equalsNode(Function)),
|
||||
unless(hasAncestor(callExpr(callee(equalsNode(Function))))));
|
||||
|
||||
// Comment out parameter name for non-local functions.
|
||||
if (Function->isExternallyVisible() ||
|
||||
!Result.SourceManager->isInMainFile(Function->getLocation()) ||
|
||||
!Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function)) {
|
||||
!ast_matchers::match(DeclRefExpr, *Result.Context).empty() ||
|
||||
isOverrideMethod(Function)) {
|
||||
SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
|
||||
// Note: We always add a space before the '/*' to not accidentally create a
|
||||
// '*/*' for pointer types, which doesn't start a comment. clang-format will
|
||||
@@ -153,13 +95,19 @@ void UnusedParametersCheck::warnOnUnusedParameter(
|
||||
MyDiag << removeParameter(Result, FD, ParamIndex);
|
||||
|
||||
// Fix all call sites.
|
||||
for (const auto *Call : Indexer->getFnCalls(Function))
|
||||
MyDiag << removeArgument(Result, Call, ParamIndex);
|
||||
auto CallMatches = ast_matchers::match(
|
||||
decl(forEachDescendant(
|
||||
callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))),
|
||||
*Result.Context->getTranslationUnitDecl(), *Result.Context);
|
||||
for (const auto &Match : CallMatches)
|
||||
MyDiag << removeArgument(Result, Match.getNodeAs<CallExpr>("x"),
|
||||
ParamIndex);
|
||||
}
|
||||
|
||||
void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
|
||||
if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
|
||||
if (!Function->doesThisDeclarationHaveABody() ||
|
||||
!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
|
||||
return;
|
||||
if (const auto *Method = dyn_cast<CXXMethodDecl>(Function))
|
||||
if (Method->isLambdaStaticInvoker())
|
||||
|
||||
@@ -20,15 +20,12 @@ namespace misc {
|
||||
/// turned on.
|
||||
class UnusedParametersCheck : public ClangTidyCheck {
|
||||
public:
|
||||
UnusedParametersCheck(StringRef Name, ClangTidyContext *Context);
|
||||
~UnusedParametersCheck();
|
||||
UnusedParametersCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
class IndexerVisitor;
|
||||
std::unique_ptr<IndexerVisitor> Indexer;
|
||||
|
||||
void
|
||||
warnOnUnusedParameter(const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const FunctionDecl *Function, unsigned ParamIndex);
|
||||
|
||||
@@ -48,9 +48,6 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
|
||||
}
|
||||
|
||||
void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
|
||||
return;
|
||||
|
||||
if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
|
||||
// Ignores using-declarations defined in macros.
|
||||
if (Using->getLocation().isMacroID())
|
||||
|
||||
@@ -384,13 +384,6 @@ void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// the direct ancestor of the std::move() that isn't one of the node
|
||||
// types ignored by ignoringParenImpCasts().
|
||||
stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
|
||||
// Don't allow an InitListExpr to be the moving call. An InitListExpr
|
||||
// has both a syntactic and a semantic form, and the parent-child
|
||||
// relationships are different between the two. This could cause an
|
||||
// InitListExpr to be analyzed as the moving call in addition to the
|
||||
// Expr that we actually want, resulting in two diagnostics with
|
||||
// different code locations for the same move.
|
||||
unless(initListExpr()),
|
||||
unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
|
||||
.bind("moving-call"),
|
||||
this);
|
||||
@@ -405,7 +398,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
|
||||
const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
|
||||
|
||||
if (!MovingCall || !MovingCall->getExprLoc().isValid())
|
||||
if (!MovingCall)
|
||||
MovingCall = CallMove;
|
||||
|
||||
Stmt *FunctionBody = nullptr;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user