Compare commits
262 Commits
llvmorg-5.
...
llvmorg-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d2c8a6d22 | ||
|
|
e0cba6fbc2 | ||
|
|
a3a80c717d | ||
|
|
4dbd3c9853 | ||
|
|
b9f83dd2e7 | ||
|
|
4351822ac5 | ||
|
|
60d1be210d | ||
|
|
af61ee821b | ||
|
|
fedffd766d | ||
|
|
101b70d6d1 | ||
|
|
77105d9817 | ||
|
|
2a764cb0b8 | ||
|
|
a35f06ccc7 | ||
|
|
ecdc943a5a | ||
|
|
c0ab938bba | ||
|
|
81155380be | ||
|
|
8f20452cf2 | ||
|
|
84bc83fd78 | ||
|
|
ea32d3a23d | ||
|
|
385d0dd52d | ||
|
|
6ad55e925a | ||
|
|
30a0597a0d | ||
|
|
f2f09fb329 | ||
|
|
e354bf82ba | ||
|
|
651925a496 | ||
|
|
628944dbd8 | ||
|
|
4e4e391da8 | ||
|
|
b6574c9047 | ||
|
|
be4dc0e165 | ||
|
|
6a9d7718ed | ||
|
|
fa81086df9 | ||
|
|
6c5819e34b | ||
|
|
07f06bbd97 | ||
|
|
9b24c0ffae | ||
|
|
9abeb69615 | ||
|
|
39ba7e0552 | ||
|
|
d31f4c6106 | ||
|
|
ea9e182ade | ||
|
|
dffdec69e1 | ||
|
|
f03c72a577 | ||
|
|
28be27a04e | ||
|
|
92215b0dd0 | ||
|
|
9ef730ae0a | ||
|
|
2d9a3bee4c | ||
|
|
ef1c6718fd | ||
|
|
b20996e713 | ||
|
|
e228322c1c | ||
|
|
4c9e37fcc4 | ||
|
|
9ed146d6a8 | ||
|
|
7a61678035 | ||
|
|
64e66b01b2 | ||
|
|
6e3c56e785 | ||
|
|
37c07a6294 | ||
|
|
085abb5d1d | ||
|
|
92f45d1333 | ||
|
|
7e841d753b | ||
|
|
18bc2ed14a | ||
|
|
93e02253d6 | ||
|
|
973b3c6f29 | ||
|
|
1d2a9aa20d | ||
|
|
f0625368ba | ||
|
|
50c62001ca | ||
|
|
e4301bee75 | ||
|
|
8cdf2c90f0 | ||
|
|
b29f5b907f | ||
|
|
8f588dd56e | ||
|
|
4be2d4c3af | ||
|
|
f10332cab5 | ||
|
|
bb8359ff25 | ||
|
|
d8ce57ff2f | ||
|
|
7585cbd858 | ||
|
|
8340bdc9d2 | ||
|
|
e6401e289e | ||
|
|
75d716d345 | ||
|
|
df22cb2bc3 | ||
|
|
40ddbe66df | ||
|
|
901f3d19d8 | ||
|
|
e4565b26dc | ||
|
|
30d2ca75b2 | ||
|
|
84a7df7eaf | ||
|
|
2234ce5751 | ||
|
|
88c94367bf | ||
|
|
81624a1b81 | ||
|
|
55bcb7bea7 | ||
|
|
6b412de086 | ||
|
|
5cf014d867 | ||
|
|
9d2aa79b33 | ||
|
|
0dae6cdb69 | ||
|
|
6d4022f7f6 | ||
|
|
372b086a97 | ||
|
|
1486635524 | ||
|
|
728f55c9ac | ||
|
|
5ef6392a23 | ||
|
|
de7b942bde | ||
|
|
639afbbbf9 | ||
|
|
7ed3d6575a | ||
|
|
685924c788 | ||
|
|
b2d7415042 | ||
|
|
08c8744ae8 | ||
|
|
f5bcfc4fc2 | ||
|
|
d2d30b50c1 | ||
|
|
d51f07da55 | ||
|
|
08d315f5c5 | ||
|
|
e9f61a123a | ||
|
|
f6bf66347d | ||
|
|
2646c1259d | ||
|
|
cc13b7042d | ||
|
|
64f980a01b | ||
|
|
3f173e827a | ||
|
|
7863d07ace | ||
|
|
24b3307b8a | ||
|
|
b16d386b54 | ||
|
|
4feb6fe3a3 | ||
|
|
2c03fbfd18 | ||
|
|
a33f8c9ee0 | ||
|
|
2a46c56ee9 | ||
|
|
492e5db50e | ||
|
|
a90d20129d | ||
|
|
6c9c1dcd65 | ||
|
|
6f9000213b | ||
|
|
4ce9a1a5f5 | ||
|
|
942a2456ac | ||
|
|
3449325506 | ||
|
|
2bace4c6ed | ||
|
|
55b9030c31 | ||
|
|
0cb1d7aac0 | ||
|
|
3915840558 | ||
|
|
e222c9e1a4 | ||
|
|
a5166ef900 | ||
|
|
98def3001e | ||
|
|
1c81157a21 | ||
|
|
322fcb0c9b | ||
|
|
aacbb87535 | ||
|
|
06d5a61a5d | ||
|
|
971e92b417 | ||
|
|
4b156f75c9 | ||
|
|
a49d21edfb | ||
|
|
2bf62cbd91 | ||
|
|
484f12f661 | ||
|
|
f015491478 | ||
|
|
fc44467537 | ||
|
|
c740b0f251 | ||
|
|
d355b771d6 | ||
|
|
08cbe67bbf | ||
|
|
3b19a92b75 | ||
|
|
40a62e6d56 | ||
|
|
42bd5807a3 | ||
|
|
85e644f424 | ||
|
|
83f8b92b34 | ||
|
|
d2cd7c5902 | ||
|
|
76d2581450 | ||
|
|
13225a76de | ||
|
|
368a2bed7b | ||
|
|
95959916e7 | ||
|
|
7370495d29 | ||
|
|
813999532a | ||
|
|
4c7a06eb9c | ||
|
|
61c4ba945c | ||
|
|
562712ae80 | ||
|
|
ef492f7c44 | ||
|
|
3c78fb290a | ||
|
|
4262851e1b | ||
|
|
ff38e3da85 | ||
|
|
6bdbb229c0 | ||
|
|
fcd989333f | ||
|
|
e82b3c8f5f | ||
|
|
bc626ae563 | ||
|
|
4de5839c99 | ||
|
|
5ba548388f | ||
|
|
6036ee499c | ||
|
|
0ee1c0452b | ||
|
|
5a09dfa964 | ||
|
|
668c2339e1 | ||
|
|
2ffe33d726 | ||
|
|
3f0867ead3 | ||
|
|
c76f4b6602 | ||
|
|
28e01d94e1 | ||
|
|
192e2e2ca9 | ||
|
|
c25f426db2 | ||
|
|
66671a5b81 | ||
|
|
21bac108cf | ||
|
|
b625dc1f9e | ||
|
|
ca6c6b95d5 | ||
|
|
b2459060c8 | ||
|
|
0abd0b2d75 | ||
|
|
3e548d31e8 | ||
|
|
f48d9e60c0 | ||
|
|
904a8f6ac8 | ||
|
|
20b8eb19ee | ||
|
|
13b8dc2040 | ||
|
|
6a183487cc | ||
|
|
869abd3c4a | ||
|
|
d7eab8630c | ||
|
|
3df9c41538 | ||
|
|
8338208af2 | ||
|
|
b33695227a | ||
|
|
444a522e5d | ||
|
|
9b74891142 | ||
|
|
5f262c819d | ||
|
|
569deba640 | ||
|
|
2c74c29d92 | ||
|
|
c369220352 | ||
|
|
001a4b73cd | ||
|
|
405f701d60 | ||
|
|
dfb3dabbf1 | ||
|
|
c326e800f2 | ||
|
|
e3b981281d | ||
|
|
12646940aa | ||
|
|
fe62b2b0a7 | ||
|
|
f6c040eca0 | ||
|
|
317fbe2805 | ||
|
|
fe82a366df | ||
|
|
75c7375ee8 | ||
|
|
ec68271f06 | ||
|
|
4bd3da119e | ||
|
|
009ef32ff2 | ||
|
|
3869e6a789 | ||
|
|
d8f7f82712 | ||
|
|
aa124fa230 | ||
|
|
23513f4039 | ||
|
|
54f837917c | ||
|
|
800eae7e0b | ||
|
|
cdd225435f | ||
|
|
cd8406104c | ||
|
|
87d61cf9aa | ||
|
|
0e9a6b6dc0 | ||
|
|
832d6a68de | ||
|
|
b0ca78fb51 | ||
|
|
fc546f8627 | ||
|
|
6b823ba449 | ||
|
|
9a45867caf | ||
|
|
94f35ea76f | ||
|
|
014e7b834f | ||
|
|
0cec800e8d | ||
|
|
6323faf644 | ||
|
|
d180cc9112 | ||
|
|
81d4e6005c | ||
|
|
062184f19b | ||
|
|
b3ff622079 | ||
|
|
6207270016 | ||
|
|
c7bfb6d36a | ||
|
|
60c15ed5ff | ||
|
|
5975e88f88 | ||
|
|
b8f0391a7c | ||
|
|
dddf67f5d5 | ||
|
|
3c89b2c953 | ||
|
|
ffd65c839c | ||
|
|
aa9583d2ed | ||
|
|
5b15c0f87d | ||
|
|
2fbdb127ea | ||
|
|
6b7ac2a98c | ||
|
|
e1dceeca1a | ||
|
|
00cc493db6 | ||
|
|
9149bf6772 | ||
|
|
b4a2b91353 | ||
|
|
2cabf384c2 | ||
|
|
a0911d3f03 | ||
|
|
98ab17b1b3 | ||
|
|
b79ae58145 | ||
|
|
6e814ffb39 | ||
|
|
e25ce394b0 | ||
|
|
93c80e5bff |
@@ -1,6 +1,5 @@
|
|||||||
add_subdirectory(clang-apply-replacements)
|
add_subdirectory(clang-apply-replacements)
|
||||||
add_subdirectory(clang-modernize)
|
add_subdirectory(clang-modernize)
|
||||||
add_subdirectory(clang-rename)
|
|
||||||
add_subdirectory(modularize)
|
add_subdirectory(modularize)
|
||||||
add_subdirectory(module-map-checker)
|
add_subdirectory(module-map-checker)
|
||||||
add_subdirectory(remove-cstr-calls)
|
add_subdirectory(remove-cstr-calls)
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ include $(CLANG_LEVEL)/../../Makefile.config
|
|||||||
|
|
||||||
PARALLEL_DIRS := remove-cstr-calls tool-template modularize \
|
PARALLEL_DIRS := remove-cstr-calls tool-template modularize \
|
||||||
module-map-checker pp-trace
|
module-map-checker pp-trace
|
||||||
DIRS := clang-apply-replacements clang-modernize clang-rename clang-tidy \
|
DIRS := clang-apply-replacements clang-modernize clang-tidy clang-query \
|
||||||
clang-query unittests
|
unittests
|
||||||
|
|
||||||
include $(CLANG_LEVEL)/Makefile
|
include $(CLANG_LEVEL)/Makefile
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ add_clang_library(clangApplyReplacements
|
|||||||
lib/Tooling/ApplyReplacements.cpp
|
lib/Tooling/ApplyReplacements.cpp
|
||||||
|
|
||||||
LINK_LIBS
|
LINK_LIBS
|
||||||
clangAST
|
|
||||||
clangBasic
|
clangBasic
|
||||||
clangRewrite
|
clangRewrite
|
||||||
clangToolingCore
|
clangTooling
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|||||||
@@ -45,9 +45,8 @@ TUReplacements;
|
|||||||
typedef std::vector<std::string> TUReplacementFiles;
|
typedef std::vector<std::string> TUReplacementFiles;
|
||||||
|
|
||||||
/// \brief Map mapping file name to Replacements targeting that file.
|
/// \brief Map mapping file name to Replacements targeting that file.
|
||||||
typedef llvm::DenseMap<const clang::FileEntry *,
|
typedef llvm::StringMap<std::vector<clang::tooling::Replacement> >
|
||||||
std::vector<clang::tooling::Replacement>>
|
FileToReplacementsMap;
|
||||||
FileToReplacementsMap;
|
|
||||||
|
|
||||||
/// \brief Recursively descends through a directory structure rooted at \p
|
/// \brief Recursively descends through a directory structure rooted at \p
|
||||||
/// Directory and attempts to deserialize *.yaml files as
|
/// Directory and attempts to deserialize *.yaml files as
|
||||||
|
|||||||
@@ -98,26 +98,29 @@ static void reportConflict(
|
|||||||
|
|
||||||
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
|
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
|
||||||
errs() << "The following changes conflict:\n";
|
errs() << "The following changes conflict:\n";
|
||||||
for (const tooling::Replacement &R : ConflictingReplacements) {
|
for (const tooling::Replacement *I = ConflictingReplacements.begin(),
|
||||||
if (R.getLength() == 0) {
|
*E = ConflictingReplacements.end();
|
||||||
errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
|
I != E; ++I) {
|
||||||
<< SM.getColumnNumber(FID, R.getOffset()) << " "
|
if (I->getLength() == 0) {
|
||||||
<< R.getReplacementText() << "\n";
|
errs() << " Insert at " << SM.getLineNumber(FID, I->getOffset()) << ":"
|
||||||
|
<< SM.getColumnNumber(FID, I->getOffset()) << " "
|
||||||
|
<< I->getReplacementText() << "\n";
|
||||||
} else {
|
} else {
|
||||||
if (R.getReplacementText().empty())
|
if (I->getReplacementText().empty())
|
||||||
errs() << " Remove ";
|
errs() << " Remove ";
|
||||||
else
|
else
|
||||||
errs() << " Replace ";
|
errs() << " Replace ";
|
||||||
|
|
||||||
errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
|
errs() << SM.getLineNumber(FID, I->getOffset()) << ":"
|
||||||
<< SM.getColumnNumber(FID, R.getOffset()) << "-"
|
<< SM.getColumnNumber(FID, I->getOffset()) << "-"
|
||||||
<< SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
|
<< SM.getLineNumber(FID, I->getOffset() + I->getLength() - 1)
|
||||||
<< SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
|
<< ":"
|
||||||
|
<< SM.getColumnNumber(FID, I->getOffset() + I->getLength() - 1);
|
||||||
|
|
||||||
if (R.getReplacementText().empty())
|
if (I->getReplacementText().empty())
|
||||||
errs() << "\n";
|
errs() << "\n";
|
||||||
else
|
else
|
||||||
errs() << " with \"" << R.getReplacementText() << "\"\n";
|
errs() << " with \"" << I->getReplacementText() << "\"\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,24 +140,33 @@ static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
|
|||||||
SourceManager &SM) {
|
SourceManager &SM) {
|
||||||
bool conflictsFound = false;
|
bool conflictsFound = false;
|
||||||
|
|
||||||
for (auto &FileAndReplacements : Replacements) {
|
for (FileToReplacementsMap::iterator I = Replacements.begin(),
|
||||||
const FileEntry *Entry = FileAndReplacements.first;
|
E = Replacements.end();
|
||||||
auto &Replacements = FileAndReplacements.second;
|
I != E; ++I) {
|
||||||
assert(Entry != nullptr && "No file entry!");
|
|
||||||
|
const FileEntry *Entry = SM.getFileManager().getFile(I->getKey());
|
||||||
|
if (!Entry) {
|
||||||
|
errs() << "Described file '" << I->getKey()
|
||||||
|
<< "' doesn't exist. Ignoring...\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<tooling::Range> Conflicts;
|
std::vector<tooling::Range> Conflicts;
|
||||||
tooling::deduplicate(FileAndReplacements.second, Conflicts);
|
tooling::deduplicate(I->getValue(), Conflicts);
|
||||||
|
|
||||||
if (Conflicts.empty())
|
if (Conflicts.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
conflictsFound = true;
|
conflictsFound = true;
|
||||||
|
|
||||||
errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
|
errs() << "There are conflicting changes to " << I->getKey() << ":\n";
|
||||||
|
|
||||||
for (const tooling::Range &Conflict : Conflicts) {
|
for (std::vector<tooling::Range>::const_iterator
|
||||||
auto ConflictingReplacements = llvm::makeArrayRef(
|
ConflictI = Conflicts.begin(),
|
||||||
&Replacements[Conflict.getOffset()], Conflict.getLength());
|
ConflictE = Conflicts.end();
|
||||||
|
ConflictI != ConflictE; ++ConflictI) {
|
||||||
|
ArrayRef<tooling::Replacement> ConflictingReplacements(
|
||||||
|
&I->getValue()[ConflictI->getOffset()], ConflictI->getLength());
|
||||||
reportConflict(Entry, ConflictingReplacements, SM);
|
reportConflict(Entry, ConflictingReplacements, SM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,20 +179,14 @@ bool mergeAndDeduplicate(const TUReplacements &TUs,
|
|||||||
clang::SourceManager &SM) {
|
clang::SourceManager &SM) {
|
||||||
|
|
||||||
// Group all replacements by target file.
|
// Group all replacements by target file.
|
||||||
std::set<StringRef> Warned;
|
for (TUReplacements::const_iterator TUI = TUs.begin(), TUE = TUs.end();
|
||||||
for (const auto &TU : TUs) {
|
TUI != TUE; ++TUI)
|
||||||
for (const tooling::Replacement &R : TU.Replacements) {
|
for (std::vector<tooling::Replacement>::const_iterator
|
||||||
// Use the file manager to deduplicate paths. FileEntries are
|
RI = TUI->Replacements.begin(),
|
||||||
// automatically canonicalized.
|
RE = TUI->Replacements.end();
|
||||||
const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
|
RI != RE; ++RI)
|
||||||
if (!Entry && Warned.insert(R.getFilePath()).second) {
|
GroupedReplacements[RI->getFilePath()].push_back(*RI);
|
||||||
errs() << "Described file '" << R.getFilePath()
|
|
||||||
<< "' doesn't exist. Ignoring...\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GroupedReplacements[Entry].push_back(R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask clang to deduplicate and report conflicts.
|
// Ask clang to deduplicate and report conflicts.
|
||||||
if (deduplicateAndDetectConflicts(GroupedReplacements, SM))
|
if (deduplicateAndDetectConflicts(GroupedReplacements, SM))
|
||||||
@@ -198,8 +204,10 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
|
|||||||
// data structure for applying replacements. Rewriter certainly doesn't care.
|
// data structure for applying replacements. Rewriter certainly doesn't care.
|
||||||
// However, until we nail down the design of ReplacementGroups, might as well
|
// However, until we nail down the design of ReplacementGroups, might as well
|
||||||
// leave this as is.
|
// leave this as is.
|
||||||
for (const auto &FileAndReplacements : GroupedReplacements) {
|
for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
|
||||||
if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites))
|
E = GroupedReplacements.end();
|
||||||
|
I != E; ++I) {
|
||||||
|
if (!tooling::applyAllReplacements(I->getValue(), Rewrites))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +223,11 @@ RangeVector calculateChangedRanges(
|
|||||||
// NOTE: This is O(n^2) in the number of replacements. If this starts to
|
// NOTE: This is O(n^2) in the number of replacements. If this starts to
|
||||||
// become a problem inline shiftedCodePosition() here and do shifts in a
|
// become a problem inline shiftedCodePosition() here and do shifts in a
|
||||||
// single run through this loop.
|
// single run through this loop.
|
||||||
for (const tooling::Replacement &R : Replaces) {
|
for (std::vector<clang::tooling::Replacement>::const_iterator
|
||||||
|
I = Replaces.begin(),
|
||||||
|
E = Replaces.end();
|
||||||
|
I != E; ++I) {
|
||||||
|
const tooling::Replacement &R = *I;
|
||||||
unsigned Offset = tooling::shiftedCodePosition(Replaces, R.getOffset());
|
unsigned Offset = tooling::shiftedCodePosition(Replaces, R.getOffset());
|
||||||
unsigned Length = R.getReplacementText().size();
|
unsigned Length = R.getReplacementText().size();
|
||||||
|
|
||||||
@@ -233,10 +245,11 @@ bool writeFiles(const clang::Rewriter &Rewrites) {
|
|||||||
const char *FileName =
|
const char *FileName =
|
||||||
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
|
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
|
||||||
|
|
||||||
std::error_code EC;
|
std::string ErrorInfo;
|
||||||
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
|
|
||||||
if (EC) {
|
llvm::raw_fd_ostream FileStream(FileName, ErrorInfo, llvm::sys::fs::F_Text);
|
||||||
errs() << "Warning: Could not write to " << EC.message() << "\n";
|
if (!ErrorInfo.empty()) {
|
||||||
|
errs() << "Warning: Could not write to " << FileName << "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
BufferI->second.write(FileStream);
|
BufferI->second.write(FileStream);
|
||||||
@@ -248,12 +261,13 @@ bool writeFiles(const clang::Rewriter &Rewrites) {
|
|||||||
bool deleteReplacementFiles(const TUReplacementFiles &Files,
|
bool deleteReplacementFiles(const TUReplacementFiles &Files,
|
||||||
clang::DiagnosticsEngine &Diagnostics) {
|
clang::DiagnosticsEngine &Diagnostics) {
|
||||||
bool Success = true;
|
bool Success = true;
|
||||||
for (const auto &Filename : Files) {
|
for (TUReplacementFiles::const_iterator I = Files.begin(), E = Files.end();
|
||||||
std::error_code Error = llvm::sys::fs::remove(Filename);
|
I != E; ++I) {
|
||||||
|
std::error_code Error = llvm::sys::fs::remove(*I);
|
||||||
if (Error) {
|
if (Error) {
|
||||||
Success = false;
|
Success = false;
|
||||||
// FIXME: Use Diagnostics for outputting errors.
|
// FIXME: Use Diagnostics for outputting errors.
|
||||||
errs() << "Error deleting file: " << Filename << "\n";
|
errs() << "Error deleting file: " << *I << "\n";
|
||||||
errs() << Error.message() << "\n";
|
errs() << Error.message() << "\n";
|
||||||
errs() << "Please delete the file manually\n";
|
errs() << "Please delete the file manually\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ target_link_libraries(clang-apply-replacements
|
|||||||
clangBasic
|
clangBasic
|
||||||
clangFormat
|
clangFormat
|
||||||
clangRewrite
|
clangRewrite
|
||||||
clangToolingCore
|
clangTooling
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS clang-apply-replacements
|
install(TARGETS clang-apply-replacements
|
||||||
|
|||||||
@@ -199,10 +199,11 @@ int main(int argc, char **argv) {
|
|||||||
// Only include our options in -help output.
|
// Only include our options in -help output.
|
||||||
StringMap<cl::Option*> OptMap;
|
StringMap<cl::Option*> OptMap;
|
||||||
cl::getRegisteredOptions(OptMap);
|
cl::getRegisteredOptions(OptMap);
|
||||||
const char **EndOpts = std::end(OptionsToShow);
|
const char **EndOpts = OptionsToShow + array_lengthof(OptionsToShow);
|
||||||
for (const auto &Opt : OptMap) {
|
for (StringMap<cl::Option *>::iterator I = OptMap.begin(), E = OptMap.end();
|
||||||
if (std::find(OptionsToShow, EndOpts, Opt.getKey()) == EndOpts)
|
I != E; ++I) {
|
||||||
Opt.getValue()->setHiddenFlag(cl::ReallyHidden);
|
if (std::find(OptionsToShow, EndOpts, I->getKey()) == EndOpts)
|
||||||
|
I->getValue()->setHiddenFlag(cl::ReallyHidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
cl::SetVersionPrinter(&printVersion);
|
cl::SetVersionPrinter(&printVersion);
|
||||||
@@ -245,34 +246,36 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
Rewriter ReplacementsRewriter(SM, LangOptions());
|
Rewriter ReplacementsRewriter(SM, LangOptions());
|
||||||
|
|
||||||
for (const auto &FileAndReplacements : GroupedReplacements) {
|
for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
|
||||||
// This shouldn't happen but if a file somehow has no replacements skip to
|
E = GroupedReplacements.end();
|
||||||
// next file.
|
I != E; ++I) {
|
||||||
if (FileAndReplacements.second.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string NewFileData;
|
std::string NewFileData;
|
||||||
const char *FileName = FileAndReplacements.first->getName();
|
|
||||||
if (!applyReplacements(FileAndReplacements.second, NewFileData,
|
// This shouldn't happen but if a file somehow has no replacements skip to
|
||||||
Diagnostics)) {
|
// next file.
|
||||||
errs() << "Failed to apply replacements to " << FileName << "\n";
|
if (I->getValue().empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!applyReplacements(I->getValue(), NewFileData, Diagnostics)) {
|
||||||
|
errs() << "Failed to apply replacements to " << I->getKey() << "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply formatting if requested.
|
// Apply formatting if requested.
|
||||||
if (DoFormat &&
|
if (DoFormat && !applyFormatting(I->getValue(), NewFileData, NewFileData,
|
||||||
!applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
|
FormatStyle, Diagnostics)) {
|
||||||
FormatStyle, Diagnostics)) {
|
errs() << "Failed to apply reformatting replacements for " << I->getKey()
|
||||||
errs() << "Failed to apply reformatting replacements for " << FileName
|
|
||||||
<< "\n";
|
<< "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write new file to disk
|
// Write new file to disk
|
||||||
std::error_code EC;
|
std::string ErrorInfo;
|
||||||
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
|
llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo,
|
||||||
if (EC) {
|
llvm::sys::fs::F_Text);
|
||||||
llvm::errs() << "Could not open " << FileName << " for writing\n";
|
if (!ErrorInfo.empty()) {
|
||||||
|
llvm::errs() << "Could not open " << I->getKey() << " for writing\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ TOOL_NO_EXPORTS = 1
|
|||||||
SOURCES = ClangApplyReplacementsMain.cpp
|
SOURCES = ClangApplyReplacementsMain.cpp
|
||||||
|
|
||||||
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
|
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
|
||||||
USEDLIBS = clangApplyReplacements.a clangFormat.a \
|
USEDLIBS = clangApplyReplacements.a clangFormat.a clangTooling.a clangFrontend.a \
|
||||||
clangTooling.a clangToolingCore.a clangFrontend.a \
|
|
||||||
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
|
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
|
||||||
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
|
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
|
||||||
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
|
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ int AddOverrideTransform::apply(const CompilationDatabase &Database,
|
|||||||
// Make Fixer available to handleBeginSource().
|
// Make Fixer available to handleBeginSource().
|
||||||
this->Fixer = &Fixer;
|
this->Fixer = &Fixer;
|
||||||
|
|
||||||
if (int result = AddOverrideTool.run(createActionFactory(Finder).get())) {
|
if (int result = AddOverrideTool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,4 @@ add_clang_library(modernizeCore
|
|||||||
clangFrontend
|
clangFrontend
|
||||||
clangLex
|
clangLex
|
||||||
clangTooling
|
clangTooling
|
||||||
clangToolingCore
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ class IncludeDirectivesPPCallback : public clang::PPCallbacks {
|
|||||||
public:
|
public:
|
||||||
IncludeDirectivesPPCallback(IncludeDirectives *Self)
|
IncludeDirectivesPPCallback(IncludeDirectives *Self)
|
||||||
: Self(Self), Guard(nullptr) {}
|
: Self(Self), Guard(nullptr) {}
|
||||||
virtual ~IncludeDirectivesPPCallback() {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual ~IncludeDirectivesPPCallback() {}
|
||||||
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
||||||
StringRef FileName, bool IsAngled,
|
StringRef FileName, bool IsAngled,
|
||||||
CharSourceRange FilenameRange, const FileEntry *File,
|
CharSourceRange FilenameRange, const FileEntry *File,
|
||||||
@@ -116,8 +116,7 @@ private:
|
|||||||
// checking for equality because it can also be part of the preamble if the
|
// checking for equality because it can also be part of the preamble if the
|
||||||
// preamble is the whole file.
|
// preamble is the whole file.
|
||||||
unsigned Preamble =
|
unsigned Preamble =
|
||||||
Lexer::ComputePreamble(SM.getBuffer(Guard.FID)->getBuffer(), LangOpts)
|
Lexer::ComputePreamble(SM.getBuffer(Guard.FID), LangOpts).first;
|
||||||
.first;
|
|
||||||
unsigned IfndefOffset = SM.getFileOffset(Guard.IfndefLoc);
|
unsigned IfndefOffset = SM.getFileOffset(Guard.IfndefLoc);
|
||||||
if (IfndefOffset > (Preamble + 1))
|
if (IfndefOffset > (Preamble + 1))
|
||||||
return;
|
return;
|
||||||
@@ -178,7 +177,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
|
virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
|
||||||
Guard->Count++;
|
Guard->Count++;
|
||||||
|
|
||||||
// If it's the #endif corresponding to the top-most #ifndef
|
// If it's the #endif corresponding to the top-most #ifndef
|
||||||
@@ -220,7 +219,7 @@ private:
|
|||||||
const MacroDirective *) override {
|
const MacroDirective *) override {
|
||||||
Guard->Count++;
|
Guard->Count++;
|
||||||
}
|
}
|
||||||
void Else(SourceLocation, SourceLocation) override {
|
virtual void Else(SourceLocation, SourceLocation) override {
|
||||||
Guard->Count++;
|
Guard->Count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,8 +310,7 @@ static std::pair<unsigned, bool> findDirectiveEnd(SourceLocation HashLoc,
|
|||||||
IncludeDirectives::IncludeDirectives(clang::CompilerInstance &CI)
|
IncludeDirectives::IncludeDirectives(clang::CompilerInstance &CI)
|
||||||
: CI(CI), Sources(CI.getSourceManager()) {
|
: CI(CI), Sources(CI.getSourceManager()) {
|
||||||
// addPPCallbacks takes ownership of the callback
|
// addPPCallbacks takes ownership of the callback
|
||||||
CI.getPreprocessor().addPPCallbacks(
|
CI.getPreprocessor().addPPCallbacks(new IncludeDirectivesPPCallback(this));
|
||||||
llvm::make_unique<IncludeDirectivesPPCallback>(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IncludeDirectives::lookForInclude(const FileEntry *File,
|
bool IncludeDirectives::lookForInclude(const FileEntry *File,
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ void writePerfDataJSON(
|
|||||||
SS << DirectoryName << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
|
SS << DirectoryName << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
|
||||||
<< ".json";
|
<< ".json";
|
||||||
|
|
||||||
std::error_code EC;
|
std::string ErrorInfo;
|
||||||
llvm::raw_fd_ostream FileStream(SS.str(), EC, llvm::sys::fs::F_Text);
|
llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo,
|
||||||
|
llvm::sys::fs::F_Text);
|
||||||
FileStream << "{\n";
|
FileStream << "{\n";
|
||||||
FileStream << " \"Sources\" : [\n";
|
FileStream << " \"Sources\" : [\n";
|
||||||
for (SourcePerfData::const_iterator I = TimingResults.begin(),
|
for (SourcePerfData::const_iterator I = TimingResults.begin(),
|
||||||
|
|||||||
@@ -25,12 +25,11 @@ using namespace llvm::sys;
|
|||||||
using namespace clang::tooling;
|
using namespace clang::tooling;
|
||||||
|
|
||||||
bool ReplacementHandling::findClangApplyReplacements(const char *Argv0) {
|
bool ReplacementHandling::findClangApplyReplacements(const char *Argv0) {
|
||||||
ErrorOr<std::string> CARPathOrErr =
|
CARPath = FindProgramByName("clang-apply-replacements");
|
||||||
findProgramByName("clang-apply-replacements");
|
|
||||||
if (!CARPathOrErr)
|
if (!CARPath.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
CARPath = *CARPathOrErr;
|
|
||||||
static int StaticSymbol;
|
static int StaticSymbol;
|
||||||
std::string ClangModernizePath = fs::getMainExecutable(Argv0, &StaticSymbol);
|
std::string ClangModernizePath = fs::getMainExecutable(Argv0, &StaticSymbol);
|
||||||
SmallString<128> TestPath = path::parent_path(ClangModernizePath);
|
SmallString<128> TestPath = path::parent_path(ClangModernizePath);
|
||||||
@@ -73,10 +72,11 @@ bool ReplacementHandling::serializeReplacements(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code EC;
|
std::string ErrorInfo;
|
||||||
raw_fd_ostream ReplacementsFile(ReplacementsFileName, EC, fs::F_None);
|
raw_fd_ostream ReplacementsFile(ReplacementsFileName.c_str(), ErrorInfo,
|
||||||
if (EC) {
|
fs::F_None);
|
||||||
errs() << "Error opening file: " << EC.message() << "\n";
|
if (!ErrorInfo.empty()) {
|
||||||
|
errs() << "Error opening file: " << ErrorInfo << "\n";
|
||||||
Errors = true;
|
Errors = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
ActionFactory(MatchFinder &Finder, Transform &Owner)
|
ActionFactory(MatchFinder &Finder, Transform &Owner)
|
||||||
: Finder(Finder), Owner(Owner) {}
|
: Finder(Finder), Owner(Owner) {}
|
||||||
|
|
||||||
FrontendAction *create() override {
|
virtual FrontendAction *create() override {
|
||||||
return new FactoryAdaptor(Finder, Owner);
|
return new FactoryAdaptor(Finder, Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +49,7 @@ private:
|
|||||||
FactoryAdaptor(MatchFinder &Finder, Transform &Owner)
|
FactoryAdaptor(MatchFinder &Finder, Transform &Owner)
|
||||||
: Finder(Finder), Owner(Owner) {}
|
: Finder(Finder), Owner(Owner) {}
|
||||||
|
|
||||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
|
ASTConsumer *CreateASTConsumer(CompilerInstance &, StringRef) {
|
||||||
StringRef) override {
|
|
||||||
return Finder.newASTConsumer();
|
return Finder.newASTConsumer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ private:
|
|||||||
return Owner.handleBeginSource(CI, Filename);
|
return Owner.handleBeginSource(CI, Filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EndSourceFileAction() override {
|
virtual void EndSourceFileAction() override {
|
||||||
Owner.handleEndSource();
|
Owner.handleEndSource();
|
||||||
return ASTFrontendAction::EndSourceFileAction();
|
return ASTFrontendAction::EndSourceFileAction();
|
||||||
}
|
}
|
||||||
@@ -129,9 +128,8 @@ Transform::addReplacementForCurrentTU(const clang::tooling::Replacement &R) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<FrontendActionFactory>
|
FrontendActionFactory *Transform::createActionFactory(MatchFinder &Finder) {
|
||||||
Transform::createActionFactory(MatchFinder &Finder) {
|
return new ActionFactory(Finder, /*Owner=*/ *this);
|
||||||
return llvm::make_unique<ActionFactory>(Finder, /*Owner=*/*this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Version Version::getFromString(llvm::StringRef VersionStr) {
|
Version Version::getFromString(llvm::StringRef VersionStr) {
|
||||||
|
|||||||
@@ -208,8 +208,8 @@ protected:
|
|||||||
///
|
///
|
||||||
/// The factory returned by this function is responsible for calling back to
|
/// The factory returned by this function is responsible for calling back to
|
||||||
/// Transform to call handleBeginSource() and handleEndSource().
|
/// Transform to call handleBeginSource() and handleEndSource().
|
||||||
std::unique_ptr<clang::tooling::FrontendActionFactory>
|
clang::tooling::FrontendActionFactory *
|
||||||
createActionFactory(clang::ast_matchers::MatchFinder &Finder);
|
createActionFactory(clang::ast_matchers::MatchFinder &Finder);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string Name;
|
const std::string Name;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ int LoopConvertTransform::apply(const CompilationDatabase &Database,
|
|||||||
LFK_PseudoArray, /*Owner=*/ *this);
|
LFK_PseudoArray, /*Owner=*/ *this);
|
||||||
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
||||||
|
|
||||||
if (int result = LoopTool.run(createActionFactory(Finder).get())) {
|
if (int result = LoopTool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ int PassByValueTransform::apply(const tooling::CompilationDatabase &Database,
|
|||||||
// make the replacer available to handleBeginSource()
|
// make the replacer available to handleBeginSource()
|
||||||
this->Replacer = &Replacer;
|
this->Replacer = &Replacer;
|
||||||
|
|
||||||
if (Tool.run(createActionFactory(Finder).get())) {
|
if (Tool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ ReplaceAutoPtrTransform::apply(const CompilationDatabase &Database,
|
|||||||
Finder.addMatcher(makeAutoPtrUsingDeclMatcher(), &Replacer);
|
Finder.addMatcher(makeAutoPtrUsingDeclMatcher(), &Replacer);
|
||||||
Finder.addMatcher(makeTransferOwnershipExprMatcher(), &Fixer);
|
Finder.addMatcher(makeTransferOwnershipExprMatcher(), &Fixer);
|
||||||
|
|
||||||
if (Tool.run(createActionFactory(Finder).get())) {
|
if (Tool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ int UseAutoTransform::apply(const clang::tooling::CompilationDatabase &Database,
|
|||||||
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
|
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
|
||||||
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
|
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
|
||||||
|
|
||||||
if (int Result = UseAutoTool.run(createActionFactory(Finder).get())) {
|
if (int Result = UseAutoTool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,27 +383,27 @@ private:
|
|||||||
assert(MacroLoc.isFileID());
|
assert(MacroLoc.isFileID());
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const auto &Parents = Context.getParents(Start);
|
ASTContext::ParentVector Parents = Context.getParents(Start);
|
||||||
if (Parents.empty())
|
if (Parents.empty())
|
||||||
return false;
|
return false;
|
||||||
assert(Parents.size() == 1 &&
|
assert(Parents.size() == 1 &&
|
||||||
"Found an ancestor with more than one parent!");
|
"Found an ancestor with more than one parent!");
|
||||||
|
|
||||||
const ast_type_traits::DynTypedNode &Parent = Parents[0];
|
ASTContext::ParentVector::const_iterator I = Parents.begin();
|
||||||
|
|
||||||
SourceLocation Loc;
|
SourceLocation Loc;
|
||||||
if (const Decl *D = Parent.get<Decl>())
|
if (const Decl *D = I->get<Decl>())
|
||||||
Loc = D->getLocStart();
|
Loc = D->getLocStart();
|
||||||
else if (const Stmt *S = Parent.get<Stmt>())
|
else if (const Stmt *S = I->get<Stmt>())
|
||||||
Loc = S->getLocStart();
|
Loc = S->getLocStart();
|
||||||
else
|
else
|
||||||
llvm_unreachable("Expected to find Decl or Stmt containing ancestor");
|
llvm_unreachable("Expected to find Decl or Stmt containing ancestor");
|
||||||
|
|
||||||
if (!expandsFrom(Loc, MacroLoc)) {
|
if (!expandsFrom(Loc, MacroLoc)) {
|
||||||
Result = Parent;
|
Result = *I;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Start = Parent;
|
Start = *I;
|
||||||
} while (1);
|
} while (1);
|
||||||
|
|
||||||
llvm_unreachable("findContainingAncestor");
|
llvm_unreachable("findContainingAncestor");
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ int UseNullptrTransform::apply(const CompilationDatabase &Database,
|
|||||||
NullptrFixer Fixer(AcceptedChanges, MacroNames, /*Owner=*/ *this);
|
NullptrFixer Fixer(AcceptedChanges, MacroNames, /*Owner=*/ *this);
|
||||||
|
|
||||||
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
|
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
|
||||||
if (int result = UseNullptrTool.run(createActionFactory(Finder).get())) {
|
|
||||||
|
if (int result = UseNullptrTool.run(createActionFactory(Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ target_link_libraries(clang-modernize
|
|||||||
clangFrontend
|
clangFrontend
|
||||||
clangLex
|
clangLex
|
||||||
clangTooling
|
clangTooling
|
||||||
clangToolingCore
|
|
||||||
modernizeCore
|
modernizeCore
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -248,15 +248,14 @@ static CompilerVersions handleSupportedCompilers(const char *ProgName,
|
|||||||
return RequiredVersions;
|
return RequiredVersions;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CompilationDatabase>
|
CompilationDatabase *autoDetectCompilations(std::string &ErrorMessage) {
|
||||||
autoDetectCompilations(std::string &ErrorMessage) {
|
|
||||||
// Auto-detect a compilation database from BuildPath.
|
// Auto-detect a compilation database from BuildPath.
|
||||||
if (BuildPath.getNumOccurrences() > 0)
|
if (BuildPath.getNumOccurrences() > 0)
|
||||||
return CompilationDatabase::autoDetectFromDirectory(BuildPath,
|
return CompilationDatabase::autoDetectFromDirectory(BuildPath,
|
||||||
ErrorMessage);
|
ErrorMessage);
|
||||||
// Try to auto-detect a compilation database from the first source.
|
// Try to auto-detect a compilation database from the first source.
|
||||||
if (!SourcePaths.empty()) {
|
if (!SourcePaths.empty()) {
|
||||||
if (std::unique_ptr<CompilationDatabase> Compilations =
|
if (CompilationDatabase *Compilations =
|
||||||
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
|
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
|
||||||
ErrorMessage)) {
|
ErrorMessage)) {
|
||||||
// FIXME: just pass SourcePaths[0] once getCompileCommands supports
|
// FIXME: just pass SourcePaths[0] once getCompileCommands supports
|
||||||
@@ -276,7 +275,7 @@ autoDetectCompilations(std::string &ErrorMessage) {
|
|||||||
// If no compilation database can be detected from source then we create a
|
// If no compilation database can be detected from source then we create a
|
||||||
// fixed compilation database with c++11 support.
|
// fixed compilation database with c++11 support.
|
||||||
std::string CommandLine[] = { "-std=c++11" };
|
std::string CommandLine[] = { "-std=c++11" };
|
||||||
return llvm::make_unique<FixedCompilationDatabase>(".", CommandLine);
|
return new FixedCompilationDatabase(".", CommandLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorMessage = "Could not determine sources to transform";
|
ErrorMessage = "Could not determine sources to transform";
|
||||||
@@ -335,7 +334,7 @@ int main(int argc, const char **argv) {
|
|||||||
|
|
||||||
if (!Compilations) {
|
if (!Compilations) {
|
||||||
std::string ErrorMessage;
|
std::string ErrorMessage;
|
||||||
Compilations = autoDetectCompilations(ErrorMessage);
|
Compilations.reset(autoDetectCompilations(ErrorMessage));
|
||||||
if (!Compilations) {
|
if (!Compilations) {
|
||||||
llvm::errs() << llvm::sys::path::filename(argv[0]) << ": " << ErrorMessage
|
llvm::errs() << llvm::sys::path::filename(argv[0]) << ": " << ErrorMessage
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/..
|
|||||||
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
|
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
|
||||||
|
|
||||||
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
|
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
|
||||||
USEDLIBS = modernizeCore.a clangFormat.a \
|
USEDLIBS = modernizeCore.a clangFormat.a clangTooling.a clangFrontend.a \
|
||||||
clangTooling.a clangToolingCore.a clangFrontend.a \
|
|
||||||
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
|
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
|
||||||
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
|
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
|
||||||
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
|
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
|
|||||||
TD.emitDiagnostic(
|
TD.emitDiagnostic(
|
||||||
R.getBegin(), DiagnosticsEngine::Note,
|
R.getBegin(), DiagnosticsEngine::Note,
|
||||||
"\"" + BI->first + "\" binds here",
|
"\"" + BI->first + "\" binds here",
|
||||||
CharSourceRange::getTokenRange(R),
|
ArrayRef<CharSourceRange>(CharSourceRange::getTokenRange(R)),
|
||||||
None, &AST->getSourceManager());
|
ArrayRef<FixItHint>(), &AST->getSourceManager());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,12 +157,23 @@ QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
|
|||||||
return new InvalidQuery(OS.str());
|
return new InvalidQuery(OS.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QuerySessionSema : public Parser::RegistrySema {
|
||||||
|
public:
|
||||||
|
QuerySessionSema(const QuerySession &QS) : QS(QS) {}
|
||||||
|
|
||||||
|
ast_matchers::dynamic::VariantValue getNamedValue(StringRef Name) override {
|
||||||
|
return QS.NamedValues.lookup(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QuerySession &QS;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QueryRef QueryParser::completeMatcherExpression() {
|
QueryRef QueryParser::completeMatcherExpression() {
|
||||||
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
|
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
|
||||||
StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
|
StringRef(Begin, End - Begin), CompletionPos - Begin);
|
||||||
&QS.NamedValues);
|
|
||||||
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
|
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
|
||||||
E = Comps.end();
|
E = Comps.end();
|
||||||
I != E; ++I) {
|
I != E; ++I) {
|
||||||
@@ -183,6 +194,8 @@ QueryRef QueryParser::doParse() {
|
|||||||
.Case("unlet", PQK_Unlet)
|
.Case("unlet", PQK_Unlet)
|
||||||
.Default(PQK_Invalid);
|
.Default(PQK_Invalid);
|
||||||
|
|
||||||
|
QuerySessionSema S(QS);
|
||||||
|
|
||||||
switch (QKind) {
|
switch (QKind) {
|
||||||
case PQK_NoOp:
|
case PQK_NoOp:
|
||||||
return new NoOpQuery;
|
return new NoOpQuery;
|
||||||
@@ -201,8 +214,8 @@ QueryRef QueryParser::doParse() {
|
|||||||
|
|
||||||
Diagnostics Diag;
|
Diagnostics Diag;
|
||||||
ast_matchers::dynamic::VariantValue Value;
|
ast_matchers::dynamic::VariantValue Value;
|
||||||
if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
|
if (!Parser::parseExpression(StringRef(Begin, End - Begin), &S, &Value,
|
||||||
&QS.NamedValues, &Value, &Diag)) {
|
&Diag)) {
|
||||||
return makeInvalidQueryFromDiagnostics(Diag);
|
return makeInvalidQueryFromDiagnostics(Diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +228,7 @@ QueryRef QueryParser::doParse() {
|
|||||||
|
|
||||||
Diagnostics Diag;
|
Diagnostics Diag;
|
||||||
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
|
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
|
||||||
StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
|
StringRef(Begin, End - Begin), &S, &Diag);
|
||||||
if (!Matcher) {
|
if (!Matcher) {
|
||||||
return makeInvalidQueryFromDiagnostics(Diag);
|
return makeInvalidQueryFromDiagnostics(Diag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include "QueryParser.h"
|
#include "QueryParser.h"
|
||||||
#include "QuerySession.h"
|
#include "QuerySession.h"
|
||||||
#include "clang/Frontend/ASTUnit.h"
|
#include "clang/Frontend/ASTUnit.h"
|
||||||
#include "clang/Tooling/CommonOptionsParser.h"
|
#include "clang/Tooling/CompilationDatabase.h"
|
||||||
#include "clang/Tooling/Tooling.h"
|
#include "clang/Tooling/Tooling.h"
|
||||||
#include "llvm/LineEditor/LineEditor.h"
|
#include "llvm/LineEditor/LineEditor.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
@@ -46,30 +46,45 @@ using namespace clang::query;
|
|||||||
using namespace clang::tooling;
|
using namespace clang::tooling;
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
|
static cl::opt<std::string> BuildPath("b", cl::desc("Specify build path"),
|
||||||
static cl::OptionCategory ClangQueryCategory("clang-query options");
|
cl::value_desc("<path>"));
|
||||||
|
|
||||||
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
|
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
|
||||||
cl::value_desc("command"),
|
cl::value_desc("<command>"));
|
||||||
cl::cat(ClangQueryCategory));
|
|
||||||
|
|
||||||
static cl::list<std::string> CommandFiles("f",
|
static cl::list<std::string> CommandFiles("f",
|
||||||
cl::desc("Read commands from file"),
|
cl::desc("Read commands from file"),
|
||||||
cl::value_desc("file"),
|
cl::value_desc("<file>"));
|
||||||
cl::cat(ClangQueryCategory));
|
|
||||||
|
static cl::list<std::string> SourcePaths(cl::Positional,
|
||||||
|
cl::desc("<source0> [... <sourceN>]"),
|
||||||
|
cl::OneOrMore);
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
llvm::sys::PrintStackTraceOnErrorSignal();
|
llvm::sys::PrintStackTraceOnErrorSignal();
|
||||||
|
cl::ParseCommandLineOptions(argc, argv);
|
||||||
CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
|
|
||||||
|
|
||||||
if (!Commands.empty() && !CommandFiles.empty()) {
|
if (!Commands.empty() && !CommandFiles.empty()) {
|
||||||
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
|
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTool Tool(OptionsParser.getCompilations(),
|
std::unique_ptr<CompilationDatabase> Compilations(
|
||||||
OptionsParser.getSourcePathList());
|
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
|
||||||
|
if (!Compilations) { // Couldn't find a compilation DB from the command line
|
||||||
|
std::string ErrorMessage;
|
||||||
|
Compilations.reset(
|
||||||
|
!BuildPath.empty() ?
|
||||||
|
CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
|
||||||
|
CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Still no compilation DB? - bail.
|
||||||
|
if (!Compilations)
|
||||||
|
llvm::report_fatal_error(ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangTool Tool(*Compilations, SourcePaths);
|
||||||
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
||||||
if (Tool.buildASTs(ASTs) != 0)
|
if (Tool.buildASTs(ASTs) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
set(LLVM_LINK_COMPONENTS support)
|
|
||||||
|
|
||||||
add_clang_library(clangRename
|
|
||||||
USRFinder.cpp
|
|
||||||
USRFindingAction.cpp
|
|
||||||
USRLocFinder.cpp
|
|
||||||
RenamingAction.cpp
|
|
||||||
|
|
||||||
LINK_LIBS
|
|
||||||
clangAST
|
|
||||||
clangBasic
|
|
||||||
clangIndex
|
|
||||||
clangToolingCore
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(tool)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
##===- tools/extra/clang-rename/Makefile -------------------*- Makefile -*-===##
|
|
||||||
#
|
|
||||||
# The LLVM Compiler Infrastructure
|
|
||||||
#
|
|
||||||
# This file is distributed under the University of Illinois Open Source
|
|
||||||
# License. See LICENSE.TXT for details.
|
|
||||||
#
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
|
|
||||||
CLANG_LEVEL := ../../..
|
|
||||||
LIBRARYNAME = clangRename
|
|
||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
|
||||||
|
|
||||||
DIRS = tool
|
|
||||||
|
|
||||||
include $(CLANG_LEVEL)/Makefile
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
//===--- 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/Preprocessor.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
#include "clang/Tooling/CommonOptionsParser.h"
|
|
||||||
#include "clang/Tooling/Refactoring.h"
|
|
||||||
#include "clang/Tooling/Tooling.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace rename {
|
|
||||||
|
|
||||||
class RenamingASTConsumer : public ASTConsumer {
|
|
||||||
public:
|
|
||||||
RenamingASTConsumer(const std::string &NewName,
|
|
||||||
const std::string &PrevName,
|
|
||||||
const std::vector<std::string> &USRs,
|
|
||||||
tooling::Replacements &Replaces,
|
|
||||||
bool PrintLocations)
|
|
||||||
: NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
|
|
||||||
PrintLocations(PrintLocations) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleTranslationUnit(ASTContext &Context) override {
|
|
||||||
const auto &SourceMgr = Context.getSourceManager();
|
|
||||||
std::vector<SourceLocation> RenamingCandidates;
|
|
||||||
std::vector<SourceLocation> NewCandidates;
|
|
||||||
|
|
||||||
for (const auto &USR : USRs) {
|
|
||||||
NewCandidates = getLocationsOfUSR(USR, Context.getTranslationUnitDecl());
|
|
||||||
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
|
|
||||||
NewCandidates.end());
|
|
||||||
NewCandidates.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto PrevNameLen = PrevName.length();
|
|
||||||
if (PrintLocations)
|
|
||||||
for (const auto &Loc : RenamingCandidates) {
|
|
||||||
FullSourceLoc FullLoc(Loc, SourceMgr);
|
|
||||||
errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
|
|
||||||
<< ":" << FullLoc.getSpellingLineNumber() << ":"
|
|
||||||
<< FullLoc.getSpellingColumnNumber() << "\n";
|
|
||||||
Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
|
|
||||||
NewName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for (const auto &Loc : RenamingCandidates)
|
|
||||||
Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
|
|
||||||
NewName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string &NewName, &PrevName;
|
|
||||||
const std::vector<std::string> &USRs;
|
|
||||||
tooling::Replacements &Replaces;
|
|
||||||
bool PrintLocations;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
|
|
||||||
return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
|
|
||||||
Replaces, PrintLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
//===--- 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::string &NewName, const std::string &PrevName,
|
|
||||||
const std::vector<std::string> &USRs,
|
|
||||||
tooling::Replacements &Replaces, bool PrintLocations = false)
|
|
||||||
: NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
|
|
||||||
PrintLocations(PrintLocations) {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string &NewName, &PrevName;
|
|
||||||
const std::vector<std::string> &USRs;
|
|
||||||
tooling::Replacements &Replaces;
|
|
||||||
bool PrintLocations;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
//===--- 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/Lex/Lexer.h"
|
|
||||||
#include "clang/Index/USRGeneration.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 SourceManager &SourceMgr,
|
|
||||||
const SourceLocation Point)
|
|
||||||
: Result(nullptr), SourceMgr(SourceMgr),
|
|
||||||
Point(Point) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 setResult(Decl, Decl->getLocation(),
|
|
||||||
Decl->getNameAsString().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expression visitors:
|
|
||||||
|
|
||||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
|
||||||
// Check the namespace specifier first.
|
|
||||||
if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto *Decl = Expr->getFoundDecl();
|
|
||||||
return setResult(Decl, Expr->getLocation(),
|
|
||||||
Decl->getNameAsString().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VisitMemberExpr(const MemberExpr *Expr) {
|
|
||||||
const auto *Decl = Expr->getFoundDecl().getDecl();
|
|
||||||
return setResult(Decl, Expr->getMemberLoc(),
|
|
||||||
Decl->getNameAsString().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other:
|
|
||||||
|
|
||||||
const NamedDecl *getNamedDecl() {
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// \brief Determines if a namespace qualifier contains the point.
|
|
||||||
// \returns false on success and sets Result.
|
|
||||||
bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
|
||||||
while (NameLoc) {
|
|
||||||
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
|
||||||
if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(),
|
|
||||||
Decl->getNameAsString().length()))
|
|
||||||
return false;
|
|
||||||
NameLoc = NameLoc.getPrefix();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// \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 (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
|
|
||||||
!End.isFileID() || !isPointWithin(Start, End)) {
|
|
||||||
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 ||
|
|
||||||
(SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
|
|
||||||
SourceMgr.isBeforeInTranslationUnit(Point, End));
|
|
||||||
}
|
|
||||||
|
|
||||||
const NamedDecl *Result;
|
|
||||||
const SourceManager &SourceMgr;
|
|
||||||
const SourceLocation Point; // The location to find the NamedDecl.
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
|
|
||||||
const SourceLocation Point) {
|
|
||||||
const auto &SourceMgr = Context.getSourceManager();
|
|
||||||
const auto SearchFile = SourceMgr.getFilename(Point);
|
|
||||||
|
|
||||||
NamedDeclFindingASTVisitor Visitor(SourceMgr, Point);
|
|
||||||
|
|
||||||
// We only want to search the decls that exist in the same file as the point.
|
|
||||||
auto Decls = Context.getTranslationUnitDecl()->decls();
|
|
||||||
for (auto &CurrDecl : Decls) {
|
|
||||||
const auto FileLoc = CurrDecl->getLocStart();
|
|
||||||
const auto FileName = SourceMgr.getFilename(FileLoc);
|
|
||||||
// FIXME: Add test.
|
|
||||||
if (FileName == SearchFile) {
|
|
||||||
Visitor.TraverseDecl(CurrDecl);
|
|
||||||
if (const NamedDecl *Result = Visitor.getNamedDecl()) {
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 clang
|
|
||||||
} // namespace rename
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//===--- 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 <string>
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Converts a Decl into a USR.
|
|
||||||
std::string getUSRForDecl(const Decl *Decl);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
//===--- tools/extra/clang-rename/USRFindingAction.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 "USRFindingAction.h"
|
|
||||||
#include "USRFinder.h"
|
|
||||||
#include "clang/AST/AST.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/Preprocessor.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
#include "clang/Tooling/CommonOptionsParser.h"
|
|
||||||
#include "clang/Tooling/Refactoring.h"
|
|
||||||
#include "clang/Tooling/Tooling.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace rename {
|
|
||||||
|
|
||||||
// Get the USRs for the constructors of the class.
|
|
||||||
static std::vector<std::string> getAllConstructorUSRs(
|
|
||||||
const CXXRecordDecl *Decl) {
|
|
||||||
std::vector<std::string> USRs;
|
|
||||||
|
|
||||||
// We need to get the definition of the record (as opposed to any forward
|
|
||||||
// declarations) in order to find the constructor and destructor.
|
|
||||||
const auto *RecordDecl = Decl->getDefinition();
|
|
||||||
|
|
||||||
// Iterate over all the constructors and add their USRs.
|
|
||||||
for (const auto &CtorDecl : RecordDecl->ctors())
|
|
||||||
USRs.push_back(getUSRForDecl(CtorDecl));
|
|
||||||
|
|
||||||
// Ignore destructors. GetLocationsOfUSR will find the declaration of and
|
|
||||||
// explicit calls to a destructor through TagTypeLoc (and it is better for the
|
|
||||||
// purpose of renaming).
|
|
||||||
//
|
|
||||||
// For example, in the following code segment,
|
|
||||||
// 1 class C {
|
|
||||||
// 2 ~C();
|
|
||||||
// 3 };
|
|
||||||
// At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting
|
|
||||||
// from 'C'.
|
|
||||||
|
|
||||||
return USRs;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NamedDeclFindingConsumer : public ASTConsumer {
|
|
||||||
void HandleTranslationUnit(ASTContext &Context) override {
|
|
||||||
const auto &SourceMgr = Context.getSourceManager();
|
|
||||||
// The file we look for the USR in will always be the main source file.
|
|
||||||
const auto Point = SourceMgr.getLocForStartOfFile(
|
|
||||||
SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset);
|
|
||||||
if (!Point.isValid())
|
|
||||||
return;
|
|
||||||
const NamedDecl *FoundDecl = getNamedDeclAt(Context, Point);
|
|
||||||
if (FoundDecl == nullptr) {
|
|
||||||
FullSourceLoc FullLoc(Point, SourceMgr);
|
|
||||||
errs() << "clang-rename: could not find symbol at "
|
|
||||||
<< SourceMgr.getFilename(Point) << ":"
|
|
||||||
<< FullLoc.getSpellingLineNumber() << ":"
|
|
||||||
<< FullLoc.getSpellingColumnNumber() << " (offset " << SymbolOffset
|
|
||||||
<< ").\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the decl is a constructor or destructor, we want to instead take the
|
|
||||||
// decl of the parent record.
|
|
||||||
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
|
|
||||||
FoundDecl = CtorDecl->getParent();
|
|
||||||
else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
|
|
||||||
FoundDecl = DtorDecl->getParent();
|
|
||||||
|
|
||||||
// If the decl is in any way relatedpp to a class, we want to make sure we
|
|
||||||
// search for the constructor and destructor as well as everything else.
|
|
||||||
if (const auto *Record = dyn_cast<CXXRecordDecl>(FoundDecl))
|
|
||||||
*USRs = getAllConstructorUSRs(Record);
|
|
||||||
|
|
||||||
USRs->push_back(getUSRForDecl(FoundDecl));
|
|
||||||
*SpellingName = FoundDecl->getNameAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned SymbolOffset;
|
|
||||||
std::string *SpellingName;
|
|
||||||
std::vector<std::string> *USRs;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<ASTConsumer>
|
|
||||||
USRFindingAction::newASTConsumer() {
|
|
||||||
std::unique_ptr<NamedDeclFindingConsumer> Consumer(
|
|
||||||
new NamedDeclFindingConsumer);
|
|
||||||
SpellingName = "";
|
|
||||||
Consumer->SymbolOffset = SymbolOffset;
|
|
||||||
Consumer->USRs = &USRs;
|
|
||||||
Consumer->SpellingName = &SpellingName;
|
|
||||||
return std::move(Consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rename
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//===--- 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 relevent 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/Frontend/FrontendAction.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
class ASTConsumer;
|
|
||||||
class CompilerInstance;
|
|
||||||
class NamedDecl;
|
|
||||||
|
|
||||||
namespace rename {
|
|
||||||
|
|
||||||
struct USRFindingAction {
|
|
||||||
USRFindingAction(unsigned Offset) : SymbolOffset(Offset) {
|
|
||||||
}
|
|
||||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
|
||||||
|
|
||||||
// \brief get the spelling of the USR(s) as it would appear in source files.
|
|
||||||
const std::string &getUSRSpelling() {
|
|
||||||
return SpellingName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::string> &getUSRs() {
|
|
||||||
return USRs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned SymbolOffset;
|
|
||||||
std::string SpellingName;
|
|
||||||
std::vector<std::string> USRs;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
//===--- 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/SourceLocation.h"
|
|
||||||
#include "clang/Index/USRGeneration.h"
|
|
||||||
#include "llvm/ADT/SmallVector.h"
|
|
||||||
|
|
||||||
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::string USR) : USR(USR) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declaration visitors:
|
|
||||||
|
|
||||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
|
||||||
if (getUSRForDecl(Decl) == USR) {
|
|
||||||
LocationsFound.push_back(Decl->getLocation());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expression visitors:
|
|
||||||
|
|
||||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
|
||||||
const auto *Decl = Expr->getFoundDecl();
|
|
||||||
|
|
||||||
checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
|
|
||||||
if (getUSRForDecl(Decl) == USR) {
|
|
||||||
LocationsFound.push_back(Expr->getLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VisitMemberExpr(const MemberExpr *Expr) {
|
|
||||||
const auto *Decl = Expr->getFoundDecl().getDecl();
|
|
||||||
if (getUSRForDecl(Decl) == USR) {
|
|
||||||
LocationsFound.push_back(Expr->getMemberLoc());
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Namespace traversal:
|
|
||||||
void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
|
||||||
while (NameLoc) {
|
|
||||||
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
|
||||||
if (Decl && getUSRForDecl(Decl) == USR)
|
|
||||||
LocationsFound.push_back(NameLoc.getLocalBeginLoc());
|
|
||||||
NameLoc = NameLoc.getPrefix();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the locations of the USR were found.
|
|
||||||
const std::string USR;
|
|
||||||
std::vector<clang::SourceLocation> LocationsFound;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::vector<SourceLocation> getLocationsOfUSR(const std::string USR,
|
|
||||||
Decl *Decl) {
|
|
||||||
USRLocFindingASTVisitor visitor(USR);
|
|
||||||
|
|
||||||
visitor.TraverseDecl(Decl);
|
|
||||||
return visitor.getLocationsFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rename
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
//===--- 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 <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
|
|
||||||
class Decl;
|
|
||||||
class SourceLocation;
|
|
||||||
|
|
||||||
namespace rename {
|
|
||||||
|
|
||||||
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
|
|
||||||
std::vector<SourceLocation> getLocationsOfUSR(const std::string usr,
|
|
||||||
Decl *decl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
add_clang_executable(clang-rename ClangRename.cpp)
|
|
||||||
|
|
||||||
target_link_libraries(clang-rename
|
|
||||||
clangBasic
|
|
||||||
clangFrontend
|
|
||||||
clangRename
|
|
||||||
clangRewrite
|
|
||||||
clangTooling
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS clang-rename RUNTIME DESTINATION bin)
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
//===--- tools/extra/clang-rename/ClangRename.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 This file implements a clang-rename tool that automatically finds and
|
|
||||||
/// renames symbols in C++ code.
|
|
||||||
///
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "../USRFindingAction.h"
|
|
||||||
#include "../RenamingAction.h"
|
|
||||||
#include "clang/AST/ASTConsumer.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/Basic/FileManager.h"
|
|
||||||
#include "clang/Basic/LangOptions.h"
|
|
||||||
#include "clang/Basic/TargetInfo.h"
|
|
||||||
#include "clang/Basic/TargetOptions.h"
|
|
||||||
#include "clang/Frontend/CommandLineSourceLoc.h"
|
|
||||||
#include "clang/Frontend/CompilerInstance.h"
|
|
||||||
#include "clang/Frontend/FrontendAction.h"
|
|
||||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
|
||||||
#include "clang/Lex/Preprocessor.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
#include "clang/Parse/Parser.h"
|
|
||||||
#include "clang/Parse/ParseAST.h"
|
|
||||||
#include "clang/Rewrite/Core/Rewriter.h"
|
|
||||||
#include "clang/Tooling/CommonOptionsParser.h"
|
|
||||||
#include "clang/Tooling/Refactoring.h"
|
|
||||||
#include "clang/Tooling/Tooling.h"
|
|
||||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
|
||||||
#include "llvm/Support/Host.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
|
|
||||||
cl::OptionCategory ClangRenameCategory("Clang-rename options");
|
|
||||||
|
|
||||||
static cl::opt<std::string>
|
|
||||||
NewName(
|
|
||||||
"new-name",
|
|
||||||
cl::desc("The new name to change the symbol to."),
|
|
||||||
cl::cat(ClangRenameCategory));
|
|
||||||
static cl::opt<unsigned>
|
|
||||||
SymbolOffset(
|
|
||||||
"offset",
|
|
||||||
cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
|
|
||||||
cl::cat(ClangRenameCategory));
|
|
||||||
static cl::opt<bool>
|
|
||||||
Inplace(
|
|
||||||
"i",
|
|
||||||
cl::desc("Overwrite edited <file>s."),
|
|
||||||
cl::cat(ClangRenameCategory));
|
|
||||||
static cl::opt<bool>
|
|
||||||
PrintName(
|
|
||||||
"pn",
|
|
||||||
cl::desc("Print the found symbol's name prior to renaming to stderr."),
|
|
||||||
cl::cat(ClangRenameCategory));
|
|
||||||
static cl::opt<bool>
|
|
||||||
PrintLocations(
|
|
||||||
"pl",
|
|
||||||
cl::desc("Print the locations affected by renaming to stderr."),
|
|
||||||
cl::cat(ClangRenameCategory));
|
|
||||||
|
|
||||||
#define CLANG_RENAME_VERSION "0.0.1"
|
|
||||||
|
|
||||||
static void PrintVersion() {
|
|
||||||
outs() << "clang-rename version " << CLANG_RENAME_VERSION << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace clang;
|
|
||||||
|
|
||||||
const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
|
|
||||||
clang-rename renames every occurrence of a symbol found at <offset> in\n\
|
|
||||||
<source0>. If -i is specified, the edited files are overwritten to disk.\n\
|
|
||||||
Otherwise, the results are written to stdout.\n";
|
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
|
||||||
cl::SetVersionPrinter(PrintVersion);
|
|
||||||
tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
|
|
||||||
|
|
||||||
// Check the arguments for correctness.
|
|
||||||
|
|
||||||
if (NewName.empty()) {
|
|
||||||
errs() << "clang-rename: no new name provided.\n\n";
|
|
||||||
cl::PrintHelpMessage();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the USRs.
|
|
||||||
auto Files = OP.getSourcePathList();
|
|
||||||
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
|
|
||||||
rename::USRFindingAction USRAction(SymbolOffset);
|
|
||||||
|
|
||||||
// Find the USRs.
|
|
||||||
Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
|
|
||||||
const auto &USRs = USRAction.getUSRs();
|
|
||||||
const auto &PrevName = USRAction.getUSRSpelling();
|
|
||||||
|
|
||||||
if (PrevName.empty())
|
|
||||||
// An error should have already been printed.
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
if (PrintName)
|
|
||||||
errs() << "clang-rename: found name: " << PrevName;
|
|
||||||
|
|
||||||
// Perform the renaming.
|
|
||||||
rename::RenamingAction RenameAction(NewName, PrevName, USRs,
|
|
||||||
Tool.getReplacements(), PrintLocations);
|
|
||||||
auto Factory = tooling::newFrontendActionFactory(&RenameAction);
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if (Inplace) {
|
|
||||||
res = Tool.runAndSave(Factory.get());
|
|
||||||
} else {
|
|
||||||
res = Tool.run(Factory.get());
|
|
||||||
|
|
||||||
// Write every file to stdout. Right now we just barf the files without any
|
|
||||||
// indication of which files start where, other than that we print the files
|
|
||||||
// in the same order we see them.
|
|
||||||
LangOptions DefaultLangOptions;
|
|
||||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
|
|
||||||
new DiagnosticOptions();
|
|
||||||
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
|
|
||||||
DiagnosticsEngine Diagnostics(
|
|
||||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
|
|
||||||
&*DiagOpts, &DiagnosticPrinter, false);
|
|
||||||
auto &FileMgr = Tool.getFiles();
|
|
||||||
SourceManager Sources(Diagnostics, FileMgr);
|
|
||||||
Rewriter Rewrite(Sources, DefaultLangOptions);
|
|
||||||
|
|
||||||
Tool.applyAllReplacements(Rewrite);
|
|
||||||
for (const auto &File : Files) {
|
|
||||||
const auto *Entry = FileMgr.getFile(File);
|
|
||||||
auto ID = Sources.translateFile(Entry);
|
|
||||||
Rewrite.getEditBuffer(ID).write(outs());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(res);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
CLANG_LEVEL := ../../../..
|
|
||||||
TOOLNAME = clang-rename
|
|
||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
|
||||||
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
|
|
||||||
USEDLIBS = clangRename.a clangFrontend.a clangSerialization.a clangDriver.a \
|
|
||||||
clangTooling.a clangToolingCore.a \
|
|
||||||
clangParse.a clangSema.a clangIndex.a \
|
|
||||||
clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \
|
|
||||||
clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \
|
|
||||||
clangRewrite.a clangEdit.a clangAST.a clangLex.a clangBasic.a
|
|
||||||
|
|
||||||
include $(CLANG_LEVEL)/Makefile
|
|
||||||
|
|
||||||
@@ -21,12 +21,9 @@ add_clang_library(clangTidy
|
|||||||
clangStaticAnalyzerCore
|
clangStaticAnalyzerCore
|
||||||
clangStaticAnalyzerFrontend
|
clangStaticAnalyzerFrontend
|
||||||
clangTooling
|
clangTooling
|
||||||
clangToolingCore
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(tool)
|
add_subdirectory(tool)
|
||||||
add_subdirectory(llvm)
|
add_subdirectory(llvm)
|
||||||
add_subdirectory(google)
|
add_subdirectory(google)
|
||||||
add_subdirectory(misc)
|
add_subdirectory(misc)
|
||||||
add_subdirectory(readability)
|
|
||||||
add_subdirectory(utils)
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
#include "clang/Rewrite/Frontend/FrontendActions.h"
|
#include "clang/Rewrite/Frontend/FrontendActions.h"
|
||||||
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
|
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
|
||||||
#include "clang/Tooling/Refactoring.h"
|
#include "clang/Tooling/Refactoring.h"
|
||||||
#include "clang/Tooling/ReplacementsYaml.h"
|
|
||||||
#include "clang/Tooling/Tooling.h"
|
#include "clang/Tooling/Tooling.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/Process.h"
|
#include "llvm/Support/Process.h"
|
||||||
@@ -180,10 +179,10 @@ private:
|
|||||||
|
|
||||||
class ClangTidyASTConsumer : public MultiplexConsumer {
|
class ClangTidyASTConsumer : public MultiplexConsumer {
|
||||||
public:
|
public:
|
||||||
ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
|
ClangTidyASTConsumer(const SmallVectorImpl<ASTConsumer *> &Consumers,
|
||||||
std::unique_ptr<ast_matchers::MatchFinder> Finder,
|
std::unique_ptr<ast_matchers::MatchFinder> Finder,
|
||||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
|
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
|
||||||
: MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
|
: MultiplexConsumer(Consumers), Finder(std::move(Finder)),
|
||||||
Checks(std::move(Checks)) {}
|
Checks(std::move(Checks)) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -204,8 +203,8 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<clang::ASTConsumer>
|
|
||||||
ClangTidyASTConsumerFactory::CreateASTConsumer(
|
clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
|
||||||
clang::CompilerInstance &Compiler, StringRef File) {
|
clang::CompilerInstance &Compiler, StringRef File) {
|
||||||
// FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
|
// FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
|
||||||
// modify Compiler.
|
// modify Compiler.
|
||||||
@@ -214,21 +213,18 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
|
|||||||
Context.setASTContext(&Compiler.getASTContext());
|
Context.setASTContext(&Compiler.getASTContext());
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
|
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
|
||||||
CheckFactories->createChecks(&Context, Checks);
|
ChecksFilter &Filter = Context.getChecksFilter();
|
||||||
|
CheckFactories->createChecks(Filter, Checks);
|
||||||
ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
|
|
||||||
if (auto *P = Context.getCheckProfileData())
|
|
||||||
FinderOptions.CheckProfiling.emplace(P->Records);
|
|
||||||
|
|
||||||
std::unique_ptr<ast_matchers::MatchFinder> Finder(
|
std::unique_ptr<ast_matchers::MatchFinder> Finder(
|
||||||
new ast_matchers::MatchFinder(std::move(FinderOptions)));
|
new ast_matchers::MatchFinder);
|
||||||
|
|
||||||
for (auto &Check : Checks) {
|
for (auto &Check : Checks) {
|
||||||
|
Check->setContext(&Context);
|
||||||
Check->registerMatchers(&*Finder);
|
Check->registerMatchers(&*Finder);
|
||||||
Check->registerPPCallbacks(Compiler);
|
Check->registerPPCallbacks(Compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
|
SmallVector<ASTConsumer *, 2> Consumers;
|
||||||
if (!Checks.empty())
|
if (!Checks.empty())
|
||||||
Consumers.push_back(Finder->newASTConsumer());
|
Consumers.push_back(Finder->newASTConsumer());
|
||||||
|
|
||||||
@@ -238,28 +234,28 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
|
|||||||
AnalyzerOptions->Config["cfg-temporary-dtors"] =
|
AnalyzerOptions->Config["cfg-temporary-dtors"] =
|
||||||
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
|
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
|
||||||
|
|
||||||
GlobList &Filter = Context.getChecksFilter();
|
|
||||||
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
|
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
|
||||||
if (!AnalyzerOptions->CheckersControlList.empty()) {
|
if (!AnalyzerOptions->CheckersControlList.empty()) {
|
||||||
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
|
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
|
||||||
AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
|
AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
|
||||||
AnalyzerOptions->AnalyzeNestedBlocks = true;
|
AnalyzerOptions->AnalyzeNestedBlocks = true;
|
||||||
AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
|
AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
|
||||||
std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
|
ento::AnalysisASTConsumer *AnalysisConsumer = ento::CreateAnalysisConsumer(
|
||||||
ento::CreateAnalysisConsumer(Compiler);
|
Compiler.getPreprocessor(), Compiler.getFrontendOpts().OutputFile,
|
||||||
|
AnalyzerOptions, Compiler.getFrontendOpts().Plugins);
|
||||||
AnalysisConsumer->AddDiagnosticConsumer(
|
AnalysisConsumer->AddDiagnosticConsumer(
|
||||||
new AnalyzerDiagnosticConsumer(Context));
|
new AnalyzerDiagnosticConsumer(Context));
|
||||||
Consumers.push_back(std::move(AnalysisConsumer));
|
Consumers.push_back(AnalysisConsumer);
|
||||||
}
|
}
|
||||||
return llvm::make_unique<ClangTidyASTConsumer>(
|
return new ClangTidyASTConsumer(Consumers, std::move(Finder),
|
||||||
std::move(Consumers), std::move(Finder), std::move(Checks));
|
std::move(Checks));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
|
std::vector<std::string>
|
||||||
|
ClangTidyASTConsumerFactory::getCheckNames(ChecksFilter &Filter) {
|
||||||
std::vector<std::string> CheckNames;
|
std::vector<std::string> CheckNames;
|
||||||
GlobList &Filter = Context.getChecksFilter();
|
|
||||||
for (const auto &CheckFactory : *CheckFactories) {
|
for (const auto &CheckFactory : *CheckFactories) {
|
||||||
if (Filter.contains(CheckFactory.first))
|
if (Filter.isCheckEnabled(CheckFactory.first))
|
||||||
CheckNames.push_back(CheckFactory.first);
|
CheckNames.push_back(CheckFactory.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,17 +266,8 @@ std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
|
|||||||
return CheckNames;
|
return CheckNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
|
|
||||||
ClangTidyOptions::OptionMap Options;
|
|
||||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
|
|
||||||
CheckFactories->createChecks(&Context, Checks);
|
|
||||||
for (const auto &Check : Checks)
|
|
||||||
Check->storeOptions(Options);
|
|
||||||
return Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTidyASTConsumerFactory::CheckersList
|
ClangTidyASTConsumerFactory::CheckersList
|
||||||
ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
|
ClangTidyASTConsumerFactory::getCheckersControlList(ChecksFilter &Filter) {
|
||||||
CheckersList List;
|
CheckersList List;
|
||||||
|
|
||||||
bool AnalyzerChecksEnabled = false;
|
bool AnalyzerChecksEnabled = false;
|
||||||
@@ -288,7 +275,7 @@ ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
|
|||||||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||||
AnalyzerChecksEnabled =
|
AnalyzerChecksEnabled =
|
||||||
AnalyzerChecksEnabled ||
|
AnalyzerChecksEnabled ||
|
||||||
(!CheckName.startswith("debug") && Filter.contains(Checker));
|
(!CheckName.startswith("debug") && Filter.isCheckEnabled(Checker));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AnalyzerChecksEnabled) {
|
if (AnalyzerChecksEnabled) {
|
||||||
@@ -303,7 +290,7 @@ ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
|
|||||||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||||
|
|
||||||
if (CheckName.startswith("core") ||
|
if (CheckName.startswith("core") ||
|
||||||
(!CheckName.startswith("debug") && Filter.contains(Checker)))
|
(!CheckName.startswith("debug") && Filter.isCheckEnabled(Checker)))
|
||||||
List.push_back(std::make_pair(CheckName, true));
|
List.push_back(std::make_pair(CheckName, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,68 +307,40 @@ void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
|||||||
check(Result);
|
check(Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionsView::OptionsView(StringRef CheckName,
|
void ClangTidyCheck::setName(StringRef Name) {
|
||||||
const ClangTidyOptions::OptionMap &CheckOptions)
|
assert(CheckName.empty());
|
||||||
: NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
|
CheckName = Name.str();
|
||||||
|
|
||||||
std::string OptionsView::get(StringRef LocalName, std::string Default) const {
|
|
||||||
const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
|
|
||||||
if (Iter != CheckOptions.end())
|
|
||||||
return Iter->second;
|
|
||||||
return Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
|
|
||||||
StringRef LocalName, StringRef Value) const {
|
|
||||||
Options[NamePrefix + LocalName.str()] = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
|
|
||||||
StringRef LocalName, int64_t Value) const {
|
|
||||||
store(Options, LocalName, llvm::itostr(Value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
|
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
|
||||||
clang::tidy::ClangTidyContext Context(
|
clang::tidy::ClangTidyContext Context(
|
||||||
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
|
new DefaultOptionsProvider(ClangTidyGlobalOptions(), Options));
|
||||||
Options));
|
|
||||||
ClangTidyASTConsumerFactory Factory(Context);
|
ClangTidyASTConsumerFactory Factory(Context);
|
||||||
return Factory.getCheckNames();
|
return Factory.getCheckNames(Context.getChecksFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
|
ClangTidyStats runClangTidy(ClangTidyOptionsProvider *OptionsProvider,
|
||||||
clang::tidy::ClangTidyContext Context(
|
const tooling::CompilationDatabase &Compilations,
|
||||||
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
|
ArrayRef<std::string> InputFiles,
|
||||||
Options));
|
std::vector<ClangTidyError> *Errors) {
|
||||||
ClangTidyASTConsumerFactory Factory(Context);
|
|
||||||
return Factory.getCheckOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTidyStats
|
|
||||||
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
|
||||||
const tooling::CompilationDatabase &Compilations,
|
|
||||||
ArrayRef<std::string> InputFiles,
|
|
||||||
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
|
|
||||||
ClangTool Tool(Compilations, InputFiles);
|
ClangTool Tool(Compilations, InputFiles);
|
||||||
clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
|
clang::tidy::ClangTidyContext Context(OptionsProvider);
|
||||||
if (Profile)
|
|
||||||
Context.setCheckProfileData(Profile);
|
|
||||||
|
|
||||||
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
||||||
|
|
||||||
Tool.setDiagnosticConsumer(&DiagConsumer);
|
Tool.setDiagnosticConsumer(&DiagConsumer);
|
||||||
|
|
||||||
class ActionFactory : public FrontendActionFactory {
|
class ActionFactory : public FrontendActionFactory {
|
||||||
public:
|
public:
|
||||||
ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
|
ActionFactory(ClangTidyASTConsumerFactory *ConsumerFactory)
|
||||||
FrontendAction *create() override { return new Action(&ConsumerFactory); }
|
: ConsumerFactory(ConsumerFactory) {}
|
||||||
|
FrontendAction *create() override { return new Action(ConsumerFactory); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Action : public ASTFrontendAction {
|
class Action : public ASTFrontendAction {
|
||||||
public:
|
public:
|
||||||
Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
|
Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
|
||||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
|
||||||
StringRef File) override {
|
StringRef File) override {
|
||||||
return Factory->CreateASTConsumer(Compiler, File);
|
return Factory->CreateASTConsumer(Compiler, File);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,11 +348,10 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
|||||||
ClangTidyASTConsumerFactory *Factory;
|
ClangTidyASTConsumerFactory *Factory;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClangTidyASTConsumerFactory ConsumerFactory;
|
ClangTidyASTConsumerFactory *ConsumerFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionFactory Factory(Context);
|
Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context)));
|
||||||
Tool.run(&Factory);
|
|
||||||
*Errors = Context.getErrors();
|
*Errors = Context.getErrors();
|
||||||
return Context.getStats();
|
return Context.getStats();
|
||||||
}
|
}
|
||||||
@@ -405,16 +363,5 @@ void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix) {
|
|||||||
Reporter.Finish();
|
Reporter.Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void exportReplacements(const std::vector<ClangTidyError> &Errors,
|
|
||||||
raw_ostream &OS) {
|
|
||||||
tooling::TranslationUnitReplacements TUR;
|
|
||||||
for (const ClangTidyError &Error : Errors)
|
|
||||||
TUR.Replacements.insert(TUR.Replacements.end(), Error.Fix.begin(),
|
|
||||||
Error.Fix.end());
|
|
||||||
|
|
||||||
yaml::Output YAML(OS);
|
|
||||||
YAML << TUR;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "clang/Tooling/Refactoring.h"
|
#include "clang/Tooling/Refactoring.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
@@ -31,55 +28,6 @@ class CompilationDatabase;
|
|||||||
|
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
/// \brief Provides access to the \c ClangTidyCheck options via check-local
|
|
||||||
/// names.
|
|
||||||
///
|
|
||||||
/// Methods of this class prepend <tt>CheckName + "."</tt> to translate
|
|
||||||
/// check-local option names to global option names.
|
|
||||||
class OptionsView {
|
|
||||||
public:
|
|
||||||
/// \brief Initializes the instance using \p CheckName + "." as a prefix.
|
|
||||||
OptionsView(StringRef CheckName,
|
|
||||||
const ClangTidyOptions::OptionMap &CheckOptions);
|
|
||||||
|
|
||||||
/// \brief Read a named option from the \c Context.
|
|
||||||
///
|
|
||||||
/// Reads the option with the check-local name \p LocalName from the
|
|
||||||
/// \c CheckOptions. If the corresponding key is not present, returns
|
|
||||||
/// \p Default.
|
|
||||||
std::string get(StringRef LocalName, std::string Default) const;
|
|
||||||
|
|
||||||
/// \brief Read a named option from the \c Context and parse it as an integral
|
|
||||||
/// type \c T.
|
|
||||||
///
|
|
||||||
/// Reads the option with the check-local name \p LocalName from the
|
|
||||||
/// \c CheckOptions. If the corresponding key is not present, returns
|
|
||||||
/// \p Default.
|
|
||||||
template <typename T>
|
|
||||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
|
||||||
get(StringRef LocalName, T Default) const {
|
|
||||||
std::string Value = get(LocalName, "");
|
|
||||||
T Result = Default;
|
|
||||||
if (!Value.empty())
|
|
||||||
StringRef(Value).getAsInteger(10, Result);
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Stores an option with the check-local name \p LocalName with string
|
|
||||||
/// value \p Value to \p Options.
|
|
||||||
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
|
|
||||||
StringRef Value) const;
|
|
||||||
|
|
||||||
/// \brief Stores an option with the check-local name \p LocalName with
|
|
||||||
/// \c int64_t value \p Value to \p Options.
|
|
||||||
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
|
|
||||||
int64_t Value) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string NamePrefix;
|
|
||||||
const ClangTidyOptions::OptionMap &CheckOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Base class for all clang-tidy checks.
|
/// \brief Base class for all clang-tidy checks.
|
||||||
///
|
///
|
||||||
/// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the
|
/// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the
|
||||||
@@ -101,18 +49,6 @@ private:
|
|||||||
/// useful/necessary.
|
/// useful/necessary.
|
||||||
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
|
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
|
||||||
public:
|
public:
|
||||||
/// \brief Initializes the check with \p CheckName and \p Context.
|
|
||||||
///
|
|
||||||
/// Derived classes must implement the constructor with this signature or
|
|
||||||
/// delegate it. If a check needs to read options, it can do this in the
|
|
||||||
/// constructor using the Options.get() methods below.
|
|
||||||
ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
|
|
||||||
: CheckName(CheckName), Context(Context),
|
|
||||||
Options(CheckName, Context->getOptions().CheckOptions) {
|
|
||||||
assert(Context != nullptr);
|
|
||||||
assert(!CheckName.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ClangTidyCheck() {}
|
virtual ~ClangTidyCheck() {}
|
||||||
|
|
||||||
/// \brief Overwrite this to register \c PPCallbacks with \c Compiler.
|
/// \brief Overwrite this to register \c PPCallbacks with \c Compiler.
|
||||||
@@ -139,25 +75,21 @@ public:
|
|||||||
/// work in here.
|
/// work in here.
|
||||||
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
|
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
|
||||||
|
|
||||||
|
/// \brief The infrastructure sets the context to \p Ctx with this function.
|
||||||
|
void setContext(ClangTidyContext *Ctx) { Context = Ctx; }
|
||||||
|
|
||||||
/// \brief Add a diagnostic with the check's name.
|
/// \brief Add a diagnostic with the check's name.
|
||||||
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
|
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
|
||||||
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
||||||
|
|
||||||
/// \brief Should store all options supported by this check with their
|
/// \brief Sets the check name. Intended to be used by the clang-tidy
|
||||||
/// current values or default values for options that haven't been overridden.
|
/// framework. Can be called only once.
|
||||||
///
|
void setName(StringRef Name);
|
||||||
/// The check should use \c Options.store() to store each option it supports
|
|
||||||
/// whether it has the default value or it has been overridden.
|
|
||||||
virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
StringRef getID() const override { return CheckName; }
|
|
||||||
std::string CheckName;
|
|
||||||
ClangTidyContext *Context;
|
ClangTidyContext *Context;
|
||||||
|
std::string CheckName;
|
||||||
protected:
|
|
||||||
OptionsView Options;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClangTidyCheckFactories;
|
class ClangTidyCheckFactories;
|
||||||
@@ -167,18 +99,15 @@ public:
|
|||||||
ClangTidyASTConsumerFactory(ClangTidyContext &Context);
|
ClangTidyASTConsumerFactory(ClangTidyContext &Context);
|
||||||
|
|
||||||
/// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
|
/// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
|
||||||
std::unique_ptr<clang::ASTConsumer>
|
clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
|
||||||
CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
|
StringRef File);
|
||||||
|
|
||||||
/// \brief Get the list of enabled checks.
|
/// \brief Get the list of enabled checks.
|
||||||
std::vector<std::string> getCheckNames();
|
std::vector<std::string> getCheckNames(ChecksFilter &Filter);
|
||||||
|
|
||||||
/// \brief Get the union of options from all checks.
|
|
||||||
ClangTidyOptions::OptionMap getCheckOptions();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<std::pair<std::string, bool>> CheckersList;
|
typedef std::vector<std::pair<std::string, bool> > CheckersList;
|
||||||
CheckersList getCheckersControlList(GlobList &Filter);
|
CheckersList getCheckersControlList(ChecksFilter &Filter);
|
||||||
|
|
||||||
ClangTidyContext &Context;
|
ClangTidyContext &Context;
|
||||||
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
|
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
|
||||||
@@ -188,24 +117,14 @@ private:
|
|||||||
/// filters are applied.
|
/// filters are applied.
|
||||||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
|
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
|
||||||
|
|
||||||
/// \brief Returns the effective check-specific options.
|
|
||||||
///
|
|
||||||
/// The method configures ClangTidy with the specified \p Options and collects
|
|
||||||
/// effective options from all created checks. The returned set of options
|
|
||||||
/// includes default check-specific options for all keys not overridden by \p
|
|
||||||
/// Options.
|
|
||||||
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
|
|
||||||
|
|
||||||
/// \brief Run a set of clang-tidy checks on a set of files.
|
/// \brief Run a set of clang-tidy checks on a set of files.
|
||||||
///
|
///
|
||||||
/// \param Profile if provided, it enables check profile collection in
|
/// Takes ownership of the \c OptionsProvider.
|
||||||
/// MatchFinder, and will contain the result of the profile.
|
|
||||||
ClangTidyStats
|
ClangTidyStats
|
||||||
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
runClangTidy(ClangTidyOptionsProvider *OptionsProvider,
|
||||||
const tooling::CompilationDatabase &Compilations,
|
const tooling::CompilationDatabase &Compilations,
|
||||||
ArrayRef<std::string> InputFiles,
|
ArrayRef<std::string> InputFiles,
|
||||||
std::vector<ClangTidyError> *Errors,
|
std::vector<ClangTidyError> *Errors);
|
||||||
ProfileData *Profile = nullptr);
|
|
||||||
|
|
||||||
// FIXME: This interface will need to be significantly extended to be useful.
|
// FIXME: This interface will need to be significantly extended to be useful.
|
||||||
// FIXME: Implement confidence levels for displaying/fixing errors.
|
// FIXME: Implement confidence levels for displaying/fixing errors.
|
||||||
@@ -214,11 +133,6 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
|||||||
/// Errors containing fixes are automatically applied.
|
/// Errors containing fixes are automatically applied.
|
||||||
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix);
|
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix);
|
||||||
|
|
||||||
/// \brief Serializes replacements into YAML and writes them to the specified
|
|
||||||
/// output stream.
|
|
||||||
void exportReplacements(const std::vector<ClangTidyError> &Errors,
|
|
||||||
raw_ostream &OS);
|
|
||||||
|
|
||||||
} // end namespace tidy
|
} // end namespace tidy
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
||||||
|
|||||||
@@ -146,24 +146,22 @@ static llvm::Regex ConsumeGlob(StringRef &GlobList) {
|
|||||||
return llvm::Regex(RegexText);
|
return llvm::Regex(RegexText);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobList::GlobList(StringRef Globs)
|
ChecksFilter::ChecksFilter(StringRef GlobList)
|
||||||
: Positive(!ConsumeNegativeIndicator(Globs)),
|
: Positive(!ConsumeNegativeIndicator(GlobList)),
|
||||||
Regex(ConsumeGlob(Globs)),
|
Regex(ConsumeGlob(GlobList)),
|
||||||
NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
|
NextFilter(GlobList.empty() ? nullptr : new ChecksFilter(GlobList)) {}
|
||||||
|
|
||||||
bool GlobList::contains(StringRef S, bool Contains) {
|
bool ChecksFilter::isCheckEnabled(StringRef Name, bool Enabled) {
|
||||||
if (Regex.match(S))
|
if (Regex.match(Name))
|
||||||
Contains = Positive;
|
Enabled = Positive;
|
||||||
|
|
||||||
if (NextGlob)
|
if (NextFilter)
|
||||||
Contains = NextGlob->contains(S, Contains);
|
Enabled = NextFilter->isCheckEnabled(Name, Enabled);
|
||||||
return Contains;
|
return Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTidyContext::ClangTidyContext(
|
ClangTidyContext::ClangTidyContext(ClangTidyOptionsProvider *OptionsProvider)
|
||||||
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
|
: DiagEngine(nullptr), OptionsProvider(OptionsProvider) {
|
||||||
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
|
|
||||||
Profile(nullptr) {
|
|
||||||
// Before the first translation unit we can get errors related to command-line
|
// Before the first translation unit we can get errors related to command-line
|
||||||
// parsing, use empty string for the file name in this case.
|
// parsing, use empty string for the file name in this case.
|
||||||
setCurrentFile("");
|
setCurrentFile("");
|
||||||
@@ -204,10 +202,7 @@ void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
|
|||||||
|
|
||||||
void ClangTidyContext::setCurrentFile(StringRef File) {
|
void ClangTidyContext::setCurrentFile(StringRef File) {
|
||||||
CurrentFile = File;
|
CurrentFile = File;
|
||||||
// Safeguard against options with unset values.
|
CheckFilter.reset(new ChecksFilter(getOptions().Checks));
|
||||||
CurrentOptions = ClangTidyOptions::getDefaults().mergeWith(
|
|
||||||
OptionsProvider->getOptions(CurrentFile));
|
|
||||||
CheckFilter.reset(new GlobList(*getOptions().Checks));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangTidyContext::setASTContext(ASTContext *Context) {
|
void ClangTidyContext::setASTContext(ASTContext *Context) {
|
||||||
@@ -219,14 +214,10 @@ const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ClangTidyOptions &ClangTidyContext::getOptions() const {
|
const ClangTidyOptions &ClangTidyContext::getOptions() const {
|
||||||
return CurrentOptions;
|
return OptionsProvider->getOptions(CurrentFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangTidyContext::setCheckProfileData(ProfileData *P) {
|
ChecksFilter &ClangTidyContext::getChecksFilter() {
|
||||||
Profile = P;
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobList &ClangTidyContext::getChecksFilter() {
|
|
||||||
assert(CheckFilter != nullptr);
|
assert(CheckFilter != nullptr);
|
||||||
return *CheckFilter;
|
return *CheckFilter;
|
||||||
}
|
}
|
||||||
@@ -257,7 +248,7 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
|
|||||||
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
||||||
if (!Errors.empty()) {
|
if (!Errors.empty()) {
|
||||||
ClangTidyError &Error = Errors.back();
|
ClangTidyError &Error = Errors.back();
|
||||||
if (!Context.getChecksFilter().contains(Error.CheckName) &&
|
if (!Context.getChecksFilter().isCheckEnabled(Error.CheckName) &&
|
||||||
Error.DiagLevel != ClangTidyError::Error) {
|
Error.DiagLevel != ClangTidyError::Error) {
|
||||||
++Context.Stats.ErrorsIgnoredCheckFilter;
|
++Context.Stats.ErrorsIgnoredCheckFilter;
|
||||||
Errors.pop_back();
|
Errors.pop_back();
|
||||||
@@ -277,9 +268,6 @@ void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
|||||||
|
|
||||||
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
||||||
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
|
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
|
||||||
// Count warnings/errors.
|
|
||||||
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
|
|
||||||
|
|
||||||
if (DiagLevel == DiagnosticsEngine::Note) {
|
if (DiagLevel == DiagnosticsEngine::Note) {
|
||||||
assert(!Errors.empty() &&
|
assert(!Errors.empty() &&
|
||||||
"A diagnostic note can only be appended to a message.");
|
"A diagnostic note can only be appended to a message.");
|
||||||
@@ -340,7 +328,7 @@ void ClangTidyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
|
|||||||
const Preprocessor *PP) {
|
const Preprocessor *PP) {
|
||||||
// Before the first translation unit we don't need HeaderFilter, as we
|
// Before the first translation unit we don't need HeaderFilter, as we
|
||||||
// shouldn't get valid source locations in diagnostics.
|
// shouldn't get valid source locations in diagnostics.
|
||||||
HeaderFilter.reset(new llvm::Regex(*Context.getOptions().HeaderFilterRegex));
|
HeaderFilter.reset(new llvm::Regex(Context.getOptions().HeaderFilterRegex));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
|
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
|
||||||
@@ -370,8 +358,7 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SourceManager &Sources = Diags->getSourceManager();
|
const SourceManager &Sources = Diags->getSourceManager();
|
||||||
if (!*Context.getOptions().SystemHeaders &&
|
if (Sources.isInSystemHeader(Location))
|
||||||
Sources.isInSystemHeader(Location))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// FIXME: We start with a conservative approach here, but the actual type of
|
// FIXME: We start with a conservative approach here, but the actual type of
|
||||||
|
|||||||
@@ -15,9 +15,7 @@
|
|||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "clang/Tooling/Refactoring.h"
|
#include "clang/Tooling/Refactoring.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
|
||||||
#include "llvm/Support/Regex.h"
|
#include "llvm/Support/Regex.h"
|
||||||
#include "llvm/Support/Timer.h"
|
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
|
||||||
@@ -67,25 +65,24 @@ struct ClangTidyError {
|
|||||||
Level DiagLevel;
|
Level DiagLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Read-only set of strings represented as a list of positive and
|
/// \brief Filters checks by name.
|
||||||
/// negative globs. Positive globs add all matched strings to the set, negative
|
class ChecksFilter {
|
||||||
/// globs remove them in the order of appearance in the list.
|
|
||||||
class GlobList {
|
|
||||||
public:
|
public:
|
||||||
/// \brief \p GlobList is a comma-separated list of globs (only '*'
|
/// \brief \p GlobList is a comma-separated list of globs (only '*'
|
||||||
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
|
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
|
||||||
GlobList(StringRef Globs);
|
ChecksFilter(StringRef GlobList);
|
||||||
|
|
||||||
/// \brief Returns \c true if the pattern matches \p S. The result is the last
|
/// \brief Returns \c true if the check with the specified \p Name should be
|
||||||
/// matching glob's Positive flag.
|
/// enabled. The result is the last matching glob's Positive flag. If \p Name
|
||||||
bool contains(StringRef S) { return contains(S, false); }
|
/// is not matched by any globs, the check is not enabled.
|
||||||
|
bool isCheckEnabled(StringRef Name) { return isCheckEnabled(Name, false); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool contains(StringRef S, bool Contains);
|
bool isCheckEnabled(StringRef Name, bool Enabled);
|
||||||
|
|
||||||
bool Positive;
|
bool Positive;
|
||||||
llvm::Regex Regex;
|
llvm::Regex Regex;
|
||||||
std::unique_ptr<GlobList> NextGlob;
|
std::unique_ptr<ChecksFilter> NextFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
|
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
|
||||||
@@ -107,12 +104,7 @@ struct ClangTidyStats {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Container for clang-tidy profiling data.
|
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine
|
||||||
struct ProfileData {
|
|
||||||
llvm::StringMap<llvm::TimeRecord> Records;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
|
|
||||||
/// provided by this context.
|
/// provided by this context.
|
||||||
///
|
///
|
||||||
/// A \c ClangTidyCheck always has access to the active context to report
|
/// A \c ClangTidyCheck always has access to the active context to report
|
||||||
@@ -124,7 +116,9 @@ struct ProfileData {
|
|||||||
class ClangTidyContext {
|
class ClangTidyContext {
|
||||||
public:
|
public:
|
||||||
/// \brief Initializes \c ClangTidyContext instance.
|
/// \brief Initializes \c ClangTidyContext instance.
|
||||||
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
|
///
|
||||||
|
/// Takes ownership of the \c OptionsProvider.
|
||||||
|
ClangTidyContext(ClangTidyOptionsProvider *OptionsProvider);
|
||||||
|
|
||||||
/// \brief Report any errors detected using this method.
|
/// \brief Report any errors detected using this method.
|
||||||
///
|
///
|
||||||
@@ -151,7 +145,7 @@ public:
|
|||||||
StringRef getCheckName(unsigned DiagnosticID) const;
|
StringRef getCheckName(unsigned DiagnosticID) const;
|
||||||
|
|
||||||
/// \brief Returns check filter for the \c CurrentFile.
|
/// \brief Returns check filter for the \c CurrentFile.
|
||||||
GlobList &getChecksFilter();
|
ChecksFilter &getChecksFilter();
|
||||||
|
|
||||||
/// \brief Returns global options.
|
/// \brief Returns global options.
|
||||||
const ClangTidyGlobalOptions &getGlobalOptions() const;
|
const ClangTidyGlobalOptions &getGlobalOptions() const;
|
||||||
@@ -169,13 +163,6 @@ public:
|
|||||||
/// \brief Clears collected errors.
|
/// \brief Clears collected errors.
|
||||||
void clearErrors() { Errors.clear(); }
|
void clearErrors() { Errors.clear(); }
|
||||||
|
|
||||||
/// \brief Set the output struct for profile data.
|
|
||||||
///
|
|
||||||
/// Setting a non-null pointer here will enable profile collection in
|
|
||||||
/// clang-tidy.
|
|
||||||
void setCheckProfileData(ProfileData* Profile);
|
|
||||||
ProfileData* getCheckProfileData() const { return Profile; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Calls setDiagnosticsEngine() and storeError().
|
// Calls setDiagnosticsEngine() and storeError().
|
||||||
friend class ClangTidyDiagnosticConsumer;
|
friend class ClangTidyDiagnosticConsumer;
|
||||||
@@ -192,14 +179,11 @@ private:
|
|||||||
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
|
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
|
||||||
|
|
||||||
std::string CurrentFile;
|
std::string CurrentFile;
|
||||||
ClangTidyOptions CurrentOptions;
|
std::unique_ptr<ChecksFilter> CheckFilter;
|
||||||
std::unique_ptr<GlobList> CheckFilter;
|
|
||||||
|
|
||||||
ClangTidyStats Stats;
|
ClangTidyStats Stats;
|
||||||
|
|
||||||
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
|
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
|
||||||
|
|
||||||
ProfileData *Profile;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
|
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
|
||||||
|
|||||||
@@ -16,24 +16,27 @@
|
|||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
|
ClangTidyCheckFactories::~ClangTidyCheckFactories() {
|
||||||
CheckFactory Factory) {
|
for (const auto &Factory : Factories)
|
||||||
|
delete Factory.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangTidyCheckFactories::addCheckFactory(StringRef Name,
|
||||||
|
CheckFactoryBase *Factory) {
|
||||||
Factories[Name] = Factory;
|
Factories[Name] = Factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangTidyCheckFactories::createChecks(
|
void ClangTidyCheckFactories::createChecks(
|
||||||
ClangTidyContext *Context,
|
ChecksFilter &Filter,
|
||||||
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
|
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
|
||||||
GlobList &Filter = Context->getChecksFilter();
|
|
||||||
for (const auto &Factory : Factories) {
|
for (const auto &Factory : Factories) {
|
||||||
if (Filter.contains(Factory.first))
|
if (Filter.isCheckEnabled(Factory.first)) {
|
||||||
Checks.emplace_back(Factory.second(Factory.first, Context));
|
ClangTidyCheck *Check = Factory.second->createCheck();
|
||||||
|
Check->setName(Factory.first);
|
||||||
|
Checks.emplace_back(Check);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTidyOptions ClangTidyModule::getModuleOptions() {
|
|
||||||
return ClangTidyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|||||||
@@ -11,73 +11,52 @@
|
|||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H
|
||||||
|
|
||||||
#include "ClangTidy.h"
|
#include "ClangTidy.h"
|
||||||
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
/// \brief A collection of \c ClangTidyCheckFactory instances.
|
/// \brief A factory, that can instantiate a specific clang-tidy check for
|
||||||
|
/// processing a translation unit.
|
||||||
///
|
///
|
||||||
/// All clang-tidy modules register their check factories with an instance of
|
/// In order to register your check with the \c ClangTidyModule, create a
|
||||||
/// this object.
|
/// subclass of \c CheckFactoryBase and implement \c createCheck(). Then, use
|
||||||
class ClangTidyCheckFactories {
|
/// this subclass in \c ClangTidyModule::addCheckFactories().
|
||||||
|
class CheckFactoryBase {
|
||||||
public:
|
public:
|
||||||
typedef std::function<ClangTidyCheck *(
|
virtual ~CheckFactoryBase() {}
|
||||||
StringRef Name, ClangTidyContext *Context)> CheckFactory;
|
virtual ClangTidyCheck *createCheck() = 0;
|
||||||
|
|
||||||
/// \brief Registers check \p Factory with name \p Name.
|
|
||||||
///
|
|
||||||
/// For all checks that have default constructors, use \c registerCheck.
|
|
||||||
void registerCheckFactory(StringRef Name, CheckFactory Factory);
|
|
||||||
|
|
||||||
/// \brief Registers the \c CheckType with the name \p Name.
|
|
||||||
///
|
|
||||||
/// This method should be used for all \c ClangTidyChecks that don't require
|
|
||||||
/// constructor parameters.
|
|
||||||
///
|
|
||||||
/// For example, if have a clang-tidy check like:
|
|
||||||
/// \code
|
|
||||||
/// class MyTidyCheck : public ClangTidyCheck {
|
|
||||||
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
|
|
||||||
/// ..
|
|
||||||
/// }
|
|
||||||
/// };
|
|
||||||
/// \endcode
|
|
||||||
/// you can register it with:
|
|
||||||
/// \code
|
|
||||||
/// class MyModule : public ClangTidyModule {
|
|
||||||
/// void addCheckFactories(ClangTidyCheckFactories &Factories) override {
|
|
||||||
/// Factories.registerCheck<MyTidyCheck>("myproject-my-check");
|
|
||||||
/// }
|
|
||||||
/// };
|
|
||||||
/// \endcode
|
|
||||||
template <typename CheckType> void registerCheck(StringRef CheckName) {
|
|
||||||
registerCheckFactory(CheckName,
|
|
||||||
[](StringRef Name, ClangTidyContext *Context) {
|
|
||||||
return new CheckType(Name, Context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Create instances of all checks matching \p CheckRegexString and
|
|
||||||
/// store them in \p Checks.
|
|
||||||
///
|
|
||||||
/// The caller takes ownership of the return \c ClangTidyChecks.
|
|
||||||
void createChecks(ClangTidyContext *Context,
|
|
||||||
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
|
|
||||||
|
|
||||||
typedef std::map<std::string, CheckFactory> FactoryMap;
|
|
||||||
FactoryMap::const_iterator begin() const { return Factories.begin(); }
|
|
||||||
FactoryMap::const_iterator end() const { return Factories.end(); }
|
|
||||||
bool empty() const { return Factories.empty(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
FactoryMap Factories;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief A subclass of \c CheckFactoryBase that should be used for all
|
||||||
|
/// \c ClangTidyChecks that don't require constructor parameters.
|
||||||
|
///
|
||||||
|
/// For example, if have a clang-tidy check like:
|
||||||
|
/// \code
|
||||||
|
/// class MyTidyCheck : public ClangTidyCheck {
|
||||||
|
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// \endcode
|
||||||
|
/// you can register it with:
|
||||||
|
/// \code
|
||||||
|
/// class MyModule : public ClangTidyModule {
|
||||||
|
/// void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||||
|
/// CheckFactories.addCheckFactory(
|
||||||
|
/// "myproject-my-check", new ClangTidyCheckFactory<MyTidyCheck>());
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// \endcode
|
||||||
|
template <typename T> class ClangTidyCheckFactory : public CheckFactoryBase {
|
||||||
|
public:
|
||||||
|
ClangTidyCheck *createCheck() override { return new T; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClangTidyCheckFactories;
|
||||||
|
|
||||||
/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
|
/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
|
||||||
/// them a prefixed name.
|
/// them a prefixed name.
|
||||||
class ClangTidyModule {
|
class ClangTidyModule {
|
||||||
@@ -87,9 +66,36 @@ public:
|
|||||||
/// \brief Implement this function in order to register all \c CheckFactories
|
/// \brief Implement this function in order to register all \c CheckFactories
|
||||||
/// belonging to this module.
|
/// belonging to this module.
|
||||||
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
|
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Gets default options for checks defined in this module.
|
/// \brief A collection of \c ClangTidyCheckFactory instances.
|
||||||
virtual ClangTidyOptions getModuleOptions();
|
///
|
||||||
|
/// All clang-tidy modules register their check factories with an instance of
|
||||||
|
/// this object.
|
||||||
|
class ClangTidyCheckFactories {
|
||||||
|
public:
|
||||||
|
ClangTidyCheckFactories() {}
|
||||||
|
~ClangTidyCheckFactories();
|
||||||
|
|
||||||
|
/// \brief Register \p Factory with the name \p Name.
|
||||||
|
///
|
||||||
|
/// The \c ClangTidyCheckFactories object takes ownership of the \p Factory.
|
||||||
|
void addCheckFactory(StringRef Name, CheckFactoryBase *Factory);
|
||||||
|
|
||||||
|
/// \brief Create instances of all checks matching \p CheckRegexString and
|
||||||
|
/// store them in \p Checks.
|
||||||
|
///
|
||||||
|
/// The caller takes ownership of the return \c ClangTidyChecks.
|
||||||
|
void createChecks(ChecksFilter &Filter,
|
||||||
|
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
|
||||||
|
|
||||||
|
typedef std::map<std::string, CheckFactoryBase *> FactoryMap;
|
||||||
|
FactoryMap::const_iterator begin() const { return Factories.begin(); }
|
||||||
|
FactoryMap::const_iterator end() const { return Factories.end(); }
|
||||||
|
bool empty() const { return Factories.empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FactoryMap Factories;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace tidy
|
} // end namespace tidy
|
||||||
|
|||||||
@@ -8,25 +8,13 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "ClangTidyOptions.h"
|
#include "ClangTidyOptions.h"
|
||||||
#include "ClangTidyModuleRegistry.h"
|
|
||||||
#include "clang/Basic/LLVM.h"
|
|
||||||
#include "llvm/ADT/SmallString.h"
|
|
||||||
#include "llvm/Support/Errc.h"
|
|
||||||
#include "llvm/Support/Debug.h"
|
|
||||||
#include "llvm/Support/FileSystem.h"
|
|
||||||
#include "llvm/Support/Path.h"
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
#include "llvm/Support/YAMLTraits.h"
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#define DEBUG_TYPE "clang-tidy-options"
|
|
||||||
|
|
||||||
using clang::tidy::ClangTidyOptions;
|
using clang::tidy::ClangTidyOptions;
|
||||||
using clang::tidy::FileFilter;
|
using clang::tidy::FileFilter;
|
||||||
|
|
||||||
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
|
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
|
||||||
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
|
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
|
||||||
LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace yaml {
|
namespace yaml {
|
||||||
@@ -59,35 +47,11 @@ template <> struct MappingTraits<FileFilter> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct MappingTraits<ClangTidyOptions::StringPair> {
|
|
||||||
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
|
|
||||||
IO.mapRequired("key", KeyValue.first);
|
|
||||||
IO.mapRequired("value", KeyValue.second);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NOptionMap {
|
|
||||||
NOptionMap(IO &) {}
|
|
||||||
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
|
|
||||||
: Options(OptionMap.begin(), OptionMap.end()) {}
|
|
||||||
ClangTidyOptions::OptionMap denormalize(IO &) {
|
|
||||||
ClangTidyOptions::OptionMap Map;
|
|
||||||
for (const auto &KeyValue : Options)
|
|
||||||
Map[KeyValue.first] = KeyValue.second;
|
|
||||||
return Map;
|
|
||||||
}
|
|
||||||
std::vector<ClangTidyOptions::StringPair> Options;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct MappingTraits<ClangTidyOptions> {
|
template <> struct MappingTraits<ClangTidyOptions> {
|
||||||
static void mapping(IO &IO, ClangTidyOptions &Options) {
|
static void mapping(IO &IO, ClangTidyOptions &Options) {
|
||||||
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
|
|
||||||
IO, Options.CheckOptions);
|
|
||||||
IO.mapOptional("Checks", Options.Checks);
|
IO.mapOptional("Checks", Options.Checks);
|
||||||
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
|
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
|
||||||
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
|
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
|
||||||
IO.mapOptional("User", Options.User);
|
|
||||||
IO.mapOptional("CheckOptions", NOpts->Options);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,184 +61,19 @@ template <> struct MappingTraits<ClangTidyOptions> {
|
|||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
ClangTidyOptions ClangTidyOptions::getDefaults() {
|
|
||||||
ClangTidyOptions Options;
|
|
||||||
Options.Checks = "";
|
|
||||||
Options.HeaderFilterRegex = "";
|
|
||||||
Options.SystemHeaders = false;
|
|
||||||
Options.AnalyzeTemporaryDtors = false;
|
|
||||||
Options.User = llvm::None;
|
|
||||||
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
|
|
||||||
E = ClangTidyModuleRegistry::end();
|
|
||||||
I != E; ++I)
|
|
||||||
Options = Options.mergeWith(I->instantiate()->getModuleOptions());
|
|
||||||
return Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTidyOptions
|
|
||||||
ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
|
|
||||||
ClangTidyOptions Result = *this;
|
|
||||||
|
|
||||||
// Merge comma-separated glob lists by appending the new value after a comma.
|
|
||||||
if (Other.Checks)
|
|
||||||
Result.Checks =
|
|
||||||
(Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
|
|
||||||
*Other.Checks;
|
|
||||||
|
|
||||||
if (Other.HeaderFilterRegex)
|
|
||||||
Result.HeaderFilterRegex = Other.HeaderFilterRegex;
|
|
||||||
if (Other.SystemHeaders)
|
|
||||||
Result.SystemHeaders = Other.SystemHeaders;
|
|
||||||
if (Other.AnalyzeTemporaryDtors)
|
|
||||||
Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
|
|
||||||
if (Other.User)
|
|
||||||
Result.User = Other.User;
|
|
||||||
|
|
||||||
for (const auto &KeyValue : Other.CheckOptions)
|
|
||||||
Result.CheckOptions[KeyValue.first] = KeyValue.second;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOptionsProvider::FileOptionsProvider(
|
|
||||||
const ClangTidyGlobalOptions &GlobalOptions,
|
|
||||||
const ClangTidyOptions &DefaultOptions,
|
|
||||||
const ClangTidyOptions &OverrideOptions)
|
|
||||||
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
|
|
||||||
OverrideOptions(OverrideOptions) {
|
|
||||||
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
|
|
||||||
CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOptionsProvider::FileOptionsProvider(
|
|
||||||
const ClangTidyGlobalOptions &GlobalOptions,
|
|
||||||
const ClangTidyOptions &DefaultOptions,
|
|
||||||
const ClangTidyOptions &OverrideOptions,
|
|
||||||
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
|
|
||||||
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
|
|
||||||
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
|
|
||||||
CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This method has some common logic with clang::format::getStyle().
|
|
||||||
// Consider pulling out common bits to a findParentFileWithName function or
|
|
||||||
// similar.
|
|
||||||
const ClangTidyOptions &FileOptionsProvider::getOptions(StringRef FileName) {
|
|
||||||
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
|
|
||||||
SmallString<256> FilePath(FileName);
|
|
||||||
|
|
||||||
if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
|
|
||||||
llvm::errs() << "Can't make absolute path from " << FileName << ": "
|
|
||||||
<< EC.message() << "\n";
|
|
||||||
// FIXME: Figure out what to do.
|
|
||||||
} else {
|
|
||||||
FileName = FilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for a suitable configuration file in all parent directories of the
|
|
||||||
// file. Start with the immediate parent directory and move up.
|
|
||||||
StringRef Path = llvm::sys::path::parent_path(FileName);
|
|
||||||
for (StringRef CurrentPath = Path;;
|
|
||||||
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
|
|
||||||
llvm::Optional<ClangTidyOptions> Result;
|
|
||||||
|
|
||||||
auto Iter = CachedOptions.find(CurrentPath);
|
|
||||||
if (Iter != CachedOptions.end())
|
|
||||||
Result = Iter->second;
|
|
||||||
|
|
||||||
if (!Result)
|
|
||||||
Result = TryReadConfigFile(CurrentPath);
|
|
||||||
|
|
||||||
if (Result) {
|
|
||||||
// Store cached value for all intermediate directories.
|
|
||||||
while (Path != CurrentPath) {
|
|
||||||
DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
|
|
||||||
<< ".\n");
|
|
||||||
CachedOptions[Path] = *Result;
|
|
||||||
Path = llvm::sys::path::parent_path(Path);
|
|
||||||
}
|
|
||||||
return CachedOptions[Path] = *Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Optional<ClangTidyOptions>
|
|
||||||
FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
|
|
||||||
assert(!Directory.empty());
|
|
||||||
|
|
||||||
if (!llvm::sys::fs::is_directory(Directory)) {
|
|
||||||
llvm::errs() << "Error reading configuration from " << Directory
|
|
||||||
<< ": directory doesn't exist.\n";
|
|
||||||
return llvm::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
|
|
||||||
SmallString<128> ConfigFile(Directory);
|
|
||||||
llvm::sys::path::append(ConfigFile, ConfigHandler.first);
|
|
||||||
DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
|
|
||||||
|
|
||||||
bool IsFile = false;
|
|
||||||
// Ignore errors from is_regular_file: we only need to know if we can read
|
|
||||||
// the file or not.
|
|
||||||
llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
|
|
||||||
if (!IsFile)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
|
|
||||||
llvm::MemoryBuffer::getFile(ConfigFile.c_str());
|
|
||||||
if (std::error_code EC = Text.getError()) {
|
|
||||||
llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
|
|
||||||
<< "\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip empty files, e.g. files opened for writing via shell output
|
|
||||||
// redirection.
|
|
||||||
if ((*Text)->getBuffer().empty())
|
|
||||||
continue;
|
|
||||||
llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
|
|
||||||
ConfigHandler.second((*Text)->getBuffer());
|
|
||||||
if (!ParsedOptions) {
|
|
||||||
if (ParsedOptions.getError())
|
|
||||||
llvm::errs() << "Error parsing " << ConfigFile << ": "
|
|
||||||
<< ParsedOptions.getError().message() << "\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTidyOptions Defaults = DefaultOptionsProvider::getOptions(Directory);
|
|
||||||
// Only use checks from the config file.
|
|
||||||
Defaults.Checks = None;
|
|
||||||
return Defaults.mergeWith(*ParsedOptions).mergeWith(OverrideOptions);
|
|
||||||
}
|
|
||||||
return llvm::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Parses -line-filter option and stores it to the \c Options.
|
/// \brief Parses -line-filter option and stores it to the \c Options.
|
||||||
std::error_code parseLineFilter(StringRef LineFilter,
|
std::error_code parseLineFilter(const std::string &LineFilter,
|
||||||
clang::tidy::ClangTidyGlobalOptions &Options) {
|
clang::tidy::ClangTidyGlobalOptions &Options) {
|
||||||
llvm::yaml::Input Input(LineFilter);
|
llvm::yaml::Input Input(LineFilter);
|
||||||
Input >> Options.LineFilter;
|
Input >> Options.LineFilter;
|
||||||
return Input.error();
|
return Input.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
|
std::error_code parseConfiguration(const std::string &Config,
|
||||||
|
clang::tidy::ClangTidyOptions &Options) {
|
||||||
llvm::yaml::Input Input(Config);
|
llvm::yaml::Input Input(Config);
|
||||||
ClangTidyOptions Options;
|
|
||||||
Input >> Options;
|
Input >> Options;
|
||||||
if (Input.error())
|
return Input.error();
|
||||||
return Input.error();
|
|
||||||
return Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string configurationAsText(const ClangTidyOptions &Options) {
|
|
||||||
std::string Text;
|
|
||||||
llvm::raw_string_ostream Stream(Text);
|
|
||||||
llvm::yaml::Output Output(Stream);
|
|
||||||
// We use the same mapping method for input and output, so we need a non-const
|
|
||||||
// reference here.
|
|
||||||
ClangTidyOptions NonConstValue = Options;
|
|
||||||
Output << NonConstValue;
|
|
||||||
return Stream.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
|
|||||||
@@ -10,12 +10,7 @@
|
|||||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
||||||
|
|
||||||
#include "llvm/ADT/Optional.h"
|
|
||||||
#include "llvm/ADT/StringMap.h"
|
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/ErrorOr.h"
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -47,42 +42,18 @@ struct ClangTidyGlobalOptions {
|
|||||||
/// \brief Contains options for clang-tidy. These options may be read from
|
/// \brief Contains options for clang-tidy. These options may be read from
|
||||||
/// configuration files, and may be different for different translation units.
|
/// configuration files, and may be different for different translation units.
|
||||||
struct ClangTidyOptions {
|
struct ClangTidyOptions {
|
||||||
/// \brief These options are used for all settings that haven't been
|
/// \brief Allow all checks and no headers by default.
|
||||||
/// overridden by the \c OptionsProvider.
|
ClangTidyOptions() : Checks("*"), AnalyzeTemporaryDtors(false) {}
|
||||||
///
|
|
||||||
/// Allow no checks and no headers by default. This method initializes
|
|
||||||
/// check-specific options by calling \c ClangTidyModule::getModuleOptions()
|
|
||||||
/// of each registered \c ClangTidyModule.
|
|
||||||
static ClangTidyOptions getDefaults();
|
|
||||||
|
|
||||||
/// \brief Creates a new \c ClangTidyOptions instance combined from all fields
|
|
||||||
/// of this instance overridden by the fields of \p Other that have a value.
|
|
||||||
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const;
|
|
||||||
|
|
||||||
/// \brief Checks filter.
|
/// \brief Checks filter.
|
||||||
llvm::Optional<std::string> Checks;
|
std::string Checks;
|
||||||
|
|
||||||
/// \brief Output warnings from headers matching this filter. Warnings from
|
/// \brief Output warnings from headers matching this filter. Warnings from
|
||||||
/// main files will always be displayed.
|
/// main files will always be displayed.
|
||||||
llvm::Optional<std::string> HeaderFilterRegex;
|
std::string HeaderFilterRegex;
|
||||||
|
|
||||||
/// \brief Output warnings from system headers matching \c HeaderFilterRegex.
|
|
||||||
llvm::Optional<bool> SystemHeaders;
|
|
||||||
|
|
||||||
/// \brief Turns on temporary destructor-based analysis.
|
/// \brief Turns on temporary destructor-based analysis.
|
||||||
llvm::Optional<bool> AnalyzeTemporaryDtors;
|
bool AnalyzeTemporaryDtors;
|
||||||
|
|
||||||
/// \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()
|
|
||||||
/// comments in the relevant check.
|
|
||||||
llvm::Optional<std::string> User;
|
|
||||||
|
|
||||||
typedef std::pair<std::string, std::string> StringPair;
|
|
||||||
typedef std::map<std::string, std::string> OptionMap;
|
|
||||||
|
|
||||||
/// \brief Key-value mapping used to store check-specific options.
|
|
||||||
OptionMap CheckOptions;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Abstract interface for retrieving various ClangTidy options.
|
/// \brief Abstract interface for retrieving various ClangTidy options.
|
||||||
@@ -108,7 +79,7 @@ public:
|
|||||||
const ClangTidyGlobalOptions &getGlobalOptions() override {
|
const ClangTidyGlobalOptions &getGlobalOptions() override {
|
||||||
return GlobalOptions;
|
return GlobalOptions;
|
||||||
}
|
}
|
||||||
const ClangTidyOptions &getOptions(llvm::StringRef /*FileName*/) override {
|
const ClangTidyOptions &getOptions(llvm::StringRef) override {
|
||||||
return DefaultOptions;
|
return DefaultOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,98 +88,13 @@ private:
|
|||||||
ClangTidyOptions DefaultOptions;
|
ClangTidyOptions DefaultOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
|
|
||||||
/// tries to find a configuration file in the closest parent directory of each
|
|
||||||
/// source file.
|
|
||||||
///
|
|
||||||
/// By default, files named ".clang-tidy" will be considered, and the
|
|
||||||
/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
|
|
||||||
/// custom set of configuration file names and parsing functions can be
|
|
||||||
/// specified using the appropriate constructor.
|
|
||||||
class FileOptionsProvider : public DefaultOptionsProvider {
|
|
||||||
public:
|
|
||||||
// \brief A pair of configuration file base name and a function parsing
|
|
||||||
// configuration from text in the corresponding format.
|
|
||||||
typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
|
|
||||||
llvm::StringRef)>> ConfigFileHandler;
|
|
||||||
|
|
||||||
/// \brief Configuration file handlers listed in the order of priority.
|
|
||||||
///
|
|
||||||
/// Custom configuration file formats can be supported by constructing the
|
|
||||||
/// list of handlers and passing it to the appropriate \c FileOptionsProvider
|
|
||||||
/// constructor. E.g. initialization of a \c FileOptionsProvider with support
|
|
||||||
/// of a custom configuration file format for files named ".my-tidy-config"
|
|
||||||
/// could look similar to this:
|
|
||||||
/// \code
|
|
||||||
/// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
|
|
||||||
/// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
|
|
||||||
/// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
|
|
||||||
/// return llvm::make_unique<FileOptionsProvider>(
|
|
||||||
/// GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
|
|
||||||
/// \endcode
|
|
||||||
///
|
|
||||||
/// With the order of handlers shown above, the ".my-tidy-config" file would
|
|
||||||
/// take precedence over ".clang-tidy" if both reside in the same directory.
|
|
||||||
typedef std::vector<ConfigFileHandler> ConfigFileHandlers;
|
|
||||||
|
|
||||||
/// \brief Initializes the \c FileOptionsProvider instance.
|
|
||||||
///
|
|
||||||
/// \param GlobalOptions are just stored and returned to the caller of
|
|
||||||
/// \c getGlobalOptions.
|
|
||||||
///
|
|
||||||
/// \param DefaultOptions are used for all settings not specified in a
|
|
||||||
/// configuration file.
|
|
||||||
///
|
|
||||||
/// If any of the \param OverrideOptions fields are set, they will override
|
|
||||||
/// whatever options are read from the configuration file.
|
|
||||||
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
|
|
||||||
const ClangTidyOptions &DefaultOptions,
|
|
||||||
const ClangTidyOptions &OverrideOptions);
|
|
||||||
|
|
||||||
/// \brief Initializes the \c FileOptionsProvider instance with a custom set
|
|
||||||
/// of configuration file handlers.
|
|
||||||
///
|
|
||||||
/// \param GlobalOptions are just stored and returned to the caller of
|
|
||||||
/// \c getGlobalOptions.
|
|
||||||
///
|
|
||||||
/// \param DefaultOptions are used for all settings not specified in a
|
|
||||||
/// configuration file.
|
|
||||||
///
|
|
||||||
/// If any of the \param OverrideOptions fields are set, they will override
|
|
||||||
/// whatever options are read from the configuration file.
|
|
||||||
///
|
|
||||||
/// \param ConfigHandlers specifies a custom set of configuration file
|
|
||||||
/// handlers. Each handler is a pair of configuration file name and a function
|
|
||||||
/// that can parse configuration from this file type. The configuration files
|
|
||||||
/// in each directory are searched for in the order of appearance in
|
|
||||||
/// \p ConfigHandlers.
|
|
||||||
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
|
|
||||||
const ClangTidyOptions &DefaultOptions,
|
|
||||||
const ClangTidyOptions &OverrideOptions,
|
|
||||||
const ConfigFileHandlers &ConfigHandlers);
|
|
||||||
|
|
||||||
const ClangTidyOptions &getOptions(llvm::StringRef FileName) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// \brief Try to read configuration files from \p Directory using registered
|
|
||||||
/// \c ConfigHandlers.
|
|
||||||
llvm::Optional<ClangTidyOptions> TryReadConfigFile(llvm::StringRef Directory);
|
|
||||||
|
|
||||||
llvm::StringMap<ClangTidyOptions> CachedOptions;
|
|
||||||
ClangTidyOptions OverrideOptions;
|
|
||||||
ConfigFileHandlers ConfigHandlers;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Parses LineFilter from JSON and stores it to the \p Options.
|
/// \brief Parses LineFilter from JSON and stores it to the \p Options.
|
||||||
std::error_code parseLineFilter(llvm::StringRef LineFilter,
|
std::error_code parseLineFilter(const std::string &LineFilter,
|
||||||
ClangTidyGlobalOptions &Options);
|
clang::tidy::ClangTidyGlobalOptions &Options);
|
||||||
|
|
||||||
/// \brief Parses configuration from JSON and returns \c ClangTidyOptions or an
|
/// \brief Parses configuration from JSON and stores it to the \p Options.
|
||||||
/// error.
|
std::error_code parseConfiguration(const std::string &Config,
|
||||||
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(llvm::StringRef Config);
|
clang::tidy::ClangTidyOptions &Options);
|
||||||
|
|
||||||
/// \brief Serializes configuration to a YAML-encoded string.
|
|
||||||
std::string configurationAsText(const ClangTidyOptions &Options);
|
|
||||||
|
|
||||||
} // end namespace tidy
|
} // end namespace tidy
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ CLANG_LEVEL := ../../..
|
|||||||
LIBRARYNAME := clangTidy
|
LIBRARYNAME := clangTidy
|
||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
include $(CLANG_LEVEL)/../../Makefile.config
|
||||||
|
|
||||||
DIRS = utils readability llvm google misc tool
|
DIRS = llvm google misc tool
|
||||||
|
|
||||||
include $(CLANG_LEVEL)/Makefile
|
include $(CLANG_LEVEL)/Makefile
|
||||||
|
|||||||
@@ -1,225 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
|
|
||||||
#
|
|
||||||
# The LLVM Compiler Infrastructure
|
|
||||||
#
|
|
||||||
# This file is distributed under the University of Illinois Open Source
|
|
||||||
# License. See LICENSE.TXT for details.
|
|
||||||
#
|
|
||||||
#===------------------------------------------------------------------------===#
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
|
|
||||||
# and 'False' if the entry already existed.
|
|
||||||
def adapt_cmake(module_path, check_name_camel):
|
|
||||||
filename = os.path.join(module_path, 'CMakeLists.txt')
|
|
||||||
with open(filename, 'r') as f:
|
|
||||||
lines = f.read().split('\n')
|
|
||||||
# .split with separator returns one more element. Ignore it.
|
|
||||||
lines = lines[:-1]
|
|
||||||
|
|
||||||
cpp_file = check_name_camel + '.cpp'
|
|
||||||
|
|
||||||
# Figure out whether this check already exists.
|
|
||||||
for line in lines:
|
|
||||||
if line.strip() == cpp_file:
|
|
||||||
return False
|
|
||||||
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
cpp_found = False
|
|
||||||
file_added = False
|
|
||||||
for line in lines:
|
|
||||||
if not file_added and (line.endswith('.cpp') or cpp_found):
|
|
||||||
cpp_found = True
|
|
||||||
if line.strip() > cpp_file:
|
|
||||||
f.write(' ' + cpp_file + '\n')
|
|
||||||
file_added = True
|
|
||||||
f.write(line + '\n')
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
# Adds a header for the new check.
|
|
||||||
def write_header(module_path, module, check_name, check_name_camel):
|
|
||||||
filename = os.path.join(module_path, check_name_camel) + '.h'
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() +
|
|
||||||
'_' + check_name.upper().replace('-', '_') + '_H')
|
|
||||||
f.write('//===--- ')
|
|
||||||
f.write(os.path.basename(filename))
|
|
||||||
f.write(' - clang-tidy')
|
|
||||||
f.write('-' * max(0, 43 - len(os.path.basename(filename))))
|
|
||||||
f.write('*- C++ -*-===//')
|
|
||||||
f.write("""
|
|
||||||
//
|
|
||||||
// The LLVM Compiler Infrastructure
|
|
||||||
//
|
|
||||||
// This file is distributed under the University of Illinois Open Source
|
|
||||||
// License. See LICENSE.TXT for details.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef %(header_guard)s
|
|
||||||
#define %(header_guard)s
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
class %(check_name)s : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
%(check_name)s(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // %(header_guard)s
|
|
||||||
|
|
||||||
""" % {'header_guard': header_guard,
|
|
||||||
'check_name': check_name_camel})
|
|
||||||
|
|
||||||
|
|
||||||
# Adds the implementation of the new check.
|
|
||||||
def write_implementation(module_path, check_name_camel):
|
|
||||||
filename = os.path.join(module_path, check_name_camel) + '.cpp'
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write('//===--- ')
|
|
||||||
f.write(os.path.basename(filename))
|
|
||||||
f.write(' - clang-tidy')
|
|
||||||
f.write('-' * max(0, 52 - len(os.path.basename(filename))))
|
|
||||||
f.write('-===//')
|
|
||||||
f.write("""
|
|
||||||
//
|
|
||||||
// The LLVM Compiler Infrastructure
|
|
||||||
//
|
|
||||||
// This file is distributed under the University of Illinois Open Source
|
|
||||||
// License. See LICENSE.TXT for details.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "%(check_name)s.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
void %(check_name)s::registerMatchers(MatchFinder *Finder) {
|
|
||||||
// FIXME: Add matchers.
|
|
||||||
Finder->addMatcher(functionDecl().bind("x"), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
// FIXME: Add callback implementation.
|
|
||||||
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
|
|
||||||
if (MatchedDecl->getName().startswith("awesome_"))
|
|
||||||
return;
|
|
||||||
diag(MatchedDecl->getLocation(), "function '%%0' is insufficiently awesome")
|
|
||||||
<< MatchedDecl->getName()
|
|
||||||
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
""" % {'check_name': check_name_camel})
|
|
||||||
|
|
||||||
|
|
||||||
# Modifies the module to include the new check.
|
|
||||||
def adapt_module(module_path, module, check_name, check_name_camel):
|
|
||||||
filename = os.path.join(module_path, module.capitalize() + 'TidyModule.cpp')
|
|
||||||
with open(filename, 'r') as f:
|
|
||||||
lines = f.read().split('\n')
|
|
||||||
# .split with separator returns one more element. Ignore it.
|
|
||||||
lines = lines[:-1]
|
|
||||||
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
header_added = False
|
|
||||||
header_found = False
|
|
||||||
check_added = False
|
|
||||||
check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
|
|
||||||
'>(\n "' + module + '-' + check_name + '");\n')
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if not header_added:
|
|
||||||
match = re.search('#include "(.*)"', line)
|
|
||||||
if match:
|
|
||||||
header_found = True
|
|
||||||
if match.group(1) > check_name_camel:
|
|
||||||
header_added = True
|
|
||||||
f.write('#include "' + check_name_camel + '.h"\n')
|
|
||||||
elif header_found:
|
|
||||||
header_added = True
|
|
||||||
f.write('#include "' + check_name_camel + '.h"\n')
|
|
||||||
|
|
||||||
if not check_added:
|
|
||||||
if line.strip() == '}':
|
|
||||||
check_added = True
|
|
||||||
f.write(check_decl)
|
|
||||||
else:
|
|
||||||
match = re.search('registerCheck<(.*)>', line)
|
|
||||||
if match and match.group(1) > check_name_camel:
|
|
||||||
check_added = True
|
|
||||||
f.write(check_decl)
|
|
||||||
f.write(line + '\n')
|
|
||||||
|
|
||||||
|
|
||||||
# Adds a test for the check.
|
|
||||||
def write_test(module_path, module, check_name):
|
|
||||||
check_name_dashes = module + '-' + check_name
|
|
||||||
filename = os.path.join(module_path, '../../test/clang-tidy',
|
|
||||||
check_name_dashes + '.cpp')
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write(
|
|
||||||
"""// RUN: $(dirname %%s)/check_clang_tidy.sh %%s %(check_name_dashes)s %%t
|
|
||||||
// REQUIRES: shell
|
|
||||||
|
|
||||||
// FIXME: Add something that triggers the check here.
|
|
||||||
void f();
|
|
||||||
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
|
|
||||||
|
|
||||||
// FIXME: Verify the applied fix.
|
|
||||||
// * Make the CHECK patterns specific enough and try to make verified lines
|
|
||||||
// unique to avoid incorrect matches.
|
|
||||||
// * Use {{}} for regular expressions.
|
|
||||||
// CHECK-FIXES: {{^}}void awesome_f();{{$}}
|
|
||||||
|
|
||||||
// FIXME: Add something that doesn't trigger the check here.
|
|
||||||
void awesome_f2();
|
|
||||||
""" % {"check_name_dashes" : check_name_dashes})
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if len(sys.argv) != 3:
|
|
||||||
print 'Usage: add_new_check.py <module> <check>, e.g.\n'
|
|
||||||
print 'add_new_check.py misc awesome-functions\n'
|
|
||||||
return
|
|
||||||
|
|
||||||
module = sys.argv[1]
|
|
||||||
check_name = sys.argv[2]
|
|
||||||
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
|
|
||||||
check_name.split('-'))) + 'Check'
|
|
||||||
clang_tidy_path = os.path.dirname(sys.argv[0])
|
|
||||||
module_path = os.path.join(clang_tidy_path, module)
|
|
||||||
|
|
||||||
if not adapt_cmake(module_path, check_name_camel):
|
|
||||||
return
|
|
||||||
write_header(module_path, module, check_name, check_name_camel)
|
|
||||||
write_implementation(module_path, check_name_camel)
|
|
||||||
adapt_module(module_path, module, check_name, check_name_camel)
|
|
||||||
write_test(module_path, module, check_name)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -28,7 +28,10 @@ AvoidCStyleCastsCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
|||||||
// FIXME: Remove this once this is fixed in the AST.
|
// FIXME: Remove this once this is fixed in the AST.
|
||||||
unless(hasParent(substNonTypeTemplateParmExpr())),
|
unless(hasParent(substNonTypeTemplateParmExpr())),
|
||||||
// Avoid matches in template instantiations.
|
// Avoid matches in template instantiations.
|
||||||
unless(isInTemplateInstantiation())).bind("cast"),
|
unless(hasAncestor(decl(
|
||||||
|
anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
|
||||||
|
functionDecl(ast_matchers::isTemplateInstantiation()))))))
|
||||||
|
.bind("cast"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,12 +60,6 @@ bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
|
|||||||
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||||
const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
|
const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
|
||||||
|
|
||||||
auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
|
|
||||||
CastExpr->getRParenLoc());
|
|
||||||
// Ignore casts in macros.
|
|
||||||
if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Casting to void is an idiomatic way to mute "unused variable" and similar
|
// Casting to void is an idiomatic way to mute "unused variable" and similar
|
||||||
// warnings.
|
// warnings.
|
||||||
if (CastExpr->getTypeAsWritten()->isVoidType())
|
if (CastExpr->getTypeAsWritten()->isVoidType())
|
||||||
@@ -72,29 +69,23 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
CastExpr->getSubExprAsWritten()->getType().getCanonicalType();
|
CastExpr->getSubExprAsWritten()->getType().getCanonicalType();
|
||||||
QualType DestType = CastExpr->getTypeAsWritten().getCanonicalType();
|
QualType DestType = CastExpr->getTypeAsWritten().getCanonicalType();
|
||||||
|
|
||||||
|
auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
|
||||||
|
CastExpr->getRParenLoc());
|
||||||
if (SourceType == DestType) {
|
if (SourceType == DestType) {
|
||||||
diag(CastExpr->getLocStart(), "Redundant cast to the same type.")
|
diag(CastExpr->getLocStart(), "Redundant cast to the same type.")
|
||||||
<< FixItHint::CreateRemoval(ParenRange);
|
<< FixItHint::CreateRemoval(ParenRange);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rest of this check is only relevant to C++.
|
std::string DestTypeString = CastExpr->getTypeAsWritten().getAsString();
|
||||||
if (!Result.Context->getLangOpts().CPlusPlus)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Leave type spelling exactly as it was (unlike
|
|
||||||
// getTypeAsWritten().getAsString() which would spell enum types 'enum X').
|
|
||||||
StringRef DestTypeString = Lexer::getSourceText(
|
|
||||||
CharSourceRange::getTokenRange(
|
|
||||||
CastExpr->getLParenLoc().getLocWithOffset(1),
|
|
||||||
CastExpr->getRParenLoc().getLocWithOffset(-1)),
|
|
||||||
*Result.SourceManager, Result.Context->getLangOpts());
|
|
||||||
|
|
||||||
auto diag_builder =
|
auto diag_builder =
|
||||||
diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0");
|
diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0");
|
||||||
|
|
||||||
auto ReplaceWithCast = [&](StringRef CastType) {
|
auto ReplaceWithCast = [&](StringRef CastType) {
|
||||||
diag_builder << ("Use " + CastType + ".").str();
|
diag_builder << ("Use " + CastType + ".").str();
|
||||||
|
if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
|
||||||
|
return;
|
||||||
|
|
||||||
const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
|
const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
|
||||||
std::string CastText = (CastType + "<" + DestTypeString + ">").str();
|
std::string CastText = (CastType + "<" + DestTypeString + ">").str();
|
||||||
@@ -123,13 +114,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
ReplaceWithCast("const_cast");
|
ReplaceWithCast("const_cast");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// FALLTHROUGH
|
if (SourceType->isBuiltinType() && DestType->isBuiltinType()) {
|
||||||
case clang::CK_IntegralCast:
|
|
||||||
// Convert integral and no-op casts between builtin types and enums to
|
|
||||||
// static_cast. A cast from enum to integer may be unnecessary, but it's
|
|
||||||
// still retained.
|
|
||||||
if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
|
|
||||||
(DestType->isBuiltinType() || DestType->isEnumeralType())) {
|
|
||||||
ReplaceWithCast("static_cast");
|
ReplaceWithCast("static_cast");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ namespace readability {
|
|||||||
/// ones generated by -Wold-style-cast.
|
/// ones generated by -Wold-style-cast.
|
||||||
class AvoidCStyleCastsCheck : public ClangTidyCheck {
|
class AvoidCStyleCastsCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
AvoidCStyleCastsCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ add_clang_library(clangTidyGoogleModule
|
|||||||
ExplicitConstructorCheck.cpp
|
ExplicitConstructorCheck.cpp
|
||||||
ExplicitMakePairCheck.cpp
|
ExplicitMakePairCheck.cpp
|
||||||
GoogleTidyModule.cpp
|
GoogleTidyModule.cpp
|
||||||
IntegerTypesCheck.cpp
|
|
||||||
MemsetZeroLengthCheck.cpp
|
MemsetZeroLengthCheck.cpp
|
||||||
NamedParameterCheck.cpp
|
NamedParameterCheck.cpp
|
||||||
OverloadedUnaryAndCheck.cpp
|
OverloadedUnaryAndCheck.cpp
|
||||||
StringReferenceMemberCheck.cpp
|
StringReferenceMemberCheck.cpp
|
||||||
TodoCommentCheck.cpp
|
|
||||||
UnnamedNamespaceInHeaderCheck.cpp
|
UnnamedNamespaceInHeaderCheck.cpp
|
||||||
UsingNamespaceDirectiveCheck.cpp
|
UsingNamespaceDirectiveCheck.cpp
|
||||||
|
|
||||||
@@ -20,5 +18,4 @@ add_clang_library(clangTidyGoogleModule
|
|||||||
clangBasic
|
clangBasic
|
||||||
clangLex
|
clangLex
|
||||||
clangTidy
|
clangTidy
|
||||||
clangTidyReadabilityModule
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ namespace clang {
|
|||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
|
void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
|
||||||
Finder->addMatcher(constructorDecl(unless(isInstantiated())).bind("ctor"),
|
Finder->addMatcher(constructorDecl().bind("ctor"), this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks for the token matching the predicate and returns the range of the found
|
// Looks for the token matching the predicate and returns the range of the found
|
||||||
@@ -48,33 +47,15 @@ SourceRange FindToken(const SourceManager &Sources, LangOptions LangOpts,
|
|||||||
return SourceRange();
|
return SourceRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isStdInitializerList(QualType Type) {
|
|
||||||
if (const RecordType *RT = Type.getCanonicalType()->getAs<RecordType>()) {
|
|
||||||
if (ClassTemplateSpecializationDecl *Specialization =
|
|
||||||
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) {
|
|
||||||
ClassTemplateDecl *Template = Specialization->getSpecializedTemplate();
|
|
||||||
// First use the fast getName() method to avoid unnecessary calls to the
|
|
||||||
// slow getQualifiedNameAsString().
|
|
||||||
return Template->getName() == "initializer_list" &&
|
|
||||||
Template->getQualifiedNameAsString() == "std::initializer_list";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
||||||
const CXXConstructorDecl *Ctor =
|
const CXXConstructorDecl *Ctor =
|
||||||
Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
|
Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
|
||||||
// Do not be confused: isExplicit means 'explicit' keyword is present,
|
// Do not be confused: isExplicit means 'explicit' keyword is present,
|
||||||
// isImplicit means that it's a compiler-generated constructor.
|
// isImplicit means that it's a compiler-generated constructor.
|
||||||
if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
|
if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted())
|
||||||
Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool takesInitializerList = isStdInitializerList(
|
if (Ctor->isExplicit() && Ctor->isCopyOrMoveConstructor()) {
|
||||||
Ctor->getParamDecl(0)->getType().getNonReferenceType());
|
|
||||||
if (Ctor->isExplicit() &&
|
|
||||||
(Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
|
|
||||||
auto isKWExplicit = [](const Token &Tok) {
|
auto isKWExplicit = [](const Token &Tok) {
|
||||||
return Tok.is(tok::raw_identifier) &&
|
return Tok.is(tok::raw_identifier) &&
|
||||||
Tok.getRawIdentifier() == "explicit";
|
Tok.getRawIdentifier() == "explicit";
|
||||||
@@ -82,31 +63,21 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
SourceRange ExplicitTokenRange =
|
SourceRange ExplicitTokenRange =
|
||||||
FindToken(*Result.SourceManager, Result.Context->getLangOpts(),
|
FindToken(*Result.SourceManager, Result.Context->getLangOpts(),
|
||||||
Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
|
Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
|
||||||
StringRef ConstructorDescription;
|
|
||||||
if (Ctor->isMoveConstructor())
|
|
||||||
ConstructorDescription = "move";
|
|
||||||
else if (Ctor->isCopyConstructor())
|
|
||||||
ConstructorDescription = "copy";
|
|
||||||
else
|
|
||||||
ConstructorDescription = "initializer-list";
|
|
||||||
|
|
||||||
DiagnosticBuilder Diag =
|
DiagnosticBuilder Diag =
|
||||||
diag(Ctor->getLocation(),
|
diag(Ctor->getLocation(), "%0 constructor declared explicit.")
|
||||||
"%0 constructor should not be declared explicit")
|
<< (Ctor->isMoveConstructor() ? "Move" : "Copy");
|
||||||
<< ConstructorDescription;
|
|
||||||
if (ExplicitTokenRange.isValid()) {
|
if (ExplicitTokenRange.isValid()) {
|
||||||
Diag << FixItHint::CreateRemoval(
|
Diag << FixItHint::CreateRemoval(
|
||||||
CharSourceRange::getCharRange(ExplicitTokenRange));
|
CharSourceRange::getCharRange(ExplicitTokenRange));
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
|
if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
|
||||||
takesInitializerList)
|
Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SourceLocation Loc = Ctor->getLocation();
|
SourceLocation Loc = Ctor->getLocation();
|
||||||
diag(Loc, "single-argument constructors must be explicit")
|
diag(Loc, "Single-argument constructors must be explicit")
|
||||||
<< FixItHint::CreateInsertion(Loc, "explicit ");
|
<< FixItHint::CreateInsertion(Loc, "explicit ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ namespace tidy {
|
|||||||
/// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors
|
/// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors
|
||||||
class ExplicitConstructorCheck : public ClangTidyCheck {
|
class ExplicitConstructorCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
ExplicitConstructorCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ namespace ast_matchers {
|
|||||||
AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
|
AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
|
||||||
return Node.hasExplicitTemplateArgs();
|
return Node.hasExplicitTemplateArgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This should just be callee(ignoringImpCasts()) but it's not overloaded
|
||||||
|
// for Expr.
|
||||||
|
AST_MATCHER_P(CallExpr, calleeIgnoringParenImpCasts, internal::Matcher<Stmt>,
|
||||||
|
InnerMatcher) {
|
||||||
|
const Expr *ExprNode = Node.getCallee();
|
||||||
|
return (ExprNode != nullptr &&
|
||||||
|
InnerMatcher.matches(*ExprNode->IgnoreParenImpCasts(), Finder, Builder));
|
||||||
|
}
|
||||||
} // namespace ast_matchers
|
} // namespace ast_matchers
|
||||||
|
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
@@ -30,11 +39,13 @@ ExplicitMakePairCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
|||||||
// Look for std::make_pair with explicit template args. Ignore calls in
|
// Look for std::make_pair with explicit template args. Ignore calls in
|
||||||
// templates.
|
// templates.
|
||||||
Finder->addMatcher(
|
Finder->addMatcher(
|
||||||
callExpr(unless(isInTemplateInstantiation()),
|
callExpr(unless(hasAncestor(decl(anyOf(
|
||||||
callee(expr(ignoringParenImpCasts(
|
recordDecl(ast_matchers::isTemplateInstantiation()),
|
||||||
|
functionDecl(ast_matchers::isTemplateInstantiation()))))),
|
||||||
|
calleeIgnoringParenImpCasts(
|
||||||
declRefExpr(hasExplicitTemplateArgs(),
|
declRefExpr(hasExplicitTemplateArgs(),
|
||||||
to(functionDecl(hasName("::std::make_pair"))))
|
to(functionDecl(hasName("::std::make_pair"))))
|
||||||
.bind("declref"))))).bind("call"),
|
.bind("declref"))).bind("call"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ namespace build {
|
|||||||
/// Corresponding cpplint.py check name: 'build/explicit_make_pair'.
|
/// Corresponding cpplint.py check name: 'build/explicit_make_pair'.
|
||||||
class ExplicitMakePairCheck : public ClangTidyCheck {
|
class ExplicitMakePairCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
ExplicitMakePairCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,18 +13,12 @@
|
|||||||
#include "AvoidCStyleCastsCheck.h"
|
#include "AvoidCStyleCastsCheck.h"
|
||||||
#include "ExplicitConstructorCheck.h"
|
#include "ExplicitConstructorCheck.h"
|
||||||
#include "ExplicitMakePairCheck.h"
|
#include "ExplicitMakePairCheck.h"
|
||||||
#include "IntegerTypesCheck.h"
|
|
||||||
#include "MemsetZeroLengthCheck.h"
|
#include "MemsetZeroLengthCheck.h"
|
||||||
#include "NamedParameterCheck.h"
|
#include "NamedParameterCheck.h"
|
||||||
#include "OverloadedUnaryAndCheck.h"
|
#include "OverloadedUnaryAndCheck.h"
|
||||||
#include "StringReferenceMemberCheck.h"
|
#include "StringReferenceMemberCheck.h"
|
||||||
#include "TodoCommentCheck.h"
|
|
||||||
#include "UnnamedNamespaceInHeaderCheck.h"
|
#include "UnnamedNamespaceInHeaderCheck.h"
|
||||||
#include "UsingNamespaceDirectiveCheck.h"
|
#include "UsingNamespaceDirectiveCheck.h"
|
||||||
#include "../readability/BracesAroundStatementsCheck.h"
|
|
||||||
#include "../readability/FunctionSize.h"
|
|
||||||
#include "../readability/NamespaceCommentCheck.h"
|
|
||||||
#include "../readability/RedundantSmartptrGet.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
using namespace clang::ast_matchers;
|
||||||
|
|
||||||
@@ -34,47 +28,33 @@ namespace tidy {
|
|||||||
class GoogleModule : public ClangTidyModule {
|
class GoogleModule : public ClangTidyModule {
|
||||||
public:
|
public:
|
||||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||||
CheckFactories.registerCheck<build::ExplicitMakePairCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"google-build-explicit-make-pair");
|
"google-build-explicit-make-pair",
|
||||||
CheckFactories.registerCheck<build::UnnamedNamespaceInHeaderCheck>(
|
new ClangTidyCheckFactory<build::ExplicitMakePairCheck>());
|
||||||
"google-build-namespaces");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<build::UsingNamespaceDirectiveCheck>(
|
"google-build-namespaces",
|
||||||
"google-build-using-namespace");
|
new ClangTidyCheckFactory<build::UnnamedNamespaceInHeaderCheck>());
|
||||||
CheckFactories.registerCheck<ExplicitConstructorCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"google-explicit-constructor");
|
"google-build-using-namespace",
|
||||||
CheckFactories.registerCheck<runtime::IntegerTypesCheck>(
|
new ClangTidyCheckFactory<build::UsingNamespaceDirectiveCheck>());
|
||||||
"google-runtime-int");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
|
"google-explicit-constructor",
|
||||||
"google-runtime-operator");
|
new ClangTidyCheckFactory<ExplicitConstructorCheck>());
|
||||||
CheckFactories.registerCheck<runtime::StringReferenceMemberCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"google-runtime-member-string-references");
|
"google-runtime-operator",
|
||||||
CheckFactories.registerCheck<runtime::MemsetZeroLengthCheck>(
|
new ClangTidyCheckFactory<runtime::OverloadedUnaryAndCheck>());
|
||||||
"google-runtime-memset");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<readability::AvoidCStyleCastsCheck>(
|
"google-runtime-member-string-references",
|
||||||
"google-readability-casting");
|
new ClangTidyCheckFactory<runtime::StringReferenceMemberCheck>());
|
||||||
CheckFactories.registerCheck<readability::NamedParameterCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"google-readability-function");
|
"google-runtime-memset",
|
||||||
CheckFactories.registerCheck<readability::TodoCommentCheck>(
|
new ClangTidyCheckFactory<runtime::MemsetZeroLengthCheck>());
|
||||||
"google-readability-todo");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<readability::BracesAroundStatementsCheck>(
|
"google-readability-casting",
|
||||||
"google-readability-braces-around-statements");
|
new ClangTidyCheckFactory<readability::AvoidCStyleCastsCheck>());
|
||||||
CheckFactories.registerCheck<readability::FunctionSizeCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"google-readability-function-size");
|
"google-readability-function",
|
||||||
CheckFactories.registerCheck<readability::NamespaceCommentCheck>(
|
new ClangTidyCheckFactory<readability::NamedParameterCheck>());
|
||||||
"google-readability-namespace-comments");
|
|
||||||
CheckFactories.registerCheck<readability::RedundantSmartptrGet>(
|
|
||||||
"google-readability-redundant-smartptr-get");
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTidyOptions getModuleOptions() override {
|
|
||||||
ClangTidyOptions Options;
|
|
||||||
auto &Opts = Options.CheckOptions;
|
|
||||||
Opts["google-readability-braces-around-statements.ShortStatementLines"] =
|
|
||||||
"1";
|
|
||||||
Opts["google-readability-function-size.StatementThreshold"] = "800";
|
|
||||||
Opts["google-readability-namespace-comments.ShortNamespaceLines"] = "10";
|
|
||||||
Opts["google-readability-namespace-comments.SpacesBeforeComments"] = "2";
|
|
||||||
return Options;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
//===--- IntegerTypesCheck.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 "IntegerTypesCheck.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
||||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
||||||
#include "clang/Basic/CharInfo.h"
|
|
||||||
#include "clang/Basic/TargetInfo.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
namespace runtime {
|
|
||||||
|
|
||||||
using namespace ast_matchers;
|
|
||||||
|
|
||||||
void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) {
|
|
||||||
// Find all TypeLocs.
|
|
||||||
Finder->addMatcher(typeLoc().bind("tl"), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IntegerTypesCheck::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
// The relevant Style Guide rule only applies to C++.
|
|
||||||
if (!Result.Context->getLangOpts().CPlusPlus)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto TL = *Result.Nodes.getNodeAs<TypeLoc>("tl");
|
|
||||||
SourceLocation Loc = TL.getLocStart();
|
|
||||||
|
|
||||||
if (Loc.isInvalid() || Loc.isMacroID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Look through qualification.
|
|
||||||
if (auto QualLoc = TL.getAs<QualifiedTypeLoc>())
|
|
||||||
TL = QualLoc.getUnqualifiedLoc();
|
|
||||||
|
|
||||||
auto BuiltinLoc = TL.getAs<BuiltinTypeLoc>();
|
|
||||||
if (!BuiltinLoc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool IsSigned;
|
|
||||||
unsigned Width;
|
|
||||||
const TargetInfo &TargetInfo = Result.Context->getTargetInfo();
|
|
||||||
|
|
||||||
// Look for uses of short, long, long long and their unsigned versions.
|
|
||||||
switch (BuiltinLoc.getTypePtr()->getKind()) {
|
|
||||||
case BuiltinType::Short:
|
|
||||||
Width = TargetInfo.getShortWidth();
|
|
||||||
IsSigned = true;
|
|
||||||
break;
|
|
||||||
case BuiltinType::Long:
|
|
||||||
Width = TargetInfo.getLongWidth();
|
|
||||||
IsSigned = true;
|
|
||||||
break;
|
|
||||||
case BuiltinType::LongLong:
|
|
||||||
Width = TargetInfo.getLongLongWidth();
|
|
||||||
IsSigned = true;
|
|
||||||
break;
|
|
||||||
case BuiltinType::UShort:
|
|
||||||
Width = TargetInfo.getShortWidth();
|
|
||||||
IsSigned = false;
|
|
||||||
break;
|
|
||||||
case BuiltinType::ULong:
|
|
||||||
Width = TargetInfo.getLongWidth();
|
|
||||||
IsSigned = false;
|
|
||||||
break;
|
|
||||||
case BuiltinType::ULongLong:
|
|
||||||
Width = TargetInfo.getLongLongWidth();
|
|
||||||
IsSigned = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We allow "unsigned short port" as that's reasonably common and required by
|
|
||||||
// the sockets API.
|
|
||||||
const StringRef Port = "unsigned short port";
|
|
||||||
const char *Data = Result.SourceManager->getCharacterData(Loc);
|
|
||||||
if (!std::strncmp(Data, Port.data(), Port.size()) &&
|
|
||||||
!isIdentifierBody(Data[Port.size()]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string Replacement =
|
|
||||||
((IsSigned ? SignedTypePrefix : UnsignedTypePrefix) + Twine(Width) +
|
|
||||||
(AddUnderscoreT ? "_t" : "")).str();
|
|
||||||
|
|
||||||
// We don't add a fix-it as changing the type can easily break code,
|
|
||||||
// e.g. when a function requires a 'long' argument on all platforms.
|
|
||||||
// QualTypes are printed with implicit quotes.
|
|
||||||
diag(Loc, "consider replacing %0 with '%1'") << BuiltinLoc.getType()
|
|
||||||
<< Replacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace runtime
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
//===--- IntegerTypesCheck.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_INTEGERTYPESCHECK_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
namespace runtime {
|
|
||||||
|
|
||||||
/// \brief Finds uses of short, long and long long and suggest replacing them
|
|
||||||
/// with u?intXX(_t)?.
|
|
||||||
/// Correspondig cpplint.py check: runtime/int.
|
|
||||||
class IntegerTypesCheck : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
IntegerTypesCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context), UnsignedTypePrefix("uint"),
|
|
||||||
SignedTypePrefix("int"), AddUnderscoreT(false) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const StringRef UnsignedTypePrefix;
|
|
||||||
const StringRef SignedTypePrefix;
|
|
||||||
const bool AddUnderscoreT;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace runtime
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
|
|
||||||
@@ -21,12 +21,15 @@ namespace runtime {
|
|||||||
|
|
||||||
void
|
void
|
||||||
MemsetZeroLengthCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
MemsetZeroLengthCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||||
|
auto InTemplateInstantiation = hasAncestor(
|
||||||
|
decl(anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
|
||||||
|
functionDecl(ast_matchers::isTemplateInstantiation()))));
|
||||||
// Look for memset(x, y, 0) as those is most likely an argument swap.
|
// 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
|
// TODO: Also handle other standard functions that suffer from the same
|
||||||
// problem, e.g. memchr.
|
// problem, e.g. memchr.
|
||||||
Finder->addMatcher(
|
Finder->addMatcher(
|
||||||
callExpr(callee(functionDecl(hasName("::memset"))), argumentCountIs(3),
|
callExpr(callee(functionDecl(hasName("::memset"))), argumentCountIs(3),
|
||||||
unless(isInTemplateInstantiation())).bind("decl"),
|
unless(InTemplateInstantiation)).bind("decl"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ namespace runtime {
|
|||||||
/// Corresponding cpplint.py check name: 'runtime/memset'.
|
/// Corresponding cpplint.py check name: 'runtime/memset'.
|
||||||
class MemsetZeroLengthCheck : public ClangTidyCheck {
|
class MemsetZeroLengthCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
MemsetZeroLengthCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ namespace tidy {
|
|||||||
namespace readability {
|
namespace readability {
|
||||||
|
|
||||||
void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||||
Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("decl"), this);
|
Finder->addMatcher(
|
||||||
|
functionDecl(
|
||||||
|
unless(hasAncestor(decl(
|
||||||
|
anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
|
||||||
|
functionDecl(ast_matchers::isTemplateInstantiation()))))))
|
||||||
|
.bind("decl"),
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
|
void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
|
||||||
@@ -31,15 +37,6 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
if (Function->isImplicit())
|
if (Function->isImplicit())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Ignore declarations without a definition if we're not dealing with an
|
|
||||||
// overriden method.
|
|
||||||
const FunctionDecl *Definition = nullptr;
|
|
||||||
if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
|
|
||||||
Function->isDeleted()) &&
|
|
||||||
(!isa<CXXMethodDecl>(Function) ||
|
|
||||||
cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: Handle overloads.
|
// TODO: Handle overloads.
|
||||||
// TODO: We could check that all redeclarations use the same name for
|
// TODO: We could check that all redeclarations use the same name for
|
||||||
// arguments in the same position.
|
// arguments in the same position.
|
||||||
@@ -49,26 +46,11 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
if (!Parm->getName().empty())
|
if (!Parm->getName().empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't warn on the dummy argument on post-inc and post-dec operators.
|
|
||||||
if ((Function->getOverloadedOperator() == OO_PlusPlus ||
|
|
||||||
Function->getOverloadedOperator() == OO_MinusMinus) &&
|
|
||||||
Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Sanity check the source locations.
|
// Sanity check the source locations.
|
||||||
if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
|
if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
|
||||||
!SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
|
!SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip gmock testing::Unused parameters.
|
|
||||||
if (auto Typedef = Parm->getType()->getAs<clang::TypedefType>())
|
|
||||||
if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Skip std::nullptr_t.
|
|
||||||
if (Parm->getType().getCanonicalType()->isNullPtrType())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Look for comments. We explicitly want to allow idioms like
|
// Look for comments. We explicitly want to allow idioms like
|
||||||
// void foo(int /*unused*/)
|
// void foo(int /*unused*/)
|
||||||
const char *Begin = SM.getCharacterData(Parm->getLocStart());
|
const char *Begin = SM.getCharacterData(Parm->getLocStart());
|
||||||
@@ -88,34 +70,25 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
|
|||||||
"all parameters should be named in a function");
|
"all parameters should be named in a function");
|
||||||
|
|
||||||
for (auto P : UnnamedParams) {
|
for (auto P : UnnamedParams) {
|
||||||
// Fallback to an unused marker.
|
|
||||||
StringRef NewName = "unused";
|
|
||||||
|
|
||||||
// If the method is overridden, try to copy the name from the base method
|
// If the method is overridden, try to copy the name from the base method
|
||||||
// into the overrider.
|
// into the overrider.
|
||||||
|
const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
|
||||||
const auto *M = dyn_cast<CXXMethodDecl>(P.first);
|
const auto *M = dyn_cast<CXXMethodDecl>(P.first);
|
||||||
if (M && M->size_overridden_methods() > 0) {
|
if (M && M->size_overridden_methods() > 0) {
|
||||||
const ParmVarDecl *OtherParm =
|
const ParmVarDecl *OtherParm =
|
||||||
(*M->begin_overridden_methods())->getParamDecl(P.second);
|
(*M->begin_overridden_methods())->getParamDecl(P.second);
|
||||||
StringRef Name = OtherParm->getName();
|
std::string Name = OtherParm->getNameAsString();
|
||||||
if (!Name.empty())
|
if (!Name.empty()) {
|
||||||
NewName = Name;
|
D << FixItHint::CreateInsertion(Parm->getLocation(),
|
||||||
|
" /*" + Name + "*/");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the definition has a named parameter use that name.
|
// Otherwise just insert an unused marker. Note that getLocation() points
|
||||||
if (Definition) {
|
// to the place where the name would be, this allows us to also get
|
||||||
const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
|
// complex cases like function pointers right.
|
||||||
StringRef Name = DefParm->getName();
|
D << FixItHint::CreateInsertion(Parm->getLocation(), " /*unused*/");
|
||||||
if (!Name.empty())
|
|
||||||
NewName = Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now insert the comment. Note that getLocation() points to the place
|
|
||||||
// where the name would be, this allows us to also get complex cases like
|
|
||||||
// function pointers right.
|
|
||||||
const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
|
|
||||||
D << FixItHint::CreateInsertion(Parm->getLocation(),
|
|
||||||
" /*" + NewName.str() + "*/");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace readability {
|
|||||||
/// Corresponding cpplint.py check name: 'readability/function'.
|
/// Corresponding cpplint.py check name: 'readability/function'.
|
||||||
class NamedParameterCheck : public ClangTidyCheck {
|
class NamedParameterCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace runtime {
|
|||||||
/// Corresponding cpplint.py check name: 'runtime/operator'.
|
/// Corresponding cpplint.py check name: 'runtime/operator'.
|
||||||
class OverloadedUnaryAndCheck : public ClangTidyCheck {
|
class OverloadedUnaryAndCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
OverloadedUnaryAndCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,8 +26,12 @@ void StringReferenceMemberCheck::registerMatchers(
|
|||||||
auto ConstString = qualType(isConstQualified(), hasDeclaration(String));
|
auto ConstString = qualType(isConstQualified(), hasDeclaration(String));
|
||||||
|
|
||||||
// Ignore members in template instantiations.
|
// Ignore members in template instantiations.
|
||||||
|
auto InTemplateInstantiation = hasAncestor(
|
||||||
|
decl(anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
|
||||||
|
functionDecl(ast_matchers::isTemplateInstantiation()))));
|
||||||
|
|
||||||
Finder->addMatcher(fieldDecl(hasType(references(ConstString)),
|
Finder->addMatcher(fieldDecl(hasType(references(ConstString)),
|
||||||
unless(isInstantiated())).bind("member"),
|
unless(InTemplateInstantiation)).bind("member"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ namespace runtime {
|
|||||||
/// Corresponding cpplint.py check name: 'runtime/member_string_reference'.
|
/// Corresponding cpplint.py check name: 'runtime/member_string_reference'.
|
||||||
class StringReferenceMemberCheck : public ClangTidyCheck {
|
class StringReferenceMemberCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
StringReferenceMemberCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
//===--- TodoCommentCheck.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 "TodoCommentCheck.h"
|
|
||||||
#include "clang/Frontend/CompilerInstance.h"
|
|
||||||
#include "clang/Lex/Preprocessor.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
namespace readability {
|
|
||||||
|
|
||||||
class TodoCommentCheck::TodoCommentHandler : public CommentHandler {
|
|
||||||
public:
|
|
||||||
TodoCommentHandler(TodoCommentCheck &Check, llvm::Optional<std::string> User)
|
|
||||||
: Check(Check), User(User ? *User : "unknown"),
|
|
||||||
TodoMatch("^// *TODO *(\\(.*\\))?:?( )?(.*)$") {}
|
|
||||||
|
|
||||||
bool HandleComment(Preprocessor &PP, SourceRange Range) override {
|
|
||||||
StringRef Text =
|
|
||||||
Lexer::getSourceText(CharSourceRange::getCharRange(Range),
|
|
||||||
PP.getSourceManager(), PP.getLangOpts());
|
|
||||||
|
|
||||||
SmallVector<StringRef, 4> Matches;
|
|
||||||
if (!TodoMatch.match(Text, &Matches))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
StringRef Username = Matches[1];
|
|
||||||
StringRef Comment = Matches[3];
|
|
||||||
|
|
||||||
if (!Username.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string NewText = ("// TODO(" + Twine(User) + "): " + Comment).str();
|
|
||||||
|
|
||||||
Check.diag(Range.getBegin(), "missing username/bug in TODO")
|
|
||||||
<< FixItHint::CreateReplacement(CharSourceRange::getCharRange(Range),
|
|
||||||
NewText);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
TodoCommentCheck &Check;
|
|
||||||
std::string User;
|
|
||||||
llvm::Regex TodoMatch;
|
|
||||||
};
|
|
||||||
|
|
||||||
TodoCommentCheck::TodoCommentCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context),
|
|
||||||
Handler(llvm::make_unique<TodoCommentHandler>(
|
|
||||||
*this, Context->getOptions().User)) {}
|
|
||||||
|
|
||||||
void TodoCommentCheck::registerPPCallbacks(CompilerInstance &Compiler) {
|
|
||||||
Compiler.getPreprocessor().addCommentHandler(Handler.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace readability
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
//===--- TodoCommentCheck.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_TODOCOMMENTCHECK_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
namespace readability {
|
|
||||||
|
|
||||||
/// \brief Finds TODO comments without a username or bug number.
|
|
||||||
///
|
|
||||||
/// Corresponding cpplint.py check: readability/todo
|
|
||||||
class TodoCommentCheck : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
TodoCommentCheck(StringRef Name, ClangTidyContext *Context);
|
|
||||||
void registerPPCallbacks(CompilerInstance &Compiler) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class TodoCommentHandler;
|
|
||||||
std::unique_ptr<TodoCommentHandler> Handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace readability
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
|
|
||||||
@@ -22,8 +22,6 @@ namespace build {
|
|||||||
/// Corresponding cpplint.py check name: 'build/namespaces'.
|
/// Corresponding cpplint.py check name: 'build/namespaces'.
|
||||||
class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
|
class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
UnnamedNamespaceInHeaderCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace build {
|
|||||||
/// Corresponding cpplint.py check name: 'build/namespaces'.
|
/// Corresponding cpplint.py check name: 'build/namespaces'.
|
||||||
class UsingNamespaceDirectiveCheck : public ClangTidyCheck {
|
class UsingNamespaceDirectiveCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
UsingNamespaceDirectiveCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
set(LLVM_LINK_COMPONENTS support)
|
set(LLVM_LINK_COMPONENTS support)
|
||||||
|
|
||||||
add_clang_library(clangTidyLLVMModule
|
add_clang_library(clangTidyLLVMModule
|
||||||
HeaderGuardCheck.cpp
|
|
||||||
IncludeOrderCheck.cpp
|
IncludeOrderCheck.cpp
|
||||||
LLVMTidyModule.cpp
|
LLVMTidyModule.cpp
|
||||||
|
NamespaceCommentCheck.cpp
|
||||||
TwineLocalCheck.cpp
|
TwineLocalCheck.cpp
|
||||||
|
|
||||||
LINK_LIBS
|
LINK_LIBS
|
||||||
@@ -12,7 +12,4 @@ add_clang_library(clangTidyLLVMModule
|
|||||||
clangBasic
|
clangBasic
|
||||||
clangLex
|
clangLex
|
||||||
clangTidy
|
clangTidy
|
||||||
clangTidyReadabilityModule
|
|
||||||
clangTidyUtils
|
|
||||||
clangTooling
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
//===--- HeaderGuardCheck.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 "HeaderGuardCheck.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
bool LLVMHeaderGuardCheck::shouldFixHeaderGuard(StringRef Filename) {
|
|
||||||
return Filename.endswith(".h");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename,
|
|
||||||
StringRef OldGuard) {
|
|
||||||
std::string Guard = tooling::getAbsolutePath(Filename);
|
|
||||||
|
|
||||||
// Sanitize the path. There are some rules for compatibility with the historic
|
|
||||||
// style in include/llvm and include/clang which we want to preserve.
|
|
||||||
|
|
||||||
// We don't want _INCLUDE_ in our guards.
|
|
||||||
size_t PosInclude = Guard.rfind("include/");
|
|
||||||
if (PosInclude != StringRef::npos)
|
|
||||||
Guard = Guard.substr(PosInclude + std::strlen("include/"));
|
|
||||||
|
|
||||||
// For clang we drop the _TOOLS_.
|
|
||||||
size_t PosToolsClang = Guard.rfind("tools/clang/");
|
|
||||||
if (PosToolsClang != StringRef::npos)
|
|
||||||
Guard = Guard.substr(PosToolsClang + std::strlen("tools/"));
|
|
||||||
|
|
||||||
// The remainder is LLVM_FULL_PATH_TO_HEADER_H
|
|
||||||
size_t PosLLVM = Guard.rfind("llvm/");
|
|
||||||
if (PosLLVM != StringRef::npos)
|
|
||||||
Guard = Guard.substr(PosLLVM);
|
|
||||||
|
|
||||||
std::replace(Guard.begin(), Guard.end(), '/', '_');
|
|
||||||
std::replace(Guard.begin(), Guard.end(), '.', '_');
|
|
||||||
std::replace(Guard.begin(), Guard.end(), '-', '_');
|
|
||||||
|
|
||||||
// The prevalent style in clang is LLVM_CLANG_FOO_BAR_H
|
|
||||||
if (StringRef(Guard).startswith("clang"))
|
|
||||||
Guard = "LLVM_" + Guard;
|
|
||||||
|
|
||||||
return StringRef(Guard).upper();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//===--- HeaderGuardCheck.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_LLVM_HEADER_GUARD_CHECK_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
|
|
||||||
|
|
||||||
#include "../utils/HeaderGuard.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
/// Finds and fixes header guards that do not adhere to LLVM style.
|
|
||||||
class LLVMHeaderGuardCheck : public HeaderGuardCheck {
|
|
||||||
public:
|
|
||||||
LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: HeaderGuardCheck(Name, Context) {}
|
|
||||||
bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
|
|
||||||
bool shouldFixHeaderGuard(StringRef Filename) override;
|
|
||||||
std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
|
|
||||||
@@ -18,149 +18,26 @@ namespace tidy {
|
|||||||
namespace {
|
namespace {
|
||||||
class IncludeOrderPPCallbacks : public PPCallbacks {
|
class IncludeOrderPPCallbacks : public PPCallbacks {
|
||||||
public:
|
public:
|
||||||
explicit IncludeOrderPPCallbacks(ClangTidyCheck &Check, SourceManager &SM)
|
explicit IncludeOrderPPCallbacks(IncludeOrderCheck &Check) : Check(Check) {}
|
||||||
: LookForMainModule(true), Check(Check), SM(SM) {}
|
|
||||||
|
|
||||||
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
||||||
StringRef FileName, bool IsAngled,
|
StringRef FileName, bool IsAngled,
|
||||||
CharSourceRange FilenameRange, const FileEntry *File,
|
CharSourceRange FilenameRange, const FileEntry *File,
|
||||||
StringRef SearchPath, StringRef RelativePath,
|
StringRef SearchPath, StringRef RelativePath,
|
||||||
const Module *Imported) override;
|
const Module *Imported) override {
|
||||||
void EndOfMainFile() override;
|
// FIXME: This is a dummy implementation to show how to get at preprocessor
|
||||||
|
// information. Implement a real include order check.
|
||||||
|
Check.diag(HashLoc, "This is an include");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IncludeDirective {
|
IncludeOrderCheck &Check;
|
||||||
SourceLocation Loc; ///< '#' location in the include directive
|
|
||||||
CharSourceRange Range; ///< SourceRange for the file name
|
|
||||||
StringRef Filename; ///< Filename as a string
|
|
||||||
bool IsAngled; ///< true if this was an include with angle brackets
|
|
||||||
bool IsMainModule; ///< true if this was the first include in a file
|
|
||||||
};
|
|
||||||
std::vector<IncludeDirective> IncludeDirectives;
|
|
||||||
bool LookForMainModule;
|
|
||||||
|
|
||||||
ClangTidyCheck &Check;
|
|
||||||
SourceManager &SM;
|
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) {
|
void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) {
|
||||||
Compiler.getPreprocessor().addPPCallbacks(
|
Compiler.getPreprocessor()
|
||||||
llvm::make_unique<IncludeOrderPPCallbacks>(*this,
|
.addPPCallbacks(new IncludeOrderPPCallbacks(*this));
|
||||||
Compiler.getSourceManager()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule) {
|
|
||||||
// We leave the main module header at the top.
|
|
||||||
if (IsMainModule)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// LLVM and clang headers are in the penultimate position.
|
|
||||||
if (Filename.startswith("llvm/") || Filename.startswith("llvm-c/") ||
|
|
||||||
Filename.startswith("clang/") || Filename.startswith("clang-c/"))
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
// System headers are sorted to the end.
|
|
||||||
if (IsAngled || Filename.startswith("gtest/"))
|
|
||||||
return 3;
|
|
||||||
|
|
||||||
// Other headers are inserted between the main module header and LLVM headers.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IncludeOrderPPCallbacks::InclusionDirective(
|
|
||||||
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
|
|
||||||
bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
|
|
||||||
StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
|
|
||||||
// We recognize the first include as a special main module header and want
|
|
||||||
// to leave it in the top position.
|
|
||||||
IncludeDirective ID = {HashLoc, FilenameRange, FileName, IsAngled, false};
|
|
||||||
if (LookForMainModule && !IsAngled) {
|
|
||||||
ID.IsMainModule = true;
|
|
||||||
LookForMainModule = false;
|
|
||||||
}
|
|
||||||
IncludeDirectives.push_back(std::move(ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
void IncludeOrderPPCallbacks::EndOfMainFile() {
|
|
||||||
LookForMainModule = true;
|
|
||||||
if (IncludeDirectives.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: find duplicated includes.
|
|
||||||
|
|
||||||
// Form blocks of includes. We don't want to sort across blocks. This also
|
|
||||||
// implicitly makes us never reorder over #defines or #if directives.
|
|
||||||
// FIXME: We should be more careful about sorting below comments as we don't
|
|
||||||
// know if the comment refers to the next include or the whole block that
|
|
||||||
// follows.
|
|
||||||
std::vector<unsigned> Blocks(1, 0);
|
|
||||||
for (unsigned I = 1, E = IncludeDirectives.size(); I != E; ++I)
|
|
||||||
if (SM.getExpansionLineNumber(IncludeDirectives[I].Loc) !=
|
|
||||||
SM.getExpansionLineNumber(IncludeDirectives[I - 1].Loc) + 1)
|
|
||||||
Blocks.push_back(I);
|
|
||||||
Blocks.push_back(IncludeDirectives.size()); // Sentinel value.
|
|
||||||
|
|
||||||
// Get a vector of indices.
|
|
||||||
std::vector<unsigned> IncludeIndices;
|
|
||||||
for (unsigned I = 0, E = IncludeDirectives.size(); I != E; ++I)
|
|
||||||
IncludeIndices.push_back(I);
|
|
||||||
|
|
||||||
// Sort the includes. We first sort by priority, then lexicographically.
|
|
||||||
for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
|
|
||||||
std::sort(IncludeIndices.begin() + Blocks[BI],
|
|
||||||
IncludeIndices.begin() + Blocks[BI + 1],
|
|
||||||
[this](unsigned LHSI, unsigned RHSI) {
|
|
||||||
IncludeDirective &LHS = IncludeDirectives[LHSI];
|
|
||||||
IncludeDirective &RHS = IncludeDirectives[RHSI];
|
|
||||||
|
|
||||||
int PriorityLHS =
|
|
||||||
getPriority(LHS.Filename, LHS.IsAngled, LHS.IsMainModule);
|
|
||||||
int PriorityRHS =
|
|
||||||
getPriority(RHS.Filename, RHS.IsAngled, RHS.IsMainModule);
|
|
||||||
|
|
||||||
return std::tie(PriorityLHS, LHS.Filename) <
|
|
||||||
std::tie(PriorityRHS, RHS.Filename);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit a warning for each block and fixits for all changes within that block.
|
|
||||||
for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
|
|
||||||
// Find the first include that's not in the right position.
|
|
||||||
unsigned I, E;
|
|
||||||
for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I)
|
|
||||||
if (IncludeIndices[I] != I)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (I == E)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Emit a warning.
|
|
||||||
auto D = Check.diag(IncludeDirectives[I].Loc,
|
|
||||||
"#includes are not sorted properly");
|
|
||||||
|
|
||||||
// Emit fix-its for all following includes in this block.
|
|
||||||
for (; I != E; ++I) {
|
|
||||||
if (IncludeIndices[I] == I)
|
|
||||||
continue;
|
|
||||||
const IncludeDirective &CopyFrom = IncludeDirectives[IncludeIndices[I]];
|
|
||||||
|
|
||||||
SourceLocation FromLoc = CopyFrom.Range.getBegin();
|
|
||||||
const char *FromData = SM.getCharacterData(FromLoc);
|
|
||||||
unsigned FromLen = std::strcspn(FromData, "\n");
|
|
||||||
|
|
||||||
StringRef FixedName(FromData, FromLen);
|
|
||||||
|
|
||||||
SourceLocation ToLoc = IncludeDirectives[I].Range.getBegin();
|
|
||||||
const char *ToData = SM.getCharacterData(ToLoc);
|
|
||||||
unsigned ToLen = std::strcspn(ToData, "\n");
|
|
||||||
auto ToRange =
|
|
||||||
CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
|
|
||||||
|
|
||||||
D << FixItHint::CreateReplacement(ToRange, FixedName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IncludeDirectives.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ namespace tidy {
|
|||||||
/// see: http://llvm.org/docs/CodingStandards.html#include-style
|
/// see: http://llvm.org/docs/CodingStandards.html#include-style
|
||||||
class IncludeOrderCheck : public ClangTidyCheck {
|
class IncludeOrderCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
IncludeOrderCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerPPCallbacks(CompilerInstance &Compiler) override;
|
void registerPPCallbacks(CompilerInstance &Compiler) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@
|
|||||||
#include "../ClangTidy.h"
|
#include "../ClangTidy.h"
|
||||||
#include "../ClangTidyModule.h"
|
#include "../ClangTidyModule.h"
|
||||||
#include "../ClangTidyModuleRegistry.h"
|
#include "../ClangTidyModuleRegistry.h"
|
||||||
#include "HeaderGuardCheck.h"
|
|
||||||
#include "IncludeOrderCheck.h"
|
#include "IncludeOrderCheck.h"
|
||||||
#include "../readability/NamespaceCommentCheck.h"
|
#include "NamespaceCommentCheck.h"
|
||||||
#include "TwineLocalCheck.h"
|
#include "TwineLocalCheck.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
@@ -21,11 +20,14 @@ namespace tidy {
|
|||||||
class LLVMModule : public ClangTidyModule {
|
class LLVMModule : public ClangTidyModule {
|
||||||
public:
|
public:
|
||||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||||
CheckFactories.registerCheck<LLVMHeaderGuardCheck>("llvm-header-guard");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<IncludeOrderCheck>("llvm-include-order");
|
"llvm-include-order", new ClangTidyCheckFactory<IncludeOrderCheck>());
|
||||||
CheckFactories.registerCheck<readability::NamespaceCommentCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"llvm-namespace-comment");
|
"llvm-namespace-comment",
|
||||||
CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
|
new ClangTidyCheckFactory<NamespaceCommentCheck>());
|
||||||
|
CheckFactories.addCheckFactory(
|
||||||
|
"llvm-twine-local",
|
||||||
|
new ClangTidyCheckFactory<TwineLocalCheck>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
117
clang-tools-extra/clang-tidy/llvm/NamespaceCommentCheck.cpp
Normal file
117
clang-tools-extra/clang-tidy/llvm/NamespaceCommentCheck.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
//===--- NamespaceCommentCheck.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 "NamespaceCommentCheck.h"
|
||||||
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||||
|
#include "clang/Lex/Lexer.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
|
using namespace clang::ast_matchers;
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tidy {
|
||||||
|
|
||||||
|
NamespaceCommentCheck::NamespaceCommentCheck()
|
||||||
|
: NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
|
||||||
|
"namespace( +([a-zA-Z0-9_]+))? *(\\*/)?$",
|
||||||
|
llvm::Regex::IgnoreCase),
|
||||||
|
ShortNamespaceLines(1) {}
|
||||||
|
|
||||||
|
void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) {
|
||||||
|
Finder->addMatcher(namespaceDecl().bind("namespace"), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1,
|
||||||
|
SourceLocation Loc2) {
|
||||||
|
return Loc1.isFileID() && Loc2.isFileID() &&
|
||||||
|
Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getNamespaceComment(const NamespaceDecl *ND, bool InsertLineBreak) {
|
||||||
|
std::string Fix = "// namespace";
|
||||||
|
if (!ND->isAnonymousNamespace())
|
||||||
|
Fix.append(" ").append(ND->getNameAsString());
|
||||||
|
if (InsertLineBreak)
|
||||||
|
Fix.append("\n");
|
||||||
|
return Fix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
|
||||||
|
const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
|
||||||
|
const SourceManager &Sources = *Result.SourceManager;
|
||||||
|
|
||||||
|
if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't require closing comments for namespaces spanning less than certain
|
||||||
|
// number of lines.
|
||||||
|
unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart());
|
||||||
|
unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
|
||||||
|
if (EndLine - StartLine + 1 <= ShortNamespaceLines)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find next token after the namespace closing brace.
|
||||||
|
SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
|
||||||
|
SourceLocation Loc = AfterRBrace;
|
||||||
|
Token Tok;
|
||||||
|
// Skip whitespace until we find the next token.
|
||||||
|
while (Lexer::getRawToken(Loc, Tok, Sources, Result.Context->getLangOpts())) {
|
||||||
|
Loc = Loc.getLocWithOffset(1);
|
||||||
|
}
|
||||||
|
if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
|
||||||
|
// If we insert a line comment before the token in the same line, we need
|
||||||
|
// to insert a line break.
|
||||||
|
bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
|
||||||
|
|
||||||
|
// Try to find existing namespace closing comment on the same line.
|
||||||
|
if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
|
||||||
|
StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
|
||||||
|
SmallVector<StringRef, 6> Groups;
|
||||||
|
if (NamespaceCommentPattern.match(Comment, &Groups)) {
|
||||||
|
StringRef NamespaceNameInComment = Groups.size() >= 6 ? Groups[5] : "";
|
||||||
|
|
||||||
|
// Check if the namespace in the comment is the same.
|
||||||
|
if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) ||
|
||||||
|
ND->getNameAsString() == NamespaceNameInComment) {
|
||||||
|
// FIXME: Maybe we need a strict mode, where we always fix namespace
|
||||||
|
// comments with different format.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we need to fix the comment.
|
||||||
|
NeedLineBreak = Comment.startswith("/*");
|
||||||
|
CharSourceRange OldCommentRange = CharSourceRange::getCharRange(
|
||||||
|
SourceRange(Loc, Loc.getLocWithOffset(Tok.getLength())));
|
||||||
|
diag(Loc, "namespace closing comment refers to a wrong namespace '%0'")
|
||||||
|
<< NamespaceNameInComment
|
||||||
|
<< FixItHint::CreateReplacement(
|
||||||
|
OldCommentRange, getNamespaceComment(ND, NeedLineBreak));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not a recognized form of a namespace closing comment.
|
||||||
|
// Leave line comment on the same line. Move block comment to the next line,
|
||||||
|
// as it can be multi-line or there may be other tokens behind it.
|
||||||
|
if (Comment.startswith("//"))
|
||||||
|
NeedLineBreak = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
diag(ND->getLocation(), "namespace not terminated with a closing comment")
|
||||||
|
<< FixItHint::CreateInsertion(
|
||||||
|
AfterRBrace, " " + getNamespaceComment(ND, NeedLineBreak));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
||||||
36
clang-tools-extra/clang-tidy/llvm/NamespaceCommentCheck.h
Normal file
36
clang-tools-extra/clang-tidy/llvm/NamespaceCommentCheck.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//===--- NamespaceCommentCheck.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_LLVM_NAMESPACE_COMMENT_CHECK_H
|
||||||
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_NAMESPACE_COMMENT_CHECK_H
|
||||||
|
|
||||||
|
#include "../ClangTidy.h"
|
||||||
|
#include "llvm/Support/Regex.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tidy {
|
||||||
|
|
||||||
|
/// \brief Checks that long namespaces have a closing comment.
|
||||||
|
///
|
||||||
|
/// see: http://llvm.org/docs/CodingStandards.html#namespace-indentation
|
||||||
|
class NamespaceCommentCheck : public ClangTidyCheck {
|
||||||
|
public:
|
||||||
|
NamespaceCommentCheck();
|
||||||
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
llvm::Regex NamespaceCommentPattern;
|
||||||
|
const unsigned ShortNamespaceLines;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_NAMESPACE_COMMENT_CHECK_H
|
||||||
@@ -17,6 +17,8 @@ using namespace clang::ast_matchers;
|
|||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
|
TwineLocalCheck::TwineLocalCheck() {}
|
||||||
|
|
||||||
void TwineLocalCheck::registerMatchers(MatchFinder *Finder) {
|
void TwineLocalCheck::registerMatchers(MatchFinder *Finder) {
|
||||||
auto TwineType =
|
auto TwineType =
|
||||||
qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine"))));
|
qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine"))));
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ namespace tidy {
|
|||||||
/// and should be generally avoided.
|
/// and should be generally avoided.
|
||||||
class TwineLocalCheck : public ClangTidyCheck {
|
class TwineLocalCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
TwineLocalCheck(StringRef Name, ClangTidyContext *Context)
|
TwineLocalCheck();
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "ArgumentCommentCheck.h"
|
#include "ArgumentCommentCheck.h"
|
||||||
|
#include "../ClangTidy.h"
|
||||||
|
#include "../ClangTidyModule.h"
|
||||||
|
#include "../ClangTidyModuleRegistry.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||||
#include "clang/Lex/Lexer.h"
|
#include "clang/Lex/Lexer.h"
|
||||||
@@ -18,21 +21,11 @@ using namespace clang::ast_matchers;
|
|||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
|
|
||||||
ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
|
ArgumentCommentCheck::ArgumentCommentCheck()
|
||||||
ClangTidyContext *Context)
|
: IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
|
||||||
: ClangTidyCheck(Name, Context),
|
|
||||||
IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
|
|
||||||
|
|
||||||
void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
|
void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
|
||||||
Finder->addMatcher(
|
Finder->addMatcher(callExpr(unless(operatorCallExpr())).bind("expr"), this);
|
||||||
callExpr(unless(operatorCallExpr()),
|
|
||||||
// NewCallback's arguments relate to the pointed function, don't
|
|
||||||
// check them against NewCallback's parameter names.
|
|
||||||
// FIXME: Make this configurable.
|
|
||||||
unless(hasDeclaration(functionDecl(anyOf(
|
|
||||||
hasName("NewCallback"), hasName("NewPermanentCallback"))))))
|
|
||||||
.bind("expr"),
|
|
||||||
this);
|
|
||||||
Finder->addMatcher(constructExpr().bind("expr"), this);
|
Finder->addMatcher(constructExpr().bind("expr"), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,10 +81,10 @@ ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
|
|||||||
if (ThisED >= UpperBound)
|
if (ThisED >= UpperBound)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (unsigned I = 0, E = Params.size(); I != E; ++I) {
|
for (const auto &Param : Params) {
|
||||||
if (I == ArgIndex)
|
if (&Param - Params.begin() == ArgIndex)
|
||||||
continue;
|
continue;
|
||||||
IdentifierInfo *II = Params[I]->getIdentifier();
|
IdentifierInfo *II = Param->getIdentifier();
|
||||||
if (!II)
|
if (!II)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -121,15 +114,6 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
|
|||||||
IdentifierInfo *II = PVD->getIdentifier();
|
IdentifierInfo *II = PVD->getIdentifier();
|
||||||
if (!II)
|
if (!II)
|
||||||
continue;
|
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.
|
|
||||||
if (Template->getNumParams() <= i ||
|
|
||||||
Template->getParamDecl(i)->isParameterPack()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation BeginSLoc, EndSLoc = Args[i]->getLocStart();
|
SourceLocation BeginSLoc, EndSLoc = Args[i]->getLocStart();
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace tidy {
|
|||||||
/// \brief Checks that argument comments match parameter names.
|
/// \brief Checks that argument comments match parameter names.
|
||||||
class ArgumentCommentCheck : public ClangTidyCheck {
|
class ArgumentCommentCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
ArgumentCommentCheck(StringRef Name, ClangTidyContext *Context);
|
ArgumentCommentCheck();
|
||||||
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ void BoolPointerImplicitConversion::registerMatchers(MatchFinder *Finder) {
|
|||||||
hasSourceExpression(expr(
|
hasSourceExpression(expr(
|
||||||
hasType(pointerType(pointee(isBoolean()))),
|
hasType(pointerType(pointee(isBoolean()))),
|
||||||
ignoringParenImpCasts(declRefExpr().bind("expr")))),
|
ignoringParenImpCasts(declRefExpr().bind("expr")))),
|
||||||
isPointerToBoolean())))),
|
isPointerToBoolean()))))).bind("if"),
|
||||||
unless(isInTemplateInstantiation())).bind("if"),
|
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +41,6 @@ BoolPointerImplicitConversion::check(const MatchFinder::MatchResult &Result) {
|
|||||||
auto *If = Result.Nodes.getStmtAs<IfStmt>("if");
|
auto *If = Result.Nodes.getStmtAs<IfStmt>("if");
|
||||||
auto *Var = Result.Nodes.getStmtAs<DeclRefExpr>("expr");
|
auto *Var = Result.Nodes.getStmtAs<DeclRefExpr>("expr");
|
||||||
|
|
||||||
// Ignore macros.
|
|
||||||
if (Var->getLocStart().isMacroID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Only allow variable accesses for now, no function calls or member exprs.
|
// Only allow variable accesses for now, no function calls or member exprs.
|
||||||
// Check that we don't dereference the variable anywhere within the if. This
|
// Check that we don't dereference the variable anywhere within the if. This
|
||||||
// avoids false positives for checks of the pointer for nullptr before it is
|
// avoids false positives for checks of the pointer for nullptr before it is
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ namespace tidy {
|
|||||||
/// }
|
/// }
|
||||||
class BoolPointerImplicitConversion : public ClangTidyCheck {
|
class BoolPointerImplicitConversion : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
BoolPointerImplicitConversion(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ add_clang_library(clangTidyMiscModule
|
|||||||
ArgumentCommentCheck.cpp
|
ArgumentCommentCheck.cpp
|
||||||
BoolPointerImplicitConversion.cpp
|
BoolPointerImplicitConversion.cpp
|
||||||
MiscTidyModule.cpp
|
MiscTidyModule.cpp
|
||||||
|
RedundantSmartptrGet.cpp
|
||||||
SwappedArgumentsCheck.cpp
|
SwappedArgumentsCheck.cpp
|
||||||
UndelegatedConstructor.cpp
|
|
||||||
UnusedRAII.cpp
|
|
||||||
UniqueptrResetRelease.cpp
|
|
||||||
UseOverride.cpp
|
UseOverride.cpp
|
||||||
|
|
||||||
LINK_LIBS
|
LINK_LIBS
|
||||||
|
|||||||
@@ -12,10 +12,8 @@
|
|||||||
#include "../ClangTidyModuleRegistry.h"
|
#include "../ClangTidyModuleRegistry.h"
|
||||||
#include "ArgumentCommentCheck.h"
|
#include "ArgumentCommentCheck.h"
|
||||||
#include "BoolPointerImplicitConversion.h"
|
#include "BoolPointerImplicitConversion.h"
|
||||||
|
#include "RedundantSmartptrGet.h"
|
||||||
#include "SwappedArgumentsCheck.h"
|
#include "SwappedArgumentsCheck.h"
|
||||||
#include "UndelegatedConstructor.h"
|
|
||||||
#include "UnusedRAII.h"
|
|
||||||
#include "UniqueptrResetRelease.h"
|
|
||||||
#include "UseOverride.h"
|
#include "UseOverride.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
@@ -24,17 +22,21 @@ namespace tidy {
|
|||||||
class MiscModule : public ClangTidyModule {
|
class MiscModule : public ClangTidyModule {
|
||||||
public:
|
public:
|
||||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||||
CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<BoolPointerImplicitConversion>(
|
"misc-argument-comment",
|
||||||
"misc-bool-pointer-implicit-conversion");
|
new ClangTidyCheckFactory<ArgumentCommentCheck>());
|
||||||
CheckFactories.registerCheck<SwappedArgumentsCheck>(
|
CheckFactories.addCheckFactory(
|
||||||
"misc-swapped-arguments");
|
"misc-bool-pointer-implicit-conversion",
|
||||||
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
|
new ClangTidyCheckFactory<BoolPointerImplicitConversion>());
|
||||||
"misc-undelegated-constructor");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<UniqueptrResetRelease>(
|
"misc-redundant-smartptr-get",
|
||||||
"misc-uniqueptr-reset-release");
|
new ClangTidyCheckFactory<RedundantSmartptrGet>());
|
||||||
CheckFactories.registerCheck<UnusedRAIICheck>("misc-unused-raii");
|
CheckFactories.addCheckFactory(
|
||||||
CheckFactories.registerCheck<UseOverride>("misc-use-override");
|
"misc-swapped-arguments",
|
||||||
|
new ClangTidyCheckFactory<SwappedArgumentsCheck>());
|
||||||
|
CheckFactories.addCheckFactory(
|
||||||
|
"misc-use-override",
|
||||||
|
new ClangTidyCheckFactory<UseOverride>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ using namespace clang::ast_matchers;
|
|||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tidy {
|
namespace tidy {
|
||||||
namespace readability {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
internal::Matcher<Expr> callToGet(internal::Matcher<Decl> OnClass) {
|
internal::Matcher<Expr> callToGet(internal::Matcher<Decl> OnClass) {
|
||||||
@@ -118,6 +117,5 @@ void RedundantSmartptrGet::check(const MatchFinder::MatchResult &Result) {
|
|||||||
<< FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
|
<< FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace readability
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
34
clang-tools-extra/clang-tidy/misc/RedundantSmartptrGet.h
Normal file
34
clang-tools-extra/clang-tidy/misc/RedundantSmartptrGet.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//===--- RedundantSmartptrGet.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_REDUNDANT_SMARTPTR_GET_H
|
||||||
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_SMARTPTR_GET_H
|
||||||
|
|
||||||
|
#include "../ClangTidy.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tidy {
|
||||||
|
|
||||||
|
/// \brief Find and remove redundant calls to smart pointer's .get() method.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// ptr.get()->Foo() ==> ptr->Foo()
|
||||||
|
/// *ptr.get() ==> *ptr
|
||||||
|
/// *ptr->get() ==> **ptr
|
||||||
|
class RedundantSmartptrGet : public ClangTidyCheck {
|
||||||
|
public:
|
||||||
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_SMARTPTR_GET_H
|
||||||
|
|
||||||
@@ -19,8 +19,6 @@ namespace tidy {
|
|||||||
/// conversions.
|
/// conversions.
|
||||||
class SwappedArgumentsCheck : public ClangTidyCheck {
|
class SwappedArgumentsCheck : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
SwappedArgumentsCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
//===--- UndelegatedConstructor.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 "UndelegatedConstructor.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
|
|
||||||
namespace ast_matchers {
|
|
||||||
AST_MATCHER_P(Stmt, ignoringTemporaryExpr, internal::Matcher<Stmt>,
|
|
||||||
InnerMatcher) {
|
|
||||||
const Stmt *E = &Node;
|
|
||||||
for (;;) {
|
|
||||||
// Temporaries with non-trivial dtors.
|
|
||||||
if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
|
|
||||||
E = EWC->getSubExpr();
|
|
||||||
// Temporaries with zero or more than two ctor arguments.
|
|
||||||
else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
|
|
||||||
E = BTE->getSubExpr();
|
|
||||||
// Temporaries with exactly one ctor argument.
|
|
||||||
else if (const auto *FCE = dyn_cast<CXXFunctionalCastExpr>(E))
|
|
||||||
E = FCE->getSubExpr();
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InnerMatcher.matches(*E, Finder, Builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds a node if it's a base of an already bound node.
|
|
||||||
AST_MATCHER_P(CXXRecordDecl, baseOfBoundNode, std::string, ID) {
|
|
||||||
return Builder->removeBindings([&](const internal::BoundNodesMap &Nodes) {
|
|
||||||
const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(ID);
|
|
||||||
return Derived != &Node && !Derived->isDerivedFrom(&Node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} // namespace ast_matchers
|
|
||||||
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) {
|
|
||||||
// We look for calls to constructors of the same type in constructors. To do
|
|
||||||
// this we have to look through a variety of nodes that occur in the path,
|
|
||||||
// depending on the type's destructor and the number of arguments on the
|
|
||||||
// constructor call, this is handled by ignoringTemporaryExpr. Ignore template
|
|
||||||
// instantiations to reduce the number of duplicated warnings.
|
|
||||||
Finder->addMatcher(
|
|
||||||
compoundStmt(
|
|
||||||
hasParent(constructorDecl(ofClass(recordDecl().bind("parent")))),
|
|
||||||
forEach(ignoringTemporaryExpr(
|
|
||||||
constructExpr(hasDeclaration(constructorDecl(ofClass(
|
|
||||||
recordDecl(baseOfBoundNode("parent"))))))
|
|
||||||
.bind("construct"))),
|
|
||||||
unless(isInTemplateInstantiation())),
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UndelegatedConstructorCheck::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
const auto *E = Result.Nodes.getStmtAs<CXXConstructExpr>("construct");
|
|
||||||
diag(E->getLocStart(), "did you intend to call a delegated constructor? "
|
|
||||||
"A temporary object is created here instead");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//===--- UndelegatedConstructor.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_UNDELEGATED_CONSTRUCTOR_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATED_CONSTRUCTOR_H
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
/// \brief Finds creation of temporary objects in constructors that look like a
|
|
||||||
/// function call to another constructor of the same class. The user most likely
|
|
||||||
/// meant to use a delegating constructor or base class initializer.
|
|
||||||
class UndelegatedConstructorCheck : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
UndelegatedConstructorCheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATED_CONSTRUCTOR_H
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
//===--- UniqueptrResetRelease.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 "UniqueptrResetRelease.h"
|
|
||||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
void UniqueptrResetRelease::registerMatchers(MatchFinder *Finder) {
|
|
||||||
Finder->addMatcher(
|
|
||||||
memberCallExpr(
|
|
||||||
on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
|
|
||||||
callee(methodDecl(hasName("reset"),
|
|
||||||
ofClass(hasName("::std::unique_ptr")))),
|
|
||||||
has(memberCallExpr(
|
|
||||||
on(expr().bind("right")),
|
|
||||||
callee(memberExpr().bind("release_member")),
|
|
||||||
callee(methodDecl(hasName("release"),
|
|
||||||
ofClass(hasName("::std::unique_ptr")))))))
|
|
||||||
.bind("reset_call"),
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UniqueptrResetRelease::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
|
|
||||||
const auto *ReleaseMember =
|
|
||||||
Result.Nodes.getNodeAs<MemberExpr>("release_member");
|
|
||||||
const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
|
|
||||||
const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
|
|
||||||
const auto *ResetCall =
|
|
||||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
|
|
||||||
|
|
||||||
std::string LeftText = clang::Lexer::getSourceText(
|
|
||||||
CharSourceRange::getTokenRange(Left->getSourceRange()),
|
|
||||||
*Result.SourceManager, Result.Context->getLangOpts());
|
|
||||||
std::string RightText = clang::Lexer::getSourceText(
|
|
||||||
CharSourceRange::getTokenRange(Right->getSourceRange()),
|
|
||||||
*Result.SourceManager, Result.Context->getLangOpts());
|
|
||||||
|
|
||||||
if (ResetMember->isArrow())
|
|
||||||
LeftText = "*" + LeftText;
|
|
||||||
if (ReleaseMember->isArrow())
|
|
||||||
RightText = "*" + RightText;
|
|
||||||
// Even if x was rvalue, *x is not rvalue anymore.
|
|
||||||
if (!Right->isRValue() || ReleaseMember->isArrow())
|
|
||||||
RightText = "std::move(" + RightText + ")";
|
|
||||||
std::string NewText = LeftText + " = " + RightText;
|
|
||||||
|
|
||||||
diag(ResetMember->getExprLoc(),
|
|
||||||
"prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())")
|
|
||||||
<< FixItHint::CreateReplacement(
|
|
||||||
CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
//===--- UniqueptrResetRelease.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_UNIQUEPTR_RESET_RELEASE_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTR_RESET_RELEASE_H
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
/// \brief Find and replace unique_ptr::reset(release()) with std::move
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// std::unique_ptr<Foo> x, y;
|
|
||||||
/// x.reset(y.release()); -> x = std::move(y);
|
|
||||||
///
|
|
||||||
/// If "y" is already rvalue, std::move is not added.
|
|
||||||
/// "x" and "y" can also be std::unique_ptr<Foo>*.
|
|
||||||
class UniqueptrResetRelease : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
UniqueptrResetRelease(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTR_RESET_RELEASE_H
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
//===--- UnusedRAII.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 "UnusedRAII.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace ast_matchers {
|
|
||||||
AST_MATCHER(CXXRecordDecl, hasUserDeclaredDestructor) {
|
|
||||||
// TODO: If the dtor is there but empty we don't want to warn either.
|
|
||||||
return Node.hasDefinition() && Node.hasUserDeclaredDestructor();
|
|
||||||
}
|
|
||||||
} // namespace ast_matchers
|
|
||||||
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
void UnusedRAIICheck::registerMatchers(MatchFinder *Finder) {
|
|
||||||
// Look for temporaries that are constructed in-place and immediately
|
|
||||||
// destroyed. Look for temporaries created by a functional cast but not for
|
|
||||||
// those returned from a call.
|
|
||||||
auto BindTemp = bindTemporaryExpr(unless(has(callExpr()))).bind("temp");
|
|
||||||
Finder->addMatcher(
|
|
||||||
exprWithCleanups(unless(isInTemplateInstantiation()),
|
|
||||||
hasParent(compoundStmt().bind("compound")),
|
|
||||||
hasType(recordDecl(hasUserDeclaredDestructor())),
|
|
||||||
anyOf(has(BindTemp), has(functionalCastExpr(
|
|
||||||
has(BindTemp))))).bind("expr"),
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnusedRAIICheck::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
const auto *E = Result.Nodes.getStmtAs<Expr>("expr");
|
|
||||||
|
|
||||||
// We ignore code expanded from macros to reduce the number of false
|
|
||||||
// positives.
|
|
||||||
if (E->getLocStart().isMacroID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't emit a warning for the last statement in the surrounding compund
|
|
||||||
// statement.
|
|
||||||
const auto *CS = Result.Nodes.getStmtAs<CompoundStmt>("compound");
|
|
||||||
if (E == CS->body_back())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Emit a warning.
|
|
||||||
auto D = diag(E->getLocStart(), "object destroyed immediately after "
|
|
||||||
"creation; did you mean to name the object?");
|
|
||||||
const char *Replacement = " give_me_a_name";
|
|
||||||
|
|
||||||
// If this is a default ctor we have to remove the parens or we'll introduce a
|
|
||||||
// most vexing parse.
|
|
||||||
const auto *BTE = Result.Nodes.getStmtAs<CXXBindTemporaryExpr>("temp");
|
|
||||||
if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr()))
|
|
||||||
if (TOE->getNumArgs() == 0) {
|
|
||||||
D << FixItHint::CreateReplacement(
|
|
||||||
CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()),
|
|
||||||
Replacement);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise just suggest adding a name. To find the place to insert the name
|
|
||||||
// find the first TypeLoc in the children of E, which always points to the
|
|
||||||
// written type.
|
|
||||||
auto Matches =
|
|
||||||
match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context);
|
|
||||||
const auto *TL = selectFirst<TypeLoc>("t", Matches);
|
|
||||||
D << FixItHint::CreateInsertion(
|
|
||||||
Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager,
|
|
||||||
Result.Context->getLangOpts()),
|
|
||||||
Replacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
//===--- UnusedRAII.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_UNUSED_RAII_H
|
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_RAII_H
|
|
||||||
|
|
||||||
#include "../ClangTidy.h"
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
|
|
||||||
/// \brief Finds temporaries that look like RAII objects.
|
|
||||||
///
|
|
||||||
/// The canonical example for this is a scoped lock.
|
|
||||||
/// \code
|
|
||||||
/// {
|
|
||||||
/// scoped_lock(&global_mutex);
|
|
||||||
/// critical_section();
|
|
||||||
/// }
|
|
||||||
/// \endcode
|
|
||||||
/// The destructor of the scoped_lock is called before the critical_section is
|
|
||||||
/// entered, leaving it unprotected.
|
|
||||||
///
|
|
||||||
/// We apply a number of heuristics to reduce the false positive count of this
|
|
||||||
/// check:
|
|
||||||
/// - Ignore code expanded from macros. Testing frameworks make heavy use of
|
|
||||||
/// this.
|
|
||||||
/// - Ignore types with no user-declared constructor. Those are very unlikely
|
|
||||||
/// to be RAII objects.
|
|
||||||
/// - Ignore objects at the end of a compound statement (doesn't change behavior).
|
|
||||||
/// - Ignore objects returned from a call.
|
|
||||||
class UnusedRAIICheck : public ClangTidyCheck {
|
|
||||||
public:
|
|
||||||
UnusedRAIICheck(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
|
|
||||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_RAII_H
|
|
||||||
@@ -74,8 +74,8 @@ void UseOverride::check(const MatchFinder::MatchResult &Result) {
|
|||||||
DiagnosticBuilder Diag = diag(
|
DiagnosticBuilder Diag = diag(
|
||||||
Method->getLocation(),
|
Method->getLocation(),
|
||||||
OnlyVirtualSpecified
|
OnlyVirtualSpecified
|
||||||
? "Prefer using 'override' or (rarely) 'final' instead of 'virtual'"
|
? "Prefer using 'override' or 'final' instead of 'virtual'"
|
||||||
: "Annotate this function with 'override' or (rarely) 'final'");
|
: "Use exactly one of 'virtual', 'override' or (rarely) 'final'");
|
||||||
|
|
||||||
CharSourceRange FileRange = Lexer::makeFileCharRange(
|
CharSourceRange FileRange = Lexer::makeFileCharRange(
|
||||||
CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
|
CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
|
||||||
@@ -98,10 +98,8 @@ void UseOverride::check(const MatchFinder::MatchResult &Result) {
|
|||||||
if (Method->hasAttrs()) {
|
if (Method->hasAttrs()) {
|
||||||
for (const clang::Attr *A : Method->getAttrs()) {
|
for (const clang::Attr *A : Method->getAttrs()) {
|
||||||
if (!A->isImplicit()) {
|
if (!A->isImplicit()) {
|
||||||
SourceLocation Loc = Sources.getExpansionLoc(A->getLocation());
|
InsertLoc = Sources.getExpansionLoc(A->getLocation());
|
||||||
if (!InsertLoc.isValid() ||
|
break;
|
||||||
Sources.isBeforeInTranslationUnit(Loc, InsertLoc))
|
|
||||||
InsertLoc = Loc;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ namespace tidy {
|
|||||||
/// \brief Use C++11's 'override' and remove 'virtual' where applicable.
|
/// \brief Use C++11's 'override' and remove 'virtual' where applicable.
|
||||||
class UseOverride : public ClangTidyCheck {
|
class UseOverride : public ClangTidyCheck {
|
||||||
public:
|
public:
|
||||||
UseOverride(StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context) {}
|
|
||||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,257 +0,0 @@
|
|||||||
//===--- BracesAroundStatementsCheck.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 "BracesAroundStatementsCheck.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
|
||||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
||||||
#include "clang/Lex/Lexer.h"
|
|
||||||
|
|
||||||
using namespace clang::ast_matchers;
|
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace tidy {
|
|
||||||
namespace readability {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
|
|
||||||
const ASTContext *Context) {
|
|
||||||
Token Tok;
|
|
||||||
SourceLocation Beginning =
|
|
||||||
Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
|
|
||||||
const bool Invalid =
|
|
||||||
Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
|
|
||||||
assert(!Invalid && "Expected a valid token.");
|
|
||||||
|
|
||||||
if (Invalid)
|
|
||||||
return tok::NUM_TOKENS;
|
|
||||||
|
|
||||||
return Tok.getKind();
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
|
|
||||||
const SourceManager &SM,
|
|
||||||
const ASTContext *Context) {
|
|
||||||
assert(Loc.isValid());
|
|
||||||
for (;;) {
|
|
||||||
while (isWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()))
|
|
||||||
Loc = Loc.getLocWithOffset(1);
|
|
||||||
|
|
||||||
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
|
|
||||||
if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
|
|
||||||
return Loc;
|
|
||||||
|
|
||||||
// Fast-forward current token.
|
|
||||||
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation findEndLocation(SourceLocation LastTokenLoc,
|
|
||||||
const SourceManager &SM,
|
|
||||||
const ASTContext *Context) {
|
|
||||||
SourceLocation Loc = LastTokenLoc;
|
|
||||||
// Loc points to the beginning of the last (non-comment non-ws) token
|
|
||||||
// before end or ';'.
|
|
||||||
assert(Loc.isValid());
|
|
||||||
bool SkipEndWhitespaceAndComments = true;
|
|
||||||
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
|
|
||||||
if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
|
|
||||||
TokKind == tok::r_brace) {
|
|
||||||
// If we are at ";" or "}", we found the last token. We could use as well
|
|
||||||
// `if (isa<NullStmt>(S))`, but it wouldn't work for nested statements.
|
|
||||||
SkipEndWhitespaceAndComments = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
|
|
||||||
// Loc points past the last token before end or after ';'.
|
|
||||||
|
|
||||||
if (SkipEndWhitespaceAndComments) {
|
|
||||||
Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
|
|
||||||
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
|
|
||||||
if (TokKind == tok::semi)
|
|
||||||
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
assert(Loc.isValid());
|
|
||||||
while (isHorizontalWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()))
|
|
||||||
Loc = Loc.getLocWithOffset(1);
|
|
||||||
|
|
||||||
if (isVerticalWhitespace(*FullSourceLoc(Loc, SM).getCharacterData())) {
|
|
||||||
// EOL, insert brace before.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
|
|
||||||
if (TokKind != tok::comment) {
|
|
||||||
// Non-comment token, insert brace before.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation TokEndLoc =
|
|
||||||
Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
|
|
||||||
SourceRange TokRange(Loc, TokEndLoc);
|
|
||||||
StringRef Comment = Lexer::getSourceText(
|
|
||||||
CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
|
|
||||||
if (Comment.startswith("/*") && Comment.find('\n') != StringRef::npos) {
|
|
||||||
// Multi-line block comment, insert brace before.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else: Trailing comment, insert brace after the newline.
|
|
||||||
|
|
||||||
// Fast-forward current token.
|
|
||||||
Loc = TokEndLoc;
|
|
||||||
}
|
|
||||||
return Loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
BracesAroundStatementsCheck::BracesAroundStatementsCheck(
|
|
||||||
StringRef Name, ClangTidyContext *Context)
|
|
||||||
: ClangTidyCheck(Name, Context),
|
|
||||||
// Always add braces by default.
|
|
||||||
ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}
|
|
||||||
|
|
||||||
void
|
|
||||||
BracesAroundStatementsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|
||||||
Options.store(Opts, "ShortStatementLines", ShortStatementLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
|
|
||||||
Finder->addMatcher(ifStmt().bind("if"), this);
|
|
||||||
Finder->addMatcher(whileStmt().bind("while"), this);
|
|
||||||
Finder->addMatcher(doStmt().bind("do"), this);
|
|
||||||
Finder->addMatcher(forStmt().bind("for"), this);
|
|
||||||
Finder->addMatcher(forRangeStmt().bind("for-range"), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
BracesAroundStatementsCheck::check(const MatchFinder::MatchResult &Result) {
|
|
||||||
const SourceManager &SM = *Result.SourceManager;
|
|
||||||
const ASTContext *Context = Result.Context;
|
|
||||||
|
|
||||||
// Get location of closing parenthesis or 'do' to insert opening brace.
|
|
||||||
if (auto S = Result.Nodes.getNodeAs<ForStmt>("for")) {
|
|
||||||
checkStmt(Result, S->getBody(), S->getRParenLoc());
|
|
||||||
} else if (auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {
|
|
||||||
checkStmt(Result, S->getBody(), S->getRParenLoc());
|
|
||||||
} else if (auto S = Result.Nodes.getNodeAs<DoStmt>("do")) {
|
|
||||||
checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
|
|
||||||
} else if (auto S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
|
|
||||||
SourceLocation StartLoc = findRParenLoc(S, SM, Context);
|
|
||||||
if (StartLoc.isInvalid())
|
|
||||||
return;
|
|
||||||
checkStmt(Result, S->getBody(), StartLoc);
|
|
||||||
} else if (auto S = Result.Nodes.getNodeAs<IfStmt>("if")) {
|
|
||||||
SourceLocation StartLoc = findRParenLoc(S, SM, Context);
|
|
||||||
if (StartLoc.isInvalid())
|
|
||||||
return;
|
|
||||||
checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
|
|
||||||
const Stmt *Else = S->getElse();
|
|
||||||
if (Else && !isa<IfStmt>(Else)) {
|
|
||||||
// Omit 'else if' statements here, they will be handled directly.
|
|
||||||
checkStmt(Result, Else, S->getElseLoc());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
llvm_unreachable("Invalid match");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find location of right parenthesis closing condition
|
|
||||||
template <typename IfOrWhileStmt>
|
|
||||||
SourceLocation
|
|
||||||
BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
|
|
||||||
const SourceManager &SM,
|
|
||||||
const ASTContext *Context) {
|
|
||||||
// Skip macros
|
|
||||||
if (S->getLocStart().isMacroID())
|
|
||||||
return SourceLocation();
|
|
||||||
|
|
||||||
static const char *const ErrorMessage =
|
|
||||||
"cannot find location of closing parenthesis ')'";
|
|
||||||
SourceLocation CondEndLoc = S->getCond()->getLocEnd();
|
|
||||||
if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
|
|
||||||
CondEndLoc = CondVar->getLocEnd();
|
|
||||||
|
|
||||||
assert(CondEndLoc.isValid());
|
|
||||||
SourceLocation PastCondEndLoc =
|
|
||||||
Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
|
|
||||||
if (PastCondEndLoc.isInvalid()) {
|
|
||||||
diag(CondEndLoc, ErrorMessage);
|
|
||||||
return SourceLocation();
|
|
||||||
}
|
|
||||||
SourceLocation RParenLoc =
|
|
||||||
forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
|
|
||||||
if (RParenLoc.isInvalid()) {
|
|
||||||
diag(PastCondEndLoc, ErrorMessage);
|
|
||||||
return SourceLocation();
|
|
||||||
}
|
|
||||||
tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
|
|
||||||
if (TokKind != tok::r_paren) {
|
|
||||||
diag(RParenLoc, ErrorMessage);
|
|
||||||
return SourceLocation();
|
|
||||||
}
|
|
||||||
return RParenLoc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
BracesAroundStatementsCheck::checkStmt(const MatchFinder::MatchResult &Result,
|
|
||||||
const Stmt *S, SourceLocation InitialLoc,
|
|
||||||
SourceLocation EndLocHint) {
|
|
||||||
// 1) If there's a corresponding "else" or "while", the check inserts "} "
|
|
||||||
// right before that token.
|
|
||||||
// 2) If there's a multi-line block comment starting on the same line after
|
|
||||||
// the location we're inserting the closing brace at, or there's a non-comment
|
|
||||||
// token, the check inserts "\n}" right before that token.
|
|
||||||
// 3) Otherwise the check finds the end of line (possibly after some block or
|
|
||||||
// line comments) and inserts "\n}" right before that EOL.
|
|
||||||
if (!S || isa<CompoundStmt>(S)) {
|
|
||||||
// Already inside braces.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Skip macros.
|
|
||||||
if (S->getLocStart().isMacroID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const SourceManager &SM = *Result.SourceManager;
|
|
||||||
const ASTContext *Context = Result.Context;
|
|
||||||
|
|
||||||
// InitialLoc points at the last token before opening brace to be inserted.
|
|
||||||
assert(InitialLoc.isValid());
|
|
||||||
SourceLocation StartLoc =
|
|
||||||
Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
|
|
||||||
// StartLoc points at the location of the opening brace to be inserted.
|
|
||||||
SourceLocation EndLoc;
|
|
||||||
std::string ClosingInsertion;
|
|
||||||
if (EndLocHint.isValid()) {
|
|
||||||
EndLoc = EndLocHint;
|
|
||||||
ClosingInsertion = "} ";
|
|
||||||
} else {
|
|
||||||
EndLoc = findEndLocation(S->getLocEnd(), SM, Context);
|
|
||||||
ClosingInsertion = "\n}";
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(StartLoc.isValid());
|
|
||||||
assert(EndLoc.isValid());
|
|
||||||
// Don't require braces for statements spanning less than certain number of
|
|
||||||
// lines.
|
|
||||||
if (ShortStatementLines) {
|
|
||||||
unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
|
|
||||||
unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
|
|
||||||
if (EndLine - StartLine < ShortStatementLines)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Diag = diag(StartLoc, "statement should be inside braces");
|
|
||||||
Diag << FixItHint::CreateInsertion(StartLoc, " {")
|
|
||||||
<< FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace readability
|
|
||||||
} // namespace tidy
|
|
||||||
} // namespace clang
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user