Compare commits

..

9 Commits

Author SHA1 Message Date
John Criswell
b094f912b0 Note that a work-around is available.
llvm-svn: 24237
2005-11-08 15:40:22 +00:00
John Criswell
358cb80e7d Add a note about the Solaris configure problem that Tanya's seen.
llvm-svn: 24236
2005-11-08 15:38:11 +00:00
John Criswell
ec29828344 Include the CVS tag for the 1.6 release.
llvm-svn: 24167
2005-11-03 18:26:14 +00:00
John Criswell
1ea1e7ac4d Merge in hack from Chris that discards libg.
llvm-svn: 24166
2005-11-03 15:42:27 +00:00
John Criswell
398d2e2c28 Updated the version number.
Get the UIUC copyright to actually print.

llvm-svn: 24165
2005-11-03 15:25:04 +00:00
John Criswell
072467d8a9 Merged in revision 1.60.
llvm-svn: 24160
2005-11-02 19:38:00 +00:00
John Criswell
39a12ac598 Don't use -q; this option is not available on Solaris grep (and probably
other, traditional UNIX greps).

llvm-svn: 24156
2005-11-02 18:06:28 +00:00
John Criswell
89d3246c03 Mark these as failing on sparc instead of sparcv9.
The configure script no longer tells us that we're configuring for SparcV9
specifically.
2004-06-17-UnorderedCompares may work on SparcV8, but it's experiental
anyway.
2005-02-20-AggregateSAVEEXPR should fail on any Solaris machine, as Solaris
doesn't provide complex number support.

llvm-svn: 24155
2005-11-02 18:05:50 +00:00
CVS to SVN Conversion
53bd0b0175 This commit was manufactured by cvs2svn to create branch 'release_16'.
llvm-svn: 24138
2005-11-01 18:04:06 +00:00
34297 changed files with 417562 additions and 5418100 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,14 +0,0 @@
check_library_exists(edit el_init "" HAVE_LIBEDIT)
add_subdirectory(clang-apply-replacements)
add_subdirectory(clang-modernize)
add_subdirectory(clang-query)
add_subdirectory(clang-tidy)
add_subdirectory(modularize)
add_subdirectory(pp-trace)
add_subdirectory(remove-cstr-calls)
add_subdirectory(tool-template)
# 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: revane@gmail.com
D: clang-modernize

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,42 +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 pp-trace
DIRS := clang-apply-replacements clang-modernize clang-tidy clang-query \
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,23 +0,0 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
asmparser
bitreader
support
mc
)
add_clang_library(clangApplyReplacements
lib/Tooling/ApplyReplacements.cpp
)
target_link_libraries(clangApplyReplacements
clangTooling
clangBasic
clangRewriteFrontend
clangFormat
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
include
)
add_subdirectory(tool)

View File

@@ -1,15 +0,0 @@
##===- clang-apply-replacements/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
DIRS = lib/Tooling tool
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,136 +0,0 @@
//===-- ApplyReplacements.h - Deduplicate and apply replacements -- 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 interface for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
#define LLVM_CLANG_APPLYREPLACEMENTS_H
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/system_error.h"
#include <vector>
#include <string>
namespace clang {
class DiagnosticsEngine;
class Rewriter;
namespace format {
struct FormatStyle;
} // end namespace format
namespace replace {
/// \brief Collection of source ranges.
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements>
TUReplacements;
/// \brief Collection of TranslationUnitReplacement files.
typedef std::vector<std::string> TUReplacementFiles;
/// \brief Map mapping file name to Replacements targeting that file.
typedef llvm::StringMap<std::vector<clang::tooling::Replacement> >
FileToReplacementsMap;
/// \brief Recursively descends through a directory structure rooted at \p
/// Directory and attempts to deserialize *.yaml files as
/// TranslationUnitReplacements. All docs that successfully deserialize are
/// added to \p TUs.
///
/// Directories starting with '.' are ignored during traversal.
///
/// \param[in] Directory Directory to begin search for serialized
/// TranslationUnitReplacements.
/// \param[out] TUs Collection of all found and deserialized
/// TranslationUnitReplacements.
/// \param[out] TURFiles Collection of all TranslationUnitReplacement files
/// found in \c Directory.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns An error_code indicating success or failure in navigating the
/// directory structure.
llvm::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUReplacements &TUs,
TUReplacementFiles &TURFiles,
clang::DiagnosticsEngine &Diagnostics);
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \li true If all changes were applied successfully.
/// \li false If there were conflicts.
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
/// \brief Apply all replacements in \c GroupedReplacements.
///
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
/// to apply.
/// \param[out] Rewrites The results of applying replacements will be applied
/// to this Rewriter.
///
/// \returns \li true If all changes were applied successfully.
/// \li false If a replacement failed to apply.
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
/// \brief Given a collection of Replacements for a single file, produces a list
/// of source ranges that enclose those Replacements.
///
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements from a single file.
///
/// \returns Collection of source ranges that enclose all given Replacements.
/// One range is created for each replacement.
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replacements);
/// \brief Write the contents of \c FileContents to disk. Keys of the map are
/// filenames and values are the new contents for those files.
///
/// \param[in] Rewrites Rewriter containing written files to write to disk.
bool writeFiles(const clang::Rewriter &Rewrites);
/// \brief Delete the replacement files.
///
/// \param[in] Files Replacement files to delete.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns \li true If all files have been deleted successfully.
/// \li false If at least one or more failures occur when deleting
/// files.
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics);
} // end namespace replace
} // end namespace clang
#endif // LLVM_CLANG_APPLYREPLACEMENTS_H

View File

@@ -1,279 +0,0 @@
//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
//
// 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 deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
/// FIXME: Use Diagnostics for output instead of llvm::errs().
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "clang/Lex/Lexer.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/ArrayRef.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;
using namespace clang;
static void eatDiagnostics(const SMDiagnostic &, void *) {}
namespace clang {
namespace replace {
llvm::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUReplacements &TUs,
TUReplacementFiles & TURFiles,
clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
error_code ErrorCode;
for (recursive_directory_iterator I(Directory, ErrorCode), E;
I != E && !ErrorCode; I.increment(ErrorCode)) {
if (filename(I->path())[0] == '.') {
// Indicate not to descend into directories beginning with '.'
I.no_push();
continue;
}
if (extension(I->path()) != ".yaml")
continue;
TURFiles.push_back(I->path());
OwningPtr<MemoryBuffer> Out;
error_code BufferError = MemoryBuffer::getFile(I->path(), Out);
if (BufferError) {
errs() << "Error reading " << I->path() << ": " << BufferError.message()
<< "\n";
continue;
}
yaml::Input YIn(Out->getBuffer(), NULL, &eatDiagnostics);
tooling::TranslationUnitReplacements TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
continue;
}
// Only keep files that properly parse.
TUs.push_back(TU);
}
return ErrorCode;
}
/// \brief Dumps information for a sequence of conflicting Replacements.
///
/// \param[in] File FileEntry for the file the conflicting Replacements are
/// for.
/// \param[in] ConflictingReplacements List of conflicting Replacements.
/// \param[in] SM SourceManager used for reporting.
static void reportConflict(
const FileEntry *File,
const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
SourceManager &SM) {
FileID FID = SM.translateFile(File);
if (FID.isInvalid())
FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
errs() << "The following changes conflict:\n";
for (const tooling::Replacement *I = ConflictingReplacements.begin(),
*E = ConflictingReplacements.end();
I != E; ++I) {
if (I->getLength() == 0) {
errs() << " Insert at " << SM.getLineNumber(FID, I->getOffset()) << ":"
<< SM.getColumnNumber(FID, I->getOffset()) << " "
<< I->getReplacementText() << "\n";
} else {
if (I->getReplacementText().empty())
errs() << " Remove ";
else
errs() << " Replace ";
errs() << SM.getLineNumber(FID, I->getOffset()) << ":"
<< SM.getColumnNumber(FID, I->getOffset()) << "-"
<< SM.getLineNumber(FID, I->getOffset() + I->getLength() - 1)
<< ":"
<< SM.getColumnNumber(FID, I->getOffset() + I->getLength() - 1);
if (I->getReplacementText().empty())
errs() << "\n";
else
errs() << " with \"" << I->getReplacementText() << "\"\n";
}
}
}
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \li true if conflicts were detected
/// \li false if no conflicts were detected
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
SourceManager &SM) {
bool conflictsFound = false;
for (FileToReplacementsMap::iterator I = Replacements.begin(),
E = Replacements.end();
I != E; ++I) {
const FileEntry *Entry = SM.getFileManager().getFile(I->getKey());
if (!Entry) {
errs() << "Described file '" << I->getKey()
<< "' doesn't exist. Ignoring...\n";
continue;
}
std::vector<tooling::Range> Conflicts;
tooling::deduplicate(I->getValue(), Conflicts);
if (Conflicts.empty())
continue;
conflictsFound = true;
errs() << "There are conflicting changes to " << I->getKey() << ":\n";
for (std::vector<tooling::Range>::const_iterator
ConflictI = Conflicts.begin(),
ConflictE = Conflicts.end();
ConflictI != ConflictE; ++ConflictI) {
ArrayRef<tooling::Replacement> ConflictingReplacements(
&I->getValue()[ConflictI->getOffset()], ConflictI->getLength());
reportConflict(Entry, ConflictingReplacements, SM);
}
}
return conflictsFound;
}
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
for (TUReplacements::const_iterator TUI = TUs.begin(), TUE = TUs.end();
TUI != TUE; ++TUI)
for (std::vector<tooling::Replacement>::const_iterator
RI = TUI->Replacements.begin(),
RE = TUI->Replacements.end();
RI != RE; ++RI)
GroupedReplacements[RI->getFilePath()].push_back(*RI);
// Ask clang to deduplicate and report conflicts.
if (deduplicateAndDetectConflicts(GroupedReplacements, SM))
return false;
return true;
}
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites) {
// Apply all changes
//
// FIXME: No longer certain GroupedReplacements is really the best kind of
// data structure for applying replacements. Rewriter certainly doesn't care.
// However, until we nail down the design of ReplacementGroups, might as well
// leave this as is.
for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
E = GroupedReplacements.end();
I != E; ++I) {
if (!tooling::applyAllReplacements(I->getValue(), Rewrites))
return false;
}
return true;
}
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replaces) {
RangeVector ChangedRanges;
// Generate the new ranges from the replacements.
//
// NOTE: This is O(n^2) in the number of replacements. If this starts to
// become a problem inline shiftedCodePosition() here and do shifts in a
// single run through this loop.
for (std::vector<clang::tooling::Replacement>::const_iterator
I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
const tooling::Replacement &R = *I;
unsigned Offset = tooling::shiftedCodePosition(Replaces, R.getOffset());
unsigned Length = R.getReplacementText().size();
ChangedRanges.push_back(tooling::Range(Offset, Length));
}
return ChangedRanges;
}
bool writeFiles(const clang::Rewriter &Rewrites) {
for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
const char *FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::string ErrorInfo;
llvm::raw_fd_ostream FileStream(FileName, ErrorInfo);
if (!ErrorInfo.empty()) {
errs() << "Warning: Could not write to " << FileName << "\n";
continue;
}
BufferI->second.write(FileStream);
}
return true;
}
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics) {
bool Success = true;
for (TUReplacementFiles::const_iterator I = Files.begin(), E = Files.end();
I != E; ++I) {
error_code Error = llvm::sys::fs::remove(*I);
if (Error) {
Success = false;
// FIXME: Use Diagnostics for outputting errors.
errs() << "Error deleting file: " << *I << "\n";
errs() << Error.message() << "\n";
errs() << "Please delete the file manually\n";
}
}
return Success;
}
} // end namespace replace
} // end namespace clang

View File

@@ -1,14 +0,0 @@
##===- clang-apply-replacements/lib/Tooling/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 := clangApplyReplacements
include $(CLANG_LEVEL)/../../Makefile.config
include $(CLANG_LEVEL)/Makefile
CPP.Flags += -I$(PROJ_SRC_DIR)/../../include

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
asmparser
bitreader
support
mc
)
add_clang_executable(clang-apply-replacements
ClangApplyReplacementsMain.cpp
)
target_link_libraries(clang-apply-replacements
clangApplyReplacements
)
install(TARGETS clang-apply-replacements
RUNTIME DESTINATION bin)

View File

@@ -1,285 +0,0 @@
//===-- ClangApplyReplacementsMain.cpp - Main file for the 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 provides the main function for the
/// clang-apply-replacements tool.
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
using namespace clang;
using namespace clang::replace;
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
static cl::OptionCategory FormattingCategory("Formatting Options");
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
"merging/replacing."),
cl::init(false));
// Update this list of options to show in -help as new options are added.
// Should add even those options marked as 'Hidden'. Any option not listed
// here will get marked 'ReallyHidden' so they don't appear in any -help text.
const char *OptionsToShow[] = { "help", "version",
"remove-change-desc-files", "format",
"style-config", "style" };
static cl::opt<bool> DoFormat(
"format",
cl::desc("Enable formatting of code changed by applying replacements.\n"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
// Helper object to remove the TUReplacement files (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
public:
ScopedFileRemover(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics)
: TURFiles(Files), Diag(Diagnostics) {}
~ScopedFileRemover() {
deleteReplacementFiles(TURFiles, Diag);
}
private:
const TUReplacementFiles &TURFiles;
clang::DiagnosticsEngine &Diag;
};
void printVersion() {
outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \li true if all replacements were applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty()) return true;
if (!tooling::applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
FileManager &Files = SM.getFileManager();
StringRef FileName = Replacements.begin()->getFilePath();
const clang::FileEntry *Entry = Files.getFile(FileName);
assert(Entry && "Expected an existing file");
FileID ID = SM.translateFile(Entry);
assert(!ID.isInvalid() && "Expected a valid FileID");
const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
Result = std::string(Buffer->begin(), Buffer->end());
return true;
}
/// \brief Apply \c Replacements and return the new file contents.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \li true if all replacements applied successfully.
/// \li false if at least one replacement failed to apply.
bool applyReplacements(const std::vector<tooling::Replacement> &Replacements,
std::string &Result,
DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(Replacements, Rewrites, Result);
}
/// \brief Apply code formatting to all places where replacements were made.
///
/// \pre !Replacements.empty().
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements that were made to the file. Provided
/// to indicate where changes were made.
/// \param[in] FileData The contents of the file \b after \c Replacements have
/// been applied.
/// \param[out] FormattedFileData The contents of the file after reformatting.
/// \param[in] FormatStyle Style to apply.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \li true if reformatting replacements were all successfully
/// applied.
/// \li false if at least one reformatting replacement failed to apply.
bool applyFormatting(const std::vector<tooling::Replacement> &Replacements,
const StringRef FileData,
std::string &FormattedFileData,
const format::FormatStyle &FormatStyle,
DiagnosticsEngine &Diagnostics) {
assert(!Replacements.empty() && "Need at least one replacement");
RangeVector Ranges = calculateChangedRanges(Replacements);
StringRef FileName = Replacements.begin()->getFilePath();
tooling::Replacements R =
format::reformat(FormatStyle, FileData, Ranges, FileName);
// FIXME: Remove this copy when tooling::Replacements is implemented as a
// vector instead of a set.
std::vector<tooling::Replacement> FormattingReplacements;
std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
if (FormattingReplacements.empty()) {
FormattedFileData = FileData;
return true;
}
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
SM.overrideFileContents(Files.getFile(FileName),
llvm::MemoryBuffer::getMemBufferCopy(FileData));
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
}
int main(int argc, char **argv) {
// Only include our options in -help output.
StringMap<cl::Option*> OptMap;
cl::getRegisteredOptions(OptMap);
const char **EndOpts = OptionsToShow + array_lengthof(OptionsToShow);
for (StringMap<cl::Option *>::iterator I = OptMap.begin(), E = OptMap.end();
I != E; ++I) {
if (std::find(OptionsToShow, EndOpts, I->getKey()) == EndOpts)
I->getValue()->setHiddenFlag(cl::ReallyHidden);
}
cl::SetVersionPrinter(&printVersion);
cl::ParseCommandLineOptions(argc, argv);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
// Determine a formatting style from options.
format::FormatStyle FormatStyle;
if (DoFormat)
FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig);
TUReplacements TUs;
TUReplacementFiles TURFiles;
error_code ErrorCode =
collectReplacementsFromDirectory(Directory, TUs, TURFiles, Diagnostics);
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
<< "': " << ErrorCode.message() << "\n";
return 1;
}
// Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
// command line option) when exiting main().
OwningPtr<ScopedFileRemover> Remover;
if (RemoveTUReplacementFiles)
Remover.reset(new ScopedFileRemover(TURFiles, Diagnostics));
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
return 1;
Rewriter ReplacementsRewriter(SM, LangOptions());
for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
E = GroupedReplacements.end();
I != E; ++I) {
std::string NewFileData;
// This shouldn't happen but if a file somehow has no replacements skip to
// next file.
if (I->getValue().empty())
continue;
if (!applyReplacements(I->getValue(), NewFileData, Diagnostics)) {
errs() << "Failed to apply replacements to " << I->getKey() << "\n";
continue;
}
// Apply formatting if requested.
if (DoFormat && !applyFormatting(I->getValue(), NewFileData, NewFileData,
FormatStyle, Diagnostics)) {
errs() << "Failed to apply reformatting replacements for " << I->getKey()
<< "\n";
continue;
}
// Write new file to disk
std::string ErrorInfo;
llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo);
if (!ErrorInfo.empty()) {
llvm::errs() << "Could not open " << I->getKey() << " for writing\n";
continue;
}
FileStream << NewFileData;
}
return 0;
}

View File

@@ -1,28 +0,0 @@
##===- clang-apply-replacements/tool/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
TOOLNAME = clang-apply-replacements
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
SOURCES = ClangApplyReplacementsMain.cpp
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
USEDLIBS = clangApplyReplacements.a clangFormat.a 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
CPP.Flags += -I$(PROJ_SRC_DIR)/../include

View File

@@ -1,84 +0,0 @@
//===-- AddOverride/AddOverride.cpp - add C++11 override ------------------===//
//
// 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/CompilerInstance.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
namespace cl = llvm::cl;
static cl::opt<bool> DetectMacros(
"override-macros",
cl::desc("Detect and use macros that expand to the 'override' keyword."),
cl::cat(TransformsOptionsCategory));
int AddOverrideTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool AddOverrideTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
AddOverrideFixer Fixer(AcceptedChanges, DetectMacros,
/*Owner=*/ *this);
Finder.addMatcher(makeCandidateForOverrideAttrMatcher(), &Fixer);
// Make Fixer available to handleBeginSource().
this->Fixer = &Fixer;
if (int result = AddOverrideTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
bool AddOverrideTransform::handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) {
assert(Fixer != NULL && "Fixer must be set");
Fixer->setPreprocessor(CI.getPreprocessor());
return Transform::handleBeginSource(CI, Filename);
}
struct AddOverrideFactory : TransformFactory {
AddOverrideFactory() {
// if detecting macros is enabled, do not impose requirements on the
// compiler. It is assumed that the macros use is "C++11-aware", meaning it
// won't expand to override if the compiler doesn't support the specifier.
if (!DetectMacros) {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 7);
Since.Icc = Version(14);
Since.Msvc = Version(8);
}
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new AddOverrideTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<AddOverrideFactory>
X("add-override", "Make use of override specifier where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int AddOverrideTransformAnchorSource = 0;

View File

@@ -1,44 +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 CLANG_MODERNIZE_ADD_OVERRIDE_H
#define CLANG_MODERNIZE_ADD_OVERRIDE_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h"
class AddOverrideFixer;
/// \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(const TransformOptions &Options)
: Transform("AddOverride", Options) {}
/// \see Transform::run().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
virtual bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) LLVM_OVERRIDE;
private:
AddOverrideFixer *Fixer;
};
#endif // CLANG_MODERNIZE_ADD_OVERRIDE_H

View File

@@ -1,101 +0,0 @@
//===-- AddOverride/AddOverrideActions.cpp - add C++11 override -----------===//
//
// 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 "Core/Transform.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"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
namespace {
SourceLocation
backwardSkipWhitespacesAndComments(const SourceManager &SM,
const clang::ASTContext &Context,
SourceLocation Loc) {
for (;;) {
do {
Loc = Loc.getLocWithOffset(-1);
} while (isWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()));
Token Tok;
SourceLocation Beginning =
Lexer::GetBeginningOfToken(Loc, SM, Context.getLangOpts());
const bool Invalid =
Lexer::getRawToken(Beginning, Tok, SM, Context.getLangOpts());
assert(!Invalid && "Expected a valid token.");
if (Invalid || Tok.getKind() != tok::comment)
return Loc.getLocWithOffset(1);
}
}
} // end anonymous namespace
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");
if (const FunctionDecl *TemplateMethod = M->getTemplateInstantiationPattern())
M = cast<CXXMethodDecl>(TemplateMethod);
if (!Owner.isFileModifiable(SM, M->getLocStart()))
return;
// First check that there isn't already an override attribute.
if (M->hasAttr<OverrideAttr>())
return;
// FIXME: Pure methods are not supported yet as it is difficult to track down
// the location of '= 0'.
if (M->isPure())
return;
if (M->getParent()->hasAnyDependentBases())
return;
SourceLocation StartLoc;
if (M->hasInlineBody()) {
// Insert the override specifier before the function body.
StartLoc = backwardSkipWhitespacesAndComments(SM, *Result.Context,
M->getBody()->getLocStart());
} else {
StartLoc = SM.getSpellingLoc(M->getLocEnd());
StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOptions());
}
std::string ReplacementText = " override";
if (DetectMacros) {
assert(PP != 0 && "No access to Preprocessor object for macro detection");
clang::TokenValue Tokens[] = { PP->getIdentifierInfo("override") };
llvm::StringRef MacroName = PP->getLastMacroWithSpelling(StartLoc, Tokens);
if (!MacroName.empty())
ReplacementText = (" " + MacroName).str();
}
Owner.addReplacementForCurrentTU(
tooling::Replacement(SM, StartLoc, 0, ReplacementText));
++AcceptedChanges;
}

View File

@@ -1,45 +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 CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H
#define CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
/// \brief The callback to be used for add-override migration matchers.
///
class AddOverrideFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
AddOverrideFixer(unsigned &AcceptedChanges, bool DetectMacros,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), DetectMacros(DetectMacros),
Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
void setPreprocessor(clang::Preprocessor &PP) { this->PP = &PP; }
private:
clang::Preprocessor *PP;
unsigned &AcceptedChanges;
bool DetectMacros;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H

View File

@@ -1,29 +0,0 @@
//===-- AddOverride/AddOverrideMatchers.cpp - C++11 override --------------===//
//
// 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,28 +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 CLANG_MODERNIZE_ADD_OVERRIDE_MATCHERS_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_ADD_OVERRIDE_MATCHERS_H

View File

@@ -1,7 +0,0 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${ClangReplaceLocation}
)
add_subdirectory(tool)
add_subdirectory(Core)

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(modernizeCore
ReplacementHandling.cpp
Transforms.cpp
Transform.cpp
IncludeExcludeInfo.cpp
PerfSupport.cpp
IncludeDirectives.cpp
)
target_link_libraries(modernizeCore
clangFormat
clangTooling
clangBasic
clangASTMatchers
clangRewriteFrontend
)

View File

@@ -1,59 +0,0 @@
//===-- Core/CustomMatchers.h - Perf measurement helpers -----------*- 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 custom matchers to be used by different
/// transforms that requier the same matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_CUSTOMMATCHERS_H
#define CLANG_MODERNIZE_CUSTOMMATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
namespace clang {
namespace ast_matchers {
/// \brief Matches declarations whose declaration context is the C++ standard
/// library namespace \c std.
///
/// Note that inline namespaces are silently ignored during the lookup since
/// both libstdc++ and libc++ are known to use them for versioning purposes.
///
/// Given
/// \code
/// namespace ns {
/// struct my_type {};
/// using namespace std;
/// }
///
/// using std::vector;
/// using ns::my_type;
/// using ns::list;
/// \endcode
/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
/// matches "using std::vector" and "using ns::list".
AST_MATCHER(Decl, isFromStdNamespace) {
const DeclContext *D = Node.getDeclContext();
while (D->isInlineNamespace())
D = D->getParent();
if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
return false;
const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
return Info && Info->isStr("std");
}
} // namespace ast_matchers
} // namespace clang
#endif // CLANG_MODERNIZE_CUSTOMMATCHERS_H

View File

@@ -1,474 +0,0 @@
//===-- Core/IncludeDirectives.cpp - Include directives handling ----------===//
//
// 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 defines the IncludeDirectives class that helps with
/// detecting and modifying \#include directives.
///
//===----------------------------------------------------------------------===//
#include "IncludeDirectives.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include <stack>
using namespace clang;
using namespace clang::tooling;
using llvm::StringRef;
/// \brief PPCallbacks that fills-in the include information in the given
/// \c IncludeDirectives.
class IncludeDirectivesPPCallback : public clang::PPCallbacks {
// Struct helping the detection of header guards in the various callbacks
struct GuardDetection {
GuardDetection(FileID FID)
: FID(FID), Count(0), TheMacro(0), CountAtEndif(0) {}
FileID FID;
// count for relevant preprocessor directives
unsigned Count;
// the macro that is tested in the top most ifndef for the header guard
// (e.g: GUARD_H)
const IdentifierInfo *TheMacro;
// the hash locations of #ifndef, #define, #endif
SourceLocation IfndefLoc, DefineLoc, EndifLoc;
// the value of Count once the #endif is reached
unsigned CountAtEndif;
/// \brief Check that with all the information gathered if this is a
/// potential header guard.
///
/// Meaning a top-most \#ifndef has been found, followed by a define and the
/// last preprocessor directive was the terminating \#endif.
///
/// FIXME: accept the \#if !defined identifier form too.
bool isPotentialHeaderGuard() const {
return Count == CountAtEndif && DefineLoc.isValid();
}
};
public:
IncludeDirectivesPPCallback(IncludeDirectives *Self) : Self(Self), Guard(0) {}
private:
virtual ~IncludeDirectivesPPCallback() {}
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported) LLVM_OVERRIDE {
SourceManager &SM = Self->Sources;
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(HashLoc));
assert(FE && "Valid file expected.");
IncludeDirectives::Entry E(HashLoc, File, IsAngled);
Self->FileToEntries[FE].push_back(E);
Self->IncludeAsWrittenToLocationsMap[FileName].push_back(HashLoc);
}
// Keep track of the current file in the stack
virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID) {
SourceManager &SM = Self->Sources;
switch (Reason) {
case EnterFile:
Files.push(GuardDetection(SM.getFileID(Loc)));
Guard = &Files.top();
break;
case ExitFile:
if (Guard->isPotentialHeaderGuard())
handlePotentialHeaderGuard(*Guard);
Files.pop();
Guard = &Files.top();
break;
default:
break;
}
}
/// \brief Mark this header as guarded in the IncludeDirectives if it's a
/// proper header guard.
void handlePotentialHeaderGuard(const GuardDetection &Guard) {
SourceManager &SM = Self->Sources;
const FileEntry *File = SM.getFileEntryForID(Guard.FID);
const LangOptions &LangOpts = Self->CI.getLangOpts();
// Null file can happen for the <built-in> buffer for example. They
// shouldn't have header guards though...
if (!File)
return;
// The #ifndef should be the next thing after the preamble. We aren't
// checking for equality because it can also be part of the preamble if the
// preamble is the whole file.
unsigned Preamble =
Lexer::ComputePreamble(SM.getBuffer(Guard.FID), LangOpts).first;
unsigned IfndefOffset = SM.getFileOffset(Guard.IfndefLoc);
if (IfndefOffset > (Preamble + 1))
return;
// No code is allowed in the code remaining after the #endif.
const llvm::MemoryBuffer *Buffer = SM.getBuffer(Guard.FID);
Lexer Lex(SM.getLocForStartOfFile(Guard.FID), LangOpts,
Buffer->getBufferStart(),
Buffer->getBufferStart() + SM.getFileOffset(Guard.EndifLoc),
Buffer->getBufferEnd());
// Find the first newline not part of a multi-line comment.
Token Tok;
Lex.LexFromRawLexer(Tok); // skip endif
Lex.LexFromRawLexer(Tok);
// Not a proper header guard, the remainder of the file contains something
// else than comments or whitespaces.
if (Tok.isNot(tok::eof))
return;
// Add to the location of the define to the IncludeDirectives for this file.
Self->HeaderToGuard[File] = Guard.DefineLoc;
}
virtual void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDirective *MD) {
Guard->Count++;
// If this #ifndef is the top-most directive and the symbol isn't defined
// store those information in the guard detection, the next step will be to
// check for the define.
if (Guard->Count == 1 && MD == 0) {
IdentifierInfo *MII = MacroNameTok.getIdentifierInfo();
if (MII->hasMacroDefinition())
return;
Guard->IfndefLoc = Loc;
Guard->TheMacro = MII;
}
}
virtual void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) {
Guard->Count++;
// If this #define is the second directive of the file and the symbol
// defined is the same as the one checked in the #ifndef then store the
// information about this define.
if (Guard->Count == 2 && Guard->TheMacro != 0) {
IdentifierInfo *MII = MacroNameTok.getIdentifierInfo();
// macro unrelated to the ifndef, doesn't look like a proper header guard
if (MII->getName() != Guard->TheMacro->getName())
return;
Guard->DefineLoc = MacroNameTok.getLocation();
}
}
virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) {
Guard->Count++;
// If it's the #endif corresponding to the top-most #ifndef
if (Self->Sources.getDecomposedLoc(Guard->IfndefLoc) !=
Self->Sources.getDecomposedLoc(IfLoc))
return;
// And that the top-most #ifndef was followed by the right #define
if (Guard->DefineLoc.isInvalid())
return;
// Then save the information about this #endif. Once the file is exited we
// will check if it was the final preprocessor directive.
Guard->CountAtEndif = Guard->Count;
Guard->EndifLoc = Loc;
}
virtual void MacroExpands(const Token &, const MacroDirective *, SourceRange,
const MacroArgs *) {
Guard->Count++;
}
virtual void MacroUndefined(const Token &, const MacroDirective *) {
Guard->Count++;
}
virtual void Defined(const Token &, const MacroDirective *, SourceRange) {
Guard->Count++;
}
virtual void If(SourceLocation, SourceRange, bool) { Guard->Count++; }
virtual void Elif(SourceLocation, SourceRange, bool, SourceLocation) {
Guard->Count++;
}
virtual void Ifdef(SourceLocation, const Token &, const MacroDirective *) {
Guard->Count++;
}
virtual void Else(SourceLocation, SourceLocation) { Guard->Count++; }
IncludeDirectives *Self;
// keep track of the guard info through the include stack
std::stack<GuardDetection> Files;
// convenience field pointing to Files.top().second
GuardDetection *Guard;
};
// Flags that describes where to insert newlines.
enum NewLineFlags {
// Prepend a newline at the beginning of the insertion.
NL_Prepend = 0x1,
// Prepend another newline at the end of the insertion.
NL_PrependAnother = 0x2,
// Add two newlines at the end of the insertion.
NL_AppendTwice = 0x4,
// Convenience value to enable both \c NL_Prepend and \c NL_PrependAnother.
NL_PrependTwice = NL_Prepend | NL_PrependAnother
};
/// \brief Guess the end-of-line sequence used in the given FileID. If the
/// sequence can't be guessed return an Unix-style newline.
static StringRef guessEOL(SourceManager &SM, FileID ID) {
StringRef Content = SM.getBufferData(ID);
StringRef Buffer = Content.substr(Content.find_first_of("\r\n"));
return llvm::StringSwitch<StringRef>(Buffer)
.StartsWith("\r\n", "\r\n")
.StartsWith("\n\r", "\n\r")
.StartsWith("\r", "\r")
.Default("\n");
}
/// \brief Find the end of the end of the directive, either the beginning of a
/// newline or the end of file.
//
// \return The offset into the file where the directive ends along with a
// boolean value indicating whether the directive ends because the end of file
// was reached or not.
static std::pair<unsigned, bool> findDirectiveEnd(SourceLocation HashLoc,
SourceManager &SM,
const LangOptions &LangOpts) {
FileID FID = SM.getFileID(HashLoc);
unsigned Offset = SM.getFileOffset(HashLoc);
StringRef Content = SM.getBufferData(FID);
Lexer Lex(SM.getLocForStartOfFile(FID), LangOpts, Content.begin(),
Content.begin() + Offset, Content.end());
Lex.SetCommentRetentionState(true);
Token Tok;
// This loop look for the newline after our directive but avoids the ones part
// of a multi-line comments:
//
// #include <foo> /* long \n comment */\n
// ~~ no ~~ yes
for (;;) {
// find the beginning of the end-of-line sequence
StringRef::size_type EOLOffset = Content.find_first_of("\r\n", Offset);
// ends because EOF was reached
if (EOLOffset == StringRef::npos)
return std::make_pair(Content.size(), true);
// find the token that contains our end-of-line
unsigned TokEnd = 0;
do {
Lex.LexFromRawLexer(Tok);
TokEnd = SM.getFileOffset(Tok.getLocation()) + Tok.getLength();
// happens when the whitespaces are eaten after a multiline comment
if (Tok.is(tok::eof))
return std::make_pair(EOLOffset, false);
} while (TokEnd < EOLOffset);
// the end-of-line is not part of a multi-line comment, return its location
if (Tok.isNot(tok::comment))
return std::make_pair(EOLOffset, false);
// for the next search to start after the end of this token
Offset = TokEnd;
}
}
IncludeDirectives::IncludeDirectives(clang::CompilerInstance &CI)
: CI(CI), Sources(CI.getSourceManager()) {
// addPPCallbacks takes ownership of the callback
CI.getPreprocessor().addPPCallbacks(new IncludeDirectivesPPCallback(this));
}
bool IncludeDirectives::lookForInclude(const FileEntry *File,
const LocationVec &IncludeLocs,
SeenFilesSet &Seen) const {
// mark this file as visited
Seen.insert(File);
// First check if included directly in this file
for (LocationVec::const_iterator I = IncludeLocs.begin(),
E = IncludeLocs.end();
I != E; ++I)
if (Sources.getFileEntryForID(Sources.getFileID(*I)) == File)
return true;
// Otherwise look recursively all the included files
FileToEntriesMap::const_iterator EntriesIt = FileToEntries.find(File);
if (EntriesIt == FileToEntries.end())
return false;
for (EntryVec::const_iterator I = EntriesIt->second.begin(),
E = EntriesIt->second.end();
I != E; ++I) {
// skip if this header has already been checked before
if (Seen.count(I->getIncludedFile()))
continue;
if (lookForInclude(I->getIncludedFile(), IncludeLocs, Seen))
return true;
}
return false;
}
bool IncludeDirectives::hasInclude(const FileEntry *File,
StringRef Include) const {
llvm::StringMap<LocationVec>::const_iterator It =
IncludeAsWrittenToLocationsMap.find(Include);
// Include isn't included in any file
if (It == IncludeAsWrittenToLocationsMap.end())
return false;
SeenFilesSet Seen;
return lookForInclude(File, It->getValue(), Seen);
}
Replacement IncludeDirectives::addAngledInclude(const clang::FileEntry *File,
llvm::StringRef Include) {
FileID FID = Sources.translateFile(File);
assert(!FID.isInvalid() && "Invalid file entry given!");
if (hasInclude(File, Include))
return Replacement();
unsigned Offset, NLFlags;
llvm::tie(Offset, NLFlags) = angledIncludeInsertionOffset(FID);
StringRef EOL = guessEOL(Sources, FID);
llvm::SmallString<32> InsertionText;
if (NLFlags & NL_Prepend)
InsertionText += EOL;
if (NLFlags & NL_PrependAnother)
InsertionText += EOL;
InsertionText += "#include <";
InsertionText += Include;
InsertionText += ">";
if (NLFlags & NL_AppendTwice) {
InsertionText += EOL;
InsertionText += EOL;
}
return Replacement(File->getName(), Offset, 0, InsertionText);
}
Replacement IncludeDirectives::addAngledInclude(llvm::StringRef File,
llvm::StringRef Include) {
const FileEntry *Entry = Sources.getFileManager().getFile(File);
assert(Entry && "Invalid file given!");
return addAngledInclude(Entry, Include);
}
std::pair<unsigned, unsigned>
IncludeDirectives::findFileHeaderEndOffset(FileID FID) const {
unsigned NLFlags = NL_Prepend;
StringRef Content = Sources.getBufferData(FID);
Lexer Lex(Sources.getLocForStartOfFile(FID), CI.getLangOpts(),
Content.begin(), Content.begin(), Content.end());
Lex.SetCommentRetentionState(true);
Lex.SetKeepWhitespaceMode(true);
// find the first newline not part of a multi-line comment
Token Tok;
do {
Lex.LexFromRawLexer(Tok);
unsigned Offset = Sources.getFileOffset(Tok.getLocation());
// allow one newline between the comments
if (Tok.is(tok::unknown) && isWhitespace(Content[Offset])) {
StringRef Whitespaces(Content.substr(Offset, Tok.getLength()));
if (Whitespaces.count('\n') == 1 || Whitespaces.count('\r') == 1)
Lex.LexFromRawLexer(Tok);
else {
// add an empty line to separate the file header and the inclusion
NLFlags = NL_PrependTwice;
}
}
} while (Tok.is(tok::comment));
// apparently there is no header, insertion point is the beginning of the file
if (Tok.isNot(tok::unknown))
return std::make_pair(0, NL_AppendTwice);
return std::make_pair(Sources.getFileOffset(Tok.getLocation()), NLFlags);
}
SourceLocation
IncludeDirectives::angledIncludeHintLoc(FileID FID) const {
FileToEntriesMap::const_iterator EntriesIt =
FileToEntries.find(Sources.getFileEntryForID(FID));
if (EntriesIt == FileToEntries.end())
return SourceLocation();
HeaderSearch &HeaderInfo = CI.getPreprocessor().getHeaderSearchInfo();
const EntryVec &Entries = EntriesIt->second;
EntryVec::const_reverse_iterator QuotedCandidate = Entries.rend();
for (EntryVec::const_reverse_iterator I = Entries.rbegin(),
E = Entries.rend();
I != E; ++I) {
// Headers meant for multiple inclusion can potentially appears in the
// middle of the code thus making them a poor choice for an insertion point.
if (!HeaderInfo.isFileMultipleIncludeGuarded(I->getIncludedFile()))
continue;
// return preferably the last angled include
if (I->isAngled())
return I->getHashLocation();
// keep track of the last quoted include that is guarded
if (QuotedCandidate == Entries.rend())
QuotedCandidate = I;
}
if (QuotedCandidate == Entries.rend())
return SourceLocation();
// return the last quoted-include if we couldn't find an angled one
return QuotedCandidate->getHashLocation();
}
std::pair<unsigned, unsigned>
IncludeDirectives::angledIncludeInsertionOffset(FileID FID) const {
SourceLocation Hint = angledIncludeHintLoc(FID);
unsigned NL_Flags = NL_Prepend;
// If we can't find a similar include and we are in a header check if it's a
// guarded header. If so the hint will be the location of the #define from the
// guard.
if (Hint.isInvalid()) {
const FileEntry *File = Sources.getFileEntryForID(FID);
HeaderToGuardMap::const_iterator GuardIt = HeaderToGuard.find(File);
if (GuardIt != HeaderToGuard.end()) {
// get the hash location from the #define
Hint = GuardIt->second;
// we want a blank line between the #define and the #include
NL_Flags = NL_PrependTwice;
}
}
// no hints, insertion is done after the file header
if (Hint.isInvalid())
return findFileHeaderEndOffset(FID);
unsigned Offset = findDirectiveEnd(Hint, Sources, CI.getLangOpts()).first;
return std::make_pair(Offset, NL_Flags);
}

View File

@@ -1,141 +0,0 @@
//===-- Core/IncludeDirectives.h - Include directives handling --*- 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 the IncludeDirectives class that helps with
/// detecting and modifying \#include directives.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H
#define CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <vector>
namespace clang {
class Preprocessor;
} // namespace clang
/// \brief Support for include directives handling.
///
/// This class should be created with a \c clang::CompilerInstance before the
/// file is preprocessed in order to collect the inclusion information. It can
/// be queried as long as the compiler instance is valid.
class IncludeDirectives {
public:
IncludeDirectives(clang::CompilerInstance &CI);
/// \brief Add an angled include to a the given file.
///
/// \param File A file accessible by a SourceManager
/// \param Include The include file as it should be written in the code.
///
/// \returns
/// \li A null Replacement (check using \c Replacement::isApplicable()), if
/// the \c Include is already visible from \c File.
/// \li Otherwise, a non-null Replacement that, when applied, inserts an
/// \c \#include into \c File.
clang::tooling::Replacement addAngledInclude(llvm::StringRef File,
llvm::StringRef Include);
clang::tooling::Replacement addAngledInclude(const clang::FileEntry *File,
llvm::StringRef Include);
/// \brief Check if \p Include is included by \p File or any of the files
/// \p File includes.
bool hasInclude(const clang::FileEntry *File, llvm::StringRef Include) const;
private:
friend class IncludeDirectivesPPCallback;
/// \brief Contains information about an inclusion.
class Entry {
public:
Entry(clang::SourceLocation HashLoc, const clang::FileEntry *IncludedFile,
bool Angled)
: HashLoc(HashLoc), IncludedFile(IncludedFile), Angled(Angled) {}
/// \brief The location of the '#'.
clang::SourceLocation getHashLocation() const { return HashLoc; }
/// \brief The file included by this include directive.
const clang::FileEntry *getIncludedFile() const { return IncludedFile; }
/// \brief \c true if the include use angle brackets, \c false otherwise
/// when using of quotes.
bool isAngled() const { return Angled; }
private:
clang::SourceLocation HashLoc;
const clang::FileEntry *IncludedFile;
bool Angled;
};
// A list of entries.
typedef std::vector<Entry> EntryVec;
// A list of source locations.
typedef std::vector<clang::SourceLocation> LocationVec;
// Associates files to their includes.
typedef llvm::DenseMap<const clang::FileEntry *, EntryVec> FileToEntriesMap;
// Associates headers to their include guards if any. The location is the
// location of the hash from the #define.
typedef llvm::DenseMap<const clang::FileEntry *, clang::SourceLocation>
HeaderToGuardMap;
/// \brief Type used by \c lookForInclude() to keep track of the files that
/// have already been processed.
typedef llvm::SmallPtrSet<const clang::FileEntry *, 32> SeenFilesSet;
/// \brief Recursively look if an include is included by \p File or any of the
/// headers \p File includes.
///
/// \param File The file where to start the search.
/// \param IncludeLocs These are the hash locations of the \#include
/// directives we are looking for.
/// \param Seen Used to avoid visiting a same file more than once during the
/// recursion.
bool lookForInclude(const clang::FileEntry *File,
const LocationVec &IncludeLocs, SeenFilesSet &Seen) const;
/// \brief Find the end of a file header and returns a pair (FileOffset,
/// NewLineFlags).
///
/// Source files often contain a file header (copyright, license, explanation
/// of the file content). An \#include should preferably be put after this.
std::pair<unsigned, unsigned>
findFileHeaderEndOffset(clang::FileID FID) const;
/// \brief Finds the offset where an angled include should be added and
/// returns a pair (FileOffset, NewLineFlags).
std::pair<unsigned, unsigned>
angledIncludeInsertionOffset(clang::FileID FID) const;
/// \brief Find the location of an include directive that can be used to
/// insert an inclusion after.
///
/// If no such include exists returns a null SourceLocation.
clang::SourceLocation angledIncludeHintLoc(clang::FileID FID) const;
clang::CompilerInstance &CI;
clang::SourceManager &Sources;
FileToEntriesMap FileToEntries;
// maps include filename as written in the source code to the source locations
// where it appears
llvm::StringMap<LocationVec> IncludeAsWrittenToLocationsMap;
HeaderToGuardMap HeaderToGuard;
};
#endif // CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H

View File

@@ -1,173 +0,0 @@
//===-- Core/IncludeExcludeInfo.cpp - IncludeExclude class 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 provides the implementation 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;
/// A string type to represent paths.
typedef SmallString<64> PathString;
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.
PathString 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.
bool IsSeparator = ((FileI->size() == 1) && (PathI->size() == 1) &&
sys::path::is_separator((*FileI)[0]) &&
sys::path::is_separator((*PathI)[0]));
if (!FileI->equals(*PathI) && !IsSeparator)
return false;
++FileI;
++PathI;
}
return true;
}
/// \brief Helper function for removing relative operators from a given
/// path i.e. "..", ".".
/// \a Path must be a absolute path.
std::string removeRelativeOperators(StringRef Path) {
sys::path::const_iterator PathI = sys::path::begin(Path);
sys::path::const_iterator PathE = sys::path::end(Path);
SmallVector<StringRef, 16> PathT;
while (PathI != PathE) {
if (PathI->equals("..")) {
// Test if we have reached the root then Path is invalid.
if (PathT.empty())
return "";
PathT.pop_back();
} else if (!PathI->equals("."))
PathT.push_back(*PathI);
++PathI;
}
// Rebuild the new path.
PathString NewPath;
for (SmallVectorImpl<StringRef>::iterator I = PathT.begin(), E = PathT.end();
I != E; ++I) {
llvm::sys::path::append(NewPath, *I);
}
return NewPath.str();
}
/// \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.
PathString Path = I->rtrim();
if (error_code Err = sys::fs::make_absolute(Path))
return Err;
// Remove relative operators from the path.
std::string AbsPath = removeRelativeOperators(Path);
// Add only non-empty paths to the list.
if (!AbsPath.empty())
List.push_back(AbsPath);
else
llvm::errs() << "Unable to parse input path: " << *I << "\n";
llvm::errs() << "Parse: " <<List.back() << "\n";
}
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) const {
bool InIncludeList = false;
for (std::vector<std::string>::const_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;
// If the file is in the included list but not is not explicitly excluded,
// then it is safe to transform.
return !isFileExplicitlyExcluded(FilePath);
}
bool IncludeExcludeInfo::isFileExplicitlyExcluded(StringRef FilePath) const {
for (std::vector<std::string>::const_iterator I = ExcludeList.begin(),
E = ExcludeList.end();
I != E; ++I)
if (fileHasPathPrefix(FilePath, *I))
return true;
return false;
}

View File

@@ -1,62 +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 CLANG_MODERNIZE_INCLUDEEXCLUDEINFO_H
#define CLANG_MODERNIZE_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-separated 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.
///
/// \a FilePath shouldn't contain relative operators i.e. ".." or "." since
/// Path comes from the include/exclude list of paths in which relative
/// operators were removed.
bool isFileIncluded(llvm::StringRef FilePath) const;
/// \brief Determine if a file path was explicitly excluded.
bool isFileExplicitlyExcluded(llvm::StringRef FilePath) const;
/// \brief Determine if a list of include paths was provided.
bool isIncludeListEmpty() const { return IncludeList.empty(); }
private:
std::vector<std::string> IncludeList;
std::vector<std::string> ExcludeList;
};
#endif // CLANG_MODERNIZE_INCLUDEEXCLUDEINFO_H

View File

@@ -1,14 +0,0 @@
##===- clang-modernize/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 := modernizeCore
include $(CLANG_LEVEL)/Makefile
CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include

View File

@@ -1,101 +0,0 @@
//===-- Core/PerfSupport.cpp - Perf measurement helpers -------------------===//
//
// 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 implementations for performance measuring helpers.
///
//===----------------------------------------------------------------------===//
#include "PerfSupport.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Path.h"
void collectSourcePerfData(const Transform &T, SourcePerfData &Data) {
for (Transform::TimingVec::const_iterator I = T.timing_begin(),
E = T.timing_end();
I != E; ++I) {
SourcePerfData::iterator DataI = Data.insert(
SourcePerfData::value_type(I->first, std::vector<PerfItem>())).first;
DataI->second
.push_back(PerfItem(T.getName(), I->second.getProcessTime() * 1000.0));
}
}
void writePerfDataJSON(
const llvm::StringRef DirectoryName,
const SourcePerfData &TimingResults) {
// Create directory path if it doesn't exist
llvm::sys::fs::create_directories(DirectoryName);
// Get PID and current time.
// FIXME: id_type on Windows is NOT a process id despite the function name.
// Need to call GetProcessId() providing it what get_id() returns. For now
// disabling PID-based file names until this is fixed properly.
//llvm::sys::self_process *SP = llvm::sys::process::get_self();
//id_type Pid = SP->get_id();
unsigned Pid = 0;
llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime();
std::string FileName;
llvm::raw_string_ostream SS(FileName);
SS << DirectoryName << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
<< ".json";
std::string ErrorInfo;
llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo);
FileStream << "{\n";
FileStream << " \"Sources\" : [\n";
for (SourcePerfData::const_iterator I = TimingResults.begin(),
E = TimingResults.end();
I != E; ++I) {
// Terminate the last source with a comma before continuing to the next one.
if (I != TimingResults.begin())
FileStream << ",\n";
FileStream << " {\n";
FileStream << " \"Source \" : \"" << I->first << "\",\n";
FileStream << " \"Data\" : [\n";
for (std::vector<PerfItem>::const_iterator IE = I->second.begin(),
EE = I->second.end();
IE != EE; ++IE) {
// Terminate the last perf item with a comma before continuing to the next
// one.
if (IE != I->second.begin())
FileStream << ",\n";
FileStream << " {\n";
FileStream << " \"TimerId\" : \"" << IE->Label << "\",\n";
FileStream << " \"Time\" : " << llvm::format("%.2f", IE->Duration)
<< "\n";
FileStream << " }";
}
FileStream << "\n ]\n";
FileStream << " }";
}
FileStream << "\n ]\n";
FileStream << "}";
}
void dumpPerfData(const SourcePerfData &Data) {
for (SourcePerfData::const_iterator I = Data.begin(), E = Data.end(); I != E;
++I) {
llvm::errs() << I->first << ":\n";
for (std::vector<PerfItem>::const_iterator VecI = I->second.begin(),
VecE = I->second.end();
VecI != VecE; ++VecI) {
llvm::errs() << " " << VecI->Label << ": "
<< llvm::format("%.1f", VecI->Duration) << "ms\n";
}
}
}

View File

@@ -1,57 +0,0 @@
//===-- Core/PerfSupport.h - Perf measurement helpers -----------*- 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 helper functionality for measuring performance and
/// recording data to file.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_PERFSUPPORT_H
#define CLANG_MODERNIZE_PERFSUPPORT_H
#include "Transform.h"
#include "llvm/ADT/StringRef.h"
#include <map>
#include <vector>
/// \brief A single piece of performance data: a duration in milliseconds and a
/// label for that duration.
struct PerfItem {
PerfItem(const llvm::StringRef Label, float Duration)
: Label(Label), Duration(Duration) {}
/// Label for this performance measurement.
std::string Label;
/// Duration in milliseconds.
float Duration;
};
/// Maps source file names to a vector of durations/labels.
typedef std::map<std::string, std::vector<PerfItem> > SourcePerfData;
/// Extracts durations collected by a Transform for all sources and adds them
/// to a SourcePerfData map where data is organized by source file.
extern void collectSourcePerfData(const Transform &T, SourcePerfData &Data);
/// Write timing results to a JSON formatted file.
///
/// File is placed in the directory given by \p DirectoryName. File is named in
/// a unique way with time and process ID to avoid naming collisions with
/// existing files or files being generated by other migrator processes.
void writePerfDataJSON(
const llvm::StringRef DirectoryName,
const SourcePerfData &TimingResults);
/// Dump a SourcePerfData map to llvm::errs().
extern void dumpPerfData(const SourcePerfData &Data);
#endif // CLANG_MODERNIZE_PERFSUPPORT_H

View File

@@ -1,31 +0,0 @@
//===-- Core/Refactoring.h - Stand-in for Tooling/Refactoring.h -*- 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 is meant to be used instead of clang/Tooling/Refactoring.h
/// until such time as clang::tooling::Replacements is re-implemented as a
/// vector instead of a set.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACEMENTS_VEC_H
#define CLANG_MODERNIZE_REPLACEMENTS_VEC_H
#include "clang/Tooling/Refactoring.h"
// FIXME: Remove this file when clang::tooling::Replacements becomes a vector
// instead of a set.
namespace clang {
namespace tooling {
typedef std::vector<clang::tooling::Replacement> ReplacementsVec;
}
}
#endif // CLANG_MODERNIZE_REPLACEMENTS_VEC_H

View File

@@ -1,155 +0,0 @@
//===-- Core/ReplacementHandling.cpp --------------------------------------===//
//
// 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 implementations for the ReplacementHandling class.
///
//===----------------------------------------------------------------------===//
#include "Core/ReplacementHandling.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/system_error.h"
using namespace llvm;
using namespace llvm::sys;
using namespace clang::tooling;
bool ReplacementHandling::findClangApplyReplacements(const char *Argv0) {
CARPath = FindProgramByName("clang-apply-replacements");
if (!CARPath.empty())
return true;
static int StaticSymbol;
CARPath = fs::getMainExecutable(Argv0, &StaticSymbol);
SmallString<128> TestPath = path::parent_path(CARPath);
path::append(TestPath, "clang-apply-replacements");
if (fs::can_execute(Twine(TestPath)))
CARPath = TestPath.str();
return !CARPath.empty();
}
StringRef ReplacementHandling::useTempDestinationDir() {
DestinationDir = generateTempDir();
return DestinationDir;
}
void ReplacementHandling::enableFormatting(StringRef Style,
StringRef StyleConfigDir) {
DoFormat = true;
FormatStyle = Style;
this->StyleConfigDir = StyleConfigDir;
}
bool ReplacementHandling::serializeReplacements(
const TUReplacementsMap &Replacements) {
assert(!DestinationDir.empty() && "Destination directory not set");
bool Errors = false;
for (TUReplacementsMap::const_iterator I = Replacements.begin(),
E = Replacements.end();
I != E; ++I) {
SmallString<128> ReplacementsFileName;
SmallString<64> Error;
bool Result = generateReplacementsFileName(DestinationDir,
I->getValue().MainSourceFile,
ReplacementsFileName, Error);
if (!Result) {
errs() << "Failed to generate replacements filename:" << Error << "\n";
Errors = true;
continue;
}
std::string ErrorInfo;
raw_fd_ostream ReplacementsFile(ReplacementsFileName.c_str(), ErrorInfo,
fs::F_Binary);
if (!ErrorInfo.empty()) {
errs() << "Error opening file: " << ErrorInfo << "\n";
Errors = true;
continue;
}
yaml::Output YAML(ReplacementsFile);
YAML << const_cast<TranslationUnitReplacements &>(I->getValue());
}
return !Errors;
}
bool ReplacementHandling::applyReplacements() {
SmallVector<const char *, 8> Argv;
Argv.push_back(CARPath.c_str());
std::string Style = "--style=" + FormatStyle;
std::string StyleConfig = "--style-config=" + StyleConfigDir;
if (DoFormat) {
Argv.push_back("--format");
Argv.push_back(Style.c_str());
if (!StyleConfigDir.empty())
Argv.push_back(StyleConfig.c_str());
}
Argv.push_back("--remove-change-desc-files");
Argv.push_back(DestinationDir.c_str());
// Argv array needs to be null terminated.
Argv.push_back(0);
std::string ErrorMsg;
bool ExecutionFailed = false;
int ReturnCode = ExecuteAndWait(CARPath.c_str(), Argv.data(), /* env */ 0,
/* redirects */ 0,
/* secondsToWait */ 0, /* memoryLimit */ 0,
&ErrorMsg, &ExecutionFailed);
if (ExecutionFailed || !ErrorMsg.empty()) {
errs() << "Failed to launch clang-apply-replacements: " << ErrorMsg << "\n";
errs() << "Command Line:\n";
for (const char **I = Argv.begin(), **E = Argv.end(); I != E; ++I) {
if (*I)
errs() << *I << "\n";
}
return false;
}
if (ReturnCode != 0) {
errs() << "clang-apply-replacements failed with return code " << ReturnCode
<< "\n";
return false;
}
return true;
}
std::string ReplacementHandling::generateTempDir() {
SmallString<128> Prefix;
path::system_temp_directory(true, Prefix);
path::append(Prefix, "clang-modernize");
SmallString<128> Result;
fs::createUniqueDirectory(Twine(Prefix), Result);
return Result.str();
}
bool ReplacementHandling::generateReplacementsFileName(
StringRef DestinationDir, StringRef MainSourceFile,
SmallVectorImpl<char> &Result, SmallVectorImpl<char> &Error) {
Error.clear();
SmallString<128> Prefix = DestinationDir;
path::append(Prefix, path::filename(MainSourceFile));
if (error_code EC =
fs::createUniqueFile(Prefix + "_%%_%%_%%_%%_%%_%%.yaml", Result)) {
const std::string &Msg = EC.message();
Error.append(Msg.begin(), Msg.end());
return false;
}
return true;
}

View File

@@ -1,123 +0,0 @@
//===-- Core/ReplacementHandling.h ------------------------------*- 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 defines the ReplacementHandling class which abstracts
/// serialization and application of serialized replacements.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#define CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#include "llvm/ADT/StringRef.h"
#include "Core/Transform.h"
class ReplacementHandling {
public:
ReplacementHandling() : DoFormat(false) {}
/// \brief Finds the path to the executable 'clang-apply-replacements'.
///
/// The executable is searched for on the PATH. If not found, looks in the
/// same directory as the image used to start the current executable.
///
/// \param[in] Argv0 argv[0] as passed to main().
///
/// \returns \li true if clang-apply-replacements was found.
/// \li false otherwise.
bool findClangApplyReplacements(const char *Argv0);
/// \brief Set the name of the directory in which replacements will be
/// serialized.
///
/// \param[in] Dir Destination directory name
void setDestinationDir(llvm::StringRef Dir) { DestinationDir = Dir; }
/// \brief Create a new temporary directory to serialize replacements into.
///
/// \returns The name of the directory createdy.
llvm::StringRef useTempDestinationDir();
/// \brief Enable clang-apply-replacements do code reformatting when applying
/// serialized replacements.
///
/// \param[in] Style Value to pass to clang-apply-replacement's --style
/// option.
/// \param[in] StyleConfigDir If non-empty, value to pass to
/// clang-apply-replacement's --style-config option.
void enableFormatting(llvm::StringRef Style,
llvm::StringRef StyleConfigDir = "");
/// \brief Write all TranslationUnitReplacements stored in \c Replacements
/// to disk.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
/// \pre Destination dir must exist.
///
/// \param[in] Replacements Container of replacements to serialize.
///
/// \returns \li true if all replacements were serialized successfully to
/// disk.
/// \li false otherwise.
bool serializeReplacements(const TUReplacementsMap &Replacements);
/// \brief Invoke clang-apply-replacements to apply all serialized
/// replacements stored in the destination directory.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
///
/// \returns \li true if clang-apply-replacements was successfully launched
/// and successfully completed.
/// \li false otherwise.
bool applyReplacements();
/// \brief Generate a unique filename to store the replacements.
///
/// Generates a unique filename in \c DestinationDir. The filename is generated
/// following this pattern:
///
/// DestinationDir/Prefix_%%_%%_%%_%%_%%_%%.yaml
///
/// where Prefix := llvm::sys::path::filename(MainSourceFile) and all '%' will
/// be replaced by a randomly chosen hex digit.
///
/// \param[in] DestinationDir Directory the unique file should be placed in.
/// \param[in] MainSourceFile Full path to the source file.
/// \param[out] Result The resulting unique filename.
/// \param[out] Error If an error occurs a description of that error is
/// placed in this string.
///
/// \returns \li true on success
/// \li false if a unique file name could not be created.
static bool generateReplacementsFileName(llvm::StringRef DestinationDir,
llvm::StringRef MainSourceFile,
llvm::SmallVectorImpl<char> &Result,
llvm::SmallVectorImpl<char> &Error);
/// \brief Helper to create a temporary directory name.
///
/// \post The directory named by the returned string exists.
///
/// \returns A temp directory name.
static std::string generateTempDir();
private:
std::string CARPath;
std::string DestinationDir;
bool DoFormat;
std::string FormatStyle;
std::string StyleConfigDir;
};
#endif // CLANG_MODERNIZE_REPLACEMENTHANDLING_H

View File

@@ -1,165 +0,0 @@
//===-- Core/Transform.cpp - Transform Base Class Def'n -------------------===//
//
// 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.
///
//===----------------------------------------------------------------------===//
#include "Core/Transform.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
llvm::cl::OptionCategory TransformsOptionsCategory("Transforms' options");
namespace {
using namespace tooling;
using namespace ast_matchers;
/// \brief Custom FrontendActionFactory to produce FrontendActions that simply
/// forward (Begin|End)SourceFileAction calls to a given Transform.
class ActionFactory : public clang::tooling::FrontendActionFactory {
public:
ActionFactory(MatchFinder &Finder, Transform &Owner)
: Finder(Finder), Owner(Owner) {}
virtual FrontendAction *create() LLVM_OVERRIDE {
return new FactoryAdaptor(Finder, Owner);
}
private:
class FactoryAdaptor : public ASTFrontendAction {
public:
FactoryAdaptor(MatchFinder &Finder, Transform &Owner)
: Finder(Finder), Owner(Owner) {}
ASTConsumer *CreateASTConsumer(CompilerInstance &, StringRef) {
return Finder.newASTConsumer();
}
virtual bool BeginSourceFileAction(CompilerInstance &CI,
StringRef Filename) LLVM_OVERRIDE {
if (!ASTFrontendAction::BeginSourceFileAction(CI, Filename))
return false;
return Owner.handleBeginSource(CI, Filename);
}
virtual void EndSourceFileAction() LLVM_OVERRIDE {
Owner.handleEndSource();
return ASTFrontendAction::EndSourceFileAction();
}
private:
MatchFinder &Finder;
Transform &Owner;
};
MatchFinder &Finder;
Transform &Owner;
};
} // namespace
Transform::Transform(llvm::StringRef Name, const TransformOptions &Options)
: Name(Name), GlobalOptions(Options) {
Reset();
}
Transform::~Transform() {}
bool Transform::isFileModifiable(const SourceManager &SM,
const SourceLocation &Loc) const {
if (SM.isWrittenInMainFile(Loc))
return true;
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc));
if (!FE)
return false;
return GlobalOptions.ModifiableFiles.isFileIncluded(FE->getName());
}
bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
CurrentSource = Filename;
if (Options().EnableTiming) {
Timings.push_back(std::make_pair(Filename.str(), llvm::TimeRecord()));
Timings.back().second -= llvm::TimeRecord::getCurrentTime(true);
}
return true;
}
void Transform::handleEndSource() {
CurrentSource.clear();
if (Options().EnableTiming)
Timings.back().second += llvm::TimeRecord::getCurrentTime(false);
}
void Transform::addTiming(llvm::StringRef Label, llvm::TimeRecord Duration) {
Timings.push_back(std::make_pair(Label.str(), Duration));
}
bool
Transform::addReplacementForCurrentTU(const clang::tooling::Replacement &R) {
if (CurrentSource.empty())
return false;
TranslationUnitReplacements &TU = Replacements[CurrentSource];
if (TU.MainSourceFile.empty())
TU.MainSourceFile = CurrentSource;
TU.Replacements.push_back(R);
return true;
}
FrontendActionFactory *Transform::createActionFactory(MatchFinder &Finder) {
return new ActionFactory(Finder, /*Owner=*/ *this);
}
Version Version::getFromString(llvm::StringRef VersionStr) {
llvm::StringRef MajorStr, MinorStr;
Version V;
llvm::tie(MajorStr, MinorStr) = VersionStr.split('.');
if (!MinorStr.empty()) {
llvm::StringRef Ignore;
llvm::tie(MinorStr, Ignore) = MinorStr.split('.');
if (MinorStr.getAsInteger(10, V.Minor))
return Version();
}
if (MajorStr.getAsInteger(10, V.Major))
return Version();
return V;
}
TransformFactory::~TransformFactory() {}
namespace {
bool versionSupported(Version Required, Version AvailableSince) {
// null version, means no requirements, means supported
if (Required.isNull())
return true;
return Required >= AvailableSince;
}
} // end anonymous namespace
bool TransformFactory::supportsCompilers(CompilerVersions Required) const {
return versionSupported(Required.Clang, Since.Clang) &&
versionSupported(Required.Gcc, Since.Gcc) &&
versionSupported(Required.Icc, Since.Icc) &&
versionSupported(Required.Msvc, Since.Msvc);
}

View File

@@ -1,326 +0,0 @@
//===-- Core/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 declaration for the base Transform class from
/// which all transforms must subclass.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_TRANSFORM_H
#define CLANG_MODERNIZE_TRANSFORM_H
#include "Core/IncludeExcludeInfo.h"
#include "Core/Refactoring.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Registry.h"
#include "llvm/Support/Timer.h"
#include <string>
#include <vector>
/// \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 {
class CompilerInstance;
namespace tooling {
class CompilationDatabase;
class FrontendActionFactory;
} // namespace tooling
namespace ast_matchers {
class MatchFinder;
} // namespace ast_matchers
} // namespace clang
// \brief Maps main source file names to a TranslationUnitReplacements
// structure storing replacements for that translation unit.
typedef llvm::StringMap<clang::tooling::TranslationUnitReplacements>
TUReplacementsMap;
/// \brief To group transforms' options together when printing the help.
extern llvm::cl::OptionCategory TransformsOptionsCategory;
/// \brief Container for global options affecting all transforms.
struct TransformOptions {
/// \brief Enable the use of performance timers.
bool EnableTiming;
/// \brief Contains information on which files are safe to transform and
/// which aren't.
IncludeExcludeInfo ModifiableFiles;
/// \brief Maximum allowed level of risk.
RiskLevel MaxRiskLevel;
};
/// \brief Abstract base class for all C++11 migration transforms.
///
/// Subclasses must call createActionFactory() to create a
/// FrontendActionFactory to pass to ClangTool::run(). Subclasses are also
/// responsible for calling setOverrides() before calling ClangTool::run().
///
/// If timing is enabled (see TransformOptions), per-source performance timing
/// is recorded and stored in a TimingVec for later access with timing_begin()
/// and timing_end().
class Transform {
public:
/// \brief Constructor
/// \param Name Name of the transform for human-readable purposes (e.g. -help
/// text)
/// \param Options Global options that affect all Transforms.
Transform(llvm::StringRef Name, const TransformOptions &Options);
virtual ~Transform();
/// \brief Apply a transform to all files listed in \p SourcePaths.
///
/// \param[in] Database Contains information for how to compile all files in
/// \p SourcePaths.
/// \param[in] SourcePaths list of sources to transform.
///
/// \returns \li 0 if successful
/// \li 1 otherwise
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) = 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;
}
/// \brief Tests if the file containing \a Loc is allowed to be modified by
/// the Modernizer.
bool isFileModifiable(const clang::SourceManager &SM,
const clang::SourceLocation &Loc) const;
/// \brief Whether a transformation with a risk level of \p RiskLevel is
/// acceptable or not.
bool isAcceptableRiskLevel(RiskLevel RiskLevel) const {
return RiskLevel <= GlobalOptions.MaxRiskLevel;
}
/// \brief Called before parsing a translation unit for a FrontendAction.
///
/// Transform uses this function to apply file overrides and start
/// performance timers. Subclasses overriding this function must call it
/// before returning.
virtual bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename);
/// \brief Called after FrontendAction has been run over a translation unit.
///
/// Transform uses this function to stop performance timers. Subclasses
/// overriding this function must call it before returning. A call to
/// handleEndSource() for a given translation unit is expected to be called
/// immediately after the corresponding handleBeginSource() call.
virtual void handleEndSource();
/// \brief Performance timing data is stored as a vector of pairs. Pairs are
/// formed of:
/// \li Name of source file.
/// \li Elapsed time.
typedef std::vector<std::pair<std::string, llvm::TimeRecord> > TimingVec;
/// \brief Return an iterator to the start of collected timing data.
TimingVec::const_iterator timing_begin() const { return Timings.begin(); }
/// \brief Return an iterator to the start of collected timing data.
TimingVec::const_iterator timing_end() const { return Timings.end(); }
/// \brief Add a Replacement to the list for the current translation unit.
///
/// \returns \li true on success
/// \li false if there is no current translation unit
bool addReplacementForCurrentTU(const clang::tooling::Replacement &R);
/// \brief Accessor to Replacements across all transformed translation units.
const TUReplacementsMap &getAllReplacements() const {
return Replacements;
}
protected:
void setAcceptedChanges(unsigned Changes) {
AcceptedChanges = Changes;
}
void setRejectedChanges(unsigned Changes) {
RejectedChanges = Changes;
}
void setDeferredChanges(unsigned Changes) {
DeferredChanges = Changes;
}
/// \brief Allows subclasses to manually add performance timer data.
///
/// \p Label should probably include the source file name somehow as the
/// duration info is simply added to the vector of timing data which holds
/// data for all sources processed by this transform.
void addTiming(llvm::StringRef Label, llvm::TimeRecord Duration);
/// \brief Provide access for subclasses to the TransformOptions they were
/// created with.
const TransformOptions &Options() { return GlobalOptions; }
/// \brief Subclasses must call this function to create a
/// FrontendActionFactory to pass to ClangTool.
///
/// The factory returned by this function is responsible for calling back to
/// Transform to call handleBeginSource() and handleEndSource().
clang::tooling::FrontendActionFactory *
createActionFactory(clang::ast_matchers::MatchFinder &Finder);
private:
const std::string Name;
const TransformOptions &GlobalOptions;
TUReplacementsMap Replacements;
std::string CurrentSource;
TimingVec Timings;
unsigned AcceptedChanges;
unsigned RejectedChanges;
unsigned DeferredChanges;
};
/// \brief Describes a version number of the form major[.minor] (minor being
/// optional).
struct Version {
explicit Version(unsigned Major = 0, unsigned Minor = 0)
: Major(Major), Minor(Minor) {}
bool operator<(Version RHS) const {
if (Major < RHS.Major)
return true;
if (Major == RHS.Major)
return Minor < RHS.Minor;
return false;
}
bool operator==(Version RHS) const {
return Major == RHS.Major && Minor == RHS.Minor;
}
bool operator!=(Version RHS) const { return !(*this == RHS); }
bool operator>(Version RHS) const { return RHS < *this; }
bool operator<=(Version RHS) const { return !(*this > RHS); }
bool operator>=(Version RHS) const { return !(*this < RHS); }
bool isNull() const { return Minor == 0 && Major == 0; }
unsigned getMajor() const { return Major; }
unsigned getMinor() const { return Minor; }
/// \brief Creates a version from a string of the form \c major[.minor].
///
/// Note that any version component after \c minor is ignored.
///
/// \return A null version is returned on error.
static Version getFromString(llvm::StringRef VersionStr);
private:
unsigned Major;
unsigned Minor;
};
/// \brief Convenience structure to store the version of some compilers.
struct CompilerVersions {
Version Clang, Gcc, Icc, Msvc;
};
/// \brief A factory that can instantiate a specific transform.
///
/// Each transform should subclass this class and implement
/// \c createTransform().
///
/// In the sub-classed factory constructor, specify the earliest versions since
/// the compilers in \c CompilerVersions support the feature introduced by the
/// transform. See the example below.
///
/// Note that you should use \c TransformFactoryRegistry to register the
/// transform globally.
///
/// Example:
/// \code
/// class MyTransform : public Transform { ... };
///
/// struct MyFactory : TransformFactory {
/// MyFactory() {
/// Since.Clang = Version(3, 0);
/// Since.Gcc = Version(4, 7);
/// Since.Icc = Version(12);
/// Since.Msvc = Version(10);
/// }
///
/// Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
/// return new MyTransform(Opts);
/// }
/// };
///
/// // Register the factory using this statically initialized variable.
/// static TransformFactoryRegistry::Add<MyFactory>
/// X("my-transform", "<Short description of my transform>");
///
/// // This anchor is used to force the linker to link in the generated object
/// // file and thus register the factory.
/// volatile int MyTransformAnchorSource = 0;
/// \endcode
class TransformFactory {
public:
virtual ~TransformFactory();
virtual Transform *createTransform(const TransformOptions &) = 0;
/// \brief Whether the transform is supported by the required compilers or
/// not.
bool supportsCompilers(CompilerVersions Required) const;
protected:
/// \brief Since when the C++11 feature introduced by this transform has been
/// available.
///
/// Can be set by the sub-class in the constructor body.
CompilerVersions Since;
};
typedef llvm::Registry<TransformFactory> TransformFactoryRegistry;
#endif // CLANG_MODERNIZE_TRANSFORM_H

View File

@@ -1,71 +0,0 @@
//===-- Core/Transforms.cpp - class Transforms 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 provides the implementation for class Transforms.
///
//===----------------------------------------------------------------------===//
#include "Core/Transforms.h"
#include "Core/Transform.h"
namespace cl = llvm::cl;
cl::OptionCategory TransformCategory("Transforms");
Transforms::~Transforms() {
for (std::vector<Transform *>::iterator I = ChosenTransforms.begin(),
E = ChosenTransforms.end();
I != E; ++I)
delete *I;
for (OptionMap::iterator I = Options.begin(), E = Options.end(); I != E; ++I)
delete I->getValue();
}
void Transforms::registerTransforms() {
for (TransformFactoryRegistry::iterator I = TransformFactoryRegistry::begin(),
E = TransformFactoryRegistry::end();
I != E; ++I)
Options[I->getName()] = new cl::opt<bool>(
I->getName(), cl::desc(I->getDesc()), cl::cat(TransformCategory));
}
bool Transforms::hasAnyExplicitOption() const {
for (OptionMap::const_iterator I = Options.begin(), E = Options.end(); I != E;
++I)
if (*I->second)
return true;
return false;
}
void
Transforms::createSelectedTransforms(const TransformOptions &GlobalOptions,
const CompilerVersions &RequiredVersions) {
// if at least one transform is set explicitly on the command line, do not
// enable non-explicit ones
bool EnableAllTransformsByDefault = !hasAnyExplicitOption();
for (TransformFactoryRegistry::iterator I = TransformFactoryRegistry::begin(),
E = TransformFactoryRegistry::end();
I != E; ++I) {
bool ExplicitlyEnabled = *Options[I->getName()];
bool OptionEnabled = EnableAllTransformsByDefault || ExplicitlyEnabled;
if (!OptionEnabled)
continue;
llvm::OwningPtr<TransformFactory> Factory(I->instantiate());
if (Factory->supportsCompilers(RequiredVersions))
ChosenTransforms.push_back(Factory->createTransform(GlobalOptions));
else if (ExplicitlyEnabled)
llvm::errs() << "note: " << '-' << I->getName()
<< ": transform not available for specified compilers\n";
}
}

View File

@@ -1,85 +0,0 @@
//===-- Core/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 CLANG_MODERNIZE_TRANSFORMS_H
#define CLANG_MODERNIZE_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;
struct TransformOptions;
struct CompilerVersions;
typedef Transform *(*TransformCreator)(const TransformOptions &);
template <typename T>
Transform *ConstructTransform(const TransformOptions &Options) {
return new T(Options);
}
/// \brief To group transforms together when printing the help.
extern llvm::cl::OptionCategory TransformCategory;
/// \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 all available transforms causing them to be made
/// available on the command line.
///
/// Be sure to register all transforms *before* parsing command line options.
void registerTransforms();
/// \brief Instantiate all transforms that were selected on the command line.
///
/// Call *after* parsing options.
void createSelectedTransforms(const TransformOptions &Options,
const CompilerVersions &RequiredVersions);
/// \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:
bool hasAnyExplicitOption() const;
typedef llvm::StringMap<llvm::cl::opt<bool> *> OptionMap;
private:
TransformVec ChosenTransforms;
OptionMap Options;
};
#endif // CLANG_MODERNIZE_TRANSFORMS_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,115 +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 CLANG_MODERNIZE_LOOP_ACTIONS_H
#define CLANG_MODERNIZE_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
};
struct TUTrackingInfo {
/// \brief Reset and initialize per-TU tracking information.
///
/// Must be called before using container accessors.
void reset() {
ParentFinder.reset(new StmtAncestorASTVisitor);
GeneratedDecls.clear();
ReplacedVars.clear();
}
/// \name Accessors
/// \{
StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
/// \}
private:
llvm::OwningPtr<StmtAncestorASTVisitor> ParentFinder;
StmtGeneratedVarNameMap GeneratedDecls;
ReplacedVarsMap ReplacedVars;
};
/// \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(TUTrackingInfo &TUInfo, unsigned *AcceptedChanges,
unsigned *DeferredChanges, unsigned *RejectedChanges,
RiskLevel MaxRisk, LoopFixerKind FixerKind, Transform &Owner)
: TUInfo(TUInfo), AcceptedChanges(AcceptedChanges),
DeferredChanges(DeferredChanges), RejectedChanges(RejectedChanges),
MaxRisk(MaxRisk), FixerKind(FixerKind), Owner(Owner) {}
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
private:
TUTrackingInfo &TUInfo;
unsigned *AcceptedChanges;
unsigned *DeferredChanges;
unsigned *RejectedChanges;
RiskLevel MaxRisk;
LoopFixerKind FixerKind;
Transform &Owner;
/// \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,
bool DerefByConstRef);
/// \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,
bool DerefByConstRef, 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 // CLANG_MODERNIZE_LOOP_ACTIONS_H

View File

@@ -1,91 +0,0 @@
//===-- LoopConvert/LoopConvert.cpp - C++11 for-loop migration ------------===//
//
// 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/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
int LoopConvertTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool LoopTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
unsigned DeferredChanges = 0;
unsigned RejectedChanges = 0;
TUInfo.reset(new TUTrackingInfo);
MatchFinder Finder;
LoopFixer ArrayLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel, LFK_Array,
/*Owner=*/ *this);
Finder.addMatcher(makeArrayLoopMatcher(), &ArrayLoopFixer);
LoopFixer IteratorLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel,
LFK_Iterator, /*Owner=*/ *this);
Finder.addMatcher(makeIteratorLoopMatcher(), &IteratorLoopFixer);
LoopFixer PseudoarrrayLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel,
LFK_PseudoArray, /*Owner=*/ *this);
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
if (int result = LoopTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
setRejectedChanges(RejectedChanges);
setDeferredChanges(DeferredChanges);
return 0;
}
bool
LoopConvertTransform::handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) {
// Reset and initialize per-TU tracking structures.
TUInfo->reset();
return Transform::handleBeginSource(CI, Filename);
}
struct LoopConvertFactory : TransformFactory {
LoopConvertFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new LoopConvertTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<LoopConvertFactory>
X("loop-convert", "Make use of range-based for loops where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int LoopConvertTransformAnchorSource = 0;

View File

@@ -1,43 +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 CLANG_MODERNIZE_LOOP_CONVERT_H
#define CLANG_MODERNIZE_LOOP_CONVERT_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h" // For LLVM_OVERRIDE
// Forward decl for private implementation.
struct TUTrackingInfo;
/// \brief Subclass of Transform that transforms for-loops into range-based
/// for-loops where possible.
class LoopConvertTransform : public Transform {
public:
LoopConvertTransform(const TransformOptions &Options)
: Transform("LoopConvert", Options) {}
/// \see Transform::run().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
virtual bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) LLVM_OVERRIDE;
private:
llvm::OwningPtr<TUTrackingInfo> TUInfo;
};
#endif // CLANG_MODERNIZE_LOOP_CONVERT_H

View File

@@ -1,346 +0,0 @@
//===-- LoopConvert/LoopMatchers.cpp - Matchers for for loops -------------===//
//
// 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 BeginCallName[] = "beginCall";
const char EndCallName[] = "endCall";
const char ConditionEndVarName[] = "conditionEndVar";
const char EndVarName[] = "endVar";
const char DerefByValueResultName[] = "derefByValueResult";
const char DerefByRefResultName[] = "derefByRefResult";
// 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 identifiers are bound to these 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"))
)
).bind(BeginCallName);
DeclarationMatcher InitDeclMatcher =
varDecl(
hasInitializer(
anyOf(
ignoringParenImpCasts(BeginCallMatcher),
materializeTemporaryExpr(ignoringParenImpCasts(BeginCallMatcher)),
hasDescendant(BeginCallMatcher)
)
)
).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())
)
).bind(DerefByRefResultName)
)
)
)
)
)
);
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() {
// Test that the incoming type has a record declaration that has methods
// called 'begin' and 'end'. If the incoming type is const, then make sure
// these methods are also marked const.
//
// FIXME: To be completely thorough this matcher should also ensure the
// return type of begin/end is an iterator that dereferences to the same as
// what operator[] or at() returns. Such a test isn't likely to fail except
// for pathological cases.
//
// FIXME: Also, a record doesn't necessarily need begin() and end(). Free
// functions called begin() and end() taking the container as an argument
// are also allowed.
TypeMatcher RecordWithBeginEnd =
qualType(anyOf(
qualType(
isConstQualified(),
hasDeclaration(
recordDecl(
hasMethod(
methodDecl(
hasName("begin"),
isConst()
)
),
hasMethod(
methodDecl(
hasName("end"),
isConst()
)
)
)
) // hasDeclaration
), // qualType
qualType(
unless(isConstQualified()),
hasDeclaration(
recordDecl(
hasMethod(hasName("begin")),
hasMethod(hasName("end"))
)
)
) // qualType
)
);
StatementMatcher SizeCallMatcher =
memberCallExpr(argumentCountIs(0),
callee(methodDecl(anyOf(hasName("size"),
hasName("length")))),
on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
hasType(RecordWithBeginEnd))));
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,42 +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 CLANG_MODERNIZE_LOOP_MATCHERS_H
#define CLANG_MODERNIZE_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 BeginCallName[];
extern const char EndExprName[];
extern const char EndCallName[];
extern const char EndVarName[];
extern const char DerefByValueResultName[];
extern const char DerefByRefResultName[];
clang::ast_matchers::StatementMatcher makeArrayLoopMatcher();
clang::ast_matchers::StatementMatcher makeIteratorLoopMatcher();
clang::ast_matchers::StatementMatcher makePseudoArrayLoopMatcher();
#endif // CLANG_MODERNIZE_LOOP_MATCHERS_H

View File

@@ -1,140 +0,0 @@
//===-- LoopConvert/StmtAncestor.cpp - AST property visitors --------------===//
//
// 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,201 +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 CLANG_MODERNIZE_STMT_ANCESTOR_H
#define CLANG_MODERNIZE_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 transformed,
/// 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 // CLANG_MODERNIZE_STMT_ANCESTOR_H

View File

@@ -1,95 +0,0 @@
//===-- LoopConvert/VariableNaming.cpp - Gererate variable names ----------===//
//
// 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,59 +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 CLANG_MODERNIZE_VARIABLE_NAMING_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_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,76 +0,0 @@
//===-- PassByValue.cpp ---------------------------------------------------===//
//
// 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 ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "PassByValue.h"
#include "PassByValueActions.h"
#include "PassByValueMatchers.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
int PassByValueTransform::apply(const tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
unsigned RejectedChanges = 0;
MatchFinder Finder;
ConstructorParamReplacer Replacer(AcceptedChanges, RejectedChanges,
/*Owner=*/ *this);
Finder.addMatcher(makePassByValueCtorParamMatcher(), &Replacer);
// make the replacer available to handleBeginSource()
this->Replacer = &Replacer;
if (Tool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return 1;
}
setAcceptedChanges(AcceptedChanges);
setRejectedChanges(RejectedChanges);
return 0;
}
bool PassByValueTransform::handleBeginSource(CompilerInstance &CI,
llvm::StringRef Filename) {
assert(Replacer && "Replacer not set");
IncludeManager.reset(new IncludeDirectives(CI));
Replacer->setIncludeDirectives(IncludeManager.get());
return Transform::handleBeginSource(CI, Filename);
}
struct PassByValueFactory : TransformFactory {
PassByValueFactory() {
// Based on the Replace Auto-Ptr Transform that is also using std::move().
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new PassByValueTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<PassByValueFactory>
X("pass-by-value", "Pass parameters by value where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int PassByValueTransformAnchorSource = 0;

View File

@@ -1,73 +0,0 @@
//===-- PassByValue.h -------------------------------------------*- 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 declaration of the PassByValueTransform
/// class.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_PASS_BY_VALUE_H
#define CLANG_MODERNIZE_PASS_BY_VALUE_H
#include "Core/Transform.h"
#include "Core/IncludeDirectives.h"
class ConstructorParamReplacer;
/// \brief Subclass of Transform that uses pass-by-value semantic when move
/// constructors are available to avoid copies.
///
/// When a class constructor accepts an object by const reference with the
/// intention of copying the object the copy can be avoided in certain
/// situations if the object has a move constructor. First, the constructor is
/// changed to accept the object by value instead. Then this argument is moved
/// instead of copied into class-local storage. If an l-value is provided to the
/// constructor, there is no difference in the number of copies made. However,
/// if an r-value is passed, the copy is avoided completely.
///
/// For example, given:
/// \code
/// #include <string>
///
/// class A {
/// std::string S;
/// public:
/// A(const std::string &S) : S(S) {}
/// };
/// \endcode
/// the code is transformed to:
/// \code
/// #include <string>
///
/// class A {
/// std::string S;
/// public:
/// A(std::string S) : S(std::move(S)) {}
/// };
/// \endcode
class PassByValueTransform : public Transform {
public:
PassByValueTransform(const TransformOptions &Options)
: Transform("PassByValue", Options), Replacer(0) {}
/// \see Transform::apply().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
private:
/// \brief Setups the \c IncludeDirectives for the replacer.
virtual bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) LLVM_OVERRIDE;
llvm::OwningPtr<IncludeDirectives> IncludeManager;
ConstructorParamReplacer *Replacer;
};
#endif // CLANG_MODERNIZE_PASS_BY_VALUE_H

View File

@@ -1,175 +0,0 @@
//===-- PassByValueActions.cpp --------------------------------------------===//
//
// 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 ASTMatcher callback for the
/// PassByValue transform.
///
//===----------------------------------------------------------------------===//
#include "PassByValueActions.h"
#include "PassByValueMatchers.h"
#include "Core/IncludeDirectives.h"
#include "Core/Transform.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
namespace {
/// \brief \c clang::RecursiveASTVisitor that checks that the given
/// \c ParmVarDecl is used exactly one time.
///
/// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
class ExactlyOneUsageVisitor
: public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
public:
ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl) : ParamDecl(ParamDecl) {}
/// \brief Whether or not the parameter variable is referred only once in the
/// given constructor.
bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
Count = 0;
TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
return Count == 1;
}
private:
/// \brief Counts the number of references to a variable.
///
/// Stops the AST traversal if more than one usage is found.
bool VisitDeclRefExpr(DeclRefExpr *D) {
if (const ParmVarDecl *To = llvm::dyn_cast<ParmVarDecl>(D->getDecl()))
if (To == ParamDecl) {
++Count;
if (Count > 1)
// no need to look further, used more than once
return false;
}
return true;
}
const ParmVarDecl *ParamDecl;
unsigned Count;
};
} // end anonymous namespace
/// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
///
/// Checks both in the init-list and the body of the constructor.
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
const ParmVarDecl *ParamDecl) {
ExactlyOneUsageVisitor Visitor(ParamDecl);
return Visitor.hasExactlyOneUsageIn(Ctor);
}
/// \brief Find all references to \p ParamDecl across all of the
/// redeclarations of \p Ctor.
static void
collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl,
llvm::SmallVectorImpl<const ParmVarDecl *> &Results) {
unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
for (CXXConstructorDecl::redecl_iterator I = Ctor->redecls_begin(),
E = Ctor->redecls_end();
I != E; ++I)
Results.push_back((*I)->getParamDecl(ParamIdx));
}
void ConstructorParamReplacer::run(const MatchFinder::MatchResult &Result) {
assert(IncludeManager && "Include directives manager not set.");
SourceManager &SM = *Result.SourceManager;
const CXXConstructorDecl *Ctor =
Result.Nodes.getNodeAs<CXXConstructorDecl>(PassByValueCtorId);
const ParmVarDecl *ParamDecl =
Result.Nodes.getNodeAs<ParmVarDecl>(PassByValueParamId);
const CXXCtorInitializer *Initializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>(PassByValueInitializerId);
assert(Ctor && ParamDecl && Initializer && "Bad Callback, missing node.");
// Check this now to avoid unnecessary work. The param locations are checked
// later.
if (!Owner.isFileModifiable(SM, Initializer->getSourceLocation()))
return;
// The parameter will be in an unspecified state after the move, so check if
// the parameter is used for anything else other than the copy. If so do not
// apply any changes.
if (!paramReferredExactlyOnce(Ctor, ParamDecl))
return;
llvm::SmallVector<const ParmVarDecl *, 2> AllParamDecls;
collectParamDecls(Ctor, ParamDecl, AllParamDecls);
// Generate all replacements for the params.
llvm::SmallVector<Replacement, 2> ParamReplaces;
for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) {
TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc();
ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>();
SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd());
CharSourceRange CharRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Range), SM, LangOptions());
// do not generate a replacement when the parameter is already a value
if (RefTL.isNull())
continue;
// transform non-value parameters (e.g: const-ref) to values
TypeLoc ValueTypeLoc = RefTL.getPointeeLoc();
llvm::SmallString<32> ValueStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(ValueTypeLoc.getSourceRange()), SM,
LangOptions());
// If it's impossible to change one of the parameter (e.g: comes from an
// unmodifiable header) quit the callback now, do not generate any changes.
if (CharRange.isInvalid() || ValueStr.empty() ||
!Owner.isFileModifiable(SM, CharRange.getBegin()))
return;
// 'const Foo &param' -> 'Foo param'
// ~~~~~~~~~~~ ~~~^
ValueStr += ' ';
ParamReplaces.push_back(Replacement(SM, CharRange, ValueStr));
}
// Reject the changes if the the risk level is not acceptable.
if (!Owner.isAcceptableRiskLevel(RL_Reasonable)) {
RejectedChanges++;
return;
}
// if needed, include <utility> in the file that uses std::move()
const FileEntry *STDMoveFile =
SM.getFileEntryForID(SM.getFileID(Initializer->getLParenLoc()));
const tooling::Replacement &IncludeReplace =
IncludeManager->addAngledInclude(STDMoveFile, "utility");
if (IncludeReplace.isApplicable()) {
Owner.addReplacementForCurrentTU(IncludeReplace);
AcceptedChanges++;
}
// const-ref params becomes values (const Foo & -> Foo)
for (const Replacement *I = ParamReplaces.begin(), *E = ParamReplaces.end();
I != E; ++I) {
Owner.addReplacementForCurrentTU(*I);
}
AcceptedChanges += ParamReplaces.size();
// move the value in the init-list
Owner.addReplacementForCurrentTU(Replacement(
SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move("));
Owner.addReplacementForCurrentTU(
Replacement(SM, Initializer->getRParenLoc(), 0, ")"));
AcceptedChanges += 2;
}

View File

@@ -1,74 +0,0 @@
//===-- PassByValueActions.h ------------------------------------*- 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 ASTMatcher callback for the
/// PassByValue transform.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H
#define CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
class IncludeDirectives;
/// \brief Callback that replaces const-ref parameters in constructors to use
/// pass-by-value semantic where applicable.
///
/// Modifications done by the callback:
/// - \#include \<utility\> is added if necessary for the definition of
/// \c std::move() to be available.
/// - The parameter type is changed from const-ref to value-type.
/// - In the init-list the parameter is moved.
///
/// Example:
/// \code
/// + #include <utility>
///
/// class Foo(const std::string &S) {
/// public:
/// - Foo(const std::string &S) : S(S) {}
/// + Foo(std::string S) : S(std::move(S)) {}
///
/// private:
/// std::string S;
/// };
/// \endcode
///
/// \note Since an include may be added by this matcher it's necessary to call
/// \c setIncludeDirectives() with an up-to-date \c IncludeDirectives. This is
/// typically done by overloading \c Transform::handleBeginSource().
class ConstructorParamReplacer
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
ConstructorParamReplacer(unsigned &AcceptedChanges, unsigned &RejectedChanges,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), RejectedChanges(RejectedChanges),
Owner(Owner), IncludeManager(0) {}
void setIncludeDirectives(IncludeDirectives *Includes) {
IncludeManager = Includes;
}
private:
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
LLVM_OVERRIDE;
unsigned &AcceptedChanges;
unsigned &RejectedChanges;
Transform &Owner;
IncludeDirectives *IncludeManager;
};
#endif // CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H

View File

@@ -1,94 +0,0 @@
//===-- PassByValueMatchers.cpp -------------------------------------------===//
//
// 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 names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#include "PassByValueMatchers.h"
const char *PassByValueCtorId = "Ctor";
const char *PassByValueParamId = "Param";
const char *PassByValueInitializerId = "Initializer";
namespace clang {
namespace ast_matchers {
/// \brief Matches move constructible classes.
///
/// Given
/// \code
/// // POD types are trivially move constructible
/// struct Foo { int a; };
///
/// struct Bar {
/// Bar(Bar &&) = deleted;
/// int a;
/// };
/// \endcode
/// recordDecl(isMoveConstructible())
/// matches "Foo".
AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
for (CXXRecordDecl::ctor_iterator I = Node.ctor_begin(), E = Node.ctor_end(); I != E; ++I) {
const CXXConstructorDecl *Ctor = *I;
if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
return true;
}
return false;
}
/// \brief Matches non-deleted copy constructors.
///
/// Given
/// \code
/// struct Foo { Foo(const Foo &) = default; };
/// struct Bar { Bar(const Bar &) = deleted; };
/// \endcode
/// constructorDecl(isNonDeletedCopyConstructor())
/// matches "Foo(const Foo &)".
AST_MATCHER(CXXConstructorDecl, isNonDeletedCopyConstructor) {
return Node.isCopyConstructor() && !Node.isDeleted();
}
} // namespace ast_matchers
} // namespace clang
using namespace clang;
using namespace clang::ast_matchers;
static TypeMatcher constRefType() {
return lValueReferenceType(pointee(isConstQualified()));
}
static TypeMatcher nonConstValueType() {
return qualType(unless(anyOf(referenceType(), isConstQualified())));
}
DeclarationMatcher makePassByValueCtorParamMatcher() {
return constructorDecl(
forEachConstructorInitializer(ctorInitializer(
// Clang builds a CXXConstructExpr only when it knowns which
// constructor will be called. In dependent contexts a ParenListExpr
// is generated instead of a CXXConstructExpr, filtering out templates
// automatically for us.
withInitializer(constructExpr(
has(declRefExpr(to(
parmVarDecl(hasType(qualType(
// match only const-ref or a non-const value
// parameters, rvalues and const-values
// shouldn't be modified.
anyOf(constRefType(), nonConstValueType()))))
.bind(PassByValueParamId)))),
hasDeclaration(constructorDecl(
isNonDeletedCopyConstructor(),
hasDeclContext(recordDecl(isMoveConstructible())))))))
.bind(PassByValueInitializerId)))
.bind(PassByValueCtorId);
}

View File

@@ -1,44 +0,0 @@
//===-- PassByValueMatchers.h -----------------------------------*- 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 CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
/// \name Names to bind with matched expressions
/// @{
extern const char *PassByValueCtorId;
extern const char *PassByValueParamId;
extern const char *PassByValueInitializerId;
/// @}
/// \brief Creates a matcher that finds class field initializations that can
/// benefit from using the move constructor.
///
/// \code
/// class A {
/// public:
/// A(const std::string &S) : S(S) {}
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PassByValueCtorId
/// ~~~~~~~~~~~~~~~~~~~~ PassByValueParamId
/// ~ PassByValueInitializerId
/// private:
/// std::string S;
/// };
/// \endcode
clang::ast_matchers::DeclarationMatcher makePassByValueCtorParamMatcher();
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H

View File

@@ -1,67 +0,0 @@
//===-- ReplaceAutoPtr.cpp ---------- std::auto_ptr replacement -----------===//
//
// 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 ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtr.h"
#include "ReplaceAutoPtrActions.h"
#include "ReplaceAutoPtrMatchers.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
int
ReplaceAutoPtrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
AutoPtrReplacer Replacer(AcceptedChanges, /*Owner=*/ *this);
OwnershipTransferFixer Fixer(AcceptedChanges, /*Owner=*/ *this);
Finder.addMatcher(makeAutoPtrTypeLocMatcher(), &Replacer);
Finder.addMatcher(makeAutoPtrUsingDeclMatcher(), &Replacer);
Finder.addMatcher(makeTransferOwnershipExprMatcher(), &Fixer);
if (Tool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return 1;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
struct ReplaceAutoPtrFactory : TransformFactory {
ReplaceAutoPtrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new ReplaceAutoPtrTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<ReplaceAutoPtrFactory>
X("replace-auto_ptr", "Replace std::auto_ptr (deprecated) by std::unique_ptr"
" (EXPERIMENTAL)");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int ReplaceAutoPtrTransformAnchorSource = 0;

View File

@@ -1,54 +0,0 @@
//===-- ReplaceAutoPtr.h ------------ std::auto_ptr replacement -*- 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 declaration of the ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h"
/// \brief Subclass of Transform that transforms the deprecated \c std::auto_ptr
/// into the C++11 \c std::unique_ptr.
///
/// Note that both the \c std::auto_ptr type and the transfer of ownership are
/// transformed. \c std::auto_ptr provides two ways to transfer the ownership,
/// the copy-constructor and the assignment operator. Unlike most classes theses
/// operations do not 'copy' the resource but they 'steal' it.
/// \c std::unique_ptr uses move semantics instead, which makes the intent of
/// transferring the resource explicit. This difference between the two smart
/// pointers requires to wrap the copy-ctor and assign-operator with
/// \c std::move().
///
/// For example, given:
/// \code
/// std::auto_ptr<int> i, j;
/// i = j;
/// \endcode
/// the code is transformed to:
/// \code
/// std::unique_ptr<int> i, j;
/// i = std::move(j);
/// \endcode
class ReplaceAutoPtrTransform : public Transform {
public:
ReplaceAutoPtrTransform(const TransformOptions &Options)
: Transform("ReplaceAutoPtr", Options) {}
/// \see Transform::run().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_H

View File

@@ -1,108 +0,0 @@
//===-- ReplaceAutoPtrActions.cpp --- std::auto_ptr replacement -----------===//
//
// 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 ASTMatcher callback for the
/// ReplaceAutoPtr transform.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtrActions.h"
#include "ReplaceAutoPtrMatchers.h"
#include "Core/Transform.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
namespace {
/// \brief Verifies that the token at \p BeginningOfToken is 'auto_ptr'.
bool checkTokenIsAutoPtr(clang::SourceLocation BeginningOfToken,
const clang::SourceManager &SM,
const clang::LangOptions &LangOptions) {
llvm::SmallVector<char, 8> Buffer;
bool Invalid = false;
llvm::StringRef Res =
Lexer::getSpelling(BeginningOfToken, Buffer, SM, LangOptions, &Invalid);
if (Invalid)
return false;
return Res == "auto_ptr";
}
} // end anonymous namespace
void AutoPtrReplacer::run(const MatchFinder::MatchResult &Result) {
SourceManager &SM = *Result.SourceManager;
SourceLocation IdentifierLoc;
if (const TypeLoc *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
IdentifierLoc = locateFromTypeLoc(*TL, SM);
} else {
const UsingDecl *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId);
assert(D && "Bad Callback. No node provided.");
IdentifierLoc = locateFromUsingDecl(D, SM);
}
if (IdentifierLoc.isMacroID())
IdentifierLoc = SM.getSpellingLoc(IdentifierLoc);
if (!Owner.isFileModifiable(SM, IdentifierLoc))
return;
// make sure that only the 'auto_ptr' token is replaced and not the template
// aliases [temp.alias]
if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions()))
return;
Owner.addReplacementForCurrentTU(
Replacement(SM, IdentifierLoc, strlen("auto_ptr"), "unique_ptr"));
++AcceptedChanges;
}
SourceLocation AutoPtrReplacer::locateFromTypeLoc(TypeLoc AutoPtrTypeLoc,
const SourceManager &SM) {
TemplateSpecializationTypeLoc TL =
AutoPtrTypeLoc.getAs<TemplateSpecializationTypeLoc>();
if (TL.isNull())
return SourceLocation();
return TL.getTemplateNameLoc();
}
SourceLocation
AutoPtrReplacer::locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl,
const SourceManager &SM) {
return UsingAutoPtrDecl->getNameInfo().getBeginLoc();
}
void OwnershipTransferFixer::run(const MatchFinder::MatchResult &Result) {
SourceManager &SM = *Result.SourceManager;
const Expr *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId);
assert(E && "Bad Callback. No node provided.");
CharSourceRange Range = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
if (Range.isInvalid())
return;
if (!Owner.isFileModifiable(SM, Range.getBegin()))
return;
Owner.addReplacementForCurrentTU(
Replacement(SM, Range.getBegin(), 0, "std::move("));
Owner.addReplacementForCurrentTU(Replacement(SM, Range.getEnd(), 0, ")"));
AcceptedChanges += 2;
}

View File

@@ -1,99 +0,0 @@
//===-- ReplaceAutoPtrActions.h ----- std::auto_ptr replacement -*- 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 ASTMatcher callback for the
/// ReplaceAutoPtr transform.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
/// \brief The callback to be used when replacing the \c std::auto_ptr types and
/// using declarations.
class AutoPtrReplacer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
AutoPtrReplacer(unsigned &AcceptedChanges, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
LLVM_OVERRIDE;
private:
/// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc.
///
/// \code
/// std::auto_ptr<int> i;
/// ^~~~~~~~~~~~~
/// \endcode
/// The caret represents the location returned and the tildes cover the
/// parameter \p AutoPtrTypeLoc.
///
/// \return An invalid \c SourceLocation if not found, otherwise the location
/// of the beginning of the \c auto_ptr token.
clang::SourceLocation locateFromTypeLoc(clang::TypeLoc AutoPtrTypeLoc,
const clang::SourceManager &SM);
/// \brief Locates the \c auto_ptr token in using declarations.
///
/// \code
/// using std::auto_ptr;
/// ^
/// \endcode
/// The caret represents the location returned.
///
/// \return An invalid \c SourceLocation if not found, otherwise the
/// location of the beginning of the \c auto_ptr token.
clang::SourceLocation
locateFromUsingDecl(const clang::UsingDecl *UsingAutoPtrDecl,
const clang::SourceManager &SM);
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
/// \brief The callback to be used to fix the ownership transfers of
/// \c auto_ptr,
///
/// \c unique_ptr requires to use \c std::move() explicitly in order to transfer
/// the ownership.
///
/// Given:
/// \code
/// std::auto_ptr<int> a, b;
/// a = b;
/// \endcode
/// The last statement is transformed to:
/// \code
/// a = std::move(b);
/// \endcode
class OwnershipTransferFixer
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
OwnershipTransferFixer(unsigned &AcceptedChanges, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
LLVM_OVERRIDE;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H

View File

@@ -1,81 +0,0 @@
//===-- ReplaceAutoPtrMatchers.cpp -- std::auto_ptr replacement -----------===//
//
// 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 names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtrMatchers.h"
#include "Core/CustomMatchers.h"
const char *AutoPtrTokenId = "AutoPtrTokenId";
const char *AutoPtrOwnershipTransferId = "AutoPtrOwnershipTransferId";
namespace clang {
namespace ast_matchers {
/// \brief Matches expressions that are lvalues.
///
/// In the following example, a[0] matches expr(isLValue()):
/// \code
/// std::string a[2];
/// std::string b;
/// b = a[0];
/// b = "this string won't match";
/// \endcode
AST_MATCHER(Expr, isLValue) {
return Node.getValueKind() == VK_LValue;
}
} // end namespace ast_matchers
} // end namespace clang
using namespace clang;
using namespace clang::ast_matchers;
// shared matchers
static DeclarationMatcher AutoPtrDecl =
recordDecl(hasName("auto_ptr"), isFromStdNamespace());
static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
// Matcher that finds expressions that are candidates to be wrapped with
// 'std::move()'.
//
// Binds the id \c AutoPtrOwnershipTransferId to the expression.
static StatementMatcher MovableArgumentMatcher = expr(
allOf(isLValue(), hasType(AutoPtrType))).bind(AutoPtrOwnershipTransferId);
TypeLocMatcher makeAutoPtrTypeLocMatcher() {
// skip elaboratedType() as the named type will match soon thereafter.
return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType()))))
.bind(AutoPtrTokenId);
}
DeclarationMatcher makeAutoPtrUsingDeclMatcher() {
return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(
allOf(hasName("auto_ptr"), isFromStdNamespace())))).bind(AutoPtrTokenId);
}
StatementMatcher makeTransferOwnershipExprMatcher() {
StatementMatcher assignOperator =
operatorCallExpr(allOf(
hasOverloadedOperatorName("="),
callee(methodDecl(ofClass(AutoPtrDecl))),
hasArgument(1, MovableArgumentMatcher)));
StatementMatcher copyCtor =
constructExpr(allOf(hasType(AutoPtrType),
argumentCountIs(1),
hasArgument(0, MovableArgumentMatcher)));
return anyOf(assignOperator, copyCtor);
}

View File

@@ -1,64 +0,0 @@
//===-- ReplaceAutoPtrMatchers.h ---- std::auto_ptr replacement -*- 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 CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
/// Names to bind with matched expressions.
extern const char *AutoPtrTokenId;
extern const char *AutoPtrOwnershipTransferId;
/// \brief Creates a matcher that finds the locations of types referring to the
/// \c std::auto_ptr() type.
///
/// \code
/// std::auto_ptr<int> a;
/// ^~~~~~~~~~~~~
///
/// typedef std::auto_ptr<int> int_ptr_t;
/// ^~~~~~~~~~~~~
///
/// std::auto_ptr<int> fn(std::auto_ptr<int>);
/// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
///
/// <etc...>
/// \endcode
clang::ast_matchers::TypeLocMatcher makeAutoPtrTypeLocMatcher();
/// \brief Creates a matcher that finds the using declarations referring to
/// \c std::auto_ptr.
///
/// \code
/// using std::auto_ptr;
/// ^~~~~~~~~~~~~~~~~~~
/// \endcode
clang::ast_matchers::DeclarationMatcher makeAutoPtrUsingDeclMatcher();
/// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and
/// assign-operator expressions.
///
/// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression,
/// this is the part that has to be wrapped by \c std::move().
///
/// \code
/// std::auto_ptr<int> i, j;
/// i = j;
/// ~~~~^
/// \endcode
clang::ast_matchers::StatementMatcher makeTransferOwnershipExprMatcher();
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H

View File

@@ -1,68 +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 clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseAutoTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
ReplacementsVec Replaces;
IteratorReplacer ReplaceIterators(AcceptedChanges, Options().MaxRiskLevel,
/*Owner=*/ *this);
NewReplacer ReplaceNew(AcceptedChanges, Options().MaxRiskLevel,
/*Owner=*/ *this);
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
if (int Result = UseAutoTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return Result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
struct UseAutoFactory : TransformFactory {
UseAutoFactory() {
Since.Clang = Version(2, 9);
Since.Gcc = Version(4, 4);
Since.Icc = Version(12);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new UseAutoTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<UseAutoFactory>
X("use-auto", "Use of 'auto' type specifier");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int UseAutoTransformAnchorSource = 0;

View File

@@ -1,41 +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 CLANG_MODERNIZE_USE_AUTO_H
#define CLANG_MODERNIZE_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(const TransformOptions &Options)
: Transform("UseAuto", Options) {}
/// \see Transform::run().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};
#endif // CLANG_MODERNIZE_USE_AUTO_H

View File

@@ -1,147 +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 DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
const Expr *ExprInit = V->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(V->getType(), E->getType()))
return;
}
// Get the type location using the first declartion.
const VarDecl *V = cast<VarDecl>(*D->decl_begin());
TypeLoc TL = V->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);
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto"));
++AcceptedChanges;
}
void NewReplacer::run(const MatchFinder::MatchResult &Result) {
const DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
const VarDecl *FirstDecl = cast<VarDecl>(*D->decl_begin());
// Ensure that there is at least one VarDecl within de DeclStmt.
assert(FirstDecl && "No VarDecl provided");
const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
std::vector<SourceLocation> StarLocations;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
// Ensure that every DeclStmt child is a VarDecl.
assert(V && "No VarDecl provided");
const CXXNewExpr *NewExpr =
cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
// Ensure that every VarDecl has a CXXNewExpr initializer.
assert(NewExpr && "No CXXNewExpr provided");
// If VarDecl and Initializer have mismatching unqualified types.
if (!Result.Context->hasSameUnqualifiedType(V->getType(),
NewExpr->getType()))
return;
// Remove explicitly written '*' from declarations where there's more than
// one declaration in the declaration list.
if (DI == D->decl_begin())
continue;
// All subsequent delcarations should match the same non-decorated type.
if (FirstDeclType != V->getType().getCanonicalType())
return;
PointerTypeLoc Q =
V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
while (!Q.isNull()) {
StarLocations.push_back(Q.getStarLoc());
Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
}
}
// Remove '*' from declarations using the saved star locations.
for (std::vector<SourceLocation>::iterator I = StarLocations.begin(),
E = StarLocations.end();
I != E; ++I) {
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, *I, 1, ""));
}
// 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.
CharSourceRange Range(
FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange(), 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'.
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto "));
++AcceptedChanges;
}

View File

@@ -1,56 +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 CLANG_MODERNIZE_USE_AUTO_ACTIONS_H
#define CLANG_MODERNIZE_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(unsigned &AcceptedChanges, RiskLevel, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
LLVM_OVERRIDE;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
/// \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(unsigned &AcceptedChanges, RiskLevel, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
LLVM_OVERRIDE;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_USE_AUTO_ACTIONS_H

View File

@@ -1,280 +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 "Core/CustomMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang;
const char *IteratorDeclStmtId = "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(NamedDecl, hasStdContainerName) {
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) {
if (hasName(ContainerNames[i]).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(), isFromStdNamespace())
)
)
)
);
}
// \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(), isFromStdNamespace())
)
)
)
);
}
// \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(
specifiesType(
templateSpecializationType(
hasDeclaration(
namedDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
),
// 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
// \brief This matcher returns delaration statements that contain variable
// declarations with written non-list initializer for standard iterators.
StatementMatcher makeIteratorDeclMatcher() {
return declStmt(
// At least one varDecl should be a child of the declStmt to ensure it's a
// declaration list and avoid matching other declarations
// e.g. using directives.
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasWrittenNonListInitializer()),
hasType(autoType()),
unless(hasType(
isSugarFor(
anyOf(
typedefIterator(),
nestedIterator(),
iteratorFromUsingDeclaration()
)
)
))
)
)))
).bind(IteratorDeclStmtId);
}
StatementMatcher makeDeclWithNewMatcher() {
return declStmt(
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasInitializer(
ignoringParenImpCasts(newExpr())
)),
// FIXME: TypeLoc information is not reliable where CV qualifiers are
// concerned so these types can't be handled for now.
hasType(pointerType(pointee(hasCanonicalType(hasLocalQualifiers())))),
// FIXME: Handle function pointers. For now we ignore them because
// the replacement replaces the entire type specifier source range
// which includes the identifier.
hasType(
pointsTo(
pointsTo(
parenType(innerType(functionType()))
)
)
)
)
)))
).bind(DeclWithNewId);
}

View File

@@ -1,34 +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 CLANG_MODERNIZE_USE_AUTO_MATCHERS_H
#define CLANG_MODERNIZE_USE_AUTO_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
extern const char *IteratorDeclStmtId;
extern const char *DeclWithNewId;
extern const char *NewExprId;
/// \brief Create a matcher that matches declaration staments that have
/// variable declarations where the type is an iterator for an std container
/// and has an explicit initializer of the same type.
clang::ast_matchers::StatementMatcher makeIteratorDeclMatcher();
/// \brief Create a matcher that matches variable declarations that are
/// initialized by a C++ new expression.
clang::ast_matchers::StatementMatcher makeDeclWithNewMatcher();
#endif // CLANG_MODERNIZE_USE_AUTO_MATCHERS_H

View File

@@ -1,443 +0,0 @@
//===-- UseNullptr/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 cl = llvm::cl;
namespace {
const char *NullMacroName = "NULL";
bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
const SourceManager &SM, const Transform &Owner) {
return SM.isWrittenInSameFile(StartLoc, EndLoc) &&
Owner.isFileModifiable(SM, 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(Transform &Owner, 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()))
Owner.addReplacementForCurrentTU(
tooling::Replacement(SM, Range, " nullptr"));
else
Owner.addReplacementForCurrentTU(
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 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 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(ASTContext &Context, const UserMacroNames &UserNullMacros,
unsigned &AcceptedChanges, Transform &Owner)
: SM(Context.getSourceManager()), Context(Context),
UserNullMacros(UserNullMacros), AcceptedChanges(AcceptedChanges),
Owner(Owner), 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, Owner) &&
allArgUsesValid(C)) {
ReplaceWithNullptr(Owner, 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, Owner)) {
return skipSubTree();
}
ReplaceWithNullptr(Owner, SM, StartLoc, EndLoc);
++AcceptedChanges;
return skipSubTree();
} // If NullTo(Member)Pointer cast.
return true;
}
private:
bool skipSubTree() { PruneSubtree = true; return true; }
/// \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) {
SourceLocation CastLoc = CE->getLocStart();
// Step 1: Get location of macro arg and location of the macro the arg was
// provided to.
SourceLocation ArgLoc, MacroLoc;
if (!getMacroAndArgLocations(CastLoc, 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))
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 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, 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();
SourceLocation OldArgLoc = ArgLoc;
ArgLoc = Expansion.getExpansionLocStart();
if (!Expansion.isMacroArgExpansion()) {
if (!MacroLoc.isFileID())
return false;
StringRef Name =
Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
return std::find(UserNullMacros.begin(), UserNullMacros.end(), Name) !=
UserNullMacros.end();
}
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) {
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) {
// 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)) {
Result = *I;
return true;
}
Start = *I;
} while (1);
llvm_unreachable("findContainingAncestor");
}
private:
SourceManager &SM;
ASTContext &Context;
const UserMacroNames &UserNullMacros;
unsigned &AcceptedChanges;
Transform &Owner;
Expr *FirstSubExpr;
bool PruneSubtree;
};
} // namespace
NullptrFixer::NullptrFixer(unsigned &AcceptedChanges,
llvm::ArrayRef<llvm::StringRef> UserMacros,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {
UserNullMacros.insert(UserNullMacros.begin(), UserMacros.begin(),
UserMacros.end());
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(*Result.Context, UserNullMacros, AcceptedChanges,
Owner);
Visitor.TraverseStmt(const_cast<CastExpr *>(NullCast));
}

View File

@@ -1,42 +0,0 @@
//===-- UseNullptr/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 CLANG_MODERNIZE_NULLPTR_ACTIONS_H
#define CLANG_MODERNIZE_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(unsigned &AcceptedChanges,
llvm::ArrayRef<llvm::StringRef> UserMacros, Transform &Owner);
/// \brief Entry point to the callback called when matches are made.
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
private:
unsigned &AcceptedChanges;
UserMacroNames UserNullMacros;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_NULLPTR_ACTIONS_H

View File

@@ -1,70 +0,0 @@
//===-- UseNullptr/NullptrMatchers.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,31 +0,0 @@
//===-- UseNullptr/NullptrMatchers.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 CLANG_MODERNIZE_USE_NULLPTR_MATCHERS_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_USE_NULLPTR_MATCHERS_H

View File

@@ -1,79 +0,0 @@
//===-- UseNullptr/UseNullptr.cpp - C++11 nullptr migration ---------------===//
//
// 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/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
namespace cl = llvm::cl;
static cl::opt<std::string>
UserNullMacroNames("user-null-macros",
cl::desc("Comma-separated list of user-defined "
"macro names that behave like NULL"),
cl::cat(TransformsOptionsCategory), cl::init(""));
int UseNullptrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseNullptrTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
llvm::SmallVector<llvm::StringRef, 1> MacroNames;
if (!UserNullMacroNames.empty()) {
llvm::StringRef S = UserNullMacroNames;
S.split(MacroNames, ",");
}
MatchFinder Finder;
NullptrFixer Fixer(AcceptedChanges, MacroNames, /*Owner=*/ *this);
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
if (int result = UseNullptrTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
struct UseNullptrFactory : TransformFactory {
UseNullptrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(12, 1);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new UseNullptrTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<UseNullptrFactory>
X("use-nullptr", "Make use of nullptr keyword where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int UseNullptrTransformAnchorSource = 0;

View File

@@ -1,35 +0,0 @@
//===-- UseNullptr/UseNullptr.h - C++11 nullptr 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 CLANG_MODERNIZE_USE_NULLPTR_H
#define CLANG_MODERNIZE_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(const TransformOptions &Options)
: Transform("UseNullptr", Options) {}
/// \see Transform::run().
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};
#endif // CLANG_MODERNIZE_USE_NULLPTR_H

View File

@@ -1,41 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
set (ClangModernizeSources
ClangModernize.cpp
)
# FIXME: Lib-ify the transforms to simplify the build rules.
# For each transform subdirectory.
file(GLOB_RECURSE LoopConvertSources "../LoopConvert/*.cpp")
list(APPEND ClangModernizeSources ${LoopConvertSources})
file(GLOB_RECURSE UseNullptrSources "../UseNullptr/*.cpp")
list(APPEND ClangModernizeSources ${UseNullptrSources})
file(GLOB_RECURSE UseAutoSources "../UseAuto/*.cpp")
list(APPEND ClangModernizeSources ${UseAutoSources})
file(GLOB_RECURSE AddOverrideSources "../AddOverride/*.cpp")
list(APPEND ClangModernizeSources ${AddOverrideSources})
file(GLOB_RECURSE PassByValueSources "../PassByValue/*.cpp")
list(APPEND ClangModernizeSources ${PassByValueSources})
file(GLOB_RECURSE ReplaceAutoPtrSources "../ReplaceAutoPtr/*.cpp")
list(APPEND ClangModernizeSources ${ReplaceAutoPtrSources})
add_clang_executable(clang-modernize
${ClangModernizeSources}
)
add_dependencies(clang-modernize
clang-headers clang-apply-replacements
)
target_link_libraries(clang-modernize
modernizeCore
)
install(TARGETS clang-modernize
RUNTIME DESTINATION bin)

View File

@@ -1,503 +0,0 @@
//===-- ClangModernize.cpp - Main file for Clang modernization 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/PerfSupport.h"
#include "Core/ReplacementHandling.h"
#include "Core/Transform.h"
#include "Core/Transforms.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
namespace cl = llvm::cl;
using namespace clang;
using namespace clang::tooling;
TransformOptions GlobalOptions;
// All options must belong to locally defined categories for them to get shown
// by -help. We explicitly hide everything else (except -help and -version).
static cl::OptionCategory GeneralCategory("Modernizer Options");
static cl::OptionCategory FormattingCategory("Formatting Options");
static cl::OptionCategory IncludeExcludeCategory("Inclusion/Exclusion Options");
static cl::OptionCategory SerializeCategory("Serialization Options");
const cl::OptionCategory *VisibleCategories[] = {
&GeneralCategory, &FormattingCategory, &IncludeExcludeCategory,
&SerializeCategory, &TransformCategory, &TransformsOptionsCategory,
};
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
"EXAMPLES:\n\n"
"Apply all transforms on a file that doesn't require compilation arguments:\n\n"
" clang-modernize file.cpp\n"
"\n"
"Convert for loops to ranged-based for loops for all files in the compilation\n"
"database that belong in a project subtree and then reformat the code\n"
"automatically using the LLVM style:\n\n"
" clang-modernize -p build/path -include project/path -format -loop-convert\n"
"\n"
"Make use of both nullptr and the override specifier, using git ls-files:\n"
"\n"
" git ls-files '*.cpp' | xargs -I{} clang-modernize -p build/path \\\n"
" -use-nullptr -add-override -override-macros {}\n"
"\n"
"Apply all transforms supported by both clang >= 3.0 and gcc >= 4.7 to\n"
"foo.cpp and any included headers in bar:\n\n"
" clang-modernize -for-compilers=clang-3.0,gcc-4.7 foo.cpp \\\n"
" -include bar -- -std=c++11 -Ibar\n\n");
////////////////////////////////////////////////////////////////////////////////
/// General Options
// This is set to hidden on purpose. The actual help text for this option is
// included in CommonOptionsParser::HelpMessage.
static cl::opt<std::string> BuildPath("p", cl::desc("Build Path"), cl::Optional,
cl::Hidden, cl::cat(GeneralCategory));
static cl::list<std::string> SourcePaths(cl::Positional,
cl::desc("[<sources>...]"),
cl::ZeroOrMore,
cl::cat(GeneralCategory));
static cl::opt<RiskLevel, /*ExternalStorage=*/true> 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::location(GlobalOptions.MaxRiskLevel), cl::init(RL_Reasonable),
cl::cat(GeneralCategory));
static cl::opt<bool> FinalSyntaxCheck(
"final-syntax-check",
cl::desc("Check for correct syntax after applying transformations"),
cl::init(false), cl::cat(GeneralCategory));
static cl::opt<bool> SummaryMode("summary", cl::desc("Print transform summary"),
cl::init(false), cl::cat(GeneralCategory));
static cl::opt<std::string>
TimingDirectoryName("perf",
cl::desc("Capture performance data and output to specified "
"directory. Default: ./migrate_perf"),
cl::ValueOptional, cl::value_desc("directory name"),
cl::cat(GeneralCategory));
cl::opt<std::string> SupportedCompilers(
"for-compilers", cl::value_desc("string"),
cl::desc("Select transforms targeting the intersection of\n"
"language features supported by the given compilers.\n"
"Takes a comma-separated list of <compiler>-<version>.\n"
"\t<compiler> can be any of: clang, gcc, icc, msvc\n"
"\t<version> is <major>[.<minor>]\n"),
cl::cat(GeneralCategory));
////////////////////////////////////////////////////////////////////////////////
/// Format Options
static cl::opt<bool> DoFormat(
"format",
cl::desc("Enable formatting of code changed by applying replacements.\n"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
////////////////////////////////////////////////////////////////////////////////
/// Include/Exclude Options
static cl::opt<std::string>
IncludePaths("include",
cl::desc("Comma-separated list of paths to consider to be "
"transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludePaths("exclude", cl::desc("Comma-separated list of paths that can not "
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
IncludeFromFile("include-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths to consider to "
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludeFromFile("exclude-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths that can not be "
"transformed"),
cl::cat(IncludeExcludeCategory));
////////////////////////////////////////////////////////////////////////////////
/// Serialization Options
static cl::opt<bool>
SerializeOnly("serialize-replacements",
cl::desc("Serialize translation unit replacements to "
"disk instead of changing files."),
cl::init(false),
cl::cat(SerializeCategory));
static cl::opt<std::string>
SerializeLocation("serialize-dir",
cl::desc("Path to an existing directory in which to write\n"
"serialized replacements. Default behaviour is to\n"
"write to a temporary directory.\n"),
cl::cat(SerializeCategory));
////////////////////////////////////////////////////////////////////////////////
void printVersion() {
llvm::outs() << "clang-modernizer version " CLANG_VERSION_STRING
<< "\n";
}
/// \brief Extract the minimum compiler versions as requested on the command
/// line by the switch \c -for-compilers.
///
/// \param ProgName The name of the program, \c argv[0], used to print errors.
/// \param Error If an error occur while parsing the versions this parameter is
/// set to \c true, otherwise it will be left untouched.
static CompilerVersions handleSupportedCompilers(const char *ProgName,
bool &Error) {
if (SupportedCompilers.getNumOccurrences() == 0)
return CompilerVersions();
CompilerVersions RequiredVersions;
llvm::SmallVector<llvm::StringRef, 4> Compilers;
llvm::StringRef(SupportedCompilers).split(Compilers, ",");
for (llvm::SmallVectorImpl<llvm::StringRef>::iterator I = Compilers.begin(),
E = Compilers.end();
I != E; ++I) {
llvm::StringRef Compiler, VersionStr;
llvm::tie(Compiler, VersionStr) = I->split('-');
Version *V = llvm::StringSwitch<Version *>(Compiler)
.Case("clang", &RequiredVersions.Clang)
.Case("gcc", &RequiredVersions.Gcc).Case("icc", &RequiredVersions.Icc)
.Case("msvc", &RequiredVersions.Msvc).Default(NULL);
if (V == NULL) {
llvm::errs() << ProgName << ": " << Compiler
<< ": unsupported platform\n";
Error = true;
continue;
}
if (VersionStr.empty()) {
llvm::errs() << ProgName << ": " << *I
<< ": missing version number in platform\n";
Error = true;
continue;
}
Version Version = Version::getFromString(VersionStr);
if (Version.isNull()) {
llvm::errs()
<< ProgName << ": " << *I
<< ": invalid version, please use \"<major>[.<minor>]\" instead of \""
<< VersionStr << "\"\n";
Error = true;
continue;
}
// support the lowest version given
if (V->isNull() || Version < *V)
*V = Version;
}
return RequiredVersions;
}
CompilationDatabase *autoDetectCompilations(std::string &ErrorMessage) {
// Auto-detect a compilation database from BuildPath.
if (BuildPath.getNumOccurrences() > 0)
return CompilationDatabase::autoDetectFromDirectory(BuildPath,
ErrorMessage);
// Try to auto-detect a compilation database from the first source.
if (!SourcePaths.empty()) {
if (CompilationDatabase *Compilations =
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
ErrorMessage)) {
// FIXME: just pass SourcePaths[0] once getCompileCommands supports
// non-absolute paths.
SmallString<64> Path(SourcePaths[0]);
llvm::sys::fs::make_absolute(Path);
std::vector<CompileCommand> Commands =
Compilations->getCompileCommands(Path);
// Ignore a detected compilation database that doesn't contain source0
// since it is probably an unrelated compilation database.
if (!Commands.empty())
return Compilations;
}
// Reset ErrorMessage since a fix compilation database will be created if
// it fails to detect one from source.
ErrorMessage = "";
// If no compilation database can be detected from source then we create a
// fixed compilation database with c++11 support.
std::string CommandLine[] = { "-std=c++11" };
return new FixedCompilationDatabase(".", CommandLine);
}
ErrorMessage = "Could not determine sources to transform";
return 0;
}
// Predicate definition for determining whether a file is not included.
static bool isFileNotIncludedPredicate(llvm::StringRef FilePath) {
return !GlobalOptions.ModifiableFiles.isFileIncluded(FilePath);
}
// Predicate definition for determining if a file was explicitly excluded.
static bool isFileExplicitlyExcludedPredicate(llvm::StringRef FilePath) {
if (GlobalOptions.ModifiableFiles.isFileExplicitlyExcluded(FilePath)) {
llvm::errs() << "Warning \"" << FilePath << "\" will not be transformed "
<< "because it's in the excluded list.\n";
return true;
}
return false;
}
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal();
Transforms TransformManager;
ReplacementHandling ReplacementHandler;
TransformManager.registerTransforms();
// Hide all options we don't define ourselves. Move pre-defined 'help',
// 'help-list', and 'version' to our general category.
llvm::StringMap<cl::Option*> Options;
cl::getRegisteredOptions(Options);
const cl::OptionCategory **CategoryEnd =
VisibleCategories + llvm::array_lengthof(VisibleCategories);
for (llvm::StringMap<cl::Option *>::iterator I = Options.begin(),
E = Options.end();
I != E; ++I) {
if (I->first() == "help" || I->first() == "version" ||
I->first() == "help-list")
I->second->setCategory(GeneralCategory);
else if (std::find(VisibleCategories, CategoryEnd, I->second->Category) ==
CategoryEnd)
I->second->setHiddenFlag(cl::ReallyHidden);
}
cl::SetVersionPrinter(&printVersion);
// Parse options and generate compilations.
OwningPtr<CompilationDatabase> Compilations(
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
cl::ParseCommandLineOptions(argc, argv);
// Populate the ModifiableFiles structure.
GlobalOptions.ModifiableFiles.readListFromString(IncludePaths, ExcludePaths);
GlobalOptions.ModifiableFiles.readListFromFile(IncludeFromFile,
ExcludeFromFile);
if (!Compilations) {
std::string ErrorMessage;
Compilations.reset(autoDetectCompilations(ErrorMessage));
if (!Compilations) {
llvm::errs() << llvm::sys::path::filename(argv[0]) << ": " << ErrorMessage
<< "\n";
return 1;
}
}
// Populate source files.
std::vector<std::string> Sources;
if (!SourcePaths.empty()) {
// Use only files that are not explicitly excluded.
std::remove_copy_if(SourcePaths.begin(), SourcePaths.end(),
std::back_inserter(Sources),
isFileExplicitlyExcludedPredicate);
} else {
if (GlobalOptions.ModifiableFiles.isIncludeListEmpty()) {
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": Use -include to indicate which files of "
<< "the compilatiion database to transform.\n";
return 1;
}
// Use source paths from the compilation database.
// We only transform files that are explicitly included.
Sources = Compilations->getAllFiles();
std::vector<std::string>::iterator E = std::remove_if(
Sources.begin(), Sources.end(), isFileNotIncludedPredicate);
Sources.erase(E, Sources.end());
}
if (Sources.empty()) {
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": Could not determine sources to transform.\n";
return 1;
}
// Enable timming.
GlobalOptions.EnableTiming = TimingDirectoryName.getNumOccurrences() > 0;
bool CmdSwitchError = false;
CompilerVersions RequiredVersions =
handleSupportedCompilers(argv[0], CmdSwitchError);
if (CmdSwitchError)
return 1;
TransformManager.createSelectedTransforms(GlobalOptions, RequiredVersions);
if (TransformManager.begin() == TransformManager.end()) {
if (SupportedCompilers.empty())
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": no selected transforms\n";
else
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": no transforms available for specified compilers\n";
return 1;
}
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
// FIXME: Make this DiagnosticsEngine available to all Transforms probably via
// GlobalOptions.
// If SerializeReplacements is requested, then code reformatting must be
// turned off and only one transform should be requested.
if (SerializeOnly &&
(std::distance(TransformManager.begin(), TransformManager.end()) > 1 ||
DoFormat)) {
llvm::errs() << "Serialization of replacements requested for multiple "
"transforms.\nChanges from only one transform can be "
"serialized.\n";
return 1;
}
// If we're asked to apply changes to files on disk, need to locate
// clang-apply-replacements.
if (!SerializeOnly) {
if (!ReplacementHandler.findClangApplyReplacements(argv[0])) {
llvm::errs() << "Could not find clang-apply-replacements\n";
return 1;
}
if (DoFormat)
ReplacementHandler.enableFormatting(FormatStyleOpt, FormatStyleConfig);
}
StringRef TempDestinationDir;
if (SerializeLocation.getNumOccurrences() > 0)
ReplacementHandler.setDestinationDir(SerializeLocation);
else
TempDestinationDir = ReplacementHandler.useTempDestinationDir();
SourcePerfData PerfData;
for (Transforms::const_iterator I = TransformManager.begin(),
E = TransformManager.end();
I != E; ++I) {
Transform *T = *I;
if (T->apply(*Compilations, Sources) != 0) {
// FIXME: Improve ClangTool to not abort if just one file fails.
return 1;
}
if (GlobalOptions.EnableTiming)
collectSourcePerfData(*T, PerfData);
if (SummaryMode) {
llvm::outs() << "Transform: " << T->getName()
<< " - Accepted: " << T->getAcceptedChanges();
if (T->getChangesNotMade()) {
llvm::outs() << " - Rejected: " << T->getRejectedChanges()
<< " - Deferred: " << T->getDeferredChanges();
}
llvm::outs() << "\n";
}
if (!ReplacementHandler.serializeReplacements(T->getAllReplacements()))
return 1;
if (!SerializeOnly)
if (!ReplacementHandler.applyReplacements())
return 1;
}
// Let the user know which temporary directory the replacements got written
// to.
if (SerializeOnly && !TempDestinationDir.empty())
llvm::errs() << "Replacements serialized to: " << TempDestinationDir << "\n";
if (FinalSyntaxCheck) {
ClangTool SyntaxTool(*Compilations, SourcePaths);
if (SyntaxTool.run(newFrontendActionFactory<SyntaxOnlyAction>()) != 0)
return 1;
}
// Report execution times.
if (GlobalOptions.EnableTiming && !PerfData.empty()) {
std::string DirectoryName = TimingDirectoryName;
// Use default directory name.
if (DirectoryName.empty())
DirectoryName = "./migrate_perf";
writePerfDataJSON(DirectoryName, PerfData);
}
return 0;
}
// These anchors are used to force the linker to link the transforms
extern volatile int AddOverrideTransformAnchorSource;
extern volatile int LoopConvertTransformAnchorSource;
extern volatile int PassByValueTransformAnchorSource;
extern volatile int ReplaceAutoPtrTransformAnchorSource;
extern volatile int UseAutoTransformAnchorSource;
extern volatile int UseNullptrTransformAnchorSource;
static int TransformsAnchorsDestination[] = {
AddOverrideTransformAnchorSource,
LoopConvertTransformAnchorSource,
PassByValueTransformAnchorSource,
ReplaceAutoPtrTransformAnchorSource,
UseAutoTransformAnchorSource,
UseNullptrTransformAnchorSource
};

View File

@@ -1,57 +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 = clang-modernize
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
SOURCES = ClangModernize.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
SOURCES += $(addprefix ../PassByValue/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../PassByValue/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../PassByValue/.objdir
SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../ReplaceAutoPtr/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
USEDLIBS = modernizeCore.a clangFormat.a 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
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,13 +0,0 @@
add_clang_library(clangQuery
Query.cpp
QueryParser.cpp
)
target_link_libraries(clangQuery
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
)
add_subdirectory(tool)

View File

@@ -1,14 +0,0 @@
##===- tools/extra/clang-query/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 := clangQuery
include $(CLANG_LEVEL)/../../Makefile.config
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,133 +0,0 @@
//===---- Query.cpp - clang-query query -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
Query::~Query() {}
bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << ErrStr << "\n";
return false;
}
bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
return true;
}
bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << "Available commands:\n\n"
" match MATCHER, m MATCHER "
"Match the loaded ASTs against the given matcher.\n"
" set bind-root (true|false) "
"Set whether to bind the root matcher to \"root\".\n"
" set output (diag|print|dump) "
"Set whether to print bindings as diagnostics,\n"
" "
"AST pretty prints or AST dumps.\n\n";
return true;
}
namespace {
struct CollectBoundNodes : MatchFinder::MatchCallback {
std::vector<BoundNodes> &Bindings;
CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
void run(const MatchFinder::MatchResult &Result) {
Bindings.push_back(Result.Nodes);
}
};
}
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
for (llvm::ArrayRef<ASTUnit *>::iterator I = QS.ASTs.begin(),
E = QS.ASTs.end();
I != E; ++I) {
ASTUnit *AST = *I;
MatchFinder Finder;
std::vector<BoundNodes> Matches;
DynTypedMatcher MaybeBoundMatcher = Matcher;
if (QS.BindRoot) {
llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
if (M)
MaybeBoundMatcher = *M;
}
CollectBoundNodes Collect(Matches);
if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
OS << "Not a valid top-level matcher.\n";
return false;
}
Finder.matchAST(AST->getASTContext());
for (std::vector<BoundNodes>::iterator MI = Matches.begin(),
ME = Matches.end();
MI != ME; ++MI) {
OS << "\nMatch #" << ++MatchCount << ":\n\n";
for (BoundNodes::IDToNodeMap::const_iterator BI = MI->getMap().begin(),
BE = MI->getMap().end();
BI != BE; ++BI) {
switch (QS.OutKind) {
case OK_Diag: {
clang::SourceRange R = BI->second.getSourceRange();
if (R.isValid()) {
TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
&AST->getDiagnostics().getDiagnosticOptions());
TD.emitDiagnostic(
R.getBegin(), DiagnosticsEngine::Note,
"\"" + BI->first + "\" binds here",
ArrayRef<CharSourceRange>(CharSourceRange::getTokenRange(R)),
ArrayRef<FixItHint>(), &AST->getSourceManager());
}
break;
}
case OK_Print: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
OS << "\n";
break;
}
case OK_Dump: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.dump(OS, AST->getSourceManager());
OS << "\n";
break;
}
}
}
if (MI->getMap().empty())
OS << "No bindings.\n";
}
}
OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
return true;
}
#ifndef _MSC_VER
const QueryKind SetQueryKind<bool>::value;
const QueryKind SetQueryKind<OutputKind>::value;
#endif
} // namespace query
} // namespace clang

View File

@@ -1,119 +0,0 @@
//===--- Query.h - clang-query ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
#include <string>
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
namespace clang {
namespace query {
enum OutputKind {
OK_Diag,
OK_Print,
OK_Dump
};
enum QueryKind {
QK_Invalid,
QK_NoOp,
QK_Help,
QK_Match,
QK_SetBool,
QK_SetOutputKind
};
class QuerySession;
struct Query : llvm::RefCountedBase<Query> {
Query(QueryKind Kind) : Kind(Kind) {}
virtual ~Query();
/// Perform the query on \p QS and print output to \p OS.
///
/// \return false if an error occurs, otherwise return true.
virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
const QueryKind Kind;
};
typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
/// Any query which resulted in a parse error. The error message is in ErrStr.
struct InvalidQuery : Query {
InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
std::string ErrStr;
static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
};
/// No-op query (i.e. a blank line).
struct NoOpQuery : Query {
NoOpQuery() : Query(QK_NoOp) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
};
/// Query for "help".
struct HelpQuery : Query {
HelpQuery() : Query(QK_Help) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
};
/// Query for "match MATCHER".
struct MatchQuery : Query {
MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
: Query(QK_Match), Matcher(Matcher) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
ast_matchers::dynamic::DynTypedMatcher Matcher;
static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
};
template <typename T> struct SetQueryKind {};
template <> struct SetQueryKind<bool> {
static const QueryKind value = QK_SetBool;
};
template <> struct SetQueryKind<OutputKind> {
static const QueryKind value = QK_SetOutputKind;
};
/// Query for "set VAR VALUE".
template <typename T> struct SetQuery : Query {
SetQuery(T QuerySession::*Var, T Value)
: Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE {
QS.*Var = Value;
return true;
}
static bool classof(const Query *Q) {
return Q->Kind == SetQueryKind<T>::value;
}
T QuerySession::*Var;
T Value;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,167 +0,0 @@
//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
using namespace llvm;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
// Lex any amount of whitespace followed by a "word" (any sequence of
// non-whitespace characters) from the start of region [Begin,End). If no word
// is found before End, return StringRef(). Begin is adjusted to exclude the
// lexed region.
static StringRef LexWord(const char *&Begin, const char *End) {
while (true) {
if (Begin == End)
return StringRef();
if (!isWhitespace(*Begin))
break;
++Begin;
}
const char *WordBegin = Begin;
while (true) {
++Begin;
if (Begin == End || isWhitespace(*Begin))
return StringRef(WordBegin, Begin - WordBegin);
}
}
static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
unsigned Value = StringSwitch<unsigned>(ValStr)
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
if (Value == ~0u) {
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
}
return new SetQuery<bool>(Var, Value);
}
static QueryRef ParseSetOutputKind(StringRef ValStr) {
unsigned OutKind = StringSwitch<unsigned>(ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("dump", OK_Dump)
.Default(~0u);
if (OutKind == ~0u) {
return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
ValStr + "'");
}
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}
static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
const char *Extra = Begin;
if (!LexWord(Begin, End).empty())
return new InvalidQuery("unexpected extra input: '" +
StringRef(Extra, End - Extra) + "'");
return Q;
}
enum ParsedQueryKind {
PQK_Invalid,
PQK_NoOp,
PQK_Help,
PQK_Match,
PQK_Set
};
enum ParsedQueryVariable {
PQV_Invalid,
PQV_Output,
PQV_BindRoot
};
QueryRef ParseQuery(StringRef Line) {
const char *Begin = Line.data();
const char *End = Line.data() + Line.size();
StringRef CommandStr = LexWord(Begin, End);
ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
.Case("", PQK_NoOp)
.Case("help", PQK_Help)
.Case("m", PQK_Match)
.Case("match", PQK_Match)
.Case("set", PQK_Set)
.Default(PQK_Invalid);
switch (QKind) {
case PQK_NoOp:
return new NoOpQuery;
case PQK_Help:
return EndQuery(Begin, End, new HelpQuery);
case PQK_Match: {
Diagnostics Diag;
Optional<DynTypedMatcher> Matcher =
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
if (!Matcher) {
std::string ErrStr;
llvm::raw_string_ostream OS(ErrStr);
Diag.printToStreamFull(OS);
return new InvalidQuery(OS.str());
}
return new MatchQuery(*Matcher);
}
case PQK_Set: {
StringRef VarStr = LexWord(Begin, End);
if (VarStr.empty())
return new InvalidQuery("expected variable name");
ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
.Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot)
.Default(PQV_Invalid);
if (Var == PQV_Invalid)
return new InvalidQuery("unknown variable: '" + VarStr + "'");
StringRef ValStr = LexWord(Begin, End);
if (ValStr.empty())
return new InvalidQuery("expected variable value");
QueryRef Q;
switch (Var) {
case PQV_Output:
Q = ParseSetOutputKind(ValStr);
break;
case PQV_BindRoot:
Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
break;
case PQV_Invalid:
llvm_unreachable("Invalid query kind");
}
return EndQuery(Begin, End, Q);
}
case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
llvm_unreachable("Invalid query kind");
}
} // namespace query
} // namespace clang

View File

@@ -1,27 +0,0 @@
//===--- QueryParser.h - clang-query ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
#include "Query.h"
namespace clang {
namespace query {
/// \brief Parse \p Line.
///
/// \return A reference to the parsed query object, which may be an
/// \c InvalidQuery if a parse error occurs.
QueryRef ParseQuery(StringRef Line);
} // namespace query
} // namespace clang
#endif

View File

@@ -1,36 +0,0 @@
//===--- QuerySession.h - clang-query ---------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
#include "llvm/ADT/ArrayRef.h"
#include "Query.h"
namespace clang {
class ASTUnit;
namespace query {
/// Represents the state for a particular clang-query session.
class QuerySession {
public:
QuerySession(llvm::ArrayRef<ASTUnit *> ASTs)
: ASTs(ASTs), OutKind(OK_Diag), BindRoot(true) {}
llvm::ArrayRef<ASTUnit *> ASTs;
OutputKind OutKind;
bool BindRoot;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,11 +0,0 @@
if(HAVE_LIBEDIT)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
add_clang_executable(clang-query ClangQuery.cpp)
target_link_libraries(clang-query
edit
clangFrontend
clangQuery
clangTooling
)
endif()

View File

@@ -1,158 +0,0 @@
//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool is for interactive exploration of the Clang AST using AST matchers.
// It currently allows the user to enter a matcher at an interactive prompt and
// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
// Example session:
//
// $ cat foo.c
// void foo(void) {}
// $ clang-query foo.c --
// clang-query> match functionDecl()
//
// Match #1:
//
// foo.c:1:1: note: "root" binds here
// void foo(void) {}
// ^~~~~~~~~~~~~~~~~
// 1 match.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QuerySession.h"
#include "QueryParser.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
#include <fstream>
#include <string>
#include <histedit.h>
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
using namespace clang::query;
using namespace clang::tooling;
using namespace llvm;
static cl::opt<std::string> BuildPath("b", cl::desc("Specify build path"),
cl::value_desc("<path>"));
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
cl::value_desc("<command>"));
static cl::list<std::string> CommandFiles("f",
cl::desc("Read commands from file"),
cl::value_desc("<file>"));
static cl::list<std::string> SourcePaths(cl::Positional,
cl::desc("<source0> [... <sourceN>]"),
cl::OneOrMore);
static char *ReturnPrompt(EditLine *EL) {
static char Prompt[] = "clang-query> ";
return Prompt;
}
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal();
cl::ParseCommandLineOptions(argc, argv);
if (!Commands.empty() && !CommandFiles.empty()) {
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
return 1;
}
llvm::OwningPtr<CompilationDatabase> Compilations(
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
if (!Compilations) { // Couldn't find a compilation DB from the command line
std::string ErrorMessage;
Compilations.reset(
!BuildPath.empty() ?
CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
);
// Still no compilation DB? - bail.
if (!Compilations)
llvm::report_fatal_error(ErrorMessage);
}
ClangTool Tool(*Compilations, SourcePaths);
std::vector<ASTUnit *> ASTs;
if (Tool.buildASTs(ASTs) != 0)
return 1;
QuerySession QS(ASTs);
if (!Commands.empty()) {
for (cl::list<std::string>::iterator I = Commands.begin(),
E = Commands.end();
I != E; ++I) {
QueryRef Q = ParseQuery(I->c_str());
if (!Q->run(llvm::outs(), QS))
return 1;
}
} else if (!CommandFiles.empty()) {
for (cl::list<std::string>::iterator I = CommandFiles.begin(),
E = CommandFiles.end();
I != E; ++I) {
std::ifstream Input(I->c_str());
if (!Input.is_open()) {
llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
return 1;
}
while (Input.good()) {
std::string Line;
std::getline(Input, Line);
QueryRef Q = ParseQuery(Line.c_str());
if (!Q->run(llvm::outs(), QS))
return 1;
}
}
} else {
History *Hist = history_init();
HistEvent Event;
history(Hist, &Event, H_SETSIZE, 100);
EditLine *EL = el_init("clang-query", stdin, stdout, stderr);
el_set(EL, EL_PROMPT, ReturnPrompt);
el_set(EL, EL_EDITOR, "emacs");
el_set(EL, EL_HIST, history, Hist);
int Count;
while (const char *Line = el_gets(EL, &Count)) {
if (Count == 0)
break;
history(Hist, &Event, H_ENTER, Line);
QueryRef Q = ParseQuery(Line);
Q->run(llvm::outs(), QS);
}
history_end(Hist);
el_end(EL);
llvm::outs() << "\n";
}
llvm::DeleteContainerPointers(ASTs);
return 0;
}

View File

@@ -1,23 +0,0 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
asmparser
bitreader
support
mc
)
add_clang_library(clangTidy
ClangTidy.cpp
ClangTidyModule.cpp
)
target_link_libraries(clangTidy
clangTooling
clangBasic
clangRewriteFrontend
clangStaticAnalyzerFrontend
clangStaticAnalyzerCheckers
)
add_subdirectory(tool)
add_subdirectory(llvm)
add_subdirectory(google)

View File

@@ -1,279 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements a clang-tidy tool.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidy.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyModuleRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Frontend/FixItRewriter.h"
#include "clang/Rewrite/Frontend/FrontendActions.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include <vector>
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
namespace clang {
namespace tidy {
namespace {
/// \brief A combined ASTConsumer that forwards calls to two different
/// consumers.
///
/// FIXME: This currently forwards just enough methods for the static analyzer
/// and the \c MatchFinder's consumer to work; expand this to all methods of
/// ASTConsumer and put it into a common location.
class CombiningASTConsumer : public ASTConsumer {
public:
CombiningASTConsumer(ASTConsumer *Consumer1, ASTConsumer *Consumer2)
: Consumer1(Consumer1), Consumer2(Consumer2) {}
virtual void Initialize(ASTContext &Context) LLVM_OVERRIDE {
Consumer1->Initialize(Context);
Consumer2->Initialize(Context);
}
virtual bool HandleTopLevelDecl(DeclGroupRef D) LLVM_OVERRIDE {
return Consumer1->HandleTopLevelDecl(D) && Consumer2->HandleTopLevelDecl(D);
}
virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) LLVM_OVERRIDE {
Consumer1->HandleTopLevelDeclInObjCContainer(D);
Consumer2->HandleTopLevelDeclInObjCContainer(D);
}
virtual void HandleTranslationUnit(ASTContext &Context) LLVM_OVERRIDE {
Consumer1->HandleTranslationUnit(Context);
Consumer2->HandleTranslationUnit(Context);
}
private:
llvm::OwningPtr<ASTConsumer> Consumer1;
llvm::OwningPtr<ASTConsumer> Consumer2;
};
/// \brief Action that runs clang-tidy and static analyzer checks.
///
/// FIXME: Note that this inherits from \c AnalysisAction as this is the only
/// way we can currently get to AnalysisAction::CreateASTConsumer. Ideally
/// we'd want to build a more generic way to use \c FrontendAction based
/// checkers in clang-tidy, but that needs some preparation work first.
class ClangTidyAction : public ento::AnalysisAction {
public:
ClangTidyAction(StringRef CheckRegexString,
SmallVectorImpl<ClangTidyCheck *> &Checks,
ClangTidyContext &Context, MatchFinder &Finder)
: CheckRegexString(CheckRegexString), Checks(Checks), Context(Context),
Finder(Finder) {}
private:
clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
StringRef File) LLVM_OVERRIDE {
AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
llvm::Regex CheckRegex(CheckRegexString);
// Always add all core checkers if any other static analyzer checks are
// enabled. This is currently necessary, as other path sensitive checks rely
// on the core checkers.
if (CheckRegex.match("clang-analyzer-"))
Options->CheckersControlList.push_back(std::make_pair("core", true));
// Run our regex against all possible static analyzer checkers.
// Note that debug checkers print values / run programs to visualize the CFG
// and are thus not applicable to clang-tidy in general.
#define GET_CHECKERS
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
if (!StringRef(FULLNAME).startswith("core") && \
!StringRef(FULLNAME).startswith("debug") && \
CheckRegex.match("clang-analyzer-" FULLNAME)) \
Options->CheckersControlList.push_back(std::make_pair(FULLNAME, true));
#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
#undef GET_CHECKERS
Options->AnalysisStoreOpt = RegionStoreModel;
Options->AnalysisDiagOpt = PD_TEXT;
Options->AnalyzeNestedBlocks = true;
Options->eagerlyAssumeBinOpBifurcation = true;
return new CombiningASTConsumer(
Finder.newASTConsumer(),
ento::AnalysisAction::CreateASTConsumer(Compiler, File));
}
virtual bool BeginSourceFileAction(CompilerInstance &Compiler,
llvm::StringRef Filename) LLVM_OVERRIDE {
if (!ento::AnalysisAction::BeginSourceFileAction(Compiler, Filename))
return false;
Context.setSourceManager(&Compiler.getSourceManager());
for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
E = Checks.end();
I != E; ++I)
(*I)->registerPPCallbacks(Compiler);
return true;
}
std::string CheckRegexString;
SmallVectorImpl<ClangTidyCheck *> &Checks;
ClangTidyContext &Context;
MatchFinder &Finder;
};
class ClangTidyActionFactory : public FrontendActionFactory {
public:
ClangTidyActionFactory(StringRef CheckRegexString, ClangTidyContext &Context)
: CheckRegexString(CheckRegexString), Context(Context) {
ClangTidyCheckFactories CheckFactories;
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I) {
OwningPtr<ClangTidyModule> Module(I->instantiate());
Module->addCheckFactories(CheckFactories);
}
SmallVector<ClangTidyCheck *, 16> Checks;
CheckFactories.createChecks(CheckRegexString, Checks);
for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
E = Checks.end();
I != E; ++I) {
(*I)->setContext(&Context);
(*I)->registerMatchers(&Finder);
}
}
virtual FrontendAction *create() {
return new ClangTidyAction(CheckRegexString, Checks, Context, Finder);
}
private:
std::string CheckRegexString;
SmallVector<ClangTidyCheck *, 8> Checks;
ClangTidyContext &Context;
MatchFinder Finder;
};
} // namespace
ClangTidyMessage::ClangTidyMessage(StringRef Message) : Message(Message) {}
ClangTidyMessage::ClangTidyMessage(StringRef Message,
const SourceManager &Sources,
SourceLocation Loc)
: Message(Message) {
FilePath = Sources.getFilename(Loc);
FileOffset = Sources.getFileOffset(Loc);
}
ClangTidyError::ClangTidyError(const ClangTidyMessage &Message)
: Message(Message) {}
DiagnosticBuilder ClangTidyContext::Diag(SourceLocation Loc,
StringRef Message) {
return DiagEngine->Report(
Loc, DiagEngine->getCustomDiagID(DiagnosticsEngine::Warning, Message));
}
void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
DiagEngine = Engine;
}
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
DiagEngine->setSourceManager(SourceMgr);
}
/// \brief Store a \c ClangTidyError.
void ClangTidyContext::storeError(const ClangTidyError &Error) {
Errors->push_back(Error);
}
void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Context->setSourceManager(Result.SourceManager);
check(Result);
}
FrontendActionFactory *createClangTidyActionFactory(StringRef CheckRegexString,
ClangTidyContext &Context) {
return new ClangTidyActionFactory(CheckRegexString, Context);
}
void runClangTidy(StringRef CheckRegexString,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> Ranges,
SmallVectorImpl<ClangTidyError> *Errors) {
// FIXME: Ranges are currently full files. Support selecting specific
// (line-)ranges.
ClangTool Tool(Compilations, Ranges);
clang::tidy::ClangTidyContext Context(Errors);
ClangTidyDiagnosticConsumer DiagConsumer(Context);
Tool.setDiagnosticConsumer(&DiagConsumer);
Tool.run(createClangTidyActionFactory(CheckRegexString, Context));
}
static void reportDiagnostic(const ClangTidyMessage &Message,
SourceManager &SourceMgr,
DiagnosticsEngine::Level Level,
DiagnosticsEngine &Diags) {
SourceLocation Loc;
if (!Message.FilePath.empty()) {
const FileEntry *File =
SourceMgr.getFileManager().getFile(Message.FilePath);
FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
Loc = SourceMgr.getLocForStartOfFile(ID);
Loc = Loc.getLocWithOffset(Message.FileOffset);
}
Diags.Report(Loc, Diags.getCustomDiagID(Level, Message.Message));
}
void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {
FileManager Files((FileSystemOptions()));
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
DiagnosticConsumer *DiagPrinter =
new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts);
DiagnosticsEngine Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
&*DiagOpts, DiagPrinter);
DiagPrinter->BeginSourceFile(LangOptions());
SourceManager SourceMgr(Diags, Files);
Rewriter Rewrite(SourceMgr, LangOptions());
for (SmallVectorImpl<ClangTidyError>::iterator I = Errors.begin(),
E = Errors.end();
I != E; ++I) {
reportDiagnostic(I->Message, SourceMgr, DiagnosticsEngine::Warning, Diags);
for (unsigned i = 0, e = I->Notes.size(); i != e; ++i) {
reportDiagnostic(I->Notes[i], SourceMgr, DiagnosticsEngine::Note, Diags);
}
tooling::applyAllReplacements(I->Fix, Rewrite);
}
// FIXME: Run clang-format on changes.
if (Fix)
Rewrite.overwriteChangedFiles();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,109 +0,0 @@
//===--- ClangTidy.h - clang-tidy -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring.h"
#include "ClangTidyDiagnosticConsumer.h"
namespace clang {
class CompilerInstance;
namespace ast_matchers {
class MatchFinder;
}
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief Base class for all clang-tidy checks.
///
/// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the
/// base class's methods. E.g. to implement a check that validates namespace
/// declarations, overwrite \c registerMatchers:
///
/// \code
/// registerMatchers(ast_matchers::MatchFinder *Finder) {
/// Finder->addMatcher(namespaceDecl().bind("namespace"), this);
/// }
/// \endcode
///
/// and then overwrite \c check(const MatchResult &Result) to do the actual
/// check for each match.
///
/// A new \c ClangTidyCheck instance is created per translation unit.
///
/// FIXME: Figure out whether carrying information from one TU to another is
/// useful/necessary.
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
public:
virtual ~ClangTidyCheck() {}
/// \brief Overwrite this to register \c PPCallbacks with \c Compiler.
///
/// This should be used for clang-tidy checks that analyze preprocessor-
/// dependent properties, e.g. the order of include directives.
virtual void registerPPCallbacks(CompilerInstance &Compiler) {}
/// \brief Overwrite this to register ASTMatchers with \p Finder.
///
/// This should be used by clang-tidy checks that analyze code properties that
/// dependent on AST knowledge.
///
/// You can register as many matchers as necessary with \p Finder. Usually,
/// "this" will be used as callback, but you can also specify other callback
/// classes. Thereby, different matchers can trigger different callbacks.
///
/// If you need to merge information between the different matchers, you can
/// store these as members of the derived class. However, note that all
/// matches occur in the order of the AST traversal.
virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
/// \brief \c ClangTidyChecks that register ASTMatchers should do the actual
/// work in here.
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
/// \brief The infrastructure sets the context to \p Ctx with this function.
void setContext(ClangTidyContext *Ctx) { Context = Ctx; }
protected:
ClangTidyContext *Context;
private:
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
};
/// \brief Returns an action factory that runs the specified clang-tidy checks.
tooling::FrontendActionFactory *
createClangTidyActionFactory(StringRef CheckRegexString,
ClangTidyContext &Context);
/// \brief Run a set of clang-tidy checks on a set of files.
void runClangTidy(StringRef CheckRegexString,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> Ranges,
SmallVectorImpl<ClangTidyError> *Errors);
// FIXME: This interface will need to be significantly extended to be useful.
// FIXME: Implement confidence levels for displaying/fixing errors.
//
/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
/// Errors containing fixes are automatically applied.
void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix);
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H

View File

@@ -1,163 +0,0 @@
//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/SmallString.h"
namespace clang {
class CompilerInstance;
namespace ast_matchers {
class MatchFinder;
}
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief A message from a clang-tidy check.
///
/// Note that this is independent of a \c SourceManager.
struct ClangTidyMessage {
ClangTidyMessage(StringRef Message = "");
ClangTidyMessage(StringRef Message, const SourceManager &Sources,
SourceLocation Loc);
std::string Message;
std::string FilePath;
unsigned FileOffset;
};
/// \brief A detected error complete with information to display diagnostic and
/// automatic fix.
///
/// This is used as an intermediate format to transport Diagnostics without a
/// dependency on a SourceManager.
///
/// FIXME: Make Diagnostics flexible enough to support this directly.
struct ClangTidyError {
ClangTidyError(const ClangTidyMessage &Message);
ClangTidyMessage Message;
tooling::Replacements Fix;
SmallVector<ClangTidyMessage, 1> Notes;
};
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine
/// provided by this context.
///
/// A \c ClangTidyCheck always has access to the active context to report
/// warnings like:
/// \code
/// Context->Diag(Loc, "Single-argument constructors must be explicit")
/// << FixItHint::CreateInsertion(Loc, "explicit ");
/// \endcode
class ClangTidyContext {
public:
ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors) : Errors(Errors) {}
/// \brief Report any errors detected using this method.
///
/// This is still under heavy development and will likely change towards using
/// tablegen'd diagnostic IDs.
/// FIXME: Figure out a way to manage ID spaces.
DiagnosticBuilder Diag(SourceLocation Loc, StringRef Message);
/// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
/// correctly.
///
/// This is called from the \c ClangTidyCheck base class.
void setDiagnosticsEngine(DiagnosticsEngine *Engine);
/// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
///
/// This is called from the \c ClangTidyCheck base class.
void setSourceManager(SourceManager *SourceMgr);
private:
friend class ClangTidyDiagnosticConsumer; // Calls storeError().
/// \brief Store a \c ClangTidyError.
void storeError(const ClangTidyError &Error);
SmallVectorImpl<ClangTidyError> *Errors;
DiagnosticsEngine *DiagEngine;
};
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
/// \c SourceManager-independent \c ClangTidyError.
//
// FIXME: If we move away from unit-tests, this can be moved to a private
// implementation file.
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
public:
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx) : Context(Ctx) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Diags.reset(new DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
/*ShouldOwnClient=*/false));
Context.setDiagnosticsEngine(Diags.get());
}
// FIXME: The concept of converting between FixItHints and Replacements is
// more generic and should be pulled out into a more useful Diagnostics
// library.
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
if (DiagLevel != DiagnosticsEngine::Note) {
Errors.push_back(ClangTidyError(getMessage(Info)));
} else {
Errors.back().Notes.push_back(getMessage(Info));
}
addFixes(Info, Errors.back());
}
virtual void finish() {
for (unsigned i = 0, e = Errors.size(); i != e; ++i) {
Context.storeError(Errors[i]);
}
}
private:
void addFixes(const Diagnostic &Info, ClangTidyError &Error) {
if (!Info.hasSourceManager())
return;
SourceManager &SourceMgr = Info.getSourceManager();
tooling::Replacements Replacements;
for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) {
Error.Fix.insert(tooling::Replacement(
SourceMgr, Info.getFixItHint(i).RemoveRange.getBegin(), 0,
Info.getFixItHint(i).CodeToInsert));
}
}
ClangTidyMessage getMessage(const Diagnostic &Info) const {
SmallString<100> Buf;
Info.FormatDiagnostic(Buf);
if (!Info.hasSourceManager()) {
return ClangTidyMessage(Buf.str());
}
return ClangTidyMessage(Buf.str(), Info.getSourceManager(),
Info.getLocation());
}
ClangTidyContext &Context;
OwningPtr<DiagnosticsEngine> Diags;
SmallVector<ClangTidyError, 8> Errors;
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H

View File

@@ -1,49 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file Implements classes required to build clang-tidy modules.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyModule.h"
#include "llvm/Support/Regex.h"
namespace clang {
namespace tidy {
CheckFactoryBase::~CheckFactoryBase() {}
ClangTidyCheckFactories::~ClangTidyCheckFactories() {
for (std::map<std::string, CheckFactoryBase *>::iterator
I = Factories.begin(),
E = Factories.end();
I != E; ++I) {
delete I->second;
}
}
void ClangTidyCheckFactories::addCheckFactory(StringRef Name,
CheckFactoryBase *Factory) {
Factories[Name] = Factory;
}
void ClangTidyCheckFactories::createChecks(
StringRef CheckRegexString, SmallVectorImpl<ClangTidyCheck *> &Checks) {
llvm::Regex CheckRegex(CheckRegexString);
for (std::map<std::string, CheckFactoryBase *>::iterator
I = Factories.begin(),
E = Factories.end();
I != E; ++I) {
if (CheckRegex.match(I->first))
Checks.push_back(I->second->createCheck());
}
}
} // namespace tidy
} // namespace clang

View File

@@ -1,98 +0,0 @@
//===--- ClangTidyModule.h - clang-tidy -------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H
#include "ClangTidy.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <utility>
namespace clang {
namespace tidy {
/// \brief A factory, that can instantiate a specific clang-tidy check for
/// processing a translation unit.
///
/// In order to register your check with the \c ClangTidyModule, create a
/// subclass of \c CheckFactoryBase and implement \c createCheck(). Then, use
/// this subclass in \c ClangTidyModule::addCheckFactories().
class CheckFactoryBase {
public:
virtual ~CheckFactoryBase();
virtual ClangTidyCheck *createCheck() = 0;
};
/// \brief A subclass of \c CheckFactoryBase that should be used for all
/// \c ClangTidyChecks that don't require constructor parameters.
///
/// For example, if have a clang-tidy check like:
/// \code
/// class MyTidyCheck : public ClangTidyCheck {
/// virtual void registerMatchers(ast_matchers::MatchFinder *Finder) { .. }
/// };
/// \endcode
/// you can register it with:
/// \code
/// class MyModule : public ClangTidyModule {
/// virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) {
/// CheckFactories.addCheckFactory(
/// "myproject-my-check", new ClangTidyCheckFactory<MyTidyCheck>());
/// }
/// };
/// \endcode
template <typename T> class ClangTidyCheckFactory : public CheckFactoryBase {
public:
virtual ClangTidyCheck *createCheck() { return new T; }
};
class ClangTidyCheckFactories;
/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
/// them a prefixed name.
class ClangTidyModule {
public:
virtual ~ClangTidyModule() {}
/// \brief Implement this function in order to register all \c CheckFactories
/// belonging to this module.
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
};
/// \brief A collection of \c ClangTidyCheckFactory instances.
///
/// All clang-tidy modules register their check factories with an instance of
/// this object.
class ClangTidyCheckFactories {
public:
ClangTidyCheckFactories() {}
~ClangTidyCheckFactories();
/// \brief Register \p Factory with the name \p Name.
///
/// The \c ClangTidyCheckFactories object takes ownership of the \p Factory.
void addCheckFactory(StringRef Name, CheckFactoryBase *Factory);
/// \brief Create instances of all checks matching \p CheckRegexString and
/// store them in \p Checks.
///
/// The caller takes ownership of the return \c ClangTidyChecks.
void createChecks(StringRef CheckRegexString,
SmallVectorImpl<ClangTidyCheck *> &Checks);
private:
StringRef FilterRegex;
std::map<std::string, CheckFactoryBase *> Factories;
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H

View File

@@ -1,24 +0,0 @@
//===--- ClangTidyModuleRegistry.h - clang-tidy -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H
#include "ClangTidyModule.h"
#include "llvm/Support/Registry.h"
namespace clang {
namespace tidy {
typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry;
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H

View File

@@ -1,16 +0,0 @@
##===- tools/extra/clang-tidy/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 := clangTidy
include $(CLANG_LEVEL)/../../Makefile.config
DIRS = llvm google tool
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,11 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyGoogleModule
GoogleTidyModule.cpp
)
target_link_libraries(clangTidyGoogleModule
clangTidy
clangTooling
clangBasic
clangASTMatchers
)

View File

@@ -1,61 +0,0 @@
//===--- GoogleTidyModule.cpp - clang-tidy --------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "GoogleTidyModule.h"
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PPCallbacks.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
void
ExplicitConstructorCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
Finder->addMatcher(constructorDecl().bind("construct"), this);
}
void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
const CXXConstructorDecl *Ctor =
Result.Nodes.getNodeAs<CXXConstructorDecl>("construct");
if (!Ctor->isExplicit() && !Ctor->isImplicit() && Ctor->getNumParams() >= 1 &&
Ctor->getMinRequiredArguments() <= 1) {
SourceLocation Loc = Ctor->getLocation();
Context->Diag(Loc, "Single-argument constructors must be explicit")
<< FixItHint::CreateInsertion(Loc, "explicit ");
}
}
class GoogleModule : public ClangTidyModule {
public:
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) {
CheckFactories.addCheckFactory(
"google-explicit-constructor",
new ClangTidyCheckFactory<ExplicitConstructorCheck>());
}
};
// Register the GoogleTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<GoogleModule> X("google-module",
"Adds Google lint checks.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the GoogleModule.
volatile int GoogleModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,31 +0,0 @@
//===--- GoogleTidyModule.h - clang-tidy ------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
/// \brief Checks that all single-argument constructors are explicit.
///
/// see:
/// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors
class ExplicitConstructorCheck : public ClangTidyCheck {
public:
virtual void registerMatchers(ast_matchers::MatchFinder *Finder);
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result);
};
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H

View File

@@ -1,12 +0,0 @@
##===- clang-tidy/google/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 := clangTidyGoogleModule
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,11 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyLLVMModule
LLVMTidyModule.cpp
)
target_link_libraries(clangTidyLLVMModule
clangTidy
clangTooling
clangBasic
clangASTMatchers
)

View File

@@ -1,104 +0,0 @@
//===--- LLVMTidyModule.cpp - clang-tidy ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "LLVMTidyModule.h"
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PPCallbacks.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
void
NamespaceCommentCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
Finder->addMatcher(namespaceDecl().bind("namespace"), this);
}
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
Token Tok;
SourceLocation Loc = ND->getRBraceLoc().getLocWithOffset(1);
while (Lexer::getRawToken(Loc, Tok, *Result.SourceManager,
Result.Context->getLangOpts())) {
Loc = Loc.getLocWithOffset(1);
}
// FIXME: Check that this namespace is "long".
if (Tok.is(tok::comment)) {
// FIXME: Check comment content.
return;
}
std::string Fix = " // namespace";
if (!ND->isAnonymousNamespace())
Fix = Fix.append(" ").append(ND->getNameAsString());
Context->Diag(ND->getLocation(),
"namespace not terminated with a closing comment")
<< FixItHint::CreateInsertion(ND->getRBraceLoc().getLocWithOffset(1),
Fix);
}
namespace {
class IncludeOrderPPCallbacks : public PPCallbacks {
public:
explicit IncludeOrderPPCallbacks(ClangTidyContext &Context)
: Context(Context) {}
virtual void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange,
const FileEntry *File, StringRef SearchPath,
StringRef RelativePath,
const Module *Imported) {
// FIXME: This is a dummy implementation to show how to get at preprocessor
// information. Implement a real include order check.
Context.Diag(HashLoc, "This is an include");
}
private:
ClangTidyContext &Context;
};
} // namespace
void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Compiler.getPreprocessor()
.addPPCallbacks(new IncludeOrderPPCallbacks(*Context));
}
class LLVMModule : public ClangTidyModule {
public:
virtual ~LLVMModule() {}
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) {
CheckFactories.addCheckFactory(
"llvm-include-order", new ClangTidyCheckFactory<IncludeOrderCheck>());
CheckFactories.addCheckFactory(
"llvm-namespace-comment",
new ClangTidyCheckFactory<NamespaceCommentCheck>());
}
};
// Register the LLVMTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<LLVMModule> X("llvm-module",
"Adds LLVM lint checks.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the LLVMModule.
volatile int LLVMModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,38 +0,0 @@
//===--- LLVMTidyModule.h - clang-tidy --------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_LLVM_TIDY_MODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_LLVM_TIDY_MODULE_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
/// \brief Checks the correct order of \c #includes.
///
/// see: http://llvm.org/docs/CodingStandards.html#include-style
class IncludeOrderCheck : public ClangTidyCheck {
public:
virtual void registerPPCallbacks(CompilerInstance &Compiler);
};
/// \brief Checks that long namespaces have a closing comment.
///
/// see: http://llvm.org/docs/CodingStandards.html#namespace-indentation
class NamespaceCommentCheck : public ClangTidyCheck {
public:
virtual void registerMatchers(ast_matchers::MatchFinder *Finder);
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result);
};
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TIDY_LLVM_MODULE_H

Some files were not shown because too many files have changed in this diff Show More