Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,181 @@
//===--- AvoidBindCheck.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 "AvoidBindCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstddef>
#include <string>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
namespace {
enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
struct BindArgument {
StringRef Tokens;
BindArgumentKind Kind = BK_Other;
size_t PlaceHolderIndex = 0;
};
} // end namespace
static SmallVector<BindArgument, 4>
buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) {
SmallVector<BindArgument, 4> BindArguments;
llvm::Regex MatchPlaceholder("^_([0-9]+)$");
// Start at index 1 as first argument to bind is the function name.
for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
const Expr *E = C->getArg(I);
BindArgument B;
if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
const auto *TE = M->GetTemporaryExpr();
B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
}
B.Tokens = Lexer::getSourceText(
CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
*Result.SourceManager, Result.Context->getLangOpts());
SmallVector<StringRef, 2> Matches;
if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
B.Kind = BK_Placeholder;
B.PlaceHolderIndex = std::stoi(Matches[1]);
}
BindArguments.push_back(B);
}
return BindArguments;
}
static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
llvm::raw_ostream &Stream) {
auto MaxPlaceholderIt =
std::max_element(Args.begin(), Args.end(),
[](const BindArgument &B1, const BindArgument &B2) {
return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
});
// Placeholders (if present) have index 1 or greater.
if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
return;
size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
Stream << "(";
StringRef Delimiter = "";
for (size_t I = 1; I <= PlaceholderCount; ++I) {
Stream << Delimiter << "auto && arg" << I;
Delimiter = ", ";
}
Stream << ")";
}
static void addFunctionCallArgs(const ArrayRef<BindArgument> Args,
llvm::raw_ostream &Stream) {
StringRef Delimiter = "";
for (const auto &B : Args) {
if (B.PlaceHolderIndex)
Stream << Delimiter << "arg" << B.PlaceHolderIndex;
else
Stream << Delimiter << B.Tokens;
Delimiter = ", ";
}
}
static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
llvm::SmallSet<size_t, 4> PlaceHolderIndices;
for (const BindArgument &B : Args) {
if (B.PlaceHolderIndex) {
if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
return true;
}
}
return false;
}
void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
return;
Finder->addMatcher(
callExpr(
callee(namedDecl(hasName("::std::bind"))),
hasArgument(0, declRefExpr(to(functionDecl().bind("f"))).bind("ref")))
.bind("bind"),
this);
}
void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind");
const auto Args = buildBindArguments(Result, MatchedDecl);
// Do not attempt to create fixits for nested call expressions.
// FIXME: Create lambda capture variables to capture output of calls.
// NOTE: Supporting nested std::bind will be more difficult due to placeholder
// sharing between outer and inner std:bind invocations.
if (llvm::any_of(Args,
[](const BindArgument &B) { return B.Kind == BK_CallExpr; }))
return;
// Do not attempt to create fixits when placeholders are reused.
// Unused placeholders are supported by requiring C++14 generic lambdas.
// FIXME: Support this case by deducing the common type.
if (isPlaceHolderIndexRepeated(Args))
return;
const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("f");
// std::bind can support argument count mismatch between its arguments and the
// bound function's arguments. Do not attempt to generate a fixit for such
// cases.
// FIXME: Support this case by creating unused lambda capture variables.
if (F->getNumParams() != Args.size())
return;
std::string Buffer;
llvm::raw_string_ostream Stream(Buffer);
bool HasCapturedArgument = llvm::any_of(
Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
addPlaceholderArgs(Args, Stream);
Stream << " { return ";
Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
Stream << "(";
addFunctionCallArgs(Args, Stream);
Stream << "); };";
Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
Stream.str());
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,36 @@
//===--- AvoidBindCheck.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_MODERNIZE_AVOID_BIND_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace modernize {
/// Replace simple uses of std::bind with a lambda.
///
/// FIXME: Add support for function references and member function references.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-avoid-std-bind.html
class AvoidBindCheck : public ClangTidyCheck {
public:
AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H

View File

@@ -0,0 +1,40 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyModernizeModule
AvoidBindCheck.cpp
DeprecatedHeadersCheck.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
MakeSmartPtrCheck.cpp
MakeSharedCheck.cpp
MakeUniqueCheck.cpp
ModernizeTidyModule.cpp
PassByValueCheck.cpp
RawStringLiteralCheck.cpp
RedundantVoidArgCheck.cpp
ReplaceAutoPtrCheck.cpp
ReplaceRandomShuffleCheck.cpp
ReturnBracedInitListCheck.cpp
ShrinkToFitCheck.cpp
UnaryStaticAssertCheck.cpp
UseAutoCheck.cpp
UseBoolLiteralsCheck.cpp
UseDefaultMemberInitCheck.cpp
UseEmplaceCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
UseNoexceptCheck.cpp
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseTransparentFunctorsCheck.cpp
UseUsingCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyReadabilityModule
clangTidyUtils
)

View File

@@ -0,0 +1,123 @@
//===--- DeprecatedHeadersCheck.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 "DeprecatedHeadersCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include <vector>
namespace clang {
namespace tidy {
namespace modernize {
namespace {
class IncludeModernizePPCallbacks : public PPCallbacks {
public:
explicit IncludeModernizePPCallbacks(ClangTidyCheck &Check,
LangOptions LangOpts);
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported) override;
private:
ClangTidyCheck &Check;
LangOptions LangOpts;
llvm::StringMap<std::string> CStyledHeaderToCxx;
llvm::StringSet<> DeleteHeaders;
};
} // namespace
void DeprecatedHeadersCheck::registerPPCallbacks(CompilerInstance &Compiler) {
if (this->getLangOpts().CPlusPlus) {
Compiler.getPreprocessor().addPPCallbacks(
::llvm::make_unique<IncludeModernizePPCallbacks>(*this,
this->getLangOpts()));
}
}
IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(ClangTidyCheck &Check,
LangOptions LangOpts)
: Check(Check), LangOpts(LangOpts) {
for (const auto &KeyValue :
std::vector<std::pair<llvm::StringRef, std::string>>(
{{"assert.h", "cassert"},
{"complex.h", "complex"},
{"ctype.h", "cctype"},
{"errno.h", "cerrno"},
{"float.h", "cfloat"},
{"limits.h", "climits"},
{"locale.h", "clocale"},
{"math.h", "cmath"},
{"setjmp.h", "csetjmp"},
{"signal.h", "csignal"},
{"stdarg.h", "cstdarg"},
{"stddef.h", "cstddef"},
{"stdio.h", "cstdio"},
{"stdlib.h", "cstdlib"},
{"string.h", "cstring"},
{"time.h", "ctime"},
{"wchar.h", "cwchar"},
{"wctype.h", "cwctype"}})) {
CStyledHeaderToCxx.insert(KeyValue);
}
// Add C++ 11 headers.
if (LangOpts.CPlusPlus11) {
for (const auto &KeyValue :
std::vector<std::pair<llvm::StringRef, std::string>>(
{{"fenv.h", "cfenv"},
{"stdint.h", "cstdint"},
{"inttypes.h", "cinttypes"},
{"tgmath.h", "ctgmath"},
{"uchar.h", "cuchar"}})) {
CStyledHeaderToCxx.insert(KeyValue);
}
}
for (const auto &Key :
std::vector<std::string>({"stdalign.h", "stdbool.h", "iso646.h"})) {
DeleteHeaders.insert(Key);
}
}
void IncludeModernizePPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
// FIXME: Take care of library symbols from the global namespace.
//
// Reasonable options for the check:
//
// 1. Insert std prefix for every such symbol occurrence.
// 2. Insert `using namespace std;` to the beginning of TU.
// 3. Do nothing and let the user deal with the migration himself.
if (CStyledHeaderToCxx.count(FileName) != 0) {
std::string Replacement =
(llvm::Twine("<") + CStyledHeaderToCxx[FileName] + ">").str();
Check.diag(FilenameRange.getBegin(), "inclusion of deprecated C++ header "
"'%0'; consider using '%1' instead")
<< FileName << CStyledHeaderToCxx[FileName]
<< FixItHint::CreateReplacement(FilenameRange.getAsRange(),
Replacement);
} else if (DeleteHeaders.count(FileName) != 0) {
Check.diag(FilenameRange.getBegin(),
"including '%0' has no effect in C++; consider removing it")
<< FileName << FixItHint::CreateRemoval(
SourceRange(HashLoc, FilenameRange.getEnd()));
}
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,47 @@
//===--- DeprecatedHeadersCheck.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_MODERNIZE_C_HEADERS_TO_CXX_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace modernize {
/// This check replaces deprecated C library headers with their C++ STL
/// alternatives.
///
/// Before:
/// ~~~{.cpp}
/// #include <header.h>
/// ~~~
///
/// After:
/// ~~~{.cpp}
/// #include <cheader>
/// ~~~
///
/// Example: ``<stdio.h> => <cstdio>``
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-headers.html
class DeprecatedHeadersCheck : public ClangTidyCheck {
public:
DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
//===--- LoopConvertCheck.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_MODERNIZE_LOOP_CONVERT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
#include "../ClangTidy.h"
#include "LoopConvertUtils.h"
namespace clang {
namespace tidy {
namespace modernize {
class LoopConvertCheck : public ClangTidyCheck {
public:
LoopConvertCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
struct RangeDescriptor {
RangeDescriptor();
bool ContainerNeedsDereference;
bool DerefByConstRef;
bool DerefByValue;
std::string ContainerString;
QualType ElemType;
};
void getAliasRange(SourceManager &SM, SourceRange &DeclRange);
void doConversion(ASTContext *Context, const VarDecl *IndexVar,
const ValueDecl *MaybeContainer, const UsageResult &Usages,
const DeclStmt *AliasDecl, bool AliasUseRequired,
bool AliasFromForInit, const ForStmt *Loop,
RangeDescriptor Descriptor);
StringRef getContainerString(ASTContext *Context, const ForStmt *Loop,
const Expr *ContainerExpr);
void getArrayLoopQualifiers(ASTContext *Context,
const ast_matchers::BoundNodes &Nodes,
const Expr *ContainerExpr,
const UsageResult &Usages,
RangeDescriptor &Descriptor);
void getIteratorLoopQualifiers(ASTContext *Context,
const ast_matchers::BoundNodes &Nodes,
RangeDescriptor &Descriptor);
void determineRangeDescriptor(ASTContext *Context,
const ast_matchers::BoundNodes &Nodes,
const ForStmt *Loop, LoopFixerKind FixerKind,
const Expr *ContainerExpr,
const UsageResult &Usages,
RangeDescriptor &Descriptor);
bool isConvertible(ASTContext *Context, const ast_matchers::BoundNodes &Nodes,
const ForStmt *Loop, LoopFixerKind FixerKind);
std::unique_ptr<TUTrackingInfo> TUInfo;
const unsigned long long MaxCopySize;
const Confidence::Level MinConfidence;
const VariableNamer::NamingStyle NamingStyle;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,467 @@
//===--- LoopConvertUtils.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_MODERNIZE_LOOP_CONVERT_UTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
namespace clang {
namespace tidy {
namespace modernize {
enum LoopFixerKind { LFK_Array, LFK_Iterator, LFK_PseudoArray };
/// 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(nullptr); }
/// \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() = default;
/// Find the components of an expression and place them in a ComponentVector.
void findExprComponents(const clang::Expr *SourceExpr) {
TraverseStmt(const_cast<clang::Expr *>(SourceExpr));
}
/// 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);
};
/// \brief The information needed to describe a valid convertible usage
/// of an array index or iterator.
struct Usage {
enum UsageKind {
// Regular usages of the loop index (the ones not specified below). Some
// examples:
// \code
// int X = 8 * Arr[i];
// ^~~~~~
// f(param1, param2, *It);
// ^~~
// if (Vec[i].SomeBool) {}
// ^~~~~~
// \endcode
UK_Default,
// Indicates whether this is an access to a member through the arrow
// operator on pointers or iterators.
UK_MemberThroughArrow,
// If the variable is being captured by a lambda, indicates whether the
// capture was done by value or by reference.
UK_CaptureByCopy,
UK_CaptureByRef
};
// The expression that is going to be converted. Null in case of lambda
// captures.
const Expr *Expression;
UsageKind Kind;
// Range that covers this usage.
SourceRange Range;
explicit Usage(const Expr *E)
: Expression(E), Kind(UK_Default), Range(Expression->getSourceRange()) {}
Usage(const Expr *E, UsageKind Kind, SourceRange Range)
: Expression(E), Kind(Kind), Range(std::move(Range)) {}
};
/// \brief A class to encapsulate lowering of the tool's confidence level.
class Confidence {
public:
enum Level {
// Transformations that are likely to change semantics.
CL_Risky,
// Transformations that might change semantics.
CL_Reasonable,
// Transformations that will not change semantics.
CL_Safe
};
/// \brief Initialize confidence level.
explicit Confidence(Confidence::Level Level) : CurrentLevel(Level) {}
/// \brief Lower the internal confidence level to Level, but do not raise it.
void lowerTo(Confidence::Level Level) {
CurrentLevel = std::min(Level, CurrentLevel);
}
/// \brief Return the internal confidence level.
Level getLevel() const { return CurrentLevel; }
private:
Level CurrentLevel;
};
// The main computational result of ForLoopIndexVisitor.
typedef llvm::SmallVector<Usage, 8> UsageResult;
// General functions used by ForLoopIndexUseVisitor and LoopConvertCheck.
const Expr *digThroughConstructors(const Expr *E);
bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second);
const DeclRefExpr *getDeclRef(const Expr *E);
bool areSameVariable(const ValueDecl *First, const ValueDecl *Second);
/// \brief Discover usages of expressions consisting of index or iterator
/// access.
///
/// Given an index variable, recursively crawls a for loop to discover if the
/// index variable is used in a way consistent with range-based for loop access.
class ForLoopIndexUseVisitor
: public RecursiveASTVisitor<ForLoopIndexUseVisitor> {
public:
ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar,
const VarDecl *EndVar, const Expr *ContainerExpr,
const Expr *ArrayBoundExpr,
bool ContainerNeedsDereference);
/// \brief Finds all uses of IndexVar in Body, placing all usages in Usages,
/// and returns true if IndexVar was only used in a way consistent with a
/// range-based for loop.
///
/// The general strategy is to reject any DeclRefExprs referencing IndexVar,
/// with the exception of certain acceptable patterns.
/// For arrays, the DeclRefExpr for IndexVar must appear as the index of an
/// ArraySubscriptExpression. Iterator-based loops may dereference
/// IndexVar or call methods through operator-> (builtin or overloaded).
/// Array-like containers may use IndexVar as a parameter to the at() member
/// function and in overloaded operator[].
bool findAndVerifyUsages(const Stmt *Body);
/// \brief Add a set of components that we should consider relevant to the
/// container.
void addComponents(const ComponentVector &Components);
/// \brief Accessor for Usages.
const UsageResult &getUsages() const { return Usages; }
/// \brief Adds the Usage if it was not added before.
void addUsage(const Usage &U);
/// \brief Get the container indexed by IndexVar, if any.
const Expr *getContainerIndexed() const { return ContainerExpr; }
/// \brief Returns the statement declaring the variable created as an alias
/// for the loop element, if any.
const DeclStmt *getAliasDecl() const { return AliasDecl; }
/// \brief Accessor for ConfidenceLevel.
Confidence::Level getConfidenceLevel() const {
return ConfidenceLevel.getLevel();
}
/// \brief Indicates if the alias declaration was in a place where it cannot
/// simply be removed but rather replaced with a use of the alias variable.
/// For example, variables declared in the condition of an if, switch, or for
/// stmt.
bool aliasUseRequired() const { return ReplaceWithAliasUse; }
/// \brief Indicates if the alias declaration came from the init clause of a
/// nested for loop. SourceRanges provided by Clang for DeclStmts in this
/// case need to be adjusted.
bool aliasFromForInit() const { return AliasFromForInit; }
private:
/// Typedef used in CRTP functions.
typedef RecursiveASTVisitor<ForLoopIndexUseVisitor> VisitorBase;
friend class RecursiveASTVisitor<ForLoopIndexUseVisitor>;
/// Overriden methods for RecursiveASTVisitor's traversal.
bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E);
bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
Expr *Init);
bool TraverseMemberExpr(MemberExpr *Member);
bool TraverseUnaryDeref(UnaryOperator *Uop);
bool VisitDeclRefExpr(DeclRefExpr *E);
bool VisitDeclStmt(DeclStmt *S);
bool TraverseStmt(Stmt *S);
/// \brief Add an expression to the list of expressions on which the container
/// expression depends.
void addComponent(const Expr *E);
// Input member variables:
ASTContext *Context;
/// The index variable's VarDecl.
const VarDecl *IndexVar;
/// The loop's 'end' variable, which cannot be mentioned at all.
const VarDecl *EndVar;
/// The Expr which refers to the container.
const Expr *ContainerExpr;
/// The Expr which refers to the terminating condition for array-based loops.
const Expr *ArrayBoundExpr;
bool ContainerNeedsDereference;
// Output member variables:
/// A container which holds all usages of IndexVar as the index of
/// ArraySubscriptExpressions.
UsageResult Usages;
llvm::SmallSet<SourceLocation, 8> UsageLocations;
bool OnlyUsedAsIndex;
/// The DeclStmt for an alias to the container element.
const DeclStmt *AliasDecl;
Confidence ConfidenceLevel;
/// \brief A list of expressions on which ContainerExpr depends.
///
/// If any of these expressions are encountered outside of an acceptable usage
/// of the loop element, lower our confidence level.
llvm::SmallVector<std::pair<const Expr *, llvm::FoldingSetNodeID>, 16>
DependentExprs;
/// The parent-in-waiting. Will become the real parent once we traverse down
/// one level in the AST.
const Stmt *NextStmtParent;
/// The actual parent of a node when Visit*() calls are made. Only the
/// parentage of DeclStmt's to possible iteration/selection statements is of
/// importance.
const Stmt *CurrStmtParent;
/// \see aliasUseRequired().
bool ReplaceWithAliasUse;
/// \see aliasFromForInit().
bool AliasFromForInit;
};
struct TUTrackingInfo {
/// \brief Reset and initialize per-TU tracking information.
///
/// Must be called before using container accessors.
TUTrackingInfo() : ParentFinder(new StmtAncestorASTVisitor) {}
StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
private:
std::unique_ptr<StmtAncestorASTVisitor> ParentFinder;
StmtGeneratedVarNameMap GeneratedDecls;
ReplacedVarsMap ReplacedVars;
};
/// \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:
// Supported naming styles.
enum NamingStyle {
NS_CamelBack,
NS_CamelCase,
NS_LowerCase,
NS_UpperCase,
};
VariableNamer(StmtGeneratedVarNameMap *GeneratedDecls,
const StmtParentMap *ReverseAST, const clang::Stmt *SourceStmt,
const clang::VarDecl *OldIndex,
const clang::ValueDecl *TheContainer,
const clang::ASTContext *Context, NamingStyle Style)
: GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
Context(Context), Style(Style) {}
/// \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::ValueDecl *TheContainer;
const clang::ASTContext *Context;
const NamingStyle Style;
// 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);
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H

View File

@@ -0,0 +1,32 @@
//===--- MakeSharedCheck.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 "MakeSharedCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
MakeSharedCheck::MakeSharedCheck(StringRef Name, ClangTidyContext *Context)
: MakeSmartPtrCheck(Name, Context, "std::make_shared") {}
MakeSharedCheck::SmartPtrTypeMatcher
MakeSharedCheck::getSmartPointerTypeMatcher() const {
return qualType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(classTemplateSpecializationDecl(
hasName("::std::shared_ptr"), templateArgumentCountIs(1),
hasTemplateArgument(0, templateArgument(refersToType(
qualType().bind(PointerType)))))))));
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,43 @@
//===--- MakeSharedCheck.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_MODERNIZE_MAKE_SHARED_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
#include "MakeSmartPtrCheck.h"
namespace clang {
namespace tidy {
namespace modernize {
/// Replace the pattern:
/// \code
/// std::shared_ptr<type>(new type(args...))
/// \endcode
///
/// With the safer version:
/// \code
/// std::make_shared<type>(args...)
/// \endcode
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html
class MakeSharedCheck : public MakeSmartPtrCheck {
public:
MakeSharedCheck(StringRef Name, ClangTidyContext *Context);
protected:
SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H

View File

@@ -0,0 +1,368 @@
//===--- MakeSmartPtrCheck.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 "MakeSharedCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
namespace {
constexpr char StdMemoryHeader[] = "memory";
std::string GetNewExprName(const CXXNewExpr *NewExpr,
const SourceManager &SM,
const LangOptions &Lang) {
StringRef WrittenName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
SM, Lang);
if (NewExpr->isArray()) {
return WrittenName.str() + "[]";
}
return WrittenName.str();
}
} // namespace
const char MakeSmartPtrCheck::PointerType[] = "pointerType";
const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name,
ClangTidyContext* Context,
StringRef MakeSmartPtrFunctionName)
: ClangTidyCheck(Name, Context),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
MakeSmartPtrFunctionHeader(
Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
MakeSmartPtrFunctionName(
Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
}
void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
if (getLangOpts().CPlusPlus11) {
Inserter.reset(new utils::IncludeInserter(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}
}
void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus11)
return;
// Calling make_smart_ptr from within a member function of a type with a
// private or protected constructor would be ill-formed.
auto CanCallCtor = unless(has(ignoringImpCasts(
cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
Finder->addMatcher(
cxxBindTemporaryExpr(has(ignoringParenImpCasts(
cxxConstructExpr(
hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
hasArgument(0,
cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
equalsBoundNode(PointerType))))),
CanCallCtor)
.bind(NewExpression)),
unless(isInTemplateInstantiation()))
.bind(ConstructorCall)))),
this);
Finder->addMatcher(
cxxMemberCallExpr(
thisPointerType(getSmartPointerTypeMatcher()),
callee(cxxMethodDecl(hasName("reset"))),
hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)),
unless(isInTemplateInstantiation()))
.bind(ResetCall),
this);
}
void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
// 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
// pointer, 'make_smart_ptr' refers to 'std::make_shared' or
// 'std::make_unique' or other function that creates smart_ptr.
SourceManager &SM = *Result.SourceManager;
const auto *Construct =
Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
if (New->getNumPlacementArgs() != 0)
return;
if (Construct)
checkConstruct(SM, Construct, Type, New);
else if (Reset)
checkReset(SM, Reset, New);
}
void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
const CXXConstructExpr *Construct,
const QualType *Type,
const CXXNewExpr *New) {
SourceLocation ConstructCallStart = Construct->getExprLoc();
bool InMacro = ConstructCallStart.isMacroID();
if (InMacro && IgnoreMacros) {
return;
}
bool Invalid = false;
StringRef ExprStr = Lexer::getSourceText(
CharSourceRange::getCharRange(
ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
SM, getLangOpts(), &Invalid);
if (Invalid)
return;
auto Diag = diag(ConstructCallStart, "use %0 instead")
<< MakeSmartPtrFunctionName;
// Disable the fix in macros.
if (InMacro) {
return;
}
if (!replaceNew(Diag, New, SM)) {
return;
}
// Find the location of the template's left angle.
size_t LAngle = ExprStr.find("<");
SourceLocation ConstructCallEnd;
if (LAngle == StringRef::npos) {
// If the template argument is missing (because it is part of the alias)
// we have to add it back.
ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
Diag << FixItHint::CreateInsertion(
ConstructCallEnd,
"<" + GetNewExprName(New, SM, getLangOpts()) + ">");
} else {
ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
}
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
MakeSmartPtrFunctionName);
// If the smart_ptr is built with brace enclosed direct initialization, use
// parenthesis instead.
if (Construct->isListInitialization()) {
SourceRange BraceRange = Construct->getParenOrBraceRange();
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(
BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
"(");
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(BraceRange.getEnd(),
BraceRange.getEnd().getLocWithOffset(1)),
")");
}
insertHeader(Diag, SM.getFileID(ConstructCallStart));
}
void MakeSmartPtrCheck::checkReset(SourceManager &SM,
const CXXMemberCallExpr *Reset,
const CXXNewExpr *New) {
const auto *Expr = cast<MemberExpr>(Reset->getCallee());
SourceLocation OperatorLoc = Expr->getOperatorLoc();
SourceLocation ResetCallStart = Reset->getExprLoc();
SourceLocation ExprStart = Expr->getLocStart();
SourceLocation ExprEnd =
Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
bool InMacro = ExprStart.isMacroID();
if (InMacro && IgnoreMacros) {
return;
}
// There are some cases where we don't have operator ("." or "->") of the
// "reset" expression, e.g. call "reset()" method directly in the subclass of
// "std::unique_ptr<>". We skip these cases.
if (OperatorLoc.isInvalid()) {
return;
}
auto Diag = diag(ResetCallStart, "use %0 instead")
<< MakeSmartPtrFunctionName;
// Disable the fix in macros.
if (InMacro) {
return;
}
if (!replaceNew(Diag, New, SM)) {
return;
}
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
(llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
GetNewExprName(New, SM, getLangOpts()) + ">")
.str());
if (Expr->isArrow())
Diag << FixItHint::CreateInsertion(ExprStart, "*");
insertHeader(Diag, SM.getFileID(OperatorLoc));
}
bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
const CXXNewExpr *New,
SourceManager& SM) {
SourceLocation NewStart = New->getSourceRange().getBegin();
SourceLocation NewEnd = New->getSourceRange().getEnd();
std::string ArraySizeExpr;
if (const auto* ArraySize = New->getArraySize()) {
ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
ArraySize->getSourceRange()),
SM, getLangOpts())
.str();
}
switch (New->getInitializationStyle()) {
case CXXNewExpr::NoInit: {
if (ArraySizeExpr.empty()) {
Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
} else {
// New array expression without written initializer:
// smart_ptr<Foo[]>(new Foo[5]);
Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
ArraySizeExpr);
}
break;
}
case CXXNewExpr::CallInit: {
// FIXME: Add fixes for constructors with parameters that can be created
// with a C++11 braced-init-list (e.g. std::vector, std::map).
// Unlike ordinal cases, braced list can not be deduced in
// std::make_smart_ptr, we need to specify the type explicitly in the fixes:
// struct S { S(std::initializer_list<int>, int); };
// struct S2 { S2(std::vector<int>); };
// smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
// smart_ptr<S>(new S({}, 1));
// smart_ptr<S2>(new S2({1})); // implicit conversion:
// // std::initializer_list => std::vector
// The above samples have to be replaced with:
// std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
// std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
// std::make_smart_ptr<S2>(std::vector<int>({1}));
if (const auto *CE = New->getConstructExpr()) {
for (const auto *Arg : CE->arguments()) {
if (isa<CXXStdInitializerListExpr>(Arg)) {
return false;
}
// Check the implicit conversion from the std::initializer_list type to
// a class type.
if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(Arg)) {
if (ImplicitCE->isStdInitListInitialization()) {
return false;
}
}
}
}
if (ArraySizeExpr.empty()) {
SourceRange InitRange = New->getDirectInitRange();
Diag << FixItHint::CreateRemoval(
SourceRange(NewStart, InitRange.getBegin()));
Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
}
else {
// New array expression with default/value initialization:
// smart_ptr<Foo[]>(new int[5]());
// smart_ptr<Foo[]>(new Foo[5]());
Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
ArraySizeExpr);
}
break;
}
case CXXNewExpr::ListInit: {
// Range of the substring that we do not want to remove.
SourceRange InitRange;
if (const auto *NewConstruct = New->getConstructExpr()) {
if (NewConstruct->isStdInitListInitialization()) {
// FIXME: Add fixes for direct initialization with the initializer-list
// constructor. Similar to the above CallInit case, the type has to be
// specified explicitly in the fixes.
// struct S { S(std::initializer_list<int>); };
// smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
// smart_ptr<S>(new S{}); // use initializer-list consturctor
// The above cases have to be replaced with:
// std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
// std::make_smart_ptr<S>(std::initializer_list<int>({}));
return false;
} else {
// Direct initialization with ordinary constructors.
// struct S { S(int x); S(); };
// smart_ptr<S>(new S{5});
// smart_ptr<S>(new S{}); // use default constructor
// The arguments in the initialization list are going to be forwarded to
// the constructor, so this has to be replaced with:
// std::make_smart_ptr<S>(5);
// std::make_smart_ptr<S>();
InitRange = SourceRange(
NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
}
} else {
// Aggregate initialization.
// smart_ptr<Pair>(new Pair{first, second});
// Has to be replaced with:
// smart_ptr<Pair>(Pair{first, second});
InitRange = SourceRange(
New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
New->getInitializer()->getSourceRange().getEnd());
}
Diag << FixItHint::CreateRemoval(
CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
Diag << FixItHint::CreateRemoval(
SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
break;
}
}
return true;
}
void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
if (MakeSmartPtrFunctionHeader.empty()) {
return;
}
if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
FD, MakeSmartPtrFunctionHeader,
/*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
Diag << *IncludeFixit;
}
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,70 @@
//===--- MakeSmartPtrCheck.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_MODERNIZE_MAKE_SMART_PTR_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
#include "../ClangTidy.h"
#include "../utils/IncludeInserter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace clang {
namespace tidy {
namespace modernize {
/// Base class for MakeSharedCheck and MakeUniqueCheck.
class MakeSmartPtrCheck : public ClangTidyCheck {
public:
MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
StringRef MakeSmartPtrFunctionName);
void registerMatchers(ast_matchers::MatchFinder *Finder) final;
void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
protected:
using SmartPtrTypeMatcher = ast_matchers::internal::BindableMatcher<QualType>;
/// Returns matcher that match with different smart pointer types.
///
/// Requires to bind pointer type (qualType) with PointerType string declared
/// in this class.
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const = 0;
static const char PointerType[];
static const char ConstructorCall[];
static const char ResetCall[];
static const char NewExpression[];
private:
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::string MakeSmartPtrFunctionHeader;
const std::string MakeSmartPtrFunctionName;
const bool IgnoreMacros;
void checkConstruct(SourceManager &SM, const CXXConstructExpr *Construct,
const QualType *Type, const CXXNewExpr *New);
void checkReset(SourceManager &SM, const CXXMemberCallExpr *Member,
const CXXNewExpr *New);
/// Returns true when the fixes for replacing CXXNewExpr are generated.
bool replaceNew(DiagnosticBuilder &Diag, const CXXNewExpr *New,
SourceManager &SM);
void insertHeader(DiagnosticBuilder &Diag, FileID FD);
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H

View File

@@ -0,0 +1,41 @@
//===--- MakeUniqueCheck.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 "MakeUniqueCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
MakeUniqueCheck::MakeUniqueCheck(StringRef Name,
clang::tidy::ClangTidyContext *Context)
: MakeSmartPtrCheck(Name, Context, "std::make_unique") {}
MakeUniqueCheck::SmartPtrTypeMatcher
MakeUniqueCheck::getSmartPointerTypeMatcher() const {
return qualType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(classTemplateSpecializationDecl(
hasName("::std::unique_ptr"), templateArgumentCountIs(2),
hasTemplateArgument(
0, templateArgument(refersToType(qualType().bind(PointerType)))),
hasTemplateArgument(
1, templateArgument(refersToType(
qualType(hasDeclaration(classTemplateSpecializationDecl(
hasName("::std::default_delete"),
templateArgumentCountIs(1),
hasTemplateArgument(
0, templateArgument(refersToType(qualType(
equalsBoundNode(PointerType))))))))))))))));
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,40 @@
//===--- MakeUniqueCheck.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_MODERNIZE_MAKE_UNIQUE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
#include "MakeSmartPtrCheck.h"
namespace clang {
namespace tidy {
namespace modernize {
/// Replace the pattern:
/// \code
/// std::unique_ptr<type>(new type(args...))
/// \endcode
///
/// With the C++14 version:
/// \code
/// std::make_unique<type>(args...)
/// \endcode
class MakeUniqueCheck : public MakeSmartPtrCheck {
public:
MakeUniqueCheck(StringRef Name, ClangTidyContext *Context);
protected:
SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H

View File

@@ -0,0 +1,113 @@
//===--- ModernizeTidyModule.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 "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "AvoidBindCheck.h"
#include "DeprecatedHeadersCheck.h"
#include "LoopConvertCheck.h"
#include "MakeSharedCheck.h"
#include "MakeUniqueCheck.h"
#include "PassByValueCheck.h"
#include "RawStringLiteralCheck.h"
#include "RedundantVoidArgCheck.h"
#include "ReplaceAutoPtrCheck.h"
#include "ReplaceRandomShuffleCheck.h"
#include "ReturnBracedInitListCheck.h"
#include "ShrinkToFitCheck.h"
#include "UnaryStaticAssertCheck.h"
#include "UseAutoCheck.h"
#include "UseBoolLiteralsCheck.h"
#include "UseDefaultMemberInitCheck.h"
#include "UseEmplaceCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
#include "UseNoexceptCheck.h"
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseTransparentFunctorsCheck.h"
#include "UseUsingCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
class ModernizeModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidBindCheck>("modernize-avoid-bind");
CheckFactories.registerCheck<DeprecatedHeadersCheck>(
"modernize-deprecated-headers");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
CheckFactories.registerCheck<RawStringLiteralCheck>(
"modernize-raw-string-literal");
CheckFactories.registerCheck<RedundantVoidArgCheck>(
"modernize-redundant-void-arg");
CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
"modernize-replace-auto-ptr");
CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
"modernize-replace-random-shuffle");
CheckFactories.registerCheck<ReturnBracedInitListCheck>(
"modernize-return-braced-init-list");
CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
CheckFactories.registerCheck<UnaryStaticAssertCheck>(
"modernize-unary-static-assert");
CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
CheckFactories.registerCheck<UseBoolLiteralsCheck>(
"modernize-use-bool-literals");
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
"modernize-use-default-member-init");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
"modernize-use-transparent-functors");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
}
ClangTidyOptions getModuleOptions() override {
ClangTidyOptions Options;
auto &Opts = Options.CheckOptions;
// For types whose size in bytes is above this threshold, we prefer taking a
// const-reference than making a copy.
Opts["modernize-loop-convert.MaxCopySize"] = "16";
Opts["modernize-loop-convert.MinConfidence"] = "reasonable";
Opts["modernize-loop-convert.NamingStyle"] = "CamelCase";
Opts["modernize-pass-by-value.IncludeStyle"] = "llvm"; // Also: "google".
Opts["modernize-replace-auto-ptr.IncludeStyle"] = "llvm"; // Also: "google".
// Comma-separated list of macros that behave like NULL.
Opts["modernize-use-nullptr.NullMacros"] = "NULL";
return Options;
}
};
// Register the ModernizeTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<ModernizeModule> X("modernize-module",
"Add modernize checks.");
} // namespace modernize
// This anchor is used to force the linker to link in the generated object file
// and thus register the ModernizeModule.
volatile int ModernizeModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,232 @@
//===--- PassByValueCheck.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 "PassByValueCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
using namespace llvm;
namespace clang {
namespace tidy {
namespace modernize {
/// \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 (const CXXConstructorDecl *Ctor : Node.ctors()) {
if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
return true;
}
return false;
}
static TypeMatcher constRefType() {
return lValueReferenceType(pointee(isConstQualified()));
}
static TypeMatcher nonConstValueType() {
return qualType(unless(anyOf(referenceType(), isConstQualified())));
}
/// \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) {
/// \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 = 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;
};
return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
}
/// \brief Find all references to \p ParamDecl across all of the
/// redeclarations of \p Ctor.
static SmallVector<const ParmVarDecl *, 2>
collectParamDecls(const CXXConstructorDecl *Ctor,
const ParmVarDecl *ParamDecl) {
SmallVector<const ParmVarDecl *, 2> Results;
unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
for (const FunctionDecl *Redecl : Ctor->redecls())
Results.push_back(Redecl->getParamDecl(ParamIdx));
return Results;
}
PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle",
utils::IncludeSorter::toString(IncludeStyle));
Options.store(Opts, "ValuesOnly", ValuesOnly);
}
void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
// Only register the matchers for C++; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
cxxConstructorDecl(
forEachConstructorInitializer(
cxxCtorInitializer(
unless(isBaseInitializer()),
// Clang builds a CXXConstructExpr only when it knows which
// constructor will be called. In dependent contexts a
// ParenListExpr is generated instead of a CXXConstructExpr,
// filtering out templates automatically for us.
withInitializer(cxxConstructExpr(
has(ignoringParenImpCasts(declRefExpr(to(
parmVarDecl(
hasType(qualType(
// Match only const-ref or a non-const value
// parameters. Rvalues and const-values
// shouldn't be modified.
ValuesOnly ? nonConstValueType()
: anyOf(constRefType(),
nonConstValueType()))))
.bind("Param"))))),
hasDeclaration(cxxConstructorDecl(
isCopyConstructor(), unless(isDeleted()),
hasDeclContext(
cxxRecordDecl(isMoveConstructible())))))))
.bind("Initializer")))
.bind("Ctor"),
this);
}
void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) {
// Only register the preprocessor callbacks for C++; the functionality
// currently does not provide any benefit to other languages, despite being
// benign.
if (getLangOpts().CPlusPlus) {
Inserter.reset(new utils::IncludeInserter(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}
}
void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
const auto *Initializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
SourceManager &SM = *Result.SourceManager;
// If the parameter is used or anything other than the copy, do not apply
// the changes.
if (!paramReferredExactlyOnce(Ctor, ParamDecl))
return;
// If the parameter is trivial to copy, don't move it. Moving a trivivally
// copyable type will cause a problem with performance-move-const-arg
if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
*Result.Context))
return;
auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move");
// Iterate over all declarations of the constructor.
for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
// Do not replace if it is already a value, skip.
if (RefTL.isNull())
continue;
TypeLoc ValueTL = RefTL.getPointeeLoc();
auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(),
ParamTL.getLocEnd());
std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
ValueTL.getSourceRange()),
SM, getLangOpts())
.str();
ValueStr += ' ';
Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
}
// Use std::move in the initialization list.
Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
<< FixItHint::CreateInsertion(
Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
Result.SourceManager->getFileID(Initializer->getSourceLocation()),
"utility",
/*IsAngled=*/true)) {
Diag << *IncludeFixit;
}
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,40 @@
//===--- PassByValueCheck.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_MODERNIZE_PASS_BY_VALUE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
#include "../ClangTidy.h"
#include "../utils/IncludeInserter.h"
#include <memory>
namespace clang {
namespace tidy {
namespace modernize {
class PassByValueCheck : public ClangTidyCheck {
public:
PassByValueCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const bool ValuesOnly;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H

View File

@@ -0,0 +1,150 @@
//===--- RawStringLiteralCheck.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 "RawStringLiteralCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
namespace {
bool containsEscapes(StringRef HayStack, StringRef Escapes) {
size_t BackSlash = HayStack.find('\\');
if (BackSlash == StringRef::npos)
return false;
while (BackSlash != StringRef::npos) {
if (Escapes.find(HayStack[BackSlash + 1]) == StringRef::npos)
return false;
BackSlash = HayStack.find('\\', BackSlash + 2);
}
return true;
}
bool isRawStringLiteral(StringRef Text) {
// Already a raw string literal if R comes before ".
const size_t QuotePos = Text.find('"');
assert(QuotePos != StringRef::npos);
return (QuotePos > 0) && (Text[QuotePos - 1] == 'R');
}
bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
const StringLiteral *Literal) {
// FIXME: Handle L"", u8"", u"" and U"" literals.
if (!Literal->isAscii())
return false;
StringRef Bytes = Literal->getBytes();
// Non-printing characters disqualify this literal:
// \007 = \a bell
// \010 = \b backspace
// \011 = \t horizontal tab
// \012 = \n new line
// \013 = \v vertical tab
// \014 = \f form feed
// \015 = \r carriage return
// \177 = delete
if (Bytes.find_first_of(StringRef("\000\001\002\003\004\005\006\a"
"\b\t\n\v\f\r\016\017"
"\020\021\022\023\024\025\026\027"
"\030\031\032\033\034\035\036\037"
"\177",
33)) != StringRef::npos)
return false;
CharSourceRange CharRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Literal->getSourceRange()),
*Result.SourceManager, Result.Context->getLangOpts());
StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
Result.Context->getLangOpts());
if (isRawStringLiteral(Text))
return false;
return containsEscapes(Text, R"('\"?x01)");
}
bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
return Bytes.find(Delimiter.empty()
? std::string(R"lit()")lit")
: (")" + Delimiter + R"(")")) != StringRef::npos;
}
std::string asRawStringLiteral(const StringLiteral *Literal,
const std::string &DelimiterStem) {
const StringRef Bytes = Literal->getBytes();
std::string Delimiter;
for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
}
if (Delimiter.empty())
return (R"(R"()" + Bytes + R"lit()")lit").str();
return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
}
} // namespace
RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
DelimiterStem(Options.get("DelimiterStem", "lit")),
ReplaceShorterLiterals(Options.get("ReplaceShorterLiterals", false)) {}
void RawStringLiteralCheck::storeOptions(ClangTidyOptions::OptionMap &Options) {
ClangTidyCheck::storeOptions(Options);
this->Options.store(Options, "ReplaceShorterLiterals",
ReplaceShorterLiterals);
}
void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
// Raw string literals require C++11 or later.
if (!getLangOpts().CPlusPlus11)
return;
Finder->addMatcher(
stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
}
void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
if (Literal->getLocStart().isMacroID())
return;
if (containsEscapedCharacters(Result, Literal)) {
std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
if (ReplaceShorterLiterals ||
Replacement.length() <=
Lexer::MeasureTokenLength(Literal->getLocStart(),
*Result.SourceManager, getLangOpts()))
replaceWithRawStringLiteral(Result, Literal, Replacement);
}
}
void RawStringLiteralCheck::replaceWithRawStringLiteral(
const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
StringRef Replacement) {
CharSourceRange CharRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Literal->getSourceRange()),
*Result.SourceManager, getLangOpts());
diag(Literal->getLocStart(),
"escaped string literal can be written as a raw string literal")
<< FixItHint::CreateReplacement(CharRange, Replacement);
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,45 @@
//===--- RawStringLiteralCheck.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_MODERNIZE_RAW_STRING_LITERAL_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace modernize {
/// This check replaces string literals with escaped characters to
/// raw string literals.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-raw-string-literal.html
class RawStringLiteralCheck : public ClangTidyCheck {
public:
RawStringLiteralCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
void replaceWithRawStringLiteral(
const ast_matchers::MatchFinder::MatchResult &Result,
const StringLiteral *Literal, StringRef Replacement);
std::string DelimiterStem;
const bool ReplaceShorterLiterals;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H

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