Skip to content

Instantly share code, notes, and snippets.

@yak1ex
Last active June 4, 2018 12:39
Show Gist options
  • Select an option

  • Save yak1ex/547e3bfb733e88b5190f3e83236ab455 to your computer and use it in GitHub Desktop.

Select an option

Save yak1ex/547e3bfb733e88b5190f3e83236ab455 to your computer and use it in GitHub Desktop.
Convert plain C array to C++ std::array by using clang::Tooling
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Applies edits generated by a clang tool that was run on Chromium code.
Synopsis:
cat run_tool.out | extract_edits.py | apply_edits.py <build dir> <filters...>
For example - to apply edits only to WTF sources:
... | apply_edits.py out/gn third_party/WebKit/Source/wtf
In addition to filters specified on the command line, the tool also skips edits
that apply to files that are not covered by git.
"""
import argparse
import collections
import functools
import multiprocessing
import os
import os.path
import subprocess
import sys
# monkey patch for cygwin
realpath = os.path.realpath
def myrealpath(path):
prev = None
while prev != path:
prev = path
path = realpath(path)
return path
os.path.realpath = myrealpath
script_dir = os.path.dirname(os.path.realpath(__file__))
#tool_dir = os.path.abspath(os.path.join(script_dir, '../pylib'))
#sys.path.insert(0, tool_dir)
#from clang import compile_db
Edit = collections.namedtuple('Edit',
('edit_type', 'offset', 'length', 'replacement'))
def _GetFilesFromGit(paths=None):
"""Gets the list of files in the git repository.
Args:
paths: Prefix filter for the returned paths. May contain multiple entries.
"""
args = []
if sys.platform == 'win32':
args.append('git.bat')
else:
args.append('git')
args.append('ls-files')
if paths:
args.extend(paths)
command = subprocess.Popen(args, stdout=subprocess.PIPE)
output, _ = command.communicate()
return [os.path.realpath(p) for p in output.splitlines()]
def _GetFilesFromSvn(paths=None):
"""Gets the list of files in the svn repository.
Args:
paths: Currently not supported.
"""
args = ['svn', 'ls']
if paths:
args.extend(paths)
command = subprocess.Popen(args, stdout=subprocess.PIPE)
output, _ = command.communicate()
return [os.path.realpath(p) for p in output.splitlines()]
def _GetFilesFromFind(paths=None):
"""Gets the list of files in the directory.
Args:
paths: Arguments passed to find. "-type f" is always prepended.
"""
args = ['find', '.', '-type', 'f']
if paths:
args.extend(paths)
command = subprocess.Popen(args, stdout=subprocess.PIPE)
output, _ = command.communicate()
return [os.path.realpath(p) for p in output.splitlines()]
def _ParseEditsFromStdin(build_directory):
"""Extracts generated list of edits from the tool's stdout.
The expected format is documented at the top of this file.
Args:
build_directory: Directory that contains the compile database. Used to
normalize the filenames.
stdout: The stdout from running the clang tool.
Returns:
A dictionary mapping filenames to the associated edits.
"""
path_to_resolved_path = {}
def _ResolvePath(path):
if path in path_to_resolved_path:
return path_to_resolved_path[path]
if not os.path.isfile(path):
resolved_path = os.path.realpath(os.path.join(build_directory, path))
else:
resolved_path = path
if not os.path.isfile(resolved_path):
sys.stderr.write('Edit applies to a non-existent file: %s\n' % path)
resolved_path = None
path_to_resolved_path[path] = resolved_path
return resolved_path
edits = collections.defaultdict(list)
for line in sys.stdin:
line = line.rstrip("\n\r")
try:
edit_type, path, offset, length, replacement = line.split(':::', 4)
replacement = replacement.replace('\0', '\n')
path = _ResolvePath(path)
if not path: continue
edits[path].append(Edit(edit_type, int(offset), int(length), replacement))
except ValueError:
sys.stderr.write('Unable to parse edit: %s\n' % line)
return edits
def _ApplyEditsToSingleFile(filename, edits):
# Sort the edits and iterate through them in reverse order. Sorting allows
# duplicate edits to be quickly skipped, while reversing means that
# subsequent edits don't need to have their offsets updated with each edit
# applied.
edit_count = 0
error_count = 0
edits.sort()
last_edit = None
with open(filename, 'rb+') as f:
contents = bytearray(f.read())
for edit in reversed(edits):
if edit == last_edit:
continue
if (last_edit is not None and edit.edit_type == last_edit.edit_type and
edit.offset == last_edit.offset and edit.length == last_edit.length):
sys.stderr.write(
'Conflicting edit: %s at offset %d, length %d: "%s" != "%s"\n' %
(filename, edit.offset, edit.length, edit.replacement,
last_edit.replacement))
error_count += 1
continue
last_edit = edit
contents[edit.offset:edit.offset + edit.length] = edit.replacement
if not edit.replacement:
_ExtendDeletionIfElementIsInList(contents, edit.offset)
edit_count += 1
f.seek(0)
f.truncate()
f.write(contents)
return (edit_count, error_count)
def _ApplyEdits(edits):
"""Apply the generated edits.
Args:
edits: A dict mapping filenames to Edit instances that apply to that file.
"""
edit_count = 0
error_count = 0
done_files = 0
for k, v in edits.iteritems():
tmp_edit_count, tmp_error_count = _ApplyEditsToSingleFile(k, v)
edit_count += tmp_edit_count
error_count += tmp_error_count
done_files += 1
percentage = (float(done_files) / len(edits)) * 100
sys.stdout.write('Applied %d edits (%d errors) to %d files [%.2f%%]\r' %
(edit_count, error_count, done_files, percentage))
sys.stdout.write('\n')
return -error_count
_WHITESPACE_BYTES = frozenset((ord('\t'), ord('\n'), ord('\r'), ord(' ')))
def _ExtendDeletionIfElementIsInList(contents, offset):
"""Extends the range of a deletion if the deleted element was part of a list.
This rewriter helper makes it easy for refactoring tools to remove elements
from a list. Even if a matcher callback knows that it is removing an element
from a list, it may not have enough information to accurately remove the list
element; for example, another matcher callback may end up removing an adjacent
list element, or all the list elements may end up being removed.
With this helper, refactoring tools can simply remove the list element and not
worry about having to include the comma in the replacement.
Args:
contents: A bytearray with the deletion already applied.
offset: The offset in the bytearray where the deleted range used to be.
"""
char_before = char_after = None
left_trim_count = 0
for byte in reversed(contents[:offset]):
left_trim_count += 1
if byte in _WHITESPACE_BYTES:
continue
if byte in (ord(','), ord(':'), ord('('), ord('{')):
char_before = chr(byte)
break
right_trim_count = 0
for byte in contents[offset:]:
right_trim_count += 1
if byte in _WHITESPACE_BYTES:
continue
if byte == ord(','):
char_after = chr(byte)
break
if char_before:
if char_after:
del contents[offset:offset + right_trim_count]
elif char_before in (',', ':'):
del contents[offset - left_trim_count:offset]
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'-p',
required=True,
help='path to the build dir (dir that edit paths are relative to)')
parser.add_argument(
'-t',
default='git',
help='target path check method git, svn, find (default: git)')
parser.add_argument(
'path_filter',
nargs='*',
help='optional paths to filter what files the tool is run on')
args = parser.parse_args()
if args.t == 'git':
filenames = set(_GetFilesFromGit(args.path_filter))
elif args.t == 'svn':
filenames = set(_GetFilesFromSvn(args.path_filter))
elif args.t == 'find':
filenames = set(_GetFilesFromFind(args.path_filter))
else:
sys.stderr.write('Unknown -t argument: %s' % (args.t))
sys.exit(1)
edits = _ParseEditsFromStdin(args.p)
return _ApplyEdits(
{k: v for k, v in edits.iteritems()
if os.path.realpath(k) in filenames})
if __name__ == '__main__':
sys.exit(main())
import sys
import re
import os.path
import itertools
import subprocess
import xml.etree.ElementTree as ET
import ntpath
# TODO: Set C++ compile mode by using etree like replacing
# <ItemGroup>
# <ClCompile Include="Wrapper\g_ior\g_iorfb_cmng.c" />
# </ItemGroup>
# with
# <ItemGroup>
# <ClCompile Include="Wrapper\g_ior\g_iorfb_cmng.c">
# <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsCpp</CompileAs>
# </ClCompile>
# </ItemGroup>
def find_upward(file, target):
dir = ''
newdir = os.path.dirname(file)
while dir != newdir:
dir = newdir
path = dir + '/' + target
if os.path.exists(path):
return path
else:
newdir = os.path.dirname(dir)
return ''
def process(file):
targets = []
input = open(file)
line = input.readline()
while line:
m = re.match(r"#include \"([^.]*\.c)\"", line)
if m:
targets.append(m.group(1))
line = input.readline()
return targets
def make_compiler_flags(gpj):
input = open(gpj)
dir = os.path.dirname(gpj)
print dir + '/compile_flags.txt'
output = open(dir + '/compiler_flags.txt', 'w')
line = input.readline()
while line:
m = re.match(r"\s*-I\s*(.*)", line)
if m:
output.write('-I'+m.group(1)+"\n")
line = input.readline()
output.write("-D__farcall=\n-D__ELF__=1\n")
def make_pathmap(root):
return {f: d+'/'+f for d, sd, fl in os.walk(root + '/User/App') for f in fl if f[-2:] == '.c'}
def main():
# prj = find_upward(sys.argv[1], 'APA.gpj')
# make_compiler_flags(prj)
# root = os.path.dirname(prj)
# pathmap = make_pathmap(root)
# for i in itertools.chain.from_iterable(map(process, sys.argv[1:])):
# subprocess.call(['./bin/tool-template.exe', pathmap[i]])
# FIXME: wrong targets, should be map(basename, sys.argv[1:])
target = {f: 1 for f in itertools.chain.from_iterable(map(process, sys.argv[1:]))}
vcxproj = find_upward(sys.argv[1], 'CSRApp.vcxproj')
tree = ET.parse(vcxproj)
ns = {'msb': 'http://schemas.microsoft.com/developer/msbuild/2003'}
for e in tree.getroot().findall('./msb:ItemGroup/msb:ClCompile', ns):
if ntpath.basename(e.get('Include')) in target:
e.append(ET.fromstring('<CompileAs xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Condition="'+"'"+'$(Configuration)|$(Platform)'+"'"+'=='+"'"+'Debug|Win32'+"'"+'">CompileAsCpp</CompileAs>'))
tree.write('test.xml')
# FIXME: xmlns:ns0 and ns0:
if __name__ == '__main__':
sys.exit(main())
#!/usr/bin/env python
# Copyright (c) 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script to extract edits from clang tool output.
If a clang tool emits edits, then the edits should look like this:
...
==== BEGIN EDITS ====
<edit1>
<edit2>
...
==== END EDITS ====
...
extract_edits.py takes input that is concatenated from multiple tool invocations
and extract just the edits. In other words, given the following input:
...
==== BEGIN EDITS ====
<edit1>
<edit2>
==== END EDITS ====
...
==== BEGIN EDITS ====
<yet another edit1>
<yet another edit2>
==== END EDITS ====
...
extract_edits.py would emit the following output:
<edit1>
<edit2>
<yet another edit1>
<yet another edit2>
This python script is mainly needed on Windows.
On unix this script can be replaced with running sed as follows:
$ cat run_tool.debug.out \
| sed '/^==== BEGIN EDITS ====$/,/^==== END EDITS ====$/{//!b};d'
| sort | uniq
"""
import sys
def main():
# TODO(dcheng): extract_edits.py should normalize paths. Doing this in
# apply_edits.py is too late, as a common use case is to apply edits from many
# different platforms.
unique_lines = set()
inside_marker_lines = False
for line in sys.stdin:
line = line.rstrip("\n\r")
if line == '==== BEGIN EDITS ====':
inside_marker_lines = True
continue
if line == '==== END EDITS ====':
inside_marker_lines = False
continue
if inside_marker_lines and line not in unique_lines:
unique_lines.add(line)
print line
return 0
if __name__ == '__main__':
sys.exit(main())
//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements an empty refactoring tool using the clang tooling.
// The goal is to lower the "barrier to entry" for writing refactoring tools.
//
// Usage:
// tool-template <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 tool-template on all files in a subtree of the
// source tree, use:
//
// /path/in/subtree $ find . -name '*.cpp'|
// xargs tool-template /path/to/build
//
//===----------------------------------------------------------------------===//
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
namespace clang {
namespace ast_matchers {
AST_POLYMORPHIC_MATCHER(isExpansionInMainFileBase,
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc)) {
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;
}
auto MainFileEntry = SourceManager.getFileEntryForID(SourceManager.getMainFileID());
if (!MainFileEntry) {
return false;
}
// FIXME: Make sure to be absolute path, instead of just calling tryGetRealPathName()
// ref: https://github.com/llvm-mirror/clang-tools-extra/blob/master/clang-move/ClangMove.cpp
auto MainFilename = MainFileEntry->tryGetRealPathName();
auto Filename = FileEntry->tryGetRealPathName();
auto LastDotMain = MainFilename.find_last_of('.');
auto LastDot = Filename.find_last_of('.');
return MainFilename.substr(0, LastDotMain) == Filename.substr(0, LastDot);
}
} // namespace ast_matchers
} // namespace clang
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace llvm;
// TODO: Fix passed to arrayify function with reference to std::array
// ref. https://chromium.googlesource.com/chromium/src/+/lkcr/docs/clang_tool_refactoring.md
// https://chromium.googlesource.com/chromium/src/+/master/tools/clang/scripts/extract_edits.py
// https://chromium.googlesource.com/chromium/src/+/master/tools/clang/scripts/apply_edits.py
namespace {
typedef std::map<std::string, Replacements> AllReplacements; // one Replacements holds changes in one file
class InclusionCheck : public PPCallbacks {
public:
InclusionCheck(SourceManager &SourceManager_, AllReplacements &AllReplacements) : SourceManager_(SourceManager_), AllReplacements_(AllReplacements), Level(0), Seen(false), Left(false) {}
void InclusionDirective (SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, const clang::Module *Imported) override {
if(SourceManager_.isInMainFile(HashLoc)) {
if(!Seen) {
AtomicChange Change(SourceManager_, HashLoc);
AllReplacements_[Change.getFilePath()].add(Replacement(SourceManager_, CharSourceRange::getCharRange(SourceRange(HashLoc, HashLoc)), std::string("#ifdef __cplusplus")+'\0'+"extern \"C\" {"+'\0'+"#endif"+'\0'));
}
if(Level == 0) {
End = FilenameRange.getEnd();
Token tok;
if(!Lexer::getRawToken(End, tok, SourceManager_, LangOptions(), true)) {
End = tok.getLocation();
}
} else {
Left = true;
}
Seen = true;
}
}
void If(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind ConditionValue) override {
if(SourceManager_.isInMainFile(Loc)) ++Level;
}
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
if(SourceManager_.isInMainFile(Loc)) --Level;
if(Left && Level == 0) {
Token tok;
Lexer::getRawToken(Loc, tok, SourceManager_, LangOptions(), true);
End = tok.getEndLoc();
if(!Lexer::getRawToken(End, tok, SourceManager_, LangOptions(), true)) {
End = tok.getLocation();
}
Left = false;
}
}
void EndOfMainFile() override {
AtomicChange Change(SourceManager_, End);
AllReplacements_[Change.getFilePath()].add(Replacement(SourceManager_, CharSourceRange::getCharRange(SourceRange(End, End)), std::string("#ifdef __cplusplus")+'\0'+"}"+'\0'+"#endif"+'\0'));
}
private:
SourceManager &SourceManager_;
AllReplacements &AllReplacements_;
SourceLocation End;
int Level;
bool Seen;
bool Left;
};
class InclusionChecker : public SourceFileCallbacks {
public:
InclusionChecker(AllReplacements &AllReplacements) : AllReplacements_(AllReplacements) {}
bool handleBeginSource(CompilerInstance &Compiler) override {
std::unique_ptr<PPCallbacks> p = make_unique<InclusionCheck>(Compiler.getSourceManager(), AllReplacements_);
Compiler.getPreprocessor().addPPCallbacks(std::move(p));
return true;
}
private:
AllReplacements &AllReplacements_;
};
class ToolTemplateCallback : public MatchFinder::MatchCallback {
public:
ToolTemplateCallback(ExecutionContext &Context, AllReplacements &AllReplacements) : Context(Context), AllReplacements_(AllReplacements), first(true) {}
void run(const MatchFinder::MatchResult &Result) override {
#if 0 /* emitting #include <array> is placed at wrapper */
if (first) {
auto loc = Result.SourceManager->getLocForStartOfFile(Result.SourceManager->getMainFileID());
AtomicChange Change(*Result.SourceManager, loc);
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getCharRange(SourceRange(loc, loc)), std::string("#include <array>")+'\0'));
first = false;
}
#endif
// TODO: This routine will get called for each thing that the matchers
// find.
// At this point, you can examine the match, and do whatever you want,
// including replacing the matched text with other text
auto *D = Result.Nodes.getNodeAs<ValueDecl>("decl");
// assert(D);
// Use AtomicChange to get a key.
if (D && D->getLocStart().isValid()) { // Declarations
AtomicChange Change(*Result.SourceManager, D->getLocStart());
// Context.reportResult(Change.getKey(), D->getNameAsString());
// Context.reportResult(Change.getKey()+"QN", D->getQualifiedNameAsString());
// Context.reportResult(Change.getKey()+"type", D->getType().getAsString());
// Context.reportResult(Change.getKey()+"convtype", convType(D->getType().getAsString(), false));
Context.reportResult(Change.getKey()+"decl", getSourceFragment(D, *Result.SourceManager));
auto *ot = D->getType().getTypePtr()->getAs<DecayedType>();
if (ot) { // (decaying) parameter
// Context.reportResult(Change.getKey()+"otype", ot->getOriginalType().getAsString());
if (ot->getOriginalType().getTypePtr()->isConstantArrayType()) {
// Context.reportResult(Change.getKey()+"convotype", convType(ot->getOriginalType().getAsString(), true));
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(D->getSourceRange()), convType(ot->getOriginalType().getAsString(), true) + " " + D->getNameAsString()));
} else if (ot->getOriginalType().getTypePtr()->isArrayType()) {
Context.reportResult(Change.getKey()+"WARN", "Unknown bounds array parameter");
}
} else { // not (decaying) parameter
auto *DD = Result.Nodes.getNodeAs<VarDecl>("decl");
if (DD && DD->hasInit()) { // variable declaration with initializer
// auto init = DD->getInit();
// Context.reportResult(Change.getKey()+"init", getSourceFragment(init, *Result.SourceManager));
auto range = getRangeWithInit(DD, *Result.SourceManager);
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(range[0]), convType(DD->getType().getAsString(), false) + " " + DD->getNameAsString()));
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(range[1]), "{{"));
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(range[2]), "}}"));
} else { // variable declaration w/o initializer or field declaration
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(D->getSourceRange()), convType(D->getType().getAsString(), false) + " " + D->getNameAsString()));
}
}
}
auto *E = Result.Nodes.getNodeAs<Expr>("expr");
auto *RE = Result.Nodes.getNodeAs<Expr>("base");
if (E && E->getLocStart().isValid() && RE) { // address taken // TODO: limit in call expression
AtomicChange Change(*Result.SourceManager, E->getLocStart());
Context.reportResult(Change.getKey()+"expr", getSourceFragment(E, *Result.SourceManager));
// Context.reportResult(Change.getKey()+"base", getSourceFragment(RE, *Result.SourceManager));
AllReplacements_[Change.getFilePath()].add(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(E->getSourceRange()), getSourceFragment(RE, *Result.SourceManager)));
}
}
void onStartOfTranslationUnit() override {
Context.reportResult("START", "Start of TU.");
}
void onEndOfTranslationUnit() override {
Context.reportResult("END", "End of TU.");
}
private:
// based on https://stackoverflow.com/a/23081770
template<typename T>
std::string getSourceFragment(const T* t, SourceManager &sm) {
if (!t) return {};
std::string text = Lexer::getSourceText(CharSourceRange::getTokenRange(t->getSourceRange()), sm, LangOptions(), 0);
if (text.size() > 0 && (text.at(text.size()-1) == ',')) // the text can be ""
return Lexer::getSourceText(CharSourceRange::getCharRange(t->getSourceRange()), sm, LangOptions(), 0);
return text;
}
std::array<SourceRange, 3> getRangeWithInit(const VarDecl *d, SourceManager &sm) {
auto loc = d->getLocStart(), locEnd = d->getLocEnd();
auto initStart = d->getInit()->getLocStart();
Token tok;
SourceLocation slEnd;
SourceRange srLBrace, srRBrace;
bool inInit = false, passedLBrace = false;
while (sm.isBeforeInTranslationUnit(loc, locEnd)) {
Lexer::getRawToken(loc, tok, sm, LangOptions(), true);
if (!passedLBrace && tok.is(tok::l_brace)) {
srLBrace = SourceRange(tok.getLocation(), tok.getLastLoc());
passedLBrace = true;
}
if (tok.is(tok::r_brace)) {
srRBrace = SourceRange(tok.getLocation(), tok.getLastLoc());
}
if (!inInit && sm.isBeforeInTranslationUnit(loc, initStart)) {
if (!tok.is(tok::equal) && !tok.is(tok::l_brace)) {
slEnd = tok.getLastLoc();
}
} else {
inInit = true;
}
loc = tok.getEndLoc();
}
return { SourceRange(d->getLocStart(), slEnd), srLBrace, srRBrace };
}
std::string convType(const std::string &s, bool isRef) {
if (s.size() == 0) return {};
std::vector<std::string> bounds;
auto itTemp = s.end();
bool inBounds = false;
for (auto it = --s.end(); it != s.begin(); --it) {
switch (*it) {
case ']':
inBounds = true;
itTemp = it;
break;
case '[':
bounds.emplace_back(it + 1, itTemp);
inBounds = false;
break;
case ' ':
case '\t':
// skip
break;
default:
if (!inBounds) {
itTemp = it + 1;
it = s.begin() + 1; // break for loop
}
break;
}
}
std::string elemType(s.begin(), itTemp);
for(auto bound: bounds) {
elemType = std::string("std::array<") + elemType + ", " + bound +">";
}
if (isRef && bounds.size()) elemType += " &";
return elemType;
}
ExecutionContext &Context;
AllReplacements &AllReplacements_;
bool first;
};
} // end anonymous namespace
// Set up the command line options
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::OptionCategory ToolTemplateCategory("tool-template options");
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
argc, argv, ToolTemplateCategory);
if (!Executor) {
llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
return 1;
}
ast_matchers::MatchFinder Finder;
AllReplacements AllReplacements_;
ToolTemplateCallback Callback(*Executor->get()->getExecutionContext(), AllReplacements_);
// TODO: Put your matchers here.
// Use Finder.addMatcher(...) to define the patterns in the AST that you
// want to match against. You are not limited to just one matcher!
//
// Finder.addMatcher(
// varDecl(hasType(qualType(hasCanonicalType(pointerType(pointee(constantArrayType())))))).bind("decl"),
// &Callback);
// for arrays in parameter
Finder.addMatcher(
parmVarDecl(isExpansionInMainFileBase(), hasType(decayedType(hasDecayedType(pointerType(unless(pointee(functionType()))))))).bind("decl"),
&Callback);
// for array vars
Finder.addMatcher(
varDecl(isExpansionInMainFileBase(), hasType(constantArrayType())).bind("decl"),
&Callback);
// for array as fields
Finder.addMatcher(
fieldDecl(isExpansionInMainFileBase(), hasType(constantArrayType())).bind("decl"),
&Callback);
// for pass by &array[0]
Finder.addMatcher(
expr(isExpansionInMainFileBase(), unaryOperator(hasOperatorName("&"), hasUnaryOperand(arraySubscriptExpr(hasBase(implicitCastExpr(hasSourceExpression(declRefExpr().bind("base")))), hasIndex(integerLiteral(equals(0))))))).bind("expr"),
&Callback);
// for pass by &array[]...[0]
Finder.addMatcher(
expr(isExpansionInMainFileBase(), unaryOperator(hasOperatorName("&"), hasUnaryOperand(arraySubscriptExpr(hasBase(implicitCastExpr(hasSourceExpression(arraySubscriptExpr().bind("base")))), hasIndex(integerLiteral(equals(0))))))).bind("expr"),
&Callback);
InclusionChecker InclusionChecker_(AllReplacements_);
auto Err = Executor->get()->execute(newFrontendActionFactory(&Finder, &InclusionChecker_));
if (Err) {
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
}
Executor->get()->getToolResults()->forEachResult(
[](llvm::StringRef key, llvm::StringRef value) {
llvm::errs() << "----" << key.str() << "\n" << value.str() << "\n";
});
llvm::outs() << "==== BEGIN EDITS ====\n";
for (const auto& Replacements_ : AllReplacements_) {
for (const auto &r : Replacements_.second) {
llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
<< ":::" << r.getLength() << ":::" << r.getReplacementText()
<< "\n";
}
}
llvm::outs() << "==== END EDITS ====\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment