зеркало из https://github.com/microsoft/clang.git
This patch implements an AST matching framework that allows to write
tools that match on the C++ ASTs. The main interface is in ASTMatchers.h, an example implementation of a tool that removes redundant .c_str() calls is in the example RemoveCStrCalls.cpp. Various contributions: Zhanyong Wan, Chandler Carruth, Marcin Kowalczyk, Wei Xu, James Dennett. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132374 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
d65e091631
Коммит
64cbdf3709
|
@ -4,3 +4,4 @@ add_clang_executable(clang-check
|
|||
ClangCheck.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(RemoveCStrCalls)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
set(LLVM_USED_LIBS clangTooling clangBasic clangAST)
|
||||
|
||||
add_clang_executable(remove-cstr-calls
|
||||
RemoveCStrCalls.cpp
|
||||
)
|
|
@ -0,0 +1,229 @@
|
|||
//===- examples/Tooling/RemoveCStrCalls.cpp - Redundant c_str call removal ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a tool that prints replacements that remove redundant
|
||||
// calls of c_str() on strings.
|
||||
//
|
||||
// Usage:
|
||||
// remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
|
||||
//
|
||||
// Where <cmake-output-dir> is a CMake build directory in which a file named
|
||||
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
|
||||
// CMake to get this output).
|
||||
//
|
||||
// <file1> ... specify the paths of files in the CMake source tree. This path
|
||||
// is looked up in the compile command database. If the path of a file is
|
||||
// absolute, it needs to point into CMake's source tree. If the path is
|
||||
// relative, the current working directory needs to be in the CMake source
|
||||
// tree and the file must be in a subdirectory of the current working
|
||||
// directory. "./" prefixes in the relative files will be automatically
|
||||
// removed, but the rest of a relative path must be a suffix of a path in
|
||||
// the compile command line database.
|
||||
//
|
||||
// For example, to use remove-cstr-calls on all files in a subtree of the
|
||||
// source tree, use:
|
||||
//
|
||||
// /path/in/subtree $ find . -name '*.cpp'|
|
||||
// xargs remove-cstr-calls /path/to/source
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Tooling/ASTMatchers.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
|
||||
using namespace clang::tooling::match;
|
||||
|
||||
// FIXME: Pull out helper methods in here into more fitting places.
|
||||
|
||||
// Returns the text that makes up 'node' in the source.
|
||||
// Returns an empty string if the text cannot be found.
|
||||
template <typename T>
|
||||
std::string GetText(const clang::SourceManager &SourceManager, const T &Node) {
|
||||
clang::SourceLocation StartSpellingLocatino =
|
||||
SourceManager.getSpellingLoc(Node.getLocStart());
|
||||
clang::SourceLocation EndSpellingLocation =
|
||||
SourceManager.getSpellingLoc(Node.getLocEnd());
|
||||
if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
|
||||
return std::string();
|
||||
}
|
||||
bool Invalid = true;
|
||||
const char *Text =
|
||||
SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
|
||||
if (Invalid) {
|
||||
return std::string();
|
||||
}
|
||||
std::pair<clang::FileID, unsigned> Start =
|
||||
SourceManager.getDecomposedLoc(StartSpellingLocatino);
|
||||
std::pair<clang::FileID, unsigned> End =
|
||||
SourceManager.getDecomposedLoc(clang::Lexer::getLocForEndOfToken(
|
||||
EndSpellingLocation, 0, SourceManager, clang::LangOptions()));
|
||||
if (Start.first != End.first) {
|
||||
// Start and end are in different files.
|
||||
return std::string();
|
||||
}
|
||||
if (End.second < Start.second) {
|
||||
// Shuffling text with macros may cause this.
|
||||
return std::string();
|
||||
}
|
||||
return std::string(Text, End.second - Start.second);
|
||||
}
|
||||
|
||||
// Returns the position of the spelling location of a node inside a file.
|
||||
// The format is:
|
||||
// "<start_line>:<start_column>:<end_line>:<end_column>"
|
||||
template <typename T1>
|
||||
void PrintPosition(
|
||||
llvm::raw_ostream &OS,
|
||||
const clang::SourceManager &SourceManager, const T1 &Node) {
|
||||
clang::SourceLocation StartSpellingLocation =
|
||||
SourceManager.getSpellingLoc(Node.getLocStart());
|
||||
clang::SourceLocation EndSpellingLocation =
|
||||
SourceManager.getSpellingLoc(Node.getLocEnd());
|
||||
clang::PresumedLoc Start =
|
||||
SourceManager.getPresumedLoc(StartSpellingLocation);
|
||||
clang::SourceLocation EndToken = clang::Lexer::getLocForEndOfToken(
|
||||
EndSpellingLocation, 1, SourceManager, clang::LangOptions());
|
||||
clang::PresumedLoc End = SourceManager.getPresumedLoc(EndToken);
|
||||
OS << Start.getLine() << ":" << Start.getColumn() << ":"
|
||||
<< End.getLine() << ":" << End.getColumn();
|
||||
}
|
||||
|
||||
class ReportPosition : public clang::tooling::MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void Run(const clang::tooling::MatchFinder::MatchResult &Result) {
|
||||
llvm::outs() << "Found!\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Return true if expr needs to be put in parens when it is an
|
||||
// argument of a prefix unary operator, e.g. when it is a binary or
|
||||
// ternary operator syntactically.
|
||||
bool NeedParensAfterUnaryOperator(const clang::Expr &ExprNode) {
|
||||
if (llvm::dyn_cast<clang::BinaryOperator>(&ExprNode) ||
|
||||
llvm::dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
|
||||
return true;
|
||||
}
|
||||
if (const clang::CXXOperatorCallExpr *op =
|
||||
llvm::dyn_cast<clang::CXXOperatorCallExpr>(&ExprNode)) {
|
||||
return op->getNumArgs() == 2 &&
|
||||
op->getOperator() != clang::OO_PlusPlus &&
|
||||
op->getOperator() != clang::OO_MinusMinus &&
|
||||
op->getOperator() != clang::OO_Call &&
|
||||
op->getOperator() != clang::OO_Subscript;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format a pointer to an expression: prefix with '*' but simplify
|
||||
// when it already begins with '&'. Return empty string on failure.
|
||||
std::string FormatDereference(const clang::SourceManager &SourceManager,
|
||||
const clang::Expr &ExprNode) {
|
||||
if (const clang::UnaryOperator *Op =
|
||||
llvm::dyn_cast<clang::UnaryOperator>(&ExprNode)) {
|
||||
if (Op->getOpcode() == clang::UO_AddrOf) {
|
||||
// Strip leading '&'.
|
||||
return GetText(SourceManager, *Op->getSubExpr()->IgnoreParens());
|
||||
}
|
||||
}
|
||||
const std::string Text = GetText(SourceManager, ExprNode);
|
||||
if (Text.empty()) return std::string();
|
||||
// Add leading '*'.
|
||||
if (NeedParensAfterUnaryOperator(ExprNode)) {
|
||||
return std::string("*(") + Text + ")";
|
||||
}
|
||||
return std::string("*") + Text;
|
||||
}
|
||||
|
||||
class FixCStrCall : public clang::tooling::MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void Run(const clang::tooling::MatchFinder::MatchResult &Result) {
|
||||
const clang::CallExpr *Call =
|
||||
Result.Nodes.GetStmtAs<clang::CallExpr>("call");
|
||||
const clang::Expr *Arg =
|
||||
Result.Nodes.GetStmtAs<clang::Expr>("arg");
|
||||
const bool Arrow =
|
||||
Result.Nodes.GetStmtAs<clang::MemberExpr>("member")->isArrow();
|
||||
// Replace the "call" node with the "arg" node, prefixed with '*'
|
||||
// if the call was using '->' rather than '.'.
|
||||
const std::string ArgText = Arrow ?
|
||||
FormatDereference(*Result.SourceManager, *Arg) :
|
||||
GetText(*Result.SourceManager, *Arg);
|
||||
if (ArgText.empty()) return;
|
||||
|
||||
llvm::outs() <<
|
||||
Result.SourceManager->getBufferName(Call->getLocStart(), NULL) << ":";
|
||||
PrintPosition(llvm::outs(), *Result.SourceManager, *Call);
|
||||
llvm::outs() << ":" << ArgText << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
const char *StringConstructor =
|
||||
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
|
||||
"::basic_string";
|
||||
|
||||
const char *StringCStrMethod =
|
||||
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
|
||||
"::c_str";
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
clang::tooling::ClangTool Tool(argc, argv);
|
||||
clang::tooling::MatchFinder finder;
|
||||
finder.AddMatcher(
|
||||
ConstructorCall(
|
||||
HasDeclaration(Method(HasName(StringConstructor))),
|
||||
ArgumentCountIs(2),
|
||||
// The first argument must have the form x.c_str() or p->c_str()
|
||||
// where the method is string::c_str(). We can use the copy
|
||||
// constructor of string instead (or the compiler might share
|
||||
// the string object).
|
||||
HasArgument(
|
||||
0,
|
||||
Id("call", Call(
|
||||
Callee(Id("member", MemberExpression())),
|
||||
Callee(Method(HasName(StringCStrMethod))),
|
||||
On(Id("arg", Expression()))))),
|
||||
// The second argument is the alloc object which must not be
|
||||
// present explicitly.
|
||||
HasArgument(
|
||||
1,
|
||||
DefaultArgument())), new FixCStrCall);
|
||||
finder.AddMatcher(
|
||||
ConstructorCall(
|
||||
// Implicit constructors of these classes are overloaded
|
||||
// wrt. string types and they internally make a StringRef
|
||||
// referring to the argument. Passing a string directly to
|
||||
// them is preferred to passing a char pointer.
|
||||
HasDeclaration(Method(AnyOf(
|
||||
HasName("::llvm::StringRef::StringRef"),
|
||||
HasName("::llvm::Twine::Twine")))),
|
||||
ArgumentCountIs(1),
|
||||
// The only argument must have the form x.c_str() or p->c_str()
|
||||
// where the method is string::c_str(). StringRef also has
|
||||
// a constructor from string which is more efficient (avoids
|
||||
// strlen), so we can construct StringRef from the string
|
||||
// directly.
|
||||
HasArgument(
|
||||
0,
|
||||
Id("call", Call(
|
||||
Callee(Id("member", MemberExpression())),
|
||||
Callee(Method(HasName(StringCStrMethod))),
|
||||
On(Id("arg", Expression())))))),
|
||||
new FixCStrCall);
|
||||
return Tool.Run(finder.NewFrontendActionFactory());
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
#===- replace.py - Applying code rewrites --------------------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
#
|
||||
# This script applies the rewrites generated by replace-cstr-calls on a source
|
||||
# tree.
|
||||
#
|
||||
# Usage:
|
||||
# ./replace.py < /path/to/replace-cstr-calls-output
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
import fileinput
|
||||
import re
|
||||
import sys
|
||||
|
||||
for line in sys.stdin.readlines():
|
||||
# The format is:
|
||||
# <file>:<start_line>:<start_column>:<end_line>:<end_column>:<replacement>
|
||||
# FIXME: This currently does not support files with colons, we'll need to
|
||||
# figure out a format when we implement more refactoring support.
|
||||
match = re.match(r'(.*):(\d+):(\d+):(\d+):(\d+):(.*)$', line)
|
||||
if match is not None:
|
||||
file_name = match.group(1)
|
||||
start_line, start_column = int(match.group(2)), int(match.group(3))
|
||||
end_line, end_column = int(match.group(4)), int(match.group(5))
|
||||
replacement = match.group(6)
|
||||
if start_line != end_line:
|
||||
print ('Skipping match "%s": only single line ' +
|
||||
'replacements are supported') % line.strip()
|
||||
continue
|
||||
try:
|
||||
replace_file = fileinput.input(file_name, inplace=1)
|
||||
for replace_line in replace_file:
|
||||
# FIXME: Looping over the file for each replacement is both inefficient
|
||||
# and incorrect if replacements add or remove lines.
|
||||
if replace_file.lineno() == start_line:
|
||||
sys.stdout.write(replace_line[:start_column-1] + replacement +
|
||||
replace_line[end_column:])
|
||||
else:
|
||||
sys.stdout.write(replace_line)
|
||||
except OSError, e:
|
||||
print 'Cannot open %s for editing' % file_name
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -43,11 +43,11 @@ bool RunSyntaxOnlyToolOnCode(
|
|||
/// \param Argv The command line arguments, including the path the binary
|
||||
/// was started with (Argv[0]).
|
||||
bool RunToolWithFlags(
|
||||
clang::FrontendAction* ToolAction, int Argc, char *Argv[]);
|
||||
clang::FrontendAction *ToolAction, int Argc, char *Argv[]);
|
||||
|
||||
/// \brief Converts a vector<string> into a vector<char*> suitable to pass
|
||||
/// to main-style functions taking (int Argc, char *Argv[]).
|
||||
std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command);
|
||||
std::vector<char*> CommandLineToArgv(const std::vector<std::string> *Command);
|
||||
|
||||
/// \brief Specifies the working directory and command of a compilation.
|
||||
struct CompileCommand {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,564 @@
|
|||
//===--- ASTMatchers.cpp - Structural query framework ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a framework of AST matchers that can be used to express
|
||||
// structural queries on C++ code.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Tooling/ASTMatchers.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
// Returns the value that 'a_map' maps 'key' to, or NULL if 'key' is
|
||||
// not in 'a_map'.
|
||||
template <typename Map>
|
||||
static const typename Map::mapped_type *Find(
|
||||
const Map &AMap, const typename Map::key_type &Key) {
|
||||
typename Map::const_iterator It = AMap.find(Key);
|
||||
return It == AMap.end() ? NULL : &It->second;
|
||||
}
|
||||
|
||||
// We use memoization to avoid running the same matcher on the same
|
||||
// AST node twice. This pair is the key for looking up match
|
||||
// result. It consists of an ID of the MatcherInterface (for
|
||||
// identifying the matcher) and a pointer to the AST node.
|
||||
typedef std::pair<uint64_t, const void*> UntypedMatchInput;
|
||||
|
||||
// Used to store the result of a match and possibly bound nodes.
|
||||
struct MemoizedMatchResult {
|
||||
bool ResultOfMatch;
|
||||
BoundNodes Nodes;
|
||||
};
|
||||
|
||||
// A RecursiveASTVisitor that traverses all children or all descendants of
|
||||
// a node.
|
||||
class MatchChildASTVisitor
|
||||
: public clang::RecursiveASTVisitor<MatchChildASTVisitor> {
|
||||
public:
|
||||
typedef clang::RecursiveASTVisitor<MatchChildASTVisitor> VisitorBase;
|
||||
|
||||
// Creates an AST visitor that matches 'matcher' on all children or
|
||||
// descendants of a traversed node. max_depth is the maximum depth
|
||||
// to traverse: use 1 for matching the children and INT_MAX for
|
||||
// matching the descendants.
|
||||
MatchChildASTVisitor(const UntypedBaseMatcher *BaseMatcher,
|
||||
ASTMatchFinder *Finder,
|
||||
BoundNodesBuilder *Builder,
|
||||
int MaxDepth,
|
||||
ASTMatchFinder::TraversalMethod Traversal)
|
||||
: BaseMatcher(BaseMatcher),
|
||||
Finder(Finder),
|
||||
Builder(Builder),
|
||||
CurrentDepth(-1),
|
||||
MaxDepth(MaxDepth),
|
||||
Traversal(Traversal),
|
||||
Matches(false) {}
|
||||
|
||||
// Returns true if a match is found in the subtree rooted at the
|
||||
// given AST node. This is done via a set of mutually recursive
|
||||
// functions. Here's how the recursion is done (the *wildcard can
|
||||
// actually be Decl, Stmt, or Type):
|
||||
//
|
||||
// - Traverse(node) calls BaseTraverse(node) when it needs
|
||||
// to visit the descendants of node.
|
||||
// - BaseTraverse(node) then calls (via VisitorBase::Traverse*(node))
|
||||
// Traverse*(c) for each child c of 'node'.
|
||||
// - Traverse*(c) in turn calls Traverse(c), completing the
|
||||
// recursion.
|
||||
template <typename T>
|
||||
bool FindMatch(const T &Node) {
|
||||
Reset();
|
||||
Traverse(Node);
|
||||
return Matches;
|
||||
}
|
||||
|
||||
// The following are overriding methods from the base visitor class.
|
||||
// They are public only to allow CRTP to work. They are *not *part
|
||||
// of the public API of this class.
|
||||
bool TraverseDecl(clang::Decl *DeclNode) {
|
||||
return (DeclNode == NULL) || Traverse(*DeclNode);
|
||||
}
|
||||
bool TraverseStmt(clang::Stmt *StmtNode) {
|
||||
const clang::Stmt *StmtToTraverse = StmtNode;
|
||||
if (Traversal ==
|
||||
ASTMatchFinder::kIgnoreImplicitCastsAndParentheses) {
|
||||
const clang::Expr *ExprNode = dyn_cast_or_null<clang::Expr>(StmtNode);
|
||||
if (ExprNode != NULL) {
|
||||
StmtToTraverse = ExprNode->IgnoreParenImpCasts();
|
||||
}
|
||||
}
|
||||
return (StmtToTraverse == NULL) || Traverse(*StmtToTraverse);
|
||||
}
|
||||
bool TraverseType(clang::QualType TypeNode) {
|
||||
return Traverse(TypeNode);
|
||||
}
|
||||
|
||||
bool shouldVisitTemplateInstantiations() const { return true; }
|
||||
|
||||
private:
|
||||
// Resets the state of this object.
|
||||
void Reset() {
|
||||
Matches = false;
|
||||
CurrentDepth = -1;
|
||||
}
|
||||
|
||||
// Forwards the call to the corresponding Traverse*() method in the
|
||||
// base visitor class.
|
||||
bool BaseTraverse(const clang::Decl &DeclNode) {
|
||||
return VisitorBase::TraverseDecl(const_cast<clang::Decl*>(&DeclNode));
|
||||
}
|
||||
bool BaseTraverse(const clang::Stmt &StmtNode) {
|
||||
return VisitorBase::TraverseStmt(const_cast<clang::Stmt*>(&StmtNode));
|
||||
}
|
||||
bool BaseTraverse(clang::QualType TypeNode) {
|
||||
return VisitorBase::TraverseType(TypeNode);
|
||||
}
|
||||
|
||||
// Traverses the subtree rooted at 'node'; returns true if the
|
||||
// traversal should continue after this function returns; also sets
|
||||
// matched_ to true if a match is found during the traversal.
|
||||
template <typename T>
|
||||
bool Traverse(const T &Node) {
|
||||
COMPILE_ASSERT(IsBaseType<T>::value,
|
||||
traverse_can_only_be_instantiated_with_base_type);
|
||||
++CurrentDepth;
|
||||
bool ShouldContinue;
|
||||
if (CurrentDepth == 0) {
|
||||
// We don't want to match the root node, so just recurse.
|
||||
ShouldContinue = BaseTraverse(Node);
|
||||
} else if (BaseMatcher->Matches(Node, Finder, Builder)) {
|
||||
Matches = true;
|
||||
ShouldContinue = false; // Abort as soon as a match is found.
|
||||
} else if (CurrentDepth < MaxDepth) {
|
||||
// The current node doesn't match, and we haven't reached the
|
||||
// maximum depth yet, so recurse.
|
||||
ShouldContinue = BaseTraverse(Node);
|
||||
} else {
|
||||
// The current node doesn't match, and we have reached the
|
||||
// maximum depth, so don't recurse (but continue the traversal
|
||||
// such that other nodes at the current level can be visited).
|
||||
ShouldContinue = true;
|
||||
}
|
||||
--CurrentDepth;
|
||||
return ShouldContinue;
|
||||
}
|
||||
|
||||
const UntypedBaseMatcher *const BaseMatcher;
|
||||
ASTMatchFinder *const Finder;
|
||||
BoundNodesBuilder *const Builder;
|
||||
int CurrentDepth;
|
||||
const int MaxDepth;
|
||||
const ASTMatchFinder::TraversalMethod Traversal;
|
||||
bool Matches;
|
||||
};
|
||||
|
||||
// Controls the outermost traversal of the AST and allows to match multiple
|
||||
// matchers.
|
||||
class MatchASTVisitor : public clang::RecursiveASTVisitor<MatchASTVisitor>,
|
||||
public ASTMatchFinder {
|
||||
public:
|
||||
MatchASTVisitor(std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> > *Triggers,
|
||||
clang::SourceManager *VisitorSourceManager,
|
||||
clang::LangOptions *LanguageOptions)
|
||||
: Triggers(Triggers),
|
||||
VisitorSourceManager(VisitorSourceManager),
|
||||
LanguageOptions(LanguageOptions),
|
||||
ActiveASTContext(NULL) {
|
||||
assert(VisitorSourceManager != NULL);
|
||||
assert(LanguageOptions != NULL);
|
||||
// FIXME: add rewriter_(*source_manager, *language_options)
|
||||
}
|
||||
|
||||
void set_active_ast_context(clang::ASTContext *NewActiveASTContext) {
|
||||
ActiveASTContext = NewActiveASTContext;
|
||||
}
|
||||
|
||||
// The following Visit*() and Traverse*() functions "override"
|
||||
// methods in RecursiveASTVisitor.
|
||||
|
||||
bool VisitTypedefDecl(clang::TypedefDecl *DeclNode) {
|
||||
// When we see 'typedef A B', we add name 'B' to the set of names
|
||||
// A's canonical type maps to. This is necessary for implementing
|
||||
// IsDerivedFrom(x) properly, where x can be the name of the base
|
||||
// class or any of its aliases.
|
||||
//
|
||||
// In general, the is-alias-of (as defined by typedefs) relation
|
||||
// is tree-shaped, as you can typedef a type more than once. For
|
||||
// example,
|
||||
//
|
||||
// typedef A B;
|
||||
// typedef A C;
|
||||
// typedef C D;
|
||||
// typedef C E;
|
||||
//
|
||||
// gives you
|
||||
//
|
||||
// A
|
||||
// |- B
|
||||
// `- C
|
||||
// |- D
|
||||
// `- E
|
||||
//
|
||||
// It is wrong to assume that the relation is a chain. A correct
|
||||
// implementation of IsDerivedFrom() needs to recognize that B and
|
||||
// E are aliases, even though neither is a typedef of the other.
|
||||
// Therefore, we cannot simply walk through one typedef chain to
|
||||
// find out whether the type name matches.
|
||||
const clang::Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr();
|
||||
const clang::Type *CanonicalType = // root of the typedef tree
|
||||
ActiveASTContext->getCanonicalType(TypeNode);
|
||||
TypeToUnqualifiedAliases[CanonicalType].insert(
|
||||
DeclNode->getName().str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraverseDecl(clang::Decl *DeclNode);
|
||||
bool TraverseStmt(clang::Stmt *StmtNode);
|
||||
bool TraverseType(clang::QualType TypeNode);
|
||||
bool TraverseTypeLoc(clang::TypeLoc TypeNode);
|
||||
|
||||
// Matches children or descendants of 'Node' with 'BaseMatcher'.
|
||||
template <typename T>
|
||||
bool MemoizedMatchesRecursively(
|
||||
const T &Node, const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder, int MaxDepth,
|
||||
TraversalMethod Traversal) {
|
||||
COMPILE_ASSERT((llvm::is_same<T, clang::Decl>::value) ||
|
||||
(llvm::is_same<T, clang::Stmt>::value),
|
||||
type_does_not_support_memoization);
|
||||
const UntypedMatchInput input(BaseMatcher.GetID(), &Node);
|
||||
std::pair <MemoizationMap::iterator, bool>
|
||||
InsertResult = ResultCache.insert(
|
||||
std::make_pair(input, MemoizedMatchResult()));
|
||||
if (InsertResult.second) {
|
||||
BoundNodesBuilder DescendantBoundNodesBuilder;
|
||||
InsertResult.first->second.ResultOfMatch =
|
||||
MatchesRecursively(Node, BaseMatcher, &DescendantBoundNodesBuilder,
|
||||
MaxDepth, Traversal);
|
||||
InsertResult.first->second.Nodes =
|
||||
DescendantBoundNodesBuilder.Build();
|
||||
}
|
||||
InsertResult.first->second.Nodes.CopyTo(Builder);
|
||||
return InsertResult.first->second.ResultOfMatch;
|
||||
}
|
||||
|
||||
// Matches children or descendants of 'Node' with 'BaseMatcher'.
|
||||
template <typename T>
|
||||
bool MatchesRecursively(
|
||||
const T &Node, const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder, int MaxDepth,
|
||||
TraversalMethod Traversal) {
|
||||
MatchChildASTVisitor Visitor(
|
||||
&BaseMatcher, this, Builder, MaxDepth, Traversal);
|
||||
return Visitor.FindMatch(Node);
|
||||
}
|
||||
|
||||
virtual bool ClassIsDerivedFrom(const clang::CXXRecordDecl *Declaration,
|
||||
const std::string &BaseName) const;
|
||||
|
||||
// Implements ASTMatchFinder::MatchesChildOf.
|
||||
virtual bool MatchesChildOf(const clang::Decl &DeclNode,
|
||||
const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder,
|
||||
TraversalMethod Traversal) {
|
||||
return MatchesRecursively(
|
||||
DeclNode, BaseMatcher, Builder, 1, Traversal);
|
||||
}
|
||||
virtual bool MatchesChildOf(const clang::Stmt &StmtNode,
|
||||
const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder,
|
||||
TraversalMethod Traversal) {
|
||||
return MatchesRecursively(
|
||||
StmtNode, BaseMatcher, Builder, 1, Traversal);
|
||||
}
|
||||
|
||||
// Implements ASTMatchFinder::MatchesDescendantOf.
|
||||
virtual bool MatchesDescendantOf(const clang::Decl &DeclNode,
|
||||
const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder) {
|
||||
return MemoizedMatchesRecursively(
|
||||
DeclNode, BaseMatcher, Builder, INT_MAX, kAsIs);
|
||||
}
|
||||
virtual bool MatchesDescendantOf(const clang::Stmt &StmtNode,
|
||||
const UntypedBaseMatcher &BaseMatcher,
|
||||
BoundNodesBuilder *Builder) {
|
||||
return MemoizedMatchesRecursively(
|
||||
StmtNode, BaseMatcher, Builder, INT_MAX, kAsIs);
|
||||
}
|
||||
|
||||
bool shouldVisitTemplateInstantiations() const { return true; }
|
||||
|
||||
private:
|
||||
// Returns true if 'TypeNode' is also known by the name 'Name'. In other
|
||||
// words, there is a type (including typedef) with the name 'Name'
|
||||
// that is equal to 'TypeNode'.
|
||||
bool TypeHasAlias(
|
||||
const clang::Type *TypeNode, const std::string &Name) const {
|
||||
const clang::Type *const CanonicalType =
|
||||
ActiveASTContext->getCanonicalType(TypeNode);
|
||||
const std::set<std::string> *UnqualifiedAlias =
|
||||
Find(TypeToUnqualifiedAliases, CanonicalType);
|
||||
return UnqualifiedAlias != NULL && UnqualifiedAlias->count(Name) > 0;
|
||||
}
|
||||
|
||||
// Matches all registered matchers on the given node and calls the
|
||||
// result callback for every node that matches.
|
||||
template <typename T>
|
||||
void Match(const T &node) {
|
||||
for (std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> >::const_iterator
|
||||
It = Triggers->begin(), End = Triggers->end();
|
||||
It != End; ++It) {
|
||||
BoundNodesBuilder Builder;
|
||||
if (It->first->Matches(node, this, &Builder)) {
|
||||
MatchFinder::MatchResult Result;
|
||||
Result.Nodes = Builder.Build();
|
||||
Result.Context = ActiveASTContext;
|
||||
Result.SourceManager = VisitorSourceManager;
|
||||
It->second->Run(Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> > *const Triggers;
|
||||
clang::SourceManager *const VisitorSourceManager;
|
||||
clang::LangOptions *const LanguageOptions;
|
||||
clang::ASTContext *ActiveASTContext;
|
||||
|
||||
// Maps a canonical type to the names of its typedefs.
|
||||
llvm::DenseMap<const clang::Type*, std::set<std::string> >
|
||||
TypeToUnqualifiedAliases;
|
||||
|
||||
// Maps (matcher, node) -> the match result for memoization.
|
||||
typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap;
|
||||
MemoizationMap ResultCache;
|
||||
};
|
||||
|
||||
// Returns true if the given class is directly or indirectly derived
|
||||
// from a base type with the given name. A class is considered to be
|
||||
// also derived from itself.
|
||||
bool MatchASTVisitor::ClassIsDerivedFrom(
|
||||
const clang::CXXRecordDecl *Declaration,
|
||||
const std::string &BaseName) const {
|
||||
if (std::string(Declaration->getName()) == BaseName) {
|
||||
return true;
|
||||
}
|
||||
if (!Declaration->hasDefinition()) {
|
||||
return false;
|
||||
}
|
||||
typedef clang::CXXRecordDecl::base_class_const_iterator BaseIterator;
|
||||
for (BaseIterator It = Declaration->bases_begin(),
|
||||
End = Declaration->bases_end(); It != End; ++It) {
|
||||
const clang::Type *TypeNode = It->getType().getTypePtr();
|
||||
|
||||
if (TypeHasAlias(TypeNode, BaseName))
|
||||
return true;
|
||||
|
||||
// clang::Type::getAs<...>() drills through typedefs.
|
||||
if (TypeNode->getAs<clang::DependentNameType>() != NULL ||
|
||||
TypeNode->getAs<clang::TemplateTypeParmType>() != NULL) {
|
||||
// Dependent names and template TypeNode parameters will be matched when
|
||||
// the template is instantiated.
|
||||
continue;
|
||||
}
|
||||
clang::CXXRecordDecl *ClassDecl = NULL;
|
||||
clang::TemplateSpecializationType const *TemplateType =
|
||||
TypeNode->getAs<clang::TemplateSpecializationType>();
|
||||
if (TemplateType != NULL) {
|
||||
if (TemplateType->getTemplateName().isDependent()) {
|
||||
// Dependent template specializations will be matched when the
|
||||
// template is instantiated.
|
||||
continue;
|
||||
}
|
||||
// For template specialization types which are specializing a template
|
||||
// declaration which is an explicit or partial specialization of another
|
||||
// template declaration, getAsCXXRecordDecl() returns the corresponding
|
||||
// ClassTemplateSpecializationDecl.
|
||||
//
|
||||
// For template specialization types which are specializing a template
|
||||
// declaration which is neither an explicit nor partial specialization of
|
||||
// another template declaration, getAsCXXRecordDecl() returns NULL and
|
||||
// we get the CXXRecordDecl of the templated declaration.
|
||||
clang::CXXRecordDecl *SpecializationDecl =
|
||||
TemplateType->getAsCXXRecordDecl();
|
||||
if (SpecializationDecl != NULL) {
|
||||
ClassDecl = SpecializationDecl;
|
||||
} else {
|
||||
ClassDecl = llvm::dyn_cast<clang::CXXRecordDecl>(
|
||||
TemplateType->getTemplateName()
|
||||
.getAsTemplateDecl()->getTemplatedDecl());
|
||||
}
|
||||
} else {
|
||||
ClassDecl = TypeNode->getAsCXXRecordDecl();
|
||||
}
|
||||
assert(ClassDecl != NULL);
|
||||
assert(ClassDecl != Declaration);
|
||||
if (ClassIsDerivedFrom(ClassDecl, BaseName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseDecl(clang::Decl *DeclNode) {
|
||||
if (DeclNode == NULL) {
|
||||
return true;
|
||||
}
|
||||
Match(*DeclNode);
|
||||
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseStmt(clang::Stmt *StmtNode) {
|
||||
if (StmtNode == NULL) {
|
||||
return true;
|
||||
}
|
||||
Match(*StmtNode);
|
||||
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode);
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseType(clang::QualType TypeNode) {
|
||||
Match(TypeNode);
|
||||
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseTypeLoc(clang::TypeLoc TypeLoc) {
|
||||
return clang::RecursiveASTVisitor<MatchASTVisitor>::
|
||||
TraverseType(TypeLoc.getType());
|
||||
}
|
||||
|
||||
class MatchASTConsumer : public clang::ASTConsumer {
|
||||
public:
|
||||
MatchASTConsumer(std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> > *Triggers,
|
||||
MatchFinder::ParsingDoneTestCallback *ParsingDone,
|
||||
clang::SourceManager *ConsumerSourceManager,
|
||||
clang::LangOptions *LanaguageOptions)
|
||||
: Visitor(Triggers, ConsumerSourceManager, LanaguageOptions),
|
||||
ParsingDone(ParsingDone) {}
|
||||
|
||||
private:
|
||||
virtual void HandleTranslationUnit(
|
||||
clang::ASTContext &Context) { // NOLINT: external API uses refs
|
||||
if (ParsingDone != NULL) {
|
||||
ParsingDone->Run();
|
||||
}
|
||||
Visitor.set_active_ast_context(&Context);
|
||||
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
||||
Visitor.set_active_ast_context(NULL);
|
||||
}
|
||||
|
||||
MatchASTVisitor Visitor;
|
||||
MatchFinder::ParsingDoneTestCallback *ParsingDone;
|
||||
};
|
||||
|
||||
class MatchASTAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
explicit MatchASTAction(
|
||||
std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> > *Triggers,
|
||||
MatchFinder::ParsingDoneTestCallback *ParsingDone)
|
||||
: Triggers(Triggers),
|
||||
ParsingDone(ParsingDone) {}
|
||||
|
||||
private:
|
||||
clang::ASTConsumer *CreateASTConsumer(
|
||||
clang::CompilerInstance &Compiler,
|
||||
llvm::StringRef) {
|
||||
return new MatchASTConsumer(Triggers,
|
||||
ParsingDone,
|
||||
&Compiler.getSourceManager(),
|
||||
&Compiler.getLangOpts());
|
||||
}
|
||||
|
||||
std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> > *const Triggers;
|
||||
MatchFinder::ParsingDoneTestCallback *ParsingDone;
|
||||
};
|
||||
|
||||
MatchFinder::MatchCallback::~MatchCallback() {}
|
||||
MatchFinder::ParsingDoneTestCallback::~ParsingDoneTestCallback() {}
|
||||
|
||||
MatchFinder::MatchFinder() : ParsingDone(NULL) {}
|
||||
|
||||
MatchFinder::~MatchFinder() {
|
||||
for (std::vector< std::pair<const UntypedBaseMatcher*,
|
||||
MatchFinder::MatchCallback*> >::const_iterator
|
||||
It = Triggers.begin(), End = Triggers.end();
|
||||
It != End; ++It) {
|
||||
delete It->first;
|
||||
delete It->second;
|
||||
}
|
||||
}
|
||||
|
||||
void MatchFinder::AddMatcher(const Matcher<clang::Decl> &NodeMatch,
|
||||
MatchCallback *Action) {
|
||||
Triggers.push_back(std::make_pair(
|
||||
new TypedBaseMatcher<clang::Decl>(NodeMatch), Action));
|
||||
}
|
||||
|
||||
void MatchFinder::AddMatcher(const Matcher<clang::QualType> &NodeMatch,
|
||||
MatchCallback *Action) {
|
||||
Triggers.push_back(std::make_pair(
|
||||
new TypedBaseMatcher<clang::QualType>(NodeMatch), Action));
|
||||
}
|
||||
|
||||
void MatchFinder::AddMatcher(const Matcher<clang::Stmt> &NodeMatch,
|
||||
MatchCallback *Action) {
|
||||
Triggers.push_back(std::make_pair(
|
||||
new TypedBaseMatcher<clang::Stmt>(NodeMatch), Action));
|
||||
}
|
||||
|
||||
bool MatchFinder::FindAll(const std::string &Code) {
|
||||
return RunSyntaxOnlyToolOnCode(
|
||||
new MatchASTAction(&Triggers, ParsingDone), Code);
|
||||
}
|
||||
|
||||
clang::FrontendAction *MatchFinder::NewVisitorAction() {
|
||||
return new MatchASTAction(&Triggers, ParsingDone);
|
||||
}
|
||||
|
||||
class MatchFinderFrontendActionFactory : public FrontendActionFactory {
|
||||
public:
|
||||
explicit MatchFinderFrontendActionFactory(MatchFinder *Finder)
|
||||
: Finder(Finder) {}
|
||||
|
||||
virtual clang::FrontendAction *New() {
|
||||
return Finder->NewVisitorAction();
|
||||
}
|
||||
|
||||
private:
|
||||
MatchFinder *const Finder;
|
||||
};
|
||||
|
||||
FrontendActionFactory *MatchFinder::NewFrontendActionFactory() {
|
||||
return new MatchFinderFrontendActionFactory(this);
|
||||
}
|
||||
|
||||
void MatchFinder::RegisterTestCallbackAfterParsing(
|
||||
MatchFinder::ParsingDoneTestCallback *NewParsingDone) {
|
||||
ParsingDone = NewParsingDone;
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
|
@ -1,6 +1,7 @@
|
|||
SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST)
|
||||
|
||||
add_clang_library(clangTooling
|
||||
ASTMatchers.cpp
|
||||
JsonCompileCommandLineDatabase.cpp
|
||||
Tooling.cpp
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//===--- Tooling.cpp - Running clang standalone tools --------------------===//
|
||||
//===--- Tooling.cpp - Running clang standalone tools ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -44,7 +44,7 @@ namespace {
|
|||
// - it must contain at least a program path,
|
||||
// - argv[0], ..., and argv[argc - 1] mustn't be NULL, and
|
||||
// - argv[argc] must be NULL.
|
||||
void ValidateArgv(int argc, char* argv[]) {
|
||||
void ValidateArgv(int argc, char *argv[]) {
|
||||
if (argc < 1) {
|
||||
fprintf(stderr, "ERROR: argc is %d. It must be >= 1.\n", argc);
|
||||
abort();
|
||||
|
@ -69,7 +69,7 @@ void ValidateArgv(int argc, char* argv[]) {
|
|||
// code that sets up a compiler to run tools on it, and we should refactor
|
||||
// it to be based on the same framework.
|
||||
|
||||
static clang::Diagnostic* NewTextDiagnostics() {
|
||||
static clang::Diagnostic *NewTextDiagnostics() {
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
|
||||
new clang::DiagnosticIDs());
|
||||
clang::TextDiagnosticPrinter *DiagClient = new clang::TextDiagnosticPrinter(
|
||||
|
@ -81,15 +81,15 @@ static clang::Diagnostic* NewTextDiagnostics() {
|
|||
static int StaticSymbol;
|
||||
|
||||
/// \brief Builds a clang driver initialized for running clang tools.
|
||||
static clang::driver::Driver* NewDriver(clang::Diagnostic* Diagnostics,
|
||||
const char* BinaryName) {
|
||||
static clang::driver::Driver *NewDriver(clang::Diagnostic *Diagnostics,
|
||||
const char *BinaryName) {
|
||||
// This just needs to be some symbol in the binary.
|
||||
void* const SymbolAddr = &StaticSymbol;
|
||||
void *const SymbolAddr = &StaticSymbol;
|
||||
const llvm::sys::Path ExePath =
|
||||
llvm::sys::Path::GetMainExecutable(BinaryName, SymbolAddr);
|
||||
|
||||
const std::string DefaultOutputName = "a.out";
|
||||
clang::driver::Driver* CompilerDriver = new clang::driver::Driver(
|
||||
clang::driver::Driver *CompilerDriver = new clang::driver::Driver(
|
||||
ExePath.str(), llvm::sys::getHostTriple(),
|
||||
DefaultOutputName, false, false, *Diagnostics);
|
||||
CompilerDriver->setTitle("clang_based_tool");
|
||||
|
@ -98,8 +98,8 @@ static clang::driver::Driver* NewDriver(clang::Diagnostic* Diagnostics,
|
|||
|
||||
/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs.
|
||||
/// Returns NULL on error.
|
||||
static const clang::driver::ArgStringList* GetCC1Arguments(
|
||||
clang::Diagnostic* Diagnostics, clang::driver::Compilation* Compilation) {
|
||||
static const clang::driver::ArgStringList *GetCC1Arguments(
|
||||
clang::Diagnostic *Diagnostics, clang::driver::Compilation *Compilation) {
|
||||
// We expect to get back exactly one Command job, if we didn't something
|
||||
// failed. Extract that job from the Compilation.
|
||||
const clang::driver::JobList &Jobs = Compilation->getJobs();
|
||||
|
@ -124,10 +124,10 @@ static const clang::driver::ArgStringList* GetCC1Arguments(
|
|||
}
|
||||
|
||||
/// \brief Returns a clang build invocation initialized from the CC1 flags.
|
||||
static clang::CompilerInvocation* NewInvocation(
|
||||
clang::Diagnostic* Diagnostics,
|
||||
const clang::driver::ArgStringList& CC1Args) {
|
||||
clang::CompilerInvocation* Invocation = new clang::CompilerInvocation;
|
||||
static clang::CompilerInvocation *NewInvocation(
|
||||
clang::Diagnostic *Diagnostics,
|
||||
const clang::driver::ArgStringList &CC1Args) {
|
||||
clang::CompilerInvocation *Invocation = new clang::CompilerInvocation;
|
||||
clang::CompilerInvocation::CreateFromArgs(
|
||||
*Invocation, CC1Args.data(), CC1Args.data() + CC1Args.size(),
|
||||
*Diagnostics);
|
||||
|
@ -137,11 +137,11 @@ static clang::CompilerInvocation* NewInvocation(
|
|||
|
||||
/// \brief Runs the specified clang tool action and returns whether it executed
|
||||
/// successfully.
|
||||
static bool RunInvocation(const char* BinaryName,
|
||||
clang::driver::Compilation* Compilation,
|
||||
clang::CompilerInvocation* Invocation,
|
||||
const clang::driver::ArgStringList& CC1Args,
|
||||
clang::FrontendAction* ToolAction) {
|
||||
static bool RunInvocation(const char *BinaryName,
|
||||
clang::driver::Compilation *Compilation,
|
||||
clang::CompilerInvocation *Invocation,
|
||||
const clang::driver::ArgStringList &CC1Args,
|
||||
clang::FrontendAction *ToolAction) {
|
||||
llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction);
|
||||
// Show the invocation, with -v.
|
||||
if (Invocation->getHeaderSearchOpts().Verbose) {
|
||||
|
@ -164,7 +164,7 @@ static bool RunInvocation(const char* BinaryName,
|
|||
if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes &&
|
||||
Compiler.getHeaderSearchOpts().ResourceDir.empty()) {
|
||||
// This just needs to be some symbol in the binary.
|
||||
void* const SymbolAddr = &StaticSymbol;
|
||||
void *const SymbolAddr = &StaticSymbol;
|
||||
Compiler.getHeaderSearchOpts().ResourceDir =
|
||||
clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr);
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ static bool RunInvocation(const char* BinaryName,
|
|||
|
||||
/// \brief Converts a string vector representing a Command line into a C
|
||||
/// string vector representing the Argv (including the trailing NULL).
|
||||
std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command) {
|
||||
std::vector<char*> CommandLineToArgv(const std::vector<std::string> *Command) {
|
||||
std::vector<char*> Result(Command->size() + 1);
|
||||
for (std::vector<char*>::size_type I = 0; I < Command->size(); ++I) {
|
||||
Result[I] = const_cast<char*>((*Command)[I].c_str());
|
||||
|
@ -185,14 +185,14 @@ std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command) {
|
|||
}
|
||||
|
||||
bool RunToolWithFlags(
|
||||
clang::FrontendAction* ToolAction, int Args, char* Argv[]) {
|
||||
clang::FrontendAction *ToolAction, int Args, char *Argv[]) {
|
||||
ValidateArgv(Args, Argv);
|
||||
const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics());
|
||||
const llvm::OwningPtr<clang::driver::Driver> Driver(
|
||||
NewDriver(Diagnostics.get(), Argv[0]));
|
||||
const llvm::OwningPtr<clang::driver::Compilation> Compilation(
|
||||
Driver->BuildCompilation(llvm::ArrayRef<const char*>(Argv, Args)));
|
||||
const clang::driver::ArgStringList* const CC1Args = GetCC1Arguments(
|
||||
const clang::driver::ArgStringList *const CC1Args = GetCC1Arguments(
|
||||
Diagnostics.get(), Compilation.get());
|
||||
if (CC1Args == NULL) {
|
||||
return false;
|
||||
|
@ -208,11 +208,11 @@ bool RunToolWithFlags(
|
|||
/// \param FileContents A mapping from file name to source code. For each
|
||||
/// entry a virtual file mapping will be created when running the tool.
|
||||
bool RunToolWithFlagsOnCode(
|
||||
const std::vector<std::string>& CommandLine,
|
||||
const std::map<std::string, std::string>& FileContents,
|
||||
clang::FrontendAction* ToolAction) {
|
||||
const std::vector<std::string> &CommandLine,
|
||||
const std::map<std::string, std::string> &FileContents,
|
||||
clang::FrontendAction *ToolAction) {
|
||||
const std::vector<char*> Argv = CommandLineToArgv(&CommandLine);
|
||||
const char* const BinaryName = Argv[0];
|
||||
const char *const BinaryName = Argv[0];
|
||||
|
||||
const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics());
|
||||
const llvm::OwningPtr<clang::driver::Driver> Driver(
|
||||
|
@ -224,7 +224,7 @@ bool RunToolWithFlagsOnCode(
|
|||
const llvm::OwningPtr<clang::driver::Compilation> Compilation(
|
||||
Driver->BuildCompilation(llvm::ArrayRef<const char*>(&Argv[0],
|
||||
Argv.size() - 1)));
|
||||
const clang::driver::ArgStringList* const CC1Args = GetCC1Arguments(
|
||||
const clang::driver::ArgStringList *const CC1Args = GetCC1Arguments(
|
||||
Diagnostics.get(), Compilation.get());
|
||||
if (CC1Args == NULL) {
|
||||
return false;
|
||||
|
@ -236,7 +236,7 @@ bool RunToolWithFlagsOnCode(
|
|||
It = FileContents.begin(), End = FileContents.end();
|
||||
It != End; ++It) {
|
||||
// Inject the code as the given file name into the preprocessor options.
|
||||
const llvm::MemoryBuffer* Input =
|
||||
const llvm::MemoryBuffer *Input =
|
||||
llvm::MemoryBuffer::getMemBuffer(It->second.c_str());
|
||||
Invocation->getPreprocessorOpts().addRemappedFile(It->first.c_str(), Input);
|
||||
}
|
||||
|
@ -247,8 +247,8 @@ bool RunToolWithFlagsOnCode(
|
|||
|
||||
bool RunSyntaxOnlyToolOnCode(
|
||||
clang::FrontendAction *ToolAction, llvm::StringRef Code) {
|
||||
const char* const FileName = "input.cc";
|
||||
const char* const CommandLine[] = {
|
||||
const char *const FileName = "input.cc";
|
||||
const char *const CommandLine[] = {
|
||||
"clang-tool", "-fsyntax-only", FileName
|
||||
};
|
||||
std::map<std::string, std::string> FileContents;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: rm -rf %t
|
||||
// RUN: mkdir %t
|
||||
// RUN: echo '[{"directory":".","command":"clang++ '$(llvm-config --cppflags all)' -c %s","file":"%s"}]' > %t/compile_commands.json
|
||||
// RUN: remove-cstr-calls %t %s | FileCheck %s
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace llvm { struct StringRef { StringRef(const char *p); }; }
|
||||
|
||||
void f1(const std::string &s) {
|
||||
f1(s.c_str()); // CHECK:remove-cstr-calls.cpp:11:6:11:14:s
|
||||
}
|
||||
void f2(const llvm::StringRef r) {
|
||||
std::string s;
|
||||
f2(s.c_str()); // CHECK:remove-cstr-calls.cpp:15:6:15:14:s
|
||||
}
|
||||
void f3(const llvm::StringRef &r) {
|
||||
std::string s;
|
||||
f3(s.c_str()); // CHECK:remove-cstr-calls.cpp:19:6:19:14:s
|
||||
}
|
|
@ -69,3 +69,8 @@ add_clang_unittest(JsonCompileCommandLineDatabase
|
|||
Tooling/JsonCompileCommandLineDatabaseTest.cpp
|
||||
USED_LIBS gtest gtest_main clangTooling
|
||||
)
|
||||
|
||||
add_clang_unittest(ASTMatchersTest
|
||||
Tooling/ASTMatchersTest.cpp
|
||||
USED_LIBS gtest gtest_main clangTooling
|
||||
)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче