A new Clang-based tool which converts for loops to use the range-based syntax new to C++11. Three kinds of loops can be converted: - Loops over statically allocated arrays - Loops over containers, using iterators - Loops over array-like containers, using operator[] and at() Each transformation is assigned a confidence level by the tool. The minimum require confidence level to actually apply the transformation can be specified on the command line, but the default level should be fine for most code. Like other tools based on RefactoringTool, it is easiest to use this tool with a compilation database. llvm-svn: 162627
138 lines
5.4 KiB
C++
138 lines
5.4 KiB
C++
//===-- loop-convert/LoopConvert.cpp - C++11 For loop migration -*- C++ -*-===//
|
|
//
|
|
// 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 migrates for loops to take advantage of the
|
|
// range-basead syntax new to C++11.
|
|
//
|
|
// Usage:
|
|
// loop-convert <cmake-output-dir> <file1> <file2> ...
|
|
//
|
|
// Where <cmake-output-dir> is a CMake build directory containing a file named
|
|
// compile_commands.json.
|
|
//
|
|
// <file1>... specify the pahs of files in the CMake source tree, with the same
|
|
// requirements as other tools built on LibTooling.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LoopActions.h"
|
|
#include "LoopMatchers.h"
|
|
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "clang/Tooling/Refactoring.h"
|
|
|
|
using clang::ast_matchers::MatchFinder;
|
|
namespace cl = llvm::cl;
|
|
using namespace clang::tooling;
|
|
using namespace clang::loop_migrate;
|
|
|
|
static cl::opt<std::string> BuildPath(
|
|
cl::Positional,
|
|
cl::desc("<build-path>"));
|
|
|
|
static cl::list<std::string> SourcePaths(
|
|
cl::Positional,
|
|
cl::desc("<source0> [... <sourceN>]"),
|
|
cl::OneOrMore);
|
|
|
|
// General options go here:
|
|
static cl::opt<bool> CountOnly(
|
|
"count-only", cl::desc("Do not apply transformations; only count them."));
|
|
|
|
static cl::opt<TranslationConfidenceKind> TransformationLevel(
|
|
cl::desc("Choose safety requirements for transformations:"),
|
|
cl::values(clEnumValN(TCK_Safe, "A0", "Enable safe transformations"),
|
|
clEnumValN(TCK_Reasonable, "A1",
|
|
"Enable transformations that might change semantics "
|
|
"(default)"),
|
|
clEnumValN(TCK_Risky, "A2",
|
|
"Enable transformations that are likely "
|
|
"to change semantics"),
|
|
clEnumValEnd),
|
|
cl::init(TCK_Reasonable));
|
|
|
|
int main(int argc, const char **argv) {
|
|
llvm::OwningPtr<CompilationDatabase> Compilations(
|
|
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
|
|
cl::ParseCommandLineOptions(argc, argv);
|
|
if (!Compilations) {
|
|
std::string ErrorMessage;
|
|
Compilations.reset(
|
|
!BuildPath.empty() ?
|
|
CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
|
|
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
|
|
ErrorMessage));
|
|
if (!Compilations)
|
|
llvm::report_fatal_error(ErrorMessage);
|
|
}
|
|
ClangTool SyntaxTool(*Compilations, SourcePaths);
|
|
|
|
// First, let's check to make sure there were no errors.
|
|
if (int result =
|
|
SyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>())) {
|
|
llvm::errs() << "Error compiling files.\n";
|
|
return result;
|
|
}
|
|
|
|
RefactoringTool LoopTool(*Compilations, SourcePaths);
|
|
StmtAncestorASTVisitor ParentFinder;
|
|
StmtGeneratedVarNameMap GeneratedDecls;
|
|
ReplacedVarsMap ReplacedVars;
|
|
unsigned AcceptedChanges = 0;
|
|
unsigned DeferredChanges = 0;
|
|
unsigned RejectedChanges = 0;
|
|
|
|
MatchFinder Finder;
|
|
LoopFixer ArrayLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
|
|
&GeneratedDecls, &ReplacedVars, &AcceptedChanges,
|
|
&DeferredChanges, &RejectedChanges,
|
|
CountOnly, TransformationLevel, LFK_Array);
|
|
Finder.addMatcher(makeArrayLoopMatcher(), &ArrayLoopFixer);
|
|
LoopFixer IteratorLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
|
|
&GeneratedDecls, &ReplacedVars, &AcceptedChanges,
|
|
&DeferredChanges, &RejectedChanges,
|
|
CountOnly, TransformationLevel, LFK_Iterator);
|
|
Finder.addMatcher(makeIteratorLoopMatcher(), &IteratorLoopFixer);
|
|
LoopFixer PseudoarrrayLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
|
|
&GeneratedDecls, &ReplacedVars,
|
|
&AcceptedChanges, &DeferredChanges,
|
|
&RejectedChanges, CountOnly,
|
|
TransformationLevel, LFK_PseudoArray);
|
|
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
|
if (int result = LoopTool.run(newFrontendActionFactory(&Finder))) {
|
|
llvm::errs() << "Error encountered during translation.\n";
|
|
return result;
|
|
}
|
|
|
|
llvm::outs() << "\nFor Loop Conversion:\n\t" << AcceptedChanges
|
|
<< " converted loop(s)\n\t" << DeferredChanges
|
|
<< " potentially conflicting change(s) deferred.\n\t"
|
|
<< RejectedChanges << " change(s) rejected.\n";
|
|
if (DeferredChanges > 0)
|
|
llvm::outs() << "Re-run this tool to attempt applying deferred changes.\n";
|
|
if (RejectedChanges > 0)
|
|
llvm::outs() << "Re-run this tool with a lower required confidence level "
|
|
"to apply rejected changes.\n";
|
|
|
|
if (AcceptedChanges > 0) {
|
|
// Check to see if the changes introduced any new errors.
|
|
ClangTool EndSyntaxTool(*Compilations, SourcePaths);
|
|
if (int result = EndSyntaxTool.run(
|
|
newFrontendActionFactory<clang::SyntaxOnlyAction>())) {
|
|
llvm::errs() << "Error compiling files after translation.\n";
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|