Compare commits
169 Commits
llvmorg-4.
...
llvmorg-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce25e1a77e | ||
|
|
08064c20db | ||
|
|
f7ff8644fb | ||
|
|
0f664e3843 | ||
|
|
e7d842f670 | ||
|
|
afe209420a | ||
|
|
945a843fca | ||
|
|
6c02be1058 | ||
|
|
cf3f88d23a | ||
|
|
c867c52e48 | ||
|
|
c0de4d5dfe | ||
|
|
8d1eea8bab | ||
|
|
95487b9324 | ||
|
|
1917148ae1 | ||
|
|
b97bb7bd7e | ||
|
|
ff55bde145 | ||
|
|
67b2d3e548 | ||
|
|
09a8bb52ec | ||
|
|
febb13003a | ||
|
|
a87302cea2 | ||
|
|
39970299fd | ||
|
|
cce20faf0e | ||
|
|
893ec2fa37 | ||
|
|
e4cfe21a40 | ||
|
|
52078d5b4c | ||
|
|
5050823a40 | ||
|
|
96ebd6939d | ||
|
|
4d137ce709 | ||
|
|
0462821695 | ||
|
|
648212d30e | ||
|
|
1256b9e01d | ||
|
|
bd5a61f962 | ||
|
|
c400b144b3 | ||
|
|
a1f6fd32a3 | ||
|
|
4c27d26902 | ||
|
|
178a30f3d0 | ||
|
|
d8f682e8b8 | ||
|
|
6993bdaeac | ||
|
|
743618894f | ||
|
|
de5499dc23 | ||
|
|
fcfc07ab52 | ||
|
|
ef5d170f9a | ||
|
|
6bfcc10dca | ||
|
|
723dfb5dc9 | ||
|
|
cb0913a242 | ||
|
|
c0c7d6c26d | ||
|
|
d8147e7bde | ||
|
|
2e8edcaa80 | ||
|
|
0777ee9f6f | ||
|
|
e7fedbcdbb | ||
|
|
fd4ed8eaab | ||
|
|
d26597b492 | ||
|
|
b6c05cc725 | ||
|
|
a972a1c52e | ||
|
|
b5d131ee9f | ||
|
|
d5f90e1a8f | ||
|
|
6d88d11e5c | ||
|
|
43d7c78a86 | ||
|
|
eb6343e251 | ||
|
|
6ea7621321 | ||
|
|
3d88a187c6 | ||
|
|
2a531d02a4 | ||
|
|
3e892bf1f5 | ||
|
|
6cee7f203c | ||
|
|
3d9fd91926 | ||
|
|
52360ab08e | ||
|
|
3e34550899 | ||
|
|
1969971eef | ||
|
|
8e1170431d | ||
|
|
d981f97664 | ||
|
|
eef377133d | ||
|
|
11b7a9b72f | ||
|
|
fd00608136 | ||
|
|
4ef81a7fdb | ||
|
|
4e15deb8e0 | ||
|
|
7ae7c29bfb | ||
|
|
0316f30e18 | ||
|
|
ae8bfb5779 | ||
|
|
dd08defbe8 | ||
|
|
f37ca4f446 | ||
|
|
404f2eb91d | ||
|
|
4192c1db5d | ||
|
|
cb9c50e25c | ||
|
|
59f49e1610 | ||
|
|
a2296f018d | ||
|
|
48466efc6f | ||
|
|
8a5dbfe87e | ||
|
|
ff00bfa19b | ||
|
|
94f84eb3c0 | ||
|
|
cf7397c3e9 | ||
|
|
07101cec4f | ||
|
|
2546545e27 | ||
|
|
2f58a7236e | ||
|
|
3b3179a953 | ||
|
|
abc0f33858 | ||
|
|
382e38731b | ||
|
|
eba143a88d | ||
|
|
299cf98d0d | ||
|
|
db31d91f01 | ||
|
|
7d2d8faedf | ||
|
|
55dcf07620 | ||
|
|
3f72426b35 | ||
|
|
abaeaf8db4 | ||
|
|
446b0b091d | ||
|
|
7db37f42d9 | ||
|
|
ba9e903704 | ||
|
|
d10126296d | ||
|
|
b53a8594cb | ||
|
|
458d2d9d1b | ||
|
|
ec84a0a7d0 | ||
|
|
3a27de34df | ||
|
|
5ca33d661a | ||
|
|
ad3f102610 | ||
|
|
4d46ceafb1 | ||
|
|
739e31c3c0 | ||
|
|
b6794b6182 | ||
|
|
2ec706fe1f | ||
|
|
b5792d90e2 | ||
|
|
454777f777 | ||
|
|
6da6f0883c | ||
|
|
09851a032b | ||
|
|
8a688571fa | ||
|
|
a2cc88dc6b | ||
|
|
60aa90df97 | ||
|
|
19e55fceac | ||
|
|
59c12c5799 | ||
|
|
30082a166c | ||
|
|
b3c29bd13b | ||
|
|
c6d28602de | ||
|
|
00378ef53e | ||
|
|
b699c21d07 | ||
|
|
2245ca9d1b | ||
|
|
c27cf5f781 | ||
|
|
5eaa7156fe | ||
|
|
87ac29751a | ||
|
|
7bf9e46fbe | ||
|
|
79d0e34a0a | ||
|
|
d82de6f06e | ||
|
|
d6dff71d7d | ||
|
|
5244f2f904 | ||
|
|
f26092687c | ||
|
|
a487e1a649 | ||
|
|
aaa653d4d4 | ||
|
|
8e554281db | ||
|
|
76fe58b155 | ||
|
|
3ce6eededa | ||
|
|
5bdc21f29c | ||
|
|
120ca53c91 | ||
|
|
53da0ecd27 | ||
|
|
f5408dc358 | ||
|
|
765e9c1d2a | ||
|
|
e24755b5fa | ||
|
|
9c50fd5f17 | ||
|
|
0a3b90dada | ||
|
|
6c35e88a87 | ||
|
|
e30f938b24 | ||
|
|
669800fd7c | ||
|
|
64903ae434 | ||
|
|
ebc53c200f | ||
|
|
f1c81f72da | ||
|
|
891d9aafe9 | ||
|
|
3078a53316 | ||
|
|
e7249290c2 | ||
|
|
6718a4577b | ||
|
|
b862a9d664 | ||
|
|
d60f41c121 | ||
|
|
768082d9f1 | ||
|
|
f31b1e4799 | ||
|
|
aa71f5361b |
@@ -1,15 +1,11 @@
|
||||
add_subdirectory(clang-apply-replacements)
|
||||
add_subdirectory(clang-rename)
|
||||
add_subdirectory(clang-reorder-fields)
|
||||
add_subdirectory(modularize)
|
||||
if(CLANG_ENABLE_STATIC_ANALYZER)
|
||||
add_subdirectory(clang-tidy)
|
||||
add_subdirectory(clang-tidy-vs)
|
||||
endif()
|
||||
|
||||
add_subdirectory(change-namespace)
|
||||
add_subdirectory(clang-query)
|
||||
add_subdirectory(clang-move)
|
||||
add_subdirectory(include-fixer)
|
||||
add_subdirectory(pp-trace)
|
||||
add_subdirectory(tool-template)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
support
|
||||
)
|
||||
|
||||
add_clang_library(clangChangeNamespace
|
||||
ChangeNamespace.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
@@ -1,906 +0,0 @@
|
||||
//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "ChangeNamespace.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace change_namespace {
|
||||
|
||||
namespace {
|
||||
|
||||
inline std::string
|
||||
joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
|
||||
if (Namespaces.empty())
|
||||
return "";
|
||||
std::string Result = Namespaces.front();
|
||||
for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
|
||||
Result += ("::" + *I).str();
|
||||
return Result;
|
||||
}
|
||||
|
||||
SourceLocation startLocationForType(TypeLoc TLoc) {
|
||||
// For elaborated types (e.g. `struct a::A`) we want the portion after the
|
||||
// `struct` but including the namespace qualifier, `a::`.
|
||||
if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
|
||||
NestedNameSpecifierLoc NestedNameSpecifier =
|
||||
TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
|
||||
if (NestedNameSpecifier.getNestedNameSpecifier())
|
||||
return NestedNameSpecifier.getBeginLoc();
|
||||
TLoc = TLoc.getNextTypeLoc();
|
||||
}
|
||||
return TLoc.getLocStart();
|
||||
}
|
||||
|
||||
SourceLocation endLocationForType(TypeLoc TLoc) {
|
||||
// Dig past any namespace or keyword qualifications.
|
||||
while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
|
||||
TLoc.getTypeLocClass() == TypeLoc::Qualified)
|
||||
TLoc = TLoc.getNextTypeLoc();
|
||||
|
||||
// The location for template specializations (e.g. Foo<int>) includes the
|
||||
// templated types in its location range. We want to restrict this to just
|
||||
// before the `<` character.
|
||||
if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
|
||||
return TLoc.castAs<TemplateSpecializationTypeLoc>()
|
||||
.getLAngleLoc()
|
||||
.getLocWithOffset(-1);
|
||||
return TLoc.getEndLoc();
|
||||
}
|
||||
|
||||
// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
|
||||
// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
|
||||
// is empty, nullptr is returned.
|
||||
// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
|
||||
// the NamespaceDecl of namespace "a" will be returned.
|
||||
const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
|
||||
llvm::StringRef PartialNsName) {
|
||||
if (!InnerNs || PartialNsName.empty())
|
||||
return nullptr;
|
||||
const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
|
||||
const auto *CurrentNs = InnerNs;
|
||||
llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
|
||||
PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
while (!PartialNsNameSplitted.empty()) {
|
||||
// Get the inner-most namespace in CurrentContext.
|
||||
while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
|
||||
CurrentContext = CurrentContext->getParent();
|
||||
if (!CurrentContext)
|
||||
return nullptr;
|
||||
CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
|
||||
if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
|
||||
return nullptr;
|
||||
PartialNsNameSplitted.pop_back();
|
||||
CurrentContext = CurrentContext->getParent();
|
||||
}
|
||||
return CurrentNs;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Lexer>
|
||||
getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
|
||||
const LangOptions &LangOpts) {
|
||||
if (Loc.isMacroID() &&
|
||||
!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
|
||||
return nullptr;
|
||||
// Break down the source location.
|
||||
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
|
||||
// Try to load the file buffer.
|
||||
bool InvalidTemp = false;
|
||||
llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
|
||||
if (InvalidTemp)
|
||||
return nullptr;
|
||||
|
||||
const char *TokBegin = File.data() + LocInfo.second;
|
||||
// Lex from the start of the given location.
|
||||
return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
|
||||
LangOpts, File.begin(), TokBegin, File.end());
|
||||
}
|
||||
|
||||
// FIXME: get rid of this helper function if this is supported in clang-refactor
|
||||
// library.
|
||||
static SourceLocation getStartOfNextLine(SourceLocation Loc,
|
||||
const SourceManager &SM,
|
||||
const LangOptions &LangOpts) {
|
||||
std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
|
||||
if (!Lex.get())
|
||||
return SourceLocation();
|
||||
llvm::SmallVector<char, 16> Line;
|
||||
// FIXME: this is a bit hacky to get ReadToEndOfLine work.
|
||||
Lex->setParsingPreprocessorDirective(true);
|
||||
Lex->ReadToEndOfLine(&Line);
|
||||
auto End = Loc.getLocWithOffset(Line.size());
|
||||
return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
|
||||
? End
|
||||
: End.getLocWithOffset(1);
|
||||
}
|
||||
|
||||
// Returns `R` with new range that refers to code after `Replaces` being
|
||||
// applied.
|
||||
tooling::Replacement
|
||||
getReplacementInChangedCode(const tooling::Replacements &Replaces,
|
||||
const tooling::Replacement &R) {
|
||||
unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
|
||||
unsigned NewEnd =
|
||||
Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
|
||||
return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
|
||||
R.getReplacementText());
|
||||
}
|
||||
|
||||
// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
|
||||
// applying all existing Replaces first if there is conflict.
|
||||
void addOrMergeReplacement(const tooling::Replacement &R,
|
||||
tooling::Replacements *Replaces) {
|
||||
auto Err = Replaces->add(R);
|
||||
if (Err) {
|
||||
llvm::consumeError(std::move(Err));
|
||||
auto Replace = getReplacementInChangedCode(*Replaces, R);
|
||||
*Replaces = Replaces->merge(tooling::Replacements(Replace));
|
||||
}
|
||||
}
|
||||
|
||||
tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
|
||||
llvm::StringRef ReplacementText,
|
||||
const SourceManager &SM) {
|
||||
if (!Start.isValid() || !End.isValid()) {
|
||||
llvm::errs() << "start or end location were invalid\n";
|
||||
return tooling::Replacement();
|
||||
}
|
||||
if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
|
||||
llvm::errs()
|
||||
<< "start or end location were in different macro expansions\n";
|
||||
return tooling::Replacement();
|
||||
}
|
||||
Start = SM.getSpellingLoc(Start);
|
||||
End = SM.getSpellingLoc(End);
|
||||
if (SM.getFileID(Start) != SM.getFileID(End)) {
|
||||
llvm::errs() << "start or end location were in different files\n";
|
||||
return tooling::Replacement();
|
||||
}
|
||||
return tooling::Replacement(
|
||||
SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
|
||||
SM.getSpellingLoc(End)),
|
||||
ReplacementText);
|
||||
}
|
||||
|
||||
void addReplacementOrDie(
|
||||
SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,
|
||||
const SourceManager &SM,
|
||||
std::map<std::string, tooling::Replacements> *FileToReplacements) {
|
||||
const auto R = createReplacement(Start, End, ReplacementText, SM);
|
||||
auto Err = (*FileToReplacements)[R.getFilePath()].add(R);
|
||||
if (Err)
|
||||
llvm_unreachable(llvm::toString(std::move(Err)).c_str());
|
||||
}
|
||||
|
||||
tooling::Replacement createInsertion(SourceLocation Loc,
|
||||
llvm::StringRef InsertText,
|
||||
const SourceManager &SM) {
|
||||
if (Loc.isInvalid()) {
|
||||
llvm::errs() << "insert Location is invalid.\n";
|
||||
return tooling::Replacement();
|
||||
}
|
||||
Loc = SM.getSpellingLoc(Loc);
|
||||
return tooling::Replacement(SM, Loc, 0, InsertText);
|
||||
}
|
||||
|
||||
// Returns the shortest qualified name for declaration `DeclName` in the
|
||||
// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
|
||||
// is "a::c::d", then "b::X" will be returned.
|
||||
// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
|
||||
// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
|
||||
// will have empty name.
|
||||
std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
|
||||
llvm::StringRef NsName) {
|
||||
DeclName = DeclName.ltrim(':');
|
||||
NsName = NsName.ltrim(':');
|
||||
if (DeclName.find(':') == llvm::StringRef::npos)
|
||||
return DeclName;
|
||||
|
||||
while (!DeclName.consume_front((NsName + "::").str())) {
|
||||
const auto Pos = NsName.find_last_of(':');
|
||||
if (Pos == llvm::StringRef::npos)
|
||||
return DeclName;
|
||||
assert(Pos > 0);
|
||||
NsName = NsName.substr(0, Pos - 1);
|
||||
}
|
||||
return DeclName;
|
||||
}
|
||||
|
||||
std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
|
||||
if (Code.back() != '\n')
|
||||
Code += "\n";
|
||||
llvm::SmallVector<StringRef, 4> NsSplitted;
|
||||
NestedNs.split(NsSplitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
while (!NsSplitted.empty()) {
|
||||
// FIXME: consider code style for comments.
|
||||
Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
|
||||
"} // namespace " + NsSplitted.back() + "\n")
|
||||
.str();
|
||||
NsSplitted.pop_back();
|
||||
}
|
||||
return Code;
|
||||
}
|
||||
|
||||
// Returns true if \p D is a nested DeclContext in \p Context
|
||||
bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
|
||||
while (D) {
|
||||
if (D == Context)
|
||||
return true;
|
||||
D = D->getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
|
||||
bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
|
||||
const DeclContext *DeclCtx, SourceLocation Loc) {
|
||||
SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
|
||||
Loc = SM.getSpellingLoc(Loc);
|
||||
return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
|
||||
(SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
|
||||
isNestedDeclContext(DeclCtx, D->getDeclContext()));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ChangeNamespaceTool::ChangeNamespaceTool(
|
||||
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
|
||||
std::map<std::string, tooling::Replacements> *FileToReplacements,
|
||||
llvm::StringRef FallbackStyle)
|
||||
: FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
|
||||
OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
|
||||
FilePattern(FilePattern), FilePatternRE(FilePattern) {
|
||||
FileToReplacements->clear();
|
||||
llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
|
||||
llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
|
||||
llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
|
||||
llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
|
||||
// Calculates `DiffOldNamespace` and `DiffNewNamespace`.
|
||||
while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
|
||||
OldNsSplitted.front() == NewNsSplitted.front()) {
|
||||
OldNsSplitted.erase(OldNsSplitted.begin());
|
||||
NewNsSplitted.erase(NewNsSplitted.begin());
|
||||
}
|
||||
DiffOldNamespace = joinNamespaces(OldNsSplitted);
|
||||
DiffNewNamespace = joinNamespaces(NewNsSplitted);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
std::string FullOldNs = "::" + OldNamespace;
|
||||
// Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
|
||||
// OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
|
||||
// be "a::b". Declarations in this namespace will not be visible in the new
|
||||
// namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
|
||||
llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
|
||||
llvm::StringRef(DiffOldNamespace)
|
||||
.split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
std::string Prefix = "-";
|
||||
if (!DiffOldNsSplitted.empty())
|
||||
Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
|
||||
DiffOldNsSplitted.front())
|
||||
.str();
|
||||
auto IsInMovedNs =
|
||||
allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
|
||||
isExpansionInFileMatching(FilePattern));
|
||||
auto IsVisibleInNewNs = anyOf(
|
||||
IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
|
||||
// Match using declarations.
|
||||
Finder->addMatcher(
|
||||
usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
|
||||
.bind("using"),
|
||||
this);
|
||||
// Match using namespace declarations.
|
||||
Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
|
||||
IsVisibleInNewNs)
|
||||
.bind("using_namespace"),
|
||||
this);
|
||||
// Match namespace alias declarations.
|
||||
Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
|
||||
IsVisibleInNewNs)
|
||||
.bind("namespace_alias"),
|
||||
this);
|
||||
|
||||
// Match old namespace blocks.
|
||||
Finder->addMatcher(
|
||||
namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
|
||||
.bind("old_ns"),
|
||||
this);
|
||||
|
||||
// Match class forward-declarations in the old namespace.
|
||||
// Note that forward-declarations in classes are not matched.
|
||||
Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),
|
||||
IsInMovedNs, hasParent(namespaceDecl()))
|
||||
.bind("class_fwd_decl"),
|
||||
this);
|
||||
|
||||
// Match template class forward-declarations in the old namespace.
|
||||
Finder->addMatcher(
|
||||
classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),
|
||||
IsInMovedNs, hasParent(namespaceDecl()))
|
||||
.bind("template_class_fwd_decl"),
|
||||
this);
|
||||
|
||||
// Match references to types that are not defined in the old namespace.
|
||||
// Forward-declarations in the old namespace are also matched since they will
|
||||
// be moved back to the old namespace.
|
||||
auto DeclMatcher = namedDecl(
|
||||
hasAncestor(namespaceDecl()),
|
||||
unless(anyOf(
|
||||
isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
|
||||
hasAncestor(cxxRecordDecl()),
|
||||
allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
|
||||
|
||||
// Using shadow declarations in classes always refers to base class, which
|
||||
// does not need to be qualified since it can be inferred from inheritance.
|
||||
// Note that this does not match using alias declarations.
|
||||
auto UsingShadowDeclInClass =
|
||||
usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));
|
||||
|
||||
// Match TypeLocs on the declaration. Carefully match only the outermost
|
||||
// TypeLoc and template specialization arguments (which are not outermost)
|
||||
// that are directly linked to types matching `DeclMatcher`. Nested name
|
||||
// specifier locs are handled separately below.
|
||||
Finder->addMatcher(
|
||||
typeLoc(IsInMovedNs,
|
||||
loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
|
||||
unless(anyOf(hasParent(typeLoc(loc(qualType(
|
||||
allOf(hasDeclaration(DeclMatcher),
|
||||
unless(templateSpecializationType())))))),
|
||||
hasParent(nestedNameSpecifierLoc()),
|
||||
hasAncestor(isImplicit()),
|
||||
hasAncestor(UsingShadowDeclInClass))),
|
||||
hasAncestor(decl().bind("dc")))
|
||||
.bind("type"),
|
||||
this);
|
||||
|
||||
// Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
|
||||
// special case it.
|
||||
// Since using declarations inside classes must have the base class in the
|
||||
// nested name specifier, we leave it to the nested name specifier matcher.
|
||||
Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),
|
||||
unless(UsingShadowDeclInClass))
|
||||
.bind("using_with_shadow"),
|
||||
this);
|
||||
|
||||
// Handle types in nested name specifier. Specifiers that are in a TypeLoc
|
||||
// matched above are not matched, e.g. "A::" in "A::A" is not matched since
|
||||
// "A::A" would have already been fixed.
|
||||
Finder->addMatcher(
|
||||
nestedNameSpecifierLoc(
|
||||
hasAncestor(decl(IsInMovedNs).bind("dc")),
|
||||
loc(nestedNameSpecifier(
|
||||
specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
|
||||
unless(anyOf(hasAncestor(isImplicit()),
|
||||
hasAncestor(UsingShadowDeclInClass),
|
||||
hasAncestor(typeLoc(loc(qualType(hasDeclaration(
|
||||
decl(equalsBoundNode("from_decl"))))))))))
|
||||
.bind("nested_specifier_loc"),
|
||||
this);
|
||||
|
||||
// Matches base class initializers in constructors. TypeLocs of base class
|
||||
// initializers do not need to be fixed. For example,
|
||||
// class X : public a::b::Y {
|
||||
// public:
|
||||
// X() : Y::Y() {} // Y::Y do not need namespace specifier.
|
||||
// };
|
||||
Finder->addMatcher(
|
||||
cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);
|
||||
|
||||
// Handle function.
|
||||
// Only handle functions that are defined in a namespace excluding member
|
||||
// function, static methods (qualified by nested specifier), and functions
|
||||
// defined in the global namespace.
|
||||
// Note that the matcher does not exclude calls to out-of-line static method
|
||||
// definitions, so we need to exclude them in the callback handler.
|
||||
auto FuncMatcher =
|
||||
functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
|
||||
hasAncestor(namespaceDecl(isAnonymous())),
|
||||
hasAncestor(cxxRecordDecl()))),
|
||||
hasParent(namespaceDecl()));
|
||||
Finder->addMatcher(decl(forEachDescendant(expr(anyOf(
|
||||
callExpr(callee(FuncMatcher)).bind("call"),
|
||||
declRefExpr(to(FuncMatcher.bind("func_decl")))
|
||||
.bind("func_ref")))),
|
||||
IsInMovedNs, unless(isImplicit()))
|
||||
.bind("dc"),
|
||||
this);
|
||||
|
||||
auto GlobalVarMatcher = varDecl(
|
||||
hasGlobalStorage(), hasParent(namespaceDecl()),
|
||||
unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
|
||||
Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
|
||||
to(GlobalVarMatcher.bind("var_decl")))
|
||||
.bind("var_ref"),
|
||||
this);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::run(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
|
||||
UsingDecls.insert(Using);
|
||||
} else if (const auto *UsingNamespace =
|
||||
Result.Nodes.getNodeAs<UsingDirectiveDecl>(
|
||||
"using_namespace")) {
|
||||
UsingNamespaceDecls.insert(UsingNamespace);
|
||||
} else if (const auto *NamespaceAlias =
|
||||
Result.Nodes.getNodeAs<NamespaceAliasDecl>(
|
||||
"namespace_alias")) {
|
||||
NamespaceAliasDecls.insert(NamespaceAlias);
|
||||
} else if (const auto *NsDecl =
|
||||
Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
|
||||
moveOldNamespace(Result, NsDecl);
|
||||
} else if (const auto *FwdDecl =
|
||||
Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {
|
||||
moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));
|
||||
} else if (const auto *TemplateFwdDecl =
|
||||
Result.Nodes.getNodeAs<ClassTemplateDecl>(
|
||||
"template_class_fwd_decl")) {
|
||||
moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));
|
||||
} else if (const auto *UsingWithShadow =
|
||||
Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
|
||||
fixUsingShadowDecl(Result, UsingWithShadow);
|
||||
} else if (const auto *Specifier =
|
||||
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
|
||||
"nested_specifier_loc")) {
|
||||
SourceLocation Start = Specifier->getBeginLoc();
|
||||
SourceLocation End = endLocationForType(Specifier->getTypeLoc());
|
||||
fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
|
||||
} else if (const auto *BaseInitializer =
|
||||
Result.Nodes.getNodeAs<CXXCtorInitializer>(
|
||||
"base_initializer")) {
|
||||
BaseCtorInitializerTypeLocs.push_back(
|
||||
BaseInitializer->getTypeSourceInfo()->getTypeLoc());
|
||||
} else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
|
||||
// This avoids fixing types with record types as qualifier, which is not
|
||||
// filtered by matchers in some cases, e.g. the type is templated. We should
|
||||
// handle the record type qualifier instead.
|
||||
TypeLoc Loc = *TLoc;
|
||||
while (Loc.getTypeLocClass() == TypeLoc::Qualified)
|
||||
Loc = Loc.getNextTypeLoc();
|
||||
if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
|
||||
NestedNameSpecifierLoc NestedNameSpecifier =
|
||||
Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
|
||||
const Type *SpecifierType =
|
||||
NestedNameSpecifier.getNestedNameSpecifier()->getAsType();
|
||||
if (SpecifierType && SpecifierType->isRecordType())
|
||||
return;
|
||||
}
|
||||
fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
|
||||
} else if (const auto *VarRef =
|
||||
Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
|
||||
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
|
||||
assert(Var);
|
||||
if (Var->getCanonicalDecl()->isStaticDataMember())
|
||||
return;
|
||||
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(Context && "Empty decl context.");
|
||||
fixDeclRefExpr(Result, Context->getDeclContext(),
|
||||
llvm::cast<NamedDecl>(Var), VarRef);
|
||||
} else if (const auto *FuncRef =
|
||||
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
|
||||
// If this reference has been processed as a function call, we do not
|
||||
// process it again.
|
||||
if (ProcessedFuncRefs.count(FuncRef))
|
||||
return;
|
||||
ProcessedFuncRefs.insert(FuncRef);
|
||||
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
|
||||
assert(Func);
|
||||
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(Context && "Empty decl context.");
|
||||
fixDeclRefExpr(Result, Context->getDeclContext(),
|
||||
llvm::cast<NamedDecl>(Func), FuncRef);
|
||||
} else {
|
||||
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
|
||||
assert(Call != nullptr && "Expecting callback for CallExpr.");
|
||||
const auto *CalleeFuncRef =
|
||||
llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
|
||||
ProcessedFuncRefs.insert(CalleeFuncRef);
|
||||
const FunctionDecl *Func = Call->getDirectCallee();
|
||||
assert(Func != nullptr);
|
||||
// FIXME: ignore overloaded operators. This would miss cases where operators
|
||||
// are called by qualified names (i.e. "ns::operator <"). Ignore such
|
||||
// cases for now.
|
||||
if (Func->isOverloadedOperator())
|
||||
return;
|
||||
// Ignore out-of-line static methods since they will be handled by nested
|
||||
// name specifiers.
|
||||
if (Func->getCanonicalDecl()->getStorageClass() ==
|
||||
StorageClass::SC_Static &&
|
||||
Func->isOutOfLine())
|
||||
return;
|
||||
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(Context && "Empty decl context.");
|
||||
SourceRange CalleeRange = Call->getCallee()->getSourceRange();
|
||||
replaceQualifiedSymbolInDeclContext(
|
||||
Result, Context->getDeclContext(), CalleeRange.getBegin(),
|
||||
CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
|
||||
}
|
||||
}
|
||||
|
||||
static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
|
||||
const SourceManager &SM,
|
||||
const LangOptions &LangOpts) {
|
||||
std::unique_ptr<Lexer> Lex =
|
||||
getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
|
||||
assert(Lex.get() &&
|
||||
"Failed to create lexer from the beginning of namespace.");
|
||||
if (!Lex.get())
|
||||
return SourceLocation();
|
||||
Token Tok;
|
||||
while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
|
||||
}
|
||||
return Tok.isNot(tok::TokenKind::l_brace)
|
||||
? SourceLocation()
|
||||
: Tok.getEndLoc().getLocWithOffset(1);
|
||||
}
|
||||
|
||||
// Stores information about a moved namespace in `MoveNamespaces` and leaves
|
||||
// the actual movement to `onEndOfTranslationUnit()`.
|
||||
void ChangeNamespaceTool::moveOldNamespace(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const NamespaceDecl *NsDecl) {
|
||||
// If the namespace is empty, do nothing.
|
||||
if (Decl::castToDeclContext(NsDecl)->decls_empty())
|
||||
return;
|
||||
|
||||
const SourceManager &SM = *Result.SourceManager;
|
||||
// Get the range of the code in the old namespace.
|
||||
SourceLocation Start =
|
||||
getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());
|
||||
assert(Start.isValid() && "Can't find l_brace for namespace.");
|
||||
MoveNamespace MoveNs;
|
||||
MoveNs.Offset = SM.getFileOffset(Start);
|
||||
// The range of the moved namespace is from the location just past the left
|
||||
// brace to the location right before the right brace.
|
||||
MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;
|
||||
|
||||
// Insert the new namespace after `DiffOldNamespace`. For example, if
|
||||
// `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
|
||||
// "x::y" will be inserted inside the existing namespace "a" and after "a::b".
|
||||
// `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
|
||||
// in the above example.
|
||||
// If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
|
||||
// namespace will be a nested namespace in the old namespace.
|
||||
const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
|
||||
SourceLocation InsertionLoc = Start;
|
||||
if (OuterNs) {
|
||||
SourceLocation LocAfterNs = getStartOfNextLine(
|
||||
OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());
|
||||
assert(LocAfterNs.isValid() &&
|
||||
"Failed to get location after DiffOldNamespace");
|
||||
InsertionLoc = LocAfterNs;
|
||||
}
|
||||
MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));
|
||||
MoveNs.FID = SM.getFileID(Start);
|
||||
MoveNs.SourceMgr = Result.SourceManager;
|
||||
MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs);
|
||||
}
|
||||
|
||||
// Removes a class forward declaration from the code in the moved namespace and
|
||||
// creates an `InsertForwardDeclaration` to insert the forward declaration back
|
||||
// into the old namespace after moving code from the old namespace to the new
|
||||
// namespace.
|
||||
// For example, changing "a" to "x":
|
||||
// Old code:
|
||||
// namespace a {
|
||||
// class FWD;
|
||||
// class A { FWD *fwd; }
|
||||
// } // a
|
||||
// New code:
|
||||
// namespace a {
|
||||
// class FWD;
|
||||
// } // a
|
||||
// namespace x {
|
||||
// class A { a::FWD *fwd; }
|
||||
// } // x
|
||||
void ChangeNamespaceTool::moveClassForwardDeclaration(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const NamedDecl *FwdDecl) {
|
||||
SourceLocation Start = FwdDecl->getLocStart();
|
||||
SourceLocation End = FwdDecl->getLocEnd();
|
||||
SourceLocation AfterSemi = Lexer::findLocationAfterToken(
|
||||
End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
|
||||
/*SkipTrailingWhitespaceAndNewLine=*/true);
|
||||
if (AfterSemi.isValid())
|
||||
End = AfterSemi.getLocWithOffset(-1);
|
||||
// Delete the forward declaration from the code to be moved.
|
||||
addReplacementOrDie(Start, End, "", *Result.SourceManager,
|
||||
&FileToReplacements);
|
||||
llvm::StringRef Code = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(
|
||||
Result.SourceManager->getSpellingLoc(Start),
|
||||
Result.SourceManager->getSpellingLoc(End)),
|
||||
*Result.SourceManager, Result.Context->getLangOpts());
|
||||
// Insert the forward declaration back into the old namespace after moving the
|
||||
// code from old namespace to new namespace.
|
||||
// Insertion information is stored in `InsertFwdDecls` and actual
|
||||
// insertion will be performed in `onEndOfTranslationUnit`.
|
||||
// Get the (old) namespace that contains the forward declaration.
|
||||
const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
|
||||
// The namespace contains the forward declaration, so it must not be empty.
|
||||
assert(!NsDecl->decls_empty());
|
||||
const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
|
||||
Code, *Result.SourceManager);
|
||||
InsertForwardDeclaration InsertFwd;
|
||||
InsertFwd.InsertionOffset = Insertion.getOffset();
|
||||
InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
|
||||
InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
|
||||
}
|
||||
|
||||
// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
|
||||
// FromDecl with the shortest qualified name possible when the reference is in
|
||||
// `NewNamespace`.
|
||||
void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
|
||||
const NamedDecl *FromDecl) {
|
||||
const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
|
||||
if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
|
||||
// This should not happen in usual unless the TypeLoc is in function type
|
||||
// parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
|
||||
// `T` will be the translation unit. We simply use fully-qualified name
|
||||
// here.
|
||||
// Note that `FromDecl` must not be defined in the old namespace (according
|
||||
// to `DeclMatcher`), so its fully-qualified name will not change after
|
||||
// changing the namespace.
|
||||
addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
|
||||
*Result.SourceManager, &FileToReplacements);
|
||||
return;
|
||||
}
|
||||
const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
|
||||
// Calculate the name of the `NsDecl` after it is moved to new namespace.
|
||||
std::string OldNs = NsDecl->getQualifiedNameAsString();
|
||||
llvm::StringRef Postfix = OldNs;
|
||||
bool Consumed = Postfix.consume_front(OldNamespace);
|
||||
assert(Consumed && "Expect OldNS to start with OldNamespace.");
|
||||
(void)Consumed;
|
||||
const std::string NewNs = (NewNamespace + Postfix).str();
|
||||
|
||||
llvm::StringRef NestedName = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(
|
||||
Result.SourceManager->getSpellingLoc(Start),
|
||||
Result.SourceManager->getSpellingLoc(End)),
|
||||
*Result.SourceManager, Result.Context->getLangOpts());
|
||||
// If the symbol is already fully qualified, no change needs to be make.
|
||||
if (NestedName.startswith("::"))
|
||||
return;
|
||||
std::string FromDeclName = FromDecl->getQualifiedNameAsString();
|
||||
std::string ReplaceName =
|
||||
getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
|
||||
// Checks if there is any using namespace declarations that can shorten the
|
||||
// qualified name.
|
||||
for (const auto *UsingNamespace : UsingNamespaceDecls) {
|
||||
if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
|
||||
Start))
|
||||
continue;
|
||||
StringRef FromDeclNameRef = FromDeclName;
|
||||
if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
|
||||
->getQualifiedNameAsString())) {
|
||||
FromDeclNameRef = FromDeclNameRef.drop_front(2);
|
||||
if (FromDeclNameRef.size() < ReplaceName.size())
|
||||
ReplaceName = FromDeclNameRef;
|
||||
}
|
||||
}
|
||||
// Checks if there is any namespace alias declarations that can shorten the
|
||||
// qualified name.
|
||||
for (const auto *NamespaceAlias : NamespaceAliasDecls) {
|
||||
if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,
|
||||
Start))
|
||||
continue;
|
||||
StringRef FromDeclNameRef = FromDeclName;
|
||||
if (FromDeclNameRef.consume_front(
|
||||
NamespaceAlias->getNamespace()->getQualifiedNameAsString() +
|
||||
"::")) {
|
||||
std::string AliasName = NamespaceAlias->getNameAsString();
|
||||
std::string AliasQualifiedName =
|
||||
NamespaceAlias->getQualifiedNameAsString();
|
||||
// We only consider namespace aliases define in the global namepspace or
|
||||
// in namespaces that are directly visible from the reference, i.e.
|
||||
// ancestor of the `OldNs`. Note that declarations in ancestor namespaces
|
||||
// but not visible in the new namespace is filtered out by
|
||||
// "IsVisibleInNewNs" matcher.
|
||||
if (AliasQualifiedName != AliasName) {
|
||||
// The alias is defined in some namespace.
|
||||
assert(StringRef(AliasQualifiedName).endswith("::" + AliasName));
|
||||
llvm::StringRef AliasNs =
|
||||
StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);
|
||||
if (!llvm::StringRef(OldNs).startswith(AliasNs))
|
||||
continue;
|
||||
}
|
||||
std::string NameWithAliasNamespace =
|
||||
(AliasName + "::" + FromDeclNameRef).str();
|
||||
if (NameWithAliasNamespace.size() < ReplaceName.size())
|
||||
ReplaceName = NameWithAliasNamespace;
|
||||
}
|
||||
}
|
||||
// Checks if there is any using shadow declarations that can shorten the
|
||||
// qualified name.
|
||||
bool Matched = false;
|
||||
for (const UsingDecl *Using : UsingDecls) {
|
||||
if (Matched)
|
||||
break;
|
||||
if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
|
||||
for (const auto *UsingShadow : Using->shadows()) {
|
||||
const auto *TargetDecl = UsingShadow->getTargetDecl();
|
||||
if (TargetDecl == FromDecl) {
|
||||
ReplaceName = FromDecl->getNameAsString();
|
||||
Matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the new nested name in the new namespace is the same as it was in the
|
||||
// old namespace, we don't create replacement.
|
||||
if (NestedName == ReplaceName)
|
||||
return;
|
||||
// If the reference need to be fully-qualified, add a leading "::" unless
|
||||
// NewNamespace is the global namespace.
|
||||
if (ReplaceName == FromDeclName && !NewNamespace.empty())
|
||||
ReplaceName = "::" + ReplaceName;
|
||||
addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
|
||||
&FileToReplacements);
|
||||
}
|
||||
|
||||
// Replace the [Start, End] of `Type` with the shortest qualified name when the
|
||||
// `Type` is in `NewNamespace`.
|
||||
void ChangeNamespaceTool::fixTypeLoc(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
|
||||
SourceLocation End, TypeLoc Type) {
|
||||
// FIXME: do not rename template parameter.
|
||||
if (Start.isInvalid() || End.isInvalid())
|
||||
return;
|
||||
// Types of CXXCtorInitializers do not need to be fixed.
|
||||
if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
|
||||
return;
|
||||
// The declaration which this TypeLoc refers to.
|
||||
const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
|
||||
// `hasDeclaration` gives underlying declaration, but if the type is
|
||||
// a typedef type, we need to use the typedef type instead.
|
||||
auto IsInMovedNs = [&](const NamedDecl *D) {
|
||||
if (!llvm::StringRef(D->getQualifiedNameAsString())
|
||||
.startswith(OldNamespace + "::"))
|
||||
return false;
|
||||
auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart());
|
||||
if (ExpansionLoc.isInvalid())
|
||||
return false;
|
||||
llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
|
||||
return FilePatternRE.match(Filename);
|
||||
};
|
||||
// Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
|
||||
// `Type` is an alias type, we make `FromDecl` the type alias declaration.
|
||||
// Also, don't fix the \p Type if it refers to a type alias decl in the moved
|
||||
// namespace since the alias decl will be moved along with the type reference.
|
||||
if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
|
||||
FromDecl = Typedef->getDecl();
|
||||
if (IsInMovedNs(FromDecl))
|
||||
return;
|
||||
} else if (auto *TemplateType =
|
||||
Type.getType()->getAs<TemplateSpecializationType>()) {
|
||||
if (TemplateType->isTypeAlias()) {
|
||||
FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
|
||||
if (IsInMovedNs(FromDecl))
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(DeclCtx && "Empty decl context.");
|
||||
replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
|
||||
End, FromDecl);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::fixUsingShadowDecl(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const UsingDecl *UsingDeclaration) {
|
||||
SourceLocation Start = UsingDeclaration->getLocStart();
|
||||
SourceLocation End = UsingDeclaration->getLocEnd();
|
||||
if (Start.isInvalid() || End.isInvalid())
|
||||
return;
|
||||
|
||||
assert(UsingDeclaration->shadow_size() > 0);
|
||||
// FIXME: it might not be always accurate to use the first using-decl.
|
||||
const NamedDecl *TargetDecl =
|
||||
UsingDeclaration->shadow_begin()->getTargetDecl();
|
||||
std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
|
||||
// FIXME: check if target_decl_name is in moved ns, which doesn't make much
|
||||
// sense. If this happens, we need to use name with the new namespace.
|
||||
// Use fully qualified name in UsingDecl for now.
|
||||
addReplacementOrDie(Start, End, "using ::" + TargetDeclName,
|
||||
*Result.SourceManager, &FileToReplacements);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::fixDeclRefExpr(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const DeclContext *UseContext, const NamedDecl *From,
|
||||
const DeclRefExpr *Ref) {
|
||||
SourceRange RefRange = Ref->getSourceRange();
|
||||
replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
|
||||
RefRange.getEnd(), From);
|
||||
}
|
||||
|
||||
void ChangeNamespaceTool::onEndOfTranslationUnit() {
|
||||
// Move namespace blocks and insert forward declaration to old namespace.
|
||||
for (const auto &FileAndNsMoves : MoveNamespaces) {
|
||||
auto &NsMoves = FileAndNsMoves.second;
|
||||
if (NsMoves.empty())
|
||||
continue;
|
||||
const std::string &FilePath = FileAndNsMoves.first;
|
||||
auto &Replaces = FileToReplacements[FilePath];
|
||||
auto &SM = *NsMoves.begin()->SourceMgr;
|
||||
llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
|
||||
auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
|
||||
if (!ChangedCode) {
|
||||
llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
// Replacements on the changed code for moving namespaces and inserting
|
||||
// forward declarations to old namespaces.
|
||||
tooling::Replacements NewReplacements;
|
||||
// Cut the changed code from the old namespace and paste the code in the new
|
||||
// namespace.
|
||||
for (const auto &NsMove : NsMoves) {
|
||||
// Calculate the range of the old namespace block in the changed
|
||||
// code.
|
||||
const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
|
||||
const unsigned NewLength =
|
||||
Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
|
||||
NewOffset;
|
||||
tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
|
||||
std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
|
||||
std::string MovedCodeWrappedInNewNs =
|
||||
wrapCodeInNamespace(DiffNewNamespace, MovedCode);
|
||||
// Calculate the new offset at which the code will be inserted in the
|
||||
// changed code.
|
||||
unsigned NewInsertionOffset =
|
||||
Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
|
||||
tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
|
||||
MovedCodeWrappedInNewNs);
|
||||
addOrMergeReplacement(Deletion, &NewReplacements);
|
||||
addOrMergeReplacement(Insertion, &NewReplacements);
|
||||
}
|
||||
// After moving namespaces, insert forward declarations back to old
|
||||
// namespaces.
|
||||
const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
|
||||
for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
|
||||
unsigned NewInsertionOffset =
|
||||
Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
|
||||
tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
|
||||
FwdDeclInsertion.ForwardDeclText);
|
||||
addOrMergeReplacement(Insertion, &NewReplacements);
|
||||
}
|
||||
// Add replacements referring to the changed code to existing replacements,
|
||||
// which refers to the original code.
|
||||
Replaces = Replaces.merge(NewReplacements);
|
||||
format::FormatStyle Style =
|
||||
format::getStyle("file", FilePath, FallbackStyle);
|
||||
// Clean up old namespaces if there is nothing in it after moving.
|
||||
auto CleanReplacements =
|
||||
format::cleanupAroundReplacements(Code, Replaces, Style);
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
FileToReplacements[FilePath] = *CleanReplacements;
|
||||
}
|
||||
|
||||
// Make sure we don't generate replacements for files that do not match
|
||||
// FilePattern.
|
||||
for (auto &Entry : FileToReplacements)
|
||||
if (!FilePatternRE.match(Entry.first))
|
||||
Entry.second.clear();
|
||||
}
|
||||
|
||||
} // namespace change_namespace
|
||||
} // namespace clang
|
||||
@@ -1,172 +0,0 @@
|
||||
//===-- ChangeNamespace.h -- Change namespace ------------------*- 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_CHANGE_NAMESPACE_CHANGENAMESPACE_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
namespace change_namespace {
|
||||
|
||||
// This tool can be used to change the surrounding namespaces of class/function
|
||||
// definitions. Classes/functions in the moved namespace will have new
|
||||
// namespaces while references to symbols (e.g. types, functions) which are not
|
||||
// defined in the changed namespace will be correctly qualified by prepending
|
||||
// namespace specifiers before them.
|
||||
// This will try to add shortest namespace specifiers possible. When a symbol
|
||||
// reference needs to be fully-qualified, this adds a "::" prefix to the
|
||||
// namespace specifiers unless the new namespace is the global namespace.
|
||||
// For classes, only classes that are declared/defined in the given namespace in
|
||||
// speficifed files will be moved: forward declarations will remain in the old
|
||||
// namespace.
|
||||
// For example, changing "a" to "x":
|
||||
// Old code:
|
||||
// namespace a {
|
||||
// class FWD;
|
||||
// class A { FWD *fwd; }
|
||||
// } // a
|
||||
// New code:
|
||||
// namespace a {
|
||||
// class FWD;
|
||||
// } // a
|
||||
// namespace x {
|
||||
// class A { ::a::FWD *fwd; }
|
||||
// } // x
|
||||
// FIXME: support moving typedef, enums across namespaces.
|
||||
class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
// Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
|
||||
// files matching `FilePattern`.
|
||||
ChangeNamespaceTool(
|
||||
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
|
||||
std::map<std::string, tooling::Replacements> *FileToReplacements,
|
||||
llvm::StringRef FallbackStyle = "LLVM");
|
||||
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder);
|
||||
|
||||
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
// Moves the changed code in old namespaces but leaves class forward
|
||||
// declarations behind.
|
||||
void onEndOfTranslationUnit() override;
|
||||
|
||||
private:
|
||||
void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const NamespaceDecl *NsDecl);
|
||||
|
||||
void moveClassForwardDeclaration(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const NamedDecl *FwdDecl);
|
||||
|
||||
void replaceQualifiedSymbolInDeclContext(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
|
||||
const NamedDecl *FromDecl);
|
||||
|
||||
void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
SourceLocation Start, SourceLocation End, TypeLoc Type);
|
||||
|
||||
void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const UsingDecl *UsingDeclaration);
|
||||
|
||||
void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
|
||||
const DeclContext *UseContext, const NamedDecl *From,
|
||||
const DeclRefExpr *Ref);
|
||||
|
||||
// Information about moving an old namespace.
|
||||
struct MoveNamespace {
|
||||
// The start offset of the namespace block being moved in the original
|
||||
// code.
|
||||
unsigned Offset;
|
||||
// The length of the namespace block in the original code.
|
||||
unsigned Length;
|
||||
// The offset at which the new namespace block will be inserted in the
|
||||
// original code.
|
||||
unsigned InsertionOffset;
|
||||
// The file in which the namespace is declared.
|
||||
FileID FID;
|
||||
SourceManager *SourceMgr;
|
||||
};
|
||||
|
||||
// Information about inserting a class forward declaration.
|
||||
struct InsertForwardDeclaration {
|
||||
// The offset at while the forward declaration will be inserted in the
|
||||
// original code.
|
||||
unsigned InsertionOffset;
|
||||
// The code to be inserted.
|
||||
std::string ForwardDeclText;
|
||||
};
|
||||
|
||||
std::string FallbackStyle;
|
||||
// In match callbacks, this contains replacements for replacing `typeLoc`s in
|
||||
// and deleting forward declarations in the moved namespace blocks.
|
||||
// In `onEndOfTranslationUnit` callback, the previous added replacements are
|
||||
// applied (on the moved namespace blocks), and then changed code in old
|
||||
// namespaces re moved to new namespaces, and previously deleted forward
|
||||
// declarations are inserted back to old namespaces, from which they are
|
||||
// deleted.
|
||||
std::map<std::string, tooling::Replacements> &FileToReplacements;
|
||||
// A fully qualified name of the old namespace without "::" prefix, e.g.
|
||||
// "a::b::c".
|
||||
std::string OldNamespace;
|
||||
// A fully qualified name of the new namespace without "::" prefix, e.g.
|
||||
// "x::y::z".
|
||||
std::string NewNamespace;
|
||||
// The longest suffix in the old namespace that does not overlap the new
|
||||
// namespace.
|
||||
// For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
|
||||
// "a::x::y", then `DiffOldNamespace` will be "b::c".
|
||||
std::string DiffOldNamespace;
|
||||
// The longest suffix in the new namespace that does not overlap the old
|
||||
// namespace.
|
||||
// For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
|
||||
// "a::x::y", then `DiffNewNamespace` will be "x::y".
|
||||
std::string DiffNewNamespace;
|
||||
// A regex pattern that matches files to be processed.
|
||||
std::string FilePattern;
|
||||
llvm::Regex FilePatternRE;
|
||||
// Information about moved namespaces grouped by file.
|
||||
// Since we are modifying code in old namespaces (e.g. add namespace
|
||||
// spedifiers) as well as moving them, we store information about namespaces
|
||||
// to be moved and only move them after all modifications are finished (i.e.
|
||||
// in `onEndOfTranslationUnit`).
|
||||
std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
|
||||
// Information about forward declaration insertions grouped by files.
|
||||
// A class forward declaration is not moved, so it will be deleted from the
|
||||
// moved code block and inserted back into the old namespace. The insertion
|
||||
// will be done after removing the code from the old namespace and before
|
||||
// inserting it to the new namespace.
|
||||
std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
|
||||
// Records all using declarations, which can be used to shorten namespace
|
||||
// specifiers.
|
||||
llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
|
||||
// Records all using namespace declarations, which can be used to shorten
|
||||
// namespace specifiers.
|
||||
llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
|
||||
// Records all namespace alias declarations, which can be used to shorten
|
||||
// namespace specifiers.
|
||||
llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
|
||||
// TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
|
||||
// be fixed.
|
||||
llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
|
||||
// Since a DeclRefExpr for a function call can be matched twice (one as
|
||||
// CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
|
||||
// been processed so that we don't handle them twice.
|
||||
llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
|
||||
};
|
||||
|
||||
} // namespace change_namespace
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
|
||||
@@ -1,23 +0,0 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
Support
|
||||
)
|
||||
|
||||
add_clang_executable(clang-change-namespace
|
||||
ClangChangeNamespace.cpp
|
||||
)
|
||||
target_link_libraries(clang-change-namespace
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangChangeNamespace
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangRewrite
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
install(TARGETS clang-change-namespace
|
||||
RUNTIME DESTINATION bin)
|
||||
@@ -1,114 +0,0 @@
|
||||
//===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This tool can be used to change the surrounding namespaces of class/function
|
||||
// definitions.
|
||||
//
|
||||
// Example: test.cc
|
||||
// namespace na {
|
||||
// class X {};
|
||||
// namespace nb {
|
||||
// class Y { X x; };
|
||||
// } // namespace nb
|
||||
// } // namespace na
|
||||
// To move the definition of class Y from namespace "na::nb" to "x::y", run:
|
||||
// clang-change-namespace --old_namespace "na::nb" \
|
||||
// --new_namespace "x::y" --file_pattern "test.cc" test.cc --
|
||||
// Output:
|
||||
// namespace na {
|
||||
// class X {};
|
||||
// } // namespace na
|
||||
// namespace x {
|
||||
// namespace y {
|
||||
// class Y { na::X x; };
|
||||
// } // namespace y
|
||||
// } // namespace x
|
||||
|
||||
#include "ChangeNamespace.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
|
||||
|
||||
cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
|
||||
cl::desc("Old namespace."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
|
||||
cl::desc("New namespace."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<std::string> FilePattern(
|
||||
"file_pattern", cl::Required,
|
||||
cl::desc("Only rename namespaces in files that match the given pattern."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
|
||||
cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
cl::opt<std::string> Style("style",
|
||||
cl::desc("The style name used for reformatting."),
|
||||
cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
tooling::CommonOptionsParser OptionsParser(argc, argv,
|
||||
ChangeNamespaceCategory);
|
||||
const auto &Files = OptionsParser.getSourcePathList();
|
||||
tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
|
||||
change_namespace::ChangeNamespaceTool NamespaceTool(
|
||||
OldNamespace, NewNamespace, FilePattern, &Tool.getReplacements(), Style);
|
||||
ast_matchers::MatchFinder Finder;
|
||||
NamespaceTool.registerMatchers(&Finder);
|
||||
std::unique_ptr<tooling::FrontendActionFactory> Factory =
|
||||
tooling::newFrontendActionFactory(&Finder);
|
||||
|
||||
if (int Result = Tool.run(Factory.get()))
|
||||
return Result;
|
||||
LangOptions DefaultLangOptions;
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
clang::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);
|
||||
|
||||
if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
|
||||
llvm::errs() << "Failed applying all replacements.\n";
|
||||
return 1;
|
||||
}
|
||||
if (Inplace)
|
||||
return Rewrite.overwriteChangedFiles();
|
||||
|
||||
for (const auto &File : Files) {
|
||||
const auto *Entry = FileMgr.getFile(File);
|
||||
|
||||
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
|
||||
// FIXME: print results in parsable format, e.g. JSON.
|
||||
outs() << "============== " << File << " ==============\n";
|
||||
Rewrite.getEditBuffer(ID).write(llvm::outs());
|
||||
outs() << "\n============================================\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
|
||||
#define LLVM_CLANG_APPLYREPLACEMENTS_H
|
||||
|
||||
#include "clang/Tooling/Core/Diagnostic.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@@ -39,14 +38,12 @@ namespace replace {
|
||||
typedef std::vector<clang::tooling::Range> RangeVector;
|
||||
|
||||
/// \brief Collection of TranslationUnitReplacements.
|
||||
typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
|
||||
typedef std::vector<clang::tooling::TranslationUnitReplacements>
|
||||
TUReplacements;
|
||||
|
||||
/// \brief Collection of TranslationUnitReplacement files.
|
||||
typedef std::vector<std::string> TUReplacementFiles;
|
||||
|
||||
/// \brief Collection of TranslationUniDiagnostics.
|
||||
typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
|
||||
|
||||
/// \brief Map mapping file name to Replacements targeting that file.
|
||||
typedef llvm::DenseMap<const clang::FileEntry *,
|
||||
std::vector<clang::tooling::Replacement>>
|
||||
@@ -62,20 +59,18 @@ typedef llvm::DenseMap<const clang::FileEntry *,
|
||||
/// \param[in] Directory Directory to begin search for serialized
|
||||
/// TranslationUnitReplacements.
|
||||
/// \param[out] TUs Collection of all found and deserialized
|
||||
/// TranslationUnitReplacements or TranslationUnitDiagnostics.
|
||||
/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
|
||||
/// TranslationUnitReplacements.
|
||||
/// \param[out] TURFiles Collection of all TranslationUnitReplacement files
|
||||
/// found in \c Directory.
|
||||
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
|
||||
///
|
||||
/// \returns An error_code indicating success or failure in navigating the
|
||||
/// directory structure.
|
||||
std::error_code collectReplacementsFromDirectory(
|
||||
const llvm::StringRef Directory, TUReplacements &TUs,
|
||||
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
|
||||
|
||||
std::error_code collectReplacementsFromDirectory(
|
||||
const llvm::StringRef Directory, TUDiagnostics &TUs,
|
||||
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
|
||||
std::error_code
|
||||
collectReplacementsFromDirectory(const llvm::StringRef Directory,
|
||||
TUReplacements &TUs,
|
||||
TUReplacementFiles &TURFiles,
|
||||
clang::DiagnosticsEngine &Diagnostics);
|
||||
|
||||
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
|
||||
/// in \c TUs. If conflicts occur, no Replacements are applied.
|
||||
@@ -83,8 +78,7 @@ std::error_code collectReplacementsFromDirectory(
|
||||
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
|
||||
/// value[i+1].getOffset().
|
||||
///
|
||||
/// \param[in] TUs Collection of TranslationUnitReplacements or
|
||||
/// TranslationUnitDiagnostics to merge,
|
||||
/// \param[in] TUs Collection of TranslationUnitReplacements to merge,
|
||||
/// deduplicate, and test for conflicts.
|
||||
/// \param[out] GroupedReplacements Container grouping all Replacements by the
|
||||
/// file they target.
|
||||
@@ -97,15 +91,6 @@ bool mergeAndDeduplicate(const TUReplacements &TUs,
|
||||
FileToReplacementsMap &GroupedReplacements,
|
||||
clang::SourceManager &SM);
|
||||
|
||||
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
|
||||
FileToReplacementsMap &GroupedReplacements,
|
||||
clang::SourceManager &SM);
|
||||
|
||||
// FIXME: Remove this function after changing clang-apply-replacements to use
|
||||
// Replacements class.
|
||||
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
|
||||
Rewriter &Rewrite);
|
||||
|
||||
/// \brief Apply all replacements in \c GroupedReplacements.
|
||||
///
|
||||
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
|
||||
@@ -125,7 +110,7 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
|
||||
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
|
||||
///
|
||||
/// \param[in] Replacements Replacements from a single file.
|
||||
///
|
||||
///
|
||||
/// \returns Collection of source ranges that enclose all given Replacements.
|
||||
/// One range is created for each replacement.
|
||||
RangeVector calculateChangedRanges(
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/DiagnosticsYaml.h"
|
||||
#include "clang/Tooling/ReplacementsYaml.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
@@ -31,58 +30,16 @@
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
|
||||
static void eatDiagnostics(const SMDiagnostic &, void *) {}
|
||||
|
||||
namespace clang {
|
||||
namespace replace {
|
||||
|
||||
std::error_code collectReplacementsFromDirectory(
|
||||
const llvm::StringRef Directory, TUReplacements &TUs,
|
||||
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
|
||||
using namespace llvm::sys::fs;
|
||||
using namespace llvm::sys::path;
|
||||
|
||||
std::error_code ErrorCode;
|
||||
|
||||
for (recursive_directory_iterator I(Directory, ErrorCode), E;
|
||||
I != E && !ErrorCode; I.increment(ErrorCode)) {
|
||||
if (filename(I->path())[0] == '.') {
|
||||
// Indicate not to descend into directories beginning with '.'
|
||||
I.no_push();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extension(I->path()) != ".yaml")
|
||||
continue;
|
||||
|
||||
TUFiles.push_back(I->path());
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
|
||||
MemoryBuffer::getFile(I->path());
|
||||
if (std::error_code BufferError = Out.getError()) {
|
||||
errs() << "Error reading " << I->path() << ": " << BufferError.message()
|
||||
<< "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
|
||||
tooling::TranslationUnitReplacements TU;
|
||||
YIn >> TU;
|
||||
if (YIn.error()) {
|
||||
// File doesn't appear to be a header change description. Ignore it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only keep files that properly parse.
|
||||
TUs.push_back(TU);
|
||||
}
|
||||
|
||||
return ErrorCode;
|
||||
}
|
||||
|
||||
std::error_code
|
||||
collectReplacementsFromDirectory(const llvm::StringRef Directory,
|
||||
TUDiagnostics &TUs, TUReplacementFiles &TUFiles,
|
||||
TUReplacements &TUs,
|
||||
TUReplacementFiles & TURFiles,
|
||||
clang::DiagnosticsEngine &Diagnostics) {
|
||||
using namespace llvm::sys::fs;
|
||||
using namespace llvm::sys::path;
|
||||
@@ -100,7 +57,7 @@ collectReplacementsFromDirectory(const llvm::StringRef Directory,
|
||||
if (extension(I->path()) != ".yaml")
|
||||
continue;
|
||||
|
||||
TUFiles.push_back(I->path());
|
||||
TURFiles.push_back(I->path());
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
|
||||
MemoryBuffer::getFile(I->path());
|
||||
@@ -111,7 +68,7 @@ collectReplacementsFromDirectory(const llvm::StringRef Directory,
|
||||
}
|
||||
|
||||
yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
|
||||
tooling::TranslationUnitDiagnostics TU;
|
||||
tooling::TranslationUnitReplacements TU;
|
||||
YIn >> TU;
|
||||
if (YIn.error()) {
|
||||
// File doesn't appear to be a header change description. Ignore it.
|
||||
@@ -165,78 +122,6 @@ static void reportConflict(
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove this function after changing clang-apply-replacements to use
|
||||
// Replacements class.
|
||||
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
|
||||
Rewriter &Rewrite) {
|
||||
bool Result = true;
|
||||
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
|
||||
if (I->isApplicable()) {
|
||||
Result = I->apply(Rewrite) && Result;
|
||||
} else {
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
|
||||
// is replaced with tooling::Replacements class.
|
||||
static void deduplicate(std::vector<tooling::Replacement> &Replaces,
|
||||
std::vector<tooling::Range> &Conflicts) {
|
||||
if (Replaces.empty())
|
||||
return;
|
||||
|
||||
auto LessNoPath = [](const tooling::Replacement &LHS,
|
||||
const tooling::Replacement &RHS) {
|
||||
if (LHS.getOffset() != RHS.getOffset())
|
||||
return LHS.getOffset() < RHS.getOffset();
|
||||
if (LHS.getLength() != RHS.getLength())
|
||||
return LHS.getLength() < RHS.getLength();
|
||||
return LHS.getReplacementText() < RHS.getReplacementText();
|
||||
};
|
||||
|
||||
auto EqualNoPath = [](const tooling::Replacement &LHS,
|
||||
const tooling::Replacement &RHS) {
|
||||
return LHS.getOffset() == RHS.getOffset() &&
|
||||
LHS.getLength() == RHS.getLength() &&
|
||||
LHS.getReplacementText() == RHS.getReplacementText();
|
||||
};
|
||||
|
||||
// Deduplicate. We don't want to deduplicate based on the path as we assume
|
||||
// that all replacements refer to the same file (or are symlinks).
|
||||
std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
|
||||
Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
|
||||
Replaces.end());
|
||||
|
||||
// Detect conflicts
|
||||
tooling::Range ConflictRange(Replaces.front().getOffset(),
|
||||
Replaces.front().getLength());
|
||||
unsigned ConflictStart = 0;
|
||||
unsigned ConflictLength = 1;
|
||||
for (unsigned i = 1; i < Replaces.size(); ++i) {
|
||||
tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
|
||||
if (ConflictRange.overlapsWith(Current)) {
|
||||
// Extend conflicted range
|
||||
ConflictRange =
|
||||
tooling::Range(ConflictRange.getOffset(),
|
||||
std::max(ConflictRange.getLength(),
|
||||
Current.getOffset() + Current.getLength() -
|
||||
ConflictRange.getOffset()));
|
||||
++ConflictLength;
|
||||
} else {
|
||||
if (ConflictLength > 1)
|
||||
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
|
||||
ConflictRange = Current;
|
||||
ConflictStart = i;
|
||||
ConflictLength = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ConflictLength > 1)
|
||||
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
|
||||
}
|
||||
|
||||
/// \brief Deduplicates and tests for conflicts among the replacements for each
|
||||
/// file in \c Replacements. Any conflicts found are reported.
|
||||
///
|
||||
@@ -259,7 +144,7 @@ static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
|
||||
assert(Entry != nullptr && "No file entry!");
|
||||
|
||||
std::vector<tooling::Range> Conflicts;
|
||||
deduplicate(FileAndReplacements.second, Conflicts);
|
||||
tooling::deduplicate(FileAndReplacements.second, Conflicts);
|
||||
|
||||
if (Conflicts.empty())
|
||||
continue;
|
||||
@@ -302,34 +187,6 @@ bool mergeAndDeduplicate(const TUReplacements &TUs,
|
||||
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
|
||||
}
|
||||
|
||||
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
|
||||
FileToReplacementsMap &GroupedReplacements,
|
||||
clang::SourceManager &SM) {
|
||||
|
||||
// Group all replacements by target file.
|
||||
std::set<StringRef> Warned;
|
||||
for (const auto &TU : TUs) {
|
||||
for (const auto &D : TU.Diagnostics) {
|
||||
for (const auto &Fix : D.Fix) {
|
||||
for (const tooling::Replacement &R : Fix.second) {
|
||||
// Use the file manager to deduplicate paths. FileEntries are
|
||||
// automatically canonicalized.
|
||||
const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
|
||||
if (!Entry && Warned.insert(R.getFilePath()).second) {
|
||||
errs() << "Described file '" << R.getFilePath()
|
||||
<< "' doesn't exist. Ignoring...\n";
|
||||
continue;
|
||||
}
|
||||
GroupedReplacements[Entry].push_back(R);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ask clang to deduplicate and report conflicts.
|
||||
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
|
||||
}
|
||||
|
||||
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
|
||||
clang::Rewriter &Rewrites) {
|
||||
|
||||
@@ -340,7 +197,7 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
|
||||
// However, until we nail down the design of ReplacementGroups, might as well
|
||||
// leave this as is.
|
||||
for (const auto &FileAndReplacements : GroupedReplacements) {
|
||||
if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
|
||||
if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -365,9 +222,10 @@ RangeVector calculateChangedRanges(
|
||||
|
||||
bool writeFiles(const clang::Rewriter &Rewrites) {
|
||||
|
||||
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
|
||||
for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
|
||||
BufferE = Rewrites.buffer_end();
|
||||
BufferI != BufferE; ++BufferI) {
|
||||
StringRef FileName =
|
||||
const char *FileName =
|
||||
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
|
||||
|
||||
std::error_code EC;
|
||||
|
||||
@@ -43,6 +43,7 @@ static cl::opt<bool> RemoveTUReplacementFiles(
|
||||
"merging/replacing."),
|
||||
cl::init(false), cl::cat(ReplacementCategory));
|
||||
|
||||
|
||||
static cl::opt<bool> DoFormat(
|
||||
"format",
|
||||
cl::desc("Enable formatting of code changed by applying replacements.\n"
|
||||
@@ -62,11 +63,11 @@ static cl::opt<std::string> FormatStyleConfig(
|
||||
cl::init(""), cl::cat(FormattingCategory));
|
||||
|
||||
static cl::opt<std::string>
|
||||
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
|
||||
cl::init("LLVM"), cl::cat(FormattingCategory));
|
||||
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
|
||||
cl::init("LLVM"), cl::cat(FormattingCategory));
|
||||
|
||||
namespace {
|
||||
// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
|
||||
// Helper object to remove the TUReplacement files (triggered by
|
||||
// "remove-change-desc-files" command line option) when exiting current scope.
|
||||
class ScopedFileRemover {
|
||||
public:
|
||||
@@ -74,7 +75,9 @@ public:
|
||||
clang::DiagnosticsEngine &Diagnostics)
|
||||
: TURFiles(Files), Diag(Diagnostics) {}
|
||||
|
||||
~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
|
||||
~ScopedFileRemover() {
|
||||
deleteReplacementFiles(TURFiles, Diag);
|
||||
}
|
||||
|
||||
private:
|
||||
const TUReplacementFiles &TURFiles;
|
||||
@@ -103,10 +106,9 @@ static void printVersion() {
|
||||
static bool
|
||||
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
|
||||
Rewriter &Rewrites, std::string &Result) {
|
||||
if (Replacements.empty())
|
||||
return true;
|
||||
if (Replacements.empty()) return true;
|
||||
|
||||
if (!applyAllReplacements(Replacements, Rewrites))
|
||||
if (!tooling::applyAllReplacements(Replacements, Rewrites))
|
||||
return false;
|
||||
|
||||
SourceManager &SM = Rewrites.getSourceMgr();
|
||||
@@ -204,23 +206,19 @@ int main(int argc, char **argv) {
|
||||
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
|
||||
DiagOpts.get());
|
||||
|
||||
// Determine a formatting style from options.
|
||||
format::FormatStyle FormatStyle;
|
||||
if (DoFormat)
|
||||
FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
|
||||
|
||||
TUReplacements TURs;
|
||||
TUReplacementFiles TUFiles;
|
||||
TUReplacements TUs;
|
||||
TUReplacementFiles TURFiles;
|
||||
|
||||
std::error_code ErrorCode =
|
||||
collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
|
||||
|
||||
TUDiagnostics TUDs;
|
||||
TUFiles.clear();
|
||||
ErrorCode =
|
||||
collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
|
||||
collectReplacementsFromDirectory(Directory, TUs, TURFiles, Diagnostics);
|
||||
|
||||
if (ErrorCode) {
|
||||
errs() << "Trouble iterating over directory '" << Directory
|
||||
@@ -232,15 +230,13 @@ int main(int argc, char **argv) {
|
||||
// command line option) when exiting main().
|
||||
std::unique_ptr<ScopedFileRemover> Remover;
|
||||
if (RemoveTUReplacementFiles)
|
||||
Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
|
||||
Remover.reset(new ScopedFileRemover(TURFiles, Diagnostics));
|
||||
|
||||
FileManager Files((FileSystemOptions()));
|
||||
SourceManager SM(Diagnostics, Files);
|
||||
|
||||
FileToReplacementsMap GroupedReplacements;
|
||||
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
|
||||
return 1;
|
||||
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
|
||||
if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
|
||||
return 1;
|
||||
|
||||
Rewriter ReplacementsRewriter(SM, LangOptions());
|
||||
@@ -252,7 +248,7 @@ int main(int argc, char **argv) {
|
||||
continue;
|
||||
|
||||
std::string NewFileData;
|
||||
StringRef FileName = FileAndReplacements.first->getName();
|
||||
const char *FileName = FileAndReplacements.first->getName();
|
||||
if (!applyReplacements(FileAndReplacements.second, NewFileData,
|
||||
Diagnostics)) {
|
||||
errs() << "Failed to apply replacements to " << FileName << "\n";
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
support
|
||||
)
|
||||
|
||||
add_clang_library(clangMove
|
||||
ClangMove.cpp
|
||||
HelperDeclRefGraph.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAnalysis
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
@@ -1,883 +0,0 @@
|
||||
//===-- ClangMove.cpp - Implement ClangMove functationalities ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ClangMove.h"
|
||||
#include "HelperDeclRefGraph.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
#define DEBUG_TYPE "clang-move"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace move {
|
||||
namespace {
|
||||
|
||||
// FIXME: Move to ASTMatchers.
|
||||
AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
|
||||
|
||||
AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
|
||||
ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
|
||||
const auto *Context = Node.getDeclContext();
|
||||
if (!Context)
|
||||
return false;
|
||||
while (const auto *NextContext = Context->getParent()) {
|
||||
if (isa<NamespaceDecl>(NextContext) ||
|
||||
isa<TranslationUnitDecl>(NextContext))
|
||||
break;
|
||||
Context = NextContext;
|
||||
}
|
||||
return InnerMatcher.matches(*Decl::castFromDeclContext(Context), Finder,
|
||||
Builder);
|
||||
}
|
||||
|
||||
AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
|
||||
ast_matchers::internal::Matcher<CXXRecordDecl>, InnerMatcher) {
|
||||
const CXXRecordDecl *Parent = Node.getParent();
|
||||
if (!Parent)
|
||||
return false;
|
||||
while (const auto *NextParent =
|
||||
dyn_cast<CXXRecordDecl>(Parent->getParent())) {
|
||||
Parent = NextParent;
|
||||
}
|
||||
|
||||
return InnerMatcher.matches(*Parent, Finder, Builder);
|
||||
}
|
||||
|
||||
// Make the Path absolute using the CurrentDir if the Path is not an absolute
|
||||
// path. An empty Path will result in an empty string.
|
||||
std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
|
||||
if (Path.empty())
|
||||
return "";
|
||||
llvm::SmallString<128> InitialDirectory(CurrentDir);
|
||||
llvm::SmallString<128> AbsolutePath(Path);
|
||||
if (std::error_code EC =
|
||||
llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath))
|
||||
llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
|
||||
<< '\n';
|
||||
llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
|
||||
llvm::sys::path::native(AbsolutePath);
|
||||
return AbsolutePath.str();
|
||||
}
|
||||
|
||||
// Make the Path absolute using the current working directory of the given
|
||||
// SourceManager if the Path is not an absolute path.
|
||||
//
|
||||
// The Path can be a path relative to the build directory, or retrieved from
|
||||
// the SourceManager.
|
||||
std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
|
||||
llvm::SmallString<128> AbsolutePath(Path);
|
||||
if (std::error_code EC =
|
||||
SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
|
||||
AbsolutePath))
|
||||
llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
|
||||
<< '\n';
|
||||
// Handle symbolic link path cases.
|
||||
// We are trying to get the real file path of the symlink.
|
||||
const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
|
||||
llvm::sys::path::parent_path(AbsolutePath.str()));
|
||||
if (Dir) {
|
||||
StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
|
||||
SmallVector<char, 128> AbsoluteFilename;
|
||||
llvm::sys::path::append(AbsoluteFilename, DirName,
|
||||
llvm::sys::path::filename(AbsolutePath.str()));
|
||||
return llvm::StringRef(AbsoluteFilename.data(), AbsoluteFilename.size())
|
||||
.str();
|
||||
}
|
||||
return AbsolutePath.str();
|
||||
}
|
||||
|
||||
// Matches AST nodes that are expanded within the given AbsoluteFilePath.
|
||||
AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,
|
||||
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
|
||||
std::string, AbsoluteFilePath) {
|
||||
auto &SourceManager = Finder->getASTContext().getSourceManager();
|
||||
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart());
|
||||
if (ExpansionLoc.isInvalid())
|
||||
return false;
|
||||
auto FileEntry =
|
||||
SourceManager.getFileEntryForID(SourceManager.getFileID(ExpansionLoc));
|
||||
if (!FileEntry)
|
||||
return false;
|
||||
return MakeAbsolutePath(SourceManager, FileEntry->getName()) ==
|
||||
AbsoluteFilePath;
|
||||
}
|
||||
|
||||
class FindAllIncludes : public clang::PPCallbacks {
|
||||
public:
|
||||
explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool)
|
||||
: SM(*SM), MoveTool(MoveTool) {}
|
||||
|
||||
void InclusionDirective(clang::SourceLocation HashLoc,
|
||||
const clang::Token & /*IncludeTok*/,
|
||||
StringRef FileName, bool IsAngled,
|
||||
clang::CharSourceRange FilenameRange,
|
||||
const clang::FileEntry * /*File*/,
|
||||
StringRef SearchPath, StringRef /*RelativePath*/,
|
||||
const clang::Module * /*Imported*/) override {
|
||||
if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
|
||||
MoveTool->addIncludes(FileName, IsAngled, SearchPath,
|
||||
FileEntry->getName(), FilenameRange, SM);
|
||||
}
|
||||
|
||||
private:
|
||||
const SourceManager &SM;
|
||||
ClangMoveTool *const MoveTool;
|
||||
};
|
||||
|
||||
/// Add a declatration being moved to new.h/cc. Note that the declaration will
|
||||
/// also be deleted in old.h/cc.
|
||||
void MoveDeclFromOldFileToNewFile(ClangMoveTool *MoveTool, const NamedDecl *D) {
|
||||
MoveTool->getMovedDecls().push_back(D);
|
||||
MoveTool->addRemovedDecl(D);
|
||||
MoveTool->getUnremovedDeclsInOldHeader().erase(D);
|
||||
}
|
||||
|
||||
class FunctionDeclarationMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit FunctionDeclarationMatch(ClangMoveTool *MoveTool)
|
||||
: MoveTool(MoveTool) {}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
const auto *FD = Result.Nodes.getNodeAs<clang::FunctionDecl>("function");
|
||||
assert(FD);
|
||||
const clang::NamedDecl *D = FD;
|
||||
if (const auto *FTD = FD->getDescribedFunctionTemplate())
|
||||
D = FTD;
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, D);
|
||||
}
|
||||
|
||||
private:
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
class TypeAliasMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit TypeAliasMatch(ClangMoveTool *MoveTool)
|
||||
: MoveTool(MoveTool) {}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
if (const auto *TD = Result.Nodes.getNodeAs<clang::TypedefDecl>("typedef"))
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, TD);
|
||||
else if (const auto *TAD =
|
||||
Result.Nodes.getNodeAs<clang::TypeAliasDecl>("type_alias")) {
|
||||
const NamedDecl * D = TAD;
|
||||
if (const auto * TD = TAD->getDescribedAliasTemplate())
|
||||
D = TD;
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, D);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
class EnumDeclarationMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit EnumDeclarationMatch(ClangMoveTool *MoveTool)
|
||||
: MoveTool(MoveTool) {}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
const auto *ED = Result.Nodes.getNodeAs<clang::EnumDecl>("enum");
|
||||
assert(ED);
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, ED);
|
||||
}
|
||||
|
||||
private:
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
class ClassDeclarationMatch : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit ClassDeclarationMatch(ClangMoveTool *MoveTool)
|
||||
: MoveTool(MoveTool) {}
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
clang::SourceManager* SM = &Result.Context->getSourceManager();
|
||||
if (const auto *CMD =
|
||||
Result.Nodes.getNodeAs<clang::CXXMethodDecl>("class_method"))
|
||||
MatchClassMethod(CMD, SM);
|
||||
else if (const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>(
|
||||
"class_static_var_decl"))
|
||||
MatchClassStaticVariable(VD, SM);
|
||||
else if (const auto *CD = Result.Nodes.getNodeAs<clang::CXXRecordDecl>(
|
||||
"moved_class"))
|
||||
MatchClassDeclaration(CD, SM);
|
||||
}
|
||||
|
||||
private:
|
||||
void MatchClassMethod(const clang::CXXMethodDecl* CMD,
|
||||
clang::SourceManager* SM) {
|
||||
// Skip inline class methods. isInline() ast matcher doesn't ignore this
|
||||
// case.
|
||||
if (!CMD->isInlined()) {
|
||||
MoveTool->getMovedDecls().push_back(CMD);
|
||||
MoveTool->addRemovedDecl(CMD);
|
||||
// Get template class method from its method declaration as
|
||||
// UnremovedDecls stores template class method.
|
||||
if (const auto *FTD = CMD->getDescribedFunctionTemplate())
|
||||
MoveTool->getUnremovedDeclsInOldHeader().erase(FTD);
|
||||
else
|
||||
MoveTool->getUnremovedDeclsInOldHeader().erase(CMD);
|
||||
}
|
||||
}
|
||||
|
||||
void MatchClassStaticVariable(const clang::NamedDecl *VD,
|
||||
clang::SourceManager* SM) {
|
||||
MoveDeclFromOldFileToNewFile(MoveTool, VD);
|
||||
}
|
||||
|
||||
void MatchClassDeclaration(const clang::CXXRecordDecl *CD,
|
||||
clang::SourceManager* SM) {
|
||||
// Get class template from its class declaration as UnremovedDecls stores
|
||||
// class template.
|
||||
if (const auto *TC = CD->getDescribedClassTemplate())
|
||||
MoveTool->getMovedDecls().push_back(TC);
|
||||
else
|
||||
MoveTool->getMovedDecls().push_back(CD);
|
||||
MoveTool->addRemovedDecl(MoveTool->getMovedDecls().back());
|
||||
MoveTool->getUnremovedDeclsInOldHeader().erase(
|
||||
MoveTool->getMovedDecls().back());
|
||||
}
|
||||
|
||||
ClangMoveTool *MoveTool;
|
||||
};
|
||||
|
||||
// Expand to get the end location of the line where the EndLoc of the given
|
||||
// Decl.
|
||||
SourceLocation
|
||||
getLocForEndOfDecl(const clang::Decl *D,
|
||||
const LangOptions &LangOpts = clang::LangOptions()) {
|
||||
const auto &SM = D->getASTContext().getSourceManager();
|
||||
auto EndExpansionLoc = SM.getExpansionLoc(D->getLocEnd());
|
||||
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
|
||||
// Try to load the file buffer.
|
||||
bool InvalidTemp = false;
|
||||
llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
|
||||
if (InvalidTemp)
|
||||
return SourceLocation();
|
||||
|
||||
const char *TokBegin = File.data() + LocInfo.second;
|
||||
// Lex from the start of the given location.
|
||||
Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
|
||||
TokBegin, File.end());
|
||||
|
||||
llvm::SmallVector<char, 16> Line;
|
||||
// FIXME: this is a bit hacky to get ReadToEndOfLine work.
|
||||
Lex.setParsingPreprocessorDirective(true);
|
||||
Lex.ReadToEndOfLine(&Line);
|
||||
SourceLocation EndLoc = EndExpansionLoc.getLocWithOffset(Line.size());
|
||||
// If we already reach EOF, just return the EOF SourceLocation;
|
||||
// otherwise, move 1 offset ahead to include the trailing newline character
|
||||
// '\n'.
|
||||
return SM.getLocForEndOfFile(LocInfo.first) == EndLoc
|
||||
? EndLoc
|
||||
: EndLoc.getLocWithOffset(1);
|
||||
}
|
||||
|
||||
// Get full range of a Decl including the comments associated with it.
|
||||
clang::CharSourceRange
|
||||
getFullRange(const clang::Decl *D,
|
||||
const clang::LangOptions &options = clang::LangOptions()) {
|
||||
const auto &SM = D->getASTContext().getSourceManager();
|
||||
clang::SourceRange Full(SM.getExpansionLoc(D->getLocStart()),
|
||||
getLocForEndOfDecl(D));
|
||||
// Expand to comments that are associated with the Decl.
|
||||
if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
|
||||
if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getLocEnd()))
|
||||
Full.setEnd(Comment->getLocEnd());
|
||||
// FIXME: Don't delete a preceding comment, if there are no other entities
|
||||
// it could refer to.
|
||||
if (SM.isBeforeInTranslationUnit(Comment->getLocStart(), Full.getBegin()))
|
||||
Full.setBegin(Comment->getLocStart());
|
||||
}
|
||||
|
||||
return clang::CharSourceRange::getCharRange(Full);
|
||||
}
|
||||
|
||||
std::string getDeclarationSourceText(const clang::Decl *D) {
|
||||
const auto &SM = D->getASTContext().getSourceManager();
|
||||
llvm::StringRef SourceText =
|
||||
clang::Lexer::getSourceText(getFullRange(D), SM, clang::LangOptions());
|
||||
return SourceText.str();
|
||||
}
|
||||
|
||||
bool isInHeaderFile(const clang::Decl *D,
|
||||
llvm::StringRef OriginalRunningDirectory,
|
||||
llvm::StringRef OldHeader) {
|
||||
const auto &SM = D->getASTContext().getSourceManager();
|
||||
if (OldHeader.empty())
|
||||
return false;
|
||||
auto ExpansionLoc = SM.getExpansionLoc(D->getLocStart());
|
||||
if (ExpansionLoc.isInvalid())
|
||||
return false;
|
||||
|
||||
if (const auto *FE = SM.getFileEntryForID(SM.getFileID(ExpansionLoc))) {
|
||||
return MakeAbsolutePath(SM, FE->getName()) ==
|
||||
MakeAbsolutePath(OriginalRunningDirectory, OldHeader);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> getNamespaces(const clang::Decl *D) {
|
||||
std::vector<std::string> Namespaces;
|
||||
for (const auto *Context = D->getDeclContext(); Context;
|
||||
Context = Context->getParent()) {
|
||||
if (llvm::isa<clang::TranslationUnitDecl>(Context) ||
|
||||
llvm::isa<clang::LinkageSpecDecl>(Context))
|
||||
break;
|
||||
|
||||
if (const auto *ND = llvm::dyn_cast<clang::NamespaceDecl>(Context))
|
||||
Namespaces.push_back(ND->getName().str());
|
||||
}
|
||||
std::reverse(Namespaces.begin(), Namespaces.end());
|
||||
return Namespaces;
|
||||
}
|
||||
|
||||
clang::tooling::Replacements
|
||||
createInsertedReplacements(const std::vector<std::string> &Includes,
|
||||
const std::vector<const NamedDecl *> &Decls,
|
||||
llvm::StringRef FileName, bool IsHeader = false,
|
||||
StringRef OldHeaderInclude = "") {
|
||||
std::string NewCode;
|
||||
std::string GuardName(FileName);
|
||||
if (IsHeader) {
|
||||
for (size_t i = 0; i < GuardName.size(); ++i) {
|
||||
if (!isAlphanumeric(GuardName[i]))
|
||||
GuardName[i] = '_';
|
||||
}
|
||||
GuardName = StringRef(GuardName).upper();
|
||||
NewCode += "#ifndef " + GuardName + "\n";
|
||||
NewCode += "#define " + GuardName + "\n\n";
|
||||
}
|
||||
|
||||
NewCode += OldHeaderInclude;
|
||||
// Add #Includes.
|
||||
for (const auto &Include : Includes)
|
||||
NewCode += Include;
|
||||
|
||||
if (!Includes.empty())
|
||||
NewCode += "\n";
|
||||
|
||||
// Add moved class definition and its related declarations. All declarations
|
||||
// in same namespace are grouped together.
|
||||
//
|
||||
// Record namespaces where the current position is in.
|
||||
std::vector<std::string> CurrentNamespaces;
|
||||
for (const auto *MovedDecl : Decls) {
|
||||
// The namespaces of the declaration being moved.
|
||||
std::vector<std::string> DeclNamespaces = getNamespaces(MovedDecl);
|
||||
auto CurrentIt = CurrentNamespaces.begin();
|
||||
auto DeclIt = DeclNamespaces.begin();
|
||||
// Skip the common prefix.
|
||||
while (CurrentIt != CurrentNamespaces.end() &&
|
||||
DeclIt != DeclNamespaces.end()) {
|
||||
if (*CurrentIt != *DeclIt)
|
||||
break;
|
||||
++CurrentIt;
|
||||
++DeclIt;
|
||||
}
|
||||
// Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
|
||||
// which is used for next iteration of this loop.
|
||||
std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
|
||||
CurrentIt);
|
||||
NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
|
||||
|
||||
|
||||
// End with CurrentNamespace.
|
||||
bool HasEndCurrentNamespace = false;
|
||||
auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
|
||||
for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
|
||||
--RemainingSize, ++It) {
|
||||
assert(It < CurrentNamespaces.rend());
|
||||
NewCode += "} // namespace " + *It + "\n";
|
||||
HasEndCurrentNamespace = true;
|
||||
}
|
||||
// Add trailing '\n' after the nested namespace definition.
|
||||
if (HasEndCurrentNamespace)
|
||||
NewCode += "\n";
|
||||
|
||||
// If the moved declaration is not in CurrentNamespace, add extra namespace
|
||||
// definitions.
|
||||
bool IsInNewNamespace = false;
|
||||
while (DeclIt != DeclNamespaces.end()) {
|
||||
NewCode += "namespace " + *DeclIt + " {\n";
|
||||
IsInNewNamespace = true;
|
||||
++DeclIt;
|
||||
}
|
||||
// If the moved declaration is in same namespace CurrentNamespace, add
|
||||
// a preceeding `\n' before the moved declaration.
|
||||
// FIXME: Don't add empty lines between using declarations.
|
||||
if (!IsInNewNamespace)
|
||||
NewCode += "\n";
|
||||
NewCode += getDeclarationSourceText(MovedDecl);
|
||||
CurrentNamespaces = std::move(NextNamespaces);
|
||||
}
|
||||
std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
|
||||
for (const auto &NS : CurrentNamespaces)
|
||||
NewCode += "} // namespace " + NS + "\n";
|
||||
|
||||
if (IsHeader)
|
||||
NewCode += "\n#endif // " + GuardName + "\n";
|
||||
return clang::tooling::Replacements(
|
||||
clang::tooling::Replacement(FileName, 0, 0, NewCode));
|
||||
}
|
||||
|
||||
// Return a set of all decls which are used/referenced by the given Decls.
|
||||
// Specically, given a class member declaration, this method will return all
|
||||
// decls which are used by the whole class.
|
||||
llvm::DenseSet<const Decl *>
|
||||
getUsedDecls(const HelperDeclRefGraph *RG,
|
||||
const std::vector<const NamedDecl *> &Decls) {
|
||||
assert(RG);
|
||||
llvm::DenseSet<const CallGraphNode *> Nodes;
|
||||
for (const auto *D : Decls) {
|
||||
auto Result = RG->getReachableNodes(
|
||||
HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
|
||||
Nodes.insert(Result.begin(), Result.end());
|
||||
}
|
||||
llvm::DenseSet<const Decl *> Results;
|
||||
for (const auto *Node : Nodes)
|
||||
Results.insert(Node->getDecl());
|
||||
return Results;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer>
|
||||
ClangMoveAction::CreateASTConsumer(clang::CompilerInstance &Compiler,
|
||||
StringRef /*InFile*/) {
|
||||
Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllIncludes>(
|
||||
&Compiler.getSourceManager(), &MoveTool));
|
||||
return MatchFinder.newASTConsumer();
|
||||
}
|
||||
|
||||
ClangMoveTool::ClangMoveTool(ClangMoveContext *const Context,
|
||||
DeclarationReporter *const Reporter)
|
||||
: Context(Context), Reporter(Reporter) {
|
||||
if (!Context->Spec.NewHeader.empty())
|
||||
CCIncludes.push_back("#include \"" + Context->Spec.NewHeader + "\"\n");
|
||||
}
|
||||
|
||||
void ClangMoveTool::addRemovedDecl(const NamedDecl *Decl) {
|
||||
const auto &SM = Decl->getASTContext().getSourceManager();
|
||||
auto Loc = Decl->getLocation();
|
||||
StringRef FilePath = SM.getFilename(Loc);
|
||||
FilePathToFileID[FilePath] = SM.getFileID(Loc);
|
||||
RemovedDecls.push_back(Decl);
|
||||
}
|
||||
|
||||
void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
auto InOldHeader =
|
||||
isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
|
||||
auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
|
||||
auto InOldFiles = anyOf(InOldHeader, InOldCC);
|
||||
auto ForwardDecls =
|
||||
cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())));
|
||||
auto TopLevelDecl =
|
||||
hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
|
||||
|
||||
//============================================================================
|
||||
// Matchers for old header
|
||||
//============================================================================
|
||||
// Match all top-level named declarations (e.g. function, variable, enum) in
|
||||
// old header, exclude forward class declarations and namespace declarations.
|
||||
//
|
||||
// We consider declarations inside a class belongs to the class. So these
|
||||
// declarations will be ignored.
|
||||
auto AllDeclsInHeader = namedDecl(
|
||||
unless(ForwardDecls), unless(namespaceDecl()),
|
||||
unless(usingDirectiveDecl()), // using namespace decl.
|
||||
unless(classTemplateDecl(has(ForwardDecls))), // template forward decl.
|
||||
InOldHeader,
|
||||
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
|
||||
hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
|
||||
Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
|
||||
|
||||
// Don't register other matchers when dumping all declarations in header.
|
||||
if (Context->DumpDeclarations)
|
||||
return;
|
||||
|
||||
// Match forward declarations in old header.
|
||||
Finder->addMatcher(namedDecl(ForwardDecls, InOldHeader).bind("fwd_decl"),
|
||||
this);
|
||||
|
||||
//============================================================================
|
||||
// Matchers for old cc
|
||||
//============================================================================
|
||||
auto IsOldCCTopLevelDecl = allOf(
|
||||
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC);
|
||||
// Matching using decls/type alias decls which are in named/anonymous/global
|
||||
// namespace, these decls are always copied to new.h/cc. Those in classes,
|
||||
// functions are covered in other matchers.
|
||||
Finder->addMatcher(
|
||||
namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
|
||||
usingDirectiveDecl(IsOldCCTopLevelDecl),
|
||||
typeAliasDecl(IsOldCCTopLevelDecl)))
|
||||
.bind("using_decl"),
|
||||
this);
|
||||
|
||||
// Match static functions/variable definitions which are defined in named
|
||||
// namespaces.
|
||||
Optional<ast_matchers::internal::Matcher<NamedDecl>> HasAnySymbolNames;
|
||||
for (StringRef SymbolName : Context->Spec.Names) {
|
||||
llvm::StringRef GlobalSymbolName = SymbolName.trim().ltrim(':');
|
||||
const auto HasName = hasName(("::" + GlobalSymbolName).str());
|
||||
HasAnySymbolNames =
|
||||
HasAnySymbolNames ? anyOf(*HasAnySymbolNames, HasName) : HasName;
|
||||
}
|
||||
|
||||
if (!HasAnySymbolNames) {
|
||||
llvm::errs() << "No symbols being moved.\n";
|
||||
return;
|
||||
}
|
||||
auto InMovedClass =
|
||||
hasOutermostEnclosingClass(cxxRecordDecl(*HasAnySymbolNames));
|
||||
|
||||
// Matchers for helper declarations in old.cc.
|
||||
auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
|
||||
auto DefinitionInOldCC = allOf(isDefinition(), unless(InMovedClass), InOldCC);
|
||||
auto IsOldCCHelperDefinition =
|
||||
allOf(DefinitionInOldCC, anyOf(isStaticStorageClass(), InAnonymousNS));
|
||||
// Match helper classes separately with helper functions/variables since we
|
||||
// want to reuse these matchers in finding helpers usage below.
|
||||
auto HelperFuncOrVar = namedDecl(anyOf(functionDecl(IsOldCCHelperDefinition),
|
||||
varDecl(IsOldCCHelperDefinition)));
|
||||
auto HelperClasses = cxxRecordDecl(DefinitionInOldCC, InAnonymousNS);
|
||||
// Save all helper declarations in old.cc.
|
||||
Finder->addMatcher(
|
||||
namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
|
||||
this);
|
||||
|
||||
// Construct an AST-based call graph of helper declarations in old.cc.
|
||||
// In the following matcheres, "dc" is a caller while "helper_decls" and
|
||||
// "used_class" is a callee, so a new edge starting from caller to callee will
|
||||
// be add in the graph.
|
||||
//
|
||||
// Find helper function/variable usages.
|
||||
Finder->addMatcher(
|
||||
declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc")))
|
||||
.bind("func_ref"),
|
||||
&RGBuilder);
|
||||
// Find helper class usages.
|
||||
Finder->addMatcher(
|
||||
typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))),
|
||||
hasAncestor(decl().bind("dc"))),
|
||||
&RGBuilder);
|
||||
|
||||
//============================================================================
|
||||
// Matchers for old files, including old.h/old.cc
|
||||
//============================================================================
|
||||
// Create a MatchCallback for class declarations.
|
||||
MatchCallbacks.push_back(llvm::make_unique<ClassDeclarationMatch>(this));
|
||||
// Match moved class declarations.
|
||||
auto MovedClass = cxxRecordDecl(InOldFiles, *HasAnySymbolNames,
|
||||
isDefinition(), TopLevelDecl)
|
||||
.bind("moved_class");
|
||||
Finder->addMatcher(MovedClass, MatchCallbacks.back().get());
|
||||
// Match moved class methods (static methods included) which are defined
|
||||
// outside moved class declaration.
|
||||
Finder->addMatcher(
|
||||
cxxMethodDecl(InOldFiles, ofOutermostEnclosingClass(*HasAnySymbolNames),
|
||||
isDefinition())
|
||||
.bind("class_method"),
|
||||
MatchCallbacks.back().get());
|
||||
// Match static member variable definition of the moved class.
|
||||
Finder->addMatcher(
|
||||
varDecl(InMovedClass, InOldFiles, isDefinition(), isStaticDataMember())
|
||||
.bind("class_static_var_decl"),
|
||||
MatchCallbacks.back().get());
|
||||
|
||||
MatchCallbacks.push_back(llvm::make_unique<FunctionDeclarationMatch>(this));
|
||||
Finder->addMatcher(functionDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl)
|
||||
.bind("function"),
|
||||
MatchCallbacks.back().get());
|
||||
|
||||
// Match enum definition in old.h. Enum helpers (which are defined in old.cc)
|
||||
// will not be moved for now no matter whether they are used or not.
|
||||
MatchCallbacks.push_back(llvm::make_unique<EnumDeclarationMatch>(this));
|
||||
Finder->addMatcher(
|
||||
enumDecl(InOldHeader, *HasAnySymbolNames, isDefinition(), TopLevelDecl)
|
||||
.bind("enum"),
|
||||
MatchCallbacks.back().get());
|
||||
|
||||
// Match type alias in old.h, this includes "typedef" and "using" type alias
|
||||
// declarations. Type alias helpers (which are defined in old.cc) will not be
|
||||
// moved for now no matter whether they are used or not.
|
||||
MatchCallbacks.push_back(llvm::make_unique<TypeAliasMatch>(this));
|
||||
Finder->addMatcher(namedDecl(anyOf(typedefDecl().bind("typedef"),
|
||||
typeAliasDecl().bind("type_alias")),
|
||||
InOldHeader, *HasAnySymbolNames, TopLevelDecl),
|
||||
MatchCallbacks.back().get());
|
||||
}
|
||||
|
||||
void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
if (const auto *D =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("decls_in_header")) {
|
||||
UnremovedDeclsInOldHeader.insert(D);
|
||||
} else if (const auto *FWD =
|
||||
Result.Nodes.getNodeAs<clang::CXXRecordDecl>("fwd_decl")) {
|
||||
// Skip all forward declarations which appear after moved class declaration.
|
||||
if (RemovedDecls.empty()) {
|
||||
if (const auto *DCT = FWD->getDescribedClassTemplate())
|
||||
MovedDecls.push_back(DCT);
|
||||
else
|
||||
MovedDecls.push_back(FWD);
|
||||
}
|
||||
} else if (const auto *ND =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("static_decls")) {
|
||||
MovedDecls.push_back(ND);
|
||||
} else if (const auto *ND =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
|
||||
MovedDecls.push_back(ND);
|
||||
HelperDeclarations.push_back(ND);
|
||||
} else if (const auto *UD =
|
||||
Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
|
||||
MovedDecls.push_back(UD);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
|
||||
return MakeAbsolutePath(Context->OriginalRunningDirectory, Path);
|
||||
}
|
||||
|
||||
void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
|
||||
llvm::StringRef SearchPath,
|
||||
llvm::StringRef FileName,
|
||||
clang::CharSourceRange IncludeFilenameRange,
|
||||
const SourceManager &SM) {
|
||||
SmallVector<char, 128> HeaderWithSearchPath;
|
||||
llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
|
||||
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
|
||||
if (AbsoluteOldHeader ==
|
||||
MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
|
||||
HeaderWithSearchPath.size()))) {
|
||||
OldHeaderIncludeRange = IncludeFilenameRange;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string IncludeLine =
|
||||
IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
|
||||
: ("#include \"" + IncludeHeader + "\"\n").str();
|
||||
|
||||
std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
|
||||
if (AbsoluteOldHeader == AbsoluteCurrentFile) {
|
||||
HeaderIncludes.push_back(IncludeLine);
|
||||
} else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
|
||||
CCIncludes.push_back(IncludeLine);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangMoveTool::removeDeclsInOldFiles() {
|
||||
if (RemovedDecls.empty()) return;
|
||||
|
||||
// If old_header is not specified (only move declarations from old.cc), remain
|
||||
// all the helper function declarations in old.cc as UnremovedDeclsInOldHeader
|
||||
// is empty in this case, there is no way to verify unused/used helpers.
|
||||
if (!Context->Spec.OldHeader.empty()) {
|
||||
std::vector<const NamedDecl *> UnremovedDecls;
|
||||
for (const auto *D : UnremovedDeclsInOldHeader)
|
||||
UnremovedDecls.push_back(D);
|
||||
|
||||
auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls);
|
||||
|
||||
// We remove the helper declarations which are not used in the old.cc after
|
||||
// moving the given declarations.
|
||||
for (const auto *D : HelperDeclarations) {
|
||||
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D))) {
|
||||
DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
|
||||
<< D->getNameAsString() << " " << D << "\n");
|
||||
RemovedDecls.push_back(D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *RemovedDecl : RemovedDecls) {
|
||||
const auto &SM = RemovedDecl->getASTContext().getSourceManager();
|
||||
auto Range = getFullRange(RemovedDecl);
|
||||
clang::tooling::Replacement RemoveReplacement(
|
||||
SM,
|
||||
clang::CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()),
|
||||
"");
|
||||
std::string FilePath = RemoveReplacement.getFilePath().str();
|
||||
auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement);
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
}
|
||||
const auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
|
||||
|
||||
// Post process of cleanup around all the replacements.
|
||||
for (auto &FileAndReplacements : Context->FileToReplacements) {
|
||||
StringRef FilePath = FileAndReplacements.first;
|
||||
// Add #include of new header to old header.
|
||||
if (Context->Spec.OldDependOnNew &&
|
||||
MakeAbsolutePath(SM, FilePath) ==
|
||||
makeAbsolutePath(Context->Spec.OldHeader)) {
|
||||
// FIXME: Minimize the include path like include-fixer.
|
||||
std::string IncludeNewH =
|
||||
"#include \"" + Context->Spec.NewHeader + "\"\n";
|
||||
// This replacment for inserting header will be cleaned up at the end.
|
||||
auto Err = FileAndReplacements.second.add(
|
||||
tooling::Replacement(FilePath, UINT_MAX, 0, IncludeNewH));
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
}
|
||||
|
||||
auto SI = FilePathToFileID.find(FilePath);
|
||||
// Ignore replacements for new.h/cc.
|
||||
if (SI == FilePathToFileID.end()) continue;
|
||||
llvm::StringRef Code = SM.getBufferData(SI->second);
|
||||
format::FormatStyle Style =
|
||||
format::getStyle("file", FilePath, Context->FallbackStyle);
|
||||
auto CleanReplacements = format::cleanupAroundReplacements(
|
||||
Code, Context->FileToReplacements[FilePath], Style);
|
||||
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
Context->FileToReplacements[FilePath] = *CleanReplacements;
|
||||
}
|
||||
}
|
||||
|
||||
void ClangMoveTool::moveDeclsToNewFiles() {
|
||||
std::vector<const NamedDecl *> NewHeaderDecls;
|
||||
std::vector<const NamedDecl *> NewCCDecls;
|
||||
for (const auto *MovedDecl : MovedDecls) {
|
||||
if (isInHeaderFile(MovedDecl, Context->OriginalRunningDirectory,
|
||||
Context->Spec.OldHeader))
|
||||
NewHeaderDecls.push_back(MovedDecl);
|
||||
else
|
||||
NewCCDecls.push_back(MovedDecl);
|
||||
}
|
||||
|
||||
auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
|
||||
std::vector<const NamedDecl *> ActualNewCCDecls;
|
||||
|
||||
// Filter out all unused helpers in NewCCDecls.
|
||||
// We only move the used helpers (including transively used helpers) and the
|
||||
// given symbols being moved.
|
||||
for (const auto *D : NewCCDecls) {
|
||||
if (llvm::is_contained(HelperDeclarations, D) &&
|
||||
!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D)))
|
||||
continue;
|
||||
|
||||
DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
|
||||
<< " " << D << "\n");
|
||||
ActualNewCCDecls.push_back(D);
|
||||
}
|
||||
|
||||
if (!Context->Spec.NewHeader.empty()) {
|
||||
std::string OldHeaderInclude =
|
||||
Context->Spec.NewDependOnOld
|
||||
? "#include \"" + Context->Spec.OldHeader + "\"\n"
|
||||
: "";
|
||||
Context->FileToReplacements[Context->Spec.NewHeader] =
|
||||
createInsertedReplacements(HeaderIncludes, NewHeaderDecls,
|
||||
Context->Spec.NewHeader, /*IsHeader=*/true,
|
||||
OldHeaderInclude);
|
||||
}
|
||||
if (!Context->Spec.NewCC.empty())
|
||||
Context->FileToReplacements[Context->Spec.NewCC] =
|
||||
createInsertedReplacements(CCIncludes, ActualNewCCDecls,
|
||||
Context->Spec.NewCC);
|
||||
}
|
||||
|
||||
// Move all contents from OldFile to NewFile.
|
||||
void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
|
||||
StringRef NewFile) {
|
||||
const FileEntry *FE = SM.getFileManager().getFile(makeAbsolutePath(OldFile));
|
||||
if (!FE) {
|
||||
llvm::errs() << "Failed to get file: " << OldFile << "\n";
|
||||
return;
|
||||
}
|
||||
FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User);
|
||||
auto Begin = SM.getLocForStartOfFile(ID);
|
||||
auto End = SM.getLocForEndOfFile(ID);
|
||||
clang::tooling::Replacement RemoveAll (
|
||||
SM, clang::CharSourceRange::getCharRange(Begin, End), "");
|
||||
std::string FilePath = RemoveAll.getFilePath().str();
|
||||
Context->FileToReplacements[FilePath] =
|
||||
clang::tooling::Replacements(RemoveAll);
|
||||
|
||||
StringRef Code = SM.getBufferData(ID);
|
||||
if (!NewFile.empty()) {
|
||||
auto AllCode = clang::tooling::Replacements(
|
||||
clang::tooling::Replacement(NewFile, 0, 0, Code));
|
||||
// If we are moving from old.cc, an extra step is required: excluding
|
||||
// the #include of "old.h", instead, we replace it with #include of "new.h".
|
||||
if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) {
|
||||
AllCode = AllCode.merge(
|
||||
clang::tooling::Replacements(clang::tooling::Replacement(
|
||||
SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"')));
|
||||
}
|
||||
Context->FileToReplacements[NewFile] = std::move(AllCode);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangMoveTool::onEndOfTranslationUnit() {
|
||||
if (Context->DumpDeclarations) {
|
||||
assert(Reporter);
|
||||
for (const auto *Decl : UnremovedDeclsInOldHeader) {
|
||||
auto Kind = Decl->getKind();
|
||||
const std::string QualifiedName = Decl->getQualifiedNameAsString();
|
||||
if (Kind == Decl::Kind::Function || Kind == Decl::Kind::FunctionTemplate)
|
||||
Reporter->reportDeclaration(QualifiedName, "Function");
|
||||
else if (Kind == Decl::Kind::ClassTemplate ||
|
||||
Kind == Decl::Kind::CXXRecord)
|
||||
Reporter->reportDeclaration(QualifiedName, "Class");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (RemovedDecls.empty())
|
||||
return;
|
||||
// Ignore symbols that are not supported (e.g. typedef and enum) when
|
||||
// checking if there is unremoved symbol in old header. This makes sure that
|
||||
// we always move old files to new files when all symbols produced from
|
||||
// dump_decls are moved.
|
||||
auto IsSupportedKind = [](const clang::NamedDecl *Decl) {
|
||||
switch (Decl->getKind()) {
|
||||
case Decl::Kind::Function:
|
||||
case Decl::Kind::FunctionTemplate:
|
||||
case Decl::Kind::ClassTemplate:
|
||||
case Decl::Kind::CXXRecord:
|
||||
case Decl::Kind::Enum:
|
||||
case Decl::Kind::Typedef:
|
||||
case Decl::Kind::TypeAlias:
|
||||
case Decl::Kind::TypeAliasTemplate:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if (std::none_of(UnremovedDeclsInOldHeader.begin(),
|
||||
UnremovedDeclsInOldHeader.end(), IsSupportedKind) &&
|
||||
!Context->Spec.OldHeader.empty()) {
|
||||
auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
|
||||
moveAll(SM, Context->Spec.OldHeader, Context->Spec.NewHeader);
|
||||
moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
|
||||
return;
|
||||
}
|
||||
DEBUG(RGBuilder.getGraph()->dump());
|
||||
moveDeclsToNewFiles();
|
||||
removeDeclsInOldFiles();
|
||||
}
|
||||
|
||||
} // namespace move
|
||||
} // namespace clang
|
||||
@@ -1,229 +0,0 @@
|
||||
//===-- ClangMove.h - Clang move -----------------------------------------===//
|
||||
//
|
||||
// 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_MOVE_CLANGMOVE_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
|
||||
|
||||
#include "HelperDeclRefGraph.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace move {
|
||||
|
||||
// A reporter which collects and reports declarations in old header.
|
||||
class DeclarationReporter {
|
||||
public:
|
||||
DeclarationReporter() = default;
|
||||
~DeclarationReporter() = default;
|
||||
|
||||
void reportDeclaration(llvm::StringRef DeclarationName,
|
||||
llvm::StringRef Type) {
|
||||
DeclarationList.emplace_back(DeclarationName, Type);
|
||||
};
|
||||
|
||||
// A <DeclarationName, DeclarationKind> pair.
|
||||
// The DeclarationName is a fully qualified name for the declaration, like
|
||||
// A::B::Foo. The DeclarationKind is a string represents the kind of the
|
||||
// declaration, currently only "Function" and "Class" are supported.
|
||||
typedef std::pair<std::string, std::string> DeclarationPair;
|
||||
|
||||
const std::vector<DeclarationPair> getDeclarationList() const {
|
||||
return DeclarationList;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<DeclarationPair> DeclarationList;
|
||||
};
|
||||
|
||||
// Specify declarations being moved. It contains all information of the moved
|
||||
// declarations.
|
||||
struct MoveDefinitionSpec {
|
||||
// The list of fully qualified names, e.g. Foo, a::Foo, b::Foo.
|
||||
SmallVector<std::string, 4> Names;
|
||||
// The file path of old header, can be relative path and absolute path.
|
||||
std::string OldHeader;
|
||||
// The file path of old cc, can be relative path and absolute path.
|
||||
std::string OldCC;
|
||||
// The file path of new header, can be relative path and absolute path.
|
||||
std::string NewHeader;
|
||||
// The file path of new cc, can be relative path and absolute path.
|
||||
std::string NewCC;
|
||||
// Whether old.h depends on new.h. If true, #include "new.h" will be added
|
||||
// in old.h.
|
||||
bool OldDependOnNew = false;
|
||||
// Whether new.h depends on old.h. If true, #include "old.h" will be added
|
||||
// in new.h.
|
||||
bool NewDependOnOld = false;
|
||||
};
|
||||
|
||||
// A Context which contains extra options which are used in ClangMoveTool.
|
||||
struct ClangMoveContext {
|
||||
MoveDefinitionSpec Spec;
|
||||
// The Key is file path, value is the replacements being applied to the file.
|
||||
std::map<std::string, tooling::Replacements> &FileToReplacements;
|
||||
// The original working directory where the local clang-move binary runs.
|
||||
//
|
||||
// clang-move will change its current working directory to the build
|
||||
// directory when analyzing the source file. We save the original working
|
||||
// directory in order to get the absolute file path for the fields in Spec.
|
||||
std::string OriginalRunningDirectory;
|
||||
// The name of a predefined code style.
|
||||
std::string FallbackStyle;
|
||||
// Whether dump all declarations in old header.
|
||||
bool DumpDeclarations;
|
||||
};
|
||||
|
||||
// This tool is used to move class/function definitions from the given source
|
||||
// files (old.h/cc) to new files (new.h/cc).
|
||||
// The goal of this tool is to make the new/old files as compilable as possible.
|
||||
//
|
||||
// When moving a symbol,all used helper declarations (e.g. static
|
||||
// functions/variables definitions in global/named namespace,
|
||||
// functions/variables/classes definitions in anonymous namespace) used by the
|
||||
// moved symbol in old.cc are moved to the new.cc. In addition, all
|
||||
// using-declarations in old.cc are also moved to new.cc; forward class
|
||||
// declarations in old.h are also moved to new.h.
|
||||
//
|
||||
// The remaining helper declarations which are unused by non-moved symbols in
|
||||
// old.cc will be removed.
|
||||
//
|
||||
// Note: When all declarations in old header are being moved, all code in
|
||||
// old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols
|
||||
// that are not supported (e.g. typedef and enum) so that we always move old
|
||||
// files to new files when all symbols produced from dump_decls are moved.
|
||||
class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
ClangMoveTool(ClangMoveContext *const Context,
|
||||
DeclarationReporter *const Reporter);
|
||||
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder);
|
||||
|
||||
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
void onEndOfTranslationUnit() override;
|
||||
|
||||
/// Add #includes from old.h/cc files.
|
||||
///
|
||||
/// \param IncludeHeader The name of the file being included, as written in
|
||||
/// the source code.
|
||||
/// \param IsAngled Whether the file name was enclosed in angle brackets.
|
||||
/// \param SearchPath The search path which was used to find the IncludeHeader
|
||||
/// in the file system. It can be a relative path or an absolute path.
|
||||
/// \param FileName The name of file where the IncludeHeader comes from.
|
||||
/// \param IncludeFilenameRange The source range for the written file name in
|
||||
/// #include (i.e. "old.h" for #include "old.h") in old.cc.
|
||||
/// \param SM The SourceManager.
|
||||
void addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
|
||||
llvm::StringRef SearchPath, llvm::StringRef FileName,
|
||||
clang::CharSourceRange IncludeFilenameRange,
|
||||
const SourceManager &SM);
|
||||
|
||||
std::vector<const NamedDecl *> &getMovedDecls() { return MovedDecls; }
|
||||
|
||||
/// Add declarations being removed from old.h/cc. For each declarations, the
|
||||
/// method also records the mapping relationship between the corresponding
|
||||
/// FilePath and its FileID.
|
||||
void addRemovedDecl(const NamedDecl *Decl);
|
||||
|
||||
llvm::SmallPtrSet<const NamedDecl *, 8> &getUnremovedDeclsInOldHeader() {
|
||||
return UnremovedDeclsInOldHeader;
|
||||
}
|
||||
|
||||
private:
|
||||
// Make the Path absolute using the OrignalRunningDirectory if the Path is not
|
||||
// an absolute path. An empty Path will result in an empty string.
|
||||
std::string makeAbsolutePath(StringRef Path);
|
||||
|
||||
void removeDeclsInOldFiles();
|
||||
void moveDeclsToNewFiles();
|
||||
void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);
|
||||
|
||||
// Stores all MatchCallbacks created by this tool.
|
||||
std::vector<std::unique_ptr<ast_matchers::MatchFinder::MatchCallback>>
|
||||
MatchCallbacks;
|
||||
// Store all potential declarations (decls being moved, forward decls) that
|
||||
// might need to move to new.h/cc. It includes all helper declarations
|
||||
// (include unused ones) by default. The unused ones will be filtered out in
|
||||
// the last stage. Saving in an AST-visited order.
|
||||
std::vector<const NamedDecl *> MovedDecls;
|
||||
// The declarations that needs to be removed in old.cc/h.
|
||||
std::vector<const NamedDecl *> RemovedDecls;
|
||||
// The #includes in old_header.h.
|
||||
std::vector<std::string> HeaderIncludes;
|
||||
// The #includes in old_cc.cc.
|
||||
std::vector<std::string> CCIncludes;
|
||||
// Records all helper declarations (function/variable/class definitions in
|
||||
// anonymous namespaces, static function/variable definitions in global/named
|
||||
// namespaces) in old.cc. saving in an AST-visited order.
|
||||
std::vector<const NamedDecl *> HelperDeclarations;
|
||||
// The unmoved named declarations in old header.
|
||||
llvm::SmallPtrSet<const NamedDecl*, 8> UnremovedDeclsInOldHeader;
|
||||
/// The source range for the written file name in #include (i.e. "old.h" for
|
||||
/// #include "old.h") in old.cc, including the enclosing quotes or angle
|
||||
/// brackets.
|
||||
clang::CharSourceRange OldHeaderIncludeRange;
|
||||
/// Mapping from FilePath to FileID, which can be used in post processes like
|
||||
/// cleanup around replacements.
|
||||
llvm::StringMap<FileID> FilePathToFileID;
|
||||
/// A context contains all running options. It is not owned.
|
||||
ClangMoveContext *const Context;
|
||||
/// A reporter to report all declarations from old header. It is not owned.
|
||||
DeclarationReporter *const Reporter;
|
||||
/// Builder for helper declarations reference graph.
|
||||
HelperDeclRGBuilder RGBuilder;
|
||||
};
|
||||
|
||||
class ClangMoveAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
ClangMoveAction(ClangMoveContext *const Context,
|
||||
DeclarationReporter *const Reporter)
|
||||
: MoveTool(Context, Reporter) {
|
||||
MoveTool.registerMatchers(&MatchFinder);
|
||||
}
|
||||
|
||||
~ClangMoveAction() override = default;
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer>
|
||||
CreateASTConsumer(clang::CompilerInstance &Compiler,
|
||||
llvm::StringRef InFile) override;
|
||||
|
||||
private:
|
||||
ast_matchers::MatchFinder MatchFinder;
|
||||
ClangMoveTool MoveTool;
|
||||
};
|
||||
|
||||
class ClangMoveActionFactory : public tooling::FrontendActionFactory {
|
||||
public:
|
||||
ClangMoveActionFactory(ClangMoveContext *const Context,
|
||||
DeclarationReporter *const Reporter = nullptr)
|
||||
: Context(Context), Reporter(Reporter) {}
|
||||
|
||||
clang::FrontendAction *create() override {
|
||||
return new ClangMoveAction(Context, Reporter);
|
||||
}
|
||||
|
||||
private:
|
||||
// Not owned.
|
||||
ClangMoveContext *const Context;
|
||||
DeclarationReporter *const Reporter;
|
||||
};
|
||||
|
||||
} // namespace move
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
|
||||
@@ -1,128 +0,0 @@
|
||||
//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "HelperDeclRefGraph.h"
|
||||
#include "ClangMove.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace move {
|
||||
|
||||
void HelperDeclRefGraph::print(raw_ostream &OS) const {
|
||||
OS << " --- Call graph Dump --- \n";
|
||||
for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
|
||||
const CallGraphNode *N = (I->second).get();
|
||||
|
||||
OS << " Declarations: ";
|
||||
N->print(OS);
|
||||
OS << " (" << N << ") ";
|
||||
OS << " calls: ";
|
||||
for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
|
||||
(*CI)->print(OS);
|
||||
OS << " (" << CI << ") ";
|
||||
}
|
||||
OS << '\n';
|
||||
}
|
||||
OS.flush();
|
||||
}
|
||||
|
||||
void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
|
||||
assert(Caller);
|
||||
assert(Callee);
|
||||
|
||||
// Ignore the case where Caller equals Callee. This happens in the static
|
||||
// class member definitions in global namespace like "int CLASS::static_var =
|
||||
// 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
|
||||
// CXXRecordDecl.
|
||||
if (Caller == Callee) return;
|
||||
|
||||
// Allocate a new node, mark it as root, and process it's calls.
|
||||
CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
|
||||
CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
|
||||
CallerNode->addCallee(CalleeNode);
|
||||
}
|
||||
|
||||
void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
|
||||
|
||||
CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
|
||||
F = F->getCanonicalDecl();
|
||||
std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
|
||||
if (Node)
|
||||
return Node.get();
|
||||
|
||||
Node = llvm::make_unique<CallGraphNode>(F);
|
||||
return Node.get();
|
||||
}
|
||||
|
||||
CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
|
||||
auto I = DeclMap.find(D->getCanonicalDecl());
|
||||
return I == DeclMap.end() ? nullptr : I->second.get();
|
||||
}
|
||||
|
||||
llvm::DenseSet<const CallGraphNode *>
|
||||
HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
|
||||
const auto *RootNode = getNode(Root);
|
||||
if (!RootNode)
|
||||
return {};
|
||||
llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
|
||||
std::function<void(const CallGraphNode *)> VisitNode =
|
||||
[&](const CallGraphNode *Node) {
|
||||
if (ConnectedNodes.count(Node))
|
||||
return;
|
||||
ConnectedNodes.insert(Node);
|
||||
for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
|
||||
VisitNode(*It);
|
||||
};
|
||||
|
||||
VisitNode(RootNode);
|
||||
return ConnectedNodes;
|
||||
}
|
||||
|
||||
const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
|
||||
const auto *DC = D->getDeclContext();
|
||||
const auto *Result = D;
|
||||
while (DC) {
|
||||
if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
|
||||
Result = RD;
|
||||
else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
|
||||
Result = FD;
|
||||
DC = DC->getParent();
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void HelperDeclRGBuilder::run(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
// Construct the graph by adding a directed edge from caller to callee.
|
||||
//
|
||||
// "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
|
||||
// might be not the targetted Caller Decl, we always use the outmost enclosing
|
||||
// FunctionDecl/CXXRecordDecl of "dc". For example,
|
||||
//
|
||||
// int MoveClass::F() { int a = helper(); return a; }
|
||||
//
|
||||
// The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
|
||||
// to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
|
||||
if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
|
||||
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(DC);
|
||||
|
||||
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
|
||||
getOutmostClassOrFunDecl(FuncRef->getDecl()));
|
||||
} else if (const auto *UsedClass =
|
||||
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
|
||||
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
||||
assert(DC);
|
||||
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace move
|
||||
} // namespace clang
|
||||
@@ -1,99 +0,0 @@
|
||||
//===-- UsedHelperDeclFinder.h - AST-based call graph for helper decls ----===//
|
||||
//
|
||||
// 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_MOVE_USED_HELPER_DECL_FINDER_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Analysis/CallGraph.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace move {
|
||||
|
||||
// A reference graph for finding used/unused helper declarations in a single
|
||||
// translation unit (e.g. old.cc). We don't reuse CallGraph in clang/Analysis
|
||||
// because that CallGraph only supports function declarations.
|
||||
//
|
||||
// Helper declarations include following types:
|
||||
// * function/variable/class definitions in an anonymous namespace.
|
||||
// * static function/variable definitions in a global/named namespace.
|
||||
//
|
||||
// The reference graph is a directed graph. Each node in the graph represents a
|
||||
// helper declaration in old.cc or a non-moved/moved declaration (e.g. class,
|
||||
// function) in old.h, which means each node is associated with a Decl.
|
||||
//
|
||||
// To construct the graph, we use AST matcher to find interesting Decls (usually
|
||||
// a pair of Caller and Callee), and add an edge from the Caller node to the
|
||||
// Callee node.
|
||||
//
|
||||
// Specially, for a class, it might have multiple declarations such methods
|
||||
// and member variables. We only use a single node to present this class, and
|
||||
// this node is associated with the class declaration (CXXRecordDecl).
|
||||
//
|
||||
// The graph has 3 types of edges:
|
||||
// 1. moved_decl => helper_decl
|
||||
// 2. non_moved_decl => helper_decl
|
||||
// 3. helper_decl => helper_decl
|
||||
class HelperDeclRefGraph {
|
||||
public:
|
||||
HelperDeclRefGraph() = default;
|
||||
~HelperDeclRefGraph() = default;
|
||||
|
||||
// Add a directed edge from the caller node to the callee node.
|
||||
// A new node will be created if the node for Caller/Callee doesn't exist.
|
||||
//
|
||||
// Note that, all class member declarations are represented by a single node
|
||||
// in the graph. The corresponding Decl of this node is the class declaration.
|
||||
void addEdge(const Decl *Caller, const Decl *Callee);
|
||||
CallGraphNode *getNode(const Decl *D) const;
|
||||
|
||||
// Get all reachable nodes in the graph from the given declaration D's node,
|
||||
// including D.
|
||||
llvm::DenseSet<const CallGraphNode *> getReachableNodes(const Decl *D) const;
|
||||
|
||||
// Dump the call graph for debug purpose.
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
void print(raw_ostream &OS) const;
|
||||
// Lookup a node for the given declaration D. If not found, insert a new
|
||||
// node into the graph.
|
||||
CallGraphNode *getOrInsertNode(Decl *D);
|
||||
|
||||
typedef llvm::DenseMap<const Decl *, std::unique_ptr<CallGraphNode>>
|
||||
DeclMapTy;
|
||||
|
||||
// DeclMap owns all CallGraphNodes.
|
||||
DeclMapTy DeclMap;
|
||||
};
|
||||
|
||||
// A builder helps to construct a call graph of helper declarations.
|
||||
class HelperDeclRGBuilder : public ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
HelperDeclRGBuilder() : RG(new HelperDeclRefGraph) {}
|
||||
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
const HelperDeclRefGraph *getGraph() const { return RG.get(); }
|
||||
|
||||
// Find out the outmost enclosing class/function declaration of a given D.
|
||||
// For a CXXMethodDecl, get its CXXRecordDecl; For a VarDecl/FunctionDecl, get
|
||||
// its outmost enclosing FunctionDecl or CXXRecordDecl.
|
||||
// Return D if not found.
|
||||
static const Decl *getOutmostClassOrFunDecl(const Decl *D);
|
||||
|
||||
private:
|
||||
std::unique_ptr<HelperDeclRefGraph> RG;
|
||||
};
|
||||
|
||||
} // namespace move
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
|
||||
@@ -1,17 +0,0 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
add_clang_executable(clang-move
|
||||
ClangMoveMain.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(clang-move
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangMove
|
||||
clangRewrite
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
@@ -1,211 +0,0 @@
|
||||
//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ClangMove.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/ArgumentsAdjusters.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
std::error_code CreateNewFile(const llvm::Twine &path) {
|
||||
int fd = 0;
|
||||
if (std::error_code ec =
|
||||
llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
|
||||
return ec;
|
||||
|
||||
return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
|
||||
}
|
||||
|
||||
cl::OptionCategory ClangMoveCategory("clang-move options");
|
||||
|
||||
cl::list<std::string> Names("names", cl::CommaSeparated,
|
||||
cl::desc("The list of the names of classes being "
|
||||
"moved, e.g. \"Foo,a::Foo,b::Foo\"."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
OldHeader("old_header",
|
||||
cl::desc("The relative/absolute file path of old header."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
NewHeader("new_header",
|
||||
cl::desc("The relative/absolute file path of new header."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<bool>
|
||||
OldDependOnNew("old_depend_on_new",
|
||||
cl::desc("Whether old header will depend on new header. If "
|
||||
"true, clang-move will "
|
||||
"add #include of new header to old header."),
|
||||
cl::init(false), cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<bool>
|
||||
NewDependOnOld("new_depend_on_old",
|
||||
cl::desc("Whether new header will depend on old header. If "
|
||||
"true, clang-move will "
|
||||
"add #include of old header to new header."),
|
||||
cl::init(false), cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
Style("style",
|
||||
cl::desc("The style name used for reformatting. Default is \"llvm\""),
|
||||
cl::init("llvm"), cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<bool> Dump("dump_result",
|
||||
cl::desc("Dump results in JSON format to stdout."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
cl::opt<bool> DumpDecls(
|
||||
"dump_decls",
|
||||
cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
|
||||
"the option is specified, other command options will be ignored. "
|
||||
"An empty JSON will be returned if old header isn't specified."),
|
||||
cl::cat(ClangMoveCategory));
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
|
||||
|
||||
if (OldDependOnNew && NewDependOnOld) {
|
||||
llvm::errs() << "Provide either --old_depend_on_new or "
|
||||
"--new_depend_on_old. clang-move doesn't support these two "
|
||||
"options at same time (It will introduce include cycle).\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList());
|
||||
// Add "-fparse-all-comments" compile option to make clang parse all comments.
|
||||
Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
|
||||
"-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
|
||||
move::MoveDefinitionSpec Spec;
|
||||
Spec.Names = {Names.begin(), Names.end()};
|
||||
Spec.OldHeader = OldHeader;
|
||||
Spec.NewHeader = NewHeader;
|
||||
Spec.OldCC = OldCC;
|
||||
Spec.NewCC = NewCC;
|
||||
Spec.OldDependOnNew = OldDependOnNew;
|
||||
Spec.NewDependOnOld = NewDependOnOld;
|
||||
|
||||
llvm::SmallString<128> InitialDirectory;
|
||||
if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
|
||||
llvm::report_fatal_error("Cannot detect current path: " +
|
||||
Twine(EC.message()));
|
||||
|
||||
move::ClangMoveContext Context{Spec, Tool.getReplacements(),
|
||||
InitialDirectory.str(), Style, DumpDecls};
|
||||
move::DeclarationReporter Reporter;
|
||||
auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
|
||||
&Context, &Reporter);
|
||||
|
||||
int CodeStatus = Tool.run(Factory.get());
|
||||
if (CodeStatus)
|
||||
return CodeStatus;
|
||||
|
||||
if (DumpDecls) {
|
||||
llvm::outs() << "[\n";
|
||||
const auto &Declarations = Reporter.getDeclarationList();
|
||||
for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
|
||||
llvm::outs() << " {\n";
|
||||
llvm::outs() << " \"DeclarationName\": \"" << I->first << "\",\n";
|
||||
llvm::outs() << " \"DeclarationType\": \"" << I->second << "\"\n";
|
||||
llvm::outs() << " }";
|
||||
// Don't print trailing "," at the end of last element.
|
||||
if (I != std::prev(E))
|
||||
llvm::outs() << ",\n";
|
||||
}
|
||||
llvm::outs() << "\n]\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!NewCC.empty()) {
|
||||
std::error_code EC = CreateNewFile(NewCC);
|
||||
if (EC) {
|
||||
llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
|
||||
<< "\n";
|
||||
return EC.value();
|
||||
}
|
||||
}
|
||||
if (!NewHeader.empty()) {
|
||||
std::error_code EC = CreateNewFile(NewHeader);
|
||||
if (EC) {
|
||||
llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
|
||||
<< "\n";
|
||||
return EC.value();
|
||||
}
|
||||
}
|
||||
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
|
||||
&DiagnosticPrinter, false);
|
||||
auto &FileMgr = Tool.getFiles();
|
||||
SourceManager SM(Diagnostics, FileMgr);
|
||||
Rewriter Rewrite(SM, LangOptions());
|
||||
|
||||
if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
|
||||
llvm::errs() << "Failed applying all replacements.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Dump) {
|
||||
std::set<llvm::StringRef> Files;
|
||||
for (const auto &it : Tool.getReplacements())
|
||||
Files.insert(it.first);
|
||||
auto WriteToJson = [&](llvm::raw_ostream &OS) {
|
||||
OS << "[\n";
|
||||
for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
|
||||
OS << " {\n";
|
||||
OS << " \"FilePath\": \"" << *I << "\",\n";
|
||||
const auto *Entry = FileMgr.getFile(*I);
|
||||
auto ID = SM.translateFile(Entry);
|
||||
std::string Content;
|
||||
llvm::raw_string_ostream ContentStream(Content);
|
||||
Rewrite.getEditBuffer(ID).write(ContentStream);
|
||||
OS << " \"SourceText\": \""
|
||||
<< llvm::yaml::escape(ContentStream.str()) << "\"\n";
|
||||
OS << " }";
|
||||
if (I != std::prev(E))
|
||||
OS << ",\n";
|
||||
}
|
||||
OS << "\n]\n";
|
||||
};
|
||||
WriteToJson(llvm::outs());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Rewrite.overwriteChangedFiles();
|
||||
}
|
||||
@@ -65,7 +65,7 @@ struct CollectBoundNodes : MatchFinder::MatchCallback {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
|
||||
unsigned MatchCount = 0;
|
||||
@@ -86,21 +86,25 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
|
||||
}
|
||||
Finder.matchAST(AST->getASTContext());
|
||||
|
||||
for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
|
||||
for (std::vector<BoundNodes>::iterator MI = Matches.begin(),
|
||||
ME = Matches.end();
|
||||
MI != ME; ++MI) {
|
||||
OS << "\nMatch #" << ++MatchCount << ":\n\n";
|
||||
|
||||
for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
|
||||
++BI) {
|
||||
for (BoundNodes::IDToNodeMap::const_iterator BI = MI->getMap().begin(),
|
||||
BE = MI->getMap().end();
|
||||
BI != BE; ++BI) {
|
||||
switch (QS.OutKind) {
|
||||
case OK_Diag: {
|
||||
clang::SourceRange R = BI->second.getSourceRange();
|
||||
if (R.isValid()) {
|
||||
TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
|
||||
&AST->getDiagnostics().getDiagnosticOptions());
|
||||
TD.emitDiagnostic(R.getBegin(), DiagnosticsEngine::Note,
|
||||
"\"" + BI->first + "\" binds here",
|
||||
CharSourceRange::getTokenRange(R), None,
|
||||
&AST->getSourceManager());
|
||||
TD.emitDiagnostic(
|
||||
R.getBegin(), DiagnosticsEngine::Note,
|
||||
"\"" + BI->first + "\" binds here",
|
||||
CharSourceRange::getTokenRange(R),
|
||||
None, &AST->getSourceManager());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
namespace clang {
|
||||
namespace query {
|
||||
|
||||
enum OutputKind { OK_Diag, OK_Print, OK_Dump };
|
||||
enum OutputKind {
|
||||
OK_Diag,
|
||||
OK_Print,
|
||||
OK_Dump
|
||||
};
|
||||
|
||||
enum QueryKind {
|
||||
QK_Invalid,
|
||||
|
||||
@@ -76,7 +76,9 @@ template <typename T> struct QueryParser::LexOrCompleteWord {
|
||||
return *this;
|
||||
}
|
||||
|
||||
T Default(const T &Value) const { return Switch.Default(Value); }
|
||||
T Default(const T& Value) const {
|
||||
return Switch.Default(Value);
|
||||
}
|
||||
};
|
||||
|
||||
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
|
||||
@@ -99,9 +101,9 @@ QueryParser::lexOrCompleteWord(StringRef &Word) {
|
||||
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
|
||||
StringRef ValStr;
|
||||
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
|
||||
.Case("false", 0)
|
||||
.Case("true", 1)
|
||||
.Default(~0u);
|
||||
.Case("false", 0)
|
||||
.Case("true", 1)
|
||||
.Default(~0u);
|
||||
if (Value == ~0u) {
|
||||
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
|
||||
}
|
||||
@@ -143,7 +145,11 @@ enum ParsedQueryKind {
|
||||
PQK_Quit
|
||||
};
|
||||
|
||||
enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
|
||||
enum ParsedQueryVariable {
|
||||
PQV_Invalid,
|
||||
PQV_Output,
|
||||
PQV_BindRoot
|
||||
};
|
||||
|
||||
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
|
||||
std::string ErrStr;
|
||||
@@ -152,13 +158,15 @@ QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
|
||||
return new InvalidQuery(OS.str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
QueryRef QueryParser::completeMatcherExpression() {
|
||||
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
|
||||
StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
|
||||
&QS.NamedValues);
|
||||
for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
|
||||
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
|
||||
E = Comps.end();
|
||||
I != E; ++I) {
|
||||
Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
|
||||
}
|
||||
return QueryRef();
|
||||
|
||||
@@ -37,7 +37,8 @@ public:
|
||||
|
||||
private:
|
||||
QueryParser(StringRef Line, const QuerySession &QS)
|
||||
: Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
|
||||
: Begin(Line.begin()), End(Line.end()),
|
||||
CompletionPos(nullptr), QS(QS) {}
|
||||
|
||||
StringRef lexWord();
|
||||
|
||||
|
||||
@@ -77,13 +77,17 @@ int main(int argc, const char **argv) {
|
||||
QuerySession QS(ASTs);
|
||||
|
||||
if (!Commands.empty()) {
|
||||
for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
|
||||
QueryRef Q = QueryParser::parse(*I, QS);
|
||||
for (cl::list<std::string>::iterator I = Commands.begin(),
|
||||
E = Commands.end();
|
||||
I != E; ++I) {
|
||||
QueryRef Q = QueryParser::parse(I->c_str(), QS);
|
||||
if (!Q->run(llvm::outs(), QS))
|
||||
return 1;
|
||||
}
|
||||
} else if (!CommandFiles.empty()) {
|
||||
for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
|
||||
for (cl::list<std::string>::iterator I = CommandFiles.begin(),
|
||||
E = CommandFiles.end();
|
||||
I != E; ++I) {
|
||||
std::ifstream Input(I->c_str());
|
||||
if (!Input.is_open()) {
|
||||
llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
|
||||
@@ -93,7 +97,7 @@ int main(int argc, const char **argv) {
|
||||
std::string Line;
|
||||
std::getline(Input, Line);
|
||||
|
||||
QueryRef Q = QueryParser::parse(Line, QS);
|
||||
QueryRef Q = QueryParser::parse(Line.c_str(), QS);
|
||||
if (!Q->run(llvm::outs(), QS))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ add_clang_library(clangRename
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangIndex
|
||||
clangLex
|
||||
|
||||
@@ -34,59 +34,54 @@ namespace rename {
|
||||
|
||||
class RenamingASTConsumer : public ASTConsumer {
|
||||
public:
|
||||
RenamingASTConsumer(
|
||||
const std::vector<std::string> &NewNames,
|
||||
const std::vector<std::string> &PrevNames,
|
||||
const std::vector<std::vector<std::string>> &USRList,
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces,
|
||||
bool PrintLocations)
|
||||
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
|
||||
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
|
||||
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
for (unsigned I = 0; I < NewNames.size(); ++I)
|
||||
HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
|
||||
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 HandleOneRename(ASTContext &Context, const std::string &NewName,
|
||||
const std::string &PrevName,
|
||||
const std::vector<std::string> &USRs) {
|
||||
const SourceManager &SourceMgr = Context.getSourceManager();
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
const auto &SourceMgr = Context.getSourceManager();
|
||||
std::vector<SourceLocation> RenamingCandidates;
|
||||
std::vector<SourceLocation> NewCandidates;
|
||||
|
||||
NewCandidates =
|
||||
getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl());
|
||||
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
|
||||
NewCandidates.end());
|
||||
for (const auto &USR : USRs) {
|
||||
NewCandidates = getLocationsOfUSR(USR, PrevName,
|
||||
Context.getTranslationUnitDecl());
|
||||
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
|
||||
NewCandidates.end());
|
||||
NewCandidates.clear();
|
||||
}
|
||||
|
||||
unsigned PrevNameLen = PrevName.length();
|
||||
for (const auto &Loc : RenamingCandidates) {
|
||||
if (PrintLocations) {
|
||||
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));
|
||||
}
|
||||
// FIXME: better error handling.
|
||||
tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
|
||||
llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
|
||||
if (Err)
|
||||
llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
|
||||
<< llvm::toString(std::move(Err)) << "\n";
|
||||
}
|
||||
else
|
||||
for (const auto &Loc : RenamingCandidates)
|
||||
Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
|
||||
NewName));
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::string> &NewNames, &PrevNames;
|
||||
const std::vector<std::vector<std::string>> &USRList;
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces;
|
||||
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>(NewNames, PrevNames, USRList,
|
||||
FileToReplaces, PrintLocations);
|
||||
return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
|
||||
Replaces, PrintLocations);
|
||||
}
|
||||
|
||||
} // namespace rename
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
#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"
|
||||
|
||||
@@ -25,24 +25,23 @@ namespace rename {
|
||||
|
||||
class RenamingAction {
|
||||
public:
|
||||
RenamingAction(const std::vector<std::string> &NewNames,
|
||||
const std::vector<std::string> &PrevNames,
|
||||
const std::vector<std::vector<std::string>> &USRList,
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces,
|
||||
bool PrintLocations = false)
|
||||
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
|
||||
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
|
||||
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::vector<std::string> &NewNames, &PrevNames;
|
||||
const std::vector<std::vector<std::string>> &USRList;
|
||||
std::map<std::string, tooling::Replacements> &FileToReplaces;
|
||||
const std::string &NewName, &PrevName;
|
||||
const std::vector<std::string> &USRs;
|
||||
tooling::Replacements &Replaces;
|
||||
bool PrintLocations;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
|
||||
|
||||
@@ -34,15 +34,19 @@ class NamedDeclFindingASTVisitor
|
||||
public:
|
||||
// \brief Finds the NamedDecl at a point in the source.
|
||||
// \param Point the location in the source to search for the NamedDecl.
|
||||
explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
|
||||
const ASTContext &Context)
|
||||
: Result(nullptr), Point(Point), Context(Context) {}
|
||||
explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
|
||||
const SourceLocation Point)
|
||||
: Result(nullptr), SourceMgr(SourceMgr),
|
||||
Point(Point) {
|
||||
}
|
||||
|
||||
// \brief Finds the NamedDecl for a name in the source.
|
||||
// \param Name the fully qualified name.
|
||||
explicit NamedDeclFindingASTVisitor(const std::string &Name,
|
||||
const ASTContext &Context)
|
||||
: Result(nullptr), Name(Name), Context(Context) {}
|
||||
explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
|
||||
const std::string &Name)
|
||||
: Result(nullptr), SourceMgr(SourceMgr),
|
||||
Name(Name) {
|
||||
}
|
||||
|
||||
// Declaration visitors:
|
||||
|
||||
@@ -51,92 +55,63 @@ public:
|
||||
// checking if the point lies within the length of the name of the declaration
|
||||
// and the start location is sufficient.
|
||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
||||
return dyn_cast<CXXConversionDecl>(Decl)
|
||||
? true
|
||||
: setResult(Decl, Decl->getLocation(),
|
||||
Decl->getNameAsString().length());
|
||||
return setResult(Decl, Decl->getLocation(),
|
||||
Decl->getNameAsString().length());
|
||||
}
|
||||
|
||||
// Expression visitors:
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl();
|
||||
// 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 NamedDecl *Decl = Expr->getFoundDecl().getDecl();
|
||||
const auto *Decl = Expr->getFoundDecl().getDecl();
|
||||
return setResult(Decl, Expr->getMemberLoc(),
|
||||
Decl->getNameAsString().length());
|
||||
}
|
||||
|
||||
// Other visitors:
|
||||
// Other:
|
||||
|
||||
bool VisitTypeLoc(const TypeLoc Loc) {
|
||||
const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
|
||||
const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
|
||||
TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
|
||||
if (const auto *TemplateTypeParm =
|
||||
dyn_cast<TemplateTypeParmType>(Loc.getType()))
|
||||
return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
|
||||
if (const auto *TemplateSpecType =
|
||||
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
|
||||
return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
|
||||
TypeBeginLoc, TypeEndLoc);
|
||||
}
|
||||
return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
|
||||
TypeEndLoc);
|
||||
const NamedDecl *getNamedDecl() {
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
|
||||
for (const auto *Initializer : ConstructorDecl->inits()) {
|
||||
// Ignore implicit initializers.
|
||||
if (!Initializer->isWritten())
|
||||
continue;
|
||||
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
|
||||
const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
|
||||
InitEndLoc = Lexer::getLocForEndOfToken(
|
||||
InitBeginLoc, 0, Context.getSourceManager(),
|
||||
Context.getLangOpts());
|
||||
if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Other:
|
||||
|
||||
const NamedDecl *getNamedDecl() { return Result; }
|
||||
|
||||
// \brief Determines if a namespace qualifier contains the point.
|
||||
// \returns false on success and sets Result.
|
||||
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
||||
while (NameLoc) {
|
||||
const NamespaceDecl *Decl =
|
||||
NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
||||
setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
|
||||
NameLoc = NameLoc.getPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// \brief Sets Result to Decl if the Point is within Start and End.
|
||||
// \returns false on success.
|
||||
bool setResult(const NamedDecl *Decl, SourceLocation Start,
|
||||
SourceLocation End) {
|
||||
if (!Decl)
|
||||
return true;
|
||||
if (Name.empty()) {
|
||||
// Offset is used to find the declaration.
|
||||
if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
|
||||
!End.isFileID() || !isPointWithin(Start, End))
|
||||
!End.isFileID() || !isPointWithin(Start, End)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Fully qualified name is used to find the declaration.
|
||||
if (Name != Decl->getQualifiedNameAsString())
|
||||
if (Name != Decl->getQualifiedNameAsString()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Result = Decl;
|
||||
return false;
|
||||
@@ -144,7 +119,8 @@ private:
|
||||
|
||||
// \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) {
|
||||
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));
|
||||
@@ -154,48 +130,55 @@ private:
|
||||
bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
|
||||
// FIXME: Add tests for Point == End.
|
||||
return Point == Start || Point == End ||
|
||||
(Context.getSourceManager().isBeforeInTranslationUnit(Start,
|
||||
Point) &&
|
||||
Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
|
||||
(SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
|
||||
SourceMgr.isBeforeInTranslationUnit(Point, End));
|
||||
}
|
||||
|
||||
const NamedDecl *Result;
|
||||
const SourceManager &SourceMgr;
|
||||
const SourceLocation Point; // The location to find the NamedDecl.
|
||||
const std::string Name;
|
||||
const ASTContext &Context;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
|
||||
const SourceLocation Point) {
|
||||
const SourceManager &SM = Context.getSourceManager();
|
||||
NamedDeclFindingASTVisitor Visitor(Point, Context);
|
||||
const auto &SourceMgr = Context.getSourceManager();
|
||||
const auto SearchFile = SourceMgr.getFilename(Point);
|
||||
|
||||
// Try to be clever about pruning down the number of top-level declarations we
|
||||
// see. If both start and end is either before or after the point we're
|
||||
// looking for the point cannot be inside of this decl. Don't even look at it.
|
||||
for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
|
||||
SourceLocation StartLoc = CurrDecl->getLocStart();
|
||||
SourceLocation EndLoc = CurrDecl->getLocEnd();
|
||||
if (StartLoc.isValid() && EndLoc.isValid() &&
|
||||
SM.isBeforeInTranslationUnit(StartLoc, Point) !=
|
||||
SM.isBeforeInTranslationUnit(EndLoc, Point))
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
|
||||
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
|
||||
Visitor.handleNestedNameSpecifierLoc(Location);
|
||||
|
||||
return Visitor.getNamedDecl();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
|
||||
const std::string &Name) {
|
||||
NamedDeclFindingASTVisitor Visitor(Name, Context);
|
||||
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
||||
const auto &SourceMgr = Context.getSourceManager();
|
||||
NamedDeclFindingASTVisitor Visitor(SourceMgr, Name);
|
||||
auto Decls = Context.getTranslationUnitDecl()->decls();
|
||||
|
||||
return Visitor.getNamedDecl();
|
||||
for (auto &CurrDecl : Decls) {
|
||||
Visitor.TraverseDecl(CurrDecl);
|
||||
if (const NamedDecl *Result = Visitor.getNamedDecl()) {
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string getUSRForDecl(const Decl *Decl) {
|
||||
|
||||
@@ -12,21 +12,12 @@
|
||||
/// code.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class Decl;
|
||||
class SourceLocation;
|
||||
@@ -48,37 +39,7 @@ const NamedDecl *getNamedDeclFor(const ASTContext &Context,
|
||||
// Converts a Decl into a USR.
|
||||
std::string getUSRForDecl(const Decl *Decl);
|
||||
|
||||
// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
|
||||
class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
explicit NestedNameSpecifierLocFinder(ASTContext &Context)
|
||||
: Context(Context) {}
|
||||
|
||||
std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
|
||||
addMatchers();
|
||||
Finder.matchAST(Context);
|
||||
return Locations;
|
||||
}
|
||||
|
||||
private:
|
||||
void addMatchers() {
|
||||
const auto NestedNameSpecifierLocMatcher =
|
||||
nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
|
||||
Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
|
||||
}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override {
|
||||
const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
|
||||
"nestedNameSpecifierLoc");
|
||||
Locations.push_back(*NNS);
|
||||
}
|
||||
|
||||
ASTContext &Context;
|
||||
std::vector<NestedNameSpecifierLoc> Locations;
|
||||
MatchFinder Finder;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Provides an action to find USR for the symbol at <offset>, as well as
|
||||
/// all additional USRs.
|
||||
/// \brief Provides an action to rename every symbol at a point.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -18,8 +17,6 @@
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
@@ -28,9 +25,6 @@
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,187 +33,89 @@ using namespace llvm;
|
||||
namespace clang {
|
||||
namespace rename {
|
||||
|
||||
namespace {
|
||||
// \brief NamedDeclFindingConsumer should delegate finding USRs of given Decl to
|
||||
// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
|
||||
// Decl refers to class and adds USRs of all overridden methods if Decl refers
|
||||
// to virtual method.
|
||||
class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
|
||||
public:
|
||||
AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
|
||||
: FoundDecl(FoundDecl), Context(Context) {}
|
||||
// Get the USRs for the constructors of the class.
|
||||
static std::vector<std::string> getAllConstructorUSRs(
|
||||
const CXXRecordDecl *Decl) {
|
||||
std::vector<std::string> USRs;
|
||||
|
||||
std::vector<std::string> Find() {
|
||||
// Fill OverriddenMethods and PartialSpecs storages.
|
||||
TraverseDecl(Context.getTranslationUnitDecl());
|
||||
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
|
||||
addUSRsOfOverridenFunctions(MethodDecl);
|
||||
for (const auto &OverriddenMethod : OverriddenMethods) {
|
||||
if (checkIfOverriddenFunctionAscends(OverriddenMethod))
|
||||
USRSet.insert(getUSRForDecl(OverriddenMethod));
|
||||
}
|
||||
} else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
|
||||
handleCXXRecordDecl(RecordDecl);
|
||||
} else if (const auto *TemplateDecl =
|
||||
dyn_cast<ClassTemplateDecl>(FoundDecl)) {
|
||||
handleClassTemplateDecl(TemplateDecl);
|
||||
// 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 = nullptr;
|
||||
if (OldName.empty()) {
|
||||
FoundDecl = getNamedDeclAt(Context, Point);
|
||||
} else {
|
||||
USRSet.insert(getUSRForDecl(FoundDecl));
|
||||
FoundDecl = getNamedDeclFor(Context, OldName);
|
||||
}
|
||||
return std::vector<std::string>(USRSet.begin(), USRSet.end());
|
||||
}
|
||||
|
||||
bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
|
||||
if (MethodDecl->isVirtual())
|
||||
OverriddenMethods.push_back(MethodDecl);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitClassTemplatePartialSpecializationDecl(
|
||||
const ClassTemplatePartialSpecializationDecl *PartialSpec) {
|
||||
PartialSpecs.push_back(PartialSpec);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
|
||||
RecordDecl = RecordDecl->getDefinition();
|
||||
if (const auto *ClassTemplateSpecDecl =
|
||||
dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
|
||||
handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
|
||||
addUSRsOfCtorDtors(RecordDecl);
|
||||
}
|
||||
|
||||
void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
|
||||
for (const auto *Specialization : TemplateDecl->specializations())
|
||||
addUSRsOfCtorDtors(Specialization);
|
||||
|
||||
for (const auto *PartialSpec : PartialSpecs) {
|
||||
if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
|
||||
addUSRsOfCtorDtors(PartialSpec);
|
||||
}
|
||||
addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
|
||||
}
|
||||
|
||||
void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
|
||||
RecordDecl = RecordDecl->getDefinition();
|
||||
|
||||
for (const auto *CtorDecl : RecordDecl->ctors())
|
||||
USRSet.insert(getUSRForDecl(CtorDecl));
|
||||
|
||||
USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
|
||||
USRSet.insert(getUSRForDecl(RecordDecl));
|
||||
}
|
||||
|
||||
void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
|
||||
USRSet.insert(getUSRForDecl(MethodDecl));
|
||||
// Recursively visit each OverridenMethod.
|
||||
for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
|
||||
addUSRsOfOverridenFunctions(OverriddenMethod);
|
||||
}
|
||||
|
||||
bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
|
||||
for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
|
||||
if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
|
||||
return true;
|
||||
return checkIfOverriddenFunctionAscends(OverriddenMethod);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const Decl *FoundDecl;
|
||||
ASTContext &Context;
|
||||
std::set<std::string> USRSet;
|
||||
std::vector<const CXXMethodDecl *> OverriddenMethods;
|
||||
std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class NamedDeclFindingConsumer : public ASTConsumer {
|
||||
public:
|
||||
NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
|
||||
ArrayRef<std::string> QualifiedNames,
|
||||
std::vector<std::string> &SpellingNames,
|
||||
std::vector<std::vector<std::string>> &USRList,
|
||||
bool &ErrorOccurred)
|
||||
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
|
||||
SpellingNames(SpellingNames), USRList(USRList),
|
||||
ErrorOccurred(ErrorOccurred) {}
|
||||
|
||||
private:
|
||||
bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
|
||||
unsigned SymbolOffset, const std::string &QualifiedName) {
|
||||
DiagnosticsEngine &Engine = Context.getDiagnostics();
|
||||
const FileID MainFileID = SourceMgr.getMainFileID();
|
||||
|
||||
if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
|
||||
ErrorOccurred = true;
|
||||
unsigned InvalidOffset = Engine.getCustomDiagID(
|
||||
DiagnosticsEngine::Error,
|
||||
"SourceLocation in file %0 at offset %1 is invalid");
|
||||
Engine.Report(SourceLocation(), InvalidOffset)
|
||||
<< SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
|
||||
return false;
|
||||
}
|
||||
|
||||
const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
|
||||
.getLocWithOffset(SymbolOffset);
|
||||
const NamedDecl *FoundDecl = QualifiedName.empty()
|
||||
? getNamedDeclAt(Context, Point)
|
||||
: getNamedDeclFor(Context, QualifiedName);
|
||||
|
||||
if (FoundDecl == nullptr) {
|
||||
if (QualifiedName.empty()) {
|
||||
FullSourceLoc FullLoc(Point, SourceMgr);
|
||||
unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
|
||||
DiagnosticsEngine::Error,
|
||||
"clang-rename could not find symbol (offset %0)");
|
||||
Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
|
||||
ErrorOccurred = true;
|
||||
return false;
|
||||
}
|
||||
unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
|
||||
DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
|
||||
Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
|
||||
ErrorOccurred = true;
|
||||
return false;
|
||||
FullSourceLoc FullLoc(Point, SourceMgr);
|
||||
errs() << "clang-rename: could not find symbol at "
|
||||
<< SourceMgr.getFilename(Point) << ":"
|
||||
<< FullLoc.getSpellingLineNumber() << ":"
|
||||
<< FullLoc.getSpellingColumnNumber() << " (offset " << SymbolOffset
|
||||
<< ").\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// If FoundDecl is a constructor or destructor, we want to instead take
|
||||
// the Decl of the corresponding class.
|
||||
// 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();
|
||||
|
||||
SpellingNames.push_back(FoundDecl->getNameAsString());
|
||||
AdditionalUSRFinder Finder(FoundDecl, Context);
|
||||
USRList.push_back(Finder.Find());
|
||||
return true;
|
||||
// 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();
|
||||
}
|
||||
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
const SourceManager &SourceMgr = Context.getSourceManager();
|
||||
for (unsigned Offset : SymbolOffsets) {
|
||||
if (!FindSymbol(Context, SourceMgr, Offset, ""))
|
||||
return;
|
||||
}
|
||||
for (const std::string &QualifiedName : QualifiedNames) {
|
||||
if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<unsigned> SymbolOffsets;
|
||||
ArrayRef<std::string> QualifiedNames;
|
||||
std::vector<std::string> &SpellingNames;
|
||||
std::vector<std::vector<std::string>> &USRList;
|
||||
bool &ErrorOccurred;
|
||||
unsigned SymbolOffset;
|
||||
std::string OldName;
|
||||
std::string *SpellingName;
|
||||
std::vector<std::string> *USRs;
|
||||
};
|
||||
|
||||
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
|
||||
return llvm::make_unique<NamedDeclFindingConsumer>(
|
||||
SymbolOffsets, QualifiedNames, SpellingNames, USRList, ErrorOccurred);
|
||||
std::unique_ptr<ASTConsumer>
|
||||
USRFindingAction::newASTConsumer() {
|
||||
std::unique_ptr<NamedDeclFindingConsumer> Consumer(
|
||||
new NamedDeclFindingConsumer);
|
||||
SpellingName = "";
|
||||
Consumer->SymbolOffset = SymbolOffset;
|
||||
Consumer->OldName = OldName;
|
||||
Consumer->USRs = &USRs;
|
||||
Consumer->SpellingName = &SpellingName;
|
||||
return std::move(Consumer);
|
||||
}
|
||||
|
||||
} // namespace rename
|
||||
|
||||
@@ -12,14 +12,10 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
|
||||
namespace clang {
|
||||
class ASTConsumer;
|
||||
@@ -29,25 +25,23 @@ class NamedDecl;
|
||||
namespace rename {
|
||||
|
||||
struct USRFindingAction {
|
||||
USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
|
||||
ArrayRef<std::string> QualifiedNames)
|
||||
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
|
||||
ErrorOccurred(false) {}
|
||||
USRFindingAction(unsigned Offset, const std::string &Name)
|
||||
: SymbolOffset(Offset), OldName(Name) {}
|
||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
||||
|
||||
ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
|
||||
ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
|
||||
bool errorOccurred() { return ErrorOccurred; }
|
||||
// \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:
|
||||
std::vector<unsigned> SymbolOffsets;
|
||||
std::vector<std::string> QualifiedNames;
|
||||
std::vector<std::string> SpellingNames;
|
||||
std::vector<std::vector<std::string>> USRList;
|
||||
bool ErrorOccurred;
|
||||
unsigned SymbolOffset;
|
||||
std::string OldName;
|
||||
std::string SpellingName;
|
||||
std::vector<std::string> USRs;
|
||||
};
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
|
||||
|
||||
@@ -18,16 +18,10 @@
|
||||
#include "USRFinder.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <cstddef>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@@ -35,78 +29,140 @@ namespace clang {
|
||||
namespace rename {
|
||||
|
||||
namespace {
|
||||
|
||||
// \brief This visitor recursively searches for all instances of a USR in a
|
||||
// translation unit and stores them for later usage.
|
||||
class USRLocFindingASTVisitor
|
||||
: public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
|
||||
public:
|
||||
explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
|
||||
StringRef PrevName,
|
||||
const ASTContext &Context)
|
||||
: USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
|
||||
}
|
||||
explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName)
|
||||
: USR(USR), PrevName(PrevName) {}
|
||||
|
||||
// Declaration visitors:
|
||||
|
||||
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
|
||||
for (const auto *Initializer : ConstructorDecl->inits()) {
|
||||
// Ignore implicit initializers.
|
||||
if (!Initializer->isWritten())
|
||||
continue;
|
||||
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
|
||||
if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
|
||||
LocationsFound.push_back(Initializer->getSourceLocation());
|
||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
||||
if (getUSRForDecl(Decl) == USR) {
|
||||
LocationsFound.push_back(Decl->getLocation());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitVarDecl(clang::VarDecl *Decl) {
|
||||
clang::QualType Type = Decl->getType();
|
||||
const clang::RecordDecl *RecordDecl = Type->getPointeeCXXRecordDecl();
|
||||
if (RecordDecl) {
|
||||
if (getUSRForDecl(RecordDecl) == USR) {
|
||||
// The declaration refers to a type that is to be renamed.
|
||||
LocationsFound.push_back(Decl->getTypeSpecStartLoc());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitNamedDecl(const NamedDecl *Decl) {
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
|
||||
checkAndAddLocation(Decl->getLocation());
|
||||
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
|
||||
const ASTContext &Context = ConstructorDecl->getASTContext();
|
||||
for (auto &Initializer : ConstructorDecl->inits()) {
|
||||
if (Initializer->getSourceOrder() == -1) {
|
||||
// Ignore implicit initializers.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const clang::FieldDecl *FieldDecl = Initializer->getAnyMember()) {
|
||||
if (getUSRForDecl(FieldDecl) == USR) {
|
||||
// The initializer refers to a field that is to be renamed.
|
||||
SourceLocation Location = Initializer->getSourceLocation();
|
||||
StringRef TokenName = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(Location),
|
||||
Context.getSourceManager(), Context.getLangOpts());
|
||||
if (TokenName == PrevName) {
|
||||
// The token of the source location we find actually has the old
|
||||
// name.
|
||||
LocationsFound.push_back(Initializer->getSourceLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getUSRForDecl(ConstructorDecl) == USR) {
|
||||
// This takes care of the class name part of a non-inline ctor definition.
|
||||
LocationsFound.push_back(ConstructorDecl->getLocStart());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitCXXDestructorDecl(clang::CXXDestructorDecl *DestructorDecl) {
|
||||
if (getUSRForDecl(DestructorDecl->getParent()) == USR) {
|
||||
// Handles "~Foo" from "Foo::~Foo".
|
||||
SourceLocation Location = DestructorDecl->getLocation();
|
||||
const ASTContext &Context = DestructorDecl->getASTContext();
|
||||
StringRef LLVM_ATTRIBUTE_UNUSED TokenName = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(Location), Context.getSourceManager(),
|
||||
Context.getLangOpts());
|
||||
// 1 is the length of the "~" string that is not to be touched by the
|
||||
// rename.
|
||||
assert(TokenName.startswith("~"));
|
||||
LocationsFound.push_back(Location.getLocWithOffset(1));
|
||||
|
||||
if (DestructorDecl->isThisDeclarationADefinition()) {
|
||||
// Handles "Foo" from "Foo::~Foo".
|
||||
LocationsFound.push_back(DestructorDecl->getLocStart());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expression visitors:
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl();
|
||||
const auto *Decl = Expr->getFoundDecl();
|
||||
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
|
||||
checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
|
||||
if (getUSRForDecl(Decl) == USR) {
|
||||
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
|
||||
SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
|
||||
checkAndAddLocation(Location);
|
||||
LocationsFound.push_back(Location);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitMemberExpr(const MemberExpr *Expr) {
|
||||
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
|
||||
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
|
||||
const auto *Decl = Expr->getFoundDecl().getDecl();
|
||||
if (getUSRForDecl(Decl) == USR) {
|
||||
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
|
||||
SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
|
||||
checkAndAddLocation(Location);
|
||||
LocationsFound.push_back(Location);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Other visitors:
|
||||
bool VisitCXXConstructExpr(const CXXConstructExpr *Expr) {
|
||||
CXXConstructorDecl *Decl = Expr->getConstructor();
|
||||
|
||||
bool VisitTypeLoc(const TypeLoc Loc) {
|
||||
if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
|
||||
USRSet.end())
|
||||
checkAndAddLocation(Loc.getBeginLoc());
|
||||
if (const auto *TemplateTypeParm =
|
||||
dyn_cast<TemplateTypeParmType>(Loc.getType())) {
|
||||
if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
|
||||
USRSet.end())
|
||||
checkAndAddLocation(Loc.getBeginLoc());
|
||||
if (getUSRForDecl(Decl) == USR) {
|
||||
// This takes care of 'new <name>' expressions.
|
||||
LocationsFound.push_back(Expr->getLocation());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitCXXStaticCastExpr(clang::CXXStaticCastExpr *Expr) {
|
||||
return handleCXXNamedCastExpr(Expr);
|
||||
}
|
||||
|
||||
bool VisitCXXDynamicCastExpr(clang::CXXDynamicCastExpr *Expr) {
|
||||
return handleCXXNamedCastExpr(Expr);
|
||||
}
|
||||
|
||||
bool VisitCXXReinterpretCastExpr(clang::CXXReinterpretCastExpr *Expr) {
|
||||
return handleCXXNamedCastExpr(Expr);
|
||||
}
|
||||
|
||||
bool VisitCXXConstCastExpr(clang::CXXConstCastExpr *Expr) {
|
||||
return handleCXXNamedCastExpr(Expr);
|
||||
}
|
||||
|
||||
// Non-visitors:
|
||||
|
||||
// \brief Returns a list of unique locations. Duplicate or overlapping
|
||||
@@ -115,51 +171,48 @@ public:
|
||||
return LocationsFound;
|
||||
}
|
||||
|
||||
private:
|
||||
// Namespace traversal:
|
||||
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
||||
void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
|
||||
while (NameLoc) {
|
||||
const NamespaceDecl *Decl =
|
||||
NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
||||
if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
|
||||
checkAndAddLocation(NameLoc.getLocalBeginLoc());
|
||||
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
|
||||
if (Decl && getUSRForDecl(Decl) == USR)
|
||||
LocationsFound.push_back(NameLoc.getLocalBeginLoc());
|
||||
NameLoc = NameLoc.getPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void checkAndAddLocation(SourceLocation Loc) {
|
||||
const SourceLocation BeginLoc = Loc;
|
||||
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
|
||||
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
|
||||
StringRef TokenName =
|
||||
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
|
||||
Context.getSourceManager(), Context.getLangOpts());
|
||||
size_t Offset = TokenName.find(PrevName);
|
||||
bool handleCXXNamedCastExpr(clang::CXXNamedCastExpr *Expr) {
|
||||
clang::QualType Type = Expr->getType();
|
||||
// See if this a cast of a pointer.
|
||||
const RecordDecl *Decl = Type->getPointeeCXXRecordDecl();
|
||||
if (!Decl) {
|
||||
// See if this is a cast of a reference.
|
||||
Decl = Type->getAsCXXRecordDecl();
|
||||
}
|
||||
|
||||
// The token of the source location we find actually has the old
|
||||
// name.
|
||||
if (Offset != StringRef::npos)
|
||||
LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
|
||||
if (Decl && getUSRForDecl(Decl) == USR) {
|
||||
SourceLocation Location =
|
||||
Expr->getTypeInfoAsWritten()->getTypeLoc().getBeginLoc();
|
||||
LocationsFound.push_back(Location);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::set<std::string> USRSet;
|
||||
// All the locations of the USR were found.
|
||||
const std::string USR;
|
||||
// Old name that is renamed.
|
||||
const std::string PrevName;
|
||||
std::vector<clang::SourceLocation> LocationsFound;
|
||||
const ASTContext &Context;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<SourceLocation>
|
||||
getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
|
||||
Decl *Decl) {
|
||||
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
|
||||
std::vector<SourceLocation> getLocationsOfUSR(StringRef USR, StringRef PrevName,
|
||||
Decl *Decl) {
|
||||
USRLocFindingASTVisitor Visitor(USR, PrevName);
|
||||
|
||||
Visitor.TraverseDecl(Decl);
|
||||
NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
|
||||
|
||||
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
|
||||
Visitor.handleNestedNameSpecifierLoc(Location);
|
||||
|
||||
return Visitor.getLocationsFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ namespace rename {
|
||||
|
||||
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
|
||||
std::vector<SourceLocation>
|
||||
getLocationsOfUSRs(const std::vector<std::string> &USRs,
|
||||
llvm::StringRef PrevName, Decl *Decl);
|
||||
getLocationsOfUSR(llvm::StringRef USR, llvm::StringRef PrevName, Decl *Decl);
|
||||
|
||||
} // namespace rename
|
||||
} // namespace clang
|
||||
|
||||
@@ -10,10 +10,3 @@ target_link_libraries(clang-rename
|
||||
)
|
||||
|
||||
install(TARGETS clang-rename RUNTIME DESTINATION bin)
|
||||
|
||||
install(PROGRAMS clang-rename.py
|
||||
DESTINATION share/clang
|
||||
COMPONENT clang-rename)
|
||||
install(PROGRAMS clang-rename.el
|
||||
DESTINATION share/clang
|
||||
COMPONENT clang-rename)
|
||||
|
||||
@@ -13,171 +13,119 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../RenamingAction.h"
|
||||
#include "../USRFindingAction.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "../RenamingAction.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/TokenKinds.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/Lexer.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Parse/ParseAST.h"
|
||||
#include "clang/Parse/Parser.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/ReplacementsYaml.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cstdlib>
|
||||
#include "llvm/Support/Host.h"
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
/// \brief An oldname -> newname rename.
|
||||
struct RenameAllInfo {
|
||||
unsigned Offset = 0;
|
||||
std::string QualifiedName;
|
||||
std::string NewName;
|
||||
};
|
||||
cl::OptionCategory ClangRenameCategory("Clang-rename options");
|
||||
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
|
||||
/// \brief Specialized MappingTraits to describe how a RenameAllInfo is
|
||||
/// (de)serialized.
|
||||
template <> struct MappingTraits<RenameAllInfo> {
|
||||
static void mapping(IO &IO, RenameAllInfo &Info) {
|
||||
IO.mapOptional("Offset", Info.Offset);
|
||||
IO.mapOptional("QualifiedName", Info.QualifiedName);
|
||||
IO.mapRequired("NewName", Info.NewName);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace yaml
|
||||
} // end namespace llvm
|
||||
|
||||
static cl::OptionCategory ClangRenameOptions("clang-rename common options");
|
||||
|
||||
static cl::list<unsigned> SymbolOffsets(
|
||||
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::ZeroOrMore, cl::cat(ClangRenameOptions));
|
||||
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
|
||||
cl::cat(ClangRenameOptions));
|
||||
static cl::list<std::string>
|
||||
QualifiedNames("qualified-name",
|
||||
cl::desc("The fully qualified name of the symbol."),
|
||||
cl::ZeroOrMore, cl::cat(ClangRenameOptions));
|
||||
|
||||
static cl::list<std::string>
|
||||
NewNames("new-name", cl::desc("The new name to change the symbol to."),
|
||||
cl::ZeroOrMore, cl::cat(ClangRenameOptions));
|
||||
static cl::opt<bool> PrintName(
|
||||
cl::cat(ClangRenameCategory));
|
||||
static cl::opt<std::string>
|
||||
OldName(
|
||||
"old-name",
|
||||
cl::desc("The fully qualified name of the symbol, if -offset is not used."),
|
||||
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(ClangRenameOptions));
|
||||
static cl::opt<bool> PrintLocations(
|
||||
"pl", cl::desc("Print the locations affected by renaming to stderr."),
|
||||
cl::cat(ClangRenameOptions));
|
||||
cl::cat(ClangRenameCategory));
|
||||
static cl::opt<bool>
|
||||
PrintLocations(
|
||||
"pl",
|
||||
cl::desc("Print the locations affected by renaming to stderr."),
|
||||
cl::cat(ClangRenameCategory));
|
||||
static cl::opt<std::string>
|
||||
ExportFixes("export-fixes",
|
||||
cl::desc("YAML file to store suggested fixes in."),
|
||||
cl::value_desc("filename"), cl::cat(ClangRenameOptions));
|
||||
static cl::opt<std::string>
|
||||
Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
|
||||
cl::Optional, cl::cat(ClangRenameOptions));
|
||||
ExportFixes(
|
||||
"export-fixes",
|
||||
cl::desc("YAML file to store suggested fixes in."),
|
||||
cl::value_desc("filename"),
|
||||
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) {
|
||||
tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
|
||||
|
||||
if (!Input.empty()) {
|
||||
// Populate QualifiedNames and NewNames from a YAML file.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
llvm::MemoryBuffer::getFile(Input);
|
||||
if (!Buffer) {
|
||||
errs() << "clang-rename: failed to read " << Input << ": "
|
||||
<< Buffer.getError().message() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<RenameAllInfo> Infos;
|
||||
llvm::yaml::Input YAML(Buffer.get()->getBuffer());
|
||||
YAML >> Infos;
|
||||
for (const auto &Info : Infos) {
|
||||
if (!Info.QualifiedName.empty())
|
||||
QualifiedNames.push_back(Info.QualifiedName);
|
||||
else
|
||||
SymbolOffsets.push_back(Info.Offset);
|
||||
NewNames.push_back(Info.NewName);
|
||||
}
|
||||
}
|
||||
cl::SetVersionPrinter(PrintVersion);
|
||||
tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
|
||||
|
||||
// Check the arguments for correctness.
|
||||
if (NewNames.empty()) {
|
||||
errs() << "clang-rename: -new-name must be specified.\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (SymbolOffsets.empty() == QualifiedNames.empty()) {
|
||||
errs() << "clang-rename: -offset and -qualified-name can't be present at "
|
||||
"the same time.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Check if NewNames is a valid identifier in C++17.
|
||||
LangOptions Options;
|
||||
Options.CPlusPlus = true;
|
||||
Options.CPlusPlus1z = true;
|
||||
IdentifierTable Table(Options);
|
||||
for (const auto &NewName : NewNames) {
|
||||
auto NewNameTokKind = Table.get(NewName).getTokenID();
|
||||
if (!tok::isAnyIdentifier(NewNameTokKind)) {
|
||||
errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
|
||||
errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
|
||||
<< ") + number of qualified names (" << QualifiedNames.size()
|
||||
<< ") must be equal to number of new names(" << NewNames.size()
|
||||
<< ").\n\n";
|
||||
cl::PrintHelpMessage();
|
||||
|
||||
if (NewName.empty()) {
|
||||
errs() << "clang-rename: no new name provided.\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Get the USRs.
|
||||
auto Files = OP.getSourcePathList();
|
||||
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
|
||||
rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames);
|
||||
Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
|
||||
const std::vector<std::vector<std::string>> &USRList =
|
||||
FindingAction.getUSRList();
|
||||
const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
|
||||
if (PrintName) {
|
||||
for (const auto &PrevName : PrevNames) {
|
||||
outs() << "clang-rename found name: " << PrevName << '\n';
|
||||
}
|
||||
rename::USRFindingAction USRAction(SymbolOffset, OldName);
|
||||
|
||||
// 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 (FindingAction.errorOccurred()) {
|
||||
// Diagnostics are already issued at this point.
|
||||
exit(1);
|
||||
if (PrintName) {
|
||||
errs() << "clang-rename: found name: " << PrevName << '\n';
|
||||
}
|
||||
|
||||
// Perform the renaming.
|
||||
rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
|
||||
rename::RenamingAction RenameAction(NewName, PrevName, USRs,
|
||||
Tool.getReplacements(), PrintLocations);
|
||||
std::unique_ptr<tooling::FrontendActionFactory> Factory =
|
||||
tooling::newFrontendActionFactory(&RenameAction);
|
||||
auto Factory = tooling::newFrontendActionFactory(&RenameAction);
|
||||
int ExitCode;
|
||||
|
||||
if (Inplace) {
|
||||
@@ -195,10 +143,9 @@ int main(int argc, const char **argv) {
|
||||
|
||||
// Export replacements.
|
||||
tooling::TranslationUnitReplacements TUR;
|
||||
const auto &FileToReplacements = Tool.getReplacements();
|
||||
for (const auto &Entry : FileToReplacements)
|
||||
TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
|
||||
Entry.second.end());
|
||||
const tooling::Replacements &Replacements = Tool.getReplacements();
|
||||
TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
|
||||
Replacements.end());
|
||||
|
||||
yaml::Output YAML(OS);
|
||||
YAML << TUR;
|
||||
@@ -210,11 +157,12 @@ int main(int argc, const char **argv) {
|
||||
// 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();
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
|
||||
new DiagnosticOptions();
|
||||
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
|
||||
&DiagnosticPrinter, false);
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
|
||||
&*DiagOpts, &DiagnosticPrinter, false);
|
||||
auto &FileMgr = Tool.getFiles();
|
||||
SourceManager Sources(Diagnostics, FileMgr);
|
||||
Rewriter Rewrite(Sources, DefaultLangOptions);
|
||||
@@ -222,7 +170,7 @@ int main(int argc, const char **argv) {
|
||||
Tool.applyAllReplacements(Rewrite);
|
||||
for (const auto &File : Files) {
|
||||
const auto *Entry = FileMgr.getFile(File);
|
||||
const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
|
||||
auto ID = Sources.translateFile(Entry);
|
||||
Rewrite.getEditBuffer(ID).write(outs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*-
|
||||
|
||||
;; Keywords: tools, c
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; To install clang-rename.el make sure the directory of this file is in your
|
||||
;; `load-path' and add
|
||||
;;
|
||||
;; (require 'clang-rename)
|
||||
;;
|
||||
;; to your .emacs configuration.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defgroup clang-rename nil
|
||||
"Integration with clang-rename"
|
||||
:group 'c)
|
||||
|
||||
(defcustom clang-rename-binary "clang-rename"
|
||||
"Path to clang-rename executable."
|
||||
:type '(file :must-match t)
|
||||
:group 'clang-rename)
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-rename (new-name)
|
||||
"Rename all instances of the symbol at point to NEW-NAME using clang-rename."
|
||||
(interactive "sEnter a new name: ")
|
||||
(save-some-buffers :all)
|
||||
;; clang-rename should not be combined with other operations when undoing.
|
||||
(undo-boundary)
|
||||
(let ((output-buffer (get-buffer-create "*clang-rename*")))
|
||||
(with-current-buffer output-buffer (erase-buffer))
|
||||
(let ((exit-code (call-process
|
||||
clang-rename-binary nil output-buffer nil
|
||||
(format "-offset=%d"
|
||||
;; clang-rename wants file (byte) offsets, not
|
||||
;; buffer (character) positions.
|
||||
(clang-rename--bufferpos-to-filepos
|
||||
;; Emacs treats one character after a symbol as
|
||||
;; part of the symbol, but clang-rename doesn’t.
|
||||
;; Use the beginning of the current symbol, if
|
||||
;; available, to resolve the inconsistency.
|
||||
(or (car (bounds-of-thing-at-point 'symbol))
|
||||
(point))
|
||||
'exact))
|
||||
(format "-new-name=%s" new-name)
|
||||
"-i" (buffer-file-name))))
|
||||
(if (and (integerp exit-code) (zerop exit-code))
|
||||
;; Success; revert current buffer so it gets the modifications.
|
||||
(progn
|
||||
(kill-buffer output-buffer)
|
||||
(revert-buffer :ignore-auto :noconfirm :preserve-modes))
|
||||
;; Failure; append exit code to output buffer and display it.
|
||||
(let ((message (clang-rename--format-message
|
||||
"clang-rename failed with %s %s"
|
||||
(if (integerp exit-code) "exit status" "signal")
|
||||
exit-code)))
|
||||
(with-current-buffer output-buffer
|
||||
(insert ?\n message ?\n))
|
||||
(message "%s" message)
|
||||
(display-buffer output-buffer))))))
|
||||
|
||||
(defalias 'clang-rename--bufferpos-to-filepos
|
||||
(if (fboundp 'bufferpos-to-filepos)
|
||||
'bufferpos-to-filepos
|
||||
;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using
|
||||
;; ‘position-bytes’.
|
||||
(lambda (position &optional _quality _coding-system)
|
||||
(1- (position-bytes position)))))
|
||||
|
||||
;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
|
||||
;; versions.
|
||||
(defalias 'clang-rename--format-message
|
||||
(if (fboundp 'format-message) 'format-message 'format))
|
||||
|
||||
(provide 'clang-rename)
|
||||
|
||||
;;; clang-rename.el ends here
|
||||
@@ -9,13 +9,13 @@ Before installing make sure one of the following is satisfied:
|
||||
|
||||
To install, simply put this into your ~/.vimrc
|
||||
|
||||
noremap <leader>cr :pyf <path-to>/clang-rename.py<cr>
|
||||
map ,cr :pyf <path-to>/clang-rename.py<cr>
|
||||
|
||||
IMPORTANT NOTE: Before running the tool, make sure you saved the file.
|
||||
|
||||
All you have to do now is to place a cursor on a variable/function/class which
|
||||
you would like to rename and press '<leader>cr'. You will be prompted for a new
|
||||
name if the cursor points to a valid symbol.
|
||||
you would like to rename and press ',cr'. You will be prompted for a new name if
|
||||
the cursor points to a valid symbol.
|
||||
'''
|
||||
|
||||
import vim
|
||||
@@ -54,7 +54,7 @@ def main():
|
||||
print stderr
|
||||
|
||||
# Reload all buffers in Vim.
|
||||
vim.command("checktime")
|
||||
vim.command("bufdo edit")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangReorderFields
|
||||
ReorderFieldsAction.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangIndex
|
||||
clangLex
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
@@ -1,264 +0,0 @@
|
||||
//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file contains the definition of the
|
||||
/// ReorderFieldsAction::newASTConsumer method
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ReorderFieldsAction.h"
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
namespace reorder_fields {
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
/// \brief Finds the definition of a record by name.
|
||||
///
|
||||
/// \returns nullptr if the name is ambiguous or not found.
|
||||
static const CXXRecordDecl *findDefinition(StringRef RecordName,
|
||||
ASTContext &Context) {
|
||||
auto Results = match(
|
||||
recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl"),
|
||||
Context);
|
||||
if (Results.empty()) {
|
||||
llvm::errs() << "Definition of " << RecordName << " not found\n";
|
||||
return nullptr;
|
||||
}
|
||||
if (Results.size() > 1) {
|
||||
llvm::errs() << "The name " << RecordName
|
||||
<< " is ambiguous, several definitions found\n";
|
||||
return nullptr;
|
||||
}
|
||||
return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
|
||||
}
|
||||
|
||||
/// \brief Calculates the new order of fields.
|
||||
///
|
||||
/// \returns empty vector if the list of fields doesn't match the definition.
|
||||
static SmallVector<unsigned, 4>
|
||||
getNewFieldsOrder(const CXXRecordDecl *Definition,
|
||||
ArrayRef<std::string> DesiredFieldsOrder) {
|
||||
assert(Definition && "Definition is null");
|
||||
|
||||
llvm::StringMap<unsigned> NameToIndex;
|
||||
for (const auto *Field : Definition->fields())
|
||||
NameToIndex[Field->getName()] = Field->getFieldIndex();
|
||||
|
||||
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
|
||||
llvm::errs() << "Number of provided fields doesn't match definition.\n";
|
||||
return {};
|
||||
}
|
||||
SmallVector<unsigned, 4> NewFieldsOrder;
|
||||
for (const auto &Name : DesiredFieldsOrder) {
|
||||
if (!NameToIndex.count(Name)) {
|
||||
llvm::errs() << "Field " << Name << " not found in definition.\n";
|
||||
return {};
|
||||
}
|
||||
NewFieldsOrder.push_back(NameToIndex[Name]);
|
||||
}
|
||||
assert(NewFieldsOrder.size() == NameToIndex.size());
|
||||
return NewFieldsOrder;
|
||||
}
|
||||
|
||||
// FIXME: error-handling
|
||||
/// \brief Replaces one range of source code by another.
|
||||
static void
|
||||
addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
|
||||
std::map<std::string, tooling::Replacements> &Replacements) {
|
||||
StringRef NewText =
|
||||
Lexer::getSourceText(CharSourceRange::getTokenRange(New),
|
||||
Context.getSourceManager(), Context.getLangOpts());
|
||||
tooling::Replacement R(Context.getSourceManager(),
|
||||
CharSourceRange::getTokenRange(Old), NewText,
|
||||
Context.getLangOpts());
|
||||
consumeError(Replacements[R.getFilePath()].add(R));
|
||||
}
|
||||
|
||||
/// \brief Reorders fields in the definition of a struct/class.
|
||||
///
|
||||
/// At the moment reodering of fields with
|
||||
/// different accesses (public/protected/private) is not supported.
|
||||
/// \returns true on success.
|
||||
static bool reorderFieldsInDefinition(
|
||||
const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
|
||||
const ASTContext &Context,
|
||||
std::map<std::string, tooling::Replacements> &Replacements) {
|
||||
assert(Definition && "Definition is null");
|
||||
|
||||
SmallVector<const FieldDecl *, 10> Fields;
|
||||
for (const auto *Field : Definition->fields())
|
||||
Fields.push_back(Field);
|
||||
|
||||
// Check that the permutation of the fields doesn't change the accesses
|
||||
for (const auto *Field : Definition->fields()) {
|
||||
const auto FieldIndex = Field->getFieldIndex();
|
||||
if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
|
||||
llvm::errs() << "Currently reodering of fields with different accesses "
|
||||
"is not supported\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *Field : Definition->fields()) {
|
||||
const auto FieldIndex = Field->getFieldIndex();
|
||||
if (FieldIndex == NewFieldsOrder[FieldIndex])
|
||||
continue;
|
||||
addReplacement(Field->getSourceRange(),
|
||||
Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
|
||||
Context, Replacements);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Reorders initializers in a C++ struct/class constructor.
|
||||
///
|
||||
/// A constructor can have initializers for an arbitrary subset of the class's fields.
|
||||
/// Thus, we need to ensure that we reorder just the initializers that are present.
|
||||
static void reorderFieldsInConstructor(
|
||||
const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
|
||||
const ASTContext &Context,
|
||||
std::map<std::string, tooling::Replacements> &Replacements) {
|
||||
assert(CtorDecl && "Constructor declaration is null");
|
||||
if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
|
||||
return;
|
||||
|
||||
// The method FunctionDecl::isThisDeclarationADefinition returns false
|
||||
// for a defaulted function unless that function has been implicitly defined.
|
||||
// Thus this assert needs to be after the previous checks.
|
||||
assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
|
||||
|
||||
SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
|
||||
for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
|
||||
NewFieldsPositions[NewFieldsOrder[i]] = i;
|
||||
|
||||
SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
|
||||
SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
|
||||
for (const auto *Initializer : CtorDecl->inits()) {
|
||||
if (!Initializer->isWritten())
|
||||
continue;
|
||||
OldWrittenInitializersOrder.push_back(Initializer);
|
||||
NewWrittenInitializersOrder.push_back(Initializer);
|
||||
}
|
||||
auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
|
||||
const CXXCtorInitializer *RHS) {
|
||||
assert(LHS && RHS);
|
||||
return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
|
||||
NewFieldsPositions[RHS->getMember()->getFieldIndex()];
|
||||
};
|
||||
std::sort(std::begin(NewWrittenInitializersOrder),
|
||||
std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
|
||||
assert(OldWrittenInitializersOrder.size() ==
|
||||
NewWrittenInitializersOrder.size());
|
||||
for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
|
||||
if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
|
||||
addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
|
||||
NewWrittenInitializersOrder[i]->getSourceRange(), Context,
|
||||
Replacements);
|
||||
}
|
||||
|
||||
/// \brief Reorders initializers in the brace initialization of an aggregate.
|
||||
///
|
||||
/// At the moment partial initialization is not supported.
|
||||
/// \returns true on success
|
||||
static bool reorderFieldsInInitListExpr(
|
||||
const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
|
||||
const ASTContext &Context,
|
||||
std::map<std::string, tooling::Replacements> &Replacements) {
|
||||
assert(InitListEx && "Init list expression is null");
|
||||
// We care only about InitListExprs which originate from source code.
|
||||
// Implicit InitListExprs are created by the semantic analyzer.
|
||||
if (!InitListEx->isExplicit())
|
||||
return true;
|
||||
// The method InitListExpr::getSyntacticForm may return nullptr indicating that
|
||||
// the current initializer list also serves as its syntactic form.
|
||||
if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
|
||||
InitListEx = SyntacticForm;
|
||||
// If there are no initializers we do not need to change anything.
|
||||
if (!InitListEx->getNumInits())
|
||||
return true;
|
||||
if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
|
||||
llvm::errs() << "Currently only full initialization is supported\n";
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
|
||||
if (i != NewFieldsOrder[i])
|
||||
addReplacement(
|
||||
InitListEx->getInit(i)->getSourceRange(),
|
||||
InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
|
||||
Replacements);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class ReorderingConsumer : public ASTConsumer {
|
||||
StringRef RecordName;
|
||||
ArrayRef<std::string> DesiredFieldsOrder;
|
||||
std::map<std::string, tooling::Replacements> &Replacements;
|
||||
|
||||
public:
|
||||
ReorderingConsumer(StringRef RecordName,
|
||||
ArrayRef<std::string> DesiredFieldsOrder,
|
||||
std::map<std::string, tooling::Replacements> &Replacements)
|
||||
: RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
|
||||
Replacements(Replacements) {}
|
||||
|
||||
ReorderingConsumer(const ReorderingConsumer &) = delete;
|
||||
ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
|
||||
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
const CXXRecordDecl *RD = findDefinition(RecordName, Context);
|
||||
if (!RD)
|
||||
return;
|
||||
SmallVector<unsigned, 4> NewFieldsOrder =
|
||||
getNewFieldsOrder(RD, DesiredFieldsOrder);
|
||||
if (NewFieldsOrder.empty())
|
||||
return;
|
||||
if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
|
||||
return;
|
||||
for (const auto *C : RD->ctors())
|
||||
if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
|
||||
reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
|
||||
NewFieldsOrder, Context, Replacements);
|
||||
|
||||
// We only need to reorder init list expressions for aggregate types.
|
||||
// For other types the order of constructor parameters is used,
|
||||
// which we don't change at the moment.
|
||||
// Now (v0) partial initialization is not supported.
|
||||
if (RD->isAggregate())
|
||||
for (auto Result :
|
||||
match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
|
||||
Context))
|
||||
if (!reorderFieldsInInitListExpr(
|
||||
Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
|
||||
Context, Replacements)) {
|
||||
Replacements.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
|
||||
return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
|
||||
Replacements);
|
||||
}
|
||||
|
||||
} // namespace reorder_fields
|
||||
} // namespace clang
|
||||
@@ -1,47 +0,0 @@
|
||||
//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.h -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file contains the declarations of the ReorderFieldsAction class and
|
||||
/// the FieldPosition struct.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
|
||||
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
namespace clang {
|
||||
class ASTConsumer;
|
||||
|
||||
namespace reorder_fields {
|
||||
|
||||
class ReorderFieldsAction {
|
||||
llvm::StringRef RecordName;
|
||||
llvm::ArrayRef<std::string> DesiredFieldsOrder;
|
||||
std::map<std::string, tooling::Replacements> &Replacements;
|
||||
|
||||
public:
|
||||
ReorderFieldsAction(
|
||||
llvm::StringRef RecordName,
|
||||
llvm::ArrayRef<std::string> DesiredFieldsOrder,
|
||||
std::map<std::string, tooling::Replacements> &Replacements)
|
||||
: RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
|
||||
Replacements(Replacements) {}
|
||||
|
||||
ReorderFieldsAction(const ReorderFieldsAction &) = delete;
|
||||
ReorderFieldsAction &operator=(const ReorderFieldsAction &) = delete;
|
||||
|
||||
std::unique_ptr<ASTConsumer> newASTConsumer();
|
||||
};
|
||||
} // namespace reorder_fields
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
|
||||
@@ -1,12 +0,0 @@
|
||||
add_clang_executable(clang-reorder-fields ClangReorderFields.cpp)
|
||||
|
||||
target_link_libraries(clang-reorder-fields
|
||||
clangBasic
|
||||
clangFrontend
|
||||
clangReorderFields
|
||||
clangRewrite
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
)
|
||||
|
||||
install(TARGETS clang-reorder-fields RUNTIME DESTINATION bin)
|
||||
@@ -1,88 +0,0 @@
|
||||
//===-- tools/extra/clang-reorder-fields/tool/ClangReorderFields.cpp -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file contains the implementation of clang-reorder-fields tool
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../ReorderFieldsAction.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.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/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
cl::OptionCategory ClangReorderFieldsCategory("clang-reorder-fields options");
|
||||
|
||||
static cl::opt<std::string>
|
||||
RecordName("record-name", cl::Required,
|
||||
cl::desc("The name of the struct/class."),
|
||||
cl::cat(ClangReorderFieldsCategory));
|
||||
|
||||
static cl::list<std::string> FieldsOrder("fields-order", cl::CommaSeparated,
|
||||
cl::OneOrMore,
|
||||
cl::desc("The desired fields order."),
|
||||
cl::cat(ClangReorderFieldsCategory));
|
||||
|
||||
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
|
||||
cl::cat(ClangReorderFieldsCategory));
|
||||
|
||||
const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
|
||||
Usage);
|
||||
|
||||
auto Files = OP.getSourcePathList();
|
||||
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
|
||||
|
||||
reorder_fields::ReorderFieldsAction Action(RecordName, FieldsOrder,
|
||||
Tool.getReplacements());
|
||||
|
||||
auto Factory = tooling::newFrontendActionFactory(&Action);
|
||||
|
||||
if (Inplace)
|
||||
return Tool.runAndSave(Factory.get());
|
||||
|
||||
int ExitCode = Tool.run(Factory.get());
|
||||
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);
|
||||
const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
|
||||
Rewrite.getEditBuffer(ID).write(outs());
|
||||
}
|
||||
|
||||
return ExitCode;
|
||||
}
|
||||
7
clang-tools-extra/clang-tidy-vs/.gitignore
vendored
7
clang-tools-extra/clang-tidy-vs/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
obj/
|
||||
bin/
|
||||
.vs/
|
||||
Key.snk
|
||||
clang-tidy.exe
|
||||
packages/
|
||||
*.csproj.user
|
||||
@@ -1,28 +0,0 @@
|
||||
option(BUILD_CLANG_TIDY_VS_PLUGIN "Build clang-tidy VS plugin" OFF)
|
||||
if (BUILD_CLANG_TIDY_VS_PLUGIN)
|
||||
add_custom_target(clang_tidy_exe_for_vsix
|
||||
${CMAKE_COMMAND} -E copy_if_different
|
||||
"${LLVM_TOOLS_BINARY_DIR}/clang-tidy.exe"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/clang-tidy.exe"
|
||||
DEPENDS clang-tidy)
|
||||
|
||||
add_custom_target(clang_tidy_license
|
||||
${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CLANG_SOURCE_DIR}/LICENSE.TXT"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/license.txt")
|
||||
|
||||
if (NOT CLANG_TIDY_VS_VERSION)
|
||||
set(CLANG_TIDY_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
configure_file("source.extension.vsixmanifest.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest")
|
||||
|
||||
add_custom_target(clang_tidy_vsix ALL
|
||||
devenv "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy.sln" /Build Release
|
||||
DEPENDS clang_tidy_exe_for_vsix "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/bin/Release/ClangTidy.vsix"
|
||||
"${LLVM_TOOLS_BINARY_DIR}/ClangTidy.vsix"
|
||||
DEPENDS clang_tidy_exe_for_vsix clang_tidy_license)
|
||||
endif()
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangTidy", "ClangTidy\ClangTidy.csproj", "{BE261DA1-36C6-449A-95C5-4653A549170A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows entire categories of properties to be enabled, disabled, or inherited
|
||||
/// in one fell swoop. We add properties to each category with the value being
|
||||
/// this enum, and when the value is selected, we use reflection to find all other
|
||||
/// properties in the same category and perform the corresponding action.
|
||||
/// </summary>
|
||||
public enum CategoryVerb
|
||||
{
|
||||
None,
|
||||
Disable,
|
||||
Enable,
|
||||
Inherit
|
||||
}
|
||||
|
||||
public class CategoryVerbConverter : EnumConverter
|
||||
{
|
||||
public CategoryVerbConverter() : base(typeof(CategoryVerb))
|
||||
{
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
switch ((string)value)
|
||||
{
|
||||
case "Disable Category":
|
||||
return CategoryVerb.Disable;
|
||||
case "Enable Category":
|
||||
return CategoryVerb.Enable;
|
||||
case "Inherit Category":
|
||||
return CategoryVerb.Inherit;
|
||||
case "":
|
||||
return CategoryVerb.None;
|
||||
}
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value is CategoryVerb && destinationType == typeof(string))
|
||||
{
|
||||
switch ((CategoryVerb)value)
|
||||
{
|
||||
case CategoryVerb.Disable:
|
||||
return "Disable Category";
|
||||
case CategoryVerb.Enable:
|
||||
return "Enable Category";
|
||||
case CategoryVerb.Inherit:
|
||||
return "Inherit Category";
|
||||
case CategoryVerb.None:
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
public class CheckInfo
|
||||
{
|
||||
[YamlAlias("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[YamlAlias("Label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
[YamlAlias("Description")]
|
||||
public string Desc { get; set; }
|
||||
|
||||
[YamlAlias("Category")]
|
||||
public string Category { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the list of checks from Yaml and builds a description of each one.
|
||||
/// This list of checks is then used by the PropertyGrid to determine what
|
||||
/// items to display.
|
||||
/// </summary>
|
||||
public static class CheckDatabase
|
||||
{
|
||||
static CheckInfo[] Checks_ = null;
|
||||
|
||||
class CheckRoot
|
||||
{
|
||||
[YamlAlias("Checks")]
|
||||
public CheckInfo[] Checks { get; set; }
|
||||
}
|
||||
|
||||
static CheckDatabase()
|
||||
{
|
||||
using (StringReader Reader = new StringReader(Resources.ClangTidyChecks))
|
||||
{
|
||||
Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());
|
||||
var Root = D.Deserialize<CheckRoot>(Reader);
|
||||
Checks_ = Root.Checks;
|
||||
|
||||
HashSet<string> Names = new HashSet<string>();
|
||||
foreach (var Check in Checks_)
|
||||
{
|
||||
if (Names.Contains(Check.Name))
|
||||
continue;
|
||||
Names.Add(Check.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CheckInfo> Checks
|
||||
{
|
||||
get
|
||||
{
|
||||
return Checks_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
/// <summary>
|
||||
/// CheckTree is used to group checks into categories and subcategories. For
|
||||
/// example, given the following list of checks:
|
||||
///
|
||||
/// llvm-include-order
|
||||
/// llvm-namespace-comment
|
||||
/// llvm-twine-local
|
||||
/// llvm-header-guard
|
||||
/// google-runtime-member-string-references
|
||||
/// google-runtime-int
|
||||
/// google-readability-namespace-comments
|
||||
///
|
||||
/// the corresponding CheckTree would look like this:
|
||||
///
|
||||
/// llvm
|
||||
/// include-order
|
||||
/// namespace-comment
|
||||
/// twine-local
|
||||
/// header-guard
|
||||
/// google
|
||||
/// runtime
|
||||
/// member-string-references
|
||||
/// int
|
||||
/// readability
|
||||
/// namespace-comments
|
||||
/// redundant-smartptr-get
|
||||
///
|
||||
/// This is useful when serializing a set of options out to a .clang-tidy file,
|
||||
/// because we need to decide the most efficient way to serialize the sequence
|
||||
/// of check commands, when to use wildcards, etc. For example, if everything
|
||||
/// under google is inherited, we can simply leave that entry out entirely from
|
||||
/// the .clang-tidy file. On the other hand, if anything is inherited, we *must
|
||||
/// not* add or remove google-* by wildcard because that, by definition, means
|
||||
/// the property is no longer inherited. When we can categorize the checks into
|
||||
/// groups and subgroups like this, it is possible to efficiently serialize to
|
||||
/// a minimal representative .clang-tidy file.
|
||||
/// </summary>
|
||||
|
||||
public abstract class CheckTreeNode
|
||||
{
|
||||
private string Name_;
|
||||
private CheckTreeNode Parent_;
|
||||
|
||||
protected CheckTreeNode(string Name, CheckTreeNode Parent)
|
||||
{
|
||||
Name_ = Name;
|
||||
Parent_ = Parent;
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Parent_ == null)
|
||||
return null;
|
||||
string ParentPath = Parent_.Path;
|
||||
if (ParentPath == null)
|
||||
return Name_;
|
||||
return ParentPath + "-" + Name_;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return Name_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract int CountChecks { get; }
|
||||
public abstract int CountExplicitlyDisabledChecks { get; }
|
||||
public abstract int CountExplicitlyEnabledChecks { get; }
|
||||
public abstract int CountInheritedChecks { get; }
|
||||
}
|
||||
|
||||
public class CheckTree : CheckTreeNode
|
||||
{
|
||||
private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>();
|
||||
public CheckTree()
|
||||
: base(null, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private CheckTree(string Name, CheckTree Parent)
|
||||
: base(Name, Parent)
|
||||
{
|
||||
}
|
||||
|
||||
private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)
|
||||
{
|
||||
Children_[Name] = new CheckLeaf(Name, this, Property);
|
||||
}
|
||||
|
||||
private CheckTree AddOrCreateSubgroup(string Name)
|
||||
{
|
||||
CheckTreeNode Subgroup = null;
|
||||
if (Children_.TryGetValue(Name, out Subgroup))
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
|
||||
return (CheckTree)Subgroup;
|
||||
}
|
||||
|
||||
CheckTree SG = new CheckTree(Name, this);
|
||||
Children_[Name] = SG;
|
||||
return SG;
|
||||
}
|
||||
|
||||
public static CheckTree Build(ClangTidyProperties Config)
|
||||
{
|
||||
// Since some check names contain dashes in them, it doesn't make sense to
|
||||
// simply split all check names by dash and construct a huge tree. For
|
||||
// example, in the check called google-runtime-member-string-references,
|
||||
// we don't need each of those to be a different subgroup. So instead we
|
||||
// explicitly specify the common breaking points at which a user might want
|
||||
// to use a -* and everything else falls as a leaf under one of these
|
||||
// categories.
|
||||
// FIXME: This should be configurable without recompilation
|
||||
CheckTree Root = new CheckTree();
|
||||
string[][] Groups = new string[][] {
|
||||
new string[] {"boost"},
|
||||
new string[] {"cert"},
|
||||
new string[] {"clang", "diagnostic"},
|
||||
new string[] {"cppcoreguidelines", "interfaces"},
|
||||
new string[] {"cppcoreguidelines", "pro", "bounds"},
|
||||
new string[] {"cppcoreguidelines", "pro", "type"},
|
||||
new string[] {"google", "build"},
|
||||
new string[] {"google", "readability"},
|
||||
new string[] {"google", "runtime"},
|
||||
new string[] {"llvm"},
|
||||
new string[] {"misc"},
|
||||
};
|
||||
|
||||
foreach (string[] Group in Groups)
|
||||
{
|
||||
CheckTree Subgroup = Root;
|
||||
foreach (string Component in Group)
|
||||
Subgroup = Subgroup.AddOrCreateSubgroup(Component);
|
||||
}
|
||||
|
||||
var Props = Config.GetProperties()
|
||||
.Cast<PropertyDescriptor>()
|
||||
.OfType<DynamicPropertyDescriptor<bool>>()
|
||||
.Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0)
|
||||
.Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>(
|
||||
x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName));
|
||||
var PropArray = Props.ToArray();
|
||||
foreach (var CheckInfo in PropArray)
|
||||
{
|
||||
string LeafName = null;
|
||||
CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName);
|
||||
Tree.AddLeaf(LeafName, CheckInfo.Key);
|
||||
}
|
||||
return Root;
|
||||
}
|
||||
|
||||
private CheckTree LocateCheckLeafGroup(string Check, out string LeafName)
|
||||
{
|
||||
string[] Components = Check.Split('-');
|
||||
string FirstComponent = Components.FirstOrDefault();
|
||||
if (FirstComponent == null)
|
||||
{
|
||||
LeafName = Check;
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckTreeNode Subgroup = null;
|
||||
if (!Children_.TryGetValue(FirstComponent, out Subgroup))
|
||||
{
|
||||
LeafName = Check;
|
||||
return this;
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
|
||||
CheckTree Child = (CheckTree)Subgroup;
|
||||
string ChildName = Check.Substring(FirstComponent.Length + 1);
|
||||
return Child.LocateCheckLeafGroup(ChildName, out LeafName);
|
||||
}
|
||||
|
||||
public override int CountChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; });
|
||||
}
|
||||
}
|
||||
|
||||
public override int CountExplicitlyDisabledChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; });
|
||||
}
|
||||
}
|
||||
|
||||
public override int CountExplicitlyEnabledChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; });
|
||||
}
|
||||
}
|
||||
public override int CountInheritedChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; });
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<string, CheckTreeNode> Children
|
||||
{
|
||||
get { return Children_; }
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckLeaf : CheckTreeNode
|
||||
{
|
||||
private DynamicPropertyDescriptor<bool> Property_;
|
||||
|
||||
public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)
|
||||
: base(Name, Parent)
|
||||
{
|
||||
Property_ = Property;
|
||||
}
|
||||
|
||||
public override int CountChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override int CountExplicitlyDisabledChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Property_.IsInheriting)
|
||||
return 0;
|
||||
return (bool)Property_.GetValue(null) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override int CountExplicitlyEnabledChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Property_.IsInheriting)
|
||||
return 0;
|
||||
return (bool)Property_.GetValue(null) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override int CountInheritedChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Property_.IsInheriting) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{BE261DA1-36C6-449A-95C5-4653A549170A}</ProjectGuid>
|
||||
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LLVM.ClangTidy</RootNamespace>
|
||||
<AssemblyName>ClangTidy</AssemblyName>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>0</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Editor, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.OLE.Interop" />
|
||||
<Reference Include="Microsoft.VisualStudio.Settings.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.Interop" />
|
||||
<Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
|
||||
<Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
|
||||
<Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Design" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="YamlDotNet, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\YamlDotNet.3.3.0\lib\net35\YamlDotNet.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="YamlDotNet.Dynamic, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="EnvDTE">
|
||||
<Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>
|
||||
<VersionMajor>8</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="EnvDTE100">
|
||||
<Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>
|
||||
<VersionMajor>10</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="EnvDTE80">
|
||||
<Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>
|
||||
<VersionMajor>8</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="EnvDTE90">
|
||||
<Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>
|
||||
<VersionMajor>9</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="Microsoft.VisualStudio.CommandBars">
|
||||
<Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>
|
||||
<VersionMajor>8</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="stdole">
|
||||
<Guid>{00020430-0000-0000-C000-000000000046}</Guid>
|
||||
<VersionMajor>2</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CategoryVerb.cs" />
|
||||
<Compile Include="CheckDatabase.cs" />
|
||||
<Compile Include="CheckTree.cs" />
|
||||
<Compile Include="ClangTidyCheckAttribute.cs" />
|
||||
<Compile Include="ClangTidyConfigurationPage.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ClangTidyProperties.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="DynamicPropertyConverter.cs" />
|
||||
<Compile Include="DynamicPropertyDescriptor.cs" />
|
||||
<Compile Include="ForwardingPropertyDescriptor.cs" />
|
||||
<Compile Include="Guids.cs" />
|
||||
<Compile Include="DynamicPropertyComponent.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="DynamicPropertyComponent.Designer.cs" />
|
||||
<Compile Include="ClangTidyConfigParser.cs" />
|
||||
<Compile Include="Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="ClangTidyPackage.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PkgCmdID.cs" />
|
||||
<Compile Include="ClangTidyPropertyGrid.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ClangTidyPropertyGrid.Designer.cs">
|
||||
<DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Utility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ClangTidyPropertyGrid.resx">
|
||||
<DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="VSPackage.resx">
|
||||
<MergeWithCTO>true</MergeWithCTO>
|
||||
<ManifestResourceName>VSPackage</ManifestResourceName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Key.snk" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Resources\ClangTidyChecks.yaml" />
|
||||
<None Include="source.extension.vsixmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<VSCTCompile Include="ClangTidy.vsct">
|
||||
<ResourceName>Menus.ctmenu</ResourceName>
|
||||
<SubType>Designer</SubType>
|
||||
</VSCTCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\Images_32bit.bmp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="clang-tidy.exe">
|
||||
<IncludeInVSIX>true</IncludeInVSIX>
|
||||
</Content>
|
||||
<Content Include="license.txt">
|
||||
<IncludeInVSIX>true</IncludeInVSIX>
|
||||
</Content>
|
||||
<Content Include="Resources\Package.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Windows Installer 4.5</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<UseCodebase>true</UseCodebase>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(SDKToolsPath)\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="BeforeBuild">
|
||||
<Exec ContinueOnError="false" Command=""..\packages\Brutal.Dev.StrongNameSigner.1.8.0\tools\StrongNameSigner.Console.exe" -in "..\packages" -l Summary" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,118 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<!-- This is the file that defines the actual layout and type of the commands.
|
||||
It is divided in different sections (e.g. command definition, command
|
||||
placement, ...), with each defining a specific set of properties.
|
||||
See the comment before each section for more details about how to
|
||||
use it. -->
|
||||
|
||||
<!-- The VSCT compiler (the tool that translates this file into the binary
|
||||
format that VisualStudio will consume) has the ability to run a preprocessor
|
||||
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
|
||||
it is possible to define includes and macros with the same syntax used
|
||||
in C++ files. Using this ability of the compiler here, we include some files
|
||||
defining some of the constants that we will use inside the file. -->
|
||||
|
||||
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
|
||||
<Extern href="stdidcmd.h"/>
|
||||
|
||||
<!--This header contains the command ids for the menus provided by the shell. -->
|
||||
<Extern href="vsshlids.h"/>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--The Commands section is where we the commands, menus and menu groups are defined.
|
||||
This section uses a Guid to identify the package that provides the command defined inside it. -->
|
||||
<Commands package="guidClangTidyPkg">
|
||||
<!-- Inside this section we have different sub-sections: one for the menus, another
|
||||
for the menu groups, one for the buttons (the actual commands), one for the combos
|
||||
and the last one for the bitmaps used. Each element is identified by a command id that
|
||||
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
|
||||
called "command set" and is used to group different command inside a logically related
|
||||
group; your package should define its own command set in order to avoid collisions
|
||||
with command ids defined by other packages. -->
|
||||
|
||||
|
||||
<!-- In this section you can define new menu groups. A menu group is a container for
|
||||
other menus or buttons (commands); from a visual point of view you can see the
|
||||
group as the part of a menu contained between two lines. The parent of a group
|
||||
must be a menu. -->
|
||||
<Groups>
|
||||
|
||||
<Group guid="guidClangTidyCmdSet" id="MyMenuGroup" priority="0x0600">
|
||||
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
|
||||
</Group>
|
||||
|
||||
|
||||
|
||||
</Groups>
|
||||
|
||||
<!--Buttons section. -->
|
||||
<!--This section defines the elements the user can interact with, like a menu command or a button
|
||||
or combo box in a toolbar. -->
|
||||
<Buttons>
|
||||
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
|
||||
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
|
||||
the CommandFlag node.
|
||||
You can add more than one CommandFlag node e.g.:
|
||||
<CommandFlag>DefaultInvisible</CommandFlag>
|
||||
<CommandFlag>DynamicVisibility</CommandFlag>
|
||||
If you do not want an image next to your command, remove the Icon node /> -->
|
||||
|
||||
<Button guid="guidClangTidyCmdSet" id="cmdidClangTidy" priority="0x0100" type="Button">
|
||||
<Parent guid="guidClangTidyCmdSet" id="MyMenuGroup" />
|
||||
<Icon guid="guidImages" id="bmpPic1" />
|
||||
<Strings>
|
||||
<ButtonText>ClangTidy</ButtonText>
|
||||
</Strings>
|
||||
</Button>
|
||||
|
||||
|
||||
|
||||
</Buttons>
|
||||
|
||||
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
|
||||
<Bitmaps>
|
||||
<!-- The bitmap id is defined in a way that is a little bit different from the others:
|
||||
the declaration starts with a guid for the bitmap strip, then there is the resource id of the
|
||||
bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
|
||||
inside a button definition. An important aspect of this declaration is that the element id
|
||||
must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
|
||||
<Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
|
||||
|
||||
</Bitmaps>
|
||||
|
||||
</Commands>
|
||||
|
||||
|
||||
<KeyBindings>
|
||||
<KeyBinding guid="guidClangTidyCmdSet" id="cmdidClangTidy" editor="guidTextEditor" key1="R" mod1="Control" key2="T" mod2="Control"/>
|
||||
</KeyBindings>
|
||||
|
||||
|
||||
|
||||
<Symbols>
|
||||
<!-- This is the package guid. -->
|
||||
<GuidSymbol name="guidClangTidyPkg" value="{AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C}" />
|
||||
|
||||
<!-- This is the guid used to group the menu commands together -->
|
||||
<GuidSymbol name="guidClangTidyCmdSet" value="{9E0F0493-6493-46DE-AEE1-ACD8F60F265E}">
|
||||
<IDSymbol name="MyMenuGroup" value="0x1020" />
|
||||
<IDSymbol name="cmdidClangTidy" value="0x0100" />
|
||||
</GuidSymbol>
|
||||
|
||||
<GuidSymbol name="guidTextEditor" value="{E10FAD35-7FB8-4991-A269-EF88F12166C9}" />
|
||||
|
||||
|
||||
<GuidSymbol name="guidImages" value="{942F126F-942D-428A-84B4-4AC7C523D0B2}" >
|
||||
<IDSymbol name="bmpPic1" value="1" />
|
||||
<IDSymbol name="bmpPic2" value="2" />
|
||||
<IDSymbol name="bmpPicSearch" value="3" />
|
||||
<IDSymbol name="bmpPicX" value="4" />
|
||||
<IDSymbol name="bmpPicArrows" value="5" />
|
||||
</GuidSymbol>
|
||||
</Symbols>
|
||||
|
||||
</CommandTable>
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
public class ClangTidyCheckAttribute : Attribute
|
||||
{
|
||||
private string CheckName_;
|
||||
public ClangTidyCheckAttribute(string CheckName)
|
||||
{
|
||||
this.CheckName_ = CheckName;
|
||||
}
|
||||
|
||||
public string CheckName
|
||||
{
|
||||
get { return CheckName_; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
static class ClangTidyConfigParser
|
||||
{
|
||||
public class CheckOption
|
||||
{
|
||||
[YamlAlias("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[YamlAlias("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
public class ClangTidyYaml
|
||||
{
|
||||
[YamlAlias("Checks")]
|
||||
public string Checks { get; set; }
|
||||
|
||||
[YamlAlias("CheckOptions")]
|
||||
public List<CheckOption> CheckOptions { get; set; }
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<string, ClangTidyProperties>> ParseConfigurationChain(string ClangTidyFile)
|
||||
{
|
||||
List<KeyValuePair<string, ClangTidyProperties>> Result = new List<KeyValuePair<string, ClangTidyProperties>>();
|
||||
Result.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
|
||||
|
||||
foreach (string P in Utility.SplitPath(ClangTidyFile).Reverse())
|
||||
{
|
||||
if (!Utility.HasClangTidyFile(P))
|
||||
continue;
|
||||
|
||||
string ConfigFile = Path.Combine(P, ".clang-tidy");
|
||||
|
||||
using (StreamReader Reader = new StreamReader(ConfigFile))
|
||||
{
|
||||
Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());
|
||||
ClangTidyYaml Y = D.Deserialize<ClangTidyYaml>(Reader);
|
||||
ClangTidyProperties Parent = Result[Result.Count - 1].Value;
|
||||
ClangTidyProperties NewProps = new ClangTidyProperties(Parent);
|
||||
SetPropertiesFromYaml(Y, NewProps);
|
||||
Result.Add(new KeyValuePair<string, ClangTidyProperties>(P, NewProps));
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
enum TreeLevelOp
|
||||
{
|
||||
Enable,
|
||||
Disable,
|
||||
Inherit
|
||||
}
|
||||
|
||||
public static void SerializeClangTidyFile(ClangTidyProperties Props, string ClangTidyFilePath)
|
||||
{
|
||||
List<string> CommandList = new List<string>();
|
||||
SerializeCheckTree(CommandList, Props.GetCheckTree(), TreeLevelOp.Inherit);
|
||||
|
||||
CommandList.Sort((x, y) =>
|
||||
{
|
||||
bool LeftSub = x.StartsWith("-");
|
||||
bool RightSub = y.StartsWith("-");
|
||||
if (LeftSub && !RightSub)
|
||||
return -1;
|
||||
if (RightSub && !LeftSub)
|
||||
return 1;
|
||||
return StringComparer.CurrentCulture.Compare(x, y);
|
||||
});
|
||||
|
||||
string ConfigFile = Path.Combine(ClangTidyFilePath, ".clang-tidy");
|
||||
using (StreamWriter Writer = new StreamWriter(ConfigFile))
|
||||
{
|
||||
Serializer S = new Serializer(namingConvention: new PascalCaseNamingConvention());
|
||||
ClangTidyYaml Yaml = new ClangTidyYaml();
|
||||
Yaml.Checks = String.Join(",", CommandList.ToArray());
|
||||
S.Serialize(Writer, Yaml);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the given check tree into serialized list of commands that can be written to
|
||||
/// the Yaml. The goal here is to determine the minimal sequence of check commands that
|
||||
/// will produce the exact configuration displayed in the UI. This is complicated by the
|
||||
/// fact that an inherited True is not the same as an explicitly specified True. If the
|
||||
/// user has chosen to inherit a setting in a .clang-tidy file, then changing it in the
|
||||
/// parent should show the reflected changes in the current file as well. So we cannot
|
||||
/// simply -* everything and then add in the checks we need, because -* immediately marks
|
||||
/// every single check as explicitly false, thus disabling inheritance.
|
||||
/// </summary>
|
||||
/// <param name="CommandList">State passed through this recursive algorithm representing
|
||||
/// the sequence of commands we have determined so far.
|
||||
/// </param>
|
||||
/// <param name="Tree">The check tree to serialize. This is the parameter that will be
|
||||
/// recursed on as successive subtrees get serialized to `CommandList`.
|
||||
/// </param>
|
||||
/// <param name="CurrentOp">The current state of the subtree. For example, if the
|
||||
/// algorithm decides to -* an entire subtree and then add back one single check,
|
||||
/// after adding a -subtree-* command to CommandList, it would pass in a value of
|
||||
/// CurrentOp=TreeLevelOp.Disable when it recurses down. This allows deeper iterations
|
||||
/// of the algorithm to know what kind of command (if any) needs to be added to CommandList
|
||||
/// in order to put a particular check into a particular state.
|
||||
/// </param>
|
||||
private static void SerializeCheckTree(List<string> CommandList, CheckTree Tree, TreeLevelOp CurrentOp)
|
||||
{
|
||||
int NumChecks = Tree.CountChecks;
|
||||
int NumDisabled = Tree.CountExplicitlyDisabledChecks;
|
||||
int NumEnabled = Tree.CountExplicitlyEnabledChecks;
|
||||
int NumInherited = Tree.CountInheritedChecks;
|
||||
|
||||
if (NumChecks == 0)
|
||||
return;
|
||||
|
||||
if (NumInherited > 0)
|
||||
System.Diagnostics.Debug.Assert(CurrentOp == TreeLevelOp.Inherit);
|
||||
|
||||
// If this entire tree is inherited, just exit, nothing about this needs to
|
||||
// go in the clang-tidy file.
|
||||
if (NumInherited == NumChecks)
|
||||
return;
|
||||
|
||||
TreeLevelOp NewOp = CurrentOp;
|
||||
// If there are no inherited properties in this subtree, decide whether to
|
||||
// explicitly enable or disable this subtree. Decide by looking at whether
|
||||
// there is a larger proportion of disabled or enabled descendants. If
|
||||
// there are more disabled items in this subtree for example, disabling the
|
||||
// subtree will lead to a smaller configuration file.
|
||||
if (NumInherited == 0)
|
||||
{
|
||||
if (NumDisabled >= NumEnabled)
|
||||
NewOp = TreeLevelOp.Disable;
|
||||
else
|
||||
NewOp = TreeLevelOp.Enable;
|
||||
}
|
||||
|
||||
if (NewOp == TreeLevelOp.Disable)
|
||||
{
|
||||
// Only add an explicit disable command if the tree was not already disabled
|
||||
// to begin with.
|
||||
if (CurrentOp != TreeLevelOp.Disable)
|
||||
{
|
||||
string WildcardPath = "*";
|
||||
if (Tree.Path != null)
|
||||
WildcardPath = Tree.Path + "-" + WildcardPath;
|
||||
CommandList.Add("-" + WildcardPath);
|
||||
}
|
||||
// If the entire subtree was disabled, there's no point descending.
|
||||
if (NumDisabled == NumChecks)
|
||||
return;
|
||||
}
|
||||
else if (NewOp == TreeLevelOp.Enable)
|
||||
{
|
||||
// Only add an explicit enable command if the tree was not already enabled
|
||||
// to begin with. Note that if we're at the root, all checks are already
|
||||
// enabled by default, so there's no need to explicitly include *
|
||||
if (CurrentOp != TreeLevelOp.Enable && Tree.Path != null)
|
||||
{
|
||||
string WildcardPath = Tree.Path + "-*";
|
||||
CommandList.Add(WildcardPath);
|
||||
}
|
||||
// If the entire subtree was enabled, there's no point descending.
|
||||
if (NumEnabled == NumChecks)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var Child in Tree.Children)
|
||||
{
|
||||
if (Child.Value is CheckLeaf)
|
||||
{
|
||||
CheckLeaf Leaf = (CheckLeaf)Child.Value;
|
||||
if (Leaf.CountExplicitlyEnabledChecks == 1 && NewOp != TreeLevelOp.Enable)
|
||||
CommandList.Add(Leaf.Path);
|
||||
else if (Leaf.CountExplicitlyDisabledChecks == 1 && NewOp != TreeLevelOp.Disable)
|
||||
CommandList.Add("-" + Leaf.Path);
|
||||
continue;
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.Assert(Child.Value is CheckTree);
|
||||
CheckTree ChildTree = (CheckTree)Child.Value;
|
||||
SerializeCheckTree(CommandList, ChildTree, NewOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetPropertiesFromYaml(ClangTidyYaml Yaml, ClangTidyProperties Props)
|
||||
{
|
||||
string[] CheckCommands = Yaml.Checks.Split(',');
|
||||
foreach (string Command in CheckCommands)
|
||||
{
|
||||
if (Command == null || Command.Length == 0)
|
||||
continue;
|
||||
bool Add = true;
|
||||
string Pattern = Command;
|
||||
if (Pattern[0] == '-')
|
||||
{
|
||||
Pattern = Pattern.Substring(1);
|
||||
Add = false;
|
||||
}
|
||||
|
||||
foreach (var Match in CheckDatabase.Checks.Where(x => Utility.MatchWildcardString(x.Name, Pattern)))
|
||||
{
|
||||
Props.SetDynamicValue(Match.Name, Add);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using Microsoft.VisualStudio;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
[ClassInterface(ClassInterfaceType.AutoDual)]
|
||||
[CLSCompliant(false), ComVisible(true)]
|
||||
public class ClangTidyConfigurationPage : DialogPage
|
||||
{
|
||||
ClangTidyPropertyGrid Grid = null;
|
||||
protected override IWin32Window Window
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Grid == null)
|
||||
Grid = new ClangTidyPropertyGrid();
|
||||
return Grid;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveSetting(PropertyDescriptor property)
|
||||
{
|
||||
base.SaveSetting(property);
|
||||
}
|
||||
|
||||
public override void SaveSettingsToStorage()
|
||||
{
|
||||
if (Grid != null)
|
||||
Grid.SaveSettingsToStorage();
|
||||
|
||||
base.SaveSettingsToStorage();
|
||||
}
|
||||
|
||||
public override void ResetSettings()
|
||||
{
|
||||
base.ResetSettings();
|
||||
}
|
||||
|
||||
protected override void LoadSettingFromStorage(PropertyDescriptor prop)
|
||||
{
|
||||
base.LoadSettingFromStorage(prop);
|
||||
}
|
||||
|
||||
public override void LoadSettingsFromStorage()
|
||||
{
|
||||
if (Grid != null)
|
||||
Grid.InitializeSettings();
|
||||
base.LoadSettingsFromStorage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
//===-- ClangTidyPackages.cs - VSPackage for clang-tidy ----------*- C# -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class contains a VS extension package that runs clang-tidy over a
|
||||
// file in a VS text editor.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
using Microsoft.VisualStudio.Editor;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Microsoft.VisualStudio.TextManager.Interop;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.Design;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
[PackageRegistration(UseManagedResourcesOnly = true)]
|
||||
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
|
||||
[ProvideMenuResource("Menus.ctmenu", 1)]
|
||||
[Guid(GuidList.guidClangTidyPkgString)]
|
||||
[ProvideOptionPage(typeof(ClangTidyConfigurationPage), "LLVM/Clang", "ClangTidy", 0, 0, true)]
|
||||
public sealed class ClangTidyPackage : Package
|
||||
{
|
||||
#region Package Members
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
|
||||
if (commandService != null)
|
||||
{
|
||||
var menuCommandID = new CommandID(GuidList.guidClangTidyCmdSet, (int)PkgCmdIDList.cmdidClangTidy);
|
||||
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
|
||||
commandService.AddCommand(menuItem);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void MenuItemCallback(object sender, EventArgs args)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
|
||||
public class ClangTidyProperties : DynamicPropertyComponent
|
||||
{
|
||||
private static ClangTidyProperties RootProperties_ = null;
|
||||
private CheckTree CheckTree_;
|
||||
private bool HasUnsavedChanges_ = false;
|
||||
|
||||
public struct CheckMapping
|
||||
{
|
||||
public string CheckName;
|
||||
public string Property;
|
||||
}
|
||||
|
||||
public ClangTidyProperties()
|
||||
: base(null)
|
||||
{
|
||||
AddClangCheckProperties();
|
||||
CheckTree_ = CheckTree.Build(this);
|
||||
}
|
||||
|
||||
public ClangTidyProperties(DynamicPropertyComponent Parent)
|
||||
: base(Parent)
|
||||
{
|
||||
AddClangCheckProperties();
|
||||
CheckTree_ = CheckTree.Build(this);
|
||||
}
|
||||
|
||||
static ClangTidyProperties()
|
||||
{
|
||||
RootProperties_ = new ClangTidyProperties(null);
|
||||
}
|
||||
|
||||
public static ClangTidyProperties RootProperties
|
||||
{
|
||||
get { return RootProperties_; }
|
||||
}
|
||||
|
||||
private void AddClangCheckProperties()
|
||||
{
|
||||
// Add each check in the check database
|
||||
HashSet<string> Categories = new HashSet<string>();
|
||||
foreach (var Check in CheckDatabase.Checks)
|
||||
{
|
||||
string Name = Check.Name.Replace('-', '_');
|
||||
List<Attribute> Attrs = new List<Attribute>();
|
||||
Attrs.Add(new CategoryAttribute(Check.Category));
|
||||
Attrs.Add(new DisplayNameAttribute(Check.Label));
|
||||
Attrs.Add(new DefaultValueAttribute(true));
|
||||
Attrs.Add(new DescriptionAttribute(Check.Desc));
|
||||
Attrs.Add(new ClangTidyCheckAttribute(Check.Name));
|
||||
Categories.Add(Check.Category);
|
||||
AddDynamicProperty<bool>(Check.Name, Attrs.ToArray());
|
||||
}
|
||||
|
||||
// Add a category verb for each unique category.
|
||||
foreach (string Cat in Categories)
|
||||
{
|
||||
List<Attribute> Attrs = new List<Attribute>();
|
||||
Attrs.Add(new CategoryAttribute(Cat));
|
||||
Attrs.Add(new DisplayNameAttribute("(Category Verbs)"));
|
||||
Attrs.Add(new TypeConverterAttribute(typeof(CategoryVerbConverter)));
|
||||
Attrs.Add(new DefaultValueAttribute(CategoryVerb.None));
|
||||
AddDynamicProperty<CategoryVerb>(Cat + "Verb", Attrs.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public CheckTree GetCheckTree() { return CheckTree_; }
|
||||
public bool GetHasUnsavedChanges() { return HasUnsavedChanges_; }
|
||||
public void SetHasUnsavedChanges(bool Value) { HasUnsavedChanges_ = Value; }
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
partial class ClangTidyPropertyGrid
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
|
||||
this.clangTidyProperties1 = new LLVM.ClangTidy.ClangTidyProperties();
|
||||
this.clangTidyConfigurationPage1 = new LLVM.ClangTidy.ClangTidyConfigurationPage();
|
||||
this.linkLabelPath = new System.Windows.Forms.LinkLabel();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(14, 17);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(88, 13);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "Configuration File";
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(108, 14);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(222, 20);
|
||||
this.textBox1.TabIndex = 1;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.Location = new System.Drawing.Point(336, 14);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(78, 20);
|
||||
this.button1.TabIndex = 2;
|
||||
this.button1.Text = "Browse";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// propertyGrid1
|
||||
//
|
||||
this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.propertyGrid1.Location = new System.Drawing.Point(20, 73);
|
||||
this.propertyGrid1.Name = "propertyGrid1";
|
||||
this.propertyGrid1.SelectedObject = this.clangTidyProperties1;
|
||||
this.propertyGrid1.Size = new System.Drawing.Size(391, 384);
|
||||
this.propertyGrid1.TabIndex = 6;
|
||||
this.propertyGrid1.ViewBorderColor = System.Drawing.SystemColors.ControlDarkDark;
|
||||
this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged);
|
||||
//
|
||||
// linkLabelPath
|
||||
//
|
||||
this.linkLabelPath.AutoSize = true;
|
||||
this.linkLabelPath.Location = new System.Drawing.Point(29, 50);
|
||||
this.linkLabelPath.Name = "linkLabelPath";
|
||||
this.linkLabelPath.Size = new System.Drawing.Size(55, 13);
|
||||
this.linkLabelPath.TabIndex = 7;
|
||||
this.linkLabelPath.TabStop = true;
|
||||
this.linkLabelPath.Text = "linkLabel1";
|
||||
this.linkLabelPath.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelPath_LinkClicked);
|
||||
//
|
||||
// ClangTidyPropertyGrid
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.linkLabelPath);
|
||||
this.Controls.Add(this.propertyGrid1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Name = "ClangTidyPropertyGrid";
|
||||
this.Size = new System.Drawing.Size(444, 469);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.PropertyGrid propertyGrid1;
|
||||
private ClangTidyProperties clangTidyProperties1;
|
||||
private ClangTidyConfigurationPage clangTidyConfigurationPage1;
|
||||
private System.Windows.Forms.LinkLabel linkLabelPath;
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
//===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class contains a UserControl consisting of a .NET PropertyGrid control
|
||||
// allowing configuration of checks and check options for ClangTidy.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
/// <summary>
|
||||
/// A UserControl displaying a PropertyGrid allowing configuration of clang-tidy
|
||||
/// checks and check options, as well as serialization and deserialization of
|
||||
/// clang-tidy configuration files. When a configuration file is loaded, the
|
||||
/// entire chain of configuration files is analyzed based on the file path,
|
||||
/// and quick access is provided to edit or view any of the files in the
|
||||
/// configuration chain, allowing easy visualization of where values come from
|
||||
/// (similar in spirit to the -explain-config option of clang-tidy).
|
||||
/// </summary>
|
||||
public partial class ClangTidyPropertyGrid : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The sequence of .clang-tidy configuration files, starting from the root
|
||||
/// of the filesystem, down to the selected file.
|
||||
/// </summary>
|
||||
List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;
|
||||
|
||||
public ClangTidyPropertyGrid()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeSettings();
|
||||
}
|
||||
|
||||
private enum ShouldCancel
|
||||
{
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
public void SaveSettingsToStorage()
|
||||
{
|
||||
PersistUnsavedChanges(false);
|
||||
}
|
||||
|
||||
private ShouldCancel PersistUnsavedChanges(bool PromptFirst)
|
||||
{
|
||||
var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());
|
||||
if (UnsavedResults.Count() == 0)
|
||||
return ShouldCancel.No;
|
||||
|
||||
bool ShouldSave = false;
|
||||
if (PromptFirst)
|
||||
{
|
||||
var Response = MessageBox.Show(
|
||||
"You have unsaved changes! Do you want to save before loading a new file?",
|
||||
"clang-tidy",
|
||||
MessageBoxButtons.YesNoCancel);
|
||||
|
||||
ShouldSave = (Response == DialogResult.Yes);
|
||||
if (Response == DialogResult.Cancel)
|
||||
return ShouldCancel.Yes;
|
||||
}
|
||||
else
|
||||
ShouldSave = true;
|
||||
|
||||
if (ShouldSave)
|
||||
{
|
||||
foreach (var Result in UnsavedResults)
|
||||
{
|
||||
ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);
|
||||
Result.Value.SetHasUnsavedChanges(false);
|
||||
}
|
||||
}
|
||||
return ShouldCancel.No;
|
||||
}
|
||||
|
||||
public void InitializeSettings()
|
||||
{
|
||||
PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();
|
||||
PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
|
||||
reloadPropertyChain();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
ShouldCancel Cancel = PersistUnsavedChanges(true);
|
||||
if (Cancel == ShouldCancel.Yes)
|
||||
return;
|
||||
|
||||
using (OpenFileDialog D = new OpenFileDialog())
|
||||
{
|
||||
D.Filter = "Clang Tidy files|.clang-tidy";
|
||||
D.CheckPathExists = true;
|
||||
D.CheckFileExists = true;
|
||||
|
||||
if (D.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
PropertyChain_.Clear();
|
||||
PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);
|
||||
textBox1.Text = D.FileName;
|
||||
reloadPropertyChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string DefaultText = "(Default)";
|
||||
private static readonly string BrowseText = "Browse for a file to edit its properties";
|
||||
|
||||
/// <summary>
|
||||
/// After a new configuration file is chosen, analyzes the directory hierarchy
|
||||
/// and finds all .clang-tidy files in the path, parses them and updates the
|
||||
/// PropertyGrid and quick-access LinkLabel control to reflect the new property
|
||||
/// chain.
|
||||
/// </summary>
|
||||
private void reloadPropertyChain()
|
||||
{
|
||||
StringBuilder LinkBuilder = new StringBuilder();
|
||||
LinkBuilder.Append(DefaultText);
|
||||
LinkBuilder.Append(" > ");
|
||||
int PrefixLength = LinkBuilder.Length;
|
||||
|
||||
if (PropertyChain_.Count == 1)
|
||||
LinkBuilder.Append(BrowseText);
|
||||
else
|
||||
LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);
|
||||
|
||||
linkLabelPath.Text = LinkBuilder.ToString();
|
||||
|
||||
// Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual
|
||||
// components of the path are clickable iff they contain a .clang-tidy file.
|
||||
// Clicking one of the links then updates the PropertyGrid to display the
|
||||
// selected .clang-tidy file.
|
||||
ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;
|
||||
linkLabelPath.Links.Clear();
|
||||
linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);
|
||||
foreach (var Prop in PropertyChain_.Skip(1))
|
||||
{
|
||||
LastProps = Prop.Value;
|
||||
string ClangTidyFolder = Path.GetFileName(Prop.Key);
|
||||
int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;
|
||||
linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);
|
||||
}
|
||||
propertyGrid1.SelectedObject = LastProps;
|
||||
}
|
||||
|
||||
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
|
||||
{
|
||||
ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;
|
||||
Props.SetHasUnsavedChanges(true);
|
||||
|
||||
// When a CategoryVerb is selected, perform the corresponding action.
|
||||
PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;
|
||||
if (!(e.ChangedItem.Value is CategoryVerb))
|
||||
return;
|
||||
|
||||
CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;
|
||||
if (Action == CategoryVerb.None)
|
||||
return;
|
||||
|
||||
var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();
|
||||
if (Category == null)
|
||||
return;
|
||||
var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });
|
||||
foreach (PropertyDescriptor P in SameCategoryProps)
|
||||
{
|
||||
if (P == Property)
|
||||
continue;
|
||||
switch (Action)
|
||||
{
|
||||
case CategoryVerb.Disable:
|
||||
P.SetValue(propertyGrid1.SelectedObject, false);
|
||||
break;
|
||||
case CategoryVerb.Enable:
|
||||
P.SetValue(propertyGrid1.SelectedObject, true);
|
||||
break;
|
||||
case CategoryVerb.Inherit:
|
||||
P.ResetValue(propertyGrid1.SelectedObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Property.ResetValue(propertyGrid1.SelectedObject);
|
||||
propertyGrid1.Invalidate();
|
||||
}
|
||||
|
||||
private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;
|
||||
propertyGrid1.SelectedObject = Props;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="clangTidyConfigurationPage1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>183, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
partial class DynamicPropertyComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
/// <summary>
|
||||
/// The goal of this class is to enable displaying of a PropertyGrid in much the
|
||||
/// same way that Visual Studio's C++ project system does. A project or file can
|
||||
/// have properties which might inherit from their parent, or be overridden.
|
||||
/// It turns out this is somewhat non-trivial. The .NET PropertyGrid is good makes
|
||||
/// displaying simple properties with a static notion of what constitutes a
|
||||
/// "default" value very easy. You simply apply an Attribute to the class that says
|
||||
/// what the default value is and you're done. But when you try to introduce the idea
|
||||
/// that a property's default value depends on some other factor, things get much more
|
||||
/// complicated due to the static nature of Attributes.
|
||||
///
|
||||
/// The solution to this is to inherit from ICustomTypeDescriptor. This is the mechanism
|
||||
/// by which you can inject or modify attributes or properties at runtime. The .NET
|
||||
/// PropertyGrid is designed in such a way that instead of using simple .NET Reflection to
|
||||
/// look for the properties and attributes on a class, it will invoke the methods of
|
||||
/// ICustomTypeDescriptor (if your type inherits from it), and ask those methods. Our
|
||||
/// implementation of ICustomTypeDescriptor works by waiting until the PropertyGrid requests
|
||||
/// PropertyDescriptors for each of the properties, and then "decorating" them with our
|
||||
/// own custom PropertyDescriptor implementation which understands the proeprty inheritance
|
||||
/// model we wish to implement.
|
||||
/// </summary>
|
||||
public partial class DynamicPropertyComponent : Component, ICustomTypeDescriptor
|
||||
{
|
||||
PropertyDescriptorCollection DynamicProperties_ = new PropertyDescriptorCollection(null);
|
||||
private DynamicPropertyComponent Parent_;
|
||||
|
||||
public DynamicPropertyComponent(DynamicPropertyComponent Parent)
|
||||
{
|
||||
Parent_ = Parent;
|
||||
}
|
||||
|
||||
public DynamicPropertyComponent(DynamicPropertyComponent Parent, IContainer container)
|
||||
{
|
||||
Parent_ = Parent;
|
||||
|
||||
container.Add(this);
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public AttributeCollection GetAttributes()
|
||||
{
|
||||
return TypeDescriptor.GetAttributes(GetType());
|
||||
}
|
||||
|
||||
public string GetClassName()
|
||||
{
|
||||
return TypeDescriptor.GetClassName(GetType());
|
||||
}
|
||||
|
||||
public string GetComponentName()
|
||||
{
|
||||
return TypeDescriptor.GetComponentName(GetType());
|
||||
}
|
||||
|
||||
public TypeConverter GetConverter()
|
||||
{
|
||||
return TypeDescriptor.GetConverter(GetType());
|
||||
}
|
||||
|
||||
public EventDescriptor GetDefaultEvent()
|
||||
{
|
||||
return TypeDescriptor.GetDefaultEvent(GetType());
|
||||
}
|
||||
|
||||
public PropertyDescriptor GetDefaultProperty()
|
||||
{
|
||||
return TypeDescriptor.GetDefaultProperty(GetType());
|
||||
}
|
||||
|
||||
public object GetEditor(Type editorBaseType)
|
||||
{
|
||||
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
|
||||
}
|
||||
|
||||
public EventDescriptorCollection GetEvents()
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType());
|
||||
}
|
||||
|
||||
public EventDescriptorCollection GetEvents(Attribute[] attributes)
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType(), attributes);
|
||||
}
|
||||
|
||||
public PropertyDescriptorCollection GetProperties()
|
||||
{
|
||||
return DynamicProperties_;
|
||||
}
|
||||
|
||||
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
|
||||
{
|
||||
var Props = DynamicProperties_.OfType<PropertyDescriptor>();
|
||||
var Filtered = Props.Where(x => x.Attributes.Contains(attributes)).ToArray();
|
||||
return new PropertyDescriptorCollection(Filtered);
|
||||
}
|
||||
|
||||
public object GetPropertyOwner(PropertyDescriptor pd)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void SetDynamicValue<T>(string Name, T Value)
|
||||
{
|
||||
Name = Name.Replace('-', '_');
|
||||
DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);
|
||||
Descriptor.SetValue(this, Value);
|
||||
}
|
||||
|
||||
public T GetDynamicValue<T>(string Name)
|
||||
{
|
||||
Name = Name.Replace('-', '_');
|
||||
DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);
|
||||
return (T)Descriptor.GetValue(this);
|
||||
}
|
||||
|
||||
protected void AddDynamicProperty<T>(string Name, Attribute[] Attributes)
|
||||
{
|
||||
Name = Name.Replace('-', '_');
|
||||
|
||||
// If we have a parent, find the corresponding PropertyDescriptor with the same
|
||||
// name from the parent.
|
||||
DynamicPropertyDescriptor<T> ParentDescriptor = null;
|
||||
if (Parent_ != null)
|
||||
ParentDescriptor = (DynamicPropertyDescriptor<T>)Parent_.GetProperties().Find(Name, false);
|
||||
|
||||
DynamicProperties_.Add(new DynamicPropertyDescriptor<T>(Name, ParentDescriptor, Name, Attributes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
class MagicInheritance
|
||||
{
|
||||
public static readonly string Value = "{3A27184D-1774-489B-9BB7-7191B8E8E622}";
|
||||
public static readonly string Text = "<Inherit from project or parent>";
|
||||
}
|
||||
|
||||
|
||||
class DynamicPropertyConverter<T> : TypeConverter
|
||||
{
|
||||
private DynamicPropertyDescriptor<T> Descriptor_;
|
||||
private TypeConverter Root_;
|
||||
|
||||
public DynamicPropertyConverter(DynamicPropertyDescriptor<T> Descriptor, TypeConverter Root)
|
||||
{
|
||||
Descriptor_ = Descriptor;
|
||||
Root_ = Root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there are specific values that can be chosen from a dropdown
|
||||
/// for this property. Regardless of whether standard values are supported for
|
||||
/// the underlying type, we always support standard values because we need to
|
||||
/// display the inheritance option.
|
||||
/// </summary>
|
||||
/// <returns>true</returns>
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the set of all standard values that can be chosen from a dropdown for this
|
||||
/// property. If the underlying type supports standard values, we want to include
|
||||
/// all those. Additionally, we want to display the option to inherit the value,
|
||||
/// but only if the value is not already inheriting.
|
||||
/// </summary>
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
||||
{
|
||||
List<object> Values = new List<object>();
|
||||
if (Root_.GetStandardValuesSupported(context))
|
||||
{
|
||||
StandardValuesCollection RootValues = Root_.GetStandardValues(context);
|
||||
Values.AddRange(RootValues.Cast<object>());
|
||||
}
|
||||
if (!Descriptor_.IsInheriting)
|
||||
Values.Add(MagicInheritance.Value);
|
||||
StandardValuesCollection Result = new StandardValuesCollection(Values);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this property can accept values other than those specified
|
||||
/// in the dropdown (for example by manually typing into the field).
|
||||
/// </summary>
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
||||
{
|
||||
// Although we add items to the dropdown list, we do not change whether or not
|
||||
// the set of values are exclusive. If the user could type into the field before
|
||||
// they still can. And if they couldn't before, they still can't.
|
||||
return Root_.GetStandardValuesExclusive(context);
|
||||
}
|
||||
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return Root_.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return Root_.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value.Equals(MagicInheritance.Value))
|
||||
return MagicInheritance.Text;
|
||||
return Root_.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value.GetType() == destinationType)
|
||||
return value;
|
||||
|
||||
return Root_.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
|
||||
{
|
||||
return Root_.CreateInstance(context, propertyValues);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Root_.Equals(obj);
|
||||
}
|
||||
|
||||
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return Root_.GetCreateInstanceSupported(context);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Root_.GetHashCode();
|
||||
}
|
||||
|
||||
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
|
||||
{
|
||||
return Root_.GetProperties(context, value, attributes);
|
||||
}
|
||||
|
||||
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return Root_.GetPropertiesSupported(context);
|
||||
}
|
||||
|
||||
public override bool IsValid(ITypeDescriptorContext context, object value)
|
||||
{
|
||||
return Root_.IsValid(context, value);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Root_.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
public class DynamicPropertyDescriptor<T> : PropertyDescriptor
|
||||
{
|
||||
T Value_;
|
||||
DynamicPropertyDescriptor<T> Parent_;
|
||||
bool IsInheriting_;
|
||||
object Component_;
|
||||
|
||||
public DynamicPropertyDescriptor(object Component, DynamicPropertyDescriptor<T> Parent, string Name, Attribute[] Attrs)
|
||||
: base(Name, Attrs)
|
||||
{
|
||||
foreach (DefaultValueAttribute Attr in Attrs.OfType<DefaultValueAttribute>())
|
||||
{
|
||||
Value_ = (T)Attr.Value;
|
||||
}
|
||||
Parent_ = Parent;
|
||||
IsInheriting_ = true;
|
||||
Component_ = Component;
|
||||
}
|
||||
|
||||
public bool IsInheriting { get { return IsInheriting_; } set { IsInheriting_ = value; } }
|
||||
public DynamicPropertyDescriptor<T> Parent { get { return Parent_; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this property's value should be considered "default" (e.g.
|
||||
/// displayed in bold in the property grid). Root properties are unmodifiable and
|
||||
/// always default. Non-root properties are default iff they are inheriting.
|
||||
/// That is to say, if a property is explicitly set to False, the property should
|
||||
/// be serialized even if the parent is also False. It would only not be serialized
|
||||
/// if the user had explicitly chosen to inherit it.
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
return (Parent_ != null) && !IsInheriting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value back to the default. For root properties, this essentially does
|
||||
/// nothing as they are read-only anyway. For non-root properties, this only means
|
||||
/// that the property is now inheriting.
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
IsInheriting_ = true;
|
||||
}
|
||||
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
// This is a bit of a trick. If the user chose the inheritance option from the
|
||||
// dropdown, we will try to set the value to that string. So look for that and
|
||||
// then just reset the value.
|
||||
if (value.Equals(MagicInheritance.Text))
|
||||
ResetValue(component);
|
||||
else
|
||||
{
|
||||
// By explicitly setting the value, this property is no longer inheriting,
|
||||
// even if the value the property is being set to is the same as that of
|
||||
// the parent.
|
||||
IsInheriting_ = false;
|
||||
Value_ = (T)value;
|
||||
}
|
||||
}
|
||||
|
||||
public override TypeConverter Converter
|
||||
{
|
||||
get
|
||||
{
|
||||
// We need to return a DynamicPropertyConverter<> that can deal with our requirement
|
||||
// to inject the inherit property option into the dropdown. But we still need to use
|
||||
// the "real" converter to do the actual work for the underlying type. Therefore,
|
||||
// we need to look for a TypeConverter<> attribute on the property, and if it is present
|
||||
// forward an instance of that converter to the DynamicPropertyConverter<>. Otherwise,
|
||||
// forward an instance of the default converter for type T to the DynamicPropertyConverter<>.
|
||||
TypeConverter UnderlyingConverter = null;
|
||||
var ConverterAttr = this.Attributes.OfType<TypeConverterAttribute>().LastOrDefault();
|
||||
if (ConverterAttr != null)
|
||||
{
|
||||
Type ConverterType = Type.GetType(ConverterAttr.ConverterTypeName);
|
||||
UnderlyingConverter = (TypeConverter)Activator.CreateInstance(ConverterType);
|
||||
}
|
||||
else
|
||||
UnderlyingConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
|
||||
return new DynamicPropertyConverter<T>(this, UnderlyingConverter);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Parent_ == null);
|
||||
}
|
||||
}
|
||||
|
||||
public override Type ComponentType
|
||||
{
|
||||
get
|
||||
{
|
||||
return Component_.GetType();
|
||||
}
|
||||
}
|
||||
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
// Return either this property's value or the parents value, depending on
|
||||
// whether or not this property is inheriting.
|
||||
if (IsInheriting_ && Parent != null)
|
||||
return Parent.GetValue(component);
|
||||
return Value_;
|
||||
}
|
||||
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
return !IsReadOnly;
|
||||
}
|
||||
|
||||
public override Type PropertyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
/// <summary>
|
||||
/// A decorator of sorts. Accepts a PropertyDescriptor to its constructor
|
||||
/// and forwards all calls to the underlying PropertyDescriptor. In this way
|
||||
/// we can inherit from ForwardingPropertyDescriptor and override only the
|
||||
/// few methods we need to customize the behavior of, while allowing the
|
||||
/// underlying PropertyDescriptor to do the real work.
|
||||
/// </summary>
|
||||
public abstract class ForwardingPropertyDescriptor : PropertyDescriptor
|
||||
{
|
||||
private readonly PropertyDescriptor root;
|
||||
protected PropertyDescriptor Root { get { return root; } }
|
||||
protected ForwardingPropertyDescriptor(PropertyDescriptor root)
|
||||
: base(root)
|
||||
{
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public override void AddValueChanged(object component, EventHandler handler)
|
||||
{
|
||||
root.AddValueChanged(component, handler);
|
||||
}
|
||||
|
||||
public override AttributeCollection Attributes
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.Attributes;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
return root.CanResetValue(component);
|
||||
}
|
||||
|
||||
public override string Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.Category;
|
||||
}
|
||||
}
|
||||
|
||||
public override Type ComponentType
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.ComponentType;
|
||||
}
|
||||
}
|
||||
|
||||
public override TypeConverter Converter
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.Converter;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.Description;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DesignTimeOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.DesignTimeOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public override string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return root.Equals(obj);
|
||||
}
|
||||
|
||||
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
|
||||
{
|
||||
return root.GetChildProperties(instance, filter);
|
||||
}
|
||||
|
||||
public override object GetEditor(Type editorBaseType)
|
||||
{
|
||||
return root.GetEditor(editorBaseType);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return root.GetHashCode();
|
||||
}
|
||||
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
return root.GetValue(component);
|
||||
}
|
||||
|
||||
public override bool IsBrowsable
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.IsBrowsable;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsLocalizable
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.IsLocalizable;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public override Type PropertyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.PropertyType;
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveValueChanged(object component, EventHandler handler)
|
||||
{
|
||||
root.RemoveValueChanged(component, handler);
|
||||
}
|
||||
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
root.ResetValue(component);
|
||||
}
|
||||
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
root.SetValue(component, value);
|
||||
}
|
||||
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
return root.ShouldSerializeValue(component);
|
||||
}
|
||||
|
||||
public override bool SupportsChangeEvents
|
||||
{
|
||||
get
|
||||
{
|
||||
return root.SupportsChangeEvents;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return root.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project. Project-level
|
||||
// suppressions either have no target or are given a specific target
|
||||
// and scoped to a namespace, type, member, etc.
|
||||
//
|
||||
// To add a suppression to this file, right-click the message in the
|
||||
// Error List, point to "Suppress Message(s)", and click "In Project
|
||||
// Suppression File". You do not need to add suppressions to this
|
||||
// file manually.
|
||||
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
static class GuidList
|
||||
{
|
||||
public const string guidClangTidyPkgString = "AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C";
|
||||
public const string guidClangTidyCmdSetString = "9E0F0493-6493-46DE-AEE1-ACD8F60F265E";
|
||||
|
||||
public static readonly Guid guidClangTidyCmdSet = new Guid(guidClangTidyCmdSetString);
|
||||
};
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
static class PkgCmdIDList
|
||||
{
|
||||
public const uint cmdidClangTidy = 0x100;
|
||||
};
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ClangFormat")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("LLVM")]
|
||||
[assembly: AssemblyProduct("ClangFormat")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: CLSCompliant(false)]
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
// FIXME: Add a way to have this generated automatically by CMake
|
||||
[assembly: AssemblyVersion("1.1.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
||||
@@ -1,81 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace LLVM.ClangTidy {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangTidy.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ---
|
||||
///Checks:
|
||||
///Checks:
|
||||
/// - Name: cert-dcl54-cpp
|
||||
/// Label: Overloaded allocation function pairs
|
||||
/// Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope
|
||||
/// Category: CERT Secure Coding Standards
|
||||
/// - Name: cppcoreguidelines-interfaces-global-init
|
||||
/// Label: I.22 - Complex Global Initializers
|
||||
/// Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global object [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string ClangTidyChecks {
|
||||
get {
|
||||
return ResourceManager.GetString("ClangTidyChecks", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="ClangTidyChecks" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\ClangTidyChecks.yaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,321 +0,0 @@
|
||||
---
|
||||
Checks:
|
||||
# This file should be updated when new checks are added, and eventually we should
|
||||
# generate this file automatically from the .rst files in clang-tidy.
|
||||
- Category: CERT Secure Coding Standards
|
||||
Label: Overloaded allocation function pairs
|
||||
Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope
|
||||
Name: cert-dcl54-cpp
|
||||
- Category: C++ Core Guidelines
|
||||
Label: I.22 - Complex Global Initializers
|
||||
Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global objects
|
||||
Name: cppcoreguidelines-interfaces-global-init
|
||||
- Category: CERT Secure Coding Standards
|
||||
Label: DCL50-CPP
|
||||
Description: Checks for violations of CERT DCL50-CPP - Do not define a C-style variadic function
|
||||
Name: cert-dcl50-cpp
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Bounds.1 - No pointer arithmetic
|
||||
Description: Checks for violations of Core Guideline Bounds.3 - Don't use pointer arithmetic. Use span<> instead.
|
||||
Name: cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Bounds.2 - Constant array indices
|
||||
Description: Checks for violations of Core Bounds.2 - Only index into arrays using constant expressions.
|
||||
Name: cppcoreguidelines-pro-bounds-constant-array-index
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Bounds.3 - Array to Pointer Decay
|
||||
Description: Checks for violations of Core Guideline Bounds.3 - No array-to-pointer decay
|
||||
Name: cppcoreguidelines-pro-bounds-array-to-pointer-decay
|
||||
- Category: C++ Core Guidelines
|
||||
Label: const_cast (Type.3)
|
||||
Description: Checks for violations of Core Guideline Type.3 - Don't use const_cast to cast away const
|
||||
Name: cppcoreguidelines-pro-type-const-cast
|
||||
- Category: C++ Core Guidelines
|
||||
Label: C style casts (Type.4)
|
||||
Description: Checks for violations of Core Guideline Type.3 - Don't use C-style (T)expression casts that would perform a static downcast, const_cast, or reinterpret_cast
|
||||
Name: cppcoreguidelines-pro-type-cstyle-cast
|
||||
- Category: C++ Core Guidelines
|
||||
Label: reinterpret_cast (Type.1)
|
||||
Description: Checks for violations of Core Guideline Type.1 - Don't use reinterpret_cast.
|
||||
Name: cppcoreguidelines-pro-type-reinterpret-cast
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Prefer dynamic_cast (Type.2)
|
||||
Description: Checks for violations of Core Guideline Type.2 - Don't use static_cast downcasts. Use dynamic_cast instead.
|
||||
Name: cppcoreguidelines-pro-type-static-cast-downcast
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Member variable initialization (Type.6)
|
||||
Description: Checks for violations of Core Guideline Type.6 - Always initialize a member variable.
|
||||
Name: cppcoreguidelines-pro-type-member-init
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Avoid unions (Type.7)
|
||||
Description: Checks for violations of Core Guideline Type.7 - Avoid accessing members of raw unions. Use variant instead.
|
||||
Name: cppcoreguidelines-pro-type-union-access
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Don't use varargs (Type.8)
|
||||
Description: Checks for violations of Core Guideline Type.8 - Avoid reading varargs or passing vararg arguments. Prefer variadic templates instead.
|
||||
Name: cppcoreguidelines-pro-type-vararg
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Don't slice (ES.63 & C.145)
|
||||
Description: Checks for violations of Core Guidelines ES.63 (Don't slice) and C.145 (Access polymorphic objects through pointers and references)
|
||||
Name: cppcoreguidelines-slicing
|
||||
- Category: C++ Core Guidelines
|
||||
Label: Detect unsafe special functions (C.21)
|
||||
Description: Checks for violations of Core Guidelines C.21 - If you define or =delete any default operation, define or =delete them all.
|
||||
Name: cppcoreguidelines-special-member-functions
|
||||
- Category: Google Style Guide
|
||||
Label: Forbid explicitly parameterized make_pair
|
||||
Description:
|
||||
Name: google-build-explicit-make-pair
|
||||
- Category: Google Style Guide
|
||||
Label: Anonymous namespace in headers
|
||||
Description:
|
||||
Name: google-build-namespaces
|
||||
- Category: Google Style Guide
|
||||
Label: Find using namespace directives
|
||||
Description:
|
||||
Name: google-build-using-namespace
|
||||
- Category: Google Style Guide
|
||||
Label: Default arguments in virtual methods
|
||||
Description:
|
||||
Name: google-default-arguments
|
||||
- Category: Google Style Guide
|
||||
Label: explicit constructors
|
||||
Description:
|
||||
Name: google-explicit-constructor
|
||||
- Category: Google Style Guide
|
||||
Label: Global namespace pollution in headers
|
||||
Description:
|
||||
Name: google-global-names-in-headers
|
||||
- Category: Google Style Guide
|
||||
Label: Braces around statements
|
||||
Description:
|
||||
Name: google-readability-braces-around-statements
|
||||
- Category: Google Style Guide
|
||||
Label: No C-style casts
|
||||
Description:
|
||||
Name: google-readability-casting
|
||||
- Category: Google Style Guide
|
||||
Label: Find large functions
|
||||
Description:
|
||||
Name: google-readability-function-size
|
||||
- Category: Google Style Guide
|
||||
Label: Namespace closing comments
|
||||
Description:
|
||||
Name: google-readability-namespace-comments
|
||||
- Category: Google Style Guide
|
||||
Label: Find unnecessary calls to .get()
|
||||
Description:
|
||||
Name: google-readability-redundant-smartptr-get
|
||||
- Category: Google Style Guide
|
||||
Label: Find noncomformant TODO comments
|
||||
Description:
|
||||
Name: google-readability-todo
|
||||
- Category: Google Style Guide
|
||||
Label: Find implementation-specific integral types
|
||||
Description:
|
||||
Name: google-runtime-int
|
||||
- Category: Google Style Guide
|
||||
Label: Find const string references
|
||||
Description:
|
||||
Name: google-runtime-member-string-references
|
||||
- Category: Google Style Guide
|
||||
Label: Find zero-length memsets
|
||||
Description:
|
||||
Name: google-runtime-memset
|
||||
- Category: Google Style Guide
|
||||
Label: Find overloads of operator&
|
||||
Description:
|
||||
Name: google-runtime-operator
|
||||
- Category: Google Style Guide
|
||||
Label: Check usage of non-const references
|
||||
Description:
|
||||
Name: google-runtime-references
|
||||
- Category: LLVM Style Guide
|
||||
Label: LLVM header guards
|
||||
Description:
|
||||
Name: llvm-header-guard
|
||||
- Category: LLVM Style Guide
|
||||
Label: LLVM include order
|
||||
Description:
|
||||
Name: llvm-include-order
|
||||
- Category: LLVM Style Guide
|
||||
Label: LLVM namespace comments
|
||||
Description:
|
||||
Name: llvm-namespace-comment
|
||||
- Category: LLVM Style Guide
|
||||
Label: Find local twines
|
||||
Description:
|
||||
Name: llvm-twine-local
|
||||
- Category: Clang Diagnostics
|
||||
Label: Warnings
|
||||
Description:
|
||||
Name: clang-diagnostic-warning
|
||||
- Category: Clang Diagnostics
|
||||
Label: Errors
|
||||
Description:
|
||||
Name: clang-diagnostic-error
|
||||
- Category: Clang Diagnostics
|
||||
Label: Unknown
|
||||
Description:
|
||||
Name: clang-diagnostic-unknown
|
||||
- Category: Miscellaneous
|
||||
Label: Validate argument comments
|
||||
Description:
|
||||
Name: misc-argument-comment
|
||||
- Category: Miscellaneous
|
||||
Label: Side effects in assert()
|
||||
Description:
|
||||
Name: misc-assert-side-effect
|
||||
- Category: Miscellaneous
|
||||
Label: bool / pointer implicit conversions
|
||||
Description:
|
||||
Name: misc-bool-pointer-implicit-conversion
|
||||
- Category: Miscellaneous
|
||||
Label: Dangling handles
|
||||
Description:
|
||||
Name: misc-dangling-handle
|
||||
- Category: Miscellaneous
|
||||
Label: Definitions in headers
|
||||
Description:
|
||||
Name: misc-definitions-in-headers
|
||||
- Category: Miscellaneous
|
||||
Label: Type mismatch in fold operations
|
||||
Description:
|
||||
Name: misc-fold-init-type
|
||||
- Category: Miscellaneous
|
||||
Label: Forward declaration namespace
|
||||
Description:
|
||||
Name: misc-forward-declaration-namespace
|
||||
- Category: Miscellaneous
|
||||
Label: Inaccurate erase
|
||||
Description:
|
||||
Name: misc-inaccurate-erase
|
||||
- Category: Miscellaneous
|
||||
Label: Incorrect rounding
|
||||
Description:
|
||||
Name: misc-incorrect-roundings
|
||||
- Category: Miscellaneous
|
||||
Label: Inefficient STL algorithms
|
||||
Description:
|
||||
Name: misc-inefficient-algorithm
|
||||
- Category: Miscellaneous
|
||||
Label: Macro parentheses
|
||||
Description:
|
||||
Name: misc-macro-parentheses
|
||||
- Category: Miscellaneous
|
||||
Label: Macro repeated side effects
|
||||
Description:
|
||||
Name: misc-macro-repeated-side-effects
|
||||
- Category: Miscellaneous
|
||||
Label: Misplaced const
|
||||
Description:
|
||||
Name: misc-misplaced-const
|
||||
- Category: Miscellaneous
|
||||
Label: Misplaced widening casts
|
||||
Description:
|
||||
Name: misc-misplaced-widening-cast
|
||||
- Category: Miscellaneous
|
||||
Label: Move constructor const arguments
|
||||
Description:
|
||||
Name: misc-move-const-arg
|
||||
- Category: Miscellaneous
|
||||
Label: Move constructor initialization
|
||||
Description:
|
||||
Name: misc-move-constructor-init
|
||||
- Category: Miscellaneous
|
||||
Label: Multi-statement macros
|
||||
Description:
|
||||
Name: misc-multiple-statement-macro
|
||||
- Category: Miscellaneous
|
||||
Label: Verify new / delete overloads
|
||||
Description:
|
||||
Name: misc-new-delete-overloads
|
||||
- Category: Miscellaneous
|
||||
Label: Ensure move constructors are noexcept
|
||||
Description:
|
||||
Name: misc-noexcept-move-constructor
|
||||
- Category: Miscellaneous
|
||||
Label: Copying of non-copyable objects
|
||||
Description:
|
||||
Name: misc-non-copyable-objects
|
||||
- Category: Miscellaneous
|
||||
Label: Find redundant expressions
|
||||
Description:
|
||||
Name: misc-redundant-expression
|
||||
- Category: Miscellaneous
|
||||
Label: sizeof() on stl containers
|
||||
Description:
|
||||
Name: misc-sizeof-container
|
||||
- Category: Miscellaneous
|
||||
Label: Suspicious sizeof() usage
|
||||
Description:
|
||||
Name: misc-sizeof-expression
|
||||
- Category: Miscellaneous
|
||||
Label: Replace assert with static_assert
|
||||
Description:
|
||||
Name: misc-static-assert
|
||||
- Category: Miscellaneous
|
||||
Label: Suspicious string constructor
|
||||
Description:
|
||||
Name: misc-string-constructor
|
||||
- Category: Miscellaneous
|
||||
Label: String integer assignment
|
||||
Description:
|
||||
Name: misc-string-integer-assignment
|
||||
- Category: Miscellaneous
|
||||
Label: String literal with embedded null
|
||||
Description:
|
||||
Name: misc-string-literal-with-embedded-nul
|
||||
- Category: Miscellaneous
|
||||
Label: Suspicious missing comma
|
||||
Description:
|
||||
Name: misc-suspicious-missing-comma
|
||||
- Category: Miscellaneous
|
||||
Label: Suspicious semicolon
|
||||
Description:
|
||||
Name: misc-suspicious-semicolon
|
||||
- Category: Miscellaneous
|
||||
Label: Suspicious string compare
|
||||
Description:
|
||||
Name: misc-suspicious-string-compare
|
||||
- Category: Miscellaneous
|
||||
Label: Swapped arguments
|
||||
Description:
|
||||
Name: misc-swapped-arguments
|
||||
- Category: Miscellaneous
|
||||
Label: Throw by value / catch by reference
|
||||
Description:
|
||||
Name: misc-throw-by-value-catch-by-reference
|
||||
- Category: Miscellaneous
|
||||
Label: Unconventional operator=()
|
||||
Description:
|
||||
Name: misc-unconventional-assign-operator
|
||||
- Category: Miscellaneous
|
||||
Label: Undelegated constructor
|
||||
Description:
|
||||
Name: misc-undelegated-constructor
|
||||
- Category: Miscellaneous
|
||||
Label: unique_ptr<> reset / release
|
||||
Description:
|
||||
Name: misc-uniqueptr-reset-release
|
||||
- Category: Miscellaneous
|
||||
Label: Unused Alias Decls
|
||||
Description:
|
||||
Name: misc-unused-alias-decls
|
||||
- Category: Miscellaneous
|
||||
Label: Unused Params
|
||||
Description:
|
||||
Name: misc-unused-parameters
|
||||
- Category: Miscellaneous
|
||||
Label: Unused Raii
|
||||
Description:
|
||||
Name: misc-unused-raii
|
||||
- Category: Miscellaneous
|
||||
Label: Unused Using Decls
|
||||
Description:
|
||||
Name: misc-unused-using-decls
|
||||
- Category: Miscellaneous
|
||||
Label: Virtual Near Miss
|
||||
Description:
|
||||
Name: misc-virtual-near-miss
|
||||
...
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LLVM.ClangTidy
|
||||
{
|
||||
static class Utility
|
||||
{
|
||||
public static IEnumerable<string> SplitPath(string FileOrDir)
|
||||
{
|
||||
string P = Path.GetDirectoryName(FileOrDir);
|
||||
do
|
||||
{
|
||||
yield return P;
|
||||
P = Path.GetDirectoryName(P);
|
||||
} while (P != null);
|
||||
}
|
||||
|
||||
public static bool HasClangTidyFile(string Folder)
|
||||
{
|
||||
string ClangTidy = Path.Combine(Folder, ".clang-tidy");
|
||||
return File.Exists(ClangTidy);
|
||||
}
|
||||
|
||||
public static bool MatchWildcardString(string Value, string Pattern)
|
||||
{
|
||||
string RE = Regex.Escape(Pattern).Replace(@"\*", ".*");
|
||||
return Regex.IsMatch(Value, RE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="110" xml:space="preserve">
|
||||
<value>ClangTidy</value>
|
||||
</data>
|
||||
<data name="112" xml:space="preserve">
|
||||
<value>Analyzes code by calling the clang-tidy executable.</value>
|
||||
</data>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,63 +0,0 @@
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
<none yet>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Brutal.Dev.StrongNameSigner" version="1.8.0" targetFramework="net45" />
|
||||
<package id="YamlDotNet" version="3.3.0" targetFramework="net45" />
|
||||
<package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
|
||||
<Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">
|
||||
<Name>ClangFormat</Name>
|
||||
<Author>LLVM</Author>
|
||||
<Version>4.0.0</Version>
|
||||
<Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>
|
||||
<Locale>1033</Locale>
|
||||
<MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>
|
||||
<License>license.txt</License>
|
||||
<InstalledByMsi>false</InstalledByMsi>
|
||||
<SupportedProducts>
|
||||
<VisualStudio Version="10.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="11.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="12.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="14.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
</SupportedProducts>
|
||||
<SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
|
||||
</Identifier>
|
||||
<References>
|
||||
<Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
|
||||
<Name>Visual Studio MPF</Name>
|
||||
</Reference>
|
||||
</References>
|
||||
<Content>
|
||||
<VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
|
||||
</Content>
|
||||
</Vsix>
|
||||
@@ -1,17 +0,0 @@
|
||||
This directory contains a VSPackage project to generate a Visual Studio extension
|
||||
for clang-tidy.
|
||||
|
||||
Build prerequisites are:
|
||||
- Visual Studio 2013 Professional
|
||||
- Visual Studio 2013 SDK
|
||||
- Visual Studio 2010 Professional (?)
|
||||
- Visual Studio 2010 SDK (?)
|
||||
|
||||
The extension is built using CMake by setting BUILD_CLANG_TIDY_VS_PLUGIN=ON
|
||||
when configuring a Clang build, and building the clang_tidy_vsix target.
|
||||
|
||||
The CMake build will copy clang-tidy.exe and LICENSE.TXT into the ClangTidy/
|
||||
directory so they can be bundled with the plug-in, as well as creating
|
||||
ClangTidy/source.extension.vsixmanifest. Once the plug-in has been built with
|
||||
CMake once, it can be built manually from the ClangTidy.sln solution in Visual
|
||||
Studio.
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
|
||||
<Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">
|
||||
<Name>ClangTidy</Name>
|
||||
<Author>LLVM</Author>
|
||||
<Version>@CLANG_TIDY_VS_VERSION@</Version>
|
||||
<Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>
|
||||
<Locale>1033</Locale>
|
||||
<MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>
|
||||
<License>license.txt</License>
|
||||
<InstalledByMsi>false</InstalledByMsi>
|
||||
<SupportedProducts>
|
||||
<VisualStudio Version="10.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="11.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="12.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
<VisualStudio Version="14.0">
|
||||
<Edition>Pro</Edition>
|
||||
</VisualStudio>
|
||||
</SupportedProducts>
|
||||
<SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
|
||||
</Identifier>
|
||||
<References>
|
||||
<Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
|
||||
<Name>Visual Studio MPF</Name>
|
||||
</Reference>
|
||||
</References>
|
||||
<Content>
|
||||
<VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
|
||||
</Content>
|
||||
</Vsix>
|
||||
@@ -15,7 +15,6 @@ add_clang_library(clangTidy
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangRewrite
|
||||
@@ -35,7 +34,6 @@ add_subdirectory(cppcoreguidelines)
|
||||
add_subdirectory(google)
|
||||
add_subdirectory(misc)
|
||||
add_subdirectory(modernize)
|
||||
add_subdirectory(mpi)
|
||||
add_subdirectory(performance)
|
||||
add_subdirectory(readability)
|
||||
add_subdirectory(utils)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Frontend/ASTConsumers.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
@@ -33,9 +32,7 @@
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Rewrite/Frontend/FixItRewriter.h"
|
||||
#include "clang/Rewrite/Frontend/FrontendActions.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
||||
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
|
||||
#include "clang/Tooling/DiagnosticsYaml.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/ReplacementsYaml.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
@@ -49,7 +46,7 @@ using namespace clang::driver;
|
||||
using namespace clang::tooling;
|
||||
using namespace llvm;
|
||||
|
||||
LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
|
||||
template class llvm::Registry<clang::tidy::ClangTidyModule>;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
@@ -57,6 +54,15 @@ namespace tidy {
|
||||
namespace {
|
||||
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
|
||||
|
||||
static const StringRef StaticAnalyzerChecks[] = {
|
||||
#define GET_CHECKERS
|
||||
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
|
||||
FULLNAME,
|
||||
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
|
||||
#undef CHECKER
|
||||
#undef GET_CHECKERS
|
||||
};
|
||||
|
||||
class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
|
||||
public:
|
||||
AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
|
||||
@@ -89,13 +95,14 @@ private:
|
||||
|
||||
class ErrorReporter {
|
||||
public:
|
||||
ErrorReporter(bool ApplyFixes, StringRef FormatStyle)
|
||||
ErrorReporter(bool ApplyFixes)
|
||||
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
|
||||
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
|
||||
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
|
||||
DiagPrinter),
|
||||
SourceMgr(Diags, Files), ApplyFixes(ApplyFixes), TotalFixes(0),
|
||||
AppliedFixes(0), WarningsAsErrors(0), FormatStyle(FormatStyle) {
|
||||
SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
|
||||
ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0),
|
||||
WarningsAsErrors(0) {
|
||||
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
|
||||
DiagPrinter->BeginSourceFile(LangOpts);
|
||||
}
|
||||
@@ -103,14 +110,14 @@ public:
|
||||
SourceManager &getSourceManager() { return SourceMgr; }
|
||||
|
||||
void reportDiagnostic(const ClangTidyError &Error) {
|
||||
const tooling::DiagnosticMessage &Message = Error.Message;
|
||||
const ClangTidyMessage &Message = Error.Message;
|
||||
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
|
||||
// Contains a pair for each attempted fix: location and whether the fix was
|
||||
// applied successfully.
|
||||
SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
|
||||
{
|
||||
auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
|
||||
std::string Name = Error.DiagnosticName;
|
||||
std::string Name = Error.CheckName;
|
||||
if (Error.IsWarningAsError) {
|
||||
Name += ",-warnings-as-errors";
|
||||
Level = DiagnosticsEngine::Error;
|
||||
@@ -118,59 +125,27 @@ public:
|
||||
}
|
||||
auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
|
||||
<< Message.Message << Name;
|
||||
for (const auto &FileAndReplacements : Error.Fix) {
|
||||
for (const auto &Repl : FileAndReplacements.second) {
|
||||
// Retrieve the source range for applicable fixes. Macro definitions
|
||||
// on the command line have locations in a virtual buffer and don't
|
||||
// have valid file paths and are therefore not applicable.
|
||||
SourceRange Range;
|
||||
SourceLocation FixLoc;
|
||||
++TotalFixes;
|
||||
bool CanBeApplied = false;
|
||||
if (Repl.isApplicable()) {
|
||||
SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
|
||||
Files.makeAbsolutePath(FixAbsoluteFilePath);
|
||||
if (ApplyFixes) {
|
||||
tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
|
||||
Repl.getLength(),
|
||||
Repl.getReplacementText());
|
||||
Replacements &Replacements = FileReplacements[R.getFilePath()];
|
||||
llvm::Error Err = Replacements.add(R);
|
||||
if (Err) {
|
||||
// FIXME: Implement better conflict handling.
|
||||
llvm::errs() << "Trying to resolve conflict: "
|
||||
<< llvm::toString(std::move(Err)) << "\n";
|
||||
unsigned NewOffset =
|
||||
Replacements.getShiftedCodePosition(R.getOffset());
|
||||
unsigned NewLength = Replacements.getShiftedCodePosition(
|
||||
R.getOffset() + R.getLength()) -
|
||||
NewOffset;
|
||||
if (NewLength == R.getLength()) {
|
||||
R = Replacement(R.getFilePath(), NewOffset, NewLength,
|
||||
R.getReplacementText());
|
||||
Replacements = Replacements.merge(tooling::Replacements(R));
|
||||
CanBeApplied = true;
|
||||
++AppliedFixes;
|
||||
} else {
|
||||
llvm::errs()
|
||||
<< "Can't resolve conflict, skipping the replacement.\n";
|
||||
}
|
||||
for (const tooling::Replacement &Fix : Error.Fix) {
|
||||
// Retrieve the source range for applicable fixes. Macro definitions
|
||||
// on the command line have locations in a virtual buffer and don't
|
||||
// have valid file paths and are therefore not applicable.
|
||||
SourceRange Range;
|
||||
SourceLocation FixLoc;
|
||||
if (Fix.isApplicable()) {
|
||||
SmallString<128> FixAbsoluteFilePath = Fix.getFilePath();
|
||||
Files.makeAbsolutePath(FixAbsoluteFilePath);
|
||||
FixLoc = getLocation(FixAbsoluteFilePath, Fix.getOffset());
|
||||
SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
|
||||
Range = SourceRange(FixLoc, FixEndLoc);
|
||||
Diag << FixItHint::CreateReplacement(Range, Fix.getReplacementText());
|
||||
}
|
||||
|
||||
} else {
|
||||
CanBeApplied = true;
|
||||
++AppliedFixes;
|
||||
}
|
||||
}
|
||||
FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
|
||||
SourceLocation FixEndLoc =
|
||||
FixLoc.getLocWithOffset(Repl.getLength());
|
||||
Range = SourceRange(FixLoc, FixEndLoc);
|
||||
Diag << FixItHint::CreateReplacement(Range,
|
||||
Repl.getReplacementText());
|
||||
}
|
||||
|
||||
if (ApplyFixes)
|
||||
FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
|
||||
++TotalFixes;
|
||||
if (ApplyFixes) {
|
||||
bool Success = Fix.isApplicable() && Fix.apply(Rewrite);
|
||||
if (Success)
|
||||
++AppliedFixes;
|
||||
FixLocations.push_back(std::make_pair(FixLoc, Success));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,43 +153,16 @@ public:
|
||||
Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
|
||||
: diag::note_fixit_failed);
|
||||
}
|
||||
for (const auto &Note : Error.Notes)
|
||||
for (const ClangTidyMessage &Note : Error.Notes)
|
||||
reportNote(Note);
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
// FIXME: Run clang-format on changes.
|
||||
if (ApplyFixes && TotalFixes > 0) {
|
||||
Rewriter Rewrite(SourceMgr, LangOpts);
|
||||
for (const auto &FileAndReplacements : FileReplacements) {
|
||||
StringRef File = FileAndReplacements.first();
|
||||
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
SourceMgr.getFileManager().getBufferForFile(File);
|
||||
if (!Buffer) {
|
||||
llvm::errs() << "Can't get buffer for file " << File << ": "
|
||||
<< Buffer.getError().message() << "\n";
|
||||
// FIXME: Maybe don't apply fixes for other files as well.
|
||||
continue;
|
||||
}
|
||||
StringRef Code = Buffer.get()->getBuffer();
|
||||
format::FormatStyle Style = format::getStyle("file", File, FormatStyle);
|
||||
llvm::Expected<Replacements> CleanReplacements =
|
||||
format::cleanupAroundReplacements(Code, FileAndReplacements.second,
|
||||
Style);
|
||||
if (!CleanReplacements) {
|
||||
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
|
||||
continue;
|
||||
}
|
||||
if (!tooling::applyAllReplacements(CleanReplacements.get(), Rewrite)) {
|
||||
llvm::errs() << "Can't apply replacements for file " << File << "\n";
|
||||
}
|
||||
}
|
||||
if (Rewrite.overwriteChangedFiles()) {
|
||||
llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
|
||||
} else {
|
||||
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
|
||||
<< TotalFixes << " suggested fixes.\n";
|
||||
}
|
||||
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
|
||||
<< TotalFixes << " suggested fixes.\n";
|
||||
Rewrite.overwriteChangedFiles();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +178,10 @@ private:
|
||||
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
|
||||
}
|
||||
|
||||
void reportNote(const tooling::DiagnosticMessage &Message) {
|
||||
void reportNote(const ClangTidyMessage &Message) {
|
||||
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
|
||||
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
|
||||
DiagnosticBuilder Diag =
|
||||
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
|
||||
<< Message.Message;
|
||||
}
|
||||
|
||||
@@ -242,12 +191,11 @@ private:
|
||||
DiagnosticConsumer *DiagPrinter;
|
||||
DiagnosticsEngine Diags;
|
||||
SourceManager SourceMgr;
|
||||
llvm::StringMap<Replacements> FileReplacements;
|
||||
Rewriter Rewrite;
|
||||
bool ApplyFixes;
|
||||
unsigned TotalFixes;
|
||||
unsigned AppliedFixes;
|
||||
unsigned WarningsAsErrors;
|
||||
StringRef FormatStyle;
|
||||
};
|
||||
|
||||
class ClangTidyASTConsumer : public MultiplexConsumer {
|
||||
@@ -287,36 +235,6 @@ static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::vector<std::pair<std::string, bool>> CheckersList;
|
||||
|
||||
static CheckersList getCheckersControlList(GlobList &Filter) {
|
||||
CheckersList List;
|
||||
|
||||
const auto &RegisteredCheckers =
|
||||
AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
|
||||
bool AnalyzerChecksEnabled = false;
|
||||
for (StringRef CheckName : RegisteredCheckers) {
|
||||
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
AnalyzerChecksEnabled |= Filter.contains(ClangTidyCheckName);
|
||||
}
|
||||
|
||||
if (!AnalyzerChecksEnabled)
|
||||
return List;
|
||||
|
||||
// List all static analyzer checkers that our filter enables.
|
||||
//
|
||||
// Always add all core checkers if any other static analyzer check is enabled.
|
||||
// This is currently necessary, as other path sensitive checks rely on the
|
||||
// core checkers.
|
||||
for (StringRef CheckName : RegisteredCheckers) {
|
||||
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
|
||||
if (CheckName.startswith("core") || Filter.contains(ClangTidyCheckName))
|
||||
List.emplace_back(CheckName, true);
|
||||
}
|
||||
return List;
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer>
|
||||
ClangTidyASTConsumerFactory::CreateASTConsumer(
|
||||
clang::CompilerInstance &Compiler, StringRef File) {
|
||||
@@ -400,6 +318,37 @@ ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
|
||||
return Options;
|
||||
}
|
||||
|
||||
ClangTidyASTConsumerFactory::CheckersList
|
||||
ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
|
||||
CheckersList List;
|
||||
|
||||
bool AnalyzerChecksEnabled = false;
|
||||
for (StringRef CheckName : StaticAnalyzerChecks) {
|
||||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
AnalyzerChecksEnabled =
|
||||
AnalyzerChecksEnabled ||
|
||||
(!CheckName.startswith("debug") && Filter.contains(Checker));
|
||||
}
|
||||
|
||||
if (AnalyzerChecksEnabled) {
|
||||
// Run our regex against all possible static analyzer checkers. Note that
|
||||
// debug checkers print values / run programs to visualize the CFG and are
|
||||
// thus not applicable to clang-tidy in general.
|
||||
//
|
||||
// Always add all core checkers if any other static analyzer checks are
|
||||
// enabled. This is currently necessary, as other path sensitive checks
|
||||
// rely on the core checkers.
|
||||
for (StringRef CheckName : StaticAnalyzerChecks) {
|
||||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
|
||||
if (CheckName.startswith("core") ||
|
||||
(!CheckName.startswith("debug") && Filter.contains(Checker)))
|
||||
List.push_back(std::make_pair(CheckName, true));
|
||||
}
|
||||
}
|
||||
return List;
|
||||
}
|
||||
|
||||
DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
|
||||
DiagnosticIDs::Level Level) {
|
||||
return Context->diag(CheckName, Loc, Message, Level);
|
||||
@@ -461,7 +410,7 @@ ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
|
||||
|
||||
ClangTidyStats
|
||||
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
const CompilationDatabase &Compilations,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
|
||||
ClangTool Tool(Compilations, InputFiles);
|
||||
@@ -471,14 +420,10 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
ArgumentsAdjuster PerFileExtraArgumentsInserter =
|
||||
[&Context](const CommandLineArguments &Args, StringRef Filename) {
|
||||
ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
|
||||
CommandLineArguments AdjustedArgs = Args;
|
||||
if (Opts.ExtraArgsBefore) {
|
||||
auto I = AdjustedArgs.begin();
|
||||
if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
|
||||
++I; // Skip compiler binary name, if it is there.
|
||||
AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
|
||||
Opts.ExtraArgsBefore->end());
|
||||
}
|
||||
CommandLineArguments AdjustedArgs;
|
||||
if (Opts.ExtraArgsBefore)
|
||||
AdjustedArgs = *Opts.ExtraArgsBefore;
|
||||
AdjustedArgs.insert(AdjustedArgs.begin(), Args.begin(), Args.end());
|
||||
if (Opts.ExtraArgs)
|
||||
AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
|
||||
Opts.ExtraArgs->end());
|
||||
@@ -538,8 +483,8 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
}
|
||||
|
||||
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
|
||||
StringRef FormatStyle, unsigned &WarningsAsErrorsCount) {
|
||||
ErrorReporter Reporter(Fix, FormatStyle);
|
||||
unsigned &WarningsAsErrorsCount) {
|
||||
ErrorReporter Reporter(Fix);
|
||||
vfs::FileSystem &FileSystem =
|
||||
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
|
||||
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
|
||||
@@ -562,18 +507,15 @@ void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
|
||||
WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
|
||||
}
|
||||
|
||||
void exportReplacements(const llvm::StringRef MainFilePath,
|
||||
const std::vector<ClangTidyError> &Errors,
|
||||
void exportReplacements(const std::vector<ClangTidyError> &Errors,
|
||||
raw_ostream &OS) {
|
||||
TranslationUnitDiagnostics TUD;
|
||||
TUD.MainSourceFile = MainFilePath;
|
||||
for (const auto &Error : Errors) {
|
||||
tooling::Diagnostic Diag = Error;
|
||||
TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
|
||||
}
|
||||
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 << TUD;
|
||||
YAML << TUR;
|
||||
}
|
||||
|
||||
} // namespace tidy
|
||||
|
||||
@@ -73,23 +73,6 @@ public:
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// \brief Read a named option from the ``Context`` and parse it as an
|
||||
/// integral type ``T``.
|
||||
///
|
||||
/// Reads the option with the check-local name \p LocalName from local or
|
||||
/// global ``CheckOptions``. Gets local option first. If local is not present,
|
||||
/// falls back to get global option. If global option is not present either,
|
||||
/// returns Default.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
getLocalOrGlobal(StringRef LocalName, T Default) const {
|
||||
std::string Value = getLocalOrGlobal(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,
|
||||
@@ -204,6 +187,9 @@ public:
|
||||
ClangTidyOptions::OptionMap getCheckOptions();
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::string, bool>> CheckersList;
|
||||
CheckersList getCheckersControlList(GlobList &Filter);
|
||||
|
||||
ClangTidyContext &Context;
|
||||
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
|
||||
};
|
||||
@@ -235,15 +221,13 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
// FIXME: Implement confidence levels for displaying/fixing errors.
|
||||
//
|
||||
/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
|
||||
/// Errors containing fixes are automatically applied and reformatted. If no
|
||||
/// clang-format configuration file is found, the given \P FormatStyle is used.
|
||||
/// Errors containing fixes are automatically applied.
|
||||
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
|
||||
StringRef FormatStyle, unsigned &WarningsAsErrorsCount);
|
||||
unsigned &WarningsAsErrorsCount);
|
||||
|
||||
/// \brief Serializes replacements into YAML and writes them to the specified
|
||||
/// output stream.
|
||||
void exportReplacements(StringRef MainFilePath,
|
||||
const std::vector<ClangTidyError> &Errors,
|
||||
void exportReplacements(const std::vector<ClangTidyError> &Errors,
|
||||
raw_ostream &OS);
|
||||
|
||||
} // end namespace tidy
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
|
||||
/// and ClangTidyError classes.
|
||||
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyMessage,
|
||||
/// ClangTidyContext and ClangTidyError classes.
|
||||
///
|
||||
/// This tool uses the Clang Tooling infrastructure, see
|
||||
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
|
||||
@@ -45,13 +45,13 @@ protected:
|
||||
// FIXME: Remove this once there's a better way to pass check names than
|
||||
// appending the check name to the message in ClangTidyContext::diag and
|
||||
// using getCustomDiagID.
|
||||
std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
|
||||
std::string CheckNameInMessage = " [" + Error.CheckName + "]";
|
||||
if (Message.endswith(CheckNameInMessage))
|
||||
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
|
||||
|
||||
auto TidyMessage = Loc.isValid()
|
||||
? tooling::DiagnosticMessage(Message, *SM, Loc)
|
||||
: tooling::DiagnosticMessage(Message);
|
||||
ClangTidyMessage TidyMessage = Loc.isValid()
|
||||
? ClangTidyMessage(Message, *SM, Loc)
|
||||
: ClangTidyMessage(Message);
|
||||
if (Level == DiagnosticsEngine::Note) {
|
||||
Error.Notes.push_back(TidyMessage);
|
||||
return;
|
||||
@@ -77,15 +77,7 @@ protected:
|
||||
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
|
||||
"Only file locations supported in fix-it hints.");
|
||||
|
||||
tooling::Replacement Replacement(SM, Range, FixIt.CodeToInsert);
|
||||
llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
|
||||
// FIXME: better error handling (at least, don't let other replacements be
|
||||
// applied).
|
||||
if (Err) {
|
||||
llvm::errs() << "Fix conflicts with existing fix! "
|
||||
<< llvm::toString(std::move(Err)) << "\n";
|
||||
assert(false && "Fix conflicts with existing fix!");
|
||||
}
|
||||
Error.Fix.insert(tooling::Replacement(SM, Range, FixIt.CodeToInsert));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +102,23 @@ private:
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
ClangTidyMessage::ClangTidyMessage(StringRef Message)
|
||||
: Message(Message), FileOffset(0) {}
|
||||
|
||||
ClangTidyMessage::ClangTidyMessage(StringRef Message,
|
||||
const SourceManager &Sources,
|
||||
SourceLocation Loc)
|
||||
: Message(Message) {
|
||||
assert(Loc.isValid() && Loc.isFileID());
|
||||
FilePath = Sources.getFilename(Loc);
|
||||
FileOffset = Sources.getFileOffset(Loc);
|
||||
}
|
||||
|
||||
ClangTidyError::ClangTidyError(StringRef CheckName,
|
||||
ClangTidyError::Level DiagLevel,
|
||||
StringRef BuildDirectory, bool IsWarningAsError)
|
||||
: tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
|
||||
bool IsWarningAsError,
|
||||
StringRef BuildDirectory)
|
||||
: CheckName(CheckName), BuildDirectory(BuildDirectory), DiagLevel(DiagLevel),
|
||||
IsWarningAsError(IsWarningAsError) {}
|
||||
|
||||
// Returns true if GlobList starts with the negative indicator ('-'), removes it
|
||||
@@ -171,7 +176,8 @@ DiagnosticBuilder ClangTidyContext::diag(
|
||||
assert(Loc.isValid());
|
||||
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
|
||||
Level, (Description + " [" + CheckName + "]").str());
|
||||
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
|
||||
if (CheckNamesByDiagnosticID.count(ID) == 0)
|
||||
CheckNamesByDiagnosticID.insert(std::make_pair(ID, CheckName.str()));
|
||||
return DiagEngine->Report(Loc, ID);
|
||||
}
|
||||
|
||||
@@ -237,7 +243,7 @@ StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
|
||||
|
||||
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
|
||||
: Context(Ctx), LastErrorRelatesToUserCode(false),
|
||||
LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
|
||||
LastErrorPassesLineFilter(false) {
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
Diags.reset(new DiagnosticsEngine(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
|
||||
@@ -248,7 +254,7 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
|
||||
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
||||
if (!Errors.empty()) {
|
||||
ClangTidyError &Error = Errors.back();
|
||||
if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
|
||||
if (!Context.getChecksFilter().contains(Error.CheckName) &&
|
||||
Error.DiagLevel != ClangTidyError::Error) {
|
||||
++Context.Stats.ErrorsIgnoredCheckFilter;
|
||||
Errors.pop_back();
|
||||
@@ -266,7 +272,7 @@ void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
||||
LastErrorPassesLineFilter = false;
|
||||
}
|
||||
|
||||
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
|
||||
static bool LineIsMarkedWithNOLINT(SourceManager& SM, SourceLocation Loc) {
|
||||
bool Invalid;
|
||||
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
|
||||
if (!Invalid) {
|
||||
@@ -282,34 +288,15 @@ static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
|
||||
SourceLocation Loc) {
|
||||
while (true) {
|
||||
if (LineIsMarkedWithNOLINT(SM, Loc))
|
||||
return true;
|
||||
if (!Loc.isMacroID())
|
||||
return false;
|
||||
Loc = SM.getImmediateExpansionRange(Loc).first;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
||||
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
|
||||
if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
|
||||
return;
|
||||
|
||||
if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
|
||||
if (Info.getLocation().isValid() &&
|
||||
DiagLevel != DiagnosticsEngine::Error &&
|
||||
DiagLevel != DiagnosticsEngine::Fatal &&
|
||||
LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
|
||||
Info.getLocation())) {
|
||||
LineIsMarkedWithNOLINT(Diags->getSourceManager(), Info.getLocation())) {
|
||||
++Context.Stats.ErrorsIgnoredNOLINT;
|
||||
// Ignored a warning, should ignore related notes as well
|
||||
LastErrorWasIgnored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LastErrorWasIgnored = false;
|
||||
// Count warnings/errors.
|
||||
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
|
||||
|
||||
@@ -354,8 +341,8 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
||||
bool IsWarningAsError =
|
||||
DiagLevel == DiagnosticsEngine::Warning &&
|
||||
Context.getWarningAsErrorFilter().contains(CheckName);
|
||||
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
|
||||
IsWarningAsError);
|
||||
Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError,
|
||||
Context.getCurrentBuildDirectory()));
|
||||
}
|
||||
|
||||
ClangTidyDiagnosticRenderer Converter(
|
||||
@@ -440,7 +427,7 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
// replacements. To detect overlapping replacements, we use a sweep line
|
||||
// algorithm over these sets of intervals.
|
||||
// An event here consists of the opening or closing of an interval. During the
|
||||
// process, we maintain a counter with the amount of open intervals. If we
|
||||
// proccess, we maintain a counter with the amount of open intervals. If we
|
||||
// find an endpoint of an interval and this counter is different from 0, it
|
||||
// means that this interval overlaps with another one, so we set it as
|
||||
// inapplicable.
|
||||
@@ -462,7 +449,7 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
// priority than begin events.
|
||||
//
|
||||
// * If we have several begin points at the same position, we will mark as
|
||||
// inapplicable the ones that we process later, so the first one has to
|
||||
// inapplicable the ones that we proccess later, so the first one has to
|
||||
// be the one with the latest end point, because this one will contain
|
||||
// all the other intervals. For the same reason, if we have several end
|
||||
// points in the same position, the last one has to be the one with the
|
||||
@@ -470,14 +457,14 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
// position of the complementary.
|
||||
//
|
||||
// * In case of two equal intervals, the one whose error is bigger can
|
||||
// potentially contain the other one, so we want to process its begin
|
||||
// potentially contain the other one, so we want to proccess its begin
|
||||
// points before and its end points later.
|
||||
//
|
||||
// * Finally, if we have two equal intervals whose errors have the same
|
||||
// size, none of them will be strictly contained inside the other.
|
||||
// Sorting by ErrorId will guarantee that the begin point of the first
|
||||
// one will be processed before, disallowing the second one, and the
|
||||
// end point of the first one will also be processed before,
|
||||
// one will be proccessed before, disallowing the second one, and the
|
||||
// end point of the first one will also be proccessed before,
|
||||
// disallowing the first one.
|
||||
if (Type == ET_Begin)
|
||||
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
|
||||
@@ -502,28 +489,25 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
std::vector<int> Sizes;
|
||||
for (const auto &Error : Errors) {
|
||||
int Size = 0;
|
||||
for (const auto &FileAndReplaces : Error.Fix) {
|
||||
for (const auto &Replace : FileAndReplaces.second)
|
||||
Size += Replace.getLength();
|
||||
}
|
||||
for (const auto &Replace : Error.Fix)
|
||||
Size += Replace.getLength();
|
||||
Sizes.push_back(Size);
|
||||
}
|
||||
|
||||
// Build events from error intervals.
|
||||
std::map<std::string, std::vector<Event>> FileEvents;
|
||||
for (unsigned I = 0; I < Errors.size(); ++I) {
|
||||
for (const auto &FileAndReplace : Errors[I].Fix) {
|
||||
for (const auto &Replace : FileAndReplace.second) {
|
||||
unsigned Begin = Replace.getOffset();
|
||||
unsigned End = Begin + Replace.getLength();
|
||||
const std::string &FilePath = Replace.getFilePath();
|
||||
// FIXME: Handle empty intervals, such as those from insertions.
|
||||
if (Begin == End)
|
||||
continue;
|
||||
auto &Events = FileEvents[FilePath];
|
||||
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
|
||||
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
|
||||
}
|
||||
for (const auto &Replace : Errors[I].Fix) {
|
||||
unsigned Begin = Replace.getOffset();
|
||||
unsigned End = Begin + Replace.getLength();
|
||||
const std::string &FilePath = Replace.getFilePath();
|
||||
// FIXME: Handle empty intervals, such as those from insertions.
|
||||
if (Begin == End)
|
||||
continue;
|
||||
FileEvents[FilePath].push_back(
|
||||
Event(Begin, End, Event::ET_Begin, I, Sizes[I]));
|
||||
FileEvents[FilePath].push_back(
|
||||
Event(Begin, End, Event::ET_End, I, Sizes[I]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,8 +533,9 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
for (unsigned I = 0; I < Errors.size(); ++I) {
|
||||
if (!Apply[I]) {
|
||||
Errors[I].Fix.clear();
|
||||
Errors[I].Notes.emplace_back(
|
||||
"this fix will not be applied because it overlaps with another fix");
|
||||
Errors[I].Notes.push_back(
|
||||
ClangTidyMessage("this fix will not be applied because"
|
||||
" it overlaps with another fix"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,8 +543,8 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
|
||||
namespace {
|
||||
struct LessClangTidyError {
|
||||
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
|
||||
const tooling::DiagnosticMessage &M1 = LHS.Message;
|
||||
const tooling::DiagnosticMessage &M2 = RHS.Message;
|
||||
const ClangTidyMessage &M1 = LHS.Message;
|
||||
const ClangTidyMessage &M2 = RHS.Message;
|
||||
|
||||
return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
|
||||
std::tie(M2.FilePath, M2.FileOffset, M2.Message);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "ClangTidyOptions.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Tooling/Core/Diagnostic.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
@@ -33,6 +32,18 @@ class CompilationDatabase;
|
||||
|
||||
namespace tidy {
|
||||
|
||||
/// \brief A message from a clang-tidy check.
|
||||
///
|
||||
/// Note that this is independent of a \c SourceManager.
|
||||
struct ClangTidyMessage {
|
||||
ClangTidyMessage(StringRef Message = "");
|
||||
ClangTidyMessage(StringRef Message, const SourceManager &Sources,
|
||||
SourceLocation Loc);
|
||||
std::string Message;
|
||||
std::string FilePath;
|
||||
unsigned FileOffset;
|
||||
};
|
||||
|
||||
/// \brief A detected error complete with information to display diagnostic and
|
||||
/// automatic fix.
|
||||
///
|
||||
@@ -40,10 +51,30 @@ namespace tidy {
|
||||
/// dependency on a SourceManager.
|
||||
///
|
||||
/// FIXME: Make Diagnostics flexible enough to support this directly.
|
||||
struct ClangTidyError : tooling::Diagnostic {
|
||||
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
|
||||
bool IsWarningAsError);
|
||||
struct ClangTidyError {
|
||||
enum Level {
|
||||
Warning = DiagnosticsEngine::Warning,
|
||||
Error = DiagnosticsEngine::Error
|
||||
};
|
||||
|
||||
ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError,
|
||||
StringRef BuildDirectory);
|
||||
|
||||
std::string CheckName;
|
||||
ClangTidyMessage Message;
|
||||
tooling::Replacements Fix;
|
||||
SmallVector<ClangTidyMessage, 1> Notes;
|
||||
|
||||
// A build directory of the diagnostic source file.
|
||||
//
|
||||
// It's an absolute path which is `directory` field of the source file in
|
||||
// compilation database. If users don't specify the compilation database
|
||||
// directory, it is the current directory where clang-tidy runs.
|
||||
//
|
||||
// Note: it is empty in unittest.
|
||||
std::string BuildDirectory;
|
||||
|
||||
Level DiagLevel;
|
||||
bool IsWarningAsError;
|
||||
};
|
||||
|
||||
@@ -254,7 +285,6 @@ private:
|
||||
std::unique_ptr<llvm::Regex> HeaderFilter;
|
||||
bool LastErrorRelatesToUserCode;
|
||||
bool LastErrorPassesLineFilter;
|
||||
bool LastErrorWasIgnored;
|
||||
};
|
||||
|
||||
} // end namespace tidy
|
||||
|
||||
@@ -26,9 +26,8 @@ namespace tidy {
|
||||
/// this object.
|
||||
class ClangTidyCheckFactories {
|
||||
public:
|
||||
typedef std::function<ClangTidyCheck *(StringRef Name,
|
||||
ClangTidyContext *Context)>
|
||||
CheckFactory;
|
||||
typedef std::function<ClangTidyCheck *(
|
||||
StringRef Name, ClangTidyContext *Context)> CheckFactory;
|
||||
|
||||
/// \brief Registers check \p Factory with name \p Name.
|
||||
///
|
||||
@@ -59,8 +58,8 @@ public:
|
||||
template <typename CheckType> void registerCheck(StringRef CheckName) {
|
||||
registerCheckFactory(CheckName,
|
||||
[](StringRef Name, ClangTidyContext *Context) {
|
||||
return new CheckType(Name, Context);
|
||||
});
|
||||
return new CheckType(Name, Context);
|
||||
});
|
||||
}
|
||||
|
||||
/// \brief Create instances of all checks matching \p CheckRegexString and
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "ClangTidyModule.h"
|
||||
#include "llvm/Support/Registry.h"
|
||||
|
||||
extern template class llvm::Registry<clang::tidy::ClangTidyModule>;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
|
||||
|
||||
@@ -117,40 +117,34 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
|
||||
return Options;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
|
||||
if (Src) {
|
||||
if (Dest)
|
||||
Dest->insert(Dest->end(), Src->begin(), Src->end());
|
||||
else
|
||||
Dest = Src;
|
||||
}
|
||||
}
|
||||
|
||||
static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
|
||||
const Optional<std::string> &Src) {
|
||||
if (Src)
|
||||
Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
|
||||
if (Src)
|
||||
Dest = Src;
|
||||
}
|
||||
|
||||
ClangTidyOptions
|
||||
ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
|
||||
ClangTidyOptions Result = *this;
|
||||
|
||||
mergeCommaSeparatedLists(Result.Checks, Other.Checks);
|
||||
mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
|
||||
overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
|
||||
overrideValue(Result.SystemHeaders, Other.SystemHeaders);
|
||||
overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
|
||||
overrideValue(Result.User, Other.User);
|
||||
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
|
||||
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
|
||||
// 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.WarningsAsErrors)
|
||||
Result.WarningsAsErrors =
|
||||
(Result.WarningsAsErrors && !Result.WarningsAsErrors->empty()
|
||||
? *Result.WarningsAsErrors + ","
|
||||
: "") +
|
||||
*Other.WarningsAsErrors;
|
||||
|
||||
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;
|
||||
if (Other.ExtraArgs)
|
||||
Result.ExtraArgs = Other.ExtraArgs;
|
||||
if (Other.ExtraArgsBefore)
|
||||
Result.ExtraArgsBefore = Other.ExtraArgsBefore;
|
||||
|
||||
for (const auto &KeyValue : Other.CheckOptions)
|
||||
Result.CheckOptions[KeyValue.first] = KeyValue.second;
|
||||
@@ -215,7 +209,8 @@ FileOptionsProvider::FileOptionsProvider(
|
||||
const ClangTidyOptions &OverrideOptions,
|
||||
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
|
||||
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
|
||||
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
|
||||
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
|
||||
}
|
||||
|
||||
// FIXME: This method has some common logic with clang::format::getStyle().
|
||||
// Consider pulling out common bits to a findParentFileWithName function or
|
||||
|
||||
@@ -173,8 +173,7 @@ 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;
|
||||
llvm::StringRef)>> ConfigFileHandler;
|
||||
|
||||
/// \brief Configuration file handlers listed in the order of priority.
|
||||
///
|
||||
|
||||
@@ -226,13 +226,8 @@ def update_checks_list(clang_tidy_path):
|
||||
def format_link(doc_file):
|
||||
check_name = doc_file.replace('.rst', '')
|
||||
with open(os.path.join(docs_dir, doc_file), 'r') as doc:
|
||||
content = doc.read()
|
||||
match = re.search('.*:orphan:.*', content)
|
||||
if match:
|
||||
return ''
|
||||
|
||||
match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
|
||||
content)
|
||||
doc.read())
|
||||
if match:
|
||||
return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
|
||||
'check': check_name,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
|
||||
#include "CommandProcessorCheck.h"
|
||||
#include "FloatLoopCounter.h"
|
||||
#include "LimitedRandomnessCheck.h"
|
||||
#include "SetLongJmpCheck.h"
|
||||
#include "StaticObjectExceptionCheck.h"
|
||||
#include "StrToNumCheck.h"
|
||||
@@ -34,7 +33,8 @@ public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
// C++ checkers
|
||||
// DCL
|
||||
CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
|
||||
CheckFactories.registerCheck<VariadicFunctionDefCheck>(
|
||||
"cert-dcl50-cpp");
|
||||
CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
|
||||
"cert-dcl54-cpp");
|
||||
CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
|
||||
@@ -43,29 +43,36 @@ public:
|
||||
CheckFactories.registerCheck<misc::MoveConstructorInitCheck>(
|
||||
"cert-oop11-cpp");
|
||||
// ERR
|
||||
CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
|
||||
"cert-err09-cpp");
|
||||
CheckFactories.registerCheck<SetLongJmpCheck>("cert-err52-cpp");
|
||||
CheckFactories.registerCheck<StaticObjectExceptionCheck>("cert-err58-cpp");
|
||||
CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
|
||||
CheckFactories.registerCheck<SetLongJmpCheck>(
|
||||
"cert-err52-cpp");
|
||||
CheckFactories.registerCheck<StaticObjectExceptionCheck>(
|
||||
"cert-err58-cpp");
|
||||
CheckFactories.registerCheck<ThrownExceptionTypeCheck>(
|
||||
"cert-err60-cpp");
|
||||
CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
|
||||
"cert-err61-cpp");
|
||||
// MSC
|
||||
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
|
||||
|
||||
// C checkers
|
||||
// DCL
|
||||
CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c");
|
||||
CheckFactories.registerCheck<misc::StaticAssertCheck>(
|
||||
"cert-dcl03-c");
|
||||
// ENV
|
||||
CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
|
||||
CheckFactories.registerCheck<CommandProcessorCheck>(
|
||||
"cert-env33-c");
|
||||
// FLP
|
||||
CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
|
||||
CheckFactories.registerCheck<FloatLoopCounter>(
|
||||
"cert-flp30-c");
|
||||
// FIO
|
||||
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
|
||||
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>(
|
||||
"cert-fio38-c");
|
||||
// ERR
|
||||
CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
|
||||
// MSC
|
||||
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
|
||||
CheckFactories.registerCheck<StrToNumCheck>(
|
||||
"cert-err34-c");
|
||||
}
|
||||
ClangTidyOptions getModuleOptions() override {
|
||||
ClangTidyOptions Options;
|
||||
Options.CheckOptions["cert-oop11-cpp.UseCERTSemantics"] = "1";
|
||||
return Options;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,8 +80,8 @@ public:
|
||||
|
||||
// Register the MiscTidyModule using this statically initialized variable.
|
||||
static ClangTidyModuleRegistry::Add<cert::CERTModule>
|
||||
X("cert-module",
|
||||
"Adds lint checks corresponding to CERT secure coding guidelines.");
|
||||
X("cert-module",
|
||||
"Adds lint checks corresponding to CERT secure coding guidelines.");
|
||||
|
||||
// This anchor is used to force the linker to link in the generated object file
|
||||
// and thus register the CERTModule.
|
||||
|
||||
@@ -4,7 +4,6 @@ add_clang_library(clangTidyCERTModule
|
||||
CERTTidyModule.cpp
|
||||
CommandProcessorCheck.cpp
|
||||
FloatLoopCounter.cpp
|
||||
LimitedRandomnessCheck.cpp
|
||||
SetLongJmpCheck.cpp
|
||||
StaticObjectExceptionCheck.cpp
|
||||
StrToNumCheck.cpp
|
||||
|
||||
@@ -7,7 +7,7 @@ additions:
|
||||
Any file referencing a CERT Secure Coding guideline:
|
||||
Please allow this letter to serve as confirmation that open source projects on
|
||||
http://llvm.org are permitted to link via hypertext to the CERT(R) secure coding
|
||||
guidelines available at https://www.securecoding.cert.org.
|
||||
guidelines available at https://www.securecoding.cert.org.
|
||||
|
||||
The foregoing is permitted by the Terms of Use as follows:
|
||||
"Linking to the Service
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
//===--- LimitedRandomnessCheck.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 "LimitedRandomnessCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
void LimitedRandomnessCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(namedDecl(hasName("::rand")),
|
||||
parameterCountIs(0))))
|
||||
.bind("randomGenerator"),
|
||||
this);
|
||||
}
|
||||
|
||||
void LimitedRandomnessCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
std::string msg = "";
|
||||
if (getLangOpts().CPlusPlus)
|
||||
msg = "; use C++11 random library instead";
|
||||
|
||||
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("randomGenerator");
|
||||
diag(MatchedDecl->getLocStart(), "rand() has limited randomness" + msg);
|
||||
}
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,38 +0,0 @@
|
||||
//===--- LimitedRandomnessCheck.h - clang-tidy-------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cert {
|
||||
|
||||
/// Pseudorandom number generators are not genuinely random. The result of the
|
||||
/// std::rand() function makes no guarantees as to the quality of the random
|
||||
/// sequence produced.
|
||||
/// This check warns for the usage of std::rand() function.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc50-cpp.html
|
||||
class LimitedRandomnessCheck : public ClangTidyCheck {
|
||||
public:
|
||||
LimitedRandomnessCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace cert
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
|
||||
@@ -22,37 +22,27 @@ void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
// Match any static or thread_local variable declaration that has an
|
||||
// initializer that can throw.
|
||||
// Match any static or thread_local variable declaration that is initialized
|
||||
// with a constructor that can throw.
|
||||
Finder->addMatcher(
|
||||
varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
|
||||
unless(hasAncestor(functionDecl())),
|
||||
anyOf(hasDescendant(cxxConstructExpr(hasDeclaration(
|
||||
cxxConstructorDecl(unless(isNoThrow())).bind("func")))),
|
||||
hasDescendant(cxxNewExpr(hasDeclaration(
|
||||
functionDecl(unless(isNoThrow())).bind("func")))),
|
||||
hasDescendant(callExpr(hasDeclaration(
|
||||
functionDecl(unless(isNoThrow())).bind("func"))))))
|
||||
hasInitializer(cxxConstructExpr(hasDeclaration(
|
||||
cxxConstructorDecl(unless(isNoThrow()))
|
||||
.bind("ctor")))))
|
||||
.bind("var"),
|
||||
this);
|
||||
}
|
||||
|
||||
void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
|
||||
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
|
||||
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
|
||||
|
||||
diag(VD->getLocation(),
|
||||
"initialization of %0 with %select{static|thread_local}1 storage "
|
||||
"construction of %0 with %select{static|thread_local}1 storage "
|
||||
"duration may throw an exception that cannot be caught")
|
||||
<< VD << (VD->getStorageDuration() == SD_Static ? 0 : 1);
|
||||
|
||||
SourceLocation FuncLocation = Func->getLocation();
|
||||
if (FuncLocation.isValid()) {
|
||||
diag(FuncLocation,
|
||||
"possibly throwing %select{constructor|function}0 declared here",
|
||||
DiagnosticIDs::Note)
|
||||
<< (isa<CXXConstructorDecl>(Func) ? 0 : 1);
|
||||
}
|
||||
diag(Ctor->getLocation(), "possibly throwing constructor declared here",
|
||||
DiagnosticIDs::Note);
|
||||
}
|
||||
|
||||
} // namespace cert
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "StrToNumCheck.h"
|
||||
#include "clang/Analysis/Analyses/FormatString.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Analysis/Analyses/FormatString.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include <cassert>
|
||||
|
||||
@@ -127,7 +127,7 @@ ConversionKind ClassifyFormatString(StringRef Fmt, const LangOptions &LO,
|
||||
|
||||
Handler H;
|
||||
analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
|
||||
|
||||
|
||||
return H.get();
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
|
||||
// Formatted input functions need further checking of the format string to
|
||||
// determine whether a problematic conversion may be happening.
|
||||
Conversion = ClassifyFormatString(FmtStr, getLangOpts(),
|
||||
Conversion = ClassifyFormatString(FmtStr, Result.Context->getLangOpts(),
|
||||
Result.Context->getTargetInfo());
|
||||
if (Conversion != ConversionKind::None)
|
||||
FuncDecl = FFD;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace cert {
|
||||
class ThrownExceptionTypeCheck : public ClangTidyCheck {
|
||||
public:
|
||||
ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support)
|
||||
add_clang_library(clangTidyCppCoreGuidelinesModule
|
||||
CppCoreGuidelinesTidyModule.cpp
|
||||
InterfacesGlobalInitCheck.cpp
|
||||
NoMallocCheck.cpp
|
||||
ProBoundsArrayToPointerDecayCheck.cpp
|
||||
ProBoundsConstantArrayIndexCheck.cpp
|
||||
ProBoundsPointerArithmeticCheck.cpp
|
||||
@@ -14,8 +13,6 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
|
||||
ProTypeStaticCastDowncastCheck.cpp
|
||||
ProTypeUnionAccessCheck.cpp
|
||||
ProTypeVarargCheck.cpp
|
||||
SpecialMemberFunctionsCheck.cpp
|
||||
SlicingCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "../misc/UnconventionalAssignOperatorCheck.h"
|
||||
#include "InterfacesGlobalInitCheck.h"
|
||||
#include "NoMallocCheck.h"
|
||||
#include "ProBoundsArrayToPointerDecayCheck.h"
|
||||
#include "ProBoundsConstantArrayIndexCheck.h"
|
||||
#include "ProBoundsPointerArithmeticCheck.h"
|
||||
@@ -23,8 +22,6 @@
|
||||
#include "ProTypeStaticCastDowncastCheck.h"
|
||||
#include "ProTypeUnionAccessCheck.h"
|
||||
#include "ProTypeVarargCheck.h"
|
||||
#include "SlicingCheck.h"
|
||||
#include "SpecialMemberFunctionsCheck.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
@@ -36,7 +33,6 @@ public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
|
||||
"cppcoreguidelines-interfaces-global-init");
|
||||
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
|
||||
CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
|
||||
"cppcoreguidelines-pro-bounds-array-to-pointer-decay");
|
||||
CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
|
||||
@@ -57,9 +53,6 @@ public:
|
||||
"cppcoreguidelines-pro-type-union-access");
|
||||
CheckFactories.registerCheck<ProTypeVarargCheck>(
|
||||
"cppcoreguidelines-pro-type-vararg");
|
||||
CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
|
||||
"cppcoreguidelines-special-member-functions");
|
||||
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
|
||||
CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
|
||||
"cppcoreguidelines-c-copy-assignment-signature");
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
//===--- NoMallocCheck.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 "NoMallocCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cppcoreguidelines {
|
||||
|
||||
void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// C-style memory management is only problematic in C++.
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
// Registering malloc, will suggest RAII.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc"))))
|
||||
.bind("aquisition"),
|
||||
this);
|
||||
|
||||
// Registering realloc calls, suggest std::vector or std::string.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"),
|
||||
this);
|
||||
|
||||
// Registering free calls, will suggest RAII instead.
|
||||
Finder->addMatcher(
|
||||
callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this);
|
||||
}
|
||||
|
||||
void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const CallExpr *Call = nullptr;
|
||||
StringRef Recommendation;
|
||||
|
||||
if ((Call = Result.Nodes.getNodeAs<CallExpr>("aquisition")))
|
||||
Recommendation = "consider a container or a smart pointer";
|
||||
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
|
||||
Recommendation = "consider std::vector or std::string";
|
||||
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("free")))
|
||||
Recommendation = "use RAII";
|
||||
|
||||
assert(Call && "Unhandled binding in the Matcher");
|
||||
|
||||
diag(Call->getLocStart(), "do not manage memory manually; %0")
|
||||
<< Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd());
|
||||
}
|
||||
|
||||
} // namespace cppcoreguidelines
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
@@ -1,44 +0,0 @@
|
||||
//===--- NoMallocCheck.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_CPPCOREGUIDELINES_NO_MALLOC_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cppcoreguidelines {
|
||||
|
||||
/// This checker is concerned with C-style memory management and suggest modern
|
||||
/// alternatives to it.
|
||||
/// The check is only enabled in C++. For analyzing malloc calls see Clang
|
||||
/// Static Analyzer - unix.Malloc.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
|
||||
class NoMallocCheck : public ClangTidyCheck {
|
||||
public:
|
||||
NoMallocCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
|
||||
/// Registering for malloc, calloc, realloc and free calls.
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
|
||||
/// Checks matched function calls and gives suggestion to modernize the code.
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace cppcoreguidelines
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace cppcoreguidelines {
|
||||
|
||||
|
||||
/// This check flags all array to pointer decays
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
|
||||
@@ -45,14 +45,11 @@ void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
// Note: if a struct contains an array member, the compiler-generated
|
||||
// constructor has an arraySubscriptExpr.
|
||||
Finder->addMatcher(
|
||||
arraySubscriptExpr(
|
||||
hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
|
||||
hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
|
||||
.bind("expr"),
|
||||
this);
|
||||
Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType(
|
||||
constantArrayType().bind("type")))),
|
||||
hasIndex(expr().bind("index")))
|
||||
.bind("expr"),
|
||||
this);
|
||||
|
||||
Finder->addMatcher(
|
||||
cxxOperatorCallExpr(
|
||||
@@ -112,7 +109,8 @@ void ProBoundsConstantArrayIndexCheck::check(
|
||||
return;
|
||||
|
||||
if (Index.isSigned() && Index.isNegative()) {
|
||||
diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
|
||||
diag(Matched->getExprLoc(),
|
||||
"std::array<> index %0 is negative")
|
||||
<< Index.toString(10);
|
||||
return;
|
||||
}
|
||||
@@ -129,9 +127,8 @@ void ProBoundsConstantArrayIndexCheck::check(
|
||||
// Get uint64_t values, because different bitwidths would lead to an assertion
|
||||
// in APInt::uge.
|
||||
if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
|
||||
diag(Matched->getExprLoc(),
|
||||
"std::array<> index %0 is past the end of the array "
|
||||
"(which contains %1 elements)")
|
||||
diag(Matched->getExprLoc(), "std::array<> index %0 is past the end of the array "
|
||||
"(which contains %1 elements)")
|
||||
<< Index.toString(10) << ArraySize.toString(10, false);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user