Compare commits

..

39 Commits

Author SHA1 Message Date
Tanya Lattner
45d5bd27cb 2.0 Release docs
llvm-svn: 37312
2007-05-23 18:12:40 +00:00
Tanya Lattner
755a21f3ae Fixing patch mistake.
llvm-svn: 37303
2007-05-23 06:07:37 +00:00
Chris Lattner
688d4d27cf final updates to release notes
llvm-svn: 37300
2007-05-23 04:41:25 +00:00
Tanya Lattner
0df7b6319f Merge from mainline
llvm-svn: 37298
2007-05-23 04:22:41 +00:00
Tanya Lattner
5764e55d63 Merge from mainline
llvm-svn: 37279
2007-05-22 06:23:12 +00:00
Tanya Lattner
b5c059b832 Merge from mainline
llvm-svn: 37276
2007-05-22 05:38:40 +00:00
Chris Lattner
bd43fcf5c5 merge portability fix from mainline
llvm-svn: 37222
2007-05-18 07:07:27 +00:00
Chris Lattner
1dc0dbf64d first cut of llvm 2.0 release notes
llvm-svn: 37220
2007-05-18 06:39:06 +00:00
Tanya Lattner
8c32fe0e61 Merge from mainline
llvm-svn: 37217
2007-05-18 06:21:50 +00:00
Tanya Lattner
2e406e7d52 Merge from mainline.
llvm-svn: 37216
2007-05-18 06:20:09 +00:00
Tanya Lattner
1f6d36ff77 Merge from mainline
llvm-svn: 37215
2007-05-18 06:08:42 +00:00
Tanya Lattner
a5bea192e3 Merge from mainline.
llvm-svn: 37214
2007-05-18 06:03:56 +00:00
Tanya Lattner
28c87da11f Merge from mainline.
llvm-svn: 37213
2007-05-18 06:03:55 +00:00
Tanya Lattner
539eba2303 Merging from mainline.
llvm-svn: 37212
2007-05-18 05:59:52 +00:00
Tanya Lattner
16c912d5f0 Merging from mainline (inline asm fix)
llvm-svn: 37211
2007-05-18 05:55:42 +00:00
Chris Lattner
5e6d2c1282 merge in from mainline
llvm-svn: 37210
2007-05-18 05:51:57 +00:00
Tanya Lattner
ac864c0f53 Merge inline asm fix from mainline.
llvm-svn: 37209
2007-05-18 05:51:20 +00:00
Chris Lattner
35fb6225a6 merge from mainline
llvm-svn: 37208
2007-05-18 05:48:38 +00:00
Reid Spencer
b73b303f76 Regenerate these for the release. They weren't up to date and would
cause non-bison builds to fail.

llvm-svn: 37189
2007-05-17 22:59:30 +00:00
Reid Spencer
25c7375e28 Fix the rules for handling the case when BISON is not available. We want
this to succeed by copying the .h.cvs file to .h and the .cpp.cvs file to
.cpp.

llvm-svn: 37187
2007-05-17 22:42:28 +00:00
Reid Spencer
9a8bae25b7 Merge from mainline to require Perl for build system.
llvm-svn: 37167
2007-05-17 18:12:30 +00:00
Reid Spencer
9eb9228a7b Make Perl required - merge from mainline.
llvm-svn: 37165
2007-05-17 18:07:24 +00:00
Reid Spencer
cbf56c683f Merge arbitrary precision integer documentation from mainline.
llvm-svn: 37110
2007-05-16 18:46:48 +00:00
Tanya Lattner
407e88833c Remove from release.
llvm-svn: 37079
2007-05-15 05:18:30 +00:00
Tanya Lattner
70c8b4ce40 Merge from mainline.
llvm-svn: 37078
2007-05-15 05:15:10 +00:00
Tanya Lattner
9a3b80ebb8 Merging from mainline.
llvm-svn: 37077
2007-05-15 05:08:55 +00:00
Tanya Lattner
d1e8ff86b5 Merge from mainline.
llvm-svn: 37076
2007-05-15 05:00:54 +00:00
Tanya Lattner
05c9c7203d Merge from mainline
PR1413

llvm-svn: 37075
2007-05-15 04:57:33 +00:00
Tanya Lattner
990c707d71 Merging from mainline
llvm-svn: 36970
2007-05-10 04:52:54 +00:00
Tanya Lattner
43195b190b Merging from mainline PR 1403
llvm-svn: 36969
2007-05-10 04:47:38 +00:00
Tanya Lattner
d29e402cca Merge from mainline PR1403
llvm-svn: 36968
2007-05-10 04:42:32 +00:00
Tanya Lattner
02ac6f3362 file 2007-05-09-JumpTables.ll was added on branch release_20 on 2007-05-10 04:47:38 +0000
llvm-svn: 36961
2007-05-09 20:07:09 +00:00
CVS to SVN Conversion
ad4bf2f82b This commit was manufactured by cvs2svn to create branch 'release_20'.
llvm-svn: 36960
2007-05-09 20:07:08 +00:00
Tanya Lattner
200b3953c2 Merging from mainline. PR1399 fix.
llvm-svn: 36949
2007-05-08 23:41:39 +00:00
Tanya Lattner
e43c50a1a4 Merge from mainline
llvm-svn: 36932
2007-05-08 06:37:35 +00:00
Tanya Lattner
09489a21d9 Merging from mainline.
llvm-svn: 36931
2007-05-08 06:36:05 +00:00
Tanya Lattner
1c81a16ffc Setting version number
llvm-svn: 36927
2007-05-08 04:45:26 +00:00
Tanya Lattner
357259b227 Setting version number.
llvm-svn: 36926
2007-05-08 04:45:12 +00:00
CVS to SVN Conversion
15fe03a879 This commit was manufactured by cvs2svn to create branch 'release_20'.
llvm-svn: 36925
2007-05-08 04:45:12 +00:00
59484 changed files with 486222 additions and 9596265 deletions

View File

@@ -1,4 +0,0 @@
{
"project_id" : "clang-tools-extra",
"conduit_uri" : "https://reviews.llvm.org/"
}

View File

@@ -1,32 +0,0 @@
#==============================================================================#
# This file specifies intentionally untracked files that git should ignore.
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
#
# This file is intentionally different from the output of `git svn show-ignore`,
# as most of those are useless.
#==============================================================================#
#==============================================================================#
# File extensions to be ignored anywhere in the tree.
#==============================================================================#
# Temp files created by most text editors.
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
*.pyc
# vim swap files
.*.swp
.sw?
#==============================================================================#
# Explicit files to ignore (only matches one).
#==============================================================================#
cscope.files
cscope.out
.clang_complete
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
#==============================================================================#
docs/_build

View File

@@ -1,29 +0,0 @@
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)
# Add the common testsuite after all the tools.
# TODO: Support tests with more granularity when features are off?
if(CLANG_ENABLE_STATIC_ANALYZER AND CLANG_INCLUDE_TESTS)
add_subdirectory(test)
add_subdirectory(unittests)
endif()
option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
${LLVM_INCLUDE_DOCS})
if( CLANG_TOOLS_EXTRA_INCLUDE_DOCS )
add_subdirectory(docs)
endif()

View File

@@ -1,21 +0,0 @@
This file is a list of the people responsible for ensuring that patches for a
particular tool are reviewed, either by themself or by someone else. They are
also the gatekeepers for their part of Clang, with the final word on what goes
in or not.
The list is sorted by surname and formatted to allow easy grepping and
beautification by scripts. The fields are: name (N), email (E), web-address
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
(S).
N: Peter Collingbourne
E: peter@pcc.me.uk
D: clang-query
N: Manuel Klimek
E: klimek@google.com
D: clang-rename, all parts of clang-tools-extra not covered by someone else
N: Alexander Kornienko
E: alexfh@google.com
D: clang-tidy

View File

@@ -1,62 +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
------- ---------
clang-tidy clang-tidy/cert

View File

@@ -1,22 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Tools repository
//===----------------------------------------------------------------------===//
Welcome to the repository of extra Clang Tools. This repository holds tools
that are developed as part of the LLVM compiler infrastructure project and the
Clang frontend. These tools are kept in a separate "extra" repository to
allow lighter weight checkouts of the core Clang codebase.
This repository is only intended to be checked out inside of a full LLVM+Clang
tree, and in the 'tools/extra' subdirectory of the Clang checkout.
All discussion regarding Clang, Clang-based tools, and code in this repository
should be held using the standard Clang mailing lists:
http://lists.llvm.org/mailman/listinfo/cfe-dev
Code review for this tree should take place on the standard Clang patch and
commit lists:
http://lists.llvm.org/mailman/listinfo/cfe-commits
If you find a bug in these tools, please file it in the LLVM bug tracker:
http://llvm.org/bugs/

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -1,19 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangApplyReplacements
lib/Tooling/ApplyReplacements.cpp
LINK_LIBS
clangAST
clangBasic
clangRewrite
clangToolingCore
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
include
)
add_subdirectory(tool)

View File

@@ -1,155 +0,0 @@
//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the interface for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
//===----------------------------------------------------------------------===//
#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"
#include <string>
#include <system_error>
#include <vector>
namespace clang {
class DiagnosticsEngine;
class Rewriter;
namespace format {
struct FormatStyle;
} // end namespace format
namespace replace {
/// \brief Collection of source ranges.
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
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>>
FileToReplacementsMap;
/// \brief Recursively descends through a directory structure rooted at \p
/// Directory and attempts to deserialize *.yaml files as
/// TranslationUnitReplacements. All docs that successfully deserialize are
/// added to \p TUs.
///
/// Directories starting with '.' are ignored during traversal.
///
/// \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
/// 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);
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements or
/// TranslationUnitDiagnostics to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If there were conflicts.
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
/// to apply.
/// \param[out] Rewrites The results of applying replacements will be applied
/// to this Rewriter.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If a replacement failed to apply.
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
/// \brief Given a collection of Replacements for a single file, produces a list
/// of source ranges that enclose those Replacements.
///
/// \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(
const std::vector<clang::tooling::Replacement> &Replacements);
/// \brief Write the contents of \c FileContents to disk. Keys of the map are
/// filenames and values are the new contents for those files.
///
/// \param[in] Rewrites Rewriter containing written files to write to disk.
bool writeFiles(const clang::Rewriter &Rewrites);
/// \brief Delete the replacement files.
///
/// \param[in] Files Replacement files to delete.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns \parblock
/// \li true If all files have been deleted successfully.
/// \li false If at least one or more failures occur when deleting
/// files.
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics);
} // end namespace replace
} // end namespace clang
#endif // LLVM_CLANG_APPLYREPLACEMENTS_H

View File

@@ -1,402 +0,0 @@
//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the implementation for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
/// FIXME: Use Diagnostics for output instead of llvm::errs().
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#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"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
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,
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::TranslationUnitDiagnostics 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;
}
/// \brief Dumps information for a sequence of conflicting Replacements.
///
/// \param[in] File FileEntry for the file the conflicting Replacements are
/// for.
/// \param[in] ConflictingReplacements List of conflicting Replacements.
/// \param[in] SM SourceManager used for reporting.
static void reportConflict(
const FileEntry *File,
const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
SourceManager &SM) {
FileID FID = SM.translateFile(File);
if (FID.isInvalid())
FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
errs() << "The following changes conflict:\n";
for (const tooling::Replacement &R : ConflictingReplacements) {
if (R.getLength() == 0) {
errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << " "
<< R.getReplacementText() << "\n";
} else {
if (R.getReplacementText().empty())
errs() << " Remove ";
else
errs() << " Replace ";
errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << "-"
<< SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
<< SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
if (R.getReplacementText().empty())
errs() << "\n";
else
errs() << " with \"" << R.getReplacementText() << "\"\n";
}
}
}
// 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.
///
/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true if conflicts were detected
/// \li false if no conflicts were detected
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
SourceManager &SM) {
bool conflictsFound = false;
for (auto &FileAndReplacements : Replacements) {
const FileEntry *Entry = FileAndReplacements.first;
auto &Replacements = FileAndReplacements.second;
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
conflictsFound = true;
errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
for (const tooling::Range &Conflict : Conflicts) {
auto ConflictingReplacements = llvm::makeArrayRef(
&Replacements[Conflict.getOffset()], Conflict.getLength());
reportConflict(Entry, ConflictingReplacements, SM);
}
}
return conflictsFound;
}
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const tooling::Replacement &R : TU.Replacements) {
// 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 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) {
// Apply all changes
//
// FIXME: No longer certain GroupedReplacements is really the best kind of
// data structure for applying replacements. Rewriter certainly doesn't care.
// 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))
return false;
}
return true;
}
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replaces) {
RangeVector ChangedRanges;
// Generate the new ranges from the replacements.
int Shift = 0;
for (const tooling::Replacement &R : Replaces) {
unsigned Offset = R.getOffset() + Shift;
unsigned Length = R.getReplacementText().size();
Shift += Length - R.getLength();
ChangedRanges.push_back(tooling::Range(Offset, Length));
}
return ChangedRanges;
}
bool writeFiles(const clang::Rewriter &Rewrites) {
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
StringRef FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
if (EC) {
errs() << "Warning: Could not write to " << EC.message() << "\n";
continue;
}
BufferI->second.write(FileStream);
}
return true;
}
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics) {
bool Success = true;
for (const auto &Filename : Files) {
std::error_code Error = llvm::sys::fs::remove(Filename);
if (Error) {
Success = false;
// FIXME: Use Diagnostics for outputting errors.
errs() << "Error deleting file: " << Filename << "\n";
errs() << Error.message() << "\n";
errs() << "Please delete the file manually\n";
}
}
return Success;
}
} // end namespace replace
} // end namespace clang

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_executable(clang-apply-replacements
ClangApplyReplacementsMain.cpp
)
target_link_libraries(clang-apply-replacements
clangApplyReplacements
clangBasic
clangFormat
clangRewrite
clangToolingCore
)
install(TARGETS clang-apply-replacements
RUNTIME DESTINATION bin)

View File

@@ -1,283 +0,0 @@
//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the main function for the
/// clang-apply-replacements tool.
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
using namespace clang;
using namespace clang::replace;
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
static cl::OptionCategory ReplacementCategory("Replacement Options");
static cl::OptionCategory FormattingCategory("Formatting Options");
const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
&FormattingCategory};
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
"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"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
namespace {
// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
public:
ScopedFileRemover(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics)
: TURFiles(Files), Diag(Diagnostics) {}
~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
private:
const TUReplacementFiles &TURFiles;
clang::DiagnosticsEngine &Diag;
};
} // namespace
static void printVersion() {
outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \parblock
/// \li true if all replacements were applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty())
return true;
if (!applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
FileManager &Files = SM.getFileManager();
StringRef FileName = Replacements.begin()->getFilePath();
const clang::FileEntry *Entry = Files.getFile(FileName);
assert(Entry && "Expected an existing file");
FileID ID = SM.translateFile(Entry);
assert(ID.isValid() && "Expected a valid FileID");
const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
Result = std::string(Buffer->begin(), Buffer->end());
return true;
}
/// \brief Apply \c Replacements and return the new file contents.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if all replacements applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
applyReplacements(const std::vector<tooling::Replacement> &Replacements,
std::string &Result, DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(Replacements, Rewrites, Result);
}
/// \brief Apply code formatting to all places where replacements were made.
///
/// \pre !Replacements.empty().
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements that were made to the file. Provided
/// to indicate where changes were made.
/// \param[in] FileData The contents of the file \b after \c Replacements have
/// been applied.
/// \param[out] FormattedFileData The contents of the file after reformatting.
/// \param[in] FormatStyle Style to apply.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if reformatting replacements were all successfully
/// applied.
/// \li false if at least one reformatting replacement failed to apply.
static bool
applyFormatting(const std::vector<tooling::Replacement> &Replacements,
const StringRef FileData, std::string &FormattedFileData,
const format::FormatStyle &FormatStyle,
DiagnosticsEngine &Diagnostics) {
assert(!Replacements.empty() && "Need at least one replacement");
RangeVector Ranges = calculateChangedRanges(Replacements);
StringRef FileName = Replacements.begin()->getFilePath();
tooling::Replacements R =
format::reformat(FormatStyle, FileData, Ranges, FileName);
// FIXME: Remove this copy when tooling::Replacements is implemented as a
// vector instead of a set.
std::vector<tooling::Replacement> FormattingReplacements;
std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
if (FormattingReplacements.empty()) {
FormattedFileData = FileData;
return true;
}
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
SM.overrideFileContents(Files.getFile(FileName),
llvm::MemoryBuffer::getMemBufferCopy(FileData));
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
}
int main(int argc, char **argv) {
cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
cl::SetVersionPrinter(&printVersion);
cl::ParseCommandLineOptions(argc, argv);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
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;
std::error_code ErrorCode =
collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
TUDiagnostics TUDs;
TUFiles.clear();
ErrorCode =
collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
<< "': " << ErrorCode.message() << "\n";
return 1;
}
// Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
// command line option) when exiting main().
std::unique_ptr<ScopedFileRemover> Remover;
if (RemoveTUReplacementFiles)
Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
return 1;
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
return 1;
Rewriter ReplacementsRewriter(SM, LangOptions());
for (const auto &FileAndReplacements : GroupedReplacements) {
// This shouldn't happen but if a file somehow has no replacements skip to
// next file.
if (FileAndReplacements.second.empty())
continue;
std::string NewFileData;
StringRef FileName = FileAndReplacements.first->getName();
if (!applyReplacements(FileAndReplacements.second, NewFileData,
Diagnostics)) {
errs() << "Failed to apply replacements to " << FileName << "\n";
continue;
}
// Apply formatting if requested.
if (DoFormat &&
!applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
FormatStyle, Diagnostics)) {
errs() << "Failed to apply reformatting replacements for " << FileName
<< "\n";
continue;
}
// Write new file to disk
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Could not open " << FileName << " for writing\n";
continue;
}
FileStream << NewFileData;
}
return 0;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
)

View File

@@ -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();
}

View File

@@ -1,18 +0,0 @@
set(LLVM_LINK_COMPONENTS
lineeditor
support
)
add_clang_library(clangQuery
Query.cpp
QueryParser.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
)
add_subdirectory(tool)

View File

@@ -1,146 +0,0 @@
//===---- Query.cpp - clang-query query -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
Query::~Query() {}
bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << ErrStr << "\n";
return false;
}
bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
return true;
}
bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << "Available commands:\n\n"
" match MATCHER, m MATCHER "
"Match the loaded ASTs against the given matcher.\n"
" let NAME MATCHER, l NAME MATCHER "
"Give a matcher expression a name, to be used later\n"
" "
"as part of other expressions.\n"
" set bind-root (true|false) "
"Set whether to bind the root matcher to \"root\".\n"
" set output (diag|print|dump) "
"Set whether to print bindings as diagnostics,\n"
" "
"AST pretty prints or AST dumps.\n"
" quit "
"Terminates the query session.\n\n";
return true;
}
bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
QS.Terminate = true;
return true;
}
namespace {
struct CollectBoundNodes : MatchFinder::MatchCallback {
std::vector<BoundNodes> &Bindings;
CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
void run(const MatchFinder::MatchResult &Result) override {
Bindings.push_back(Result.Nodes);
}
};
} // namespace
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
for (auto &AST : QS.ASTs) {
MatchFinder Finder;
std::vector<BoundNodes> Matches;
DynTypedMatcher MaybeBoundMatcher = Matcher;
if (QS.BindRoot) {
llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
if (M)
MaybeBoundMatcher = *M;
}
CollectBoundNodes Collect(Matches);
if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
OS << "Not a valid top-level matcher.\n";
return false;
}
Finder.matchAST(AST->getASTContext());
for (auto 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) {
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());
}
break;
}
case OK_Print: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
OS << "\n";
break;
}
case OK_Dump: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.dump(OS, AST->getSourceManager());
OS << "\n";
break;
}
}
}
if (MI->getMap().empty())
OS << "No bindings.\n";
}
}
OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
return true;
}
bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
if (Value) {
QS.NamedValues[Name] = Value;
} else {
QS.NamedValues.erase(Name);
}
return true;
}
#ifndef _MSC_VER
const QueryKind SetQueryKind<bool>::value;
const QueryKind SetQueryKind<OutputKind>::value;
#endif
} // namespace query
} // namespace clang

View File

@@ -1,136 +0,0 @@
//===--- Query.h - clang-query ----------------------------------*- 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_QUERY_QUERY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
#include <string>
namespace clang {
namespace query {
enum OutputKind { OK_Diag, OK_Print, OK_Dump };
enum QueryKind {
QK_Invalid,
QK_NoOp,
QK_Help,
QK_Let,
QK_Match,
QK_SetBool,
QK_SetOutputKind,
QK_Quit
};
class QuerySession;
struct Query : llvm::RefCountedBase<Query> {
Query(QueryKind Kind) : Kind(Kind) {}
virtual ~Query();
/// Perform the query on \p QS and print output to \p OS.
///
/// \return false if an error occurs, otherwise return true.
virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
const QueryKind Kind;
};
typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
/// Any query which resulted in a parse error. The error message is in ErrStr.
struct InvalidQuery : Query {
InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
std::string ErrStr;
static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
};
/// No-op query (i.e. a blank line).
struct NoOpQuery : Query {
NoOpQuery() : Query(QK_NoOp) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
};
/// Query for "help".
struct HelpQuery : Query {
HelpQuery() : Query(QK_Help) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
};
/// Query for "quit".
struct QuitQuery : Query {
QuitQuery() : Query(QK_Quit) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_Quit; }
};
/// Query for "match MATCHER".
struct MatchQuery : Query {
MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
: Query(QK_Match), Matcher(Matcher) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
ast_matchers::dynamic::DynTypedMatcher Matcher;
static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
};
struct LetQuery : Query {
LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
: Query(QK_Let), Name(Name), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
std::string Name;
ast_matchers::dynamic::VariantValue Value;
static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
};
template <typename T> struct SetQueryKind {};
template <> struct SetQueryKind<bool> {
static const QueryKind value = QK_SetBool;
};
template <> struct SetQueryKind<OutputKind> {
static const QueryKind value = QK_SetOutputKind;
};
/// Query for "set VAR VALUE".
template <typename T> struct SetQuery : Query {
SetQuery(T QuerySession::*Var, T Value)
: Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
QS.*Var = Value;
return true;
}
static bool classof(const Query *Q) {
return Q->Kind == SetQueryKind<T>::value;
}
T QuerySession::*Var;
T Value;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,278 +0,0 @@
//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include <set>
using namespace llvm;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
// Lex any amount of whitespace followed by a "word" (any sequence of
// non-whitespace characters) from the start of region [Begin,End). If no word
// is found before End, return StringRef(). Begin is adjusted to exclude the
// lexed region.
StringRef QueryParser::lexWord() {
while (true) {
if (Begin == End)
return StringRef(Begin, 0);
if (!isWhitespace(*Begin))
break;
++Begin;
}
const char *WordBegin = Begin;
while (true) {
++Begin;
if (Begin == End || isWhitespace(*Begin))
return StringRef(WordBegin, Begin - WordBegin);
}
}
// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
// function for details.
template <typename T> struct QueryParser::LexOrCompleteWord {
StringSwitch<T> Switch;
QueryParser *P;
StringRef Word;
// Set to the completion point offset in Word, or StringRef::npos if
// completion point not in Word.
size_t WordCompletionPos;
LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
: Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
template <unsigned N>
LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
bool IsCompletion = true) {
StringRef CaseStr(S, N - 1);
if (WordCompletionPos == StringRef::npos)
Switch.Case(S, Value);
else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
CaseStr.substr(0, WordCompletionPos) ==
Word.substr(0, WordCompletionPos))
P->Completions.push_back(LineEditor::Completion(
(CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
return *this;
}
T Default(const T &Value) const { return Switch.Default(Value); }
};
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
template <typename T>
QueryParser::LexOrCompleteWord<T>
QueryParser::lexOrCompleteWord(StringRef &Word) {
Word = lexWord();
size_t WordCompletionPos = StringRef::npos;
if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
if (CompletionPos < Word.data())
WordCompletionPos = 0;
else
WordCompletionPos = CompletionPos - Word.data();
}
return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
}
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
StringRef ValStr;
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
if (Value == ~0u) {
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
}
return new SetQuery<bool>(Var, Value);
}
QueryRef QueryParser::parseSetOutputKind() {
StringRef ValStr;
unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("dump", OK_Dump)
.Default(~0u);
if (OutKind == ~0u) {
return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
ValStr + "'");
}
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}
QueryRef QueryParser::endQuery(QueryRef Q) {
const char *Extra = Begin;
if (!lexWord().empty())
return new InvalidQuery("unexpected extra input: '" +
StringRef(Extra, End - Extra) + "'");
return Q;
}
namespace {
enum ParsedQueryKind {
PQK_Invalid,
PQK_NoOp,
PQK_Help,
PQK_Let,
PQK_Match,
PQK_Set,
PQK_Unlet,
PQK_Quit
};
enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
std::string ErrStr;
llvm::raw_string_ostream OS(ErrStr);
Diag.printToStreamFull(OS);
return new InvalidQuery(OS.str());
}
} // 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) {
Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
}
return QueryRef();
}
QueryRef QueryParser::doParse() {
StringRef CommandStr;
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
.Case("", PQK_NoOp)
.Case("help", PQK_Help)
.Case("m", PQK_Match, /*IsCompletion=*/false)
.Case("let", PQK_Let)
.Case("match", PQK_Match)
.Case("set", PQK_Set)
.Case("unlet", PQK_Unlet)
.Case("quit", PQK_Quit)
.Default(PQK_Invalid);
switch (QKind) {
case PQK_NoOp:
return new NoOpQuery;
case PQK_Help:
return endQuery(new HelpQuery);
case PQK_Quit:
return endQuery(new QuitQuery);
case PQK_Let: {
StringRef Name = lexWord();
if (Name.empty())
return new InvalidQuery("expected variable name");
if (CompletionPos)
return completeMatcherExpression();
Diagnostics Diag;
ast_matchers::dynamic::VariantValue Value;
if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
&QS.NamedValues, &Value, &Diag)) {
return makeInvalidQueryFromDiagnostics(Diag);
}
return new LetQuery(Name, Value);
}
case PQK_Match: {
if (CompletionPos)
return completeMatcherExpression();
Diagnostics Diag;
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
if (!Matcher) {
return makeInvalidQueryFromDiagnostics(Diag);
}
return new MatchQuery(*Matcher);
}
case PQK_Set: {
StringRef VarStr;
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
.Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot)
.Default(PQV_Invalid);
if (VarStr.empty())
return new InvalidQuery("expected variable name");
if (Var == PQV_Invalid)
return new InvalidQuery("unknown variable: '" + VarStr + "'");
QueryRef Q;
switch (Var) {
case PQV_Output:
Q = parseSetOutputKind();
break;
case PQV_BindRoot:
Q = parseSetBool(&QuerySession::BindRoot);
break;
case PQV_Invalid:
llvm_unreachable("Invalid query kind");
}
return endQuery(Q);
}
case PQK_Unlet: {
StringRef Name = lexWord();
if (Name.empty())
return new InvalidQuery("expected variable name");
return endQuery(new LetQuery(Name, VariantValue()));
}
case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
llvm_unreachable("Invalid query kind");
}
QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
return QueryParser(Line, QS).doParse();
}
std::vector<LineEditor::Completion>
QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
QueryParser P(Line, QS);
P.CompletionPos = Line.data() + Pos;
P.doParse();
return P.Completions;
}
} // namespace query
} // namespace clang

View File

@@ -1,71 +0,0 @@
//===--- QueryParser.h - clang-query ----------------------------*- 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_QUERY_QUERY_PARSER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
#include "Query.h"
#include "QuerySession.h"
#include "llvm/LineEditor/LineEditor.h"
#include <cstddef>
namespace clang {
namespace query {
class QuerySession;
class QueryParser {
public:
/// Parse \a Line as a query.
///
/// \return A QueryRef representing the query, which may be an InvalidQuery.
static QueryRef parse(StringRef Line, const QuerySession &QS);
/// Compute a list of completions for \a Line assuming a cursor at
/// \param Pos characters past the start of \a Line, ordered from most
/// likely to least likely.
///
/// \return A vector of completions for \a Line.
static std::vector<llvm::LineEditor::Completion>
complete(StringRef Line, size_t Pos, const QuerySession &QS);
private:
QueryParser(StringRef Line, const QuerySession &QS)
: Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
StringRef lexWord();
template <typename T> struct LexOrCompleteWord;
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
QueryRef parseSetBool(bool QuerySession::*Var);
QueryRef parseSetOutputKind();
QueryRef completeMatcherExpression();
QueryRef endQuery(QueryRef Q);
/// \brief Parse [\p Begin,\p End).
///
/// \return A reference to the parsed query object, which may be an
/// \c InvalidQuery if a parse error occurs.
QueryRef doParse();
const char *Begin;
const char *End;
const char *CompletionPos;
std::vector<llvm::LineEditor::Completion> Completions;
const QuerySession &QS;
};
} // namespace query
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H

View File

@@ -1,40 +0,0 @@
//===--- QuerySession.h - clang-query ---------------------------*- 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_QUERY_QUERY_SESSION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
#include "Query.h"
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
namespace clang {
class ASTUnit;
namespace query {
/// Represents the state for a particular clang-query session.
class QuerySession {
public:
QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
: ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), Terminate(false) {}
llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
OutputKind OutKind;
bool BindRoot;
bool Terminate;
llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,14 +0,0 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
add_clang_executable(clang-query ClangQuery.cpp)
target_link_libraries(clang-query
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
clangQuery
clangTooling
)
install(TARGETS clang-query RUNTIME DESTINATION bin)

View File

@@ -1,116 +0,0 @@
//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool is for interactive exploration of the Clang AST using AST matchers.
// It currently allows the user to enter a matcher at an interactive prompt and
// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
// Example session:
//
// $ cat foo.c
// void foo(void) {}
// $ clang-query foo.c --
// clang-query> match functionDecl()
//
// Match #1:
//
// foo.c:1:1: note: "root" binds here
// void foo(void) {}
// ^~~~~~~~~~~~~~~~~
// 1 match.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QueryParser.h"
#include "QuerySession.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/LineEditor/LineEditor.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
#include <fstream>
#include <string>
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
using namespace clang::query;
using namespace clang::tooling;
using namespace llvm;
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::OptionCategory ClangQueryCategory("clang-query options");
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
cl::value_desc("command"),
cl::cat(ClangQueryCategory));
static cl::list<std::string> CommandFiles("f",
cl::desc("Read commands from file"),
cl::value_desc("file"),
cl::cat(ClangQueryCategory));
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
if (!Commands.empty() && !CommandFiles.empty()) {
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
return 1;
}
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
std::vector<std::unique_ptr<ASTUnit>> ASTs;
if (Tool.buildASTs(ASTs) != 0)
return 1;
QuerySession QS(ASTs);
if (!Commands.empty()) {
for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
QueryRef Q = QueryParser::parse(*I, QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
} else if (!CommandFiles.empty()) {
for (auto 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";
return 1;
}
while (Input.good()) {
std::string Line;
std::getline(Input, Line);
QueryRef Q = QueryParser::parse(Line, QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
}
} else {
LineEditor LE("clang-query");
LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
return QueryParser::complete(Line, Pos, QS);
});
while (llvm::Optional<std::string> Line = LE.readLine()) {
QueryRef Q = QueryParser::parse(*Line, QS);
Q->run(llvm::outs(), QS);
llvm::outs().flush();
if (QS.Terminate)
break;
}
}
return 0;
}

View File

@@ -1,18 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangRename
USRFinder.cpp
USRFindingAction.cpp
USRLocFinder.cpp
RenamingAction.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangIndex
clangLex
clangToolingCore
)
add_subdirectory(tool)

View File

@@ -1,93 +0,0 @@
//===--- tools/extra/clang-rename/RenamingAction.cpp - Clang rename tool --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to rename every symbol at a point.
///
//===----------------------------------------------------------------------===//
#include "RenamingAction.h"
#include "USRLocFinder.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include <string>
#include <vector>
using namespace llvm;
namespace clang {
namespace rename {
class RenamingASTConsumer : public ASTConsumer {
public:
RenamingASTConsumer(
const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &FileToReplaces,
bool PrintLocations)
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
void HandleTranslationUnit(ASTContext &Context) override {
for (unsigned I = 0; I < NewNames.size(); ++I)
HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
}
void HandleOneRename(ASTContext &Context, const std::string &NewName,
const std::string &PrevName,
const std::vector<std::string> &USRs) {
const SourceManager &SourceMgr = Context.getSourceManager();
std::vector<SourceLocation> RenamingCandidates;
std::vector<SourceLocation> NewCandidates;
NewCandidates =
getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl());
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
NewCandidates.end());
unsigned PrevNameLen = PrevName.length();
for (const auto &Loc : RenamingCandidates) {
if (PrintLocations) {
FullSourceLoc FullLoc(Loc, SourceMgr);
errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
<< ":" << FullLoc.getSpellingLineNumber() << ":"
<< FullLoc.getSpellingColumnNumber() << "\n";
}
// FIXME: better error handling.
tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
if (Err)
llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
<< llvm::toString(std::move(Err)) << "\n";
}
}
private:
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
FileToReplaces, PrintLocations);
}
} // namespace rename
} // namespace clang

View File

@@ -1,48 +0,0 @@
//===--- tools/extra/clang-rename/RenamingAction.h - Clang rename tool ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to rename every symbol at a point.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
#include "clang/Tooling/Refactoring.h"
namespace clang {
class ASTConsumer;
class CompilerInstance;
namespace rename {
class RenamingAction {
public:
RenamingAction(const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &FileToReplaces,
bool PrintLocations = false)
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
std::unique_ptr<ASTConsumer> newASTConsumer();
private:
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};
} // namespace rename
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H

View File

@@ -1,212 +0,0 @@
//===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
/// point.
///
//===----------------------------------------------------------------------===//
#include "USRFinder.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallVector.h"
using namespace llvm;
namespace clang {
namespace rename {
// NamedDeclFindingASTVisitor recursively visits each AST node to find the
// symbol underneath the cursor.
// FIXME: move to seperate .h/.cc file if this gets too large.
namespace {
class NamedDeclFindingASTVisitor
: public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
public:
// \brief Finds the NamedDecl at a point in the source.
// \param Point the location in the source to search for the NamedDecl.
explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
const ASTContext &Context)
: Result(nullptr), Point(Point), Context(Context) {}
// \brief Finds the NamedDecl for a name in the source.
// \param Name the fully qualified name.
explicit NamedDeclFindingASTVisitor(const std::string &Name,
const ASTContext &Context)
: Result(nullptr), Name(Name), Context(Context) {}
// Declaration visitors:
// \brief Checks if the point falls within the NameDecl. This covers every
// declaration of a named entity that we may come across. Usually, just
// checking if the point lies within the length of the name of the declaration
// and the start location is sufficient.
bool VisitNamedDecl(const NamedDecl *Decl) {
return dyn_cast<CXXConversionDecl>(Decl)
? true
: setResult(Decl, Decl->getLocation(),
Decl->getNameAsString().length());
}
// Expression visitors:
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
return setResult(Decl, Expr->getLocation(),
Decl->getNameAsString().length());
}
bool VisitMemberExpr(const MemberExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
return setResult(Decl, Expr->getMemberLoc(),
Decl->getNameAsString().length());
}
// Other visitors:
bool VisitTypeLoc(const TypeLoc Loc) {
const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
if (const auto *TemplateTypeParm =
dyn_cast<TemplateTypeParmType>(Loc.getType()))
return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
if (const auto *TemplateSpecType =
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
TypeBeginLoc, TypeEndLoc);
}
return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
TypeEndLoc);
}
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
for (const auto *Initializer : ConstructorDecl->inits()) {
// Ignore implicit initializers.
if (!Initializer->isWritten())
continue;
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
InitEndLoc = Lexer::getLocForEndOfToken(
InitBeginLoc, 0, Context.getSourceManager(),
Context.getLangOpts());
if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
return false;
}
}
return true;
}
// Other:
const NamedDecl *getNamedDecl() { return Result; }
// \brief Determines if a namespace qualifier contains the point.
// \returns false on success and sets Result.
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const NamespaceDecl *Decl =
NameLoc.getNestedNameSpecifier()->getAsNamespace();
setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
NameLoc = NameLoc.getPrefix();
}
}
private:
// \brief Sets Result to Decl if the Point is within Start and End.
// \returns false on success.
bool setResult(const NamedDecl *Decl, SourceLocation Start,
SourceLocation End) {
if (!Decl)
return true;
if (Name.empty()) {
// Offset is used to find the declaration.
if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
!End.isFileID() || !isPointWithin(Start, End))
return true;
} else {
// Fully qualified name is used to find the declaration.
if (Name != Decl->getQualifiedNameAsString())
return true;
}
Result = Decl;
return false;
}
// \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
// \returns false on success.
bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
// FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
return Offset == 0 ||
setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
}
// \brief Determines if the Point is within Start and End.
bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
// FIXME: Add tests for Point == End.
return Point == Start || Point == End ||
(Context.getSourceManager().isBeforeInTranslationUnit(Start,
Point) &&
Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
}
const NamedDecl *Result;
const SourceLocation Point; // The location to find the NamedDecl.
const std::string Name;
const ASTContext &Context;
};
} // namespace
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
const SourceLocation Point) {
const SourceManager &SM = Context.getSourceManager();
NamedDeclFindingASTVisitor Visitor(Point, Context);
// Try to be clever about pruning down the number of top-level declarations we
// see. If both start and end is either before or after the point we're
// looking for the point cannot be inside of this decl. Don't even look at it.
for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
SourceLocation StartLoc = CurrDecl->getLocStart();
SourceLocation EndLoc = CurrDecl->getLocEnd();
if (StartLoc.isValid() && EndLoc.isValid() &&
SM.isBeforeInTranslationUnit(StartLoc, Point) !=
SM.isBeforeInTranslationUnit(EndLoc, Point))
Visitor.TraverseDecl(CurrDecl);
}
NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
Visitor.handleNestedNameSpecifierLoc(Location);
return Visitor.getNamedDecl();
}
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
const std::string &Name) {
NamedDeclFindingASTVisitor Visitor(Name, Context);
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
return Visitor.getNamedDecl();
}
std::string getUSRForDecl(const Decl *Decl) {
llvm::SmallVector<char, 128> Buff;
// FIXME: Add test for the nullptr case.
if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
return "";
return std::string(Buff.data(), Buff.size());
}
} // namespace rename
} // namespace clang

View File

@@ -1,84 +0,0 @@
//===--- tools/extra/clang-rename/USRFinder.h - Clang rename tool ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Methods for determining the USR of a symbol at a location in source
/// code.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <string>
#include <vector>
using namespace llvm;
using namespace clang::ast_matchers;
namespace clang {
class ASTContext;
class Decl;
class SourceLocation;
class NamedDecl;
namespace rename {
// Given an AST context and a point, returns a NamedDecl identifying the symbol
// at the point. Returns null if nothing is found at the point.
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
const SourceLocation Point);
// Given an AST context and a fully qualified name, returns a NamedDecl
// identifying the symbol with a matching name. Returns null if nothing is
// found for the name.
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
const std::string &Name);
// Converts a Decl into a USR.
std::string getUSRForDecl(const Decl *Decl);
// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
public:
explicit NestedNameSpecifierLocFinder(ASTContext &Context)
: Context(Context) {}
std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
addMatchers();
Finder.matchAST(Context);
return Locations;
}
private:
void addMatchers() {
const auto NestedNameSpecifierLocMatcher =
nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
}
void run(const MatchFinder::MatchResult &Result) override {
const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nestedNameSpecifierLoc");
Locations.push_back(*NNS);
}
ASTContext &Context;
std::vector<NestedNameSpecifierLoc> Locations;
MatchFinder Finder;
};
} // namespace rename
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H

View File

@@ -1,226 +0,0 @@
//===--- tools/extra/clang-rename/USRFindingAction.cpp - Clang rename tool ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to find USR for the symbol at <offset>, as well as
/// all additional USRs.
///
//===----------------------------------------------------------------------===//
#include "USRFindingAction.h"
#include "USRFinder.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/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include <algorithm>
#include <set>
#include <string>
#include <vector>
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) {}
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);
} else {
USRSet.insert(getUSRForDecl(FoundDecl));
}
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;
}
// If FoundDecl is a constructor or destructor, we want to instead take
// the Decl of the corresponding class.
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;
}
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;
};
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
return llvm::make_unique<NamedDeclFindingConsumer>(
SymbolOffsets, QualifiedNames, SpellingNames, USRList, ErrorOccurred);
}
} // namespace rename
} // namespace clang

View File

@@ -1,53 +0,0 @@
//===--- tools/extra/clang-rename/USRFindingAction.h - Clang rename tool --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to find all relevant USRs at a point.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include <string>
#include <vector>
namespace clang {
class ASTConsumer;
class CompilerInstance;
class NamedDecl;
namespace rename {
struct USRFindingAction {
USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
ArrayRef<std::string> QualifiedNames)
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
ErrorOccurred(false) {}
std::unique_ptr<ASTConsumer> newASTConsumer();
ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
bool errorOccurred() { return ErrorOccurred; }
private:
std::vector<unsigned> SymbolOffsets;
std::vector<std::string> QualifiedNames;
std::vector<std::string> SpellingNames;
std::vector<std::vector<std::string>> USRList;
bool ErrorOccurred;
};
} // namespace rename
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H

View File

@@ -1,167 +0,0 @@
//===--- tools/extra/clang-rename/USRLocFinder.cpp - Clang rename tool ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Mehtods for finding all instances of a USR. Our strategy is very
/// simple; we just compare the USR at every relevant AST node with the one
/// provided.
///
//===----------------------------------------------------------------------===//
#include "USRLocFinder.h"
#include "USRFinder.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <cstddef>
#include <set>
#include <string>
#include <vector>
using namespace llvm;
namespace clang {
namespace rename {
namespace {
// \brief This visitor recursively searches for all instances of a USR in a
// translation unit and stores them for later usage.
class USRLocFindingASTVisitor
: public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
public:
explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
StringRef PrevName,
const ASTContext &Context)
: USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
}
// Declaration visitors:
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
for (const auto *Initializer : ConstructorDecl->inits()) {
// Ignore implicit initializers.
if (!Initializer->isWritten())
continue;
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
LocationsFound.push_back(Initializer->getSourceLocation());
}
}
return true;
}
bool VisitNamedDecl(const NamedDecl *Decl) {
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
checkAndAddLocation(Decl->getLocation());
return true;
}
// Expression visitors:
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
checkAndAddLocation(Location);
}
return true;
}
bool VisitMemberExpr(const MemberExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
checkAndAddLocation(Location);
}
return true;
}
// Other visitors:
bool VisitTypeLoc(const TypeLoc Loc) {
if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
USRSet.end())
checkAndAddLocation(Loc.getBeginLoc());
if (const auto *TemplateTypeParm =
dyn_cast<TemplateTypeParmType>(Loc.getType())) {
if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
USRSet.end())
checkAndAddLocation(Loc.getBeginLoc());
}
return true;
}
// Non-visitors:
// \brief Returns a list of unique locations. Duplicate or overlapping
// locations are erroneous and should be reported!
const std::vector<clang::SourceLocation> &getLocationsFound() const {
return LocationsFound;
}
// Namespace traversal:
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const NamespaceDecl *Decl =
NameLoc.getNestedNameSpecifier()->getAsNamespace();
if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
checkAndAddLocation(NameLoc.getLocalBeginLoc());
NameLoc = NameLoc.getPrefix();
}
}
private:
void checkAndAddLocation(SourceLocation Loc) {
const SourceLocation BeginLoc = Loc;
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
StringRef TokenName =
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
Context.getSourceManager(), Context.getLangOpts());
size_t Offset = TokenName.find(PrevName);
// The token of the source location we find actually has the old
// name.
if (Offset != StringRef::npos)
LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
}
const std::set<std::string> USRSet;
const std::string PrevName;
std::vector<clang::SourceLocation> LocationsFound;
const ASTContext &Context;
};
} // namespace
std::vector<SourceLocation>
getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
Decl *Decl) {
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
Visitor.TraverseDecl(Decl);
NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
Visitor.handleNestedNameSpecifierLoc(Location);
return Visitor.getLocationsFound();
}
} // namespace rename
} // namespace clang

View File

@@ -1,35 +0,0 @@
//===--- tools/extra/clang-rename/USRLocFinder.h - Clang rename tool ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides functionality for finding all instances of a USR in a given
/// AST.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
#include "clang/AST/AST.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>
namespace clang {
namespace rename {
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
std::vector<SourceLocation>
getLocationsOfUSRs(const std::vector<std::string> &USRs,
llvm::StringRef PrevName, Decl *Decl);
} // namespace rename
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H

View File

@@ -1,19 +0,0 @@
add_clang_executable(clang-rename ClangRename.cpp)
target_link_libraries(clang-rename
clangBasic
clangFrontend
clangRename
clangRewrite
clangTooling
clangToolingCore
)
install(TARGETS clang-rename RUNTIME DESTINATION bin)
install(PROGRAMS clang-rename.py
DESTINATION share/clang
COMPONENT clang-rename)
install(PROGRAMS clang-rename.el
DESTINATION share/clang
COMPONENT clang-rename)

View File

@@ -1,231 +0,0 @@
//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file implements a clang-rename tool that automatically finds and
/// renames symbols in C++ code.
///
//===----------------------------------------------------------------------===//
#include "../RenamingAction.h"
#include "../USRFindingAction.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.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/Frontend/TextDiagnosticPrinter.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 <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;
};
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(
"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(
"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));
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));
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);
}
}
// 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();
exit(1);
}
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';
}
}
if (FindingAction.errorOccurred()) {
// Diagnostics are already issued at this point.
exit(1);
}
// Perform the renaming.
rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
Tool.getReplacements(), PrintLocations);
std::unique_ptr<tooling::FrontendActionFactory> Factory =
tooling::newFrontendActionFactory(&RenameAction);
int ExitCode;
if (Inplace) {
ExitCode = Tool.runAndSave(Factory.get());
} else {
ExitCode = Tool.run(Factory.get());
if (!ExportFixes.empty()) {
std::error_code EC;
llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Error opening output file: " << EC.message() << '\n';
exit(1);
}
// 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());
yaml::Output YAML(OS);
YAML << TUR;
OS.close();
exit(0);
}
// Write every file to stdout. Right now we just barf the files without any
// indication of which files start where, other than that we print the files
// in the same order we see them.
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager Sources(Diagnostics, FileMgr);
Rewriter Rewrite(Sources, DefaultLangOptions);
Tool.applyAllReplacements(Rewrite);
for (const auto &File : Files) {
const auto *Entry = FileMgr.getFile(File);
const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
Rewrite.getEditBuffer(ID).write(outs());
}
}
exit(ExitCode);
}

View File

@@ -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 doesnt.
;; 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 doesnt 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

View File

@@ -1,61 +0,0 @@
'''
Minimal clang-rename integration with Vim.
Before installing make sure one of the following is satisfied:
* clang-rename is in your PATH
* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable
* `binary` in clang-rename.py points to valid to clang-rename executable
To install, simply put this into your ~/.vimrc
noremap <leader>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.
'''
import vim
import subprocess
import sys
def main():
binary = 'clang-rename'
if vim.eval('exists("g:clang_rename_path")') == "1":
binary = vim.eval('g:clang_rename_path')
# Get arguments for clang-rename binary.
offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2
if offset < 0:
print >> sys.stderr, '''Couldn\'t determine cursor position.
Is your file empty?'''
return
filename = vim.current.buffer.name
new_name_request_message = 'type new name:'
new_name = vim.eval("input('{}\n')".format(new_name_request_message))
# Call clang-rename.
command = [binary,
filename,
'-i',
'-offset', str(offset),
'-new-name', str(new_name)]
# FIXME: make it possible to run the tool on unsaved file.
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if stderr:
print stderr
# Reload all buffers in Vim.
vim.command("checktime")
if __name__ == '__main__':
main()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -1,7 +0,0 @@
obj/
bin/
.vs/
Key.snk
clang-tidy.exe
packages/
*.csproj.user

View File

@@ -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()

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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_;
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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="&quot;..\packages\Brutal.Dev.StrongNameSigner.1.8.0\tools\StrongNameSigner.Console.exe&quot; -in &quot;..\packages&quot; -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>

View File

@@ -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>

View File

@@ -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_; }
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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>

View File

@@ -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
}
}

View File

@@ -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));
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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")]

View File

@@ -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);
};
}

View File

@@ -1,7 +0,0 @@
namespace LLVM.ClangTidy
{
static class PkgCmdIDList
{
public const uint cmdidClangTidy = 0x100;
};
}

View File

@@ -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")]

View File

@@ -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]&quot;;.
/// </summary>
internal static string ClangTidyChecks {
get {
return ResourceManager.GetString("ClangTidyChecks", resourceCulture);
}
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -1,41 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangTidy
ClangTidy.cpp
ClangTidyModule.cpp
ClangTidyDiagnosticConsumer.cpp
ClangTidyOptions.cpp
DEPENDS
ClangSACheckers
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangRewrite
clangSema
clangStaticAnalyzerCore
clangStaticAnalyzerFrontend
clangTooling
clangToolingCore
)
add_subdirectory(tool)
add_subdirectory(plugin)
add_subdirectory(boost)
add_subdirectory(cert)
add_subdirectory(llvm)
add_subdirectory(cppcoreguidelines)
add_subdirectory(google)
add_subdirectory(misc)
add_subdirectory(modernize)
add_subdirectory(mpi)
add_subdirectory(performance)
add_subdirectory(readability)
add_subdirectory(utils)

View File

@@ -1,580 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements a clang-tidy tool.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidy.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyModuleRegistry.h"
#include "clang/AST/ASTConsumer.h"
#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"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/PPCallbacks.h"
#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"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include <algorithm>
#include <utility>
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
namespace clang {
namespace tidy {
namespace {
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
public:
AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
FilesMade *filesMade) override {
for (const ento::PathDiagnostic *PD : Diags) {
SmallString<64> CheckName(AnalyzerCheckNamePrefix);
CheckName += PD->getCheckName();
Context.diag(CheckName, PD->getLocation().asLocation(),
PD->getShortDescription())
<< PD->path.back()->getRanges();
for (const auto &DiagPiece :
PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
DiagPiece->getString(), DiagnosticIDs::Note)
<< DiagPiece->getRanges();
}
}
}
StringRef getName() const override { return "ClangTidyDiags"; }
bool supportsLogicalOpControlFlow() const override { return true; }
bool supportsCrossFileDiagnostics() const override { return true; }
private:
ClangTidyContext &Context;
};
class ErrorReporter {
public:
ErrorReporter(bool ApplyFixes, StringRef FormatStyle)
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
SourceMgr(Diags, Files), ApplyFixes(ApplyFixes), TotalFixes(0),
AppliedFixes(0), WarningsAsErrors(0), FormatStyle(FormatStyle) {
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagPrinter->BeginSourceFile(LangOpts);
}
SourceManager &getSourceManager() { return SourceMgr; }
void reportDiagnostic(const ClangTidyError &Error) {
const tooling::DiagnosticMessage &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;
if (Error.IsWarningAsError) {
Name += ",-warnings-as-errors";
Level = DiagnosticsEngine::Error;
WarningsAsErrors++;
}
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";
}
} 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));
}
}
}
for (auto Fix : FixLocations) {
Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
: diag::note_fixit_failed);
}
for (const auto &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";
}
}
}
unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
private:
SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
if (FilePath.empty())
return SourceLocation();
const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
void reportNote(const tooling::DiagnosticMessage &Message) {
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
<< Message.Message;
}
FileManager Files;
LangOptions LangOpts; // FIXME: use langopts from each original file
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
DiagnosticConsumer *DiagPrinter;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
llvm::StringMap<Replacements> FileReplacements;
bool ApplyFixes;
unsigned TotalFixes;
unsigned AppliedFixes;
unsigned WarningsAsErrors;
StringRef FormatStyle;
};
class ClangTidyASTConsumer : public MultiplexConsumer {
public:
ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
std::unique_ptr<ast_matchers::MatchFinder> Finder,
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
: MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Checks(std::move(Checks)) {}
private:
std::unique_ptr<ast_matchers::MatchFinder> Finder;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
};
} // namespace
ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
ClangTidyContext &Context)
: Context(Context), CheckFactories(new ClangTidyCheckFactories) {
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I) {
std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Module->addCheckFactories(*CheckFactories);
}
}
static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
AnalyzerOptionsRef AnalyzerOptions) {
StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
for (const auto &Opt : Opts.CheckOptions) {
StringRef OptName(Opt.first);
if (!OptName.startswith(AnalyzerPrefix))
continue;
AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
}
}
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) {
// FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
// modify Compiler.
Context.setSourceManager(&Compiler.getSourceManager());
Context.setCurrentFile(File);
Context.setASTContext(&Compiler.getASTContext());
auto WorkingDir = Compiler.getSourceManager()
.getFileManager()
.getVirtualFileSystem()
->getCurrentWorkingDirectory();
if (WorkingDir)
Context.setCurrentBuildDirectory(WorkingDir.get());
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
if (auto *P = Context.getCheckProfileData())
FinderOptions.CheckProfiling.emplace(P->Records);
std::unique_ptr<ast_matchers::MatchFinder> Finder(
new ast_matchers::MatchFinder(std::move(FinderOptions)));
for (auto &Check : Checks) {
Check->registerMatchers(&*Finder);
Check->registerPPCallbacks(Compiler);
}
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
if (!Checks.empty())
Consumers.push_back(Finder->newASTConsumer());
AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
// FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
// to true.
AnalyzerOptions->Config["cfg-temporary-dtors"] =
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
GlobList &Filter = Context.getChecksFilter();
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
if (!AnalyzerOptions->CheckersControlList.empty()) {
setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
AnalyzerOptions->AnalyzeNestedBlocks = true;
AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
ento::CreateAnalysisConsumer(Compiler);
AnalysisConsumer->AddDiagnosticConsumer(
new AnalyzerDiagnosticConsumer(Context));
Consumers.push_back(std::move(AnalysisConsumer));
}
return llvm::make_unique<ClangTidyASTConsumer>(
std::move(Consumers), std::move(Finder), std::move(Checks));
}
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
std::vector<std::string> CheckNames;
GlobList &Filter = Context.getChecksFilter();
for (const auto &CheckFactory : *CheckFactories) {
if (Filter.contains(CheckFactory.first))
CheckNames.push_back(CheckFactory.first);
}
for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
std::sort(CheckNames.begin(), CheckNames.end());
return CheckNames;
}
ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
ClangTidyOptions::OptionMap Options;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
for (const auto &Check : Checks)
Check->storeOptions(Options);
return Options;
}
DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
DiagnosticIDs::Level Level) {
return Context->diag(CheckName, Loc, Message, Level);
}
void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Context->setSourceManager(Result.SourceManager);
check(Result);
}
OptionsView::OptionsView(StringRef CheckName,
const ClangTidyOptions::OptionMap &CheckOptions)
: NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
return Default;
}
std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
StringRef Default) const {
auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
// Fallback to global setting, if present.
Iter = CheckOptions.find(LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
return Default;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, StringRef Value) const {
Options[NamePrefix + LocalName.str()] = Value;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, int64_t Value) const {
store(Options, LocalName, llvm::itostr(Value));
}
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckNames();
}
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckOptions();
}
ClangTidyStats
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
const CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
ClangTool Tool(Compilations, InputFiles);
clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
// Add extra arguments passed by the clang-tidy command-line.
ArgumentsAdjuster PerFileExtraArgumentsInserter =
[&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());
}
if (Opts.ExtraArgs)
AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
Opts.ExtraArgs->end());
return AdjustedArgs;
};
// Remove plugins arguments.
ArgumentsAdjuster PluginArgumentsRemover =
[&Context](const CommandLineArguments &Args, StringRef Filename) {
CommandLineArguments AdjustedArgs;
for (size_t I = 0, E = Args.size(); I < E; ++I) {
if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
(Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
Args[I + 2] == "-Xclang") {
I += 3;
} else
AdjustedArgs.push_back(Args[I]);
}
return AdjustedArgs;
};
Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
if (Profile)
Context.setCheckProfileData(Profile);
ClangTidyDiagnosticConsumer DiagConsumer(Context);
Tool.setDiagnosticConsumer(&DiagConsumer);
class ActionFactory : public FrontendActionFactory {
public:
ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
FrontendAction *create() override { return new Action(&ConsumerFactory); }
private:
class Action : public ASTFrontendAction {
public:
Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
StringRef File) override {
return Factory->CreateASTConsumer(Compiler, File);
}
private:
ClangTidyASTConsumerFactory *Factory;
};
ClangTidyASTConsumerFactory ConsumerFactory;
};
ActionFactory Factory(Context);
Tool.run(&Factory);
*Errors = Context.getErrors();
return Context.getStats();
}
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
StringRef FormatStyle, unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Fix, FormatStyle);
vfs::FileSystem &FileSystem =
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
if (!InitialWorkingDir)
llvm::report_fatal_error("Cannot get current working path.");
for (const ClangTidyError &Error : Errors) {
if (!Error.BuildDirectory.empty()) {
// By default, the working directory of file system is the current
// clang-tidy running directory.
//
// Change the directory to the one used during the analysis.
FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
}
Reporter.reportDiagnostic(Error);
// Return to the initial directory to correctly resolve next Error.
FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
}
Reporter.Finish();
WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
}
void exportReplacements(const llvm::StringRef MainFilePath,
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);
}
yaml::Output YAML(OS);
YAML << TUD;
}
} // namespace tidy
} // namespace clang

View File

@@ -1,252 +0,0 @@
//===--- ClangTidy.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_CLANGTIDY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyOptions.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <type_traits>
#include <vector>
namespace clang {
class CompilerInstance;
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief Provides access to the ``ClangTidyCheck`` options via check-local
/// names.
///
/// Methods of this class prepend ``CheckName + "."`` to translate check-local
/// option names to global option names.
class OptionsView {
public:
/// \brief Initializes the instance using \p CheckName + "." as a prefix.
OptionsView(StringRef CheckName,
const ClangTidyOptions::OptionMap &CheckOptions);
/// \brief Read a named option from the ``Context``.
///
/// Reads the option with the check-local name \p LocalName from the
/// ``CheckOptions``. If the corresponding key is not present, returns
/// \p Default.
std::string get(StringRef LocalName, StringRef Default) const;
/// \brief Read a named option from the ``Context``.
///
/// 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.
std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
/// \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 the
/// ``CheckOptions``. If the corresponding key is not present, returns
/// \p Default.
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
get(StringRef LocalName, T Default) const {
std::string Value = get(LocalName, "");
T Result = Default;
if (!Value.empty())
StringRef(Value).getAsInteger(10, Result);
return Result;
}
/// \brief 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,
StringRef Value) const;
/// \brief Stores an option with the check-local name \p LocalName with
/// ``int64_t`` value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
int64_t Value) const;
private:
std::string NamePrefix;
const ClangTidyOptions::OptionMap &CheckOptions;
};
/// \brief Base class for all clang-tidy checks.
///
/// To implement a ``ClangTidyCheck``, write a subclass and override some of the
/// base class's methods. E.g. to implement a check that validates namespace
/// declarations, override ``registerMatchers``:
///
/// ~~~{.cpp}
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
/// Finder->addMatcher(namespaceDecl().bind("namespace"), this);
/// }
/// ~~~
///
/// and then override ``check(const MatchResult &Result)`` to do the actual
/// check for each match.
///
/// A new ``ClangTidyCheck`` instance is created per translation unit.
///
/// FIXME: Figure out whether carrying information from one TU to another is
/// useful/necessary.
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
public:
/// \brief Initializes the check with \p CheckName and \p Context.
///
/// Derived classes must implement the constructor with this signature or
/// delegate it. If a check needs to read options, it can do this in the
/// constructor using the Options.get() methods below.
ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
: CheckName(CheckName), Context(Context),
Options(CheckName, Context->getOptions().CheckOptions) {
assert(Context != nullptr);
assert(!CheckName.empty());
}
/// \brief Override this to register ``PPCallbacks`` with ``Compiler``.
///
/// This should be used for clang-tidy checks that analyze preprocessor-
/// dependent properties, e.g. the order of include directives.
virtual void registerPPCallbacks(CompilerInstance &Compiler) {}
/// \brief Override this to register AST matchers with \p Finder.
///
/// This should be used by clang-tidy checks that analyze code properties that
/// dependent on AST knowledge.
///
/// You can register as many matchers as necessary with \p Finder. Usually,
/// "this" will be used as callback, but you can also specify other callback
/// classes. Thereby, different matchers can trigger different callbacks.
///
/// If you need to merge information between the different matchers, you can
/// store these as members of the derived class. However, note that all
/// matches occur in the order of the AST traversal.
virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
/// \brief ``ClangTidyChecks`` that register ASTMatchers should do the actual
/// work in here.
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
/// \brief Add a diagnostic with the check's name.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Should store all options supported by this check with their
/// current values or default values for options that haven't been overridden.
///
/// The check should use ``Options.store()`` to store each option it supports
/// whether it has the default value or it has been overridden.
virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
private:
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
StringRef getID() const override { return CheckName; }
std::string CheckName;
ClangTidyContext *Context;
protected:
OptionsView Options;
/// \brief Returns the main file name of the current translation unit.
StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
/// \brief Returns the language options from the context.
LangOptions getLangOpts() const { return Context->getLangOpts(); }
};
class ClangTidyCheckFactories;
class ClangTidyASTConsumerFactory {
public:
ClangTidyASTConsumerFactory(ClangTidyContext &Context);
/// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
/// \brief Get the list of enabled checks.
std::vector<std::string> getCheckNames();
/// \brief Get the union of options from all checks.
ClangTidyOptions::OptionMap getCheckOptions();
private:
ClangTidyContext &Context;
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
};
/// \brief Fills the list of check names that are enabled when the provided
/// filters are applied.
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
/// \brief Returns the effective check-specific options.
///
/// The method configures ClangTidy with the specified \p Options and collects
/// effective options from all created checks. The returned set of options
/// includes default check-specific options for all keys not overridden by \p
/// Options.
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
/// \brief Run a set of clang-tidy checks on a set of files.
///
/// \param Profile if provided, it enables check profile collection in
/// MatchFinder, and will contain the result of the profile.
ClangTidyStats
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
std::vector<ClangTidyError> *Errors,
ProfileData *Profile = nullptr);
// FIXME: This interface will need to be significantly extended to be useful.
// FIXME: Implement confidence levels for displaying/fixing errors.
//
/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
/// Errors containing fixes are automatically applied and reformatted. If no
/// clang-format configuration file is found, the given \P FormatStyle is used.
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
StringRef FormatStyle, unsigned &WarningsAsErrorsCount);
/// \brief Serializes replacements into YAML and writes them to the specified
/// output stream.
void exportReplacements(StringRef MainFilePath,
const std::vector<ClangTidyError> &Errors,
raw_ostream &OS);
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H

View File

@@ -1,588 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
/// and ClangTidyError classes.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyOptions.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Frontend/DiagnosticRenderer.h"
#include "llvm/ADT/SmallString.h"
#include <tuple>
#include <vector>
using namespace clang;
using namespace tidy;
namespace {
class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
public:
ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
DiagnosticOptions *DiagOpts,
ClangTidyError &Error)
: DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
protected:
void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level, StringRef Message,
ArrayRef<CharSourceRange> Ranges,
const SourceManager *SM,
DiagOrStoredDiag Info) override {
// Remove check name from the message.
// FIXME: Remove this once there's a better way to pass check names than
// appending the check name to the message in ClangTidyContext::diag and
// using getCustomDiagID.
std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
if (Message.endswith(CheckNameInMessage))
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
auto TidyMessage = Loc.isValid()
? tooling::DiagnosticMessage(Message, *SM, Loc)
: tooling::DiagnosticMessage(Message);
if (Level == DiagnosticsEngine::Note) {
Error.Notes.push_back(TidyMessage);
return;
}
assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
Error.Message = TidyMessage;
}
void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
ArrayRef<CharSourceRange> Ranges,
const SourceManager &SM) override {}
void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange> &Ranges,
ArrayRef<FixItHint> Hints,
const SourceManager &SM) override {
assert(Loc.isValid());
for (const auto &FixIt : Hints) {
CharSourceRange Range = FixIt.RemoveRange;
assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
"Invalid range in the fix-it hint.");
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!");
}
}
}
void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
const SourceManager &SM) override {}
void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,
StringRef ModuleName,
const SourceManager &SM) override {}
void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,
StringRef ModuleName,
const SourceManager &SM) override {}
void endDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level) override {
assert(!Error.Message.Message.empty() && "Message has not been set");
}
private:
ClangTidyError &Error;
};
} // end anonymous namespace
ClangTidyError::ClangTidyError(StringRef CheckName,
ClangTidyError::Level DiagLevel,
StringRef BuildDirectory, bool IsWarningAsError)
: tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
IsWarningAsError(IsWarningAsError) {}
// Returns true if GlobList starts with the negative indicator ('-'), removes it
// from the GlobList.
static bool ConsumeNegativeIndicator(StringRef &GlobList) {
if (GlobList.startswith("-")) {
GlobList = GlobList.substr(1);
return true;
}
return false;
}
// Converts first glob from the comma-separated list of globs to Regex and
// removes it and the trailing comma from the GlobList.
static llvm::Regex ConsumeGlob(StringRef &GlobList) {
StringRef Glob = GlobList.substr(0, GlobList.find(',')).trim();
GlobList = GlobList.substr(Glob.size() + 1);
SmallString<128> RegexText("^");
StringRef MetaChars("()^$|*+?.[]\\{}");
for (char C : Glob) {
if (C == '*')
RegexText.push_back('.');
else if (MetaChars.find(C) != StringRef::npos)
RegexText.push_back('\\');
RegexText.push_back(C);
}
RegexText.push_back('$');
return llvm::Regex(RegexText);
}
GlobList::GlobList(StringRef Globs)
: Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
bool GlobList::contains(StringRef S, bool Contains) {
if (Regex.match(S))
Contains = Positive;
if (NextGlob)
Contains = NextGlob->contains(S, Contains);
return Contains;
}
ClangTidyContext::ClangTidyContext(
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
Profile(nullptr) {
// Before the first translation unit we can get errors related to command-line
// parsing, use empty string for the file name in this case.
setCurrentFile("");
}
DiagnosticBuilder ClangTidyContext::diag(
StringRef CheckName, SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
assert(Loc.isValid());
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
Level, (Description + " [" + CheckName + "]").str());
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
return DiagEngine->Report(Loc, ID);
}
void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
DiagEngine = Engine;
}
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
DiagEngine->setSourceManager(SourceMgr);
}
void ClangTidyContext::setCurrentFile(StringRef File) {
CurrentFile = File;
CurrentOptions = getOptionsForFile(CurrentFile);
CheckFilter.reset(new GlobList(*getOptions().Checks));
WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors));
}
void ClangTidyContext::setASTContext(ASTContext *Context) {
DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
LangOpts = Context->getLangOpts();
}
const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
return OptionsProvider->getGlobalOptions();
}
const ClangTidyOptions &ClangTidyContext::getOptions() const {
return CurrentOptions;
}
ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
// Merge options on top of getDefaults() as a safeguard against options with
// unset values.
return ClangTidyOptions::getDefaults().mergeWith(
OptionsProvider->getOptions(File));
}
void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
GlobList &ClangTidyContext::getChecksFilter() {
assert(CheckFilter != nullptr);
return *CheckFilter;
}
GlobList &ClangTidyContext::getWarningAsErrorFilter() {
assert(WarningAsErrorFilter != nullptr);
return *WarningAsErrorFilter;
}
/// \brief Store a \c ClangTidyError.
void ClangTidyContext::storeError(const ClangTidyError &Error) {
Errors.push_back(Error);
}
StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
llvm::DenseMap<unsigned, std::string>::const_iterator I =
CheckNamesByDiagnosticID.find(DiagnosticID);
if (I != CheckNamesByDiagnosticID.end())
return I->second;
return "";
}
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
: Context(Ctx), LastErrorRelatesToUserCode(false),
LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Diags.reset(new DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
/*ShouldOwnClient=*/false));
Context.setDiagnosticsEngine(Diags.get());
}
void ClangTidyDiagnosticConsumer::finalizeLastError() {
if (!Errors.empty()) {
ClangTidyError &Error = Errors.back();
if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
Error.DiagLevel != ClangTidyError::Error) {
++Context.Stats.ErrorsIgnoredCheckFilter;
Errors.pop_back();
} else if (!LastErrorRelatesToUserCode) {
++Context.Stats.ErrorsIgnoredNonUserCode;
Errors.pop_back();
} else if (!LastErrorPassesLineFilter) {
++Context.Stats.ErrorsIgnoredLineFilter;
Errors.pop_back();
} else {
++Context.Stats.ErrorsDisplayed;
}
}
LastErrorRelatesToUserCode = false;
LastErrorPassesLineFilter = false;
}
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
bool Invalid;
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
if (!Invalid) {
const char *P = CharacterData;
while (*P != '\0' && *P != '\r' && *P != '\n')
++P;
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
// FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
if (RestOfLine.find("NOLINT") != StringRef::npos) {
return true;
}
}
return false;
}
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 &&
DiagLevel != DiagnosticsEngine::Fatal &&
LineIsMarkedWithNOLINTinMacro(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);
if (DiagLevel == DiagnosticsEngine::Note) {
assert(!Errors.empty() &&
"A diagnostic note can only be appended to a message.");
} else {
finalizeLastError();
StringRef WarningOption =
Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
Info.getID());
std::string CheckName = !WarningOption.empty()
? ("clang-diagnostic-" + WarningOption).str()
: Context.getCheckName(Info.getID()).str();
if (CheckName.empty()) {
// This is a compiler diagnostic without a warning option. Assign check
// name based on its level.
switch (DiagLevel) {
case DiagnosticsEngine::Error:
case DiagnosticsEngine::Fatal:
CheckName = "clang-diagnostic-error";
break;
case DiagnosticsEngine::Warning:
CheckName = "clang-diagnostic-warning";
break;
default:
CheckName = "clang-diagnostic-unknown";
break;
}
}
ClangTidyError::Level Level = ClangTidyError::Warning;
if (DiagLevel == DiagnosticsEngine::Error ||
DiagLevel == DiagnosticsEngine::Fatal) {
// Force reporting of Clang errors regardless of filters and non-user
// code.
Level = ClangTidyError::Error;
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
}
bool IsWarningAsError =
DiagLevel == DiagnosticsEngine::Warning &&
Context.getWarningAsErrorFilter().contains(CheckName);
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
IsWarningAsError);
}
ClangTidyDiagnosticRenderer Converter(
Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
Errors.back());
SmallString<100> Message;
Info.FormatDiagnostic(Message);
SourceManager *Sources = nullptr;
if (Info.hasSourceManager())
Sources = &Info.getSourceManager();
Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
Info.getRanges(), Info.getFixItHints(), Sources);
checkFilters(Info.getLocation());
}
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
unsigned LineNumber) const {
if (Context.getGlobalOptions().LineFilter.empty())
return true;
for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
if (FileName.endswith(Filter.Name)) {
if (Filter.LineRanges.empty())
return true;
for (const FileFilter::LineRange &Range : Filter.LineRanges) {
if (Range.first <= LineNumber && LineNumber <= Range.second)
return true;
}
return false;
}
}
return false;
}
void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
// Invalid location may mean a diagnostic in a command line, don't skip these.
if (!Location.isValid()) {
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
return;
}
const SourceManager &Sources = Diags->getSourceManager();
if (!*Context.getOptions().SystemHeaders &&
Sources.isInSystemHeader(Location))
return;
// FIXME: We start with a conservative approach here, but the actual type of
// location needed depends on the check (in particular, where this check wants
// to apply fixes).
FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
const FileEntry *File = Sources.getFileEntryForID(FID);
// -DMACRO definitions on the command line have locations in a virtual buffer
// that doesn't have a FileEntry. Don't skip these as well.
if (!File) {
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
return;
}
StringRef FileName(File->getName());
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
Sources.isInMainFile(Location) ||
getHeaderFilter()->match(FileName);
unsigned LineNumber = Sources.getExpansionLineNumber(Location);
LastErrorPassesLineFilter =
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
}
llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
if (!HeaderFilter)
HeaderFilter.reset(
new llvm::Regex(*Context.getOptions().HeaderFilterRegex));
return HeaderFilter.get();
}
void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
SmallVectorImpl<ClangTidyError> &Errors) const {
// Each error is modelled as the set of intervals in which it applies
// 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
// 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.
struct Event {
// An event can be either the begin or the end of an interval.
enum EventType {
ET_Begin = 1,
ET_End = -1,
};
Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
unsigned ErrorSize)
: Type(Type), ErrorId(ErrorId) {
// The events are going to be sorted by their position. In case of draw:
//
// * If an interval ends at the same position at which other interval
// begins, this is not an overlapping, so we want to remove the ending
// interval before adding the starting one: end events have higher
// 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
// 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
// earliest begin point. In both cases, we sort non-increasingly by the
// 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
// 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,
// disallowing the first one.
if (Type == ET_Begin)
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
else
Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
}
bool operator<(const Event &Other) const {
return Priority < Other.Priority;
}
// Determines if this event is the begin or the end of an interval.
EventType Type;
// The index of the error to which the interval that generated this event
// belongs.
unsigned ErrorId;
// The events will be sorted based on this field.
std::tuple<unsigned, EventType, int, int, unsigned> Priority;
};
// Compute error sizes.
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();
}
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]);
}
}
}
std::vector<bool> Apply(Errors.size(), true);
for (auto &FileAndEvents : FileEvents) {
std::vector<Event> &Events = FileAndEvents.second;
// Sweep.
std::sort(Events.begin(), Events.end());
int OpenIntervals = 0;
for (const auto &Event : Events) {
if (Event.Type == Event::ET_End)
--OpenIntervals;
// This has to be checked after removing the interval from the count if it
// is an end event, or before adding it if it is a begin event.
if (OpenIntervals != 0)
Apply[Event.ErrorId] = false;
if (Event.Type == Event::ET_Begin)
++OpenIntervals;
}
assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
}
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");
}
}
}
namespace {
struct LessClangTidyError {
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
const tooling::DiagnosticMessage &M1 = LHS.Message;
const tooling::DiagnosticMessage &M2 = RHS.Message;
return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
std::tie(M2.FilePath, M2.FileOffset, M2.Message);
}
};
struct EqualClangTidyError {
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
LessClangTidyError Less;
return !Less(LHS, RHS) && !Less(RHS, LHS);
}
};
} // end anonymous namespace
// Flushes the internal diagnostics buffer to the ClangTidyContext.
void ClangTidyDiagnosticConsumer::finish() {
finalizeLastError();
std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
Errors.end());
removeIncompatibleErrors(Errors);
for (const ClangTidyError &Error : Errors)
Context.storeError(Error);
Errors.clear();
}

View File

@@ -1,263 +0,0 @@
//===--- ClangTidyDiagnosticConsumer.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_CLANGTIDYDIAGNOSTICCONSUMER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#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"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
namespace clang {
class ASTContext;
class CompilerInstance;
namespace ast_matchers {
class MatchFinder;
}
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief A detected error complete with information to display diagnostic and
/// automatic fix.
///
/// This is used as an intermediate format to transport Diagnostics without a
/// 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);
bool IsWarningAsError;
};
/// \brief Read-only set of strings represented as a list of positive and
/// negative globs. Positive globs add all matched strings to the set, negative
/// globs remove them in the order of appearance in the list.
class GlobList {
public:
/// \brief \p GlobList is a comma-separated list of globs (only '*'
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
GlobList(StringRef Globs);
/// \brief Returns \c true if the pattern matches \p S. The result is the last
/// matching glob's Positive flag.
bool contains(StringRef S) { return contains(S, false); }
private:
bool contains(StringRef S, bool Contains);
bool Positive;
llvm::Regex Regex;
std::unique_ptr<GlobList> NextGlob;
};
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
/// run.
struct ClangTidyStats {
ClangTidyStats()
: ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {}
unsigned ErrorsDisplayed;
unsigned ErrorsIgnoredCheckFilter;
unsigned ErrorsIgnoredNOLINT;
unsigned ErrorsIgnoredNonUserCode;
unsigned ErrorsIgnoredLineFilter;
unsigned errorsIgnored() const {
return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
}
};
/// \brief Container for clang-tidy profiling data.
struct ProfileData {
llvm::StringMap<llvm::TimeRecord> Records;
};
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
/// provided by this context.
///
/// A \c ClangTidyCheck always has access to the active context to report
/// warnings like:
/// \code
/// Context->Diag(Loc, "Single-argument constructors must be explicit")
/// << FixItHint::CreateInsertion(Loc, "explicit ");
/// \endcode
class ClangTidyContext {
public:
/// \brief Initializes \c ClangTidyContext instance.
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
/// \brief Report any errors detected using this method.
///
/// This is still under heavy development and will likely change towards using
/// tablegen'd diagnostic IDs.
/// FIXME: Figure out a way to manage ID spaces.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
StringRef Message,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
///
/// This is called from the \c ClangTidyCheck base class.
void setSourceManager(SourceManager *SourceMgr);
/// \brief Should be called when starting to process new translation unit.
void setCurrentFile(StringRef File);
/// \brief Returns the main file name of the current translation unit.
StringRef getCurrentFile() const { return CurrentFile; }
/// \brief Sets ASTContext for the current translation unit.
void setASTContext(ASTContext *Context);
/// \brief Gets the language options from the AST context.
const LangOptions &getLangOpts() const { return LangOpts; }
/// \brief Returns the name of the clang-tidy check which produced this
/// diagnostic ID.
StringRef getCheckName(unsigned DiagnosticID) const;
/// \brief Returns check filter for the \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
GlobList &getChecksFilter();
/// \brief Returns check filter for the \c CurrentFile which
/// selects checks for upgrade to error.
GlobList &getWarningAsErrorFilter();
/// \brief Returns global options.
const ClangTidyGlobalOptions &getGlobalOptions() const;
/// \brief Returns options for \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
const ClangTidyOptions &getOptions() const;
/// \brief Returns options for \c File. Does not change or depend on
/// \c CurrentFile.
ClangTidyOptions getOptionsForFile(StringRef File) const;
/// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
/// counters.
const ClangTidyStats &getStats() const { return Stats; }
/// \brief Returns all collected errors.
const std::vector<ClangTidyError> &getErrors() const { return Errors; }
/// \brief Clears collected errors.
void clearErrors() { Errors.clear(); }
/// \brief Set the output struct for profile data.
///
/// Setting a non-null pointer here will enable profile collection in
/// clang-tidy.
void setCheckProfileData(ProfileData *Profile);
ProfileData *getCheckProfileData() const { return Profile; }
/// \brief Should be called when starting to process new translation unit.
void setCurrentBuildDirectory(StringRef BuildDirectory) {
CurrentBuildDirectory = BuildDirectory;
}
/// \brief Returns build directory of the current translation unit.
const std::string &getCurrentBuildDirectory() {
return CurrentBuildDirectory;
}
private:
// Calls setDiagnosticsEngine() and storeError().
friend class ClangTidyDiagnosticConsumer;
friend class ClangTidyPluginAction;
/// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
/// correctly.
void setDiagnosticsEngine(DiagnosticsEngine *Engine);
/// \brief Store an \p Error.
void storeError(const ClangTidyError &Error);
std::vector<ClangTidyError> Errors;
DiagnosticsEngine *DiagEngine;
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
std::string CurrentFile;
ClangTidyOptions CurrentOptions;
std::unique_ptr<GlobList> CheckFilter;
std::unique_ptr<GlobList> WarningAsErrorFilter;
LangOptions LangOpts;
ClangTidyStats Stats;
std::string CurrentBuildDirectory;
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
ProfileData *Profile;
};
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
/// \c SourceManager-independent \c ClangTidyError.
//
// FIXME: If we move away from unit-tests, this can be moved to a private
// implementation file.
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
public:
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx);
// FIXME: The concept of converting between FixItHints and Replacements is
// more generic and should be pulled out into a more useful Diagnostics
// library.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) override;
/// \brief Flushes the internal diagnostics buffer to the ClangTidyContext.
void finish() override;
private:
void finalizeLastError();
void removeIncompatibleErrors(SmallVectorImpl<ClangTidyError> &Errors) const;
/// \brief Returns the \c HeaderFilter constructed for the options set in the
/// context.
llvm::Regex *getHeaderFilter();
/// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
/// according to the diagnostic \p Location.
void checkFilters(SourceLocation Location);
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
ClangTidyContext &Context;
std::unique_ptr<DiagnosticsEngine> Diags;
SmallVector<ClangTidyError, 8> Errors;
std::unique_ptr<llvm::Regex> HeaderFilter;
bool LastErrorRelatesToUserCode;
bool LastErrorPassesLineFilter;
bool LastErrorWasIgnored;
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H

View File

@@ -1,39 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file Implements classes required to build clang-tidy modules.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyModule.h"
namespace clang {
namespace tidy {
void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
CheckFactory Factory) {
Factories[Name] = std::move(Factory);
}
void ClangTidyCheckFactories::createChecks(
ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
GlobList &Filter = Context->getChecksFilter();
for (const auto &Factory : Factories) {
if (Filter.contains(Factory.first))
Checks.emplace_back(Factory.second(Factory.first, Context));
}
}
ClangTidyOptions ClangTidyModule::getModuleOptions() {
return ClangTidyOptions();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,99 +0,0 @@
//===--- ClangTidyModule.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_CLANGTIDYMODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
#include "ClangTidy.h"
#include "llvm/ADT/StringRef.h"
#include <functional>
#include <map>
#include <string>
#include <utility>
namespace clang {
namespace tidy {
/// \brief A collection of \c ClangTidyCheckFactory instances.
///
/// All clang-tidy modules register their check factories with an instance of
/// this object.
class ClangTidyCheckFactories {
public:
typedef std::function<ClangTidyCheck *(StringRef Name,
ClangTidyContext *Context)>
CheckFactory;
/// \brief Registers check \p Factory with name \p Name.
///
/// For all checks that have default constructors, use \c registerCheck.
void registerCheckFactory(StringRef Name, CheckFactory Factory);
/// \brief Registers the \c CheckType with the name \p Name.
///
/// This method should be used for all \c ClangTidyChecks that don't require
/// constructor parameters.
///
/// For example, if have a clang-tidy check like:
/// \code
/// class MyTidyCheck : public ClangTidyCheck {
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
/// ..
/// }
/// };
/// \endcode
/// you can register it with:
/// \code
/// class MyModule : public ClangTidyModule {
/// void addCheckFactories(ClangTidyCheckFactories &Factories) override {
/// Factories.registerCheck<MyTidyCheck>("myproject-my-check");
/// }
/// };
/// \endcode
template <typename CheckType> void registerCheck(StringRef CheckName) {
registerCheckFactory(CheckName,
[](StringRef Name, ClangTidyContext *Context) {
return new CheckType(Name, Context);
});
}
/// \brief Create instances of all checks matching \p CheckRegexString and
/// store them in \p Checks.
///
/// The caller takes ownership of the return \c ClangTidyChecks.
void createChecks(ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
typedef std::map<std::string, CheckFactory> FactoryMap;
FactoryMap::const_iterator begin() const { return Factories.begin(); }
FactoryMap::const_iterator end() const { return Factories.end(); }
bool empty() const { return Factories.empty(); }
private:
FactoryMap Factories;
};
/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
/// them a prefixed name.
class ClangTidyModule {
public:
virtual ~ClangTidyModule() {}
/// \brief Implement this function in order to register all \c CheckFactories
/// belonging to this module.
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
/// \brief Gets default options for checks defined in this module.
virtual ClangTidyOptions getModuleOptions();
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H

View File

@@ -1,24 +0,0 @@
//===--- ClangTidyModuleRegistry.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_CLANGTIDYMODULEREGISTRY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
#include "ClangTidyModule.h"
#include "llvm/Support/Registry.h"
namespace clang {
namespace tidy {
typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry;
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H

View File

@@ -1,339 +0,0 @@
//===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangTidyOptions.h"
#include "ClangTidyModuleRegistry.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
#define DEBUG_TYPE "clang-tidy-options"
using clang::tidy::ClangTidyOptions;
using clang::tidy::FileFilter;
using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
namespace llvm {
namespace yaml {
// Map std::pair<int, int> to a JSON array of size 2.
template <> struct SequenceTraits<FileFilter::LineRange> {
static size_t size(IO &IO, FileFilter::LineRange &Range) {
return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
}
static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
if (Index > 1)
IO.setError("Too many elements in line range.");
return Index == 0 ? Range.first : Range.second;
}
};
template <> struct MappingTraits<FileFilter> {
static void mapping(IO &IO, FileFilter &File) {
IO.mapRequired("name", File.Name);
IO.mapOptional("lines", File.LineRanges);
}
static StringRef validate(IO &io, FileFilter &File) {
if (File.Name.empty())
return "No file name specified";
for (const FileFilter::LineRange &Range : File.LineRanges) {
if (Range.first <= 0 || Range.second <= 0)
return "Invalid line range";
}
return StringRef();
}
};
template <> struct MappingTraits<ClangTidyOptions::StringPair> {
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
IO.mapRequired("key", KeyValue.first);
IO.mapRequired("value", KeyValue.second);
}
};
struct NOptionMap {
NOptionMap(IO &) {}
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
: Options(OptionMap.begin(), OptionMap.end()) {}
ClangTidyOptions::OptionMap denormalize(IO &) {
ClangTidyOptions::OptionMap Map;
for (const auto &KeyValue : Options)
Map[KeyValue.first] = KeyValue.second;
return Map;
}
std::vector<ClangTidyOptions::StringPair> Options;
};
template <> struct MappingTraits<ClangTidyOptions> {
static void mapping(IO &IO, ClangTidyOptions &Options) {
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
IO, Options.CheckOptions);
IO.mapOptional("Checks", Options.Checks);
IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
IO.mapOptional("User", Options.User);
IO.mapOptional("CheckOptions", NOpts->Options);
IO.mapOptional("ExtraArgs", Options.ExtraArgs);
IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
}
};
} // namespace yaml
} // namespace llvm
namespace clang {
namespace tidy {
ClangTidyOptions ClangTidyOptions::getDefaults() {
ClangTidyOptions Options;
Options.Checks = "";
Options.WarningsAsErrors = "";
Options.HeaderFilterRegex = "";
Options.SystemHeaders = false;
Options.AnalyzeTemporaryDtors = false;
Options.User = llvm::None;
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I)
Options = Options.mergeWith(I->instantiate()->getModuleOptions());
return Options;
}
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);
for (const auto &KeyValue : Other.CheckOptions)
Result.CheckOptions[KeyValue.first] = KeyValue.second;
return Result;
}
const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
"clang-tidy binary";
const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
"command-line option '-checks'";
const char
ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
"command-line option '-config'";
ClangTidyOptions
ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
ClangTidyOptions Result;
for (const auto &Source : getRawOptions(FileName))
Result = Result.mergeWith(Source.first);
return Result;
}
std::vector<OptionsSource>
DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> Result;
Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
return Result;
}
ConfigOptionsProvider::ConfigOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &ConfigOptions,
const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
std::vector<OptionsSource>
ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
RawOptions.emplace_back(ConfigOptions,
OptionsSourceTypeConfigCommandLineOption);
RawOptions.emplace_back(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
return RawOptions;
}
FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions) {
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
}
FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
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
// similar.
std::vector<OptionsSource>
FileOptionsProvider::getRawOptions(StringRef FileName) {
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
OptionsSource CommandLineOptions(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
// Look for a suitable configuration file in all parent directories of the
// file. Start with the immediate parent directory and move up.
StringRef Path = llvm::sys::path::parent_path(FileName);
for (StringRef CurrentPath = Path; !CurrentPath.empty();
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
llvm::Optional<OptionsSource> Result;
auto Iter = CachedOptions.find(CurrentPath);
if (Iter != CachedOptions.end())
Result = Iter->second;
if (!Result)
Result = tryReadConfigFile(CurrentPath);
if (Result) {
// Store cached value for all intermediate directories.
while (Path != CurrentPath) {
DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
<< ".\n");
CachedOptions[Path] = *Result;
Path = llvm::sys::path::parent_path(Path);
}
CachedOptions[Path] = *Result;
RawOptions.push_back(*Result);
break;
}
}
RawOptions.push_back(CommandLineOptions);
return RawOptions;
}
llvm::Optional<OptionsSource>
FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
assert(!Directory.empty());
if (!llvm::sys::fs::is_directory(Directory)) {
llvm::errs() << "Error reading configuration from " << Directory
<< ": directory doesn't exist.\n";
return llvm::None;
}
for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
SmallString<128> ConfigFile(Directory);
llvm::sys::path::append(ConfigFile, ConfigHandler.first);
DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
bool IsFile = false;
// Ignore errors from is_regular_file: we only need to know if we can read
// the file or not.
llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
if (!IsFile)
continue;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
llvm::MemoryBuffer::getFile(ConfigFile.c_str());
if (std::error_code EC = Text.getError()) {
llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
<< "\n";
continue;
}
// Skip empty files, e.g. files opened for writing via shell output
// redirection.
if ((*Text)->getBuffer().empty())
continue;
llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
ConfigHandler.second((*Text)->getBuffer());
if (!ParsedOptions) {
if (ParsedOptions.getError())
llvm::errs() << "Error parsing " << ConfigFile << ": "
<< ParsedOptions.getError().message() << "\n";
continue;
}
return OptionsSource(*ParsedOptions, ConfigFile.c_str());
}
return llvm::None;
}
/// \brief Parses -line-filter option and stores it to the \c Options.
std::error_code parseLineFilter(StringRef LineFilter,
clang::tidy::ClangTidyGlobalOptions &Options) {
llvm::yaml::Input Input(LineFilter);
Input >> Options.LineFilter;
return Input.error();
}
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
llvm::yaml::Input Input(Config);
ClangTidyOptions Options;
Input >> Options;
if (Input.error())
return Input.error();
return Options;
}
std::string configurationAsText(const ClangTidyOptions &Options) {
std::string Text;
llvm::raw_string_ostream Stream(Text);
llvm::yaml::Output Output(Stream);
// We use the same mapping method for input and output, so we need a non-const
// reference here.
ClangTidyOptions NonConstValue = Options;
Output << NonConstValue;
return Stream.str();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,260 +0,0 @@
//===--- ClangTidyOptions.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_CLANGTIDYOPTIONS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h"
#include <functional>
#include <map>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
namespace clang {
namespace tidy {
/// \brief Contains a list of line ranges in a single file.
struct FileFilter {
/// \brief File name.
std::string Name;
/// \brief LineRange is a pair<start, end> (inclusive).
typedef std::pair<unsigned, unsigned> LineRange;
/// \brief A list of line ranges in this file, for which we show warnings.
std::vector<LineRange> LineRanges;
};
/// \brief Global options. These options are neither stored nor read from
/// configuration files.
struct ClangTidyGlobalOptions {
/// \brief Output warnings from certain line ranges of certain files only.
/// If empty, no warnings will be filtered.
std::vector<FileFilter> LineFilter;
};
/// \brief Contains options for clang-tidy. These options may be read from
/// configuration files, and may be different for different translation units.
struct ClangTidyOptions {
/// \brief These options are used for all settings that haven't been
/// overridden by the \c OptionsProvider.
///
/// Allow no checks and no headers by default. This method initializes
/// check-specific options by calling \c ClangTidyModule::getModuleOptions()
/// of each registered \c ClangTidyModule.
static ClangTidyOptions getDefaults();
/// \brief Creates a new \c ClangTidyOptions instance combined from all fields
/// of this instance overridden by the fields of \p Other that have a value.
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const;
/// \brief Checks filter.
llvm::Optional<std::string> Checks;
/// \brief WarningsAsErrors filter.
llvm::Optional<std::string> WarningsAsErrors;
/// \brief Output warnings from headers matching this filter. Warnings from
/// main files will always be displayed.
llvm::Optional<std::string> HeaderFilterRegex;
/// \brief Output warnings from system headers matching \c HeaderFilterRegex.
llvm::Optional<bool> SystemHeaders;
/// \brief Turns on temporary destructor-based analysis.
llvm::Optional<bool> AnalyzeTemporaryDtors;
/// \brief Specifies the name or e-mail of the user running clang-tidy.
///
/// This option is used, for example, to place the correct user name in TODO()
/// comments in the relevant check.
llvm::Optional<std::string> User;
typedef std::pair<std::string, std::string> StringPair;
typedef std::map<std::string, std::string> OptionMap;
/// \brief Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
typedef std::vector<std::string> ArgList;
/// \brief Add extra compilation arguments to the end of the list.
llvm::Optional<ArgList> ExtraArgs;
/// \brief Add extra compilation arguments to the start of the list.
llvm::Optional<ArgList> ExtraArgsBefore;
};
/// \brief Abstract interface for retrieving various ClangTidy options.
class ClangTidyOptionsProvider {
public:
static const char OptionsSourceTypeDefaultBinary[];
static const char OptionsSourceTypeCheckCommandLineOption[];
static const char OptionsSourceTypeConfigCommandLineOption[];
virtual ~ClangTidyOptionsProvider() {}
/// \brief Returns global options, which are independent of the file.
virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0;
/// \brief ClangTidyOptions and its source.
//
/// clang-tidy has 3 types of the sources in order of increasing priority:
/// * clang-tidy binary.
/// * '-config' commandline option or a specific configuration file. If the
/// commandline option is specified, clang-tidy will ignore the
/// configuration file.
/// * '-checks' commandline option.
typedef std::pair<ClangTidyOptions, std::string> OptionsSource;
/// \brief Returns an ordered vector of OptionsSources, in order of increasing
/// priority.
virtual std::vector<OptionsSource>
getRawOptions(llvm::StringRef FileName) = 0;
/// \brief Returns options applying to a specific translation unit with the
/// specified \p FileName.
ClangTidyOptions getOptions(llvm::StringRef FileName);
};
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
/// returns the same options for all files.
class DefaultOptionsProvider : public ClangTidyOptionsProvider {
public:
DefaultOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &Options)
: GlobalOptions(GlobalOptions), DefaultOptions(Options) {}
const ClangTidyGlobalOptions &getGlobalOptions() override {
return GlobalOptions;
}
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
private:
ClangTidyGlobalOptions GlobalOptions;
ClangTidyOptions DefaultOptions;
};
/// \brief Implementation of ClangTidyOptions interface, which is used for
/// '-config' command-line option.
class ConfigOptionsProvider : public DefaultOptionsProvider {
public:
ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &ConfigOptions,
const ClangTidyOptions &OverrideOptions);
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
private:
ClangTidyOptions ConfigOptions;
ClangTidyOptions OverrideOptions;
};
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
/// tries to find a configuration file in the closest parent directory of each
/// source file.
///
/// By default, files named ".clang-tidy" will be considered, and the
/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
/// custom set of configuration file names and parsing functions can be
/// specified using the appropriate constructor.
class FileOptionsProvider : public DefaultOptionsProvider {
public:
// \brief A pair of configuration file base name and a function parsing
// configuration from text in the corresponding format.
typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
llvm::StringRef)>>
ConfigFileHandler;
/// \brief Configuration file handlers listed in the order of priority.
///
/// Custom configuration file formats can be supported by constructing the
/// list of handlers and passing it to the appropriate \c FileOptionsProvider
/// constructor. E.g. initialization of a \c FileOptionsProvider with support
/// of a custom configuration file format for files named ".my-tidy-config"
/// could look similar to this:
/// \code
/// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
/// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
/// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
/// return llvm::make_unique<FileOptionsProvider>(
/// GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
/// \endcode
///
/// With the order of handlers shown above, the ".my-tidy-config" file would
/// take precedence over ".clang-tidy" if both reside in the same directory.
typedef std::vector<ConfigFileHandler> ConfigFileHandlers;
/// \brief Initializes the \c FileOptionsProvider instance.
///
/// \param GlobalOptions are just stored and returned to the caller of
/// \c getGlobalOptions.
///
/// \param DefaultOptions are used for all settings not specified in a
/// configuration file.
///
/// If any of the \param OverrideOptions fields are set, they will override
/// whatever options are read from the configuration file.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions);
/// \brief Initializes the \c FileOptionsProvider instance with a custom set
/// of configuration file handlers.
///
/// \param GlobalOptions are just stored and returned to the caller of
/// \c getGlobalOptions.
///
/// \param DefaultOptions are used for all settings not specified in a
/// configuration file.
///
/// If any of the \param OverrideOptions fields are set, they will override
/// whatever options are read from the configuration file.
///
/// \param ConfigHandlers specifies a custom set of configuration file
/// handlers. Each handler is a pair of configuration file name and a function
/// that can parse configuration from this file type. The configuration files
/// in each directory are searched for in the order of appearance in
/// \p ConfigHandlers.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
const ConfigFileHandlers &ConfigHandlers);
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
protected:
/// \brief Try to read configuration files from \p Directory using registered
/// \c ConfigHandlers.
llvm::Optional<OptionsSource> tryReadConfigFile(llvm::StringRef Directory);
llvm::StringMap<OptionsSource> CachedOptions;
ClangTidyOptions OverrideOptions;
ConfigFileHandlers ConfigHandlers;
};
/// \brief Parses LineFilter from JSON and stores it to the \p Options.
std::error_code parseLineFilter(llvm::StringRef LineFilter,
ClangTidyGlobalOptions &Options);
/// \brief Parses configuration from JSON and returns \c ClangTidyOptions or an
/// error.
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(llvm::StringRef Config);
/// \brief Serializes configuration to a YAML-encoded string.
std::string configurationAsText(const ClangTidyOptions &Options);
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H

View File

@@ -1,310 +0,0 @@
#!/usr/bin/env python
#
#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
import os
import re
import sys
# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
# and 'False' if the entry already existed.
def adapt_cmake(module_path, check_name_camel):
filename = os.path.join(module_path, 'CMakeLists.txt')
with open(filename, 'r') as f:
lines = f.readlines()
cpp_file = check_name_camel + '.cpp'
# Figure out whether this check already exists.
for line in lines:
if line.strip() == cpp_file:
return False
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
cpp_found = False
file_added = False
for line in lines:
cpp_line = line.strip().endswith('.cpp')
if (not file_added) and (cpp_line or cpp_found):
cpp_found = True
if (line.strip() > cpp_file) or (not cpp_line):
f.write(' ' + cpp_file + '\n')
file_added = True
f.write(line)
return True
# Adds a header for the new check.
def write_header(module_path, module, check_name, check_name_camel):
check_name_dashes = module + '-' + check_name
filename = os.path.join(module_path, check_name_camel) + '.h'
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
+ check_name.upper().replace('-', '_') + '_H')
f.write('//===--- ')
f.write(os.path.basename(filename))
f.write(' - clang-tidy')
f.write('-' * max(0, 43 - len(os.path.basename(filename))))
f.write('*- C++ -*-===//')
f.write("""
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef %(header_guard)s
#define %(header_guard)s
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace %(module)s {
/// FIXME: Write a short description.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html
class %(check_name)s : public ClangTidyCheck {
public:
%(check_name)s(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace %(module)s
} // namespace tidy
} // namespace clang
#endif // %(header_guard)s
""" % {'header_guard': header_guard,
'check_name': check_name_camel,
'check_name_dashes': check_name_dashes,
'module': module})
# Adds the implementation of the new check.
def write_implementation(module_path, module, check_name_camel):
filename = os.path.join(module_path, check_name_camel) + '.cpp'
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write('//===--- ')
f.write(os.path.basename(filename))
f.write(' - clang-tidy')
f.write('-' * max(0, 52 - len(os.path.basename(filename))))
f.write('-===//')
f.write("""
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "%(check_name)s.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace %(module)s {
void %(check_name)s::registerMatchers(MatchFinder *Finder) {
// FIXME: Add matchers.
Finder->addMatcher(functionDecl().bind("x"), this);
}
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
// FIXME: Add callback implementation.
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
if (MatchedDecl->getName().startswith("awesome_"))
return;
diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
<< MatchedDecl
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
}
} // namespace %(module)s
} // namespace tidy
} // namespace clang
""" % {'check_name': check_name_camel,
'module': module})
# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
os.listdir(module_path))[0]
filename = os.path.join(module_path, modulecpp)
with open(filename, 'r') as f:
lines = f.readlines()
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
header_added = False
header_found = False
check_added = False
check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
'>(\n "' + module + '-' + check_name + '");\n')
for line in lines:
if not header_added:
match = re.search('#include "(.*)"', line)
if match:
header_found = True
if match.group(1) > check_name_camel:
header_added = True
f.write('#include "' + check_name_camel + '.h"\n')
elif header_found:
header_added = True
f.write('#include "' + check_name_camel + '.h"\n')
if not check_added:
if line.strip() == '}':
check_added = True
f.write(check_decl)
else:
match = re.search('registerCheck<(.*)>', line)
if match and match.group(1) > check_name_camel:
check_added = True
f.write(check_decl)
f.write(line)
# Adds a test for the check.
def write_test(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
check_name_dashes + '.cpp'))
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
// FIXME: Add something that triggers the check here.
void f();
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
// FIXME: Verify the applied fix.
// * Make the CHECK patterns specific enough and try to make verified lines
// unique to avoid incorrect matches.
// * Use {{}} for regular expressions.
// CHECK-FIXES: {{^}}void awesome_f();{{$}}
// FIXME: Add something that doesn't trigger the check here.
void awesome_f2();
""" % {'check_name_dashes': check_name_dashes})
# Recreates the list of checks in the docs/clang-tidy/checks directory.
def update_checks_list(clang_tidy_path):
docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
with open(filename, 'r') as f:
lines = f.readlines()
doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
os.listdir(docs_dir))
doc_files.sort()
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)
if match:
return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
'check': check_name,
'target': match.group(1)
}
return ' %s\n' % check_name
checks = map(format_link, doc_files)
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
for line in lines:
f.write(line)
if line.startswith('.. toctree::'):
f.writelines(checks)
break
# Adds a documentation for the check.
def write_docs(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(
module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write(""".. title:: clang-tidy - %(check_name_dashes)s
%(check_name_dashes)s
%(underline)s
FIXME: Describe what patterns does the check detect and why. Give examples.
""" % {'check_name_dashes': check_name_dashes,
'underline': '=' * len(check_name_dashes)})
def main():
if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
update_checks_list(os.path.dirname(sys.argv[0]))
return
if len(sys.argv) != 3:
print """\
Usage: add_new_check.py <module> <check>, e.g.
add_new_check.py misc awesome-functions
Alternatively, run 'add_new_check.py --update-docs' to just update the list of
documentation files."""
return
module = sys.argv[1]
check_name = sys.argv[2]
if check_name.startswith(module):
print 'Check name "%s" must not start with the module "%s". Exiting.' % (
check_name, module)
return
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
check_name.split('-'))) + 'Check'
clang_tidy_path = os.path.dirname(sys.argv[0])
module_path = os.path.join(clang_tidy_path, module)
if not adapt_cmake(module_path, check_name_camel):
return
write_header(module_path, module, check_name, check_name_camel)
write_implementation(module_path, module, check_name_camel)
adapt_module(module_path, module, check_name, check_name_camel)
write_test(module_path, module, check_name)
write_docs(module_path, module, check_name)
update_checks_list(clang_tidy_path)
print('Done. Now it\'s your turn!')
if __name__ == '__main__':
main()

View File

@@ -1,38 +0,0 @@
//===------- BoostTidyModule.cpp - clang-tidy -----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "UseToStringCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace boost {
class BoostModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
}
};
// Register the BoostModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<BoostModule> X("boost-module",
"Add boost checks.");
} // namespace boost
// This anchor is used to force the linker to link in the generated object file
// and thus register the BoostModule.
volatile int BoostModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,14 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyBoostModule
BoostTidyModule.cpp
UseToStringCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)

View File

@@ -1,73 +0,0 @@
//===--- UseToStringCheck.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 "UseToStringCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace boost {
AST_MATCHER(Type, isStrictlyInteger) {
return Node.isIntegerType() && !Node.isAnyCharacterType() &&
!Node.isBooleanType();
}
void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
callExpr(
hasDeclaration(functionDecl(
returns(hasDeclaration(classTemplateSpecializationDecl(
hasName("std::basic_string"),
hasTemplateArgument(0,
templateArgument().bind("char_type"))))),
hasName("boost::lexical_cast"),
hasParameter(0, hasType(qualType(has(substTemplateTypeParmType(
isStrictlyInteger()))))))),
argumentCountIs(1), unless(isInTemplateInstantiation()))
.bind("to_string"),
this);
}
void UseToStringCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("to_string");
auto CharType =
Result.Nodes.getNodeAs<TemplateArgument>("char_type")->getAsType();
StringRef StringType;
if (CharType->isSpecificBuiltinType(BuiltinType::Char_S) ||
CharType->isSpecificBuiltinType(BuiltinType::Char_U))
StringType = "string";
else if (CharType->isSpecificBuiltinType(BuiltinType::WChar_S) ||
CharType->isSpecificBuiltinType(BuiltinType::WChar_U))
StringType = "wstring";
else
return;
auto Loc = Call->getLocStart();
auto Diag =
diag(Loc, "use std::to_%0 instead of boost::lexical_cast<std::%0>")
<< StringType;
if (Loc.isMacroID())
return;
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(Call->getLocStart(),
Call->getArg(0)->getLocStart()),
(llvm::Twine("std::to_") + StringType + "(").str());
}
} // namespace boost
} // namespace tidy
} // namespace clang

Some files were not shown because too many files have changed in this diff Show More