Compare commits
112 Commits
llvmorg-3.
...
llvmorg-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7373e62925 | ||
|
|
5e4079507b | ||
|
|
2c9d10104d | ||
|
|
f661de1210 | ||
|
|
047be17153 | ||
|
|
c3960e4210 | ||
|
|
95aa1fc276 | ||
|
|
6ae951d744 | ||
|
|
6d2c63fd73 | ||
|
|
61ebf091a0 | ||
|
|
293e62e7c8 | ||
|
|
089e557229 | ||
|
|
f28720afc7 | ||
|
|
b3936d2b2a | ||
|
|
e19abec2d1 | ||
|
|
1a3a39623e | ||
|
|
484c2de8a0 | ||
|
|
339bbccf52 | ||
|
|
8efde361c3 | ||
|
|
224d42ae5d | ||
|
|
b25d801820 | ||
|
|
f11dfe01c2 | ||
|
|
8b149dd159 | ||
|
|
3d106cc963 | ||
|
|
9fcf0885e0 | ||
|
|
26a7ebf2f2 | ||
|
|
5b7209d335 | ||
|
|
194e66b9a4 | ||
|
|
857b0d645a | ||
|
|
9fb60e5fee | ||
|
|
4696c67bb6 | ||
|
|
de2a2bf0a6 | ||
|
|
48b930dd52 | ||
|
|
18ec090938 | ||
|
|
a3e2192f30 | ||
|
|
f4aeca1510 | ||
|
|
357c72c98c | ||
|
|
8b658dbbd1 | ||
|
|
33b3289435 | ||
|
|
1816b26c98 | ||
|
|
09be38dae2 | ||
|
|
56ccb1e497 | ||
|
|
f3a5da8505 | ||
|
|
3583b549ff | ||
|
|
b3e8ea0766 | ||
|
|
ac0b930116 | ||
|
|
b69f1a1034 | ||
|
|
7b6e3e24b8 | ||
|
|
b6a86811d0 | ||
|
|
2a75f42581 | ||
|
|
754df13fa4 | ||
|
|
925ef8191a | ||
|
|
ca4839270e | ||
|
|
4bec859e65 | ||
|
|
7fa881ecfc | ||
|
|
697b8aaa83 | ||
|
|
80a63a8724 | ||
|
|
edc2a45bad | ||
|
|
ec74cd262f | ||
|
|
fde85543cf | ||
|
|
feca2ad3bc | ||
|
|
01b6cc647b | ||
|
|
262316ade1 | ||
|
|
047c3baf86 | ||
|
|
f6af8ffd11 | ||
|
|
3fbacb3b8d | ||
|
|
599b06e299 | ||
|
|
5dc26cea08 | ||
|
|
3b317658e1 | ||
|
|
15386f416f | ||
|
|
829dcd4786 | ||
|
|
58b0a3b4a2 | ||
|
|
64b1fc8f98 | ||
|
|
cda97b7432 | ||
|
|
4570c1f2a7 | ||
|
|
29386c79b0 | ||
|
|
467f91162e | ||
|
|
e3a27bc43e | ||
|
|
f18d5b4674 | ||
|
|
c1b4549c3f | ||
|
|
07081fe831 | ||
|
|
0d803938d5 | ||
|
|
b47e89cddb | ||
|
|
e8355e4126 | ||
|
|
d114ee4e2a | ||
|
|
1f7dd5b593 | ||
|
|
fc76acc0fb | ||
|
|
0472e91409 | ||
|
|
3eca4fe7cd | ||
|
|
4041ed4075 | ||
|
|
7fa64bcda0 | ||
|
|
650e64f031 | ||
|
|
31e2a9b98b | ||
|
|
46d1b0ec0a | ||
|
|
710ad401c7 | ||
|
|
52ac9c7f9c | ||
|
|
ccb5be1268 | ||
|
|
d82c42d1d6 | ||
|
|
ba61874cbb | ||
|
|
9f4e22d1e3 | ||
|
|
690f38ad64 | ||
|
|
3f582e12f6 | ||
|
|
4ee1a18837 | ||
|
|
6a22c4c854 | ||
|
|
fae92bda03 | ||
|
|
8a7f8bffd5 | ||
|
|
b15f39b1a7 | ||
|
|
c21780a296 | ||
|
|
5be7a7772f | ||
|
|
6ca80bece0 | ||
|
|
60a859b987 | ||
|
|
e8a1be67a3 |
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"project_id" : "clang-tools-extra",
|
||||
"conduit_uri" : "http://llvm-reviews.chandlerc.com/"
|
||||
}
|
||||
32
clang-tools-extra/.gitignore
vendored
32
clang-tools-extra/.gitignore
vendored
@@ -1,32 +0,0 @@
|
||||
#==============================================================================#
|
||||
# This file specifies intentionally untracked files that git should ignore.
|
||||
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
|
||||
#
|
||||
# This file is intentionally different from the output of `git svn show-ignore`,
|
||||
# as most of those are useless.
|
||||
#==============================================================================#
|
||||
|
||||
#==============================================================================#
|
||||
# File extensions to be ignored anywhere in the tree.
|
||||
#==============================================================================#
|
||||
# Temp files created by most text editors.
|
||||
*~
|
||||
# Merge files created by git.
|
||||
*.orig
|
||||
# Byte compiled python modules.
|
||||
*.pyc
|
||||
# vim swap files
|
||||
.*.swp
|
||||
.sw?
|
||||
|
||||
#==============================================================================#
|
||||
# Explicit files to ignore (only matches one).
|
||||
#==============================================================================#
|
||||
cscope.files
|
||||
cscope.out
|
||||
.clang_complete
|
||||
|
||||
#==============================================================================#
|
||||
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
|
||||
#==============================================================================#
|
||||
docs/_build
|
||||
@@ -1,14 +1,6 @@
|
||||
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_subdirectory(loop-convert)
|
||||
|
||||
# Add the common testsuite after all the tools.
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(unittests)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
This file is a list of the people responsible for ensuring that patches for a
|
||||
particular tool are reviewed, either by themself or by someone else. They are
|
||||
also the gatekeepers for their part of Clang, with the final word on what goes
|
||||
in or not.
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S).
|
||||
|
||||
N: Edwin Vane
|
||||
E: revane@gmail.com
|
||||
D: clang-modernize
|
||||
@@ -4,7 +4,7 @@ LLVM Release License
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2007-2013 University of Illinois at Urbana-Champaign.
|
||||
Copyright (c) 2007-2012 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
@@ -11,9 +11,7 @@ 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
|
||||
PARALLEL_DIRS := remove-cstr-calls tool-template loop-convert
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1,7 +0,0 @@
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${ClangReplaceLocation}
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(Core)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,15 +0,0 @@
|
||||
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../../..
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
|
||||
DIRS = Core tool
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
@@ -1,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;
|
||||
@@ -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
|
||||
@@ -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 ¶m' -> '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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
};
|
||||
@@ -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) > $@
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
add_clang_library(clangQuery
|
||||
Query.cpp
|
||||
QueryParser.cpp
|
||||
)
|
||||
target_link_libraries(clangQuery
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangDynamicASTMatchers
|
||||
clangFrontend
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyGoogleModule
|
||||
GoogleTidyModule.cpp
|
||||
)
|
||||
target_link_libraries(clangTidyGoogleModule
|
||||
clangTidy
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangASTMatchers
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyLLVMModule
|
||||
LLVMTidyModule.cpp
|
||||
)
|
||||
target_link_libraries(clangTidyLLVMModule
|
||||
clangTidy
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangASTMatchers
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,12 +0,0 @@
|
||||
##===- clang-tidy/llvm/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 := clangTidyLLVMModule
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
@@ -1,21 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
asmparser
|
||||
bitreader
|
||||
support
|
||||
mc
|
||||
)
|
||||
|
||||
add_clang_executable(clang-tidy
|
||||
ClangTidyMain.cpp
|
||||
)
|
||||
target_link_libraries(clang-tidy
|
||||
clangTidy
|
||||
clangTidyLLVMModule
|
||||
clangTidyGoogleModule
|
||||
clangStaticAnalyzerCheckers
|
||||
)
|
||||
|
||||
install(TARGETS clang-tidy
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user