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,72 @@
//===---------- ASTUtils.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 "ASTUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
namespace clang {
namespace tidy {
namespace utils {
using namespace ast_matchers;
const FunctionDecl *getSurroundingFunction(ASTContext &Context,
const Stmt &Statement) {
return selectFirst<const FunctionDecl>(
"function", match(stmt(hasAncestor(functionDecl().bind("function"))),
Statement, Context));
}
bool IsBinaryOrTernary(const Expr *E) {
const Expr *E_base = E->IgnoreImpCasts();
if (clang::isa<clang::BinaryOperator>(E_base) ||
clang::isa<clang::ConditionalOperator>(E_base)) {
return true;
}
if (const auto *Operator =
clang::dyn_cast<clang::CXXOperatorCallExpr>(E_base)) {
return Operator->isInfixBinaryOp();
}
return false;
}
bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
const LangOptions &LangOpts,
StringRef FlagName) {
// If the Flag is an integer constant, check it.
if (isa<IntegerLiteral>(Flags)) {
if (!SM.isMacroBodyExpansion(Flags->getLocStart()) &&
!SM.isMacroArgExpansion(Flags->getLocStart()))
return false;
// Get the marco name.
auto MacroName = Lexer::getSourceText(
CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts);
return MacroName == FlagName;
}
// If it's a binary OR operation.
if (const auto *BO = dyn_cast<BinaryOperator>(Flags))
if (BO->getOpcode() == clang::BinaryOperatorKind::BO_Or)
return exprHasBitFlagWithSpelling(BO->getLHS()->IgnoreParenCasts(), SM,
LangOpts, FlagName) ||
exprHasBitFlagWithSpelling(BO->getRHS()->IgnoreParenCasts(), SM,
LangOpts, FlagName);
// Otherwise, assume it has the flag.
return true;
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,34 @@
//===---------- ASTUtils.h - clang-tidy -----------------------------------===//
//
// 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_ASTUTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
#include "clang/AST/AST.h"
namespace clang {
namespace tidy {
namespace utils {
// Returns the (closest) Function declaration surrounding |Statement| or NULL.
const FunctionDecl *getSurroundingFunction(ASTContext &Context,
const Stmt &Statement);
// Determine whether Expr is a Binary or Ternary expression.
bool IsBinaryOrTernary(const Expr *E);
/// Checks whether a macro flag is present in the given argument. Only considers
/// cases of single match or match in a binary OR expression. For example,
/// <needed-flag> or <flag> | <needed-flag> | ...
bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
const LangOptions &LangOpts,
StringRef FlagName);
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H

View File

@@ -0,0 +1,24 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyUtils
ASTUtils.cpp
DeclRefExprUtils.cpp
ExprSequence.cpp
FixItHintUtils.cpp
HeaderFileExtensionsUtils.cpp
HeaderGuard.cpp
IncludeInserter.cpp
IncludeSorter.cpp
LexerUtils.cpp
NamespaceAliaser.cpp
OptionsUtils.cpp
TypeTraits.cpp
UsingInserter.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
)

View File

@@ -0,0 +1,172 @@
//===--- DeclRefExprUtils.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 "DeclRefExprUtils.h"
#include "Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
namespace clang {
namespace tidy {
namespace utils {
namespace decl_ref_expr {
using namespace ::clang::ast_matchers;
using llvm::SmallPtrSet;
namespace {
template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
for (const auto &E : S1)
if (S2.count(E) == 0)
return false;
return true;
}
// Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
template <typename Node>
void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
SmallPtrSet<const Node *, 16> &Nodes) {
for (const auto &Match : Matches)
Nodes.insert(Match.getNodeAs<Node>(ID));
}
} // namespace
// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
ASTContext &Context) {
auto DeclRefToVar =
declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
// Match method call expressions where the variable is referenced as the this
// implicit object argument and opertor call expression for member operators
// where the variable is the 0-th argument.
auto Matches = match(
findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
cxxOperatorCallExpr(ConstMethodCallee,
hasArgument(0, DeclRefToVar))))),
Stmt, Context);
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
extractNodesByIdTo(Matches, "declRef", DeclRefs);
auto ConstReferenceOrValue =
qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
unless(anyOf(referenceType(), pointerType()))));
auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
Matches =
match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
return DeclRefs;
}
// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
ASTContext &Context) {
auto DeclRefToVar =
declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
// Match method call expressions where the variable is referenced as the this
// implicit object argument and opertor call expression for member operators
// where the variable is the 0-th argument.
auto Matches =
match(decl(forEachDescendant(expr(
anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
cxxOperatorCallExpr(ConstMethodCallee,
hasArgument(0, DeclRefToVar)))))),
Decl, Context);
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
extractNodesByIdTo(Matches, "declRef", DeclRefs);
auto ConstReferenceOrValue =
qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
unless(anyOf(referenceType(), pointerType()))));
auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
Matches = match(decl(forEachDescendant(callExpr(UsedAsConstRefOrValueArg))),
Decl, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
Matches =
match(decl(forEachDescendant(cxxConstructExpr(UsedAsConstRefOrValueArg))),
Decl, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
return DeclRefs;
}
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
ASTContext &Context) {
// Collect all DeclRefExprs to the loop variable and all CallExprs and
// CXXConstructExprs where the loop variable is used as argument to a const
// reference parameter.
// If the difference is empty it is safe for the loop variable to be a const
// reference.
auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
}
SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
auto Matches = match(
findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
Stmt, Context);
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
extractNodesByIdTo(Matches, "declRef", DeclRefs);
return DeclRefs;
}
SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) {
auto Matches = match(
decl(forEachDescendant(
declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))),
Decl, Context);
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
extractNodesByIdTo(Matches, "declRef", DeclRefs);
return DeclRefs;
}
bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
ASTContext &Context) {
auto UsedAsConstRefArg = forEachArgumentWithParam(
declRefExpr(equalsNode(&DeclRef)),
parmVarDecl(hasType(matchers::isReferenceToConst())));
auto Matches = match(
decl(hasDescendant(
cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
isCopyConstructor())))
.bind("constructExpr"))),
Decl, Context);
return !Matches.empty();
}
bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
ASTContext &Context) {
auto UsedAsConstRefArg = forEachArgumentWithParam(
declRefExpr(equalsNode(&DeclRef)),
parmVarDecl(hasType(matchers::isReferenceToConst())));
auto Matches = match(
decl(hasDescendant(
cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="),
callee(cxxMethodDecl(isCopyAssignmentOperator())))
.bind("operatorCallExpr"))),
Decl, Context);
return !Matches.empty();
}
} // namespace decl_ref_expr
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,66 @@
//===--- DeclRefExprUtils.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_UTILS_DECLREFEXPRUTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/SmallPtrSet.h"
namespace clang {
namespace tidy {
namespace utils {
namespace decl_ref_expr {
/// \brief Returns true if all ``DeclRefExpr`` to the variable within ``Stmt``
/// do not modify it.
///
/// Returns ``true`` if only const methods or operators are called on the
/// variable or the variable is a const reference or value argument to a
/// ``callExpr()``.
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
ASTContext &Context);
/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt``.
llvm::SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context);
/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl``.
llvm::SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context);
/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt`` where
/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
llvm::SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
ASTContext &Context);
/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl`` where
/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
llvm::SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
ASTContext &Context);
/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-constructor
/// call expression within ``Decl``.
bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
ASTContext &Context);
/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-assignment
/// operator CallExpr within ``Decl``.
bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
ASTContext &Context);
} // namespace decl_ref_expr
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H

View File

@@ -0,0 +1,182 @@
//===---------- ExprSequence.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 "ExprSequence.h"
namespace clang {
namespace tidy {
namespace utils {
// Returns the Stmt nodes that are parents of 'S', skipping any potential
// intermediate non-Stmt nodes.
//
// In almost all cases, this function returns a single parent or no parents at
// all.
//
// The case that a Stmt has multiple parents is rare but does actually occur in
// the parts of the AST that we're interested in. Specifically, InitListExpr
// nodes cause ASTContext::getParent() to return multiple parents for certain
// nodes in their subtree because RecursiveASTVisitor visits both the syntactic
// and semantic forms of InitListExpr, and the parent-child relationships are
// different between the two forms.
static SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
ASTContext *Context) {
SmallVector<const Stmt *, 1> Result;
ASTContext::DynTypedNodeList Parents = Context->getParents(*S);
SmallVector<ast_type_traits::DynTypedNode, 1> NodesToProcess(Parents.begin(),
Parents.end());
while (!NodesToProcess.empty()) {
ast_type_traits::DynTypedNode Node = NodesToProcess.back();
NodesToProcess.pop_back();
if (const auto *S = Node.get<Stmt>()) {
Result.push_back(S);
} else {
Parents = Context->getParents(Node);
NodesToProcess.append(Parents.begin(), Parents.end());
}
}
return Result;
}
namespace {
bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor,
ASTContext *Context) {
if (Descendant == Ancestor)
return true;
for (const Stmt *Parent : getParentStmts(Descendant, Context)) {
if (isDescendantOrEqual(Parent, Ancestor, Context))
return true;
}
return false;
}
}
ExprSequence::ExprSequence(const CFG *TheCFG, ASTContext *TheContext)
: Context(TheContext) {
for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) {
SyntheticStmtSourceMap[SyntheticStmt.first] = SyntheticStmt.second;
}
}
bool ExprSequence::inSequence(const Stmt *Before, const Stmt *After) const {
Before = resolveSyntheticStmt(Before);
After = resolveSyntheticStmt(After);
// If 'After' is in the subtree of the siblings that follow 'Before' in the
// chain of successors, we know that 'After' is sequenced after 'Before'.
for (const Stmt *Successor = getSequenceSuccessor(Before); Successor;
Successor = getSequenceSuccessor(Successor)) {
if (isDescendantOrEqual(After, Successor, Context))
return true;
}
// If 'After' is a parent of 'Before' or is sequenced after one of these
// parents, we know that it is sequenced after 'Before'.
for (const Stmt *Parent : getParentStmts(Before, Context)) {
if (Parent == After || inSequence(Parent, After))
return true;
}
return false;
}
bool ExprSequence::potentiallyAfter(const Stmt *After,
const Stmt *Before) const {
return !inSequence(After, Before);
}
const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const {
for (const Stmt *Parent : getParentStmts(S, Context)) {
if (const auto *BO = dyn_cast<BinaryOperator>(Parent)) {
// Comma operator: Right-hand side is sequenced after the left-hand side.
if (BO->getLHS() == S && BO->getOpcode() == BO_Comma)
return BO->getRHS();
} else if (const auto *InitList = dyn_cast<InitListExpr>(Parent)) {
// Initializer list: Each initializer clause is sequenced after the
// clauses that precede it.
for (unsigned I = 1; I < InitList->getNumInits(); ++I) {
if (InitList->getInit(I - 1) == S)
return InitList->getInit(I);
}
} else if (const auto *Compound = dyn_cast<CompoundStmt>(Parent)) {
// Compound statement: Each sub-statement is sequenced after the
// statements that precede it.
const Stmt *Previous = nullptr;
for (const auto *Child : Compound->body()) {
if (Previous == S)
return Child;
Previous = Child;
}
} else if (const auto *TheDeclStmt = dyn_cast<DeclStmt>(Parent)) {
// Declaration: Every initializer expression is sequenced after the
// initializer expressions that precede it.
const Expr *PreviousInit = nullptr;
for (const Decl *TheDecl : TheDeclStmt->decls()) {
if (const auto *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
if (const Expr *Init = TheVarDecl->getInit()) {
if (PreviousInit == S)
return Init;
PreviousInit = Init;
}
}
}
} else if (const auto *ForRange = dyn_cast<CXXForRangeStmt>(Parent)) {
// Range-based for: Loop variable declaration is sequenced before the
// body. (We need this rule because these get placed in the same
// CFGBlock.)
if (S == ForRange->getLoopVarStmt())
return ForRange->getBody();
} else if (const auto *TheIfStmt = dyn_cast<IfStmt>(Parent)) {
// If statement: If a variable is declared inside the condition, the
// expression used to initialize the variable is sequenced before the
// evaluation of the condition.
if (S == TheIfStmt->getConditionVariableDeclStmt())
return TheIfStmt->getCond();
}
}
return nullptr;
}
const Stmt *ExprSequence::resolveSyntheticStmt(const Stmt *S) const {
if (SyntheticStmtSourceMap.count(S))
return SyntheticStmtSourceMap.lookup(S);
return S;
}
StmtToBlockMap::StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
: Context(TheContext) {
for (const auto *B : *TheCFG) {
for (const auto &Elem : *B) {
if (Optional<CFGStmt> S = Elem.getAs<CFGStmt>())
Map[S->getStmt()] = B;
}
}
}
const CFGBlock *StmtToBlockMap::blockContainingStmt(const Stmt *S) const {
while (!Map.count(S)) {
SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
if (Parents.empty())
return nullptr;
S = Parents[0];
}
return Map.lookup(S);
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,124 @@
//===------------- ExprSequence.h - clang-tidy ----------------------------===//
//
// 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_EXPRSEQUENCE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
#include "clang/Analysis/CFG.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace utils {
/// Provides information about the evaluation order of (sub-)expressions within
/// a `CFGBlock`.
///
/// While a `CFGBlock` does contain individual `CFGElement`s for some
/// sub-expressions, the order in which those `CFGElement`s appear reflects
/// only one possible order in which the sub-expressions may be evaluated.
/// However, we want to warn if any of the potential evaluation orders can lead
/// to a use-after-move, not just the one contained in the `CFGBlock`.
///
/// This class implements only a simplified version of the C++ sequencing
/// rules. The main limitation is that we do not distinguish between value
/// computation and side effect -- see the "Implementation" section for more
/// details.
///
/// Note: `SequenceChecker` from SemaChecking.cpp does a similar job (and much
/// more thoroughly), but using it would require
/// - Pulling `SequenceChecker` out into a header file (i.e. making it part of
/// the API),
/// - Removing the dependency of `SequenceChecker` on `Sema`, and
/// - (Probably) modifying `SequenceChecker` to make it suitable to be used in
/// this context.
/// For the moment, it seems preferable to re-implement our own version of
/// sequence checking that is special-cased to what we need here.
///
/// Implementation
/// --------------
///
/// `ExprSequence` uses two types of sequencing edges between nodes in the AST:
///
/// - Every `Stmt` is assumed to be sequenced after its children. This is
/// overly optimistic because the standard only states that value computations
/// of operands are sequenced before the value computation of the operator,
/// making no guarantees about side effects (in general).
///
/// For our purposes, this rule is sufficient, however, because this check is
/// interested in operations on objects, which are generally performed through
/// function calls (whether explicit and implicit). Function calls guarantee
/// that the value computations and side effects for all function arguments
/// are sequenced before the execution of the function.
///
/// - In addition, some `Stmt`s are known to be sequenced before or after
/// their siblings. For example, the `Stmt`s that make up a `CompoundStmt`are
/// all sequenced relative to each other. The function
/// `getSequenceSuccessor()` implements these sequencing rules.
class ExprSequence {
public:
/// Initializes this `ExprSequence` with sequence information for the given
/// `CFG`.
ExprSequence(const CFG *TheCFG, ASTContext *TheContext);
/// Returns whether \p Before is sequenced before \p After.
bool inSequence(const Stmt *Before, const Stmt *After) const;
/// Returns whether \p After can potentially be evaluated after \p Before.
/// This is exactly equivalent to `!inSequence(After, Before)` but makes some
/// conditions read more naturally.
bool potentiallyAfter(const Stmt *After, const Stmt *Before) const;
private:
// Returns the sibling of \p S (if any) that is directly sequenced after \p S,
// or nullptr if no such sibling exists. For example, if \p S is the child of
// a `CompoundStmt`, this would return the Stmt that directly follows \p S in
// the `CompoundStmt`.
//
// As the sequencing of many constructs that change control flow is already
// encoded in the `CFG`, this function only implements the sequencing rules
// for those constructs where sequencing cannot be inferred from the `CFG`.
const Stmt *getSequenceSuccessor(const Stmt *S) const;
const Stmt *resolveSyntheticStmt(const Stmt *S) const;
ASTContext *Context;
llvm::DenseMap<const Stmt *, const Stmt *> SyntheticStmtSourceMap;
};
/// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be
/// contained in more than one `CFGBlock`; in this case, they are mapped to the
/// innermost block (i.e. the one that is furthest from the root of the tree).
class StmtToBlockMap {
public:
/// Initializes the map for the given `CFG`.
StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext);
/// Returns the block that \p S is contained in. Some `Stmt`s may be contained
/// in more than one `CFGBlock`; in this case, this function returns the
/// innermost block (i.e. the one that is furthest from the root of the tree).
const CFGBlock *blockContainingStmt(const Stmt *S) const;
private:
ASTContext *Context;
llvm::DenseMap<const Stmt *, const CFGBlock *> Map;
};
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H

View File

@@ -0,0 +1,36 @@
//===--- FixItHintUtils.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 "FixItHintUtils.h"
#include "LexerUtils.h"
#include "clang/AST/ASTContext.h"
namespace clang {
namespace tidy {
namespace utils {
namespace fixit {
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
SourceLocation AmpLocation = Var.getLocation();
auto Token = utils::lexer::getPreviousToken(Context, AmpLocation);
if (!Token.is(tok::unknown))
AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
Context.getSourceManager(),
Context.getLangOpts());
return FixItHint::CreateInsertion(AmpLocation, "&");
}
FixItHint changeVarDeclToConst(const VarDecl &Var) {
return FixItHint::CreateInsertion(Var.getTypeSpecStartLoc(), "const ");
}
} // namespace fixit
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,32 @@
//===--- FixItHintUtils.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_UTILS_FIXITHINTUTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
namespace clang {
namespace tidy {
namespace utils {
namespace fixit {
/// \brief Creates fix to make ``VarDecl`` a reference by adding ``&``.
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context);
/// \brief Creates fix to make ``VarDecl`` const qualified.
FixItHint changeVarDeclToConst(const VarDecl &Var);
} // namespace fixit
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H

View File

@@ -0,0 +1,71 @@
//===--- HeaderFileExtensionsUtils.cpp - clang-tidy--------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "HeaderFileExtensionsUtils.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/Support/Path.h"
namespace clang {
namespace tidy {
namespace utils {
bool isExpansionLocInHeaderFile(
SourceLocation Loc, const SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions) {
SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
return isHeaderFileExtension(SM.getFilename(ExpansionLoc),
HeaderFileExtensions);
}
bool isPresumedLocInHeaderFile(
SourceLocation Loc, SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions) {
PresumedLoc PresumedLocation = SM.getPresumedLoc(Loc);
return isHeaderFileExtension(PresumedLocation.getFilename(),
HeaderFileExtensions);
}
bool isSpellingLocInHeaderFile(
SourceLocation Loc, SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions) {
SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
return isHeaderFileExtension(SM.getFilename(SpellingLoc),
HeaderFileExtensions);
}
bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
HeaderFileExtensionsSet &HeaderFileExtensions,
char delimiter) {
SmallVector<StringRef, 5> Suffixes;
AllHeaderFileExtensions.split(Suffixes, delimiter);
HeaderFileExtensions.clear();
for (StringRef Suffix : Suffixes) {
StringRef Extension = Suffix.trim();
for (StringRef::const_iterator it = Extension.begin();
it != Extension.end(); ++it) {
if (!isAlphanumeric(*it))
return false;
}
HeaderFileExtensions.insert(Extension);
}
return true;
}
bool isHeaderFileExtension(
StringRef FileName, const HeaderFileExtensionsSet &HeaderFileExtensions) {
StringRef extension = llvm::sys::path::extension(FileName);
if (extension.empty())
return false;
// Skip "." prefix.
return HeaderFileExtensions.count(extension.substr(1)) > 0;
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,56 @@
//===--- HeaderFileExtensionsUtils.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_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
namespace clang {
namespace tidy {
namespace utils {
typedef llvm::SmallSet<llvm::StringRef, 5> HeaderFileExtensionsSet;
/// \brief Checks whether expansion location of \p Loc is in header file.
bool isExpansionLocInHeaderFile(
SourceLocation Loc, const SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions);
/// \brief Checks whether presumed location of \p Loc is in header file.
bool isPresumedLocInHeaderFile(
SourceLocation Loc, SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions);
/// \brief Checks whether spelling location of \p Loc is in header file.
bool isSpellingLocInHeaderFile(
SourceLocation Loc, SourceManager &SM,
const HeaderFileExtensionsSet &HeaderFileExtensions);
/// \brief Returns recommended default value for the list of header file
/// extensions.
inline StringRef defaultHeaderFileExtensions() { return ",h,hh,hpp,hxx"; }
/// \brief Parses header file extensions from a semicolon-separated list.
bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
HeaderFileExtensionsSet &HeaderFileExtensions,
char delimiter);
/// \brief Decides whether a file has a header file extension.
bool isHeaderFileExtension(StringRef FileName,
const HeaderFileExtensionsSet &HeaderFileExtensions);
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H

View File

@@ -0,0 +1,292 @@
//===--- HeaderGuard.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 "HeaderGuard.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Path.h"
namespace clang {
namespace tidy {
namespace utils {
/// \brief canonicalize a path by removing ./ and ../ components.
static std::string cleanPath(StringRef Path) {
SmallString<256> Result = Path;
llvm::sys::path::remove_dots(Result, true);
return Result.str();
}
namespace {
class HeaderGuardPPCallbacks : public PPCallbacks {
public:
HeaderGuardPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check)
: PP(PP), Check(Check) {}
void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID) override {
// Record all files we enter. We'll need them to diagnose headers without
// guards.
SourceManager &SM = PP->getSourceManager();
if (Reason == EnterFile && FileType == SrcMgr::C_User) {
if (const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
std::string FileName = cleanPath(FE->getName());
Files[FileName] = FE;
}
}
}
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
if (MD)
return;
// Record #ifndefs that succeeded. We also need the Location of the Name.
Ifndefs[MacroNameTok.getIdentifierInfo()] =
std::make_pair(Loc, MacroNameTok.getLocation());
}
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override {
// Record all defined macros. We store the whole token to get info on the
// name later.
Macros.emplace_back(MacroNameTok, MD->getMacroInfo());
}
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
// Record all #endif and the corresponding #ifs (including #ifndefs).
EndIfs[IfLoc] = Loc;
}
void EndOfMainFile() override {
// Now that we have all this information from the preprocessor, use it!
SourceManager &SM = PP->getSourceManager();
for (const auto &MacroEntry : Macros) {
const MacroInfo *MI = MacroEntry.second;
// We use clang's header guard detection. This has the advantage of also
// emitting a warning for cases where a pseudo header guard is found but
// preceded by something blocking the header guard optimization.
if (!MI->isUsedForHeaderGuard())
continue;
const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
std::string FileName = cleanPath(FE->getName());
Files.erase(FileName);
// See if we should check and fix this header guard.
if (!Check->shouldFixHeaderGuard(FileName))
continue;
// Look up Locations for this guard.
SourceLocation Ifndef =
Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
SourceLocation Define = MacroEntry.first.getLocation();
SourceLocation EndIf =
EndIfs[Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
// If the macro Name is not equal to what we can compute, correct it in
// the #ifndef and #define.
StringRef CurHeaderGuard =
MacroEntry.first.getIdentifierInfo()->getName();
std::vector<FixItHint> FixIts;
std::string NewGuard = checkHeaderGuardDefinition(
Ifndef, Define, EndIf, FileName, CurHeaderGuard, FixIts);
// Now look at the #endif. We want a comment with the header guard. Fix it
// at the slightest deviation.
checkEndifComment(FileName, EndIf, NewGuard, FixIts);
// Bundle all fix-its into one warning. The message depends on whether we
// changed the header guard or not.
if (!FixIts.empty()) {
if (CurHeaderGuard != NewGuard) {
Check->diag(Ifndef, "header guard does not follow preferred style")
<< FixIts;
} else {
Check->diag(EndIf, "#endif for a header guard should reference the "
"guard macro in a comment")
<< FixIts;
}
}
}
// Emit warnings for headers that are missing guards.
checkGuardlessHeaders();
// Clear all state.
Macros.clear();
Files.clear();
Ifndefs.clear();
EndIfs.clear();
}
bool wouldFixEndifComment(StringRef FileName, SourceLocation EndIf,
StringRef HeaderGuard,
size_t *EndIfLenPtr = nullptr) {
if (!EndIf.isValid())
return false;
const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
size_t EndIfLen = std::strcspn(EndIfData, "\r\n");
if (EndIfLenPtr)
*EndIfLenPtr = EndIfLen;
StringRef EndIfStr(EndIfData, EndIfLen);
EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of("#endif \t"));
// Give up if there's an escaped newline.
size_t FindEscapedNewline = EndIfStr.find_last_not_of(' ');
if (FindEscapedNewline != StringRef::npos &&
EndIfStr[FindEscapedNewline] == '\\')
return false;
if (!Check->shouldSuggestEndifComment(FileName) &&
!(EndIfStr.startswith("//") ||
(EndIfStr.startswith("/*") && EndIfStr.endswith("*/"))))
return false;
return (EndIfStr != "// " + HeaderGuard.str()) &&
(EndIfStr != "/* " + HeaderGuard.str() + " */");
}
/// \brief Look for header guards that don't match the preferred style. Emit
/// fix-its and return the suggested header guard (or the original if no
/// change was made.
std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
SourceLocation Define,
SourceLocation EndIf,
StringRef FileName,
StringRef CurHeaderGuard,
std::vector<FixItHint> &FixIts) {
std::string CPPVar = Check->getHeaderGuard(FileName, CurHeaderGuard);
std::string CPPVarUnder = CPPVar + '_';
// Allow a trailing underscore iff we don't have to change the endif comment
// too.
if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
(CurHeaderGuard != CPPVarUnder ||
wouldFixEndifComment(FileName, EndIf, CurHeaderGuard))) {
FixIts.push_back(FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
CPPVar));
FixIts.push_back(FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Define, Define.getLocWithOffset(CurHeaderGuard.size())),
CPPVar));
return CPPVar;
}
return CurHeaderGuard;
}
/// \brief Checks the comment after the #endif of a header guard and fixes it
/// if it doesn't match \c HeaderGuard.
void checkEndifComment(StringRef FileName, SourceLocation EndIf,
StringRef HeaderGuard,
std::vector<FixItHint> &FixIts) {
size_t EndIfLen;
if (wouldFixEndifComment(FileName, EndIf, HeaderGuard, &EndIfLen)) {
FixIts.push_back(FixItHint::CreateReplacement(
CharSourceRange::getCharRange(EndIf,
EndIf.getLocWithOffset(EndIfLen)),
Check->formatEndIf(HeaderGuard)));
}
}
/// \brief Looks for files that were visited but didn't have a header guard.
/// Emits a warning with fixits suggesting adding one.
void checkGuardlessHeaders() {
// Look for header files that didn't have a header guard. Emit a warning and
// fix-its to add the guard.
// TODO: Insert the guard after top comments.
for (const auto &FE : Files) {
StringRef FileName = FE.getKey();
if (!Check->shouldSuggestToAddHeaderGuard(FileName))
continue;
SourceManager &SM = PP->getSourceManager();
FileID FID = SM.translateFile(FE.getValue());
SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
if (StartLoc.isInvalid())
continue;
std::string CPPVar = Check->getHeaderGuard(FileName);
std::string CPPVarUnder = CPPVar + '_'; // Allow a trailing underscore.
// If there's a macro with a name that follows the header guard convention
// but was not recognized by the preprocessor as a header guard there must
// be code outside of the guarded area. Emit a plain warning without
// fix-its.
// FIXME: Can we move it into the right spot?
bool SeenMacro = false;
for (const auto &MacroEntry : Macros) {
StringRef Name = MacroEntry.first.getIdentifierInfo()->getName();
SourceLocation DefineLoc = MacroEntry.first.getLocation();
if ((Name == CPPVar || Name == CPPVarUnder) &&
SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
Check->diag(DefineLoc, "code/includes outside of area guarded by "
"header guard; consider moving it");
SeenMacro = true;
break;
}
}
if (SeenMacro)
continue;
Check->diag(StartLoc, "header is missing header guard")
<< FixItHint::CreateInsertion(
StartLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + "\n\n")
<< FixItHint::CreateInsertion(
SM.getLocForEndOfFile(FID),
Check->shouldSuggestEndifComment(FileName)
? "\n#" + Check->formatEndIf(CPPVar) + "\n"
: "\n#endif\n");
}
}
private:
std::vector<std::pair<Token, const MacroInfo *>> Macros;
llvm::StringMap<const FileEntry *> Files;
std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
Ifndefs;
std::map<SourceLocation, SourceLocation> EndIfs;
Preprocessor *PP;
HeaderGuardCheck *Check;
};
} // namespace
void HeaderGuardCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Compiler.getPreprocessor().addPPCallbacks(
llvm::make_unique<HeaderGuardPPCallbacks>(&Compiler.getPreprocessor(),
this));
}
bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef FileName) {
return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
}
bool HeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) { return true; }
bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef FileName) {
return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
}
std::string HeaderGuardCheck::formatEndIf(StringRef HeaderGuard) {
return "endif // " + HeaderGuard.str();
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,64 @@
//===--- HeaderGuard.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_UTILS_HEADERGUARD_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
#include "../ClangTidy.h"
#include "../utils/HeaderFileExtensionsUtils.h"
namespace clang {
namespace tidy {
namespace utils {
/// Finds and fixes header guards.
/// The check supports these options:
/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
/// header files (The filename extension should not contain "." prefix).
/// ",h,hh,hpp,hxx" by default.
/// For extension-less header files, using an empty string or leaving an
/// empty string between "," if there are other filename extensions.
class HeaderGuardCheck : public ClangTidyCheck {
public:
HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
"HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions, ',');
}
void registerPPCallbacks(CompilerInstance &Compiler) override;
/// Returns ``true`` if the check should suggest inserting a trailing comment
/// on the ``#endif`` of the header guard. It will use the same name as
/// returned by ``HeaderGuardCheck::getHeaderGuard``.
virtual bool shouldSuggestEndifComment(StringRef Filename);
/// Returns ``true`` if the check should suggest changing an existing header
/// guard to the string returned by ``HeaderGuardCheck::getHeaderGuard``.
virtual bool shouldFixHeaderGuard(StringRef Filename);
/// Returns ``true`` if the check should add a header guard to the file
/// if it has none.
virtual bool shouldSuggestToAddHeaderGuard(StringRef Filename);
/// Returns a replacement for the ``#endif`` line with a comment mentioning
/// \p HeaderGuard. The replacement should start with ``endif``.
virtual std::string formatEndIf(StringRef HeaderGuard);
/// Gets the canonical header guard for a file.
virtual std::string getHeaderGuard(StringRef Filename,
StringRef OldGuard = StringRef()) = 0;
private:
std::string RawStringHeaderFileExtensions;
utils::HeaderFileExtensionsSet HeaderFileExtensions;
};
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H

View File

@@ -0,0 +1,85 @@
//===-------- IncludeInserter.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 "IncludeInserter.h"
#include "clang/Lex/Token.h"
namespace clang {
namespace tidy {
namespace utils {
class IncludeInserterCallback : public PPCallbacks {
public:
explicit IncludeInserterCallback(IncludeInserter *Inserter)
: Inserter(Inserter) {}
// Implements PPCallbacks::InclusionDerective(). Records the names and source
// locations of the inclusions in the main source file being processed.
void InclusionDirective(SourceLocation HashLocation,
const Token &IncludeToken, StringRef FileNameRef,
bool IsAngled, CharSourceRange FileNameRange,
const FileEntry * /*IncludedFile*/,
StringRef /*SearchPath*/, StringRef /*RelativePath*/,
const Module * /*ImportedModule*/) override {
Inserter->AddInclude(FileNameRef, IsAngled, HashLocation,
IncludeToken.getEndLoc());
}
private:
IncludeInserter *Inserter;
};
IncludeInserter::IncludeInserter(const SourceManager &SourceMgr,
const LangOptions &LangOpts,
IncludeSorter::IncludeStyle Style)
: SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style) {}
IncludeInserter::~IncludeInserter() {}
std::unique_ptr<PPCallbacks> IncludeInserter::CreatePPCallbacks() {
return llvm::make_unique<IncludeInserterCallback>(this);
}
llvm::Optional<FixItHint>
IncludeInserter::CreateIncludeInsertion(FileID FileID, StringRef Header,
bool IsAngled) {
// We assume the same Header will never be included both angled and not
// angled.
if (!InsertedHeaders[FileID].insert(Header).second)
return llvm::None;
if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
// This may happen if there have been no preprocessor directives in this
// file.
IncludeSorterByFile.insert(std::make_pair(
FileID,
llvm::make_unique<IncludeSorter>(
&SourceMgr, &LangOpts, FileID,
SourceMgr.getFilename(SourceMgr.getLocForStartOfFile(FileID)),
Style)));
}
return IncludeSorterByFile[FileID]->CreateIncludeInsertion(Header, IsAngled);
}
void IncludeInserter::AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation,
SourceLocation EndLocation) {
FileID FileID = SourceMgr.getFileID(HashLocation);
if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
IncludeSorterByFile.insert(std::make_pair(
FileID, llvm::make_unique<IncludeSorter>(
&SourceMgr, &LangOpts, FileID,
SourceMgr.getFilename(HashLocation), Style)));
}
IncludeSorterByFile[FileID]->AddInclude(FileName, IsAngled, HashLocation,
EndLocation);
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,84 @@
//===---------- IncludeInserter.h - clang-tidy ----------------------------===//
//
// 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_INCLUDEINSERTER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
#include "IncludeSorter.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/PPCallbacks.h"
#include <memory>
#include <string>
namespace clang {
namespace tidy {
namespace utils {
/// \brief Produces fixes to insert specified includes to source files, if not
/// yet present.
///
/// ``IncludeInserter`` can be used by ``ClangTidyCheck`` in the following
/// fashion:
/// \code
/// class MyCheck : public ClangTidyCheck {
/// public:
/// void registerPPCallbacks(CompilerInstance& Compiler) override {
/// Inserter.reset(new IncludeInserter(&Compiler.getSourceManager(),
/// &Compiler.getLangOpts()));
/// Compiler.getPreprocessor().addPPCallbacks(
/// Inserter->CreatePPCallback());
/// }
///
/// void registerMatchers(ast_matchers::MatchFinder* Finder) override { ... }
///
/// void check(
/// const ast_matchers::MatchFinder::MatchResult& Result) override {
/// ...
/// Inserter->CreateIncludeInsertion(
/// Result.SourceManager->getMainFileID(), "path/to/Header.h",
/// /*IsAngled=*/false);
/// ...
/// }
///
/// private:
/// std::unique_ptr<IncludeInserter> Inserter;
/// };
/// \endcode
class IncludeInserter {
public:
IncludeInserter(const SourceManager &SourceMgr, const LangOptions &LangOpts,
IncludeSorter::IncludeStyle Style);
~IncludeInserter();
/// Create ``PPCallbacks`` for registration with the compiler's preprocessor.
std::unique_ptr<PPCallbacks> CreatePPCallbacks();
/// Creates a \p Header inclusion directive fixit. Returns ``llvm::None`` on
/// error or if inclusion directive already exists.
llvm::Optional<FixItHint>
CreateIncludeInsertion(FileID FileID, llvm::StringRef Header, bool IsAngled);
private:
void AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation, SourceLocation EndLocation);
llvm::DenseMap<FileID, std::unique_ptr<IncludeSorter>> IncludeSorterByFile;
llvm::DenseMap<FileID, std::set<std::string>> InsertedHeaders;
const SourceManager &SourceMgr;
const LangOptions &LangOpts;
const IncludeSorter::IncludeStyle Style;
friend class IncludeInserterCallback;
};
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H

View File

@@ -0,0 +1,290 @@
//===---------- IncludeSorter.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 "IncludeSorter.h"
#include "clang/Lex/Lexer.h"
namespace clang {
namespace tidy {
namespace utils {
namespace {
StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
for (StringRef Suffix : Suffixes) {
if (Str.endswith(Suffix)) {
return Str.substr(0, Str.size() - Suffix.size());
}
}
return Str;
}
StringRef MakeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
// The list of suffixes to remove from source file names to get the
// "canonical" file names.
// E.g. tools/sort_includes.cc and tools/sort_includes_test.cc
// would both canonicalize to tools/sort_includes and tools/sort_includes.h
// (once canonicalized) will match as being the main include file associated
// with the source files.
if (Style == IncludeSorter::IS_LLVM) {
return RemoveFirstSuffix(
RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
}
return RemoveFirstSuffix(
RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
{"_unittest", "_regtest", "_test"});
}
// Scan to the end of the line and return the offset of the next line.
size_t FindNextLine(const char *Text) {
size_t EOLIndex = std::strcspn(Text, "\n");
return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1;
}
IncludeSorter::IncludeKinds
DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
bool IsAngled, IncludeSorter::IncludeStyle Style) {
// Compute the two "canonical" forms of the include's filename sans extension.
// The first form is the include's filename without ".h" or "-inl.h" at the
// end. The second form is the first form with "/public/" in the file path
// replaced by "/internal/".
if (IsAngled) {
// If the system include (<foo>) ends with ".h", then it is a normal C-style
// include. Otherwise assume it is a C++-style extensionless include.
return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude
: IncludeSorter::IK_CXXSystemInclude;
}
StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
if (CanonicalFile.endswith(CanonicalInclude)
|| CanonicalInclude.endswith(CanonicalFile)) {
return IncludeSorter::IK_MainTUInclude;
}
if (Style == IncludeSorter::IS_Google) {
std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
std::string AltCanonicalInclude =
Parts.first.str() + "/internal/" + Parts.second.str();
std::string ProtoCanonicalInclude =
Parts.first.str() + "/proto/" + Parts.second.str();
// Determine the kind of this inclusion.
if (CanonicalFile.equals(AltCanonicalInclude) ||
CanonicalFile.equals(ProtoCanonicalInclude)) {
return IncludeSorter::IK_MainTUInclude;
}
}
return IncludeSorter::IK_NonSystemInclude;
}
} // namespace
IncludeSorter::IncludeSorter(const SourceManager *SourceMgr,
const LangOptions *LangOpts, const FileID FileID,
StringRef FileName, IncludeStyle Style)
: SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
}
void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation,
SourceLocation EndLocation) {
int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
// Record the relevant location information for this inclusion directive.
IncludeLocations[FileName].push_back(
SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
SourceLocations.push_back(IncludeLocations[FileName].back());
// Stop if this inclusion is a duplicate.
if (IncludeLocations[FileName].size() > 1)
return;
// Add the included file's name to the appropriate bucket.
IncludeKinds Kind =
DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
if (Kind != IK_InvalidInclude)
IncludeBucket[Kind].push_back(FileName.str());
}
Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
bool IsAngled) {
std::string IncludeStmt =
IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
: llvm::Twine("#include \"" + FileName + "\"\n").str();
if (SourceLocations.empty()) {
// If there are no includes in this file, add it in the first line.
// FIXME: insert after the file comment or the header guard, if present.
IncludeStmt.append("\n");
return FixItHint::CreateInsertion(
SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
}
auto IncludeKind =
DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
if (!IncludeBucket[IncludeKind].empty()) {
for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
if (FileName < IncludeEntry) {
const auto &Location = IncludeLocations[IncludeEntry][0];
return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
} else if (FileName == IncludeEntry) {
return llvm::None;
}
}
// FileName comes after all include entries in bucket, insert it after
// last.
const std::string &LastInclude = IncludeBucket[IncludeKind].back();
SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
IncludeStmt);
}
// Find the non-empty include bucket to be sorted directly above
// 'IncludeKind'. If such a bucket exists, we'll want to sort the include
// after that bucket. If no such bucket exists, find the first non-empty
// include bucket in the file. In that case, we'll want to sort the include
// before that bucket.
IncludeKinds NonEmptyKind = IK_InvalidInclude;
for (int i = IK_InvalidInclude - 1; i >= 0; --i) {
if (!IncludeBucket[i].empty()) {
NonEmptyKind = static_cast<IncludeKinds>(i);
if (NonEmptyKind < IncludeKind)
break;
}
}
if (NonEmptyKind == IK_InvalidInclude) {
return llvm::None;
}
if (NonEmptyKind < IncludeKind) {
// Create a block after.
const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
IncludeStmt = '\n' + IncludeStmt;
return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
IncludeStmt);
}
// Create a block before.
const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
IncludeStmt.append("\n");
return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
IncludeStmt);
}
std::vector<FixItHint> IncludeSorter::GetEdits() {
if (SourceLocations.empty())
return {};
typedef std::map<int, std::pair<SourceRange, std::string>>
FileLineToSourceEditMap;
FileLineToSourceEditMap Edits;
auto SourceLocationIterator = SourceLocations.begin();
auto SourceLocationIteratorEnd = SourceLocations.end();
// Compute the Edits that need to be done to each line to add, replace, or
// delete inclusions.
for (int IncludeKind = 0; IncludeKind < IK_InvalidInclude; ++IncludeKind) {
std::sort(IncludeBucket[IncludeKind].begin(),
IncludeBucket[IncludeKind].end());
for (const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
auto &Location = IncludeLocations[IncludeEntry];
SourceRangeVector::iterator LocationIterator = Location.begin();
SourceRangeVector::iterator LocationIteratorEnd = Location.end();
SourceRange FirstLocation = *LocationIterator;
// If the first occurrence of a particular include is on the current
// source line we are examining, leave it alone.
if (FirstLocation == *SourceLocationIterator)
++LocationIterator;
// Add the deletion Edits for any (remaining) instances of this inclusion,
// and remove their Locations from the source Locations to be processed.
for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
int LineNumber =
SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
Edits[LineNumber] = std::make_pair(*LocationIterator, "");
SourceLocationIteratorEnd =
std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
*LocationIterator);
}
if (FirstLocation == *SourceLocationIterator) {
// Do nothing except move to the next source Location (Location of an
// inclusion in the original, unchanged source file).
++SourceLocationIterator;
continue;
}
// Add (or append to) the replacement text for this line in source file.
int LineNumber =
SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
if (Edits.find(LineNumber) == Edits.end()) {
Edits[LineNumber].first =
SourceRange(SourceLocationIterator->getBegin());
}
StringRef SourceText = Lexer::getSourceText(
CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
}
// Clear the bucket.
IncludeBucket[IncludeKind].clear();
}
// Go through the single-line Edits and combine them into blocks of Edits.
int CurrentEndLine = 0;
SourceRange CurrentRange;
std::string CurrentText;
std::vector<FixItHint> Fixes;
for (const auto &LineEdit : Edits) {
// If the current edit is on the next line after the previous edit, add it
// to the current block edit.
if (LineEdit.first == CurrentEndLine + 1 &&
CurrentRange.getBegin() != CurrentRange.getEnd()) {
SourceRange EditRange = LineEdit.second.first;
if (EditRange.getBegin() != EditRange.getEnd()) {
++CurrentEndLine;
CurrentRange.setEnd(EditRange.getEnd());
}
CurrentText += LineEdit.second.second;
// Otherwise report the current block edit and start a new block.
} else {
if (CurrentEndLine) {
Fixes.push_back(FixItHint::CreateReplacement(
CharSourceRange::getCharRange(CurrentRange), CurrentText));
}
CurrentEndLine = LineEdit.first;
CurrentRange = LineEdit.second.first;
CurrentText = LineEdit.second.second;
}
}
// Finally, report the current block edit if there is one.
if (CurrentEndLine) {
Fixes.push_back(FixItHint::CreateReplacement(
CharSourceRange::getCharRange(CurrentRange), CurrentText));
}
// Reset the remaining internal state.
SourceLocations.clear();
IncludeLocations.clear();
return Fixes;
}
IncludeSorter::IncludeStyle
IncludeSorter::parseIncludeStyle(const std::string &Value) {
return Value == "llvm" ? IS_LLVM : IS_Google;
}
StringRef IncludeSorter::toString(IncludeStyle Style) {
return Style == IS_LLVM ? "llvm" : "google";
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,86 @@
//===------------ IncludeSorter.h - clang-tidy ----------------------------===//
//
// 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_INCLUDESORTER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
#include "../ClangTidy.h"
#include <string>
namespace clang {
namespace tidy {
namespace utils {
/// Class used by ``IncludeInserterCallback`` to record the names of the
/// inclusions in a given source file being processed and generate the necessary
/// commands to sort the inclusions according to the precedence encoded in
/// ``IncludeKinds``.
class IncludeSorter {
public:
/// Supported include styles.
enum IncludeStyle { IS_LLVM = 0, IS_Google = 1 };
/// Converts "llvm" to ``IS_LLVM``, otherwise returns ``IS_Google``.
static IncludeStyle parseIncludeStyle(const std::string &Value);
/// Converts ``IncludeStyle`` to string representation.
static StringRef toString(IncludeStyle Style);
/// The classifications of inclusions, in the order they should be sorted.
enum IncludeKinds {
IK_MainTUInclude = 0, ///< e.g. ``#include "foo.h"`` when editing foo.cc
IK_CSystemInclude = 1, ///< e.g. ``#include <stdio.h>``
IK_CXXSystemInclude = 2, ///< e.g. ``#include <vector>``
IK_NonSystemInclude = 3, ///< e.g. ``#include "bar.h"``
IK_InvalidInclude = 4 ///< total number of valid ``IncludeKind``s
};
/// ``IncludeSorter`` constructor; takes the FileID and name of the file to be
/// processed by the sorter.
IncludeSorter(const SourceManager *SourceMgr, const LangOptions *LangOpts,
const FileID FileID, StringRef FileName, IncludeStyle Style);
/// Returns the ``SourceManager``-specific file ID for the file being handled
/// by the sorter.
const FileID current_FileID() const { return CurrentFileID; }
/// Adds the given include directive to the sorter.
void AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation, SourceLocation EndLocation);
/// Returns the edits needed to sort the current set of includes and reset the
/// internal state (so that different blocks of includes are sorted separately
/// within the same file).
std::vector<FixItHint> GetEdits();
/// Creates a quoted inclusion directive in the right sort order. Returns None
/// on error or if header inclusion directive for header already exists.
Optional<FixItHint> CreateIncludeInsertion(StringRef FileName, bool IsAngled);
private:
typedef SmallVector<SourceRange, 1> SourceRangeVector;
const SourceManager *SourceMgr;
const LangOptions *LangOpts;
const IncludeStyle Style;
FileID CurrentFileID;
/// The file name stripped of common suffixes.
StringRef CanonicalFile;
/// Locations of visited include directives.
SourceRangeVector SourceLocations;
/// Mapping from file name to #include locations.
llvm::StringMap<SourceRangeVector> IncludeLocations;
/// Includes sorted into buckets.
SmallVector<std::string, 1> IncludeBucket[IK_InvalidInclude];
};
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H

View File

@@ -0,0 +1,41 @@
//===--- LexerUtils.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 "LexerUtils.h"
namespace clang {
namespace tidy {
namespace utils {
namespace lexer {
Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
bool SkipComments) {
const auto &SourceManager = Context.getSourceManager();
Token Token;
Token.setKind(tok::unknown);
Location = Location.getLocWithOffset(-1);
auto StartOfFile =
SourceManager.getLocForStartOfFile(SourceManager.getFileID(Location));
while (Location != StartOfFile) {
Location = Lexer::GetBeginningOfToken(Location, SourceManager,
Context.getLangOpts());
if (!Lexer::getRawToken(Location, Token, SourceManager,
Context.getLangOpts()) &&
(!SkipComments || !Token.is(tok::comment))) {
break;
}
Location = Location.getLocWithOffset(-1);
}
return Token;
}
} // namespace lexer
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,30 @@
//===--- LexerUtils.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_UTILS_LEXER_UTILS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
namespace clang {
namespace tidy {
namespace utils {
namespace lexer {
/// Returns previous token or ``tok::unknown`` if not found.
Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
bool SkipComments = true);
} // namespace lexer
} // namespace utils
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H

View File

@@ -0,0 +1,55 @@
//===--- Matchers.h - clang-tidy-------------------------------------------===//
//
// 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_UTILS_MATCHERS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
#include "TypeTraits.h"
#include "clang/ASTMatchers/ASTMatchers.h"
namespace clang {
namespace tidy {
namespace matchers {
AST_MATCHER(BinaryOperator, isAssignmentOperator) {
return Node.isAssignmentOp();
}
AST_MATCHER(BinaryOperator, isRelationalOperator) {
return Node.isRelationalOp();
}
AST_MATCHER(BinaryOperator, isEqualityOperator) { return Node.isEqualityOp(); }
AST_MATCHER(BinaryOperator, isComparisonOperator) {
return Node.isComparisonOp();
}
AST_MATCHER(QualType, isExpensiveToCopy) {
llvm::Optional<bool> IsExpensive =
utils::type_traits::isExpensiveToCopy(Node, Finder->getASTContext());
return IsExpensive && *IsExpensive;
}
AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) {
return utils::type_traits::recordIsTriviallyDefaultConstructible(
Node, Finder->getASTContext());
}
// Returns QualType matcher for references to const.
AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isReferenceToConst) {
using namespace ast_matchers;
return referenceType(pointee(qualType(isConstQualified())));
}
} // namespace matchers
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H

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