Compare commits

..

39 Commits

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,22 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Tools repository
//===----------------------------------------------------------------------===//
Welcome to the repository of extra Clang Tools. This repository holds tools
that are developed as part of the LLVM compiler infrastructure project and the
Clang frontend. These tools are kept in a separate "extra" repository to
allow lighter weight checkouts of the core Clang codebase.
This repository is only intended to be checked out inside of a full LLVM+Clang
tree, and in the 'tools/extra' subdirectory of the Clang checkout.
All discussion regarding Clang, Clang-based tools, and code in this repository
should be held using the standard Clang mailing lists:
http://lists.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/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(tool)
add_subdirectory(Core)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
add_clang_executable(remove-cstr-calls
RemoveCStrCalls.cpp
)
target_link_libraries(remove-cstr-calls
clangEdit clangTooling clangBasic clangAST clangASTMatchers)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
#define myns nsblah
namespace nsblah {
struct MyType {
};
} // namespace nsblah

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
int *global_p = 0;
// CHECK: int *global_p = 0;

View File

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

View File

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

View File

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