Compare commits
55 Commits
llvmorg-3.
...
llvmorg-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f03d15581 | ||
|
|
be4234a9e9 | ||
|
|
29b801f435 | ||
|
|
1ef34dea9a | ||
|
|
17f8c463e7 | ||
|
|
7da587059c | ||
|
|
fc08c674ec | ||
|
|
f02883848d | ||
|
|
dd80f8b54c | ||
|
|
1dfc8e9351 | ||
|
|
2bd857302e | ||
|
|
c5606e3ec9 | ||
|
|
917e5e1b01 | ||
|
|
5e7f2a0097 | ||
|
|
8f441bf3c6 | ||
|
|
db2db17ed1 | ||
|
|
b37b039f2d | ||
|
|
04ebbe6d44 | ||
|
|
d1e6adbb6c | ||
|
|
bb9ee5498c | ||
|
|
6a49deb6e9 | ||
|
|
e236a78c52 | ||
|
|
ddfbfaaca6 | ||
|
|
385fbc17e3 | ||
|
|
3ff7fb0fad | ||
|
|
0b1d24671c | ||
|
|
26951d3d04 | ||
|
|
496c30e19e | ||
|
|
7a8da6c1bd | ||
|
|
63ee36b8ca | ||
|
|
0ac87694b7 | ||
|
|
9c81b57cff | ||
|
|
ee386407f0 | ||
|
|
e8a900fc8c | ||
|
|
2fe446131c | ||
|
|
5f15b4e8a3 | ||
|
|
988b10e319 | ||
|
|
584eaadb4a | ||
|
|
ea5f85ee5a | ||
|
|
f242e2386e | ||
|
|
67cd5deeec | ||
|
|
181a72c0c3 | ||
|
|
98eaa4638a | ||
|
|
483a943c5f | ||
|
|
047e6ff259 | ||
|
|
2cd7132e15 | ||
|
|
8234137390 | ||
|
|
8d6eb8e521 | ||
|
|
30f2ef3799 | ||
|
|
4ea5fe07cb | ||
|
|
d5920bc1c3 | ||
|
|
2878161153 | ||
|
|
6f8e540382 | ||
|
|
b4b499b7ab | ||
|
|
392e4fbdd9 |
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"project_id" : "clang-tools-extra",
|
||||
"conduit_uri" : "http://llvm-reviews.chandlerc.com/"
|
||||
}
|
||||
32
clang-tools-extra/.gitignore
vendored
32
clang-tools-extra/.gitignore
vendored
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
add_subdirectory(remove-cstr-calls)
|
||||
add_subdirectory(tool-template)
|
||||
add_subdirectory(cpp11-migrate)
|
||||
add_subdirectory(modularize)
|
||||
|
||||
# Add the common testsuite after all the tools.
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(unittests)
|
||||
@@ -1,13 +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: Edwin Vane
|
||||
E: edwin.vane@intel.com
|
||||
D: cpp11-migrate
|
||||
@@ -1,63 +0,0 @@
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2007-2013 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
<none yet>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
##===- tools/extra/Makefile --------------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../..
|
||||
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
|
||||
PARALLEL_DIRS := remove-cstr-calls tool-template modularize
|
||||
DIRS := cpp11-migrate unittests
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
###
|
||||
# Handle the nested test suite.
|
||||
|
||||
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
|
||||
$(RecursiveTargets)::
|
||||
$(Verb) for dir in test; do \
|
||||
if [ -f $(PROJ_SRC_DIR)/$${dir}/Makefile ] && [ ! -f $${dir}/Makefile ]; then \
|
||||
$(MKDIR) $${dir}; \
|
||||
$(CP) $(PROJ_SRC_DIR)/$${dir}/Makefile $${dir}/Makefile; \
|
||||
fi \
|
||||
done
|
||||
endif
|
||||
|
||||
test::
|
||||
@ $(MAKE) -C test
|
||||
|
||||
report::
|
||||
@ $(MAKE) -C test report
|
||||
|
||||
clean::
|
||||
@ $(MAKE) -C test clean
|
||||
|
||||
.PHONY: test report clean
|
||||
@@ -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.cs.uiuc.edu/mailman/listinfo/cfe-dev
|
||||
|
||||
Code review for this tree should take place on the standard Clang patch and
|
||||
commit lists:
|
||||
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
|
||||
|
||||
If you find a bug in these tools, please file it in the LLVM bug tracker:
|
||||
http://llvm.org/bugs/
|
||||
@@ -1,63 +0,0 @@
|
||||
//===-- AddOverride/AddOverride.cpp - add C++11 override -------*- 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 implementation of the AddOverrideTransform
|
||||
/// class.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AddOverride.h"
|
||||
#include "AddOverrideActions.h"
|
||||
#include "AddOverrideMatchers.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
using clang::ast_matchers::MatchFinder;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
int AddOverrideTransform::apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRisk,
|
||||
const CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) {
|
||||
RefactoringTool AddOverrideTool(Database, SourcePaths);
|
||||
|
||||
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||
E = InputStates.end();
|
||||
I != E; ++I) {
|
||||
AddOverrideTool.mapVirtualFile(I->first, I->second);
|
||||
}
|
||||
|
||||
unsigned AcceptedChanges = 0;
|
||||
|
||||
MatchFinder Finder;
|
||||
AddOverrideFixer Fixer(AddOverrideTool.getReplacements(), AcceptedChanges);
|
||||
|
||||
Finder.addMatcher(makeCandidateForOverrideAttrMatcher(), &Fixer);
|
||||
|
||||
if (int result = AddOverrideTool.run(newFrontendActionFactory(&Finder))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
RewriterContainer Rewrite(AddOverrideTool.getFiles(), InputStates);
|
||||
|
||||
// FIXME: Do something if some replacements didn't get applied?
|
||||
AddOverrideTool.applyAllReplacements(Rewrite.getRewriter());
|
||||
|
||||
collectResults(Rewrite.getRewriter(), InputStates, ResultStates);
|
||||
|
||||
setAcceptedChanges(AcceptedChanges);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
//===-- AddOverride/AddOverride.h - add C++11 override ---------*- 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 definition of the AddOverrideTransform
|
||||
/// class which is the main interface to the transform that tries to add
|
||||
/// the override keyword to declarations of member function that override
|
||||
/// virtual functions in a base class.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
/// \brief Subclass of Transform that adds the C++11 override keyword to
|
||||
/// member functions overriding base class virtual functions.
|
||||
class AddOverrideTransform : public Transform {
|
||||
public:
|
||||
AddOverrideTransform() : Transform("AddOverride") {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRiskLEvel,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) LLVM_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_H
|
||||
@@ -1,59 +0,0 @@
|
||||
//===-- AddOverride/AddOverrideActions.cpp - add C++11 override-*- 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 contains the definition of the AddOverrideFixer class
|
||||
/// which is used as an ASTMatcher callback.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AddOverrideActions.h"
|
||||
#include "AddOverrideMatchers.h"
|
||||
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
void AddOverrideFixer::run(const MatchFinder::MatchResult &Result) {
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
|
||||
const CXXMethodDecl *M = Result.Nodes.getDeclAs<CXXMethodDecl>(MethodId);
|
||||
assert(M && "Bad Callback. No node provided");
|
||||
|
||||
// First check that there isn't already an override attribute.
|
||||
if (!M->hasAttr<OverrideAttr>()) {
|
||||
if (M->getLocStart().isFileID()) {
|
||||
SourceLocation StartLoc;
|
||||
if (M->hasInlineBody()) {
|
||||
// Start at the beginning of the body and rewind back to the last
|
||||
// non-whitespace character. We will insert the override keyword
|
||||
// after that character.
|
||||
// FIXME: This transform won't work if there is a comment between
|
||||
// the end of the function prototype and the start of the body.
|
||||
StartLoc = M->getBody()->getLocStart();
|
||||
do {
|
||||
StartLoc = StartLoc.getLocWithOffset(-1);
|
||||
} while (isWhitespace(*FullSourceLoc(StartLoc, SM).getCharacterData()));
|
||||
StartLoc = StartLoc.getLocWithOffset(1);
|
||||
} else {
|
||||
StartLoc = SM.getSpellingLoc(M->getLocEnd());
|
||||
StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOptions());
|
||||
}
|
||||
Replace.insert(tooling::Replacement(SM, StartLoc, 0, " override"));
|
||||
++AcceptedChanges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
//===-- AddOverride/AddOverrideActions.h - add C++11 override --*- 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 contains the declaration of the AddOverrideFixer class
|
||||
/// which is used as a ASTMatcher callback.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_ACTIONS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_ACTIONS_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
/// \brief The callback to be used for add-override migration matchers.
|
||||
///
|
||||
class AddOverrideFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
AddOverrideFixer(clang::tooling::Replacements &Replace,
|
||||
unsigned &AcceptedChanges) :
|
||||
Replace(Replace), AcceptedChanges(AcceptedChanges) {}
|
||||
|
||||
/// \brief Entry point to the callback called when matches are made.
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
|
||||
|
||||
private:
|
||||
clang::tooling::Replacements &Replace;
|
||||
unsigned &AcceptedChanges;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_ACTIONS_H
|
||||
@@ -1,28 +0,0 @@
|
||||
//===-- AddOverride/AddOverrideMatchers.cpp - C++11 override ---*- 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 contains the definitions for matcher-generating functions
|
||||
/// and a custom AST_MATCHER for identifying casts of type CK_NullTo*.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "AddOverrideMatchers.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
const char *MethodId = "method";
|
||||
|
||||
DeclarationMatcher makeCandidateForOverrideAttrMatcher() {
|
||||
return methodDecl(hasParent(recordDecl()),
|
||||
isOverride(),
|
||||
unless(destructorDecl())).bind(MethodId);
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//===-- AddOverride/AddOverrideMatchers.h - add C++11 override -*- 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 contains the declarations for matcher-generating functions
|
||||
/// and names for bound nodes found by AST matchers.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_MATCHERS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_MATCHERS_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
|
||||
/// Name to bind with matched expressions.
|
||||
extern const char *MethodId;
|
||||
|
||||
/// \brief Create a matcher that finds member function declarations that are
|
||||
/// candidates for adding the override attribute.
|
||||
clang::ast_matchers::DeclarationMatcher makeCandidateForOverrideAttrMatcher();
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_ADD_OVERRIDE_MATCHERS_H
|
||||
@@ -1,6 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(Core)
|
||||
@@ -1,12 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(migrateCore
|
||||
Transforms.cpp
|
||||
Transform.cpp
|
||||
IncludeExcludeInfo.cpp
|
||||
)
|
||||
target_link_libraries(migrateCore
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangASTMatchers
|
||||
)
|
||||
@@ -1,128 +0,0 @@
|
||||
//===-- Core/IncludeExcludeInfo.cpp - IncludeExclude class impl -*- 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 implemention of the IncludeExcludeInfo class
|
||||
/// to handle the include and exclude command line options.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "IncludeExcludeInfo.h"
|
||||
#include "llvm/Support/CommandLine.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;
|
||||
|
||||
namespace {
|
||||
/// \brief Helper function to determine whether a file has the same path
|
||||
/// prefix as \a Path.
|
||||
///
|
||||
/// \a Path must be an absolute path.
|
||||
bool fileHasPathPrefix(StringRef File, StringRef Path) {
|
||||
// Converts File to its absolute path.
|
||||
SmallString<64> AbsoluteFile = File;
|
||||
sys::fs::make_absolute(AbsoluteFile);
|
||||
|
||||
// Convert path strings to sys::path to iterate over each of its directories.
|
||||
sys::path::const_iterator FileI = sys::path::begin(AbsoluteFile),
|
||||
FileE = sys::path::end(AbsoluteFile),
|
||||
PathI = sys::path::begin(Path),
|
||||
PathE = sys::path::end(Path);
|
||||
while (FileI != FileE && PathI != PathE) {
|
||||
// If the strings aren't equal then the two paths aren't contained within
|
||||
// each other.
|
||||
if (!FileI->equals(*PathI))
|
||||
return false;
|
||||
++FileI;
|
||||
++PathI;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Helper function to tokenize a string of paths and populate
|
||||
/// the vector.
|
||||
error_code parseCLInput(StringRef Line, std::vector<std::string> &List,
|
||||
StringRef Separator) {
|
||||
SmallVector<StringRef, 32> Tokens;
|
||||
Line.split(Tokens, Separator, /*MaxSplit=*/ -1, /*KeepEmpty=*/ false);
|
||||
for (SmallVectorImpl<StringRef>::iterator I = Tokens.begin(),
|
||||
E = Tokens.end();
|
||||
I != E; ++I) {
|
||||
// Convert each path to its absolute path.
|
||||
SmallString<64> AbsolutePath = I->rtrim();
|
||||
if (error_code Err = sys::fs::make_absolute(AbsolutePath))
|
||||
return Err;
|
||||
List.push_back(std::string(AbsolutePath.str()));
|
||||
}
|
||||
return error_code::success();
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
error_code IncludeExcludeInfo::readListFromString(StringRef IncludeString,
|
||||
StringRef ExcludeString) {
|
||||
if (error_code Err = parseCLInput(IncludeString, IncludeList,
|
||||
/*Separator=*/ ","))
|
||||
return Err;
|
||||
if (error_code Err = parseCLInput(ExcludeString, ExcludeList,
|
||||
/*Separator=*/ ","))
|
||||
return Err;
|
||||
return error_code::success();
|
||||
}
|
||||
|
||||
error_code IncludeExcludeInfo::readListFromFile(StringRef IncludeListFile,
|
||||
StringRef ExcludeListFile) {
|
||||
if (!IncludeListFile.empty()) {
|
||||
OwningPtr<MemoryBuffer> FileBuf;
|
||||
if (error_code Err = MemoryBuffer::getFile(IncludeListFile, FileBuf)) {
|
||||
errs() << "Unable to read from include file.\n";
|
||||
return Err;
|
||||
}
|
||||
if (error_code Err = parseCLInput(FileBuf->getBuffer(), IncludeList,
|
||||
/*Separator=*/ "\n"))
|
||||
return Err;
|
||||
}
|
||||
if (!ExcludeListFile.empty()) {
|
||||
OwningPtr<MemoryBuffer> FileBuf;
|
||||
if (error_code Err = MemoryBuffer::getFile(ExcludeListFile, FileBuf)) {
|
||||
errs() << "Unable to read from exclude file.\n";
|
||||
return Err;
|
||||
}
|
||||
if (error_code Err = parseCLInput(FileBuf->getBuffer(), ExcludeList,
|
||||
/*Separator=*/ "\n"))
|
||||
return Err;
|
||||
}
|
||||
return error_code::success();
|
||||
}
|
||||
|
||||
bool IncludeExcludeInfo::isFileIncluded(StringRef FilePath) {
|
||||
bool InIncludeList = false;
|
||||
|
||||
for (std::vector<std::string>::iterator I = IncludeList.begin(),
|
||||
E = IncludeList.end();
|
||||
I != E; ++I)
|
||||
if ((InIncludeList = fileHasPathPrefix(FilePath, *I)))
|
||||
break;
|
||||
// If file is not in the list of included paths then it is not necessary
|
||||
// to check the excluded path list.
|
||||
if (!InIncludeList)
|
||||
return false;
|
||||
|
||||
for (std::vector<std::string>::iterator I = ExcludeList.begin(),
|
||||
E = ExcludeList.end();
|
||||
I != E; ++I)
|
||||
if (fileHasPathPrefix(FilePath, *I))
|
||||
return false;
|
||||
|
||||
// If the file is in the included list but not in the excluded list, then
|
||||
// it is safe to transform.
|
||||
return true;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//===-- Core/IncludeExcludeInfo.h - IncludeExclude class def'n --*- 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 definition for the IncludeExcludeInfo class
|
||||
/// to handle the include and exclude command line options.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
#include <vector>
|
||||
|
||||
/// \brief Class encapsulating the handling of include and exclude paths
|
||||
/// provided by the user through command line options.
|
||||
class IncludeExcludeInfo {
|
||||
public:
|
||||
/// \brief Read and parse a comma-seperated lists of paths from
|
||||
/// \a IncludeString and \a ExcludeString.
|
||||
///
|
||||
/// Returns error_code::success() on successful parse of the strings or
|
||||
/// an error_code indicating the encountered error.
|
||||
llvm::error_code readListFromString(llvm::StringRef IncludeString,
|
||||
llvm::StringRef ExcludeString);
|
||||
|
||||
/// \brief Read and parse the lists of paths from \a IncludeListFile
|
||||
/// and \a ExcludeListFile. Each file should contain one path per line.
|
||||
///
|
||||
/// Returns error_code::success() on successful read and parse of both files
|
||||
/// or an error_code indicating the encountered error.
|
||||
llvm::error_code readListFromFile(llvm::StringRef IncludeListFile,
|
||||
llvm::StringRef ExcludeListFile);
|
||||
|
||||
/// \brief Determine if the given path is in the list of include paths but
|
||||
/// not in the list of exclude paths.
|
||||
bool isFileIncluded(llvm::StringRef FilePath);
|
||||
|
||||
private:
|
||||
std::vector<std::string> IncludeList;
|
||||
std::vector<std::string> ExcludeList;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H
|
||||
@@ -1,14 +0,0 @@
|
||||
##===- cpp11-migrate/Core/Makefile -------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
CLANG_LEVEL := ../../../..
|
||||
LIBRARYNAME := migrateCore
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
CPP.Flags += -I$(PROJ_SRC_DIR)/..
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
void collectResults(clang::Rewriter &Rewrite,
|
||||
const FileContentsByPath &InputStates,
|
||||
FileContentsByPath &Results) {
|
||||
// Copy the contents of InputStates to be modified.
|
||||
Results = InputStates;
|
||||
|
||||
for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
|
||||
E = Rewrite.buffer_end();
|
||||
I != E; ++I) {
|
||||
const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
|
||||
assert(Entry != 0 && "Expected a FileEntry");
|
||||
assert(Entry->getName() != 0 &&
|
||||
"Unexpected NULL return from FileEntry::getName()");
|
||||
|
||||
std::string ResultBuf;
|
||||
|
||||
// Get a copy of the rewritten buffer from the Rewriter.
|
||||
llvm::raw_string_ostream StringStream(ResultBuf);
|
||||
I->second.write(StringStream);
|
||||
|
||||
// Cause results to be written to ResultBuf.
|
||||
StringStream.str();
|
||||
|
||||
// FIXME: Use move semantics to avoid copies of the buffer contents if
|
||||
// benchmarking shows the copies are expensive, especially for large source
|
||||
// files.
|
||||
Results[Entry->getName()] = ResultBuf;
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
//===-- cpp11-migrate/Transform.h - Transform Base Class Def'n --*- 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 definition for the base Transform class from
|
||||
/// which all transforms must subclass.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// For RewriterContainer
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
////
|
||||
|
||||
|
||||
/// \brief Description of the riskiness of actions that can be taken by
|
||||
/// transforms.
|
||||
enum RiskLevel {
|
||||
/// Transformations that will not change semantics.
|
||||
RL_Safe,
|
||||
|
||||
/// Transformations that might change semantics.
|
||||
RL_Reasonable,
|
||||
|
||||
/// Transformations that are likely to change semantics.
|
||||
RL_Risky
|
||||
};
|
||||
|
||||
// Forward declarations
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
class CompilationDatabase;
|
||||
} // namespace tooling
|
||||
} // namespace clang
|
||||
|
||||
/// \brief The key is the path of a file, which is mapped to a
|
||||
/// buffer with the possibly modified contents of that file.
|
||||
typedef std::map<std::string, std::string> FileContentsByPath;
|
||||
|
||||
/// \brief In \p Results place copies of the buffers resulting from applying
|
||||
/// all rewrites represented by \p Rewrite.
|
||||
///
|
||||
/// \p Results is made up of pairs {filename, buffer contents}. Pairs are
|
||||
/// simply appended to \p Results.
|
||||
void collectResults(clang::Rewriter &Rewrite,
|
||||
const FileContentsByPath &InputStates,
|
||||
FileContentsByPath &Results);
|
||||
|
||||
/// \brief Class for containing a Rewriter instance and all of
|
||||
/// its lifetime dependencies.
|
||||
///
|
||||
/// Subclasses of Transform using RefactoringTools will need to create
|
||||
/// Rewriters in order to apply Replacements and get the resulting buffer.
|
||||
/// Rewriter requires some objects to exist at least as long as it does so this
|
||||
/// class contains instances of those objects.
|
||||
///
|
||||
/// FIXME: These objects should really come from somewhere more global instead
|
||||
/// of being recreated for every Transform subclass, especially diagnostics.
|
||||
class RewriterContainer {
|
||||
public:
|
||||
RewriterContainer(clang::FileManager &Files,
|
||||
const FileContentsByPath &InputStates)
|
||||
: DiagOpts(new clang::DiagnosticOptions()),
|
||||
DiagnosticPrinter(llvm::errs(), DiagOpts.getPtr()),
|
||||
Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(
|
||||
new clang::DiagnosticIDs()),
|
||||
DiagOpts.getPtr(), &DiagnosticPrinter, false),
|
||||
Sources(Diagnostics, Files),
|
||||
Rewrite(Sources, DefaultLangOptions) {
|
||||
|
||||
// Overwrite source manager's file contents with data from InputStates
|
||||
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||
E = InputStates.end();
|
||||
I != E; ++I) {
|
||||
Sources.overrideFileContents(Files.getFile(I->first),
|
||||
llvm::MemoryBuffer::getMemBuffer(I->second));
|
||||
}
|
||||
}
|
||||
|
||||
clang::Rewriter &getRewriter() { return Rewrite; }
|
||||
|
||||
private:
|
||||
clang::LangOptions DefaultLangOptions;
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts;
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter;
|
||||
clang::DiagnosticsEngine Diagnostics;
|
||||
clang::SourceManager Sources;
|
||||
clang::Rewriter Rewrite;
|
||||
};
|
||||
|
||||
/// \brief Abstract base class for all C++11 migration transforms.
|
||||
class Transform {
|
||||
public:
|
||||
Transform(llvm::StringRef Name) : Name(Name) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
virtual ~Transform() {}
|
||||
|
||||
/// \brief Apply a transform to all files listed in \p SourcePaths.
|
||||
///
|
||||
/// \p Database must contain information for how to compile all files in \p
|
||||
/// SourcePaths. \p InputStates contains the file contents of files in \p
|
||||
/// SourcePaths and should take precedence over content of files on disk.
|
||||
/// Upon return, \p ResultStates shall contain the result of performing this
|
||||
/// transform on the files listed in \p SourcePaths.
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRiskLevel,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) = 0;
|
||||
|
||||
/// \brief Query if changes were made during the last call to apply().
|
||||
bool getChangesMade() const { return AcceptedChanges > 0; }
|
||||
|
||||
/// \brief Query if changes were not made due to conflicts with other changes
|
||||
/// made during the last call to apply() or if changes were too risky for the
|
||||
/// requested risk level.
|
||||
bool getChangesNotMade() const {
|
||||
return RejectedChanges > 0 || DeferredChanges > 0;
|
||||
}
|
||||
|
||||
/// \brief Query the number of accepted changes.
|
||||
unsigned getAcceptedChanges() const { return AcceptedChanges; }
|
||||
/// \brief Query the number of changes considered too risky.
|
||||
unsigned getRejectedChanges() const { return RejectedChanges; }
|
||||
/// \brief Query the number of changes not made because they conflicted with
|
||||
/// early changes.
|
||||
unsigned getDeferredChanges() const { return DeferredChanges; }
|
||||
|
||||
/// \brief Query transform name.
|
||||
llvm::StringRef getName() const { return Name; }
|
||||
|
||||
/// \brief Reset internal state of the transform.
|
||||
///
|
||||
/// Useful if calling apply() several times with one instantiation of a
|
||||
/// transform.
|
||||
void Reset() {
|
||||
AcceptedChanges = 0;
|
||||
RejectedChanges = 0;
|
||||
DeferredChanges = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setAcceptedChanges(unsigned Changes) {
|
||||
AcceptedChanges = Changes;
|
||||
}
|
||||
void setRejectedChanges(unsigned Changes) {
|
||||
RejectedChanges = Changes;
|
||||
}
|
||||
void setDeferredChanges(unsigned Changes) {
|
||||
DeferredChanges = Changes;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string Name;
|
||||
unsigned AcceptedChanges;
|
||||
unsigned RejectedChanges;
|
||||
unsigned DeferredChanges;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
||||
@@ -1,45 +0,0 @@
|
||||
//===-- cpp11-migrate/Transforms.cpp - class Transforms Impl ----*- 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 implementation for class Transforms.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Core/Transforms.h"
|
||||
#include "Core/Transform.h"
|
||||
|
||||
namespace cl = llvm::cl;
|
||||
|
||||
Transforms::~Transforms() {
|
||||
for (std::vector<Transform*>::iterator I = ChosenTransforms.begin(),
|
||||
E = ChosenTransforms.end(); I != E; ++I) {
|
||||
delete *I;
|
||||
}
|
||||
for (OptionVec::iterator I = Options.begin(),
|
||||
E = Options.end(); I != E; ++I) {
|
||||
delete I->first;
|
||||
}
|
||||
}
|
||||
|
||||
void Transforms::registerTransform(llvm::StringRef OptName,
|
||||
llvm::StringRef Description,
|
||||
TransformCreator Creator) {
|
||||
Options.push_back(OptionVec::value_type(
|
||||
new cl::opt<bool>(OptName.data(), cl::desc(Description.data())), Creator));
|
||||
}
|
||||
|
||||
void Transforms::createSelectedTransforms() {
|
||||
for (OptionVec::iterator I = Options.begin(),
|
||||
E = Options.end(); I != E; ++I) {
|
||||
if (*I->first) {
|
||||
ChosenTransforms.push_back(I->second());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
//===-- cpp11-migrate/Transforms.h - class Transforms Def'n -----*- 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 definition for class Transforms which is
|
||||
/// responsible for defining the command-line arguments exposing
|
||||
/// transformations to the user and applying requested transforms.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORMS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORMS_H
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace llvm {
|
||||
namespace cl {
|
||||
class Option;
|
||||
} // namespace cl
|
||||
} // namespace llvm
|
||||
class Transform;
|
||||
|
||||
typedef Transform *(*TransformCreator)();
|
||||
template <typename T>
|
||||
Transform *ConstructTransform() {
|
||||
return new T();
|
||||
}
|
||||
|
||||
/// \brief Class encapsulating the creation of command line bool options
|
||||
/// for each transform and instantiating transforms chosen by the user.
|
||||
class Transforms {
|
||||
public:
|
||||
typedef std::vector<Transform*> TransformVec;
|
||||
typedef TransformVec::const_iterator const_iterator;
|
||||
|
||||
public:
|
||||
|
||||
~Transforms();
|
||||
|
||||
/// \brief Registers a transform causing the transform to be made available
|
||||
/// on the command line.
|
||||
///
|
||||
/// Be sure to register all transforms *before* parsing command line options.
|
||||
void registerTransform(llvm::StringRef OptName, llvm::StringRef Description,
|
||||
TransformCreator Creator);
|
||||
|
||||
/// \brief Instantiate all transforms that were selected on the command line.
|
||||
///
|
||||
/// Call *after* parsing options.
|
||||
void createSelectedTransforms();
|
||||
|
||||
/// \brief Return an iterator to the start of a container of instantiated
|
||||
/// transforms.
|
||||
const_iterator begin() const { return ChosenTransforms.begin(); }
|
||||
|
||||
/// \brief Return an iterator to the end of a container of instantiated
|
||||
/// transforms.
|
||||
const_iterator end() const { return ChosenTransforms.end(); }
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<llvm::cl::opt<bool>*, TransformCreator> >
|
||||
OptionVec;
|
||||
|
||||
private:
|
||||
TransformVec ChosenTransforms;
|
||||
OptionVec Options;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORMS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,103 +0,0 @@
|
||||
//===-- LoopConvert/LoopActions.h - 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file declares matchers and callbacks for use in migrating C++
|
||||
/// for loops.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_ACTIONS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_ACTIONS_H
|
||||
|
||||
#include "StmtAncestor.h"
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
struct Usage;
|
||||
class Confidence;
|
||||
// The main computational result of ForLoopIndexUseVisitor.
|
||||
typedef llvm::SmallVector<Usage, 8> UsageResult;
|
||||
|
||||
enum LoopFixerKind {
|
||||
LFK_Array,
|
||||
LFK_Iterator,
|
||||
LFK_PseudoArray
|
||||
};
|
||||
|
||||
/// \brief The callback to be used for loop migration matchers.
|
||||
///
|
||||
/// The callback does extra checking not possible in matchers, and attempts to
|
||||
/// convert the for loop, if possible.
|
||||
class LoopFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
LoopFixer(StmtAncestorASTVisitor *ParentFinder,
|
||||
clang::tooling::Replacements *Replace,
|
||||
StmtGeneratedVarNameMap *GeneratedDecls,
|
||||
ReplacedVarsMap *ReplacedVarRanges,
|
||||
unsigned *AcceptedChanges, unsigned *DeferredChanges,
|
||||
unsigned *RejectedChanges,
|
||||
RiskLevel MaxRisk,
|
||||
LoopFixerKind FixerKind) :
|
||||
ParentFinder(ParentFinder), Replace(Replace),
|
||||
GeneratedDecls(GeneratedDecls), ReplacedVarRanges(ReplacedVarRanges),
|
||||
AcceptedChanges(AcceptedChanges), DeferredChanges(DeferredChanges),
|
||||
RejectedChanges(RejectedChanges),
|
||||
MaxRisk(MaxRisk), FixerKind(FixerKind) { }
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
|
||||
|
||||
private:
|
||||
StmtAncestorASTVisitor *ParentFinder;
|
||||
clang::tooling::Replacements *Replace;
|
||||
StmtGeneratedVarNameMap *GeneratedDecls;
|
||||
ReplacedVarsMap *ReplacedVarRanges;
|
||||
unsigned *AcceptedChanges;
|
||||
unsigned *DeferredChanges;
|
||||
unsigned *RejectedChanges;
|
||||
RiskLevel MaxRisk;
|
||||
LoopFixerKind FixerKind;
|
||||
|
||||
/// \brief Computes the changes needed to convert a given for loop, and
|
||||
/// applies it.
|
||||
void doConversion(clang::ASTContext *Context,
|
||||
const clang::VarDecl *IndexVar,
|
||||
const clang::VarDecl *MaybeContainer,
|
||||
llvm::StringRef ContainerString,
|
||||
const UsageResult &Usages,
|
||||
const clang::DeclStmt *AliasDecl,
|
||||
bool AliasUseRequired,
|
||||
bool AliasFromForInit,
|
||||
const clang::ForStmt *TheLoop,
|
||||
bool ContainerNeedsDereference,
|
||||
bool DerefByValue);
|
||||
|
||||
/// \brief Given a loop header that would be convertible, discover all usages
|
||||
/// of the index variable and convert the loop if possible.
|
||||
void findAndVerifyUsages(clang::ASTContext *Context,
|
||||
const clang::VarDecl *LoopVar,
|
||||
const clang::VarDecl *EndVar,
|
||||
const clang::Expr *ContainerExpr,
|
||||
const clang::Expr *BoundExpr,
|
||||
bool ContainerNeedsDereference,
|
||||
bool DerefByValue,
|
||||
const clang::ForStmt *TheLoop,
|
||||
Confidence ConfidenceLevel);
|
||||
|
||||
/// \brief Determine if the change should be deferred or rejected, returning
|
||||
/// text which refers to the container iterated over if the change should
|
||||
/// proceed.
|
||||
llvm::StringRef checkDeferralsAndRejections(clang::ASTContext *Context,
|
||||
const clang::Expr *ContainerExpr,
|
||||
Confidence ConfidenceLevel,
|
||||
const clang::ForStmt *TheLoop);
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_ACTIONS_H
|
||||
@@ -1,84 +0,0 @@
|
||||
//===-- LoopConvert/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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file provides the implementation of the LoopConvertTransform
|
||||
/// class.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LoopConvert.h"
|
||||
#include "LoopActions.h"
|
||||
#include "LoopMatchers.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
using clang::ast_matchers::MatchFinder;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
int LoopConvertTransform::apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRisk,
|
||||
const CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) {
|
||||
RefactoringTool LoopTool(Database, SourcePaths);
|
||||
|
||||
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||
E = InputStates.end();
|
||||
I != E; ++I) {
|
||||
LoopTool.mapVirtualFile(I->first, I->second);
|
||||
}
|
||||
|
||||
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,
|
||||
MaxRisk, LFK_Array);
|
||||
Finder.addMatcher(makeArrayLoopMatcher(), &ArrayLoopFixer);
|
||||
LoopFixer IteratorLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
|
||||
&GeneratedDecls, &ReplacedVars,
|
||||
&AcceptedChanges, &DeferredChanges,
|
||||
&RejectedChanges,
|
||||
MaxRisk, LFK_Iterator);
|
||||
Finder.addMatcher(makeIteratorLoopMatcher(), &IteratorLoopFixer);
|
||||
LoopFixer PseudoarrrayLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
|
||||
&GeneratedDecls, &ReplacedVars,
|
||||
&AcceptedChanges, &DeferredChanges,
|
||||
&RejectedChanges,
|
||||
MaxRisk, LFK_PseudoArray);
|
||||
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
||||
|
||||
if (int result = LoopTool.run(newFrontendActionFactory(&Finder))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
RewriterContainer Rewrite(LoopTool.getFiles(), InputStates);
|
||||
|
||||
// FIXME: Do something if some replacements didn't get applied?
|
||||
LoopTool.applyAllReplacements(Rewrite.getRewriter());
|
||||
|
||||
collectResults(Rewrite.getRewriter(), InputStates, ResultStates);
|
||||
|
||||
setAcceptedChanges(AcceptedChanges);
|
||||
setRejectedChanges(RejectedChanges);
|
||||
setDeferredChanges(DeferredChanges);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//===-- LoopConvert/LoopConvert.h - 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file provides the definition of the LoopConvertTransform
|
||||
/// class which is the main interface to the loop-convert transform
|
||||
/// that tries to make use of range-based for loops where possible.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "llvm/Support/Compiler.h" // For LLVM_OVERRIDE
|
||||
|
||||
/// \brief Subclass of Transform that transforms for-loops into range-based
|
||||
/// for-loops where possible.
|
||||
class LoopConvertTransform : public Transform {
|
||||
public:
|
||||
LoopConvertTransform() : Transform("LoopConvert") {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRiskLevel,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) LLVM_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
||||
@@ -1,278 +0,0 @@
|
||||
//===-- LoopConvert/LoopMatchers.h - Matchers for for loops -----*- 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 contains definitions of the matchers for use in migrating
|
||||
/// C++ for loops.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "LoopMatchers.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
const char LoopName[] = "forLoop";
|
||||
const char ConditionBoundName[] = "conditionBound";
|
||||
const char ConditionVarName[] = "conditionVar";
|
||||
const char IncrementVarName[] = "incrementVar";
|
||||
const char InitVarName[] = "initVar";
|
||||
const char EndCallName[] = "endCall";
|
||||
const char ConditionEndVarName[] = "conditionEndVar";
|
||||
const char EndVarName[] = "endVar";
|
||||
const char DerefByValueResultName[] = "derefByValueResult";
|
||||
|
||||
// shared matchers
|
||||
static const TypeMatcher AnyType = anything();
|
||||
|
||||
static const StatementMatcher IntegerComparisonMatcher =
|
||||
expr(ignoringParenImpCasts(declRefExpr(to(
|
||||
varDecl(hasType(isInteger())).bind(ConditionVarName)))));
|
||||
|
||||
static const DeclarationMatcher InitToZeroMatcher =
|
||||
varDecl(hasInitializer(ignoringParenImpCasts(
|
||||
integerLiteral(equals(0))))).bind(InitVarName);
|
||||
|
||||
static const StatementMatcher IncrementVarMatcher =
|
||||
declRefExpr(to(
|
||||
varDecl(hasType(isInteger())).bind(IncrementVarName)));
|
||||
|
||||
// FIXME: How best to document complicated matcher expressions? They're fairly
|
||||
// self-documenting...but there may be some unintuitive parts.
|
||||
|
||||
/// \brief The matcher for loops over arrays.
|
||||
///
|
||||
/// In this general example, assuming 'j' and 'k' are of integral type:
|
||||
/// \code
|
||||
/// for (int i = 0; j < 3 + 2; ++k) { ... }
|
||||
/// \endcode
|
||||
/// The following string identifers are bound to the parts of the AST:
|
||||
/// ConditionVarName: 'j' (as a VarDecl)
|
||||
/// ConditionBoundName: '3 + 2' (as an Expr)
|
||||
/// InitVarName: 'i' (as a VarDecl)
|
||||
/// IncrementVarName: 'k' (as a VarDecl)
|
||||
/// LoopName: The entire for loop (as a ForStmt)
|
||||
///
|
||||
/// Client code will need to make sure that:
|
||||
/// - The three index variables identified by the matcher are the same
|
||||
/// VarDecl.
|
||||
/// - The index variable is only used as an array index.
|
||||
/// - All arrays indexed by the loop are the same.
|
||||
StatementMatcher makeArrayLoopMatcher() {
|
||||
StatementMatcher ArrayBoundMatcher =
|
||||
expr(hasType(isInteger())).bind(ConditionBoundName);
|
||||
|
||||
return forStmt(
|
||||
hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
|
||||
hasCondition(anyOf(binaryOperator(hasOperatorName("<"),
|
||||
hasLHS(IntegerComparisonMatcher),
|
||||
hasRHS(ArrayBoundMatcher)),
|
||||
binaryOperator(hasOperatorName(">"),
|
||||
hasLHS(ArrayBoundMatcher),
|
||||
hasRHS(IntegerComparisonMatcher)))),
|
||||
hasIncrement(unaryOperator(hasOperatorName("++"),
|
||||
hasUnaryOperand(IncrementVarMatcher))))
|
||||
.bind(LoopName);
|
||||
}
|
||||
|
||||
/// \brief The matcher used for iterator-based for loops.
|
||||
///
|
||||
/// This matcher is more flexible than array-based loops. It will match
|
||||
/// catch loops of the following textual forms (regardless of whether the
|
||||
/// iterator type is actually a pointer type or a class type):
|
||||
///
|
||||
/// Assuming f, g, and h are of type containerType::iterator,
|
||||
/// \code
|
||||
/// for (containerType::iterator it = container.begin(),
|
||||
/// e = createIterator(); f != g; ++h) { ... }
|
||||
/// for (containerType::iterator it = container.begin();
|
||||
/// f != anotherContainer.end(); ++h) { ... }
|
||||
/// \endcode
|
||||
/// The following string identifiers are bound to the parts of the AST:
|
||||
/// InitVarName: 'it' (as a VarDecl)
|
||||
/// ConditionVarName: 'f' (as a VarDecl)
|
||||
/// LoopName: The entire for loop (as a ForStmt)
|
||||
/// In the first example only:
|
||||
/// EndVarName: 'e' (as a VarDecl)
|
||||
/// ConditionEndVarName: 'g' (as a VarDecl)
|
||||
/// In the second example only:
|
||||
/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
|
||||
///
|
||||
/// Client code will need to make sure that:
|
||||
/// - The iterator variables 'it', 'f', and 'h' are the same
|
||||
/// - The two containers on which 'begin' and 'end' are called are the same
|
||||
/// - If the end iterator variable 'g' is defined, it is the same as 'f'
|
||||
StatementMatcher makeIteratorLoopMatcher() {
|
||||
StatementMatcher BeginCallMatcher =
|
||||
memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("begin"))));
|
||||
|
||||
DeclarationMatcher InitDeclMatcher =
|
||||
varDecl(hasInitializer(anything())).bind(InitVarName);
|
||||
|
||||
DeclarationMatcher EndDeclMatcher =
|
||||
varDecl(hasInitializer(anything())).bind(EndVarName);
|
||||
|
||||
StatementMatcher EndCallMatcher =
|
||||
memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end"))));
|
||||
|
||||
StatementMatcher IteratorBoundMatcher =
|
||||
expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
|
||||
varDecl().bind(ConditionEndVarName)))),
|
||||
ignoringParenImpCasts(
|
||||
expr(EndCallMatcher).bind(EndCallName)),
|
||||
materializeTemporaryExpr(ignoringParenImpCasts(
|
||||
expr(EndCallMatcher).bind(EndCallName)))));
|
||||
|
||||
StatementMatcher IteratorComparisonMatcher =
|
||||
expr(ignoringParenImpCasts(declRefExpr(to(
|
||||
varDecl().bind(ConditionVarName)))));
|
||||
|
||||
StatementMatcher OverloadedNEQMatcher = operatorCallExpr(
|
||||
hasOverloadedOperatorName("!="),
|
||||
argumentCountIs(2),
|
||||
hasArgument(0, IteratorComparisonMatcher),
|
||||
hasArgument(1, IteratorBoundMatcher));
|
||||
|
||||
// This matcher tests that a declaration is a CXXRecordDecl that has an
|
||||
// overloaded operator*(). If the operator*() returns by value instead of by
|
||||
// reference then the return type is tagged with DerefByValueResultName.
|
||||
internal::Matcher<VarDecl> TestDerefReturnsByValue =
|
||||
hasType(
|
||||
recordDecl(
|
||||
hasMethod(
|
||||
allOf(
|
||||
hasOverloadedOperatorName("*"),
|
||||
anyOf(
|
||||
// Tag the return type if it's by value.
|
||||
returns(
|
||||
qualType(
|
||||
unless(hasCanonicalType(referenceType()))
|
||||
).bind(DerefByValueResultName)
|
||||
),
|
||||
returns(
|
||||
// Skip loops where the iterator's operator* returns an
|
||||
// rvalue reference. This is just weird.
|
||||
qualType(unless(hasCanonicalType(rValueReferenceType())))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return
|
||||
forStmt(
|
||||
hasLoopInit(anyOf(
|
||||
declStmt(
|
||||
declCountIs(2),
|
||||
containsDeclaration(0, InitDeclMatcher),
|
||||
containsDeclaration(1, EndDeclMatcher)
|
||||
),
|
||||
declStmt(hasSingleDecl(InitDeclMatcher))
|
||||
)),
|
||||
hasCondition(anyOf(
|
||||
binaryOperator(
|
||||
hasOperatorName("!="),
|
||||
hasLHS(IteratorComparisonMatcher),
|
||||
hasRHS(IteratorBoundMatcher)
|
||||
),
|
||||
binaryOperator(
|
||||
hasOperatorName("!="),
|
||||
hasLHS(IteratorBoundMatcher),
|
||||
hasRHS(IteratorComparisonMatcher)
|
||||
),
|
||||
OverloadedNEQMatcher
|
||||
)),
|
||||
hasIncrement(anyOf(
|
||||
unaryOperator(
|
||||
hasOperatorName("++"),
|
||||
hasUnaryOperand(
|
||||
declRefExpr(to(
|
||||
varDecl(hasType(pointsTo(AnyType))).bind(IncrementVarName)
|
||||
))
|
||||
)
|
||||
),
|
||||
operatorCallExpr(
|
||||
hasOverloadedOperatorName("++"),
|
||||
hasArgument(0,
|
||||
declRefExpr(to(
|
||||
varDecl(TestDerefReturnsByValue).bind(IncrementVarName)
|
||||
))
|
||||
)
|
||||
)
|
||||
))
|
||||
).bind(LoopName);
|
||||
}
|
||||
|
||||
/// \brief The matcher used for array-like containers (pseudoarrays).
|
||||
///
|
||||
/// This matcher is more flexible than array-based loops. It will match
|
||||
/// loops of the following textual forms (regardless of whether the
|
||||
/// iterator type is actually a pointer type or a class type):
|
||||
///
|
||||
/// Assuming f, g, and h are of type containerType::iterator,
|
||||
/// \code
|
||||
/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
|
||||
/// for (int i = 0; f < container.size(); ++h) { ... }
|
||||
/// \endcode
|
||||
/// The following string identifiers are bound to the parts of the AST:
|
||||
/// InitVarName: 'i' (as a VarDecl)
|
||||
/// ConditionVarName: 'f' (as a VarDecl)
|
||||
/// LoopName: The entire for loop (as a ForStmt)
|
||||
/// In the first example only:
|
||||
/// EndVarName: 'j' (as a VarDecl)
|
||||
/// ConditionEndVarName: 'g' (as a VarDecl)
|
||||
/// In the second example only:
|
||||
/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
|
||||
///
|
||||
/// Client code will need to make sure that:
|
||||
/// - The index variables 'i', 'f', and 'h' are the same
|
||||
/// - The containers on which 'size()' is called is the container indexed
|
||||
/// - The index variable is only used in overloaded operator[] or
|
||||
/// container.at()
|
||||
/// - If the end iterator variable 'g' is defined, it is the same as 'j'
|
||||
/// - The container's iterators would not be invalidated during the loop
|
||||
StatementMatcher makePseudoArrayLoopMatcher() {
|
||||
StatementMatcher SizeCallMatcher =
|
||||
memberCallExpr(argumentCountIs(0),
|
||||
callee(methodDecl(anyOf(hasName("size"),
|
||||
hasName("length")))));
|
||||
|
||||
StatementMatcher EndInitMatcher =
|
||||
expr(anyOf(
|
||||
ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
|
||||
explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
|
||||
expr(SizeCallMatcher).bind(EndCallName))))));
|
||||
|
||||
DeclarationMatcher EndDeclMatcher =
|
||||
varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
|
||||
|
||||
StatementMatcher IndexBoundMatcher =
|
||||
expr(anyOf(
|
||||
ignoringParenImpCasts(declRefExpr(to(
|
||||
varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
|
||||
EndInitMatcher));
|
||||
|
||||
return forStmt(
|
||||
hasLoopInit(anyOf(
|
||||
declStmt(declCountIs(2),
|
||||
containsDeclaration(0, InitToZeroMatcher),
|
||||
containsDeclaration(1, EndDeclMatcher)),
|
||||
declStmt(hasSingleDecl(InitToZeroMatcher)))),
|
||||
hasCondition(anyOf(
|
||||
binaryOperator(hasOperatorName("<"),
|
||||
hasLHS(IntegerComparisonMatcher),
|
||||
hasRHS(IndexBoundMatcher)),
|
||||
binaryOperator(hasOperatorName(">"),
|
||||
hasLHS(IndexBoundMatcher),
|
||||
hasRHS(IntegerComparisonMatcher)))),
|
||||
hasIncrement(unaryOperator(
|
||||
hasOperatorName("++"),
|
||||
hasUnaryOperand(IncrementVarMatcher))))
|
||||
.bind(LoopName);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//===-- LoopConvert/LoopMatchers.h - Matchers for for loops -----*- 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 contains declarations of the matchers for use in migrating
|
||||
/// C++ for loops. The matchers are responsible for checking the general shape
|
||||
/// of the for loop, namely the init, condition, and increment portions.
|
||||
/// Further analysis will be needed to confirm that the loop is in fact
|
||||
/// convertible in the matcher callback.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_MATCHERS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_MATCHERS_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
|
||||
// Constants used for matcher name bindings
|
||||
extern const char LoopName[];
|
||||
extern const char ConditionBoundName[];
|
||||
extern const char ConditionVarName[];
|
||||
extern const char ConditionEndVarName[];
|
||||
extern const char IncrementVarName[];
|
||||
extern const char InitVarName[];
|
||||
extern const char EndExprName[];
|
||||
extern const char EndCallName[];
|
||||
extern const char EndVarName[];
|
||||
extern const char DerefByValueResultName[];
|
||||
|
||||
clang::ast_matchers::StatementMatcher makeArrayLoopMatcher();
|
||||
clang::ast_matchers::StatementMatcher makeIteratorLoopMatcher();
|
||||
clang::ast_matchers::StatementMatcher makePseudoArrayLoopMatcher();
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_MATCHERS_H
|
||||
@@ -1,139 +0,0 @@
|
||||
//===-- LoopConvert/StmtAncestor.cpp - AST property visitors ----*- 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 contains the definitions of several RecursiveASTVisitors
|
||||
/// used to build and check data structures used in loop migration.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "StmtAncestor.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
/// \brief Tracks a stack of parent statements during traversal.
|
||||
///
|
||||
/// All this really does is inject push_back() before running
|
||||
/// RecursiveASTVisitor::TraverseStmt() and pop_back() afterwards. The Stmt atop
|
||||
/// the stack is the parent of the current statement (NULL for the topmost
|
||||
/// statement).
|
||||
bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
|
||||
StmtAncestors.insert(std::make_pair(Statement, StmtStack.back()));
|
||||
StmtStack.push_back(Statement);
|
||||
RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
|
||||
StmtStack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Keep track of the DeclStmt associated with each VarDecl.
|
||||
///
|
||||
/// Combined with StmtAncestors, this provides roughly the same information as
|
||||
/// Scope, as we can map a VarDecl to its DeclStmt, then walk up the parent tree
|
||||
/// using StmtAncestors.
|
||||
bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Decls) {
|
||||
for (DeclStmt::const_decl_iterator I = Decls->decl_begin(),
|
||||
E = Decls->decl_end(); I != E; ++I)
|
||||
if (const VarDecl *V = dyn_cast<VarDecl>(*I))
|
||||
DeclParents.insert(std::make_pair(V, Decls));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief record the DeclRefExpr as part of the parent expression.
|
||||
bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
|
||||
Components.push_back(E);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief record the MemberExpr as part of the parent expression.
|
||||
bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
|
||||
Components.push_back(Member);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Forward any DeclRefExprs to a check on the referenced variable
|
||||
/// declaration.
|
||||
bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
|
||||
if (VarDecl *V = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
|
||||
return VisitVarDecl(V);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Determine if any this variable is declared inside the ContainingStmt.
|
||||
bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *V) {
|
||||
const Stmt *Curr = DeclParents->lookup(V);
|
||||
// First, see if the variable was declared within an inner scope of the loop.
|
||||
while (Curr != NULL) {
|
||||
if (Curr == ContainingStmt) {
|
||||
DependsOnInsideVariable = true;
|
||||
return false;
|
||||
}
|
||||
Curr = StmtParents->lookup(Curr);
|
||||
}
|
||||
|
||||
// Next, check if the variable was removed from existence by an earlier
|
||||
// iteration.
|
||||
for (ReplacedVarsMap::const_iterator I = ReplacedVars->begin(),
|
||||
E = ReplacedVars->end(); I != E; ++I)
|
||||
if ((*I).second == V) {
|
||||
DependsOnInsideVariable = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief If we already created a variable for TheLoop, check to make sure
|
||||
/// that the name was not already taken.
|
||||
bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
|
||||
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(TheLoop);
|
||||
if (I != GeneratedDecls->end() && I->second == Name) {
|
||||
Found = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief If any named declaration within the AST subtree has the same name,
|
||||
/// then consider Name already taken.
|
||||
bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *D) {
|
||||
const IdentifierInfo *Ident = D->getIdentifier();
|
||||
if (Ident && Ident->getName() == Name) {
|
||||
Found = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Forward any declaration references to the actual check on the
|
||||
/// referenced declaration.
|
||||
bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
|
||||
if (NamedDecl *D = dyn_cast<NamedDecl>(DeclRef->getDecl()))
|
||||
return VisitNamedDecl(D);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief If the new variable name conflicts with any type used in the loop,
|
||||
/// then we mark that variable name as taken.
|
||||
bool DeclFinderASTVisitor::VisitTypeLoc(TypeLoc TL) {
|
||||
QualType QType = TL.getType();
|
||||
|
||||
// Check if our name conflicts with a type, to handle for typedefs.
|
||||
if (QType.getAsString() == Name) {
|
||||
Found = true;
|
||||
return false;
|
||||
}
|
||||
// Check for base type conflicts. For example, when a struct is being
|
||||
// referenced in the body of the loop, the above getAsString() will return the
|
||||
// whole type (ex. "struct s"), but will be caught here.
|
||||
if (const IdentifierInfo *Ident = QType.getBaseTypeIdentifier()) {
|
||||
if (Ident->getName() == Name) {
|
||||
Found = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
//===-- LoopConvert/StmtAncestor.h - AST property visitors ------*- 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 contains the declarations of several RecursiveASTVisitors
|
||||
/// used to build and check data structures used in loop migration.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_STMT_ANCESTOR_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_STMT_ANCESTOR_H
|
||||
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
|
||||
/// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
|
||||
typedef llvm::DenseMap<const clang::Stmt*, const clang::Stmt*> StmtParentMap;
|
||||
|
||||
/// A map used to walk the AST in reverse:
|
||||
/// maps VarDecl to the to parent DeclStmt.
|
||||
typedef
|
||||
llvm::DenseMap<const clang::VarDecl*, const clang::DeclStmt*> DeclParentMap;
|
||||
|
||||
/// A map used to track which variables have been removed by a refactoring pass.
|
||||
/// It maps the parent ForStmt to the removed index variable's VarDecl.
|
||||
typedef
|
||||
llvm::DenseMap<const clang::ForStmt*, const clang::VarDecl*> ReplacedVarsMap;
|
||||
|
||||
/// A map used to remember the variable names generated in a Stmt
|
||||
typedef llvm::DenseMap<const clang::Stmt*, std::string> StmtGeneratedVarNameMap;
|
||||
|
||||
/// A vector used to store the AST subtrees of an Expr.
|
||||
typedef llvm::SmallVector<const clang::Expr*, 16> ComponentVector;
|
||||
|
||||
/// \brief Class used build the reverse AST properties needed to detect
|
||||
/// name conflicts and free variables.
|
||||
class StmtAncestorASTVisitor :
|
||||
public clang::RecursiveASTVisitor<StmtAncestorASTVisitor> {
|
||||
public:
|
||||
StmtAncestorASTVisitor() {
|
||||
StmtStack.push_back(NULL);
|
||||
}
|
||||
|
||||
/// \brief Run the analysis on the TranslationUnitDecl.
|
||||
///
|
||||
/// In case we're running this analysis multiple times, don't repeat the work.
|
||||
void gatherAncestors(const clang::TranslationUnitDecl *T) {
|
||||
if (StmtAncestors.empty())
|
||||
TraverseDecl(const_cast<clang::TranslationUnitDecl*>(T));
|
||||
}
|
||||
|
||||
/// Accessor for StmtAncestors.
|
||||
const StmtParentMap &getStmtToParentStmtMap() {
|
||||
return StmtAncestors;
|
||||
}
|
||||
|
||||
/// Accessor for DeclParents.
|
||||
const DeclParentMap &getDeclToParentStmtMap() {
|
||||
return DeclParents;
|
||||
}
|
||||
|
||||
friend class clang::RecursiveASTVisitor<StmtAncestorASTVisitor>;
|
||||
|
||||
private:
|
||||
StmtParentMap StmtAncestors;
|
||||
DeclParentMap DeclParents;
|
||||
llvm::SmallVector<const clang::Stmt*, 16> StmtStack;
|
||||
|
||||
bool TraverseStmt(clang::Stmt *Statement);
|
||||
bool VisitDeclStmt(clang::DeclStmt *Statement);
|
||||
};
|
||||
|
||||
/// Class used to find the variables and member expressions on which an
|
||||
/// arbitrary expression depends.
|
||||
class ComponentFinderASTVisitor :
|
||||
public clang::RecursiveASTVisitor<ComponentFinderASTVisitor> {
|
||||
public:
|
||||
ComponentFinderASTVisitor() { }
|
||||
|
||||
/// Find the components of an expression and place them in a ComponentVector.
|
||||
void findExprComponents(const clang::Expr *SourceExpr) {
|
||||
clang::Expr *E = const_cast<clang::Expr *>(SourceExpr);
|
||||
TraverseStmt(E);
|
||||
}
|
||||
|
||||
/// Accessor for Components.
|
||||
const ComponentVector &getComponents() {
|
||||
return Components;
|
||||
}
|
||||
|
||||
friend class clang::RecursiveASTVisitor<ComponentFinderASTVisitor>;
|
||||
|
||||
private:
|
||||
ComponentVector Components;
|
||||
|
||||
bool VisitDeclRefExpr(clang::DeclRefExpr *E);
|
||||
bool VisitMemberExpr(clang::MemberExpr *Member);
|
||||
};
|
||||
|
||||
/// Class used to determine if an expression is dependent on a variable declared
|
||||
/// inside of the loop where it would be used.
|
||||
class DependencyFinderASTVisitor :
|
||||
public clang::RecursiveASTVisitor<DependencyFinderASTVisitor> {
|
||||
public:
|
||||
DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
|
||||
const DeclParentMap *DeclParents,
|
||||
const ReplacedVarsMap *ReplacedVars,
|
||||
const clang::Stmt *ContainingStmt) :
|
||||
StmtParents(StmtParents), DeclParents(DeclParents),
|
||||
ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) { }
|
||||
|
||||
/// \brief Run the analysis on Body, and return true iff the expression
|
||||
/// depends on some variable declared within ContainingStmt.
|
||||
///
|
||||
/// This is intended to protect against hoisting the container expression
|
||||
/// outside of an inner context if part of that expression is declared in that
|
||||
/// inner context.
|
||||
///
|
||||
/// For example,
|
||||
/// \code
|
||||
/// const int N = 10, M = 20;
|
||||
/// int arr[N][M];
|
||||
/// int getRow();
|
||||
///
|
||||
/// for (int i = 0; i < M; ++i) {
|
||||
/// int k = getRow();
|
||||
/// printf("%d:", arr[k][i]);
|
||||
/// }
|
||||
/// \endcode
|
||||
/// At first glance, this loop looks like it could be changed to
|
||||
/// \code
|
||||
/// for (int elem : arr[k]) {
|
||||
/// int k = getIndex();
|
||||
/// printf("%d:", elem);
|
||||
/// }
|
||||
/// \endcode
|
||||
/// But this is malformed, since `k` is used before it is defined!
|
||||
///
|
||||
/// In order to avoid this, this class looks at the container expression
|
||||
/// `arr[k]` and decides whether or not it contains a sub-expression declared
|
||||
/// within the the loop body.
|
||||
bool dependsOnInsideVariable(const clang::Stmt *Body) {
|
||||
DependsOnInsideVariable = false;
|
||||
TraverseStmt(const_cast<clang::Stmt *>(Body));
|
||||
return DependsOnInsideVariable;
|
||||
}
|
||||
|
||||
friend class clang::RecursiveASTVisitor<DependencyFinderASTVisitor>;
|
||||
|
||||
private:
|
||||
const StmtParentMap *StmtParents;
|
||||
const DeclParentMap *DeclParents;
|
||||
const clang::Stmt *ContainingStmt;
|
||||
const ReplacedVarsMap *ReplacedVars;
|
||||
bool DependsOnInsideVariable;
|
||||
|
||||
bool VisitVarDecl(clang::VarDecl *V);
|
||||
bool VisitDeclRefExpr(clang::DeclRefExpr *D);
|
||||
};
|
||||
|
||||
/// Class used to determine if any declarations used in a Stmt would conflict
|
||||
/// with a particular identifier. This search includes the names that don't
|
||||
/// actually appear in the AST (i.e. created by a refactoring tool) by including
|
||||
/// a map from Stmts to generated names associated with those stmts.
|
||||
class DeclFinderASTVisitor :
|
||||
public clang::RecursiveASTVisitor<DeclFinderASTVisitor> {
|
||||
public:
|
||||
DeclFinderASTVisitor(const std::string &Name,
|
||||
const StmtGeneratedVarNameMap *GeneratedDecls) :
|
||||
Name(Name), GeneratedDecls(GeneratedDecls), Found(false) { }
|
||||
|
||||
/// Attempts to find any usages of variables name Name in Body, returning
|
||||
/// true when it is used in Body. This includes the generated loop variables
|
||||
/// of ForStmts which have already been transformed.
|
||||
bool findUsages(const clang::Stmt *Body) {
|
||||
Found = false;
|
||||
TraverseStmt(const_cast<clang::Stmt *>(Body));
|
||||
return Found;
|
||||
}
|
||||
|
||||
friend class clang::RecursiveASTVisitor<DeclFinderASTVisitor>;
|
||||
|
||||
private:
|
||||
std::string Name;
|
||||
/// GeneratedDecls keeps track of ForStmts which have been tranformed, mapping
|
||||
/// each modified ForStmt to the variable generated in the loop.
|
||||
const StmtGeneratedVarNameMap *GeneratedDecls;
|
||||
bool Found;
|
||||
|
||||
bool VisitForStmt(clang::ForStmt *F);
|
||||
bool VisitNamedDecl(clang::NamedDecl *D);
|
||||
bool VisitDeclRefExpr(clang::DeclRefExpr *D);
|
||||
bool VisitTypeLoc(clang::TypeLoc TL);
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_STMT_ANCESTOR_H
|
||||
@@ -1,94 +0,0 @@
|
||||
//===-- LoopConvert/VariableNaming.h - Gererate variable names --*- 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 contains the definitino of the VariableNamer class, which
|
||||
/// is responsible for generating new variable names and ensuring that they do
|
||||
/// not conflict with existing ones.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "VariableNaming.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
std::string VariableNamer::createIndexName() {
|
||||
// FIXME: Add in naming conventions to handle:
|
||||
// - Uppercase/lowercase indices
|
||||
// - How to handle conflicts
|
||||
// - An interactive process for naming
|
||||
std::string IteratorName;
|
||||
std::string ContainerName;
|
||||
if (TheContainer)
|
||||
ContainerName = TheContainer->getName().str();
|
||||
|
||||
size_t Len = ContainerName.length();
|
||||
if (Len > 1 && ContainerName[Len - 1] == 's')
|
||||
IteratorName = ContainerName.substr(0, Len - 1);
|
||||
else
|
||||
IteratorName = "elem";
|
||||
|
||||
if (!declarationExists(IteratorName))
|
||||
return IteratorName;
|
||||
|
||||
IteratorName = ContainerName + "_" + OldIndex->getName().str();
|
||||
if (!declarationExists(IteratorName))
|
||||
return IteratorName;
|
||||
|
||||
IteratorName = ContainerName + "_elem";
|
||||
if (!declarationExists(IteratorName))
|
||||
return IteratorName;
|
||||
|
||||
IteratorName += "_elem";
|
||||
if (!declarationExists(IteratorName))
|
||||
return IteratorName;
|
||||
|
||||
IteratorName = "_elem_";
|
||||
|
||||
// Someone defeated my naming scheme...
|
||||
while (declarationExists(IteratorName))
|
||||
IteratorName += "i";
|
||||
return IteratorName;
|
||||
}
|
||||
|
||||
/// \brief Determines whether or not the the name \a Symbol conflicts with
|
||||
/// language keywords or defined macros. Also checks if the name exists in
|
||||
/// LoopContext, any of its parent contexts, or any of its child statements.
|
||||
///
|
||||
/// We also check to see if the same identifier was generated by this loop
|
||||
/// converter in a loop nested within SourceStmt.
|
||||
bool VariableNamer::declarationExists(StringRef Symbol) {
|
||||
assert(Context != 0 && "Expected an ASTContext");
|
||||
IdentifierInfo &Ident = Context->Idents.get(Symbol);
|
||||
|
||||
// Check if the symbol is not an identifier (ie. is a keyword or alias).
|
||||
if (!isAnyIdentifier(Ident.getTokenID()))
|
||||
return true;
|
||||
|
||||
// Check for conflicting macro definitions.
|
||||
if (Ident.hasMacroDefinition())
|
||||
return true;
|
||||
|
||||
// Determine if the symbol was generated in a parent context.
|
||||
for (const Stmt *S = SourceStmt; S != NULL; S = ReverseAST->lookup(S)) {
|
||||
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
|
||||
if (I != GeneratedDecls->end() && I->second == Symbol)
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: Rather than detecting conflicts at their usages, we should check the
|
||||
// parent context.
|
||||
// For some reason, lookup() always returns the pair (NULL, NULL) because its
|
||||
// StoredDeclsMap is not initialized (i.e. LookupPtr.getInt() is false inside
|
||||
// of DeclContext::lookup()). Why is this?
|
||||
|
||||
// Finally, determine if the symbol was used in the loop or a child context.
|
||||
DeclFinderASTVisitor DeclFinder(Symbol, GeneratedDecls);
|
||||
return DeclFinder.findUsages(SourceStmt);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//===-- LoopConvert/VariableNaming.h - Gererate variable names --*- 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 contains the declaration of the VariableNamer class, which
|
||||
/// is responsible for generating new variable names and ensuring that they do
|
||||
/// not conflict with existing ones.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_VARIABLE_NAMING_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_VARIABLE_NAMING_H
|
||||
|
||||
#include "StmtAncestor.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
/// \brief Create names for generated variables within a particular statement.
|
||||
///
|
||||
/// VariableNamer uses a DeclContext as a reference point, checking for any
|
||||
/// conflicting declarations higher up in the context or within SourceStmt.
|
||||
/// It creates a variable name using hints from a source container and the old
|
||||
/// index, if they exist.
|
||||
class VariableNamer {
|
||||
public:
|
||||
VariableNamer(
|
||||
StmtGeneratedVarNameMap *GeneratedDecls, const StmtParentMap *ReverseAST,
|
||||
const clang::Stmt *SourceStmt, const clang::VarDecl *OldIndex,
|
||||
const clang::VarDecl *TheContainer, const clang::ASTContext *Context)
|
||||
: GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
|
||||
SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
|
||||
Context(Context) {}
|
||||
|
||||
/// \brief Generate a new index name.
|
||||
///
|
||||
/// Generates the name to be used for an inserted iterator. It relies on
|
||||
/// declarationExists() to determine that there are no naming conflicts, and
|
||||
/// tries to use some hints from the container name and the old index name.
|
||||
std::string createIndexName();
|
||||
|
||||
private:
|
||||
StmtGeneratedVarNameMap *GeneratedDecls;
|
||||
const StmtParentMap *ReverseAST;
|
||||
const clang::Stmt *SourceStmt;
|
||||
const clang::VarDecl *OldIndex;
|
||||
const clang::VarDecl *TheContainer;
|
||||
const clang::ASTContext *Context;
|
||||
|
||||
// Determine whether or not a declaration that would conflict with Symbol
|
||||
// exists in an outer context or in any statement contained in SourceStmt.
|
||||
bool declarationExists(llvm::StringRef Symbol);
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_VARIABLE_NAMING_H
|
||||
@@ -1,15 +0,0 @@
|
||||
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../..
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
|
||||
DIRS = Core tool
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
@@ -1,61 +0,0 @@
|
||||
//===-- UseAuto/UseAuto.cpp - Use auto type specifier ---------------------===//
|
||||
//
|
||||
// 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 of the UseAutoTransform class.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UseAuto.h"
|
||||
#include "UseAutoActions.h"
|
||||
#include "UseAutoMatchers.h"
|
||||
|
||||
using clang::ast_matchers::MatchFinder;
|
||||
using namespace clang;
|
||||
using namespace clang::tooling;
|
||||
|
||||
int UseAutoTransform::apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRisk,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) {
|
||||
RefactoringTool UseAutoTool(Database, SourcePaths);
|
||||
|
||||
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||
E = InputStates.end();
|
||||
I != E; ++I)
|
||||
UseAutoTool.mapVirtualFile(I->first, I->second);
|
||||
|
||||
unsigned AcceptedChanges = 0;
|
||||
|
||||
MatchFinder Finder;
|
||||
IteratorReplacer ReplaceIterators(UseAutoTool.getReplacements(),
|
||||
AcceptedChanges, MaxRisk);
|
||||
NewReplacer ReplaceNew(UseAutoTool.getReplacements(), AcceptedChanges,
|
||||
MaxRisk);
|
||||
|
||||
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
|
||||
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
|
||||
|
||||
if (int Result = UseAutoTool.run(newFrontendActionFactory(&Finder))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return Result;
|
||||
}
|
||||
|
||||
RewriterContainer Rewrite(UseAutoTool.getFiles(), InputStates);
|
||||
|
||||
// FIXME: Do something if some replacements didn't get applied?
|
||||
UseAutoTool.applyAllReplacements(Rewrite.getRewriter());
|
||||
|
||||
collectResults(Rewrite.getRewriter(), InputStates, ResultStates);
|
||||
|
||||
setAcceptedChanges(AcceptedChanges);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//===-- UseAuto/UseAuto.h - Use auto type specifier -------------*- 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 definition of the UseAutoTransform class
|
||||
/// which is the main interface to the use-auto transform that replaces
|
||||
/// type specifiers with the special C++11 'auto' type specifier in certain
|
||||
/// situations.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
/// \brief Subclass of Transform that transforms type specifiers for variable
|
||||
/// declarations into the special C++11 'auto' type specifier for certain cases:
|
||||
/// * Iterators of std containers.
|
||||
/// * More to come...
|
||||
///
|
||||
/// Other uses of the auto type specifier as outlined in C++11 [dcl.spec.auto]
|
||||
/// p2 are not handled by this transform.
|
||||
class UseAutoTransform : public Transform {
|
||||
public:
|
||||
UseAutoTransform() : Transform("UseAuto") {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRiskLEvel,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) LLVM_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
|
||||
@@ -1,116 +0,0 @@
|
||||
//===-- UseAuto/UseAutoActions.cpp - Matcher callback impl ----------------===//
|
||||
//
|
||||
// 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 contains the implementation of callbacks for the UseAuto
|
||||
/// transform.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "UseAutoActions.h"
|
||||
#include "UseAutoMatchers.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
void IteratorReplacer::run(const MatchFinder::MatchResult &Result) {
|
||||
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(IteratorDeclId);
|
||||
|
||||
assert(D && "Bad Callback. No node provided");
|
||||
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
if (!SM.isFromMainFile(D->getLocStart()))
|
||||
return;
|
||||
|
||||
const Expr *ExprInit = D->getInit();
|
||||
|
||||
// Skip expressions with cleanups from the initializer expression.
|
||||
if (const ExprWithCleanups *E = dyn_cast<ExprWithCleanups>(ExprInit))
|
||||
ExprInit = E->getSubExpr();
|
||||
|
||||
const CXXConstructExpr *Construct = cast<CXXConstructExpr>(ExprInit);
|
||||
|
||||
assert(Construct->getNumArgs() == 1u &&
|
||||
"Expected constructor with single argument");
|
||||
|
||||
// Drill down to the as-written initializer.
|
||||
const Expr *E = Construct->arg_begin()->IgnoreParenImpCasts();
|
||||
if (E != E->IgnoreConversionOperator())
|
||||
// We hit a conversion operator. Early-out now as they imply an implicit
|
||||
// conversion from a different type. Could also mean an explicit conversion
|
||||
// from the same type but that's pretty rare.
|
||||
return;
|
||||
|
||||
if (const CXXConstructExpr *NestedConstruct = dyn_cast<CXXConstructExpr>(E))
|
||||
// If we ran into an implicit conversion constructor, can't convert.
|
||||
//
|
||||
// FIXME: The following only checks if the constructor can be used
|
||||
// implicitly, not if it actually was. Cases where the converting constructor
|
||||
// was used explicitly won't get converted.
|
||||
if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
|
||||
return;
|
||||
|
||||
if (Result.Context->hasSameType(D->getType(), E->getType())) {
|
||||
TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
|
||||
|
||||
// WARNING: TypeLoc::getSourceRange() will include the identifier for things
|
||||
// like function pointers. Not a concern since this action only works with
|
||||
// iterators but something to keep in mind in the future.
|
||||
|
||||
CharSourceRange Range(TL.getSourceRange(), true);
|
||||
Replace.insert(tooling::Replacement(SM, Range, "auto"));
|
||||
++AcceptedChanges;
|
||||
}
|
||||
}
|
||||
|
||||
void NewReplacer::run(const MatchFinder::MatchResult &Result) {
|
||||
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(DeclWithNewId);
|
||||
assert(D && "Bad Callback. No node provided");
|
||||
|
||||
SourceManager &SM = *Result.SourceManager;
|
||||
if (!SM.isFromMainFile(D->getLocStart()))
|
||||
return;
|
||||
|
||||
const CXXNewExpr *NewExpr = Result.Nodes.getNodeAs<CXXNewExpr>(NewExprId);
|
||||
assert(NewExpr && "Bad Callback. No CXXNewExpr bound");
|
||||
|
||||
// If declaration and initializer have exactly the same type, just replace
|
||||
// with 'auto'.
|
||||
if (Result.Context->hasSameType(D->getType(), NewExpr->getType())) {
|
||||
TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
|
||||
CharSourceRange Range(TL.getSourceRange(), /*IsTokenRange=*/ true);
|
||||
// Space after 'auto' to handle cases where the '*' in the pointer type
|
||||
// is next to the identifier. This avoids changing 'int *p' into 'autop'.
|
||||
Replace.insert(tooling::Replacement(SM, Range, "auto "));
|
||||
++AcceptedChanges;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the CV qualifiers for the pointer differ then we still use auto, just
|
||||
// need to leave the qualifier behind.
|
||||
if (Result.Context->hasSameUnqualifiedType(D->getType(),
|
||||
NewExpr->getType())) {
|
||||
TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
|
||||
CharSourceRange Range(TL.getSourceRange(), /*IsTokenRange=*/ true);
|
||||
// Space after 'auto' to handle cases where the '*' in the pointer type
|
||||
// is next to the identifier. This avoids changing 'int *p' into 'autop'.
|
||||
Replace.insert(tooling::Replacement(SM, Range, "auto "));
|
||||
++AcceptedChanges;
|
||||
return;
|
||||
}
|
||||
|
||||
// The VarDecl and Initializer have mismatching types.
|
||||
return;
|
||||
|
||||
// FIXME: There is, however, one case we can address: when the VarDecl
|
||||
// pointee is the same as the initializer, just more CV-qualified. However,
|
||||
// TypeLoc information is not reliable where CV qualifiers are concerned so
|
||||
// we can't do anything about this case for now.
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//===-- UseAuto/Actions.h - Matcher callback ---------------------*- 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 contains the declarations for callbacks used by the
|
||||
/// UseAuto transform.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
/// \brief The callback to be used when replacing type specifiers of variable
|
||||
/// declarations that are iterators.
|
||||
class IteratorReplacer
|
||||
: public clang::ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
IteratorReplacer(clang::tooling::Replacements &Replace,
|
||||
unsigned &AcceptedChanges, RiskLevel)
|
||||
: Replace(Replace), AcceptedChanges(AcceptedChanges) {
|
||||
}
|
||||
|
||||
/// \brief Entry point to the callback called when matches are made.
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
|
||||
LLVM_OVERRIDE;
|
||||
|
||||
private:
|
||||
clang::tooling::Replacements &Replace;
|
||||
unsigned &AcceptedChanges;
|
||||
};
|
||||
|
||||
/// \brief The callback used when replacing type specifiers of variable
|
||||
/// declarations initialized by a C++ new expression.
|
||||
class NewReplacer : public clang::ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
NewReplacer(clang::tooling::Replacements &Replace, unsigned &AcceptedChanges,
|
||||
RiskLevel)
|
||||
: Replace(Replace), AcceptedChanges(AcceptedChanges) {
|
||||
}
|
||||
|
||||
/// \brief Entry point to the callback called when matches are made.
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
|
||||
LLVM_OVERRIDE;
|
||||
|
||||
private:
|
||||
clang::tooling::Replacements &Replace;
|
||||
unsigned &AcceptedChanges;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
|
||||
@@ -1,276 +0,0 @@
|
||||
//===-- UseAutoMatchers.cpp - Matchers for use-auto transform -------------===//
|
||||
//
|
||||
// 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 contains the implementation for matcher-generating
|
||||
/// functions and custom AST_MATCHERs.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "UseAutoMatchers.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
const char *IteratorDeclId = "iterator_decl";
|
||||
const char *DeclWithNewId = "decl_new";
|
||||
const char *NewExprId = "new_expr";
|
||||
|
||||
namespace clang {
|
||||
namespace ast_matchers {
|
||||
|
||||
/// \brief Matches variable declarations that have explicit initializers that
|
||||
/// are not initializer lists.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// iterator I = Container.begin();
|
||||
/// MyType A(42);
|
||||
/// MyType B{2};
|
||||
/// MyType C;
|
||||
/// \endcode
|
||||
/// varDecl(hasWrittenNonListInitializer()) matches \c I and \c A but not \c B
|
||||
/// or \c C.
|
||||
AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
|
||||
const Expr *Init = Node.getAnyInitializer();
|
||||
if (!Init)
|
||||
return false;
|
||||
|
||||
// The following test is based on DeclPrinter::VisitVarDecl() to find if an
|
||||
// initializer is implicit or not.
|
||||
bool ImplicitInit = false;
|
||||
if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) {
|
||||
if (Construct->isListInitialization())
|
||||
return false;
|
||||
ImplicitInit = Construct->getNumArgs() == 0 ||
|
||||
Construct->getArg(0)->isDefaultArgument();
|
||||
} else
|
||||
if (Node.getInitStyle() == VarDecl::ListInit)
|
||||
return false;
|
||||
|
||||
return !ImplicitInit;
|
||||
}
|
||||
|
||||
/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
|
||||
/// SugarMatcher.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// class C {};
|
||||
/// typedef C my_type
|
||||
/// typedef my_type my_other_type;
|
||||
/// \endcode
|
||||
///
|
||||
/// \c qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
|
||||
/// matches \c my_type and \c my_other_type.
|
||||
AST_MATCHER_P(QualType, isSugarFor, internal::Matcher<QualType>, SugarMatcher) {
|
||||
QualType QT = Node;
|
||||
for (;;) {
|
||||
if (SugarMatcher.matches(QT, Finder, Builder))
|
||||
return true;
|
||||
|
||||
QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
|
||||
if (NewQT == QT)
|
||||
break;
|
||||
QT = NewQT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Matches named declarations that have one of the standard iterator
|
||||
/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// iterator I;
|
||||
/// const_iterator CI;
|
||||
/// \endcode
|
||||
///
|
||||
/// \c namedDecl(hasStdIteratorName()) matches \c I and \c CI.
|
||||
AST_MATCHER(NamedDecl, hasStdIteratorName) {
|
||||
static const char *IteratorNames[] = {
|
||||
"iterator",
|
||||
"reverse_iterator",
|
||||
"const_iterator",
|
||||
"const_reverse_iterator"
|
||||
};
|
||||
|
||||
for (unsigned int i = 0;
|
||||
i < llvm::array_lengthof(IteratorNames);
|
||||
++i) {
|
||||
if (hasName(IteratorNames[i]).matches(Node, Finder, Builder))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Matches named declarations that have one of the standard container
|
||||
/// names.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// class vector {};
|
||||
/// class forward_list {};
|
||||
/// class my_vec {};
|
||||
/// \endcode
|
||||
///
|
||||
/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
|
||||
/// but not \c my_vec.
|
||||
AST_MATCHER_P(NamedDecl, hasStdContainerName, bool, WithStd) {
|
||||
static const char *ContainerNames[] = {
|
||||
"array",
|
||||
"deque",
|
||||
"forward_list",
|
||||
"list",
|
||||
"vector",
|
||||
|
||||
"map",
|
||||
"multimap",
|
||||
"set",
|
||||
"multiset",
|
||||
|
||||
"unordered_map",
|
||||
"unordered_multimap",
|
||||
"unordered_set",
|
||||
"unordered_multiset",
|
||||
|
||||
"queue",
|
||||
"priority_queue",
|
||||
"stack"
|
||||
};
|
||||
|
||||
for (unsigned int i = 0;
|
||||
i < llvm::array_lengthof(ContainerNames);
|
||||
++i) {
|
||||
std::string Name(ContainerNames[i]);
|
||||
if (WithStd)
|
||||
Name = "std::" + Name;
|
||||
if (hasName(Name).matches(Node, Finder, Builder))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ast_matchers
|
||||
} // namespace clang
|
||||
|
||||
namespace {
|
||||
// \brief Returns a TypeMatcher that matches typedefs for standard iterators
|
||||
// inside records with a standard container name.
|
||||
TypeMatcher typedefIterator() {
|
||||
return typedefType(
|
||||
hasDeclaration(
|
||||
allOf(
|
||||
namedDecl(hasStdIteratorName()),
|
||||
hasDeclContext(
|
||||
recordDecl(hasStdContainerName(true))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// \brief Returns a TypeMatcher that matches records named for standard
|
||||
// iterators nested inside records named for standard containers.
|
||||
TypeMatcher nestedIterator() {
|
||||
return recordType(
|
||||
hasDeclaration(
|
||||
allOf(
|
||||
namedDecl(hasStdIteratorName()),
|
||||
hasDeclContext(
|
||||
recordDecl(hasStdContainerName(true))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// \brief Returns a TypeMatcher that matches types declared with using
|
||||
// declarations and which name standard iterators for standard containers.
|
||||
TypeMatcher iteratorFromUsingDeclaration() {
|
||||
// Types resulting from using declarations are
|
||||
// represented by ElaboratedType.
|
||||
return elaboratedType(
|
||||
allOf(
|
||||
// Unwrap the nested name specifier to test for
|
||||
// one of the standard containers.
|
||||
hasQualifier(allOf(
|
||||
specifiesType(
|
||||
templateSpecializationType(
|
||||
hasDeclaration(
|
||||
namedDecl(hasStdContainerName(false))
|
||||
)
|
||||
)
|
||||
),
|
||||
hasPrefix(
|
||||
specifiesNamespace(hasName("std"))
|
||||
)
|
||||
)),
|
||||
// The named type is what comes after the final
|
||||
// '::' in the type. It should name one of the
|
||||
// standard iterator names.
|
||||
namesType(anyOf(
|
||||
typedefType(
|
||||
hasDeclaration(
|
||||
namedDecl(hasStdIteratorName())
|
||||
)
|
||||
),
|
||||
recordType(
|
||||
hasDeclaration(
|
||||
namedDecl(hasStdIteratorName())
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DeclarationMatcher makeIteratorDeclMatcher() {
|
||||
return varDecl(allOf(
|
||||
hasWrittenNonListInitializer(),
|
||||
unless(hasType(autoType())),
|
||||
hasType(
|
||||
isSugarFor(
|
||||
anyOf(
|
||||
typedefIterator(),
|
||||
nestedIterator(),
|
||||
iteratorFromUsingDeclaration()
|
||||
)
|
||||
)
|
||||
)
|
||||
)).bind(IteratorDeclId);
|
||||
}
|
||||
|
||||
DeclarationMatcher makeDeclWithNewMatcher() {
|
||||
return varDecl(
|
||||
hasInitializer(
|
||||
ignoringParenImpCasts(
|
||||
newExpr().bind(NewExprId)
|
||||
)
|
||||
),
|
||||
|
||||
// FIXME: TypeLoc information is not reliable where CV qualifiers are
|
||||
// concerned so these types can't be handled for now.
|
||||
unless(hasType(pointerType(pointee(hasLocalQualifiers())))),
|
||||
|
||||
// FIXME: Handle function pointers. For now we ignore them because
|
||||
// the replacement replaces the entire type specifier source range
|
||||
// which includes the identifier.
|
||||
unless(
|
||||
hasType(
|
||||
pointsTo(
|
||||
pointsTo(
|
||||
parenType(innerType(functionType()))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
).bind(DeclWithNewId);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//===-- UseAutoMatchers.h - Matchers for use-auto transform ----*- 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 contains the declarations for matcher-generating functions
|
||||
/// and names for bound nodes found by AST matchers.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
|
||||
extern const char *IteratorDeclId;
|
||||
extern const char *DeclWithNewId;
|
||||
extern const char *NewExprId;
|
||||
|
||||
/// \brief Create a matcher that matches variable declarations where the type
|
||||
/// is an iterator for an std container and has an explicit initializer of the
|
||||
/// same type.
|
||||
clang::ast_matchers::DeclarationMatcher makeIteratorDeclMatcher();
|
||||
|
||||
/// \brief Create a matcher that matches variable declarations that are
|
||||
/// initialized by a C++ new expression.
|
||||
clang::ast_matchers::DeclarationMatcher makeDeclWithNewMatcher();
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
|
||||
@@ -1,442 +0,0 @@
|
||||
//===-- nullptr-convert/NullptrActions.cpp - Matcher callback -------------===//
|
||||
//
|
||||
// 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 contains the definition of the NullptrFixer class which is
|
||||
/// used as an ASTMatcher callback. Also within this file is a helper AST
|
||||
/// visitor class used to identify sequences of explicit casts.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "NullptrActions.h"
|
||||
#include "NullptrMatchers.h"
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
const char *NullMacroName = "NULL";
|
||||
|
||||
static llvm::cl::opt<std::string> UserNullMacroNames(
|
||||
"user-null-macros", llvm::cl::desc("Comma-separated list of user-defined "
|
||||
"macro names that behave like NULL"),
|
||||
llvm::cl::init(""));
|
||||
|
||||
bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
const SourceManager &SM) {
|
||||
return SM.isFromSameFile(StartLoc, EndLoc) && SM.isFromMainFile(StartLoc);
|
||||
}
|
||||
|
||||
/// \brief Replaces the provided range with the text "nullptr", but only if
|
||||
/// the start and end location are both in main file.
|
||||
/// Returns true if and only if a replacement was made.
|
||||
void ReplaceWithNullptr(tooling::Replacements &Replace, SourceManager &SM,
|
||||
SourceLocation StartLoc, SourceLocation EndLoc) {
|
||||
CharSourceRange Range(SourceRange(StartLoc, EndLoc), true);
|
||||
// Add a space if nullptr follows an alphanumeric character. This happens
|
||||
// whenever there is an c-style explicit cast to nullptr not surrounded by
|
||||
// parentheses and right beside a return statement.
|
||||
SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
|
||||
if (isAlphanumeric(*FullSourceLoc(PreviousLocation, SM).getCharacterData()))
|
||||
Replace.insert(tooling::Replacement(SM, Range, " nullptr"));
|
||||
else
|
||||
Replace.insert(tooling::Replacement(SM, Range, "nullptr"));
|
||||
}
|
||||
|
||||
/// \brief Returns the name of the outermost macro.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// #define MY_NULL NULL
|
||||
/// \endcode
|
||||
/// If \p Loc points to NULL, this function will return the name MY_NULL.
|
||||
llvm::StringRef GetOutermostMacroName(
|
||||
SourceLocation Loc, const SourceManager &SM, const LangOptions &LO) {
|
||||
assert(Loc.isMacroID());
|
||||
SourceLocation OutermostMacroLoc;
|
||||
|
||||
while (Loc.isMacroID()) {
|
||||
OutermostMacroLoc = Loc;
|
||||
Loc = SM.getImmediateMacroCallerLoc(Loc);
|
||||
}
|
||||
|
||||
return clang::Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
|
||||
}
|
||||
|
||||
/// \brief Given the SourceLocation for a macro arg expansion, finds the
|
||||
/// non-macro SourceLocation of the macro the arg was passed to and the
|
||||
/// non-macro SourceLocation of the argument in the arg list to that macro.
|
||||
/// These results are returned via \c MacroLoc and \c ArgLoc respectively.
|
||||
/// These values are undefined if the return value is false.
|
||||
///
|
||||
/// \returns false if one of the returned SourceLocations would be a
|
||||
/// SourceLocation pointing within the definition of another macro.
|
||||
bool getMacroAndArgLocations(SourceLocation Loc, const SourceManager &SM,
|
||||
SourceLocation &ArgLoc, SourceLocation &MacroLoc) {
|
||||
assert(Loc.isMacroID() && "Only reasonble to call this on macros");
|
||||
|
||||
ArgLoc = Loc;
|
||||
|
||||
// Find the location of the immediate macro expansion.
|
||||
while (1) {
|
||||
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
|
||||
const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
|
||||
const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
|
||||
|
||||
ArgLoc = Expansion.getExpansionLocStart();
|
||||
if (!Expansion.isMacroArgExpansion()) {
|
||||
// TODO: Insert test for user-defined null macro here.
|
||||
return MacroLoc.isFileID();
|
||||
}
|
||||
|
||||
MacroLoc = SM.getImmediateExpansionRange(ArgLoc).first;
|
||||
|
||||
ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
|
||||
if (ArgLoc.isFileID())
|
||||
return true;
|
||||
|
||||
// If spelling location resides in the same FileID as macro expansion
|
||||
// location, it means there is no inner macro.
|
||||
FileID MacroFID = SM.getFileID(MacroLoc);
|
||||
if (SM.isInFileID(ArgLoc, MacroFID))
|
||||
// Don't transform this case. If the characters that caused the
|
||||
// null-conversion come from within a macro, they can't be changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm_unreachable("getMacroAndArgLocations");
|
||||
}
|
||||
|
||||
/// \brief Tests if TestMacroLoc is found while recursively unravelling
|
||||
/// expansions starting at TestLoc. TestMacroLoc.isFileID() must be true.
|
||||
/// Implementation is very similar to getMacroAndArgLocations() except in this
|
||||
/// case, it's not assumed that TestLoc is expanded from a macro argument.
|
||||
/// While unravelling expansions macro arguments are handled as with
|
||||
/// getMacroAndArgLocations() but in this function macro body expansions are
|
||||
/// also handled.
|
||||
///
|
||||
/// False means either:
|
||||
/// - TestLoc is not from a macro expansion
|
||||
/// - TestLoc is from a different macro expansion
|
||||
bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc,
|
||||
const SourceManager &SM) {
|
||||
if (TestLoc.isFileID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceLocation Loc = TestLoc, MacroLoc;
|
||||
|
||||
while (1) {
|
||||
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
|
||||
const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
|
||||
const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
|
||||
|
||||
Loc = Expansion.getExpansionLocStart();
|
||||
|
||||
if (!Expansion.isMacroArgExpansion()) {
|
||||
if (Loc.isFileID()) {
|
||||
if (Loc == TestMacroLoc)
|
||||
// Match made.
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
// Since Loc is still a macro ID and it's not an argument expansion, we
|
||||
// don't need to do the work of handling an argument expansion. Simply
|
||||
// keep recursively expanding until we hit a FileID or a macro arg
|
||||
// expansion or a macro arg expansion.
|
||||
continue;
|
||||
}
|
||||
|
||||
MacroLoc = SM.getImmediateExpansionRange(Loc).first;
|
||||
if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc)
|
||||
// Match made.
|
||||
return true;
|
||||
|
||||
Loc = Expansion.getSpellingLoc();
|
||||
Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
|
||||
if (Loc.isFileID())
|
||||
// If we made it this far without finding a match, there is no match to
|
||||
// be made.
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm_unreachable("expandsFrom");
|
||||
}
|
||||
|
||||
/// \brief Given a starting point \c Start in the AST, find an ancestor that
|
||||
/// doesn't expand from the macro called at file location \c MacroLoc.
|
||||
///
|
||||
/// \pre MacroLoc.isFileID()
|
||||
/// \returns true if such an ancestor was found, false otherwise.
|
||||
bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
|
||||
SourceLocation MacroLoc,
|
||||
ast_type_traits::DynTypedNode &Result,
|
||||
ASTContext &Context) {
|
||||
// Below we're only following the first parent back up the AST. This should
|
||||
// be fine since for the statements we care about there should only be one
|
||||
// parent as far up as we care. If this assumption doesn't hold, need to
|
||||
// revisit what to do here.
|
||||
|
||||
assert(MacroLoc.isFileID());
|
||||
|
||||
do {
|
||||
ASTContext::ParentVector Parents = Context.getParents(Start);
|
||||
if (Parents.empty())
|
||||
return false;
|
||||
assert(Parents.size() == 1 &&
|
||||
"Found an ancestor with more than one parent!");
|
||||
|
||||
ASTContext::ParentVector::const_iterator I = Parents.begin();
|
||||
|
||||
SourceLocation Loc;
|
||||
if (const Decl *D = I->get<Decl>())
|
||||
Loc = D->getLocStart();
|
||||
else if (const Stmt *S = I->get<Stmt>())
|
||||
Loc = S->getLocStart();
|
||||
else
|
||||
llvm_unreachable("Expected to find Decl or Stmt containing ancestor");
|
||||
|
||||
if (!expandsFrom(Loc, MacroLoc, Context.getSourceManager())) {
|
||||
Result = *I;
|
||||
return true;
|
||||
}
|
||||
Start = *I;
|
||||
} while(1);
|
||||
|
||||
llvm_unreachable("findContainingAncestor");
|
||||
}
|
||||
|
||||
/// \brief RecursiveASTVisitor for ensuring all nodes rooted at a given AST
|
||||
/// subtree that have file-level source locations corresponding to a macro
|
||||
/// argument have implicit NullTo(Member)Pointer nodes as ancestors.
|
||||
class MacroArgUsageVisitor : public RecursiveASTVisitor<MacroArgUsageVisitor> {
|
||||
public:
|
||||
MacroArgUsageVisitor(SourceLocation CastLoc, const SourceManager &SM)
|
||||
: CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
|
||||
InvalidFound(false) {
|
||||
assert(CastLoc.isFileID());
|
||||
}
|
||||
|
||||
bool TraverseStmt(Stmt *S) {
|
||||
bool VisitedPreviously = Visited;
|
||||
|
||||
if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
|
||||
return false;
|
||||
|
||||
// The point at which VisitedPreviously is false and Visited is true is the
|
||||
// root of a subtree containing nodes whose locations match CastLoc. It's
|
||||
// at this point we test that the Implicit NullTo(Member)Pointer cast was
|
||||
// found or not.
|
||||
if (!VisitedPreviously) {
|
||||
if (Visited && !CastFound) {
|
||||
// Found nodes with matching SourceLocations but didn't come across a
|
||||
// cast. This is an invalid macro arg use. Can stop traversal
|
||||
// completely now.
|
||||
InvalidFound = true;
|
||||
return false;
|
||||
}
|
||||
// Reset state as we unwind back up the tree.
|
||||
CastFound = false;
|
||||
Visited = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitStmt(Stmt *S) {
|
||||
if (SM.getFileLoc(S->getLocStart()) != CastLoc)
|
||||
return true;
|
||||
Visited = true;
|
||||
|
||||
const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
|
||||
if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
|
||||
Cast->getCastKind() == CK_NullToMemberPointer))
|
||||
CastFound = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foundInvalid() const { return InvalidFound; }
|
||||
|
||||
private:
|
||||
SourceLocation CastLoc;
|
||||
const SourceManager &SM;
|
||||
|
||||
bool Visited;
|
||||
bool CastFound;
|
||||
bool InvalidFound;
|
||||
};
|
||||
|
||||
/// \brief Tests that all expansions of a macro arg, one of which expands to
|
||||
/// result in \p CE, yield NullTo(Member)Pointer casts.
|
||||
bool allArgUsesValid(const CastExpr *CE, ASTContext &Context) {
|
||||
SourceLocation CastLoc = CE->getLocStart();
|
||||
const SourceManager &SM = Context.getSourceManager();
|
||||
|
||||
// Step 1: Get location of macro arg and location of the macro the arg was
|
||||
// provided to.
|
||||
SourceLocation ArgLoc, MacroLoc;
|
||||
if (!getMacroAndArgLocations(CastLoc, SM, ArgLoc, MacroLoc))
|
||||
return false;
|
||||
|
||||
// Step 2: Find the first ancestor that doesn't expand from this macro.
|
||||
ast_type_traits::DynTypedNode ContainingAncestor;
|
||||
if (!findContainingAncestor(ast_type_traits::DynTypedNode::create<Stmt>(*CE),
|
||||
MacroLoc, ContainingAncestor, Context))
|
||||
return false;
|
||||
|
||||
// Step 3:
|
||||
// Visit children of this containing parent looking for the least-descended
|
||||
// nodes of the containing parent which are macro arg expansions that expand
|
||||
// from the given arg location.
|
||||
// Visitor needs: arg loc
|
||||
MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
|
||||
if (const Decl *D = ContainingAncestor.get<Decl>())
|
||||
ArgUsageVisitor.TraverseDecl(const_cast<Decl*>(D));
|
||||
else if (const Stmt *S = ContainingAncestor.get<Stmt>())
|
||||
ArgUsageVisitor.TraverseStmt(const_cast<Stmt*>(S));
|
||||
else
|
||||
llvm_unreachable("Unhandled ContainingAncestor node type");
|
||||
|
||||
if (ArgUsageVisitor.foundInvalid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Looks for implicit casts as well as sequences of 0 or more explicit
|
||||
/// casts with an implicit null-to-pointer cast within.
|
||||
///
|
||||
/// The matcher this visitor is used with will find a single implicit cast or a
|
||||
/// top-most explicit cast (i.e. it has no explicit casts as an ancestor) where
|
||||
/// an implicit cast is nested within. However, there is no guarantee that only
|
||||
/// explicit casts exist between the found top-most explicit cast and the
|
||||
/// possibly more than one nested implicit cast. This visitor finds all cast
|
||||
/// sequences with an implicit cast to null within and creates a replacement
|
||||
/// leaving the outermost explicit cast unchanged to avoid introducing
|
||||
/// ambiguities.
|
||||
class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
|
||||
public:
|
||||
CastSequenceVisitor(tooling::Replacements &R, ASTContext &Context,
|
||||
const UserMacroNames &UserNullMacros,
|
||||
unsigned &AcceptedChanges)
|
||||
: Replace(R), SM(Context.getSourceManager()), Context(Context),
|
||||
UserNullMacros(UserNullMacros), AcceptedChanges(AcceptedChanges),
|
||||
FirstSubExpr(0), PruneSubtree(false) {}
|
||||
|
||||
bool TraverseStmt(Stmt *S) {
|
||||
// Stop traversing down the tree if requested.
|
||||
if (PruneSubtree) {
|
||||
PruneSubtree = false;
|
||||
return true;
|
||||
}
|
||||
return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
|
||||
}
|
||||
|
||||
// Only VisitStmt is overridden as we shouldn't find other base AST types
|
||||
// within a cast expression.
|
||||
bool VisitStmt(Stmt *S) {
|
||||
CastExpr *C = dyn_cast<CastExpr>(S);
|
||||
if (!C) {
|
||||
FirstSubExpr = 0;
|
||||
return true;
|
||||
} else if (!FirstSubExpr) {
|
||||
FirstSubExpr = C->getSubExpr()->IgnoreParens();
|
||||
}
|
||||
|
||||
if (C->getCastKind() == CK_NullToPointer ||
|
||||
C->getCastKind() == CK_NullToMemberPointer) {
|
||||
|
||||
SourceLocation StartLoc = FirstSubExpr->getLocStart();
|
||||
SourceLocation EndLoc = FirstSubExpr->getLocEnd();
|
||||
|
||||
// If the location comes from a macro arg expansion, *all* uses of that
|
||||
// arg must be checked to result in NullTo(Member)Pointer casts.
|
||||
//
|
||||
// If the location comes from a macro body expansion, check to see if its
|
||||
// coming from one of the allowed 'NULL' macros.
|
||||
if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
|
||||
SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
|
||||
FileLocEnd = SM.getFileLoc(EndLoc);
|
||||
if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
|
||||
allArgUsesValid(C, Context)) {
|
||||
ReplaceWithNullptr(Replace, SM, FileLocStart, FileLocEnd);
|
||||
++AcceptedChanges;
|
||||
}
|
||||
return skipSubTree();
|
||||
}
|
||||
|
||||
if (SM.isMacroBodyExpansion(StartLoc) &&
|
||||
SM.isMacroBodyExpansion(EndLoc)) {
|
||||
llvm::StringRef OutermostMacroName =
|
||||
GetOutermostMacroName(StartLoc, SM, Context.getLangOpts());
|
||||
|
||||
// Check to see if the user wants to replace the macro being expanded.
|
||||
if (std::find(UserNullMacros.begin(), UserNullMacros.end(),
|
||||
OutermostMacroName) == UserNullMacros.end()) {
|
||||
return skipSubTree();
|
||||
}
|
||||
|
||||
StartLoc = SM.getFileLoc(StartLoc);
|
||||
EndLoc = SM.getFileLoc(EndLoc);
|
||||
}
|
||||
|
||||
if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
|
||||
return skipSubTree();
|
||||
}
|
||||
ReplaceWithNullptr(Replace, SM, StartLoc, EndLoc);
|
||||
++AcceptedChanges;
|
||||
|
||||
return skipSubTree();
|
||||
} // If NullTo(Member)Pointer cast.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool skipSubTree() { PruneSubtree = true; return true; }
|
||||
|
||||
private:
|
||||
tooling::Replacements &Replace;
|
||||
SourceManager &SM;
|
||||
ASTContext &Context;
|
||||
const UserMacroNames &UserNullMacros;
|
||||
unsigned &AcceptedChanges;
|
||||
Expr *FirstSubExpr;
|
||||
bool PruneSubtree;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
NullptrFixer::NullptrFixer(clang::tooling::Replacements &Replace,
|
||||
unsigned &AcceptedChanges, RiskLevel)
|
||||
: Replace(Replace), AcceptedChanges(AcceptedChanges) {
|
||||
if (!UserNullMacroNames.empty()) {
|
||||
llvm::StringRef S = UserNullMacroNames;
|
||||
S.split(UserNullMacros, ",");
|
||||
}
|
||||
UserNullMacros.insert(UserNullMacros.begin(), llvm::StringRef(NullMacroName));
|
||||
}
|
||||
|
||||
void NullptrFixer::run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
const CastExpr *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
|
||||
assert(NullCast && "Bad Callback. No node provided");
|
||||
// Given an implicit null-ptr cast or an explicit cast with an implicit
|
||||
// null-to-pointer cast within use CastSequenceVisitor to identify sequences
|
||||
// of explicit casts that can be converted into 'nullptr'.
|
||||
CastSequenceVisitor Visitor(Replace, *Result.Context, UserNullMacros,
|
||||
AcceptedChanges);
|
||||
Visitor.TraverseStmt(const_cast<CastExpr *>(NullCast));
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//===-- nullptr-convert/NullptrActions.h - Matcher callback ------*- 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 contains the declaration of the NullptrFixer class which
|
||||
/// is used as a ASTMatcher callback.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_NULLPTR_ACTIONS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_NULLPTR_ACTIONS_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
|
||||
// The type for user-defined macro names that behave like NULL
|
||||
typedef llvm::SmallVector<llvm::StringRef, 1> UserMacroNames;
|
||||
|
||||
/// \brief The callback to be used for nullptr migration matchers.
|
||||
///
|
||||
class NullptrFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
NullptrFixer(clang::tooling::Replacements &Replace,
|
||||
unsigned &AcceptedChanges,
|
||||
RiskLevel);
|
||||
|
||||
/// \brief Entry point to the callback called when matches are made.
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
|
||||
|
||||
private:
|
||||
clang::tooling::Replacements &Replace;
|
||||
unsigned &AcceptedChanges;
|
||||
UserMacroNames UserNullMacros;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_NULLPTR_ACTIONS_H
|
||||
@@ -1,69 +0,0 @@
|
||||
//===-- nullptr-convert/Matchers.cpp - Matchers for null casts ------------===//
|
||||
//
|
||||
// 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 contains the definitions for matcher-generating functions
|
||||
/// and a custom AST_MATCHER for identifying casts of type CK_NullTo*.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "NullptrMatchers.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
const char *CastSequence = "sequence";
|
||||
|
||||
namespace clang {
|
||||
namespace ast_matchers {
|
||||
/// \brief Matches cast expressions that have a cast kind of CK_NullToPointer
|
||||
/// or CK_NullToMemberPointer.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// int *p = 0;
|
||||
/// \endcode
|
||||
/// implicitCastExpr(isNullToPointer()) matches the implicit cast clang adds
|
||||
/// around \c 0.
|
||||
AST_MATCHER(CastExpr, isNullToPointer) {
|
||||
return Node.getCastKind() == CK_NullToPointer ||
|
||||
Node.getCastKind() == CK_NullToMemberPointer;
|
||||
}
|
||||
|
||||
AST_MATCHER(Type, sugaredNullptrType) {
|
||||
const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
|
||||
if (const BuiltinType *BT = dyn_cast<BuiltinType>(DesugaredType))
|
||||
return BT->getKind() == BuiltinType::NullPtr;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace ast_matchers
|
||||
} // end namespace clang
|
||||
|
||||
StatementMatcher makeCastSequenceMatcher() {
|
||||
StatementMatcher ImplicitCastToNull =
|
||||
implicitCastExpr(
|
||||
isNullToPointer(),
|
||||
unless(
|
||||
hasSourceExpression(
|
||||
hasType(sugaredNullptrType())
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return castExpr(
|
||||
anyOf(
|
||||
ImplicitCastToNull,
|
||||
explicitCastExpr(
|
||||
hasDescendant(ImplicitCastToNull)
|
||||
)
|
||||
),
|
||||
unless(hasAncestor(explicitCastExpr()))
|
||||
).bind(CastSequence);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//===-- nullptr-convert/Matchers.h - Matchers for null casts ---*- 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 contains the declarations for matcher-generating functions
|
||||
/// and names for bound nodes found by AST matchers.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_MATCHERS_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_MATCHERS_H
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
|
||||
// Names to bind with matched expressions.
|
||||
extern const char *CastSequence;
|
||||
|
||||
/// \brief Create a matcher that finds implicit casts as well as the head of a
|
||||
/// sequence of zero or more nested explicit casts that have an implicit cast
|
||||
/// to null within.
|
||||
/// Finding sequences of explict casts is necessary so that an entire sequence
|
||||
/// can be replaced instead of just the inner-most implicit cast.
|
||||
clang::ast_matchers::StatementMatcher makeCastSequenceMatcher();
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_MATCHERS_H
|
||||
@@ -1,65 +0,0 @@
|
||||
//===-- LoopConvert/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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file provides the implementation of the UseNullptrTransform
|
||||
/// class.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UseNullptr.h"
|
||||
#include "NullptrActions.h"
|
||||
#include "NullptrMatchers.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
using clang::ast_matchers::MatchFinder;
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
|
||||
int UseNullptrTransform::apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRisk,
|
||||
const CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) {
|
||||
RefactoringTool UseNullptrTool(Database, SourcePaths);
|
||||
|
||||
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||
E = InputStates.end();
|
||||
I != E; ++I) {
|
||||
UseNullptrTool.mapVirtualFile(I->first, I->second);
|
||||
}
|
||||
|
||||
unsigned AcceptedChanges = 0;
|
||||
|
||||
MatchFinder Finder;
|
||||
NullptrFixer Fixer(UseNullptrTool.getReplacements(),
|
||||
AcceptedChanges,
|
||||
MaxRisk);
|
||||
|
||||
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
|
||||
|
||||
if (int result = UseNullptrTool.run(newFrontendActionFactory(&Finder))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
RewriterContainer Rewrite(UseNullptrTool.getFiles(), InputStates);
|
||||
|
||||
// FIXME: Do something if some replacements didn't get applied?
|
||||
UseNullptrTool.applyAllReplacements(Rewrite.getRewriter());
|
||||
|
||||
collectResults(Rewrite.getRewriter(), InputStates, ResultStates);
|
||||
|
||||
setAcceptedChanges(AcceptedChanges);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//===-- LoopConvert/LoopConvert.h - 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file provides the definition of the UseNullptrTransform
|
||||
/// class which is the main interface to the use-nullptr transform
|
||||
/// that tries to make use of nullptr where possible.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_H
|
||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_H
|
||||
|
||||
#include "Core/Transform.h"
|
||||
#include "llvm/Support/Compiler.h" // For LLVM_OVERRIDE
|
||||
|
||||
/// \brief Subclass of Transform that transforms null pointer constants into
|
||||
/// C++11's nullptr keyword where possible.
|
||||
class UseNullptrTransform : public Transform {
|
||||
public:
|
||||
UseNullptrTransform() : Transform("UseNullptr") {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
RiskLevel MaxRiskLEvel,
|
||||
const clang::tooling::CompilationDatabase &Database,
|
||||
const std::vector<std::string> &SourcePaths,
|
||||
FileContentsByPath &ResultStates) LLVM_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_NULLPTR_H
|
||||
@@ -1,32 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
set (Cpp11MigrateSources
|
||||
Cpp11Migrate.cpp
|
||||
)
|
||||
|
||||
# FIXME: Lib-ify the transforms to simplify the build rules.
|
||||
|
||||
# For each transform subdirectory.
|
||||
file(GLOB_RECURSE LoopConvertSources "../LoopConvert/*.cpp")
|
||||
list(APPEND Cpp11MigrateSources ${LoopConvertSources})
|
||||
|
||||
file(GLOB_RECURSE UseNullptrSources "../UseNullptr/*.cpp")
|
||||
list(APPEND Cpp11MigrateSources ${UseNullptrSources})
|
||||
|
||||
file(GLOB_RECURSE UseAutoSources "../UseAuto/*.cpp")
|
||||
list(APPEND Cpp11MigrateSources ${UseAutoSources})
|
||||
|
||||
file(GLOB_RECURSE AddOverrideSources "../AddOverride/*.cpp")
|
||||
list(APPEND Cpp11MigrateSources ${AddOverrideSources})
|
||||
|
||||
add_clang_executable(cpp11-migrate
|
||||
${Cpp11MigrateSources}
|
||||
)
|
||||
|
||||
add_dependencies(cpp11-migrate
|
||||
clang-headers
|
||||
)
|
||||
|
||||
target_link_libraries(cpp11-migrate
|
||||
migrateCore
|
||||
)
|
||||
@@ -1,171 +0,0 @@
|
||||
//===-- cpp11-migrate/Cpp11Migrate.cpp - Main file C++11 migration 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 the C++11 feature migration tool main function
|
||||
/// and transformation framework.
|
||||
///
|
||||
/// See user documentation for usage instructions.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Core/Transforms.h"
|
||||
#include "Core/Transform.h"
|
||||
#include "LoopConvert/LoopConvert.h"
|
||||
#include "UseNullptr/UseNullptr.h"
|
||||
#include "UseAuto/UseAuto.h"
|
||||
#include "AddOverride/AddOverride.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
|
||||
namespace cl = llvm::cl;
|
||||
using namespace clang::tooling;
|
||||
|
||||
static cl::opt<RiskLevel> MaxRiskLevel(
|
||||
"risk", cl::desc("Select a maximum risk level:"),
|
||||
cl::values(clEnumValN(RL_Safe, "safe", "Only safe transformations"),
|
||||
clEnumValN(RL_Reasonable, "reasonable",
|
||||
"Enable transformations that might change "
|
||||
"semantics (default)"),
|
||||
clEnumValN(RL_Risky, "risky",
|
||||
"Enable transformations that are likely to "
|
||||
"change semantics"),
|
||||
clEnumValEnd),
|
||||
cl::init(RL_Reasonable));
|
||||
|
||||
static cl::opt<bool> FinalSyntaxCheck(
|
||||
"final-syntax-check",
|
||||
cl::desc("Check for correct syntax after applying transformations"),
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool>
|
||||
SummaryMode("summary", cl::desc("Print transform summary"),
|
||||
cl::init(false));
|
||||
|
||||
// TODO: Remove cl::Hidden when functionality for acknowledging include/exclude
|
||||
// options are implemented in the tool.
|
||||
static cl::opt<std::string>
|
||||
IncludePaths("include", cl::Hidden,
|
||||
cl::desc("Comma seperated list of paths to consider to be "
|
||||
"transformed"));
|
||||
static cl::opt<std::string>
|
||||
ExcludePaths("exclude", cl::Hidden,
|
||||
cl::desc("Comma seperated list of paths that can not "
|
||||
"be transformed"));
|
||||
static cl::opt<std::string>
|
||||
IncludeFromFile("include-from", cl::Hidden, cl::value_desc("filename"),
|
||||
cl::desc("File containing a list of paths to consider to "
|
||||
"be transformed"));
|
||||
static cl::opt<std::string>
|
||||
ExcludeFromFile("exclude-from", cl::Hidden, cl::value_desc("filename"),
|
||||
cl::desc("File containing a list of paths that can not be "
|
||||
"transforms"));
|
||||
|
||||
class EndSyntaxArgumentsAdjuster : public ArgumentsAdjuster {
|
||||
CommandLineArguments Adjust(const CommandLineArguments &Args) {
|
||||
CommandLineArguments AdjustedArgs = Args;
|
||||
AdjustedArgs.push_back("-fsyntax-only");
|
||||
AdjustedArgs.push_back("-std=c++11");
|
||||
return AdjustedArgs;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
llvm::sys::PrintStackTraceOnErrorSignal();
|
||||
Transforms TransformManager;
|
||||
|
||||
TransformManager.registerTransform(
|
||||
"loop-convert", "Make use of range-based for loops where possible",
|
||||
&ConstructTransform<LoopConvertTransform>);
|
||||
TransformManager.registerTransform(
|
||||
"use-nullptr", "Make use of nullptr keyword where possible",
|
||||
&ConstructTransform<UseNullptrTransform>);
|
||||
TransformManager.registerTransform(
|
||||
"use-auto", "Use of 'auto' type specifier",
|
||||
&ConstructTransform<UseAutoTransform>);
|
||||
TransformManager.registerTransform(
|
||||
"add-override", "Make use of override specifier where possible",
|
||||
&ConstructTransform<AddOverrideTransform>);
|
||||
// Add more transform options here.
|
||||
|
||||
// This causes options to be parsed.
|
||||
CommonOptionsParser OptionsParser(argc, argv);
|
||||
|
||||
TransformManager.createSelectedTransforms();
|
||||
|
||||
if (TransformManager.begin() == TransformManager.end()) {
|
||||
llvm::errs() << "No selected transforms\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileContentsByPath FileStates1, FileStates2,
|
||||
*InputFileStates = &FileStates1, *OutputFileStates = &FileStates2;
|
||||
|
||||
// Apply transforms.
|
||||
for (Transforms::const_iterator I = TransformManager.begin(),
|
||||
E = TransformManager.end();
|
||||
I != E; ++I) {
|
||||
if ((*I)->apply(*InputFileStates, MaxRiskLevel,
|
||||
OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList(), *OutputFileStates) !=
|
||||
0) {
|
||||
// FIXME: Improve ClangTool to not abort if just one file fails.
|
||||
return 1;
|
||||
}
|
||||
if (SummaryMode) {
|
||||
llvm::outs() << "Transform: " << (*I)->getName()
|
||||
<< " - Accepted: "
|
||||
<< (*I)->getAcceptedChanges();
|
||||
if ((*I)->getChangesNotMade()) {
|
||||
llvm::outs() << " - Rejected: "
|
||||
<< (*I)->getRejectedChanges()
|
||||
<< " - Deferred: "
|
||||
<< (*I)->getDeferredChanges();
|
||||
}
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
std::swap(InputFileStates, OutputFileStates);
|
||||
OutputFileStates->clear();
|
||||
}
|
||||
|
||||
// Final state of files is pointed at by InputFileStates.
|
||||
|
||||
if (FinalSyntaxCheck) {
|
||||
ClangTool EndSyntaxTool(OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList());
|
||||
|
||||
// Add c++11 support to clang.
|
||||
EndSyntaxTool.setArgumentsAdjuster(new EndSyntaxArgumentsAdjuster);
|
||||
|
||||
for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
|
||||
E = InputFileStates->end();
|
||||
I != E; ++I) {
|
||||
EndSyntaxTool.mapVirtualFile(I->first, I->second);
|
||||
}
|
||||
|
||||
if (EndSyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>())
|
||||
!= 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write results to file.
|
||||
for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
|
||||
E = InputFileStates->end();
|
||||
I != E; ++I) {
|
||||
std::string ErrorInfo;
|
||||
llvm::raw_fd_ostream FileStream(I->first.c_str(), ErrorInfo,
|
||||
llvm::raw_fd_ostream::F_Binary);
|
||||
FileStream << I->second;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../../..
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
|
||||
TOOLNAME = cpp11-migrate
|
||||
|
||||
# No plugins, optimize startup time.
|
||||
TOOL_NO_EXPORTS = 1
|
||||
|
||||
SOURCES = Cpp11Migrate.cpp
|
||||
|
||||
# FIXME: All these gross relative paths will go away once transforms are lib-ified.
|
||||
|
||||
# For each Transform subdirectory add to SOURCES and BUILT_SOURCES.
|
||||
# BUILT_SOURCES ensures a subdirectory is created to house object files from
|
||||
# transform subdirectories. See below for more on .objdir.
|
||||
SOURCES += $(addprefix ../LoopConvert/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../LoopConvert/*.cpp)))
|
||||
BUILT_SOURCES = $(ObjDir)/../LoopConvert/.objdir
|
||||
SOURCES += $(addprefix ../UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../UseNullptr/*.cpp)))
|
||||
BUILT_SOURCES += $(ObjDir)/../UseNullptr/.objdir
|
||||
SOURCES += $(addprefix ../UseAuto/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../UseAuto/*.cpp)))
|
||||
BUILT_SOURCES += $(ObjDir)/../UseAuto/.objdir
|
||||
SOURCES += $(addprefix ../AddOverride/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../AddOverride/*.cpp)))
|
||||
BUILT_SOURCES += $(ObjDir)/../AddOverride/.objdir
|
||||
|
||||
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc
|
||||
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
|
||||
clangRewriteFrontend.a clangRewriteCore.a clangParse.a \
|
||||
clangSema.a clangAnalysis.a \
|
||||
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a \
|
||||
migrateCore.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
CPP.Flags += -I$(PROJ_SRC_DIR)/..
|
||||
|
||||
# BUILT_SOURCES gets used as a prereq for many top-level targets. However, at
|
||||
# the point those targets are defined, $(ObjDir) hasn't been defined and so the
|
||||
# directory to create becomes /<name>/ which is not what we want. So instead,
|
||||
# this .objdir recipe is defined at at point where $(ObjDir) is defined and
|
||||
# it's specialized to $(ObjDir) to ensure it only works on targets we want it
|
||||
# to.
|
||||
$(ObjDir)/%.objdir:
|
||||
$(Verb) $(MKDIR) $(ObjDir)/$* > /dev/null
|
||||
$(Verb) $(DOTDIR_TIMESTAMP_COMMAND) > $@
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
.. index:: Add-Override Transform
|
||||
|
||||
======================
|
||||
Add-Override Transform
|
||||
======================
|
||||
|
||||
The Add-Override Transform adds the ``override`` specifier to member
|
||||
functions that override a virtual function in a base class and that
|
||||
don't already have the specifier. The transform is enabled with the
|
||||
:option:`-add-override` option of :program:`cpp11-migrate`.
|
||||
For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual void h() const;
|
||||
};
|
||||
|
||||
class B : public A {
|
||||
public:
|
||||
void h() const;
|
||||
|
||||
// The declaration of h is transformed to
|
||||
void h() const override;
|
||||
};
|
||||
|
||||
|
||||
Known Limitations
|
||||
-----------------
|
||||
* This transform will fail if a method declaration has an inlined method
|
||||
body and there is a comment between the method declaration and the body.
|
||||
In this case, the override keyword will incorrectly be inserted at the
|
||||
end of the comment.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class B : public A {
|
||||
public:
|
||||
virtual void h() const // comment
|
||||
{ }
|
||||
|
||||
// The declaration of h is transformed to
|
||||
virtual void h() const // comment override
|
||||
{ }
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,257 +0,0 @@
|
||||
.. index:: Loop Convert Transform
|
||||
|
||||
======================
|
||||
Loop Convert Transform
|
||||
======================
|
||||
|
||||
The Loop Convert Transform is a transformation to convert ``for(...; ...;
|
||||
...)`` loops to use the new range-based loops in C++11. The transform is enabled
|
||||
with the :option:`-loop-convert` option of :program:`cpp11-migrate`.
|
||||
|
||||
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()``
|
||||
|
||||
Risk
|
||||
====
|
||||
|
||||
Risky
|
||||
^^^^^
|
||||
|
||||
In loops where the container expression is more complex than just a
|
||||
reference to a declared expression (a variable, function, enum, etc.),
|
||||
and some part of it appears elsewhere in the loop, we lower our confidence
|
||||
in the transformation due to the increased risk of changing semantics.
|
||||
Transformations for these loops are marked as `risky`, and thus will only
|
||||
be converted if the acceptable risk level is set to ``-risk=risky``.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int arr[10][20];
|
||||
int l = 5;
|
||||
|
||||
for (int j = 0; j < 20; ++j)
|
||||
int k = arr[l][j] + l; // using l outside arr[l] is considered risky
|
||||
|
||||
for (int i = 0; i < obj.getVector().size(); ++i)
|
||||
obj.foo(10); // using 'obj' is considered risky
|
||||
|
||||
See
|
||||
:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
|
||||
for an example of an incorrect transformation when the maximum acceptable risk
|
||||
level is set to `risky`.
|
||||
|
||||
Reasonable (Default)
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If a loop calls ``.end()`` or ``.size()`` after each iteration, the
|
||||
transformation for that loop is marked as `reasonable`, and thus will
|
||||
be converted if the acceptable risk level is set to ``-risk=reasonable``
|
||||
(default) or higher.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// using size() is considered reasonable
|
||||
for (int i = 0; i < container.size(); ++i)
|
||||
cout << container[i];
|
||||
|
||||
Safe
|
||||
^^^^
|
||||
|
||||
Any other loops that do not match the above criteria to be marked as
|
||||
`risky` or `reasonable` are marked `safe`, and thus will be converted
|
||||
if the acceptable risk level is set to ``-risk=safe`` or higher.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int arr[] = {1,2,3};
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
cout << arr[i];
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Original:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
const int N = 5;
|
||||
int arr[] = {1,2,3,4,5};
|
||||
vector<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
// safe transform
|
||||
for (int i = 0; i < N; ++i)
|
||||
cout << arr[i];
|
||||
|
||||
// reasonable transform
|
||||
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
|
||||
cout << *it;*
|
||||
|
||||
// reasonable transform
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
cout << v[i];
|
||||
|
||||
After transformation with risk level set to ``-risk=reasonable`` (default):
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
const int N = 5;
|
||||
int arr[] = {1,2,3,4,5};
|
||||
vector<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
// safe transform
|
||||
for (auto & elem : arr)
|
||||
cout << elem;
|
||||
|
||||
// reasonable transform
|
||||
for (auto & elem : v)
|
||||
cout << elem;
|
||||
|
||||
// reasonable transform
|
||||
for (auto & elem : v)
|
||||
cout << elem;
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
There are certain situations where the tool may erroneously perform
|
||||
transformations that remove information and change semantics. Users of the tool
|
||||
should be aware of the behaviour and limitations of the transform outlined by
|
||||
the cases below.
|
||||
|
||||
Comments inside loop headers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Comments inside the original loop header are ignored and deleted when
|
||||
transformed.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (int i = 0; i < N; /* This will be deleted */ ++i) { }
|
||||
|
||||
Range-based loops evaluate end() only once
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The C++11 range-based for loop calls ``.end()`` only once during the
|
||||
initialization of the loop. If in the original loop ``.end()`` is called after
|
||||
each iteration the semantics of the transformed loop may differ.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// The following is semantically equivalent to the C++11 range-based for loop,
|
||||
// therefore the semantics of the header will not change.
|
||||
for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
|
||||
|
||||
// Instead of calling .end() after each iteration, this loop will be
|
||||
// transformed to call .end() only once during the initialization of the loop,
|
||||
// which may affect semantics.
|
||||
for (iterator it = container.begin(); it != container.end(); ++it) { }
|
||||
|
||||
.. _IncorrectRiskyTransformation:
|
||||
|
||||
As explained above, calling member functions of the container in the body
|
||||
of the loop is considered `risky`. If the called member function modifies the
|
||||
container the semantics of the converted loop will differ due to ``.end()``
|
||||
being called only once.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool flag = false;
|
||||
for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
|
||||
// Add a copy of the first element to the end of the vector.
|
||||
if (!flag) {
|
||||
// This line makes this transformation 'risky'.
|
||||
vec.push_back(*it);
|
||||
flag = true;
|
||||
}
|
||||
cout << *it;
|
||||
}
|
||||
|
||||
The original code above prints out the contents of the container including the
|
||||
newly added element while the converted loop, shown below, will only print the
|
||||
original contents and not the newly added element.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool flag = false;
|
||||
for (auto & elem : vec) {
|
||||
// Add a copy of the first element to the end of the vector.
|
||||
if (!flag) {
|
||||
// This line makes this transformation 'risky'
|
||||
vec.push_back(elem);
|
||||
flag = true;
|
||||
}
|
||||
cout << elem;
|
||||
}
|
||||
|
||||
Semantics will also be affected if ``.end()`` has side effects. For example, in
|
||||
the case where calls to ``.end()`` are logged the semantics will change in the
|
||||
transformed loop if ``.end()`` was originally called after each iteration.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
iterator end() {
|
||||
num_of_end_calls++;
|
||||
return container.end();
|
||||
}
|
||||
|
||||
Overloaded operator->() with side effects
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Similarly, if ``operator->()`` was overloaded to have side effects, such as
|
||||
logging, the semantics will change. If the iterator's ``operator->()`` was used
|
||||
in the original loop it will be replaced with ``<container element>.<member>``
|
||||
instead due to the implicit dereference as part of the range-based for loop.
|
||||
Therefore any side effect of the overloaded ``operator->()`` will no longer be
|
||||
performed.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (iterator it = c.begin(); it != c.end(); ++it) {
|
||||
it->func(); // Using operator->()
|
||||
}
|
||||
// Will be transformed to:
|
||||
for (auto & elem : c) {
|
||||
elem.func(); // No longer using operator->()
|
||||
}
|
||||
|
||||
Pointers and references to containers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
While most of the transform's risk analysis is dedicated to determining whether
|
||||
the iterator or container was modified within the loop, it is possible to
|
||||
circumvent the analysis by accessing and modifying the container through a
|
||||
pointer or reference.
|
||||
|
||||
If the container were directly used instead of using the pointer or reference
|
||||
the following transformation would have only been applied at the ``-risk=risky``
|
||||
level since calling a member function of the container is considered `risky`.
|
||||
The transform cannot identify expressions associated with the container that are
|
||||
different than the one used in the loop header, therefore the transformation
|
||||
below ends up being performed at the ``-risk=safe`` level.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
vector<int> vec;
|
||||
|
||||
vector<int> *ptr = &vec;
|
||||
vector<int> &ref = vec;
|
||||
|
||||
for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
|
||||
if (!flag) {
|
||||
// Accessing and modifying the container is considered risky, but the risk
|
||||
// level is not raised here.
|
||||
ptr->push_back(*it);
|
||||
ref.push_back(*it);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
all: doxygen html
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ExtraClangTools.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ExtraClangTools.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/ExtraClangTools"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ExtraClangTools"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
doxygen:
|
||||
mkdir -p $(BUILDDIR)/html/doxygen
|
||||
doxygen
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
------------------------------------------------------------
|
||||
Documetation for the tools of clang-tools-extra repo project
|
||||
------------------------------------------------------------
|
||||
|
||||
Sphinx and doxygen documentation is generated by executing make.
|
||||
|
||||
Sphinx html files can be generated separately using make html.
|
||||
|
||||
Doxygen html files can also be generated using make doxygen.
|
||||
|
||||
The generated documentation will be placed in _build/html.
|
||||
@@ -1,137 +0,0 @@
|
||||
.. index:: Use-Auto Transform
|
||||
|
||||
==================
|
||||
Use-Auto Transform
|
||||
==================
|
||||
|
||||
The Use-Auto Transform is responsible for using the ``auto`` type specifier for
|
||||
variable declarations to *improve code readability and maintainability*. The
|
||||
transform is enabled with the :option:`-use-auto` option of
|
||||
:program:`cpp11-migrate`. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
std::vector<int>::iterator I = my_container.begin();
|
||||
|
||||
// transforms to:
|
||||
|
||||
auto I = my_container.begin();
|
||||
|
||||
The ``auto`` type specifier will only be introduced in situations where the
|
||||
variable type matches the type of the initializer expression. In other words
|
||||
``auto`` should deduce the same type that was originally spelled in the source.
|
||||
However, not every situation should be transformed:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int val = 42;
|
||||
InfoStruct &I = SomeObject.getInfo();
|
||||
|
||||
// Should not become:
|
||||
|
||||
auto val = 42;
|
||||
auto &I = SomeObject.getInfo();
|
||||
|
||||
In this example using ``auto`` for builtins doesn't improve readability. In
|
||||
other situations it makes the code less self-documenting impairing readability
|
||||
and maintainability. As a result, ``auto`` is used only introduced in specific
|
||||
situations described below.
|
||||
|
||||
Iterators
|
||||
=========
|
||||
|
||||
Iterator type specifiers tend to be long and used frequently, especially in
|
||||
loop constructs. Since the functions generating iterators have a common format,
|
||||
the type specifier can be replaced without obscuring the meaning of code while
|
||||
improving readability and maintainability.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (std::vector<int>::iterator I = my_container.begin(),
|
||||
E = my_container.end();
|
||||
I != E; ++I) {
|
||||
}
|
||||
|
||||
// becomes
|
||||
|
||||
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
|
||||
}
|
||||
|
||||
The transform will only replace iterator type-specifiers when all of the
|
||||
following conditions are satisfied:
|
||||
* The iterator is for one of the standard container in ``std`` namespace:
|
||||
|
||||
* ``array``
|
||||
|
||||
* ``deque``
|
||||
|
||||
* ``forward_list``
|
||||
|
||||
* ``list``
|
||||
|
||||
* ``vector``
|
||||
|
||||
* ``map``
|
||||
|
||||
* ``multimap``
|
||||
|
||||
* ``set``
|
||||
|
||||
* ``multiset``
|
||||
|
||||
* ``unordered_map``
|
||||
|
||||
* ``unordered_multimap``
|
||||
|
||||
* ``unordered_set``
|
||||
|
||||
* ``unordered_multiset``
|
||||
|
||||
* ``queue``
|
||||
|
||||
* ``priority_queue``
|
||||
|
||||
* ``stack``
|
||||
|
||||
* The iterator is one of the possible iterator types for standard containers:
|
||||
|
||||
* ``iterator``
|
||||
|
||||
* ``reverse_iterator``
|
||||
|
||||
* ``const_iterator``
|
||||
|
||||
* ``const_reverse_iterator``
|
||||
|
||||
* In addition to using iterator types directly, typedefs or other ways of
|
||||
referring to those types are also allowed. However, implementation-specific
|
||||
types for which a type like ``std::vector<int>::iterator`` is itself a
|
||||
typedef will not be transformed. Consider the following examples:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// The following direct uses of iterator types will be transformed.
|
||||
std::vector<int>::iterator I = MyVec.begin();
|
||||
{
|
||||
using namespace std;
|
||||
list<int>::iterator I = MyList.begin();
|
||||
}
|
||||
|
||||
// The type specifier for J would transform to auto since it's a typedef
|
||||
// to a standard iterator type.
|
||||
typedef std::map<int, std::string>::const_iterator map_iterator;
|
||||
map_iterator J = MyMap.begin();
|
||||
|
||||
// The following implementation-specific iterator type for which
|
||||
// std::vector<int>::iterator could be a typedef would not be transformed.
|
||||
__gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
|
||||
|
||||
* The initializer for the variable being declared is not a braced initializer
|
||||
list. Otherwise, use of ``auto`` would cause the type of the variable to be
|
||||
deduced as``std::initializer_list``.
|
||||
|
||||
Known Limitations
|
||||
-----------------
|
||||
* If the initializer is an explicit conversion constructor, the transform will
|
||||
not replace the type specifier even though it would be safe to do so.
|
||||
* User-defined iterators are not handled at this time.
|
||||
@@ -1,82 +0,0 @@
|
||||
.. index:: Use-Nullptr Transform
|
||||
|
||||
=====================
|
||||
Use-Nullptr Transform
|
||||
=====================
|
||||
|
||||
The Use-Nullptr Transform is a transformation to convert the usage of null
|
||||
pointer constants (eg. ``NULL``, ``0``) to use the new C++11 ``nullptr``
|
||||
keyword. The transform is enabled with the :option:`-use-nullptr` option of
|
||||
:program:`cpp11-migrate`.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void assignment() {
|
||||
char *a = NULL;
|
||||
char *b = 0;
|
||||
char c = 0;
|
||||
}
|
||||
|
||||
int *ret_ptr() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void assignment() {
|
||||
char *a = nullptr;
|
||||
char *b = nullptr;
|
||||
char c = 0;
|
||||
}
|
||||
|
||||
int *ret_ptr() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
User defined macros
|
||||
===================
|
||||
|
||||
By default this transform will only replace the ``NULL`` macro and will skip any
|
||||
user-defined macros that behaves like ``NULL``. The user can use the
|
||||
:option:`-user-null-macros` option to specify a comma-separated list of macro
|
||||
names that will be transformed along with ``NULL``.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#define MY_NULL (void*)0
|
||||
void assignment() {
|
||||
void *p = MY_NULL;
|
||||
}
|
||||
|
||||
|
||||
using the command-line
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cpp11-migrate -use-nullptr -user-null-macros=MY_NULL foo.cpp
|
||||
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#define MY_NULL NULL
|
||||
void assignment() {
|
||||
int *p = nullptr;
|
||||
}
|
||||
|
||||
|
||||
Risk
|
||||
====
|
||||
|
||||
:option:`-risk` has no effect in this transform.
|
||||
@@ -1,242 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Extra Clang Tools documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Feb 13 10:00:18 2013.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Extra Clang Tools'
|
||||
copyright = u'2007-2013, The Clang Team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '3.3'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '3.3'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'friendly'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'haiku'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'ExtraClangToolsdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'ExtraClangTools.tex', u'Extra Clang Tools Documentation',
|
||||
u'The Clang Team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'extraclangtools', u'Extra Clang Tools Documentation',
|
||||
[u'The Clang Team'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'ExtraClangTools', u'Extra Clang Tools Documentation',
|
||||
u'The Clang Team', 'ExtraClangTools', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
@@ -1,117 +0,0 @@
|
||||
.. index:: cpp11-migrate
|
||||
|
||||
===========================
|
||||
cpp11-migrate User's Manual
|
||||
===========================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
UseAutoTransform
|
||||
UseNullptrTransform
|
||||
LoopConvertTransform
|
||||
AddOverrideTransform
|
||||
|
||||
:program:`cpp11-migrate` is a standalone tool used to automatically convert
|
||||
C++98 and C++03 code to use features of the new C++11 standard where
|
||||
appropriate.
|
||||
|
||||
Basic Usage
|
||||
===========
|
||||
|
||||
``cpp11-migrate [options] <source0> [... <sourceN>]``
|
||||
|
||||
``<source0>...`` specify the paths of files in the CMake source tree,
|
||||
with the same requirements as other tools built on LibTooling.
|
||||
|
||||
Command Line Options
|
||||
--------------------
|
||||
|
||||
.. option:: -help
|
||||
|
||||
Displays tool usage instructions and command line options.
|
||||
|
||||
.. option:: -loop-convert
|
||||
|
||||
Makes use of C++11 range-based for loops where possible. See
|
||||
:doc:`LoopConvertTransform`.
|
||||
|
||||
.. option:: -use-nullptr
|
||||
|
||||
Makes use of the new C++11 keyword ``nullptr`` where possible.
|
||||
See :doc:`UseNullptrTransform`.
|
||||
|
||||
.. option:: -user-null-macros=<string>
|
||||
|
||||
``<string>`` is a comma-separated list of user-defined macros that behave like
|
||||
the ``NULL`` macro. The :option:`-use-nullptr` transform will replace these
|
||||
macros along with ``NULL``. See :doc:`UseNullptrTransform`.
|
||||
|
||||
.. option:: -use-auto
|
||||
|
||||
Replace the type specifier of variable declarations with the ``auto`` type
|
||||
specifier. See :doc:`UseAutoTransform`.
|
||||
|
||||
.. option:: -add-override
|
||||
|
||||
Adds the override specifier to member functions where it is appropriate. That
|
||||
is, the override specifier is added to member functions that override a
|
||||
virtual function in a base class and that don't already have the specifier.
|
||||
See :doc:`AddOverrideTransform`.
|
||||
|
||||
.. option:: -p=<build-path>
|
||||
|
||||
``<build-path>`` is a CMake build directory containing a file named
|
||||
``compile_commands.json`` which provides compiler options for building
|
||||
each source file and can be generated by specifying
|
||||
``-DCMAKE_EXPORT_COMPILE_COMMANDS`` when running CMake. If ``<build-path>``
|
||||
is not provided the ``compile_commands.json`` file is searched for through
|
||||
all parent directories.
|
||||
|
||||
Alternatively, one can provide compile options to be applied to every
|
||||
source file after the optional ``--``.
|
||||
|
||||
.. option:: -risk=<risk-level>
|
||||
|
||||
Some transformations may cause a change in semantics. In such cases the
|
||||
maximum acceptable risk level specified through the ``-risk`` command
|
||||
line option decides whether or not a transformation is applied.
|
||||
|
||||
Three different risk level options are available:
|
||||
|
||||
``-risk=safe``
|
||||
Perform only safe transformations.
|
||||
``-risk=reasonable`` (default)
|
||||
Enable transformations that may change semantics.
|
||||
``-risk=risky``
|
||||
Enable transformations that are likely to change semantics.
|
||||
|
||||
The meaning of risk is handled differently for each transform. See
|
||||
:ref:`transform documentation <transforms>` for details.
|
||||
|
||||
.. option:: -final-syntax-check
|
||||
|
||||
After applying the final transform to a file, parse the file to ensure the
|
||||
last transform did not introduce syntax errors. Syntax errors introduced by
|
||||
earlier transforms are already caught when subsequent transforms parse the
|
||||
file.
|
||||
|
||||
.. option:: -fatal-assembler-warnings
|
||||
|
||||
Treat all compiler warnings as errors.
|
||||
|
||||
|
||||
.. option:: -version
|
||||
|
||||
Displays the version information of this tool.
|
||||
|
||||
.. _transforms:
|
||||
|
||||
Transformations
|
||||
===============
|
||||
|
||||
* :doc:`LoopConvertTransform`
|
||||
|
||||
* :doc:`UseNullptrTransform`
|
||||
|
||||
* :doc:`UseAutoTransform`
|
||||
@@ -1,44 +0,0 @@
|
||||
.. Extra Clang Tools documentation master file, created by
|
||||
sphinx-quickstart on Wed Feb 13 10:00:18 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
.. title:: Welcome to Extra Clang Tools's documentation!
|
||||
|
||||
Introduction
|
||||
============
|
||||
Welcome to clang-tools-exta project which contains extra tools built using Clang's tooling API's
|
||||
|
||||
|
||||
Contents
|
||||
========
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
cpp11-migrate
|
||||
|
||||
|
||||
Doxygen Documentation
|
||||
=====================
|
||||
The Doxygen documentation describes the **internal** software that makes up the
|
||||
tools of clang-tools-extra, not the **external** use of these tools. The
|
||||
Doxygen documentation contains no instructions about how to use the tools,
|
||||
only the APIs that make up the software. For usage instructions, please see
|
||||
the user's guide or reference manual for each tool.
|
||||
|
||||
* `Doxygen documentation`_
|
||||
|
||||
.. _`Doxygen documentation`: doxygen/index.html
|
||||
|
||||
.. note::
|
||||
This documentation is generated directly from the source code with doxygen.
|
||||
Since the tools of clang-tools-extra are constantly under active development,
|
||||
what you're about to read is out of date!
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\ExtraClangTools.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\ExtraClangTools.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@@ -1,16 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
asmparser
|
||||
support
|
||||
mc
|
||||
)
|
||||
|
||||
add_clang_executable(modularize
|
||||
Modularize.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(modularize
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangRewriteFrontend
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
##===- tools/modularize/Makefile ---------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../..
|
||||
|
||||
TOOLNAME = modularize
|
||||
NO_INSTALL = 0
|
||||
|
||||
# No plugins, optimize startup time.
|
||||
TOOL_NO_EXPORTS = 1
|
||||
|
||||
LINK_COMPONENTS := mcparser bitreader support mc
|
||||
USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \
|
||||
clangTooling.a clangParse.a clangSema.a clangAnalysis.a \
|
||||
clangEdit.a clangAST.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
@@ -1,536 +0,0 @@
|
||||
//===- extra/modularize/Modularize.cpp - Check modularized headers --------===//
|
||||
//
|
||||
// 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 checks whether a set of headers provides
|
||||
// the consistent definitions required to use modules. For example, it detects
|
||||
// whether the same entity (say, a NULL macro or size_t typedef) is defined in
|
||||
// multiple headers or whether a header produces different definitions under
|
||||
// different circumstances. These conditions cause modules built from the
|
||||
// headers to behave poorly, and should be fixed before introducing a module
|
||||
// map.
|
||||
//
|
||||
// Modularize takes as argument a file name for a file containing the
|
||||
// newline-separated list of headers to check with respect to each other.
|
||||
// Lines beginning with '#' and empty lines are ignored.
|
||||
// Modularize also accepts regular front-end arguments.
|
||||
//
|
||||
// Usage: modularize [-prefix (optional header path prefix)]
|
||||
// (include-files_list) [(front-end-options) ...]
|
||||
//
|
||||
// Note that unless a "-prefix (header path)" option is specified,
|
||||
// non-absolute file paths in the header list file will be relative
|
||||
// to the header list file directory. Use -prefix to specify a different
|
||||
// directory.
|
||||
//
|
||||
// Note that by default, the underlying Clang front end assumes .h files
|
||||
// contain C source. If your .h files in the file list contain C++ source,
|
||||
// you should append the following to your command lines: -x c++
|
||||
//
|
||||
// Modularize will do normal parsing, reporting normal errors and warnings,
|
||||
// but will also report special error messages like the following:
|
||||
//
|
||||
// error: '(symbol)' defined at multiple locations:
|
||||
// (file):(row):(column)
|
||||
// (file):(row):(column)
|
||||
//
|
||||
// error: header '(file)' has different contents dependening on how it was
|
||||
// included
|
||||
//
|
||||
// The latter might be followed by messages like the following:
|
||||
//
|
||||
// note: '(symbol)' in (file) at (row):(column) not always provided
|
||||
//
|
||||
// Future directions:
|
||||
//
|
||||
// Basically, we want to add new checks for whatever we can check with respect
|
||||
// to checking headers for module'ability.
|
||||
//
|
||||
// Some ideas:
|
||||
//
|
||||
// 1. Try to figure out the preprocessor conditional directives that
|
||||
// contribute to problems.
|
||||
//
|
||||
// 2. Check for correct and consistent usage of extern "C" {} and other
|
||||
// directives. Warn about #include inside extern "C" {}.
|
||||
//
|
||||
// 3. What else?
|
||||
//
|
||||
// General clean-up and refactoring:
|
||||
//
|
||||
// 1. The Location class seems to be something that we might
|
||||
// want to design to be applicable to a wider range of tools, and stick it
|
||||
// somewhere into Tooling/ in mainline
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Config/config.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace clang::tooling;
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
// Option to specify a file name for a list of header files to check.
|
||||
cl::opt<std::string>
|
||||
ListFileName(cl::Positional,
|
||||
cl::desc("<name of file containing list of headers to check>"));
|
||||
|
||||
// Collect all other arguments, which will be passed to the front end.
|
||||
cl::list<std::string> CC1Arguments(
|
||||
cl::ConsumeAfter, cl::desc("<arguments to be passed to front end>..."));
|
||||
|
||||
// Option to specify a prefix to be prepended to the header names.
|
||||
cl::opt<std::string> HeaderPrefix(
|
||||
"prefix", cl::init(""),
|
||||
cl::desc(
|
||||
"Prepend header file paths with this prefix."
|
||||
" If not specified,"
|
||||
" the files are considered to be relative to the header list file."));
|
||||
|
||||
// Read the header list file and collect the header file names.
|
||||
error_code GetHeaderFileNames(SmallVectorImpl<std::string> &headerFileNames,
|
||||
StringRef listFileName, StringRef headerPrefix) {
|
||||
|
||||
// By default, use the path component of the list file name.
|
||||
SmallString<256> headerDirectory(listFileName);
|
||||
sys::path::remove_filename(headerDirectory);
|
||||
|
||||
// Get the prefix if we have one.
|
||||
if (headerPrefix.size() != 0)
|
||||
headerDirectory = headerPrefix;
|
||||
|
||||
// Read the header list file into a buffer.
|
||||
OwningPtr<MemoryBuffer> listBuffer;
|
||||
if (error_code ec = MemoryBuffer::getFile(ListFileName, listBuffer)) {
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Parse the header list into strings.
|
||||
SmallVector<StringRef, 32> strings;
|
||||
listBuffer->getBuffer().split(strings, "\n", -1, false);
|
||||
|
||||
// Collect the header file names from the string list.
|
||||
for (SmallVectorImpl<StringRef>::iterator I = strings.begin(),
|
||||
E = strings.end();
|
||||
I != E; ++I) {
|
||||
StringRef line = (*I).trim();
|
||||
// Ignore comments and empty lines.
|
||||
if (line.empty() || (line[0] == '#'))
|
||||
continue;
|
||||
SmallString<256> headerFileName;
|
||||
// Prepend header file name prefix if it's not absolute.
|
||||
if (sys::path::is_absolute(line))
|
||||
headerFileName = line;
|
||||
else {
|
||||
headerFileName = headerDirectory;
|
||||
sys::path::append(headerFileName, line);
|
||||
}
|
||||
// Save the resulting header file path.
|
||||
headerFileNames.push_back(headerFileName.str());
|
||||
}
|
||||
|
||||
return error_code::success();
|
||||
}
|
||||
|
||||
// FIXME: The Location class seems to be something that we might
|
||||
// want to design to be applicable to a wider range of tools, and stick it
|
||||
// somewhere into Tooling/ in mainline
|
||||
struct Location {
|
||||
const FileEntry *File;
|
||||
unsigned Line, Column;
|
||||
|
||||
Location() : File(), Line(), Column() {}
|
||||
|
||||
Location(SourceManager &SM, SourceLocation Loc) : File(), Line(), Column() {
|
||||
Loc = SM.getExpansionLoc(Loc);
|
||||
if (Loc.isInvalid())
|
||||
return;
|
||||
|
||||
std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
|
||||
File = SM.getFileEntryForID(Decomposed.first);
|
||||
if (!File)
|
||||
return;
|
||||
|
||||
Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
|
||||
Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
|
||||
}
|
||||
|
||||
operator bool() const { return File != 0; }
|
||||
|
||||
friend bool operator==(const Location &X, const Location &Y) {
|
||||
return X.File == Y.File && X.Line == Y.Line && X.Column == Y.Column;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Location &X, const Location &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
|
||||
friend bool operator<(const Location &X, const Location &Y) {
|
||||
if (X.File != Y.File)
|
||||
return X.File < Y.File;
|
||||
if (X.Line != Y.Line)
|
||||
return X.Line < Y.Line;
|
||||
return X.Column < Y.Column;
|
||||
}
|
||||
friend bool operator>(const Location &X, const Location &Y) { return Y < X; }
|
||||
friend bool operator<=(const Location &X, const Location &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
friend bool operator>=(const Location &X, const Location &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
enum EntryKind {
|
||||
EK_Tag,
|
||||
EK_Value,
|
||||
EK_Macro,
|
||||
|
||||
EK_NumberOfKinds
|
||||
} Kind;
|
||||
|
||||
Location Loc;
|
||||
|
||||
StringRef getKindName() { return getKindName(Kind); }
|
||||
static StringRef getKindName(EntryKind kind);
|
||||
};
|
||||
|
||||
// Return a string representing the given kind.
|
||||
StringRef Entry::getKindName(Entry::EntryKind kind) {
|
||||
switch (kind) {
|
||||
case EK_Tag:
|
||||
return "tag";
|
||||
case EK_Value:
|
||||
return "value";
|
||||
case EK_Macro:
|
||||
return "macro";
|
||||
case EK_NumberOfKinds:
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("invalid Entry kind");
|
||||
}
|
||||
|
||||
struct HeaderEntry {
|
||||
std::string Name;
|
||||
Location Loc;
|
||||
|
||||
friend bool operator==(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return X.Loc == Y.Loc && X.Name == Y.Name;
|
||||
}
|
||||
friend bool operator!=(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
friend bool operator<(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return X.Loc < Y.Loc || (X.Loc == Y.Loc && X.Name < Y.Name);
|
||||
}
|
||||
friend bool operator>(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return Y < X;
|
||||
}
|
||||
friend bool operator<=(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
friend bool operator>=(const HeaderEntry &X, const HeaderEntry &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<HeaderEntry> HeaderContents;
|
||||
|
||||
class EntityMap : public StringMap<SmallVector<Entry, 2> > {
|
||||
public:
|
||||
DenseMap<const FileEntry *, HeaderContents> HeaderContentMismatches;
|
||||
|
||||
void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc) {
|
||||
// Record this entity in its header.
|
||||
HeaderEntry HE = { Name, Loc };
|
||||
CurHeaderContents[Loc.File].push_back(HE);
|
||||
|
||||
// Check whether we've seen this entry before.
|
||||
SmallVector<Entry, 2> &Entries = (*this)[Name];
|
||||
for (unsigned I = 0, N = Entries.size(); I != N; ++I) {
|
||||
if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
|
||||
return;
|
||||
}
|
||||
|
||||
// We have not seen this entry before; record it.
|
||||
Entry E = { Kind, Loc };
|
||||
Entries.push_back(E);
|
||||
}
|
||||
|
||||
void mergeCurHeaderContents() {
|
||||
for (DenseMap<const FileEntry *, HeaderContents>::iterator
|
||||
H = CurHeaderContents.begin(),
|
||||
HEnd = CurHeaderContents.end();
|
||||
H != HEnd; ++H) {
|
||||
// Sort contents.
|
||||
std::sort(H->second.begin(), H->second.end());
|
||||
|
||||
// Check whether we've seen this header before.
|
||||
DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
|
||||
AllHeaderContents.find(H->first);
|
||||
if (KnownH == AllHeaderContents.end()) {
|
||||
// We haven't seen this header before; record its contents.
|
||||
AllHeaderContents.insert(*H);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the header contents are the same, we're done.
|
||||
if (H->second == KnownH->second)
|
||||
continue;
|
||||
|
||||
// Determine what changed.
|
||||
std::set_symmetric_difference(
|
||||
H->second.begin(), H->second.end(), KnownH->second.begin(),
|
||||
KnownH->second.end(),
|
||||
std::back_inserter(HeaderContentMismatches[H->first]));
|
||||
}
|
||||
|
||||
CurHeaderContents.clear();
|
||||
}
|
||||
private:
|
||||
DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
|
||||
DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
|
||||
};
|
||||
|
||||
class CollectEntitiesVisitor :
|
||||
public RecursiveASTVisitor<CollectEntitiesVisitor> {
|
||||
public:
|
||||
CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities)
|
||||
: SM(SM), Entities(Entities) {}
|
||||
|
||||
bool TraverseStmt(Stmt *S) { return true; }
|
||||
bool TraverseType(QualType T) { return true; }
|
||||
bool TraverseTypeLoc(TypeLoc TL) { return true; }
|
||||
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
|
||||
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
|
||||
return true;
|
||||
}
|
||||
bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
|
||||
return true;
|
||||
}
|
||||
bool TraverseTemplateName(TemplateName Template) { return true; }
|
||||
bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
|
||||
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
|
||||
return true;
|
||||
}
|
||||
bool TraverseTemplateArguments(const TemplateArgument *Args,
|
||||
unsigned NumArgs) {
|
||||
return true;
|
||||
}
|
||||
bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
|
||||
bool TraverseLambdaCapture(LambdaExpr::Capture C) { return true; }
|
||||
|
||||
bool VisitNamedDecl(NamedDecl *ND) {
|
||||
// We only care about file-context variables.
|
||||
if (!ND->getDeclContext()->isFileContext())
|
||||
return true;
|
||||
|
||||
// Skip declarations that tend to be properly multiply-declared.
|
||||
if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
|
||||
isa<NamespaceAliasDecl>(ND) ||
|
||||
isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
|
||||
isa<UsingShadowDecl>(ND) || isa<FunctionDecl>(ND) ||
|
||||
isa<FunctionTemplateDecl>(ND) ||
|
||||
(isa<TagDecl>(ND) &&
|
||||
!cast<TagDecl>(ND)->isThisDeclarationADefinition()))
|
||||
return true;
|
||||
|
||||
std::string Name = ND->getNameAsString();
|
||||
if (Name.empty())
|
||||
return true;
|
||||
|
||||
Location Loc(SM, ND->getLocation());
|
||||
if (!Loc)
|
||||
return true;
|
||||
|
||||
Entities.add(Name, isa<TagDecl>(ND) ? Entry::EK_Tag : Entry::EK_Value, Loc);
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
SourceManager &SM;
|
||||
EntityMap &Entities;
|
||||
};
|
||||
|
||||
class CollectEntitiesConsumer : public ASTConsumer {
|
||||
public:
|
||||
CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP)
|
||||
: Entities(Entities), PP(PP) {}
|
||||
|
||||
virtual void HandleTranslationUnit(ASTContext &Ctx) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
|
||||
// Collect declared entities.
|
||||
CollectEntitiesVisitor(SM, Entities)
|
||||
.TraverseDecl(Ctx.getTranslationUnitDecl());
|
||||
|
||||
// Collect macro definitions.
|
||||
for (Preprocessor::macro_iterator M = PP.macro_begin(),
|
||||
MEnd = PP.macro_end();
|
||||
M != MEnd; ++M) {
|
||||
Location Loc(SM, M->second->getLocation());
|
||||
if (!Loc)
|
||||
continue;
|
||||
|
||||
Entities.add(M->first->getName().str(), Entry::EK_Macro, Loc);
|
||||
}
|
||||
|
||||
// Merge header contents.
|
||||
Entities.mergeCurHeaderContents();
|
||||
}
|
||||
private:
|
||||
EntityMap &Entities;
|
||||
Preprocessor &PP;
|
||||
};
|
||||
|
||||
class CollectEntitiesAction : public SyntaxOnlyAction {
|
||||
public:
|
||||
CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {}
|
||||
protected:
|
||||
virtual clang::ASTConsumer *
|
||||
CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
||||
return new CollectEntitiesConsumer(Entities, CI.getPreprocessor());
|
||||
}
|
||||
private:
|
||||
EntityMap &Entities;
|
||||
};
|
||||
|
||||
class ModularizeFrontendActionFactory : public FrontendActionFactory {
|
||||
public:
|
||||
ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {}
|
||||
|
||||
virtual CollectEntitiesAction *create() {
|
||||
return new CollectEntitiesAction(Entities);
|
||||
}
|
||||
private:
|
||||
EntityMap &Entities;
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
|
||||
// This causes options to be parsed.
|
||||
cl::ParseCommandLineOptions(argc, argv, "modularize.\n");
|
||||
|
||||
// No go if we have no header list file.
|
||||
if (ListFileName.size() == 0) {
|
||||
cl::PrintHelpMessage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get header file names.
|
||||
SmallVector<std::string, 32> Headers;
|
||||
if (error_code ec = GetHeaderFileNames(Headers, ListFileName, HeaderPrefix)) {
|
||||
errs() << argv[0] << ": error: Unable to get header list '" << ListFileName
|
||||
<< "': " << ec.message() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create the compilation database.
|
||||
SmallString<256> PathBuf;
|
||||
sys::fs::current_path(PathBuf);
|
||||
OwningPtr<CompilationDatabase> Compilations;
|
||||
Compilations.reset(
|
||||
new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
|
||||
|
||||
// Parse all of the headers, detecting duplicates.
|
||||
EntityMap Entities;
|
||||
ClangTool Tool(*Compilations, Headers);
|
||||
int HadErrors = Tool.run(new ModularizeFrontendActionFactory(Entities));
|
||||
|
||||
// Create a place to save duplicate entity locations, separate bins per kind.
|
||||
typedef SmallVector<Location, 8> LocationArray;
|
||||
typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
|
||||
EntryBinArray EntryBins;
|
||||
int kindIndex;
|
||||
for (kindIndex = 0; kindIndex < Entry::EK_NumberOfKinds; ++kindIndex) {
|
||||
LocationArray array;
|
||||
EntryBins.push_back(array);
|
||||
}
|
||||
|
||||
// Check for the same entity being defined in multiple places.
|
||||
for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
|
||||
E != EEnd; ++E) {
|
||||
// If only one occurance, exit early.
|
||||
if (E->second.size() == 1)
|
||||
continue;
|
||||
// Clear entity locations.
|
||||
for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
|
||||
CI != CE; ++CI) {
|
||||
CI->clear();
|
||||
}
|
||||
// Walk the entities of a single name, collecting the locations,
|
||||
// separated into separate bins.
|
||||
for (unsigned I = 0, N = E->second.size(); I != N; ++I) {
|
||||
EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
|
||||
}
|
||||
// Report any duplicate entity definition errors.
|
||||
int kindIndex = 0;
|
||||
for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
|
||||
DI != DE; ++DI, ++kindIndex) {
|
||||
int eCount = DI->size();
|
||||
// If only 1 occurance, skip;
|
||||
if (eCount <= 1)
|
||||
continue;
|
||||
LocationArray::iterator FI = DI->begin();
|
||||
StringRef kindName = Entry::getKindName((Entry::EntryKind) kindIndex);
|
||||
errs() << "error: " << kindName << " '" << E->first()
|
||||
<< "' defined at multiple locations:\n";
|
||||
for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
|
||||
errs() << " " << FI->File->getName() << ":" << FI->Line << ":"
|
||||
<< FI->Column << "\n";
|
||||
}
|
||||
HadErrors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Complain about any headers that have contents that differ based on how
|
||||
// they are included.
|
||||
// FIXME: Could we provide information about which preprocessor conditionals
|
||||
// are involved?
|
||||
for (DenseMap<const FileEntry *, HeaderContents>::iterator
|
||||
H = Entities.HeaderContentMismatches.begin(),
|
||||
HEnd = Entities.HeaderContentMismatches.end();
|
||||
H != HEnd; ++H) {
|
||||
if (H->second.empty()) {
|
||||
errs() << "internal error: phantom header content mismatch\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
HadErrors = 1;
|
||||
errs() << "error: header '" << H->first->getName()
|
||||
<< "' has different contents dependening on how it was included\n";
|
||||
for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
|
||||
errs() << "note: '" << H->second[I].Name << "' in " << H->second[I]
|
||||
.Loc.File->getName() << " at " << H->second[I].Loc.Line << ":"
|
||||
<< H->second[I].Loc.Column << " not always provided\n";
|
||||
}
|
||||
}
|
||||
|
||||
return HadErrors;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
add_clang_executable(remove-cstr-calls
|
||||
RemoveCStrCalls.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(remove-cstr-calls
|
||||
clangEdit clangTooling clangBasic clangAST clangASTMatchers)
|
||||
@@ -1,25 +0,0 @@
|
||||
##===- tools/extra/remove-cstr-calls/Makefile --------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../..
|
||||
|
||||
TOOLNAME = remove-cstr-calls
|
||||
NO_INSTALL = 1
|
||||
|
||||
# No plugins, optimize startup time.
|
||||
TOOL_NO_EXPORTS = 1
|
||||
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc
|
||||
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
|
||||
clangRewriteFrontend.a clangRewriteCore.a \
|
||||
clangParse.a clangSema.a clangAnalysis.a \
|
||||
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
@@ -1,238 +0,0 @@
|
||||
//===- 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/build
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace llvm;
|
||||
using clang::tooling::newFrontendActionFactory;
|
||||
using clang::tooling::Replacement;
|
||||
using clang::tooling::CompilationDatabase;
|
||||
|
||||
// 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>
|
||||
static std::string getText(const SourceManager &SourceManager, const T &Node) {
|
||||
SourceLocation StartSpellingLocatino =
|
||||
SourceManager.getSpellingLoc(Node.getLocStart());
|
||||
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<FileID, unsigned> Start =
|
||||
SourceManager.getDecomposedLoc(StartSpellingLocatino);
|
||||
std::pair<FileID, unsigned> End =
|
||||
SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
|
||||
EndSpellingLocation, 0, SourceManager, 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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
|
||||
if (dyn_cast<clang::BinaryOperator>(&ExprNode) ||
|
||||
dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
|
||||
return true;
|
||||
}
|
||||
if (const CXXOperatorCallExpr *op =
|
||||
dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
|
||||
return op->getNumArgs() == 2 &&
|
||||
op->getOperator() != OO_PlusPlus &&
|
||||
op->getOperator() != OO_MinusMinus &&
|
||||
op->getOperator() != OO_Call &&
|
||||
op->getOperator() != OO_Subscript;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format a pointer to an expression: prefix with '*' but simplify
|
||||
// when it already begins with '&'. Return empty string on failure.
|
||||
static std::string formatDereference(const SourceManager &SourceManager,
|
||||
const Expr &ExprNode) {
|
||||
if (const clang::UnaryOperator *Op =
|
||||
dyn_cast<clang::UnaryOperator>(&ExprNode)) {
|
||||
if (Op->getOpcode() == 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;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class FixCStrCall : public ast_matchers::MatchFinder::MatchCallback {
|
||||
public:
|
||||
FixCStrCall(tooling::Replacements *Replace)
|
||||
: Replace(Replace) {}
|
||||
|
||||
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
const CallExpr *Call =
|
||||
Result.Nodes.getStmtAs<CallExpr>("call");
|
||||
const Expr *Arg =
|
||||
Result.Nodes.getStmtAs<Expr>("arg");
|
||||
const bool Arrow =
|
||||
Result.Nodes.getStmtAs<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;
|
||||
|
||||
Replace->insert(Replacement(*Result.SourceManager, Call, ArgText));
|
||||
}
|
||||
|
||||
private:
|
||||
tooling::Replacements *Replace;
|
||||
};
|
||||
} // end namespace
|
||||
|
||||
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";
|
||||
|
||||
cl::opt<std::string> BuildPath(
|
||||
cl::Positional,
|
||||
cl::desc("<build-path>"));
|
||||
|
||||
cl::list<std::string> SourcePaths(
|
||||
cl::Positional,
|
||||
cl::desc("<source0> [... <sourceN>]"),
|
||||
cl::OneOrMore);
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
llvm::sys::PrintStackTraceOnErrorSignal();
|
||||
llvm::OwningPtr<CompilationDatabase> Compilations(
|
||||
tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv));
|
||||
cl::ParseCommandLineOptions(argc, argv);
|
||||
if (!Compilations) {
|
||||
std::string ErrorMessage;
|
||||
Compilations.reset(
|
||||
CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage));
|
||||
if (!Compilations)
|
||||
llvm::report_fatal_error(ErrorMessage);
|
||||
}
|
||||
tooling::RefactoringTool Tool(*Compilations, SourcePaths);
|
||||
ast_matchers::MatchFinder Finder;
|
||||
FixCStrCall Callback(&Tool.getReplacements());
|
||||
Finder.addMatcher(
|
||||
constructExpr(
|
||||
hasDeclaration(methodDecl(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", memberCallExpr(
|
||||
callee(id("member", memberExpr())),
|
||||
callee(methodDecl(hasName(StringCStrMethod))),
|
||||
on(id("arg", expr()))))),
|
||||
// The second argument is the alloc object which must not be
|
||||
// present explicitly.
|
||||
hasArgument(
|
||||
1,
|
||||
defaultArgExpr())),
|
||||
&Callback);
|
||||
Finder.addMatcher(
|
||||
constructExpr(
|
||||
// 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(methodDecl(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", memberCallExpr(
|
||||
callee(id("member", memberExpr())),
|
||||
callee(methodDecl(hasName(StringCStrMethod))),
|
||||
on(id("arg", expr())))))),
|
||||
&Callback);
|
||||
return Tool.runAndSave(newFrontendActionFactory(&Finder));
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
# Test runner infrastructure for Clang-based tools. This configures the Clang
|
||||
# test trees for use by Lit, and delegates to LLVM's lit test handlers.
|
||||
#
|
||||
# Note that currently we don't support stand-alone builds of Clang, you must
|
||||
# be building Clang from within a combined LLVM+Clang checkout..
|
||||
|
||||
set(CLANG_TOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
set(CLANG_TOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
|
||||
)
|
||||
|
||||
option(CLANG_TOOLS_TEST_USE_VG "Run Clang tools' tests under Valgrind" OFF)
|
||||
if(CLANG_TOOLS_TEST_USE_VG)
|
||||
set(CLANG_TOOLS_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
|
||||
endif()
|
||||
|
||||
set(CLANG_TOOLS_TEST_DEPS
|
||||
# Base line deps.
|
||||
clang clang-headers FileCheck count not
|
||||
|
||||
# Individual tools we test.
|
||||
remove-cstr-calls cpp11-migrate modularize
|
||||
|
||||
# Unit tests
|
||||
ExtraToolsUnitTests
|
||||
)
|
||||
|
||||
add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
|
||||
ARGS ${CLANG_TOOLS_TEST_EXTRA_ARGS}
|
||||
)
|
||||
set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests")
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
##===- tools/extra/test/Makefile ---------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../..
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
# Test in all immediate subdirectories if unset.
|
||||
ifdef TESTSUITE
|
||||
TESTDIRS := $(TESTSUITE:%=$(PROJ_SRC_DIR)/%)
|
||||
else
|
||||
TESTDIRS ?= $(PROJ_SRC_DIR)
|
||||
endif
|
||||
|
||||
# 'lit' wants objdir paths, so it will pick up the lit.site.cfg.
|
||||
TESTDIRS := $(TESTDIRS:$(PROJ_SRC_DIR)%=$(PROJ_OBJ_DIR)%)
|
||||
|
||||
# Allow EXTRA_TESTDIRS to provide additional test directories.
|
||||
TESTDIRS += $(EXTRA_TESTDIRS)
|
||||
|
||||
ifndef TESTARGS
|
||||
ifdef VERBOSE
|
||||
TESTARGS = -v
|
||||
else
|
||||
TESTARGS = -s -v
|
||||
endif
|
||||
endif
|
||||
|
||||
# Make sure any extra test suites can find the main site config.
|
||||
LIT_ARGS := --param clang_site_config=$(PROJ_OBJ_DIR)/lit.site.cfg
|
||||
|
||||
ifdef VG
|
||||
LIT_ARGS += "--vg"
|
||||
endif
|
||||
|
||||
all:: lit.site.cfg Unit/lit.site.cfg
|
||||
@ echo '--- Running the Clang extra tools tests for $(TARGET_TRIPLE) ---'
|
||||
@ $(PYTHON) $(LLVM_SRC_ROOT)/utils/lit/lit.py \
|
||||
$(LIT_ARGS) $(TESTARGS) $(TESTDIRS)
|
||||
|
||||
FORCE:
|
||||
|
||||
lit.site.cfg: FORCE
|
||||
@echo "Making lit.site.cfg for Clang extra tools..."
|
||||
@$(ECHOPATH) s=@LLVM_SOURCE_DIR@=$(LLVM_SRC_ROOT)=g > lit.tmp
|
||||
@$(ECHOPATH) s=@LLVM_BINARY_DIR@=$(LLVM_OBJ_ROOT)=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@LLVM_TOOLS_DIR@=$(ToolDir)=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@LLVM_LIBS_DIR@=$(LibDir)=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@CLANG_TOOLS_SOURCE_DIR@=$(PROJ_SRC_DIR)/..=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@CLANG_TOOLS_BINARY_DIR@=$(PROJ_OBJ_DIR)/..=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@TARGET_TRIPLE@=$(TARGET_TRIPLE)=g >> lit.tmp
|
||||
@sed -f lit.tmp $(PROJ_SRC_DIR)/lit.site.cfg.in > $@
|
||||
@-rm -f lit.tmp
|
||||
|
||||
Unit/lit.site.cfg: FORCE
|
||||
@echo "Making Unit/lit.site.cfg for Clang extra tools..."
|
||||
@$(MKDIR) $(dir $@)
|
||||
@$(ECHOPATH) s=@CLANG_TOOLS_BINARY_DIR@=$(PROJ_OBJ_DIR)/..=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@TARGET_TRIPLE@=$(TARGET_TRIPLE)=g >> lit.tmp
|
||||
@$(ECHOPATH) s=@CLANG_TOOLS_SOURCE_DIR@=$(PROJ_SRC_DIR)/..=g >> lit.tmp
|
||||
@sed -f lit.tmp $(PROJ_SRC_DIR)/Unit/lit.site.cfg.in > $@
|
||||
@-rm -f lit.tmp
|
||||
|
||||
clean::
|
||||
@ find . -name Output | xargs rm -fr
|
||||
|
||||
.PHONY: all report clean
|
||||
@@ -1,29 +0,0 @@
|
||||
# -*- Python -*-
|
||||
|
||||
config.name = "Extra Tools Unit Tests"
|
||||
config.suffixes = [] # Seems not to matter for google tests?
|
||||
|
||||
# Test Source and Exec root dirs both point to the same directory where google
|
||||
# test binaries are built.
|
||||
extra_tools_obj_dir = getattr(config, 'extra_tools_obj_dir', None)
|
||||
if extra_tools_obj_dir is not None:
|
||||
config.test_source_root = extra_tools_obj_dir
|
||||
config.test_exec_root = config.test_source_root
|
||||
|
||||
# All GoogleTests are named to have 'Tests' as their suffix. The '.' option is
|
||||
# a special value for GoogleTest indicating that it should look through the
|
||||
# entire testsuite recursively for tests (alternatively, one could provide a
|
||||
# ;-separated list of subdirectories).
|
||||
config.test_format = lit.formats.GoogleTest('.', 'Tests')
|
||||
|
||||
# If the site-specific configuration wasn't loaded (e.g. the build system failed
|
||||
# to create it or the user is running a test file directly) try to come up with
|
||||
# sane config options.
|
||||
if config.test_exec_root is None:
|
||||
# Look for a --param=extra_tools_unit_site_config option.
|
||||
site_cfg = lit.params.get('extra_tools_unit_site_config', None)
|
||||
if site_cfg and os.path.exists(site_cfg):
|
||||
lit.load_config(config, site_cfg)
|
||||
raise SystemExit
|
||||
|
||||
# FIXME: Support out-of-tree builds? See clang/test/Unit/lit.cfg if we care.
|
||||
@@ -1,13 +0,0 @@
|
||||
## Autogenerated by LLVM/Clang configuration.
|
||||
# Do not edit!
|
||||
config.extra_tools_obj_dir = "@CLANG_TOOLS_BINARY_DIR@/unittests"
|
||||
config.extra_tools_src_dir = "@CLANG_TOOLS_SOURCE_DIR@/unittests"
|
||||
config.target_triple = "@TARGET_TRIPLE@"
|
||||
|
||||
# Make sure any custom vars defined above that are required in lit.local.cfg
|
||||
# files are made available.
|
||||
def on_clone(parent, clone, path):
|
||||
clone.extra_tools_src_dir = parent.extra_tools_src_dir
|
||||
|
||||
config.on_clone = on_clone
|
||||
lit.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/Unit/lit.cfg")
|
||||
@@ -1,110 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -add-override %t.cpp -- -I %S -std=c++11
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual ~A();
|
||||
// CHECK: virtual ~A();
|
||||
void f();
|
||||
virtual void h() const;
|
||||
// CHECK: virtual void h() const;
|
||||
virtual void i() = 0;
|
||||
// CHECK: virtual void i() = 0;
|
||||
};
|
||||
|
||||
// Test that override isn't added to non-virtual functions.
|
||||
class B : public A {
|
||||
public:
|
||||
void f();
|
||||
// CHECK: class B
|
||||
// CHECK: void f();
|
||||
};
|
||||
|
||||
// Test that override is added to functions that override virtual functions.
|
||||
class C : public A {
|
||||
public:
|
||||
void h() const;
|
||||
// CHECK: class C
|
||||
// CHECK: void h() const override;
|
||||
};
|
||||
|
||||
// Test that override isn't add to functions that overload but not override.
|
||||
class D : public A {
|
||||
public:
|
||||
void h();
|
||||
// CHECK: class D
|
||||
// CHECK: void h();
|
||||
};
|
||||
|
||||
// Test that override isn't added again to functions that already have it.
|
||||
class E : public A {
|
||||
public:
|
||||
void h() const override;
|
||||
// CHECK: class E
|
||||
// CHECK: void h() const override;
|
||||
};
|
||||
|
||||
// Test that override isn't added to the destructor.
|
||||
class F : public A {
|
||||
public:
|
||||
virtual ~F();
|
||||
// CHECK: class F
|
||||
// CHECK: virtual ~F();
|
||||
};
|
||||
|
||||
// Test that override is placed before any end of line comments.
|
||||
class G : public A {
|
||||
public:
|
||||
void h() const; // comment
|
||||
// CHECK: class G
|
||||
// CHECK: void h() const override; // comment
|
||||
};
|
||||
|
||||
// Test that override is placed correctly if there is an inline body.
|
||||
class H : public A {
|
||||
public:
|
||||
void h() const { }
|
||||
// CHECK: class H
|
||||
// CHECK: void h() const override { }
|
||||
};
|
||||
|
||||
// Test that override is placed correctly if there is a body on the next line.
|
||||
class I : public A {
|
||||
public:
|
||||
void h() const
|
||||
{ }
|
||||
// CHECK: class I
|
||||
// CHECK: void h() const override
|
||||
// CHECK: { }
|
||||
};
|
||||
|
||||
// Test that override is placed correctly if there is a body outside the class.
|
||||
class J : public A {
|
||||
public:
|
||||
void h() const;
|
||||
// CHECK: class J
|
||||
// CHECK: void h() const override;
|
||||
};
|
||||
|
||||
void J::h() const {
|
||||
// CHECK: void J::h() const {
|
||||
}
|
||||
|
||||
// Test that override is placed correctly if there is a trailing return type.
|
||||
class K : public A {
|
||||
public:
|
||||
auto h() const -> void;
|
||||
// CHECK: class K
|
||||
// CHECK: auto h() const -> void override;
|
||||
};
|
||||
|
||||
// Test that override isn't added if it is already specified via a macro.
|
||||
class L : public A {
|
||||
public:
|
||||
#define LLVM_OVERRIDE override
|
||||
void h() const LLVM_OVERRIDE;
|
||||
// CHECK: class L
|
||||
// CHECK: void h() const LLVM_OVERRIDE;
|
||||
};
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -add-override %t.cpp -- -I %S
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// XFAIL: *
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual void h() const;
|
||||
// CHECK: virtual void h() const;
|
||||
};
|
||||
|
||||
// Test that the override is correctly placed if there
|
||||
// is an inline comment between the function declaration
|
||||
// and the function body.
|
||||
// This test fails with the override keyword being added
|
||||
// to the end of the comment. This failure occurs because
|
||||
// the insertion point is incorrectly calculated if there
|
||||
// is an inline comment before the method body.
|
||||
class B : public A {
|
||||
public:
|
||||
virtual void h() const // comment
|
||||
{ }
|
||||
// CHECK: virtual void h() const override
|
||||
};
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t_risky.cpp
|
||||
// RUN: cpp11-migrate -loop-convert -use-nullptr %t.cpp --
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: cpp11-migrate -loop-convert -use-nullptr -risk=risky %t_risky.cpp --
|
||||
// RUN: FileCheck -check-prefix=RISKY -input-file=%t_risky.cpp %s
|
||||
|
||||
#define NULL 0
|
||||
|
||||
struct T {
|
||||
struct iterator {
|
||||
int *& operator*();
|
||||
const int *& operator*() const;
|
||||
iterator & operator++();
|
||||
bool operator!=(const iterator &other);
|
||||
void insert(int *);
|
||||
int *x;
|
||||
};
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
void test_loopconvert_and_nullptr_iterator() {
|
||||
T t;
|
||||
|
||||
for (T::iterator it = t.begin(); it != t.end(); ++it) {
|
||||
*it = NULL;
|
||||
}
|
||||
|
||||
// CHECK: for (auto & elem : t)
|
||||
// CHECK-NEXT: elem = nullptr;
|
||||
}
|
||||
|
||||
void test_loopconvert_and_nullptr_risky() {
|
||||
const int N = 10;
|
||||
int *(*pArr)[N];
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
(*pArr)[i] = NULL;
|
||||
}
|
||||
|
||||
// RISKY: for (auto & elem : *pArr)
|
||||
// RISKY-NEXT: elem = nullptr;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#define myns nsblah
|
||||
|
||||
namespace nsblah {
|
||||
struct MyType {
|
||||
};
|
||||
|
||||
} // namespace nsblah
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef NEGATIVE_HEADER_H
|
||||
#define NEGATIVE_HEADER_H
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
static void loopInHeader() {
|
||||
const int N = 10;
|
||||
int arr[N];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < N; ++i)
|
||||
sum += arr[i];
|
||||
}
|
||||
|
||||
#endif // NEGATIVE_HEADER_H
|
||||
@@ -1,176 +0,0 @@
|
||||
#ifndef STRUCTURES_H
|
||||
#define STRUCTURES_H
|
||||
|
||||
extern "C" {
|
||||
extern int printf(const char *restrict, ...);
|
||||
}
|
||||
|
||||
struct Val {int x; void g(); };
|
||||
|
||||
struct MutableVal {
|
||||
void constFun(int) const;
|
||||
void nonConstFun(int, int);
|
||||
void constFun(MutableVal &) const;
|
||||
void constParamFun(const MutableVal &) const;
|
||||
void nonConstParamFun(const MutableVal &);
|
||||
int x;
|
||||
};
|
||||
|
||||
struct S {
|
||||
typedef MutableVal *iterator;
|
||||
typedef const MutableVal *const_iterator;
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
struct T {
|
||||
struct iterator {
|
||||
int& operator*();
|
||||
const int& operator*()const;
|
||||
iterator& operator ++();
|
||||
bool operator!=(const iterator &other);
|
||||
void insert(int);
|
||||
int x;
|
||||
};
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
struct U {
|
||||
struct iterator {
|
||||
Val& operator*();
|
||||
const Val& operator*()const;
|
||||
iterator& operator ++();
|
||||
bool operator!=(const iterator &other);
|
||||
Val *operator->();
|
||||
};
|
||||
iterator begin();
|
||||
iterator end();
|
||||
int x;
|
||||
};
|
||||
|
||||
struct X {
|
||||
S s;
|
||||
T t;
|
||||
U u;
|
||||
S getS();
|
||||
};
|
||||
|
||||
template<typename ElemType>
|
||||
class dependent{
|
||||
public:
|
||||
struct iterator_base {
|
||||
const ElemType& operator*()const;
|
||||
iterator_base& operator ++();
|
||||
bool operator!=(const iterator_base &other) const;
|
||||
const ElemType *operator->() const;
|
||||
};
|
||||
|
||||
struct iterator : iterator_base {
|
||||
ElemType& operator*();
|
||||
iterator& operator ++();
|
||||
ElemType *operator->();
|
||||
};
|
||||
|
||||
typedef iterator_base const_iterator;
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator begin();
|
||||
iterator end();
|
||||
unsigned size() const;
|
||||
ElemType & operator[](unsigned);
|
||||
const ElemType & operator[](unsigned) const;
|
||||
ElemType & at(unsigned);
|
||||
const ElemType & at(unsigned) const;
|
||||
|
||||
// Intentionally evil.
|
||||
dependent<ElemType> operator*();
|
||||
|
||||
void foo();
|
||||
void constFoo() const;
|
||||
};
|
||||
|
||||
template<typename First, typename Second>
|
||||
class doublyDependent{
|
||||
public:
|
||||
struct Value {
|
||||
First first;
|
||||
Second second;
|
||||
};
|
||||
|
||||
struct iterator_base {
|
||||
const Value& operator*()const;
|
||||
iterator_base& operator ++();
|
||||
bool operator!=(const iterator_base &other) const;
|
||||
const Value *operator->() const;
|
||||
};
|
||||
|
||||
struct iterator : iterator_base {
|
||||
Value& operator*();
|
||||
Value& operator ++();
|
||||
Value *operator->();
|
||||
};
|
||||
|
||||
typedef iterator_base const_iterator;
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
template<typename Contained>
|
||||
class transparent {
|
||||
public:
|
||||
Contained *at();
|
||||
Contained *operator->();
|
||||
Contained operator*();
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct Nested {
|
||||
typedef IteratorType* iterator;
|
||||
IteratorType *operator->();
|
||||
IteratorType operator*();
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
// Like llvm::SmallPtrSet, the iterator has a dereference operator that returns
|
||||
// by value instead of by reference.
|
||||
template <typename T>
|
||||
struct PtrSet {
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &other) const;
|
||||
const T operator*();
|
||||
iterator &operator++();
|
||||
};
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TypedefDerefContainer {
|
||||
struct iterator {
|
||||
typedef T &deref_type;
|
||||
bool operator!=(const iterator &other) const;
|
||||
deref_type operator*();
|
||||
iterator &operator++();
|
||||
};
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RValueDerefContainer {
|
||||
struct iterator {
|
||||
typedef T &&deref_type;
|
||||
bool operator!=(const iterator &other) const;
|
||||
deref_type operator*();
|
||||
iterator &operator++();
|
||||
};
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
#endif // STRUCTURES_H
|
||||
@@ -1,155 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cp %t.cpp %t.base
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: cp %t.base %t.cpp
|
||||
// NORUN cpp11-migrate -count-only . %t.cpp -- -I %S/Inputs > %T/out
|
||||
// NORUN FileCheck -check-prefix=COUNTONLY -input-file=%T/out %s
|
||||
// RUN: diff %t.cpp %t.base
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
const int N = 6;
|
||||
const int NMinusOne = N - 1;
|
||||
int arr[N] = {1, 2, 3, 4, 5, 6};
|
||||
int (*pArr)[N] = &arr;
|
||||
|
||||
void f() {
|
||||
int sum = 0;
|
||||
// Update the number of correctly converted loops as this test changes:
|
||||
// COUNTONLY: 15 converted
|
||||
// COUNTONLY-NEXT: 0 potentially conflicting
|
||||
// COUNTONLY-NEXT: 0 change(s) rejected
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
sum += arr[i];
|
||||
int k;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr) {
|
||||
// CHECK-NEXT: sum += elem;
|
||||
// CHECK-NEXT: int k;
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("Fibonacci number is %d\n", arr[i]);
|
||||
sum += arr[i] + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int x = arr[i];
|
||||
int y = arr[i] + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: int x = elem;
|
||||
// CHECK-NEXT: int y = elem + 2;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int x = N;
|
||||
x = arr[i];
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: int x = N;
|
||||
// CHECK-NEXT: x = elem;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
arr[i] += 1;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr) {
|
||||
// CHECK-NEXT: elem += 1;
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int x = arr[i] + 2;
|
||||
arr[i] ++;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: int x = elem + 2;
|
||||
// CHECK-NEXT: elem ++;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
arr[i] = 4 + arr[i];
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: elem = 4 + elem;
|
||||
|
||||
for (int i = 0; i < NMinusOne + 1; ++i) {
|
||||
sum += arr[i];
|
||||
}
|
||||
// CHECK: for (auto & elem : arr) {
|
||||
// CHECK-NEXT: sum += elem;
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("Fibonacci number %d has address %p\n", arr[i], &arr[i]);
|
||||
sum += arr[i] + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : arr)
|
||||
// CHECK-NEXT: printf("Fibonacci number %d has address %p\n", elem, &elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
Val teas[N];
|
||||
for (int i = 0; i < N; ++i) {
|
||||
teas[i].g();
|
||||
}
|
||||
// CHECK: for (auto & tea : teas) {
|
||||
// CHECK-NEXT: tea.g();
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
|
||||
struct HasArr {
|
||||
int Arr[N];
|
||||
Val ValArr[N];
|
||||
void implicitThis() {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", Arr[i]);
|
||||
}
|
||||
// CHECK: for (auto & elem : Arr) {
|
||||
// CHECK-NEXT: printf("%d", elem);
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", ValArr[i].x);
|
||||
}
|
||||
// CHECK: for (auto & elem : ValArr) {
|
||||
// CHECK-NEXT: printf("%d", elem.x);
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
|
||||
void explicitThis() {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", this->Arr[i]);
|
||||
}
|
||||
// CHECK: for (auto & elem : this->Arr) {
|
||||
// CHECK-NEXT: printf("%d", elem);
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", this->ValArr[i].x);
|
||||
}
|
||||
// CHECK: for (auto & elem : this->ValArr) {
|
||||
// CHECK-NEXT: printf("%d", elem.x);
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
};
|
||||
|
||||
// Loops whose bounds are value-dependent shold not be converted.
|
||||
template<int N>
|
||||
void dependentExprBound() {
|
||||
for (int i = 0; i < N; ++i)
|
||||
arr[i] = 0;
|
||||
// CHECK: for (int i = 0; i < N; ++i)
|
||||
// CHECK-NEXT: arr[i] = 0;
|
||||
}
|
||||
template void dependentExprBound<20>();
|
||||
|
||||
void memberFunctionPointer() {
|
||||
Val v;
|
||||
void (Val::*mfpArr[N])(void) = { &Val::g };
|
||||
for (int i = 0; i < N; ++i)
|
||||
(v.*mfpArr[i])();
|
||||
// CHECK: for (auto & elem : mfpArr)
|
||||
// CHECK-NEXT: (v.*elem)();
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -risk=risky -- -I %S/Inputs
|
||||
// RUN: FileCheck -check-prefix=RISKY -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void f() {
|
||||
const int N = 5;
|
||||
const int M = 7;
|
||||
int (*pArr)[N];
|
||||
int Arr[N][M];
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < M; ++i) {
|
||||
sum += Arr[0][i];
|
||||
}
|
||||
// CHECK: for (int i = 0; i < M; ++i) {
|
||||
// CHECK-NEXT: sum += Arr[0][i];
|
||||
// CHECK-NEXT: }
|
||||
// RISKY: for (auto & elem : Arr[0]) {
|
||||
// RISKY-NEXT: sum += elem;
|
||||
// RISKY-NEXT: }
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
sum += (*pArr)[i];
|
||||
}
|
||||
// RISKY: for (auto & elem : *pArr) {
|
||||
// RISKY-NEXT: sum += elem;
|
||||
// RISKY-NEXT: }
|
||||
// CHECK: for (int i = 0; i < N; ++i) {
|
||||
// CHECK-NEXT: sum += (*pArr)[i];
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- && FileCheck -input-file=%t.cpp %s
|
||||
|
||||
void f() {
|
||||
const int N = 6;
|
||||
const int M = 8;
|
||||
int arr[N][M];
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int a = 0;
|
||||
int b = arr[i][a];
|
||||
}
|
||||
// CHECK: for (auto & elem : arr) {
|
||||
// CHECK-NEXT: int a = 0;
|
||||
// CHECK-NEXT: int b = elem[a];
|
||||
// CHECK-NEXT: }
|
||||
|
||||
for (int j = 0; j < M; ++j) {
|
||||
int a = 0;
|
||||
int b = arr[a][j];
|
||||
}
|
||||
// CHECK: for (int j = 0; j < M; ++j) {
|
||||
// CHECK-NEXT: int a = 0;
|
||||
// CHECK-NEXT: int b = arr[a][j];
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs -std=c++11
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -risk=risky -- -I %S/Inputs
|
||||
// RUN: FileCheck -check-prefix=RISKY -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void f() {
|
||||
/// begin()/end() - based for loops here:
|
||||
T t;
|
||||
for (T::iterator it = t.begin(), e = t.end(); it != e; ++it) {
|
||||
printf("I found %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : t)
|
||||
// CHECK-NEXT: printf("I found %d\n", elem);
|
||||
|
||||
T *pt;
|
||||
for (T::iterator it = pt->begin(), e = pt->end(); it != e; ++it) {
|
||||
printf("I found %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : *pt)
|
||||
// CHECK-NEXT: printf("I found %d\n", elem);
|
||||
|
||||
S s;
|
||||
for (S::const_iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (elem).x);
|
||||
|
||||
S *ps;
|
||||
for (S::const_iterator it = ps->begin(), e = ps->end(); it != e; ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & p : *ps)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (p).x);
|
||||
|
||||
for (S::const_iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
printf("s has value %d\n", it->x);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: printf("s has value %d\n", elem.x);
|
||||
|
||||
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
it->x = 3;
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: elem.x = 3;
|
||||
|
||||
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
(*it).x = 3;
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: (elem).x = 3;
|
||||
|
||||
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
it->nonConstFun(4, 5);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: elem.nonConstFun(4, 5);
|
||||
|
||||
U u;
|
||||
for (U::iterator it = u.begin(), e = u.end(); it != e; ++it) {
|
||||
printf("s has value %d\n", it->x);
|
||||
}
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: printf("s has value %d\n", elem.x);
|
||||
|
||||
for (U::iterator it = u.begin(), e = u.end(); it != e; ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (elem).x);
|
||||
|
||||
U::iterator A;
|
||||
for (U::iterator i = u.begin(), e = u.end(); i != e; ++i)
|
||||
int k = A->x + i->x;
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: int k = A->x + elem.x;
|
||||
|
||||
dependent<int> v;
|
||||
for (dependent<int>::const_iterator it = v.begin(), e = v.end();
|
||||
it != e; ++it) {
|
||||
printf("Fibonacci number is %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
|
||||
for (dependent<int>::const_iterator it(v.begin()), e = v.end();
|
||||
it != e; ++it) {
|
||||
printf("Fibonacci number is %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
|
||||
doublyDependent<int,int> intmap;
|
||||
for (doublyDependent<int,int>::iterator it = intmap.begin(), e = intmap.end();
|
||||
it != e; ++it) {
|
||||
printf("intmap[%d] = %d", it->first, it->second);
|
||||
}
|
||||
// CHECK: for (auto & elem : intmap)
|
||||
// CHECK-NEXT: printf("intmap[%d] = %d", elem.first, elem.second);
|
||||
|
||||
// PtrSet's iterator dereferences by value so auto & can't be used.
|
||||
{
|
||||
PtrSet<int*> int_ptrs;
|
||||
for (PtrSet<int*>::iterator I = int_ptrs.begin(),
|
||||
E = int_ptrs.end(); I != E; ++I) {
|
||||
// CHECK: for (auto && int_ptr : int_ptrs) {
|
||||
}
|
||||
}
|
||||
|
||||
// This container uses an iterator where the derefence type is a typedef of
|
||||
// a reference type. Make sure non-const auto & is still used. A failure here
|
||||
// means canonical types aren't being tested.
|
||||
{
|
||||
TypedefDerefContainer<int> int_ptrs;
|
||||
for (TypedefDerefContainer<int>::iterator I = int_ptrs.begin(),
|
||||
E = int_ptrs.end(); I != E; ++I) {
|
||||
// CHECK: for (auto & int_ptr : int_ptrs) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Iterators returning an rvalue reference should disqualify the loop from
|
||||
// transformation.
|
||||
RValueDerefContainer<int> container;
|
||||
for (RValueDerefContainer<int>::iterator I = container.begin(),
|
||||
E = container.end(); I != E; ++I) {
|
||||
// CHECK: for (RValueDerefContainer<int>::iterator I = container.begin(),
|
||||
// CHECK-NEXT: E = container.end(); I != E; ++I) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to ensure that an implicit 'this' is picked up as the container.
|
||||
// If member calls are made to 'this' within the loop, the transform becomes
|
||||
// risky as these calls may affect state that affects the loop.
|
||||
class C {
|
||||
public:
|
||||
typedef MutableVal *iterator;
|
||||
typedef const MutableVal *const_iterator;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
void doSomething();
|
||||
void doSomething() const;
|
||||
|
||||
void doLoop() {
|
||||
for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
for (iterator I = C::begin(), E = C::end(); I != E; ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// CHECK: for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// RISKY: for (auto & elem : *this) {
|
||||
doSomething();
|
||||
}
|
||||
for (iterator I = begin(); I != end(); ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
for (iterator I = begin(); I != end(); ++I) {
|
||||
// CHECK: for (iterator I = begin(); I != end(); ++I) {
|
||||
// RISKY: for (auto & elem : *this) {
|
||||
doSomething();
|
||||
}
|
||||
}
|
||||
|
||||
void doLoop() const {
|
||||
for (const_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
for (const_iterator I = C::begin(), E = C::end(); I != E; ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
for (const_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// CHECK: for (const_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// RISKY: for (auto & elem : *this) {
|
||||
doSomething();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class C2 {
|
||||
public:
|
||||
typedef MutableVal *iterator;
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
void doLoop() {
|
||||
// The implicit 'this' will have an Implicit cast to const C2* wrapped
|
||||
// around it. Make sure the replacement still happens.
|
||||
for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
// CHECK: for (auto & elem : *this) {
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cp %t.cpp %t.base
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// See PR15589 for why this test fails.
|
||||
// XFAIL: *
|
||||
|
||||
#include "macro_problem.h"
|
||||
#include "structures.h"
|
||||
|
||||
void side_effect(const myns::MyType &T);
|
||||
|
||||
void f() {
|
||||
TypedefDerefContainer<myns::MyType> container;
|
||||
for (TypedefDerefContainer<myns::MyType>::iterator I = container.begin(),
|
||||
E = container.end(); I != E; ++I) {
|
||||
myns::MyType &alias = *I;
|
||||
// CHECK: myns::MyType &ref = *I;
|
||||
side_effect(ref);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
const int N = 10;
|
||||
|
||||
Val Arr[N];
|
||||
Val &func(Val &);
|
||||
void sideEffect(int);
|
||||
|
||||
void aliasing() {
|
||||
// If the loop container is only used for a declaration of a temporary
|
||||
// variable to hold each element, we can name the new variable for the
|
||||
// converted range-based loop as the temporary variable's name.
|
||||
|
||||
// In the following case, "t" is used as a temporary variable to hold each
|
||||
// element, and thus we consider the name "t" aliased to the loop.
|
||||
// The extra blank braces are left as a placeholder for after the variable
|
||||
// declaration is deleted.
|
||||
for (int i = 0; i < N; ++i) {
|
||||
Val &t = Arr[i]; { }
|
||||
int y = t.x;
|
||||
}
|
||||
// CHECK: for (auto & t : Arr)
|
||||
// CHECK-NOT: Val &{{[a-z_]+}} =
|
||||
// CHECK-NEXT: { }
|
||||
// CHECK-NEXT: int y = t.x;
|
||||
|
||||
// The container was not only used to initialize a temporary loop variable for
|
||||
// the container's elements, so we do not alias the new loop variable.
|
||||
for (int i = 0; i < N; ++i) {
|
||||
Val &t = Arr[i];
|
||||
int y = t.x;
|
||||
int z = Arr[i].x + t.x;
|
||||
}
|
||||
// CHECK: for (auto & elem : Arr)
|
||||
// CHECK-NEXT: Val &t = elem;
|
||||
// CHECK-NEXT: int y = t.x;
|
||||
// CHECK-NEXT: int z = elem.x + t.x;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
Val t = Arr[i];
|
||||
int y = t.x;
|
||||
int z = Arr[i].x + t.x;
|
||||
}
|
||||
// CHECK: for (auto & elem : Arr)
|
||||
// CHECK-NEXT: Val t = elem;
|
||||
// CHECK-NEXT: int y = t.x;
|
||||
// CHECK-NEXT: int z = elem.x + t.x;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
Val &t = func(Arr[i]);
|
||||
int y = t.x;
|
||||
}
|
||||
// CHECK: for (auto & elem : Arr)
|
||||
// CHECK-NEXT: Val &t = func(elem);
|
||||
// CHECK-NEXT: int y = t.x;
|
||||
|
||||
int IntArr[N];
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
if (int alias = IntArr[i]) {
|
||||
sideEffect(alias);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto alias : IntArr)
|
||||
// CHECK-NEXT: if (alias) {
|
||||
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
while (int alias = IntArr[i]) {
|
||||
sideEffect(alias);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto alias : IntArr)
|
||||
// CHECK-NEXT: while (alias) {
|
||||
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
switch (int alias = IntArr[i]) {
|
||||
default:
|
||||
sideEffect(alias);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto alias : IntArr)
|
||||
// CHECK-NEXT: switch (alias) {
|
||||
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
for (int alias = IntArr[i]; alias < N; ++alias) {
|
||||
sideEffect(alias);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto alias : IntArr)
|
||||
// CHECK-NEXT: for (; alias < N; ++alias) {
|
||||
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
for (unsigned j = 0; int alias = IntArr[i]; ++j) {
|
||||
sideEffect(alias);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto alias : IntArr)
|
||||
// CHECK-NEXT: for (unsigned j = 0; alias; ++j) {
|
||||
}
|
||||
|
||||
void refs_and_vals() {
|
||||
// The following tests check that the transform correctly preserves the
|
||||
// reference or value qualifiers of the aliased variable. That is, if the
|
||||
// variable was declared as a value, the loop variable will be declared as a
|
||||
// value and vice versa for references.
|
||||
|
||||
S s;
|
||||
const S s_const = s;
|
||||
|
||||
for (S::const_iterator it = s_const.begin(); it != s_const.end(); ++it) {
|
||||
MutableVal alias = *it; { }
|
||||
alias.x = 0;
|
||||
}
|
||||
// CHECK: for (auto alias : s_const)
|
||||
// CHECK-NOT: MutableVal {{[a-z_]+}} =
|
||||
// CHECK-NEXT: { }
|
||||
// CHECK-NEXT: alias.x = 0;
|
||||
|
||||
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
MutableVal alias = *it; { }
|
||||
alias.x = 0;
|
||||
}
|
||||
// CHECK: for (auto alias : s)
|
||||
// CHECK-NOT: MutableVal {{[a-z_]+}} =
|
||||
// CHECK-NEXT: { }
|
||||
// CHECK-NEXT: alias.x = 0;
|
||||
|
||||
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
|
||||
MutableVal &alias = *it; { }
|
||||
alias.x = 0;
|
||||
}
|
||||
// CHECK: for (auto & alias : s)
|
||||
// CHECK-NOT: MutableVal &{{[a-z_]+}} =
|
||||
// CHECK-NEXT: { }
|
||||
// CHECK-NEXT: alias.x = 0;
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
#define MAX(a,b) (a > b) ? a : b
|
||||
#define DEF 5
|
||||
|
||||
const int N = 10;
|
||||
int nums[N];
|
||||
int sum = 0;
|
||||
|
||||
namespace ns {
|
||||
struct st {
|
||||
int x;
|
||||
};
|
||||
}
|
||||
|
||||
void sameNames() {
|
||||
int num = 0;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("Fibonacci number is %d\n", nums[i]);
|
||||
sum += nums[i] + 2 + num;
|
||||
(void) nums[i];
|
||||
}
|
||||
// CHECK: for (auto & nums_i : nums)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", nums_i);
|
||||
// CHECK-NEXT: sum += nums_i + 2 + num;
|
||||
// CHECK-NOT: (void) num;
|
||||
}
|
||||
|
||||
void macroConflict() {
|
||||
S MAXs;
|
||||
for (S::const_iterator it = MAXs.begin(), e = MAXs.end(); it != e; ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
printf("Max of 3 and 5: %d\n", MAX(3,5));
|
||||
}
|
||||
// CHECK: for (auto & MAXs_it : MAXs)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (MAXs_it).x);
|
||||
// CHECK-NEXT: printf("Max of 3 and 5: %d\n", MAX(3,5));
|
||||
|
||||
T DEFs;
|
||||
for (T::iterator it = DEFs.begin(), e = DEFs.end(); it != e; ++it) {
|
||||
if (*it == DEF) {
|
||||
printf("I found %d\n", *it);
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto & DEFs_it : DEFs)
|
||||
// CHECK-NEXT: if (DEFs_it == DEF) {
|
||||
// CHECK-NEXT: printf("I found %d\n", DEFs_it);
|
||||
}
|
||||
|
||||
void keywordConflict() {
|
||||
T ints;
|
||||
for (T::iterator it = ints.begin(), e = ints.end(); it != e; ++it) {
|
||||
*it = 5;
|
||||
}
|
||||
// CHECK: for (auto & ints_it : ints)
|
||||
// CHECK-NEXT: ints_it = 5;
|
||||
|
||||
U __FUNCTION__s;
|
||||
for (U::iterator it = __FUNCTION__s.begin(), e = __FUNCTION__s.end();
|
||||
it != e; ++it) {
|
||||
int __FUNCTION__s_it = (*it).x + 2;
|
||||
}
|
||||
// CHECK: for (auto & __FUNCTION__s_elem : __FUNCTION__s)
|
||||
// CHECK-NEXT: int __FUNCTION__s_it = (__FUNCTION__s_elem).x + 2;
|
||||
}
|
||||
|
||||
void typeConflict() {
|
||||
T Vals;
|
||||
// Using the name "Val", although it is the name of an existing struct, is
|
||||
// safe in this loop since it will only exist within this scope.
|
||||
for (T::iterator it = Vals.begin(), e = Vals.end(); it != e; ++it) {
|
||||
}
|
||||
// CHECK: for (auto & Val : Vals)
|
||||
|
||||
// We cannot use the name "Val" in this loop since there is a reference to
|
||||
// it in the body of the loop.
|
||||
for (T::iterator it = Vals.begin(), e = Vals.end(); it != e; ++it) {
|
||||
*it = sizeof(Val);
|
||||
}
|
||||
// CHECK: for (auto & Vals_it : Vals)
|
||||
// CHECK-NEXT: Vals_it = sizeof(Val);
|
||||
|
||||
typedef struct Val TD;
|
||||
U TDs;
|
||||
// Naming the variable "TD" within this loop is safe because the typedef
|
||||
// was never used within the loop.
|
||||
for (U::iterator it = TDs.begin(), e = TDs.end(); it != e; ++it) {
|
||||
}
|
||||
// CHECK: for (auto & TD : TDs)
|
||||
|
||||
// "TD" cannot be used in this loop since the typedef is being used.
|
||||
for (U::iterator it = TDs.begin(), e = TDs.end(); it != e; ++it) {
|
||||
TD V;
|
||||
V.x = 5;
|
||||
}
|
||||
// CHECK: for (auto & TDs_it : TDs)
|
||||
// CHECK-NEXT: TD V;
|
||||
// CHECK-NEXT: V.x = 5;
|
||||
|
||||
using ns::st;
|
||||
T sts;
|
||||
for (T::iterator it = sts.begin(), e = sts.end(); it != e; ++it) {
|
||||
*it = sizeof(st);
|
||||
}
|
||||
// CHECK: for (auto & sts_it : sts)
|
||||
// CHECK-NEXT: sts_it = sizeof(st);
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
|
||||
S s;
|
||||
T t;
|
||||
U u;
|
||||
|
||||
struct BadBeginEnd : T {
|
||||
iterator notBegin();
|
||||
iterator notEnd();
|
||||
};
|
||||
|
||||
void notBeginOrEnd() {
|
||||
BadBeginEnd Bad;
|
||||
for (T::iterator i = Bad.notBegin(), e = Bad.end(); i != e; ++i)
|
||||
int k = *i;
|
||||
|
||||
for (T::iterator i = Bad.begin(), e = Bad.notEnd(); i != e; ++i)
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
void badLoopShapes() {
|
||||
for (T::iterator i = t.begin(), e = t.end(), f = e; i != e; ++i)
|
||||
int k = *i;
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; )
|
||||
int k = *i;
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); ; ++i)
|
||||
int k = *i;
|
||||
|
||||
T::iterator outsideI;
|
||||
T::iterator outsideE;
|
||||
|
||||
for (; outsideI != outsideE ; ++outsideI)
|
||||
int k = *outsideI;
|
||||
}
|
||||
|
||||
void iteratorArrayMix() {
|
||||
int lower;
|
||||
const int N = 6;
|
||||
for (T::iterator i = t.begin(), e = t.end(); lower < N; ++i)
|
||||
int k = *i;
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); lower < N; ++lower)
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
struct ExtraConstructor : T::iterator {
|
||||
ExtraConstructor(T::iterator, int);
|
||||
explicit ExtraConstructor(T::iterator);
|
||||
};
|
||||
|
||||
void badConstructor() {
|
||||
for (T::iterator i = ExtraConstructor(t.begin(), 0), e = t.end();
|
||||
i != e; ++i)
|
||||
int k = *i;
|
||||
for (T::iterator i = ExtraConstructor(t.begin()), e = t.end(); i != e; ++i)
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
void iteratorMemberUsed() {
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
i.x = *i;
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
int k = i.x + *i;
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
int k = e.x + *i;
|
||||
}
|
||||
|
||||
void iteratorMethodCalled() {
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
i.insert(3);
|
||||
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
if (i != i)
|
||||
int k = 3;
|
||||
}
|
||||
|
||||
void iteratorOperatorCalled() {
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
int k = *(++i);
|
||||
|
||||
for (S::iterator i = s.begin(), e = s.end(); i != e; ++i)
|
||||
MutableVal k = *(++i);
|
||||
}
|
||||
|
||||
void differentContainers() {
|
||||
T other;
|
||||
for (T::iterator i = t.begin(), e = other.end(); i != e; ++i)
|
||||
int k = *i;
|
||||
|
||||
for (T::iterator i = other.begin(), e = t.end(); i != e; ++i)
|
||||
int k = *i;
|
||||
|
||||
S otherS;
|
||||
for (S::iterator i = s.begin(), e = otherS.end(); i != e; ++i)
|
||||
MutableVal k = *i;
|
||||
|
||||
for (S::iterator i = otherS.begin(), e = s.end(); i != e; ++i)
|
||||
MutableVal k = *i;
|
||||
}
|
||||
|
||||
void wrongIterators() {
|
||||
T::iterator other;
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != other; ++i)
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
struct EvilArrow : U {
|
||||
// Please, no one ever write code like this.
|
||||
U* operator->();
|
||||
};
|
||||
|
||||
void differentMemberAccessTypes() {
|
||||
EvilArrow A;
|
||||
for (EvilArrow::iterator i = A.begin(), e = A->end(); i != e; ++i)
|
||||
Val k = *i;
|
||||
for (EvilArrow::iterator i = A->begin(), e = A.end(); i != e; ++i)
|
||||
Val k = *i;
|
||||
}
|
||||
|
||||
void f(const T::iterator &it, int);
|
||||
void f(const T &it, int);
|
||||
void g(T &it, int);
|
||||
|
||||
void iteratorPassedToFunction() {
|
||||
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
f(i, *i);
|
||||
}
|
||||
|
||||
// FIXME: Disallow this except for containers passed by value and/or const
|
||||
// reference. Or maybe this is correct enough for any container?
|
||||
void containerPassedToFunction() {
|
||||
// for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
// f(t, *i);
|
||||
// for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
|
||||
// g(t, *i);
|
||||
}
|
||||
|
||||
// FIXME: These tests can be removed if this tool ever does enough analysis to
|
||||
// decide that this is a safe transformation.
|
||||
// Until then, we don't want it applied.
|
||||
void iteratorDefinedOutside() {
|
||||
T::iterator theEnd = t.end();
|
||||
for (T::iterator i = t.begin(); i != theEnd; ++i)
|
||||
int k = *i;
|
||||
|
||||
T::iterator theBegin = t.begin();
|
||||
for (T::iterator e = t.end(); theBegin != e; ++theBegin)
|
||||
int k = *theBegin;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert -risk=safe %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
|
||||
S s;
|
||||
T t;
|
||||
U u;
|
||||
|
||||
void multipleEnd() {
|
||||
for (S::iterator i = s.begin(); i != s.end(); ++i)
|
||||
MutableVal k = *i;
|
||||
|
||||
for (T::iterator i = t.begin(); i != t.end(); ++i)
|
||||
int k = *i;
|
||||
|
||||
for (U::iterator i = u.begin(); i != u.end(); ++i)
|
||||
Val k = *i;
|
||||
}
|
||||
|
||||
void f(X);
|
||||
void f(S);
|
||||
void f(T);
|
||||
|
||||
void complexContainer() {
|
||||
X x;
|
||||
for (S::iterator i = x.s.begin(), e = x.s.end(); i != e; ++i) {
|
||||
f(x);
|
||||
MutableVal k = *i;
|
||||
}
|
||||
|
||||
for (T::iterator i = x.t.begin(), e = x.t.end(); i != e; ++i) {
|
||||
f(x);
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
for (S::iterator i = x.s.begin(), e = x.s.end(); i != e; ++i) {
|
||||
f(x.s);
|
||||
MutableVal k = *i;
|
||||
}
|
||||
|
||||
for (T::iterator i = x.t.begin(), e = x.t.end(); i != e; ++i) {
|
||||
f(x.t);
|
||||
int k = *i;
|
||||
}
|
||||
|
||||
for (S::iterator i = x.getS().begin(), e = x.getS().end(); i != e; ++i) {
|
||||
f(x.getS());
|
||||
MutableVal k = *i;
|
||||
}
|
||||
|
||||
X exes[5];
|
||||
int index = 0;
|
||||
|
||||
for (S::iterator i = exes[index].getS().begin(),
|
||||
e = exes[index].getS().end(); i != e; ++i) {
|
||||
index++;
|
||||
MutableVal k = *i;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
|
||||
const int N = 6;
|
||||
dependent<int> v;
|
||||
dependent<int> *pv;
|
||||
|
||||
int sum = 0;
|
||||
|
||||
// Checks to see that non-const member functions are not called on the container
|
||||
// object.
|
||||
// These could be conceivably allowed with a lower required confidence level.
|
||||
void memberFunctionCalled() {
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
sum += v[i];
|
||||
v.foo();
|
||||
}
|
||||
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
sum += v[i];
|
||||
dependent<int>::iterator it = v.begin();
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
|
||||
const int N = 6;
|
||||
dependent<int> v;
|
||||
dependent<int> *pv;
|
||||
|
||||
transparent<dependent<int> > cv;
|
||||
int sum = 0;
|
||||
|
||||
// Checks for the index start and end:
|
||||
void indexStartAndEnd() {
|
||||
for (int i = 0; i < v.size() + 1; ++i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; i < v.size() - 1; ++i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 1; i < v.size(); ++i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 1; i < v.size(); ++i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; ; ++i)
|
||||
sum += (*pv)[i];
|
||||
}
|
||||
|
||||
// Checks for invalid increment steps:
|
||||
void increment() {
|
||||
for (int i = 0; i < v.size(); --i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; i < v.size(); i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; i < v.size();)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; i < v.size(); i += 2)
|
||||
sum ++;
|
||||
}
|
||||
|
||||
// Checks to make sure that the index isn't used outside of the container:
|
||||
void indexUse() {
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
v[i] += 1 + i;
|
||||
}
|
||||
|
||||
// Checks for incorrect loop variables.
|
||||
void mixedVariables() {
|
||||
int badIndex;
|
||||
for (int i = 0; badIndex < v.size(); ++i)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; i < v.size(); ++badIndex)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; badIndex < v.size(); ++badIndex)
|
||||
sum += v[i];
|
||||
|
||||
for (int i = 0; badIndex < v.size(); ++badIndex)
|
||||
sum += v[badIndex];
|
||||
}
|
||||
|
||||
// Checks for an array indexed in addition to the container.
|
||||
void multipleArrays() {
|
||||
int badArr[N];
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
sum += v[i] + badArr[i];
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
sum += badArr[i];
|
||||
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
int k = badArr[i];
|
||||
sum += k + 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
int k = badArr[i];
|
||||
sum += v[i] + k;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for multiple containers being indexed container.
|
||||
void multipleContainers() {
|
||||
dependent<int> badArr;
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
sum += v[i] + badArr[i];
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
sum += badArr[i];
|
||||
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
int k = badArr[i];
|
||||
sum += k + 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
int k = badArr[i];
|
||||
sum += v[i] + k;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to make sure that dereferenced pointers-to-containers behave nicely
|
||||
void derefContainer() {
|
||||
// Note the dependent<T>::operator*() returns another dependent<T>.
|
||||
// This test makes sure that we don't allow an arbitrary number of *'s.
|
||||
for (int i = 0; i < pv->size(); ++i)
|
||||
sum += (**pv).at(i);
|
||||
|
||||
for (int i = 0; i < pv->size(); ++i)
|
||||
sum += (**pv)[i];
|
||||
}
|
||||
|
||||
void wrongEnd() {
|
||||
int bad;
|
||||
for (int i = 0, e = v.size(); i < bad; ++i)
|
||||
sum += v[i];
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/negative-header.h > \
|
||||
// RUN: %T/negative-header.h
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs/
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: FileCheck -input-file=%T/negative-header.h %S/Inputs/negative-header.h
|
||||
|
||||
#include "negative-header.h"
|
||||
#include "structures.h"
|
||||
|
||||
// Single FileCheck line to make sure that no loops are converted.
|
||||
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
|
||||
|
||||
const int N = 6;
|
||||
int arr[N] = {1, 2, 3, 4, 5, 6};
|
||||
int (*pArr)[N] = &arr;
|
||||
int sum = 0;
|
||||
|
||||
// Checks for the index start and end:
|
||||
void indexStartAndEnd() {
|
||||
for (int i = 0; i < N + 1; ++i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; i < N - 1; ++i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 1; i < N; ++i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 1; i < N; ++i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; ; ++i)
|
||||
sum += (*pArr)[i];
|
||||
}
|
||||
|
||||
// Checks for invalid increment steps:
|
||||
void increment() {
|
||||
for (int i = 0; i < N; --i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; i < N; i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; i < N;)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; i < N; i += 2)
|
||||
sum ++;
|
||||
}
|
||||
|
||||
// Checks to make sure that the index isn't used outside of the array:
|
||||
void indexUse() {
|
||||
for (int i = 0; i < N; ++i)
|
||||
arr[i] += 1 + i;
|
||||
}
|
||||
|
||||
// Check for loops that don't mention arrays
|
||||
void noArray() {
|
||||
for (int i = 0; i < N; ++i)
|
||||
sum += i;
|
||||
|
||||
for (int i = 0; i < N; ++i) { }
|
||||
|
||||
for (int i = 0; i < N; ++i) ;
|
||||
}
|
||||
|
||||
// Checks for incorrect loop variables.
|
||||
void mixedVariables() {
|
||||
int badIndex;
|
||||
for (int i = 0; badIndex < N; ++i)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; i < N; ++badIndex)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; badIndex < N; ++badIndex)
|
||||
sum += arr[i];
|
||||
|
||||
for (int i = 0; badIndex < N; ++badIndex)
|
||||
sum += arr[badIndex];
|
||||
}
|
||||
|
||||
// Checks for multiple arrays indexed.
|
||||
void multipleArrays() {
|
||||
int badArr[N];
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
sum += arr[i] + badArr[i];
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int k = badArr[i];
|
||||
sum += arr[i] + k;
|
||||
}
|
||||
}
|
||||
|
||||
struct HasArr {
|
||||
int Arr[N];
|
||||
Val ValArr[N];
|
||||
};
|
||||
|
||||
struct HasIndirectArr {
|
||||
HasArr HA;
|
||||
void implicitThis() {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", HA.Arr[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", HA.ValArr[i].x);
|
||||
}
|
||||
}
|
||||
|
||||
void explicitThis() {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", this->HA.Arr[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("%d", this->HA.ValArr[i].x);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void f() {
|
||||
const int N = 10;
|
||||
const int M = 15;
|
||||
Val Arr[N];
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int j = 0; j < N; ++j) {
|
||||
int k = Arr[i].x + Arr[j].x;
|
||||
// The repeat is there to allow FileCheck to make sure the two variable
|
||||
// names aren't the same.
|
||||
int l = Arr[i].x + Arr[j].x;
|
||||
}
|
||||
}
|
||||
// CHECK: for (auto & elem : Arr)
|
||||
// CHECK-NEXT: for (auto & Arr_j : Arr)
|
||||
// CHECK-NEXT: int k = elem.x + Arr_j.x;
|
||||
// CHECK-NOT: int l = elem.x + elem.x;
|
||||
|
||||
Val Nest[N][M];
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int j = 0; j < M; ++j) {
|
||||
printf("Got item %d", Nest[i][j].x);
|
||||
}
|
||||
}
|
||||
// The inner loop is also convertible, but doesn't need to be converted
|
||||
// immediately. Update this test when that changes!
|
||||
// CHECK: for (auto & elem : Nest)
|
||||
// CHECK-NEXT: for (int j = 0; j < M; ++j)
|
||||
// CHECK-NEXT: printf("Got item %d", elem[j].x);
|
||||
|
||||
// Note that the order of M and N are switched for this test.
|
||||
for (int j = 0; j < M; ++j) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
printf("Got item %d", Nest[i][j].x);
|
||||
}
|
||||
}
|
||||
// CHECK-NOT: for (auto & {{[a-zA-Z_]+}} : Nest[i])
|
||||
// CHECK: for (int j = 0; j < M; ++j)
|
||||
// CHECK-NEXT: for (auto & elem : Nest)
|
||||
// CHECK-NEXT: printf("Got item %d", elem[j].x);
|
||||
Nested<T> NestT;
|
||||
for (Nested<T>::iterator I = NestT.begin(), E = NestT.end(); I != E; ++I) {
|
||||
for (T::iterator TI = (*I).begin(), TE = (*I).end(); TI != TE; ++TI) {
|
||||
printf("%d", *TI);
|
||||
}
|
||||
}
|
||||
// The inner loop is also convertible, but doesn't need to be converted
|
||||
// immediately. Update this test when that changes!
|
||||
// CHECK: for (auto & elem : NestT) {
|
||||
// CHECK-NEXT: for (T::iterator TI = (elem).begin(), TE = (elem).end(); TI != TE; ++TI) {
|
||||
// CHECK-NEXT: printf("%d", *TI);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: not cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
void valid() {
|
||||
const int arr[5];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
sum += arr[i];
|
||||
}
|
||||
}
|
||||
void hasSyntaxError = 3;
|
||||
// CHECK: void valid() {
|
||||
// CHECK-NEXT: const int arr[5];
|
||||
// CHECK-NEXT: int sum = 0;
|
||||
// CHECK-NEXT: for (int i = 0; i < 5; ++i) {
|
||||
// CHECK-NEXT: sum += arr[i];
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: void hasSyntaxError = 3;
|
||||
@@ -1,66 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
#include "structures.h"
|
||||
|
||||
const int N = 6;
|
||||
dependent<int> v;
|
||||
dependent<int> *pv;
|
||||
|
||||
transparent<dependent<int> > cv;
|
||||
|
||||
void f() {
|
||||
int sum = 0;
|
||||
for (int i = 0, e = v.size(); i < e; ++i) {
|
||||
printf("Fibonacci number is %d\n", v[i]);
|
||||
sum += v[i] + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
for (int i = 0, e = v.size(); i < e; ++i) {
|
||||
printf("Fibonacci number is %d\n", v.at(i));
|
||||
sum += v.at(i) + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
for (int i = 0, e = pv->size(); i < e; ++i) {
|
||||
printf("Fibonacci number is %d\n", pv->at(i));
|
||||
sum += pv->at(i) + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : *pv)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
// This test will fail if size() isn't called repeatedly, since it
|
||||
// returns unsigned int, and 0 is deduced to be signed int.
|
||||
// FIXME: Insert the necessary explicit conversion, or write out the types
|
||||
// explicitly.
|
||||
for (int i = 0; i < pv->size(); ++i) {
|
||||
printf("Fibonacci number is %d\n", (*pv).at(i));
|
||||
sum += (*pv)[i] + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : *pv)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
|
||||
for (int i = 0; i < cv->size(); ++i) {
|
||||
printf("Fibonacci number is %d\n", cv->at(i));
|
||||
sum += cv->at(i) + 2;
|
||||
}
|
||||
// CHECK: for (auto & elem : *cv)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
// CHECK-NEXT: sum += elem + 2;
|
||||
}
|
||||
|
||||
// Check for loops that don't mention containers
|
||||
void noContainer() {
|
||||
for (auto i = 0; i < v.size(); ++i) { }
|
||||
// CHECK: for (auto & elem : v) { }
|
||||
|
||||
for (auto i = 0; i < v.size(); ++i) ;
|
||||
// CHECK: for (auto & elem : v) ;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -loop-convert %t.cpp -- -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void complexContainer() {
|
||||
X exes[5];
|
||||
int index = 0;
|
||||
|
||||
for (S::iterator i = exes[index].getS().begin(), e = exes[index].getS().end(); i != e; ++i) {
|
||||
MutableVal k = *i;
|
||||
MutableVal j = *i;
|
||||
}
|
||||
// CHECK: for (auto & elem : exes[index].getS())
|
||||
// CHECK-NEXT: MutableVal k = elem;
|
||||
// CHECK-NEXT: MutableVal j = elem;
|
||||
}
|
||||
|
||||
void f() {
|
||||
/// begin()/end() - based for loops here:
|
||||
T t;
|
||||
for (T::iterator it = t.begin(); it != t.end(); ++it) {
|
||||
printf("I found %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : t)
|
||||
// CHECK-NEXT: printf("I found %d\n", elem);
|
||||
|
||||
T *pt;
|
||||
for (T::iterator it = pt->begin(); it != pt->end(); ++it) {
|
||||
printf("I found %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : *pt)
|
||||
// CHECK-NEXT: printf("I found %d\n", elem);
|
||||
|
||||
S s;
|
||||
for (S::const_iterator it = s.begin(); it != s.end(); ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (elem).x);
|
||||
|
||||
S *ps;
|
||||
for (S::const_iterator it = ps->begin(); it != ps->end(); ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & p : *ps)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (p).x);
|
||||
|
||||
for (S::const_iterator it = s.begin(); it != s.end(); ++it) {
|
||||
printf("s has value %d\n", it->x);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: printf("s has value %d\n", elem.x);
|
||||
|
||||
for (S::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
it->x = 3;
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: elem.x = 3;
|
||||
|
||||
for (S::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
(*it).x = 3;
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: (elem).x = 3;
|
||||
|
||||
for (S::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
it->nonConstFun(4, 5);
|
||||
}
|
||||
// CHECK: for (auto & elem : s)
|
||||
// CHECK-NEXT: elem.nonConstFun(4, 5);
|
||||
|
||||
U u;
|
||||
for (U::iterator it = u.begin(); it != u.end(); ++it) {
|
||||
printf("s has value %d\n", it->x);
|
||||
}
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: printf("s has value %d\n", elem.x);
|
||||
|
||||
for (U::iterator it = u.begin(); it != u.end(); ++it) {
|
||||
printf("s has value %d\n", (*it).x);
|
||||
}
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: printf("s has value %d\n", (elem).x);
|
||||
|
||||
U::iterator A;
|
||||
for (U::iterator i = u.begin(); i != u.end(); ++i)
|
||||
int k = A->x + i->x;
|
||||
// CHECK: for (auto & elem : u)
|
||||
// CHECK-NEXT: int k = A->x + elem.x;
|
||||
|
||||
dependent<int> v;
|
||||
for (dependent<int>::const_iterator it = v.begin();
|
||||
it != v.end(); ++it) {
|
||||
printf("Fibonacci number is %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
|
||||
for (dependent<int>::const_iterator it(v.begin());
|
||||
it != v.end(); ++it) {
|
||||
printf("Fibonacci number is %d\n", *it);
|
||||
}
|
||||
// CHECK: for (auto & elem : v)
|
||||
// CHECK-NEXT: printf("Fibonacci number is %d\n", elem);
|
||||
|
||||
doublyDependent<int,int> intmap;
|
||||
for (doublyDependent<int,int>::iterator it = intmap.begin();
|
||||
it != intmap.end(); ++it) {
|
||||
printf("intmap[%d] = %d", it->first, it->second);
|
||||
}
|
||||
// CHECK: for (auto & elem : intmap)
|
||||
// CHECK-NEXT: printf("intmap[%d] = %d", elem.first, elem.second);
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
//===-----------------------------------------------------------*- C++ -*--===//
|
||||
//
|
||||
// This file contains a shell implementation of a standard container with
|
||||
// iterators. This shell is targeted at supporting the container interfaces
|
||||
// recognized by cpp11-migrate's use-auto transformation. It requires the
|
||||
// preprocessor to parameterize the name of the container, and allows the
|
||||
// preprocessor to parameterize various mechanisms used in the implementation
|
||||
// of the container / iterator.
|
||||
//
|
||||
// Variations for how iterator types are presented:
|
||||
// * Typedef (array, deque, forward_list, list, vector)
|
||||
// * Nested class (map, multimap, set, multiset)
|
||||
// * Using declaration {unordered_} X {map, multimap, set, multiset}
|
||||
//
|
||||
// Variations for how container types are presented:
|
||||
// * Defined directly in namespace std
|
||||
// * Imported into namespace std with using declarations (a la libc++).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CONTAINER
|
||||
#error You must define CONTAINER to the name of the desired container.
|
||||
#endif
|
||||
|
||||
// If the test code needs multiple containers, only define our helpers once.
|
||||
#ifndef TEST_STD_CONTAINER_HELPERS
|
||||
#define TEST_STD_CONTAINER_HELPERS
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename T, int i>
|
||||
struct iterator_wrapper {
|
||||
iterator_wrapper() {}
|
||||
|
||||
// These are required for tests using iteration statements.
|
||||
bool operator!=(const iterator_wrapper<T, i>&) { return false; }
|
||||
iterator_wrapper& operator++() { return *this; }
|
||||
typename T::value_type operator*() { return typename T::value_type(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class iterator_provider {
|
||||
public:
|
||||
class iterator {
|
||||
public:
|
||||
iterator() {}
|
||||
iterator(const iterator&) {}
|
||||
};
|
||||
class const_iterator {
|
||||
public:
|
||||
const_iterator(int i=0) {}
|
||||
const_iterator(const iterator &) {}
|
||||
const_iterator(const const_iterator &) {}
|
||||
operator iterator() { return iterator(); }
|
||||
};
|
||||
class reverse_iterator {};
|
||||
class const_reverse_iterator {};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#endif // TEST_STD_CONTAINER_HELPERS
|
||||
|
||||
namespace std {
|
||||
|
||||
#if USE_INLINE_NAMESPACE
|
||||
namespace _1 {
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
class CONTAINER
|
||||
#if USE_BASE_CLASS_ITERATORS
|
||||
: internal::iterator_provider<CONTAINER<T> >
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
|
||||
#if USE_BASE_CLASS_ITERATORS
|
||||
using typename internal::iterator_provider<CONTAINER<T> >::iterator;
|
||||
using typename internal::iterator_provider<CONTAINER<T> >::const_iterator;
|
||||
using typename internal::iterator_provider<CONTAINER<T> >::reverse_iterator;
|
||||
using typename internal::iterator_provider<CONTAINER<T> >::const_reverse_iterator;
|
||||
#elif USE_INNER_CLASS_ITERATORS
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
class reverse_iterator {};
|
||||
class const_reverse_iterator {};
|
||||
#else
|
||||
typedef T value_type;
|
||||
typedef typename internal::iterator_wrapper<CONTAINER<T>, 0> iterator;
|
||||
typedef typename internal::iterator_wrapper<CONTAINER<T>, 1> const_iterator;
|
||||
typedef typename internal::iterator_wrapper<CONTAINER<T>, 3> reverse_iterator;
|
||||
typedef typename internal::iterator_wrapper<CONTAINER<T>, 2> const_reverse_iterator;
|
||||
#endif
|
||||
|
||||
// Every class requires these functions.
|
||||
CONTAINER() {}
|
||||
|
||||
iterator begin() { return iterator(); }
|
||||
iterator end() { return iterator(); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(); }
|
||||
const_iterator end() const { return const_iterator(); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(); }
|
||||
reverse_iterator rend() { return reverse_iterator(); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(); }
|
||||
|
||||
template <typename K>
|
||||
iterator find(const K &Key) { return iterator(); }
|
||||
};
|
||||
|
||||
#if USE_INLINE_NAMESPACE
|
||||
} // namespace _1
|
||||
using _1::CONTAINER;
|
||||
#endif
|
||||
|
||||
} // namespace std
|
||||
@@ -1,123 +0,0 @@
|
||||
// This file contains basic positive tests for the use-auto transform's ability
|
||||
// to replace standard iterators. Variables considered:
|
||||
// * All std container names
|
||||
// * All std iterator names
|
||||
// * Different patterns of defining iterators and containers
|
||||
//
|
||||
// // The most basic test.
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=array -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
//
|
||||
// Test variations on how the container and its iterators might be defined.
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=array \
|
||||
// RUN: -DUSE_INLINE_NAMESPACE -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=array \
|
||||
// RUN: -DUSE_BASE_CLASS_ITERATORS -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=array \
|
||||
// RUN: -DUSE_INNER_CLASS_ITERATORS -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
//
|
||||
// Test all of the other container names in a basic configuration.
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=deque -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=forward_list -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=list -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=vector -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=map -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=multimap -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=set -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=multiset -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=unordered_map -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=unordered_multimap -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=unordered_set -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=unordered_multiset -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=queue -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=priority_queue -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
//
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -DCONTAINER=stack -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#ifndef CONTAINER
|
||||
#error You must define CONTAINER to the name of the container for testing.
|
||||
#endif
|
||||
|
||||
#include "test_std_container.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
{
|
||||
std::CONTAINER<int> C;
|
||||
std::CONTAINER<int>::iterator I = C.begin();
|
||||
// CHECK: auto I = C.begin();
|
||||
}
|
||||
{
|
||||
std::CONTAINER<int> C;
|
||||
std::CONTAINER<int>::reverse_iterator I = C.rbegin();
|
||||
// CHECK: auto I = C.rbegin();
|
||||
}
|
||||
{
|
||||
const std::CONTAINER<int> C;
|
||||
std::CONTAINER<int>::const_iterator I = C.begin();
|
||||
// CHECK: auto I = C.begin();
|
||||
}
|
||||
{
|
||||
const std::CONTAINER<int> C;
|
||||
std::CONTAINER<int>::const_reverse_iterator I = C.rbegin();
|
||||
// CHECK: auto I = C.rbegin();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11 -I %S/Inputs
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
#define CONTAINER array
|
||||
#include "test_std_container.h"
|
||||
#undef CONTAINER
|
||||
|
||||
#define CONTAINER vector
|
||||
#include "test_std_container.h"
|
||||
#undef CONTAINER
|
||||
|
||||
#define CONTAINER unordered_map
|
||||
#define USE_BASE_CLASS_ITERATORS 1
|
||||
#include "test_std_container.h"
|
||||
#undef USE_BASE_CLASS_ITERATORS
|
||||
#undef CONTAINER
|
||||
|
||||
typedef std::vector<int>::iterator int_iterator;
|
||||
|
||||
namespace foo {
|
||||
template <typename T>
|
||||
class vector {
|
||||
public:
|
||||
class iterator {};
|
||||
|
||||
iterator begin() { return iterator(); }
|
||||
};
|
||||
} // namespace foo
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::vector<int> Vec;
|
||||
// CHECK: std::vector<int> Vec;
|
||||
|
||||
std::unordered_map<int> Map;
|
||||
// CHECK: std::unordered_map<int> Map;
|
||||
|
||||
// Types with more sugar should work. Types with less should not.
|
||||
{
|
||||
int_iterator more_sugar = Vec.begin();
|
||||
// CHECK: auto more_sugar = Vec.begin();
|
||||
|
||||
internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin();
|
||||
// CHECK: internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin();
|
||||
}
|
||||
|
||||
// Initialization from initializer lists isn't allowed. Using 'auto'
|
||||
// would result in std::initializer_list being deduced for the type.
|
||||
{
|
||||
std::unordered_map<int>::iterator I{Map.begin()};
|
||||
// CHECK: std::unordered_map<int>::iterator I{Map.begin()};
|
||||
|
||||
std::unordered_map<int>::iterator I2 = {Map.begin()};
|
||||
// CHECK: std::unordered_map<int>::iterator I2 = {Map.begin()};
|
||||
}
|
||||
|
||||
// Various forms of construction. Default constructors and constructors with
|
||||
// all-default parameters shouldn't get transformed. Construction from other
|
||||
// types is also not allowed.
|
||||
{
|
||||
std::unordered_map<int>::iterator copy(Map.begin());
|
||||
// CHECK: auto copy(Map.begin());
|
||||
|
||||
std::unordered_map<int>::iterator def;
|
||||
// CHECK: std::unordered_map<int>::iterator def;
|
||||
|
||||
// const_iterator has no default constructor, just one that has >0 params
|
||||
// with defaults.
|
||||
std::unordered_map<int>::const_iterator constI;
|
||||
// CHECK: std::unordered_map<int>::const_iterator constI;
|
||||
|
||||
// Uses iterator_provider::const_iterator's conversion constructor.
|
||||
|
||||
std::unordered_map<int>::const_iterator constI2 = def;
|
||||
// CHECK: std::unordered_map<int>::const_iterator constI2 = def;
|
||||
|
||||
std::unordered_map<int>::const_iterator constI3(def);
|
||||
// CHECK: std::unordered_map<int>::const_iterator constI3(def);
|
||||
|
||||
// Explicit use of conversion constructor
|
||||
|
||||
std::unordered_map<int>::const_iterator constI4 = std::unordered_map<int>::const_iterator(def);
|
||||
// CHECK: auto constI4 = std::unordered_map<int>::const_iterator(def);
|
||||
|
||||
// Uses iterator_provider::iterator's const_iterator conversion operator.
|
||||
|
||||
std::unordered_map<int>::iterator I = constI;
|
||||
// CHECK: std::unordered_map<int>::iterator I = constI;
|
||||
|
||||
std::unordered_map<int>::iterator I2(constI);
|
||||
// CHECK: std::unordered_map<int>::iterator I2(constI);
|
||||
}
|
||||
|
||||
// Weird cases of pointers and references to iterators are not transformed.
|
||||
{
|
||||
int_iterator I = Vec.begin();
|
||||
|
||||
int_iterator *IPtr = &I;
|
||||
// CHECK: int_iterator *IPtr = &I;
|
||||
|
||||
int_iterator &IRef = I;
|
||||
// CHECK: int_iterator &IRef = I;
|
||||
}
|
||||
|
||||
{
|
||||
// Variable declarations in iteration statements.
|
||||
for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) {
|
||||
// CHECK: for (auto I = Vec.begin(); I != Vec.end(); ++I) {
|
||||
}
|
||||
|
||||
// Range-based for loops.
|
||||
std::array<std::vector<int>::iterator> iter_arr;
|
||||
for (std::vector<int>::iterator I: iter_arr) {
|
||||
// CHECK: for (auto I: iter_arr) {
|
||||
}
|
||||
|
||||
// Test with init-declarator-list.
|
||||
for (int_iterator I = Vec.begin(),
|
||||
E = Vec.end(); I != E; ++I) {
|
||||
// CHECK: for (auto I = Vec.begin(),
|
||||
// CHECK-NEXT: E = Vec.end(); I != E; ++I) {
|
||||
}
|
||||
}
|
||||
|
||||
// Only std containers should be changed.
|
||||
{
|
||||
using namespace foo;
|
||||
vector<int> foo_vec;
|
||||
vector<int>::iterator I = foo_vec.begin();
|
||||
// CHECK: vector<int>::iterator I = foo_vec.begin();
|
||||
}
|
||||
|
||||
// Ensure using directives don't interfere with replacement.
|
||||
{
|
||||
using namespace std;
|
||||
vector<int> std_vec;
|
||||
vector<int>::iterator I = std_vec.begin();
|
||||
// CHECK: auto I = std_vec.begin();
|
||||
}
|
||||
|
||||
// Make sure references and cv qualifiers don't get removed (i.e. replaced
|
||||
// with just 'auto').
|
||||
{
|
||||
const auto & I = Vec.begin();
|
||||
// CHECK: const auto & I = Vec.begin();
|
||||
|
||||
auto && I2 = Vec.begin();
|
||||
// CHECK: auto && I2 = Vec.begin();
|
||||
}
|
||||
|
||||
// Passing a string as an argument to introduce a temporary object
|
||||
// that will create an expression with cleanups. Bugzilla: 15550
|
||||
{
|
||||
std::unordered_map<int> MapFind;
|
||||
std::unordered_map<int>::iterator I = MapFind.find("foo");
|
||||
// CHECK: auto I = MapFind.find("foo");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -std=c++11
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
|
||||
class MyType {
|
||||
};
|
||||
|
||||
class MyDerivedType : public MyType {
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
MyType *a = new MyType();
|
||||
// CHECK: auto a = new MyType();
|
||||
|
||||
static MyType *a_static = new MyType();
|
||||
// CHECK: static auto a_static = new MyType();
|
||||
|
||||
MyType *b = new MyDerivedType();
|
||||
// CHECK: MyType *b = new MyDerivedType();
|
||||
|
||||
void *c = new MyType();
|
||||
// CHECK: void *c = new MyType();
|
||||
|
||||
// CV-qualifier tests.
|
||||
//
|
||||
// NOTE : the form "type const" is expected here because of a deficiency in
|
||||
// TypeLoc where CV qualifiers are not considered part of the type location
|
||||
// info. That is, all that is being replaced in each case is "MyType *" and
|
||||
// not "MyType * const".
|
||||
{
|
||||
static MyType * const d_static = new MyType();
|
||||
// CHECK: static auto const d_static = new MyType();
|
||||
|
||||
MyType * const d3 = new MyType();
|
||||
// CHECK: auto const d3 = new MyType();
|
||||
|
||||
MyType * volatile d4 = new MyType();
|
||||
// CHECK: auto volatile d4 = new MyType();
|
||||
}
|
||||
|
||||
int (**func)(int, int) = new (int(*[5])(int,int));
|
||||
// CHECK: int (**func)(int, int) = new (int(*[5])(int,int));
|
||||
|
||||
int *e = new int[5];
|
||||
// CHECK: auto e = new int[5];
|
||||
|
||||
MyType *f(new MyType);
|
||||
// CHECK: auto f(new MyType);
|
||||
|
||||
MyType *g{new MyType};
|
||||
// CHECK: MyType *g{new MyType};
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-auto %t.cpp -- -std=c++11
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// XFAIL: *
|
||||
|
||||
// None of these tests can pass right now because TypeLoc information where CV
|
||||
// qualifiers are concerned is not reliable/available.
|
||||
|
||||
class MyType {
|
||||
};
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
const MyType *d = new MyType();
|
||||
// CHECK: const auto *d = new MyType();
|
||||
|
||||
volatile MyType *d2 = new MyType();
|
||||
// CHECK: volatile auto *d2 = new MyType();
|
||||
|
||||
const MyType * volatile e = new MyType();
|
||||
// CHECK: const auto * volatile d = new MyType();
|
||||
|
||||
volatile MyType * const f = new MyType();
|
||||
// CHECK: volatile auto * const d2 = new MyType();
|
||||
|
||||
const MyType *d5 = new const MyType();
|
||||
// CHECK: auto d5 = new const MyType();
|
||||
|
||||
volatile MyType *d6 = new volatile MyType();
|
||||
// CHECK: auto d6 = new volatile MyType();
|
||||
|
||||
const MyType * const d7 = new const MyType();
|
||||
// CHECK: const auto d7 = new const MyType();
|
||||
|
||||
volatile MyType * volatile d8 = new volatile MyType();
|
||||
// CHECK: volatile auto d8 = new volatile MyType();
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
int *global_p = 0;
|
||||
// CHECK: int *global_p = 0;
|
||||
@@ -1,290 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/basic.h > %T/basic.h
|
||||
// RUN: cpp11-migrate -use-nullptr %t.cpp -- -I %S
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: FileCheck -input-file=%T/basic.h %S/Inputs/basic.h
|
||||
|
||||
#include "Inputs/basic.h"
|
||||
|
||||
const unsigned int g_null = 0;
|
||||
#define NULL 0
|
||||
// CHECK: #define NULL 0
|
||||
|
||||
void test_assignment() {
|
||||
int *p1 = 0;
|
||||
// CHECK: int *p1 = nullptr;
|
||||
p1 = 0;
|
||||
// CHECK: p1 = nullptr;
|
||||
|
||||
int *p2 = NULL;
|
||||
// CHECK: int *p2 = nullptr;
|
||||
|
||||
p2 = p1;
|
||||
// CHECK: p2 = p1;
|
||||
|
||||
const int null = 0;
|
||||
int *p3 = null;
|
||||
// CHECK: int *p3 = nullptr;
|
||||
|
||||
p3 = NULL;
|
||||
// CHECK: p3 = nullptr;
|
||||
|
||||
int *p4 = p3;
|
||||
// CHECK: int *p4 = p3;
|
||||
|
||||
p4 = null;
|
||||
// CHECK: p4 = nullptr;
|
||||
|
||||
int i1 = 0;
|
||||
// CHECK: int i1 = 0;
|
||||
|
||||
int i2 = NULL;
|
||||
// CHECK: int i2 = NULL;
|
||||
|
||||
int i3 = null;
|
||||
// CHECK: int i3 = null;
|
||||
|
||||
int *p5, *p6, *p7;
|
||||
p5 = p6 = p7 = NULL;
|
||||
// CHECK: p5 = p6 = p7 = nullptr;
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
Foo(int *p = NULL) : m_p1(p) {}
|
||||
// CHECK: Foo(int *p = nullptr) : m_p1(p) {}
|
||||
|
||||
void bar(int *p = 0) {}
|
||||
// CHECK: void bar(int *p = nullptr) {}
|
||||
|
||||
void baz(int i = 0) {}
|
||||
// CHECK: void baz(int i = 0) {}
|
||||
|
||||
int *m_p1;
|
||||
static int *m_p2;
|
||||
};
|
||||
|
||||
int *Foo::m_p2 = NULL;
|
||||
// CHECK: int *Foo::m_p2 = nullptr;
|
||||
|
||||
template <typename T>
|
||||
struct Bar {
|
||||
Bar(T *p) : m_p(p) {
|
||||
m_p = static_cast<T*>(NULL);
|
||||
// CHECK: m_p = static_cast<T*>(nullptr);
|
||||
|
||||
m_p = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
|
||||
// CHECK: m_p = static_cast<T*>(nullptr);
|
||||
|
||||
m_p = static_cast<T*>(p ? p : static_cast<void*>(g_null));
|
||||
// CHECK: m_p = static_cast<T*>(p ? p : static_cast<void*>(nullptr));
|
||||
|
||||
T *p2 = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
|
||||
// CHECK: T *p2 = static_cast<T*>(nullptr);
|
||||
|
||||
m_p = NULL;
|
||||
// CHECK: m_p = nullptr;
|
||||
|
||||
int i = static_cast<int>(0.f);
|
||||
// CHECK: int i = static_cast<int>(0.f);
|
||||
T *i2 = static_cast<int>(0.f);
|
||||
// CHECK: T *i2 = nullptr;
|
||||
}
|
||||
|
||||
T *m_p;
|
||||
};
|
||||
|
||||
struct Baz {
|
||||
Baz() : i(0) {}
|
||||
int i;
|
||||
};
|
||||
|
||||
void test_cxx_cases() {
|
||||
Foo f(g_null);
|
||||
// CHECK: Foo f(nullptr);
|
||||
|
||||
f.bar(NULL);
|
||||
// CHECK: f.bar(nullptr);
|
||||
|
||||
f.baz(g_null);
|
||||
// CHECK: f.baz(g_null);
|
||||
|
||||
f.m_p1 = 0;
|
||||
// CHECK: f.m_p1 = nullptr;
|
||||
|
||||
Bar<int> b(g_null);
|
||||
// CHECK: Bar<int> b(nullptr);
|
||||
|
||||
Baz b2;
|
||||
int Baz::*memptr(0);
|
||||
// CHECK: int Baz::*memptr(nullptr);
|
||||
|
||||
memptr = 0;
|
||||
// CHECK: memptr = nullptr;
|
||||
}
|
||||
|
||||
void test_function_default_param1(void *p = 0);
|
||||
// CHECK: void test_function_default_param1(void *p = nullptr);
|
||||
|
||||
void test_function_default_param2(void *p = NULL);
|
||||
// CHECK: void test_function_default_param2(void *p = nullptr);
|
||||
|
||||
void test_function_default_param3(void *p = g_null);
|
||||
// CHECK: void test_function_default_param3(void *p = nullptr);
|
||||
|
||||
void test_function(int *p) {}
|
||||
// CHECK: void test_function(int *p) {}
|
||||
|
||||
void test_function_no_ptr_param(int i) {}
|
||||
|
||||
void test_function_call() {
|
||||
test_function(0);
|
||||
// CHECK: test_function(nullptr);
|
||||
|
||||
test_function(NULL);
|
||||
// CHECK: test_function(nullptr);
|
||||
|
||||
test_function(g_null);
|
||||
// CHECK: test_function(nullptr);
|
||||
|
||||
test_function_no_ptr_param(0);
|
||||
// CHECK: test_function_no_ptr_param(0);
|
||||
}
|
||||
|
||||
char *test_function_return1() {
|
||||
return 0;
|
||||
// CHECK: return nullptr;
|
||||
}
|
||||
|
||||
void *test_function_return2() {
|
||||
return NULL;
|
||||
// CHECK: return nullptr;
|
||||
}
|
||||
|
||||
long *test_function_return3() {
|
||||
return g_null;
|
||||
// CHECK: return nullptr;
|
||||
}
|
||||
|
||||
int test_function_return4() {
|
||||
return 0;
|
||||
// CHECK: return 0;
|
||||
}
|
||||
|
||||
int test_function_return5() {
|
||||
return NULL;
|
||||
// CHECK: return NULL;
|
||||
}
|
||||
|
||||
int test_function_return6() {
|
||||
return g_null;
|
||||
// CHECK: return g_null;
|
||||
}
|
||||
|
||||
int *test_function_return_cast1() {
|
||||
return(int)0;
|
||||
// CHECK: return nullptr;
|
||||
}
|
||||
|
||||
int *test_function_return_cast2() {
|
||||
#define RET return
|
||||
RET(int)0;
|
||||
// CHECK: RET nullptr;
|
||||
#undef RET
|
||||
}
|
||||
|
||||
// Test parentheses expressions resulting in a nullptr.
|
||||
int *test_parentheses_expression1() {
|
||||
return(0);
|
||||
// CHECK: return(nullptr);
|
||||
}
|
||||
|
||||
int *test_parentheses_expression2() {
|
||||
return(int(0.f));
|
||||
// CHECK: return(nullptr);
|
||||
}
|
||||
|
||||
int *test_nested_parentheses_expression() {
|
||||
return((((0))));
|
||||
// CHECK: return((((nullptr))));
|
||||
}
|
||||
|
||||
void *test_parentheses_explicit_cast() {
|
||||
return(static_cast<void*>(0));
|
||||
// CHECK: return(static_cast<void*>(nullptr));
|
||||
}
|
||||
|
||||
void *test_parentheses_explicit_cast_sequence1() {
|
||||
return(static_cast<void*>(static_cast<int*>((void*)NULL)));
|
||||
// CHECK: return(static_cast<void*>(nullptr));
|
||||
}
|
||||
|
||||
void *test_parentheses_explicit_cast_sequence2() {
|
||||
return(static_cast<void*>(reinterpret_cast<int*>((float*)int(0.f))));
|
||||
// CHECK: return(static_cast<void*>(nullptr));
|
||||
}
|
||||
|
||||
// Test explicit cast expressions resulting in nullptr
|
||||
struct Bam {
|
||||
Bam(int *a) {}
|
||||
Bam(float *a) {}
|
||||
Bam operator=(int *a) { return Bam(a); }
|
||||
Bam operator=(float *a) { return Bam(a); }
|
||||
};
|
||||
|
||||
void ambiguous_function(int *a) {}
|
||||
void ambiguous_function(float *a) {}
|
||||
void const_ambiguous_function(const int *p) {}
|
||||
void const_ambiguous_function(const float *p) {}
|
||||
|
||||
void test_explicit_cast_ambiguous1() {
|
||||
ambiguous_function((int*)0);
|
||||
// CHECK: ambiguous_function((int*)nullptr);
|
||||
}
|
||||
|
||||
void test_explicit_cast_ambiguous2() {
|
||||
ambiguous_function((int*)(0));
|
||||
// CHECK: ambiguous_function((int*)nullptr);
|
||||
}
|
||||
|
||||
void test_explicit_cast_ambiguous3() {
|
||||
ambiguous_function(static_cast<int*>(reinterpret_cast<int*>((float*)0)));
|
||||
// CHECK: ambiguous_function(static_cast<int*>(nullptr));
|
||||
}
|
||||
|
||||
Bam test_explicit_cast_ambiguous4() {
|
||||
return(((int*)(0)));
|
||||
// CHECK: return(((int*)nullptr));
|
||||
}
|
||||
|
||||
void test_explicit_cast_ambiguous5() {
|
||||
// Test for ambiguous overloaded constructors
|
||||
Bam k((int*)(0));
|
||||
// CHECK: Bam k((int*)nullptr);
|
||||
|
||||
// Test for ambiguous overloaded operators
|
||||
k = (int*)0;
|
||||
// CHECK: k = (int*)nullptr;
|
||||
}
|
||||
|
||||
void test_const_pointers_abiguous() {
|
||||
const_ambiguous_function((int*)0);
|
||||
// CHECK: const_ambiguous_function((int*)nullptr);
|
||||
}
|
||||
|
||||
// Test where the implicit cast to null is surrounded by another implict cast
|
||||
// with possible explict casts in-between.
|
||||
void test_const_pointers() {
|
||||
const int *const_p1 = 0;
|
||||
// CHECK: const int *const_p1 = nullptr;
|
||||
const int *const_p2 = NULL;
|
||||
// CHECK: const int *const_p2 = nullptr;
|
||||
const int *const_p3 = (int)0;
|
||||
// CHECK: const int *const_p3 = nullptr;
|
||||
const int *const_p4 = (int)0.0f;
|
||||
// CHECK: const int *const_p4 = nullptr;
|
||||
const int *const_p5 = (int*)0;
|
||||
// CHECK: const int *const_p5 = (int*)nullptr;
|
||||
int *t;
|
||||
const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(0));
|
||||
// CHECK: const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(nullptr));
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-nullptr %t.cpp -- -I %S
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// XFAIL: *
|
||||
|
||||
#define NULL 0
|
||||
|
||||
template <typename T>
|
||||
class A {
|
||||
public:
|
||||
A(T *p = NULL) {}
|
||||
// CHECK: A(T *p = nullptr) {}
|
||||
|
||||
void f() {
|
||||
Ptr = NULL;
|
||||
// CHECK: Ptr = nullptr;
|
||||
}
|
||||
|
||||
T *Ptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T *f2(T *a = NULL) {
|
||||
// CHECK: T *f2(T *a = nullptr) {
|
||||
return a ? a : NULL;
|
||||
// CHECK: return a ? a : nullptr;
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||
// RUN: cpp11-migrate -use-nullptr %t.cpp -- -I %S
|
||||
// RUN: FileCheck -input-file=%t.cpp %s
|
||||
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t2.cpp
|
||||
// RUN: cpp11-migrate -use-nullptr -user-null-macros=MY_NULL %t2.cpp -- -I %S
|
||||
// RUN: FileCheck -check-prefix=USER-SUPPLIED-NULL -input-file=%t2.cpp %s
|
||||
|
||||
#define NULL 0
|
||||
// CHECK: #define NULL 0
|
||||
|
||||
void dummy(int*) {}
|
||||
void side_effect() {}
|
||||
|
||||
#define MACRO_EXPANSION_HAS_NULL \
|
||||
void foo() { \
|
||||
dummy(0); \
|
||||
dummy(NULL); \
|
||||
side_effect(); \
|
||||
}
|
||||
// CHECK: void foo() { \
|
||||
// CHECK-NEXT: dummy(0); \
|
||||
// CHECK-NEXT: dummy(NULL); \
|
||||
|
||||
MACRO_EXPANSION_HAS_NULL;
|
||||
// CHECK: MACRO_EXPANSION_HAS_NULL;
|
||||
#undef MACRO_EXPANSION_HAS_NULL
|
||||
|
||||
|
||||
void test_macro_expansion1() {
|
||||
#define MACRO_EXPANSION_HAS_NULL \
|
||||
dummy(NULL); \
|
||||
side_effect();
|
||||
// CHECK: dummy(NULL); \
|
||||
// CHECK-NEXT: side_effect();
|
||||
|
||||
MACRO_EXPANSION_HAS_NULL;
|
||||
// CHECK: MACRO_EXPANSION_HAS_NULL;
|
||||
|
||||
#undef MACRO_EXPANSION_HAS_NULL
|
||||
}
|
||||
|
||||
// Test macro expansion with cast sequence, PR15572
|
||||
void test_macro_expansion2() {
|
||||
#define MACRO_EXPANSION_HAS_NULL \
|
||||
dummy((int*)0); \
|
||||
side_effect();
|
||||
// CHECK: dummy((int*)0); \
|
||||
// CHECK-NEXT: side_effect();
|
||||
|
||||
MACRO_EXPANSION_HAS_NULL;
|
||||
// CHECK: MACRO_EXPANSION_HAS_NULL;
|
||||
|
||||
#undef MACRO_EXPANSION_HAS_NULL
|
||||
}
|
||||
|
||||
void test_macro_expansion3() {
|
||||
#define MACRO_EXPANSION_HAS_NULL \
|
||||
dummy(NULL); \
|
||||
side_effect();
|
||||
// CHECK: dummy(NULL); \
|
||||
// CHECK-NEXT: side_effect();
|
||||
|
||||
#define OUTER_MACRO \
|
||||
MACRO_EXPANSION_HAS_NULL; \
|
||||
side_effect();
|
||||
|
||||
OUTER_MACRO;
|
||||
// CHECK: OUTER_MACRO;
|
||||
|
||||
#undef OUTER_MACRO
|
||||
#undef MACRO_EXPANSION_HAS_NULL
|
||||
}
|
||||
|
||||
void test_macro_expansion4() {
|
||||
#define MY_NULL NULL
|
||||
int *p = MY_NULL;
|
||||
// CHECK: int *p = MY_NULL;
|
||||
// USER-SUPPLIED-NULL: int *p = nullptr;
|
||||
#undef MY_NULL
|
||||
}
|
||||
|
||||
#define IS_EQ(x, y) if (x != y) return;
|
||||
void test_macro_args() {
|
||||
int i = 0;
|
||||
int *Ptr;
|
||||
|
||||
IS_EQ(static_cast<int*>(0), Ptr);
|
||||
// CHECK: IS_EQ(static_cast<int*>(nullptr), Ptr);
|
||||
IS_EQ(0, Ptr); // literal
|
||||
// CHECK: IS_EQ(nullptr, Ptr);
|
||||
IS_EQ(NULL, Ptr); // macro
|
||||
// CHECK: IS_EQ(nullptr, Ptr);
|
||||
|
||||
// These are ok since the null literal is not spelled within a macro.
|
||||
#define myassert(x) if (!(x)) return;
|
||||
myassert(0 == Ptr);
|
||||
// CHECK: myassert(nullptr == Ptr);
|
||||
myassert(NULL == Ptr);
|
||||
// CHECK: myassert(nullptr == Ptr);
|
||||
|
||||
// These are bad as the null literal is buried in a macro.
|
||||
#define BLAH(X) myassert(0 == (X));
|
||||
// CHECK: #define BLAH(X) myassert(0 == (X));
|
||||
#define BLAH2(X) myassert(NULL == (X));
|
||||
// CHECK: #define BLAH2(X) myassert(NULL == (X));
|
||||
BLAH(Ptr);
|
||||
// CHECK: BLAH(Ptr);
|
||||
BLAH2(Ptr);
|
||||
// CHECK: BLAH2(Ptr);
|
||||
|
||||
// Same as above but testing extra macro expansion.
|
||||
#define EXPECT_NULL(X) IS_EQ(0, X);
|
||||
// CHECK: #define EXPECT_NULL(X) IS_EQ(0, X);
|
||||
#define EXPECT_NULL2(X) IS_EQ(NULL, X);
|
||||
// CHECK: #define EXPECT_NULL2(X) IS_EQ(NULL, X);
|
||||
EXPECT_NULL(Ptr);
|
||||
// CHECK: EXPECT_NULL(Ptr);
|
||||
EXPECT_NULL2(Ptr);
|
||||
// CHECK: EXPECT_NULL2(Ptr);
|
||||
|
||||
// Almost the same as above but now null literal is not in a macro so ok
|
||||
// to transform.
|
||||
#define EQUALS_PTR(X) IS_EQ(X, Ptr);
|
||||
EQUALS_PTR(0);
|
||||
EQUALS_PTR(NULL);
|
||||
|
||||
// Same as above but testing extra macro expansion.
|
||||
#define EQUALS_PTR_I(X) EQUALS_PTR(X)
|
||||
EQUALS_PTR_I(0);
|
||||
// CHECK: EQUALS_PTR_I(nullptr);
|
||||
EQUALS_PTR_I(NULL);
|
||||
// CHECK: EQUALS_PTR_I(nullptr);
|
||||
|
||||
// Ok since null literal not within macro. However, now testing macro
|
||||
// used as arg to another macro.
|
||||
#define decorate(EXPR) side_effect(); EXPR;
|
||||
decorate(IS_EQ(NULL, Ptr));
|
||||
// CHECK: decorate(IS_EQ(nullptr, Ptr));
|
||||
decorate(IS_EQ(0, Ptr));
|
||||
// CHECK: decorate(IS_EQ(nullptr, Ptr));
|
||||
|
||||
// This macro causes a NullToPointer cast to happen where 0 is assigned to z
|
||||
// but the 0 literal cannot be replaced because it is also used as an
|
||||
// integer in the comparison.
|
||||
#define INT_AND_PTR_USE(X) do { int *z = X; if (X == 4) break; } while(false)
|
||||
INT_AND_PTR_USE(0);
|
||||
// CHECK: INT_AND_PTR_USE(0);
|
||||
|
||||
// Both uses of X in this case result in NullToPointer casts so replacement
|
||||
// is possible.
|
||||
#define PTR_AND_PTR_USE(X) do { int *z = X; if (X != z) break; } while(false)
|
||||
PTR_AND_PTR_USE(0);
|
||||
// CHECK: PTR_AND_PTR_USE(nullptr);
|
||||
PTR_AND_PTR_USE(NULL);
|
||||
// CHECK: PTR_AND_PTR_USE(nullptr);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user