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,308 @@
//===--- ArgumentCommentCheck.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 "ArgumentCommentCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Token.h"
#include "../utils/LexerUtils.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StrictMode(Options.getLocalOrGlobal("StrictMode", 0) != 0),
IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
void ArgumentCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StrictMode", StrictMode);
}
void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
callExpr(unless(cxxOperatorCallExpr()),
// NewCallback's arguments relate to the pointed function, don't
// check them against NewCallback's parameter names.
// FIXME: Make this configurable.
unless(hasDeclaration(functionDecl(
hasAnyName("NewCallback", "NewPermanentCallback")))))
.bind("expr"),
this);
Finder->addMatcher(cxxConstructExpr().bind("expr"), this);
}
static std::vector<std::pair<SourceLocation, StringRef>>
getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) {
std::vector<std::pair<SourceLocation, StringRef>> Comments;
auto &SM = Ctx->getSourceManager();
std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
EndLoc = SM.getDecomposedLoc(Range.getEnd());
if (BeginLoc.first != EndLoc.first)
return Comments;
bool Invalid = false;
StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
if (Invalid)
return Comments;
const char *StrData = Buffer.data() + BeginLoc.second;
Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
Buffer.begin(), StrData, Buffer.end());
TheLexer.SetCommentRetentionState(true);
while (true) {
Token Tok;
if (TheLexer.LexFromRawLexer(Tok))
break;
if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
break;
if (Tok.is(tok::comment)) {
std::pair<FileID, unsigned> CommentLoc =
SM.getDecomposedLoc(Tok.getLocation());
assert(CommentLoc.first == BeginLoc.first);
Comments.emplace_back(
Tok.getLocation(),
StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
} else {
// Clear comments found before the different token, e.g. comma.
Comments.clear();
}
}
return Comments;
}
static std::vector<std::pair<SourceLocation, StringRef>>
getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
std::vector<std::pair<SourceLocation, StringRef>> Comments;
while (Loc.isValid()) {
clang::Token Tok =
utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false);
if (Tok.isNot(tok::comment))
break;
Loc = Tok.getLocation();
Comments.emplace_back(
Loc,
Lexer::getSourceText(CharSourceRange::getCharRange(
Loc, Loc.getLocWithOffset(Tok.getLength())),
Ctx->getSourceManager(), Ctx->getLangOpts()));
}
return Comments;
}
static bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
StringRef ArgName, unsigned ArgIndex) {
std::string ArgNameLowerStr = ArgName.lower();
StringRef ArgNameLower = ArgNameLowerStr;
// The threshold is arbitrary.
unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
unsigned ThisED = ArgNameLower.edit_distance(
Params[ArgIndex]->getIdentifier()->getName().lower(),
/*AllowReplacements=*/true, UpperBound);
if (ThisED >= UpperBound)
return false;
for (unsigned I = 0, E = Params.size(); I != E; ++I) {
if (I == ArgIndex)
continue;
IdentifierInfo *II = Params[I]->getIdentifier();
if (!II)
continue;
const unsigned Threshold = 2;
// Other parameters must be an edit distance at least Threshold more away
// from this parameter. This gives us greater confidence that this is a typo
// of this parameter and not one with a similar name.
unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
/*AllowReplacements=*/true,
ThisED + Threshold);
if (OtherED < ThisED + Threshold)
return false;
}
return true;
}
static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode) {
if (StrictMode)
return InComment == InDecl;
InComment = InComment.trim('_');
InDecl = InDecl.trim('_');
// FIXME: compare_lower only works for ASCII.
return InComment.compare_lower(InDecl) == 0;
}
static bool looksLikeExpectMethod(const CXXMethodDecl *Expect) {
return Expect != nullptr && Expect->getLocation().isMacroID() &&
Expect->getNameInfo().getName().isIdentifier() &&
Expect->getName().startswith("gmock_");
}
static bool areMockAndExpectMethods(const CXXMethodDecl *Mock,
const CXXMethodDecl *Expect) {
assert(looksLikeExpectMethod(Expect));
return Mock != nullptr && Mock->getNextDeclInContext() == Expect &&
Mock->getNumParams() == Expect->getNumParams() &&
Mock->getLocation().isMacroID() &&
Mock->getNameInfo().getName().isIdentifier() &&
Mock->getName() == Expect->getName().substr(strlen("gmock_"));
}
// This uses implementation details of MOCK_METHODx_ macros: for each mocked
// method M it defines M() with appropriate signature and a method used to set
// up expectations - gmock_M() - with each argument's type changed the
// corresponding matcher. This function returns M when given either M or
// gmock_M.
static const CXXMethodDecl *findMockedMethod(const CXXMethodDecl *Method) {
if (looksLikeExpectMethod(Method)) {
const DeclContext *Ctx = Method->getDeclContext();
if (Ctx == nullptr || !Ctx->isRecord())
return nullptr;
for (const auto *D : Ctx->decls()) {
if (D->getNextDeclInContext() == Method) {
const auto *Previous = dyn_cast<CXXMethodDecl>(D);
return areMockAndExpectMethods(Previous, Method) ? Previous : nullptr;
}
}
return nullptr;
}
if (const auto *Next = dyn_cast_or_null<CXXMethodDecl>(
Method->getNextDeclInContext())) {
if (looksLikeExpectMethod(Next) && areMockAndExpectMethods(Method, Next))
return Method;
}
return nullptr;
}
// For gmock expectation builder method (the target of the call generated by
// `EXPECT_CALL(obj, Method(...))`) tries to find the real method being mocked
// (returns nullptr, if the mock method doesn't override anything). For other
// functions returns the function itself.
static const FunctionDecl *resolveMocks(const FunctionDecl *Func) {
if (const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
if (const auto *MockedMethod = findMockedMethod(Method)) {
// If mocked method overrides the real one, we can use its parameter
// names, otherwise we're out of luck.
if (MockedMethod->size_overridden_methods() > 0) {
return *MockedMethod->begin_overridden_methods();
}
return nullptr;
}
}
return Func;
}
void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
const FunctionDecl *OriginalCallee,
SourceLocation ArgBeginLoc,
llvm::ArrayRef<const Expr *> Args) {
const FunctionDecl *Callee = resolveMocks(OriginalCallee);
if (!Callee)
return;
Callee = Callee->getFirstDecl();
unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
if (NumArgs == 0)
return;
auto makeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
Ctx->getSourceManager(),
Ctx->getLangOpts());
};
for (unsigned I = 0; I < NumArgs; ++I) {
const ParmVarDecl *PVD = Callee->getParamDecl(I);
IdentifierInfo *II = PVD->getIdentifier();
if (!II)
continue;
if (auto Template = Callee->getTemplateInstantiationPattern()) {
// Don't warn on arguments for parameters instantiated from template
// parameter packs. If we find more arguments than the template
// definition has, it also means that they correspond to a parameter
// pack.
if (Template->getNumParams() <= I ||
Template->getParamDecl(I)->isParameterPack()) {
continue;
}
}
CharSourceRange BeforeArgument =
makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart());
ArgBeginLoc = Args[I]->getLocEnd();
std::vector<std::pair<SourceLocation, StringRef>> Comments;
if (BeforeArgument.isValid()) {
Comments = getCommentsInRange(Ctx, BeforeArgument);
} else {
// Fall back to parsing back from the start of the argument.
CharSourceRange ArgsRange = makeFileCharRange(
Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd());
Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin());
}
for (auto Comment : Comments) {
llvm::SmallVector<StringRef, 2> Matches;
if (IdentRE.match(Comment.second, &Matches) &&
!sameName(Matches[2], II->getName(), StrictMode)) {
{
DiagnosticBuilder Diag =
diag(Comment.first, "argument name '%0' in comment does not "
"match parameter name %1")
<< Matches[2] << II;
if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
Diag << FixItHint::CreateReplacement(
Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
}
}
diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II;
if (OriginalCallee != Callee) {
diag(OriginalCallee->getLocation(),
"actual callee (%0) is declared here", DiagnosticIDs::Note)
<< OriginalCallee;
}
}
}
}
}
void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
if (const auto *Call = dyn_cast<CallExpr>(E)) {
const FunctionDecl *Callee = Call->getDirectCallee();
if (!Callee)
return;
checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
} else {
const auto *Construct = cast<CXXConstructExpr>(E);
if (Construct->getNumArgs() == 1 &&
Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
// Ignore implicit construction.
return;
}
checkCallArgs(
Result.Context, Construct->getConstructor(),
Construct->getParenOrBraceRange().getBegin(),
llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
}
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,55 @@
//===--- ArgumentCommentCheck.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_BUGPRONE_ARGUMENTCOMMENTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ARGUMENTCOMMENTCHECK_H
#include "../ClangTidy.h"
#include "llvm/Support/Regex.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks that argument comments match parameter names.
///
/// The check understands argument comments in the form `/*parameter_name=*/`
/// that are placed right before the argument.
///
/// \code
/// void f(bool foo);
///
/// ...
/// f(/*bar=*/true);
/// // warning: argument name 'bar' in comment does not match parameter name 'foo'
/// \endcode
///
/// The check tries to detect typos and suggest automated fixes for them.
class ArgumentCommentCheck : public ClangTidyCheck {
public:
ArgumentCommentCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const bool StrictMode;
llvm::Regex IdentRE;
void checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee,
SourceLocation ArgBeginLoc,
llvm::ArrayRef<const Expr *> Args);
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ARGUMENTCOMMENTCHECK_H

View File

@@ -0,0 +1,127 @@
//===--- AssertSideEffectCheck.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 "AssertSideEffectCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <string>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) {
const Expr *E = &Node;
if (const auto *Op = dyn_cast<UnaryOperator>(E)) {
UnaryOperator::Opcode OC = Op->getOpcode();
return OC == UO_PostInc || OC == UO_PostDec || OC == UO_PreInc ||
OC == UO_PreDec;
}
if (const auto *Op = dyn_cast<BinaryOperator>(E)) {
return Op->isAssignmentOp();
}
if (const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
OverloadedOperatorKind OpKind = OpCallExpr->getOperator();
return OpKind == OO_Equal || OpKind == OO_PlusEqual ||
OpKind == OO_MinusEqual || OpKind == OO_StarEqual ||
OpKind == OO_SlashEqual || OpKind == OO_AmpEqual ||
OpKind == OO_PipeEqual || OpKind == OO_CaretEqual ||
OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual ||
OpKind == OO_PlusPlus || OpKind == OO_MinusMinus ||
OpKind == OO_PercentEqual || OpKind == OO_New ||
OpKind == OO_Delete || OpKind == OO_Array_New ||
OpKind == OO_Array_Delete;
}
if (const auto *CExpr = dyn_cast<CallExpr>(E)) {
bool Result = CheckFunctionCalls;
if (const auto *FuncDecl = CExpr->getDirectCallee()) {
if (FuncDecl->getDeclName().isIdentifier() &&
FuncDecl->getName() == "__builtin_expect") // exceptions come here
Result = false;
else if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
Result &= !MethodDecl->isConst();
}
return Result;
}
return isa<CXXNewExpr>(E) || isa<CXXDeleteExpr>(E) || isa<CXXThrowExpr>(E);
}
} // namespace
AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
CheckFunctionCalls(Options.get("CheckFunctionCalls", false)),
RawAssertList(Options.get("AssertMacros", "assert")) {
StringRef(RawAssertList).split(AssertMacros, ",", -1, false);
}
// The options are explained in AssertSideEffectCheck.h.
void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "CheckFunctionCalls", CheckFunctionCalls);
Options.store(Opts, "AssertMacros", RawAssertList);
}
void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) {
auto DescendantWithSideEffect =
hasDescendant(expr(hasSideEffect(CheckFunctionCalls)));
auto ConditionWithSideEffect = hasCondition(DescendantWithSideEffect);
Finder->addMatcher(
stmt(
anyOf(conditionalOperator(ConditionWithSideEffect),
ifStmt(ConditionWithSideEffect),
unaryOperator(hasOperatorName("!"),
hasUnaryOperand(unaryOperator(
hasOperatorName("!"),
hasUnaryOperand(DescendantWithSideEffect))))))
.bind("condStmt"),
this);
}
void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) {
const SourceManager &SM = *Result.SourceManager;
const LangOptions LangOpts = getLangOpts();
SourceLocation Loc = Result.Nodes.getNodeAs<Stmt>("condStmt")->getLocStart();
StringRef AssertMacroName;
while (Loc.isValid() && Loc.isMacroID()) {
StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LangOpts);
// Check if this macro is an assert.
if (std::find(AssertMacros.begin(), AssertMacros.end(), MacroName) !=
AssertMacros.end()) {
AssertMacroName = MacroName;
break;
}
Loc = SM.getImmediateMacroCallerLoc(Loc);
}
if (AssertMacroName.empty())
return;
diag(Loc, "found %0() with side effect") << AssertMacroName;
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,52 @@
//===--- AssertSideEffectCheck.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_BUGPRONE_ASSERTSIDEEFFECTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSERTSIDEEFFECTCHECK_H
#include "../ClangTidy.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds `assert()` with side effect.
///
/// The condition of `assert()` is evaluated only in debug builds so a
/// condition with side effect can cause different behavior in debug / release
/// builds.
///
/// There are two options:
///
/// - `AssertMacros`: A comma-separated list of the names of assert macros to
/// be checked.
/// - `CheckFunctionCalls`: Whether to treat non-const member and non-member
/// functions as they produce side effects. Disabled by default because it
/// can increase the number of false positive warnings.
class AssertSideEffectCheck : public ClangTidyCheck {
public:
AssertSideEffectCheck(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:
const bool CheckFunctionCalls;
const std::string RawAssertList;
SmallVector<StringRef, 5> AssertMacros;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSERTSIDEEFFECTCHECK_H

View File

@@ -0,0 +1,73 @@
//===--- BoolPointerImplicitConversionCheck.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 "BoolPointerImplicitConversionCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
// Look for ifs that have an implicit bool* to bool conversion in the
// condition. Filter negations.
Finder->addMatcher(
ifStmt(hasCondition(findAll(implicitCastExpr(
allOf(unless(hasParent(unaryOperator(hasOperatorName("!")))),
hasSourceExpression(expr(
hasType(pointerType(pointee(booleanType()))),
ignoringParenImpCasts(declRefExpr().bind("expr")))),
hasCastKind(CK_PointerToBoolean))))),
unless(isInTemplateInstantiation()))
.bind("if"),
this);
}
void BoolPointerImplicitConversionCheck::check(
const MatchFinder::MatchResult &Result) {
auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
// Ignore macros.
if (Var->getLocStart().isMacroID())
return;
// Only allow variable accesses for now, no function calls or member exprs.
// Check that we don't dereference the variable anywhere within the if. This
// avoids false positives for checks of the pointer for nullptr before it is
// dereferenced. If there is a dereferencing operator on this variable don't
// emit a diagnostic. Also ignore array subscripts.
const Decl *D = Var->getDecl();
auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
if (!match(findAll(
unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
*If, *Result.Context)
.empty() ||
!match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
*Result.Context)
.empty() ||
// FIXME: We should still warn if the paremater is implicitly converted to
// bool.
!match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
*If, *Result.Context)
.empty() ||
!match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
*If, *Result.Context)
.empty())
return;
diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did "
"you mean to dereference it?")
<< FixItHint::CreateInsertion(Var->getLocStart(), "*");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,42 @@
//===--- BoolPointerImplicitConversionCheck.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_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks for conditions based on implicit conversion from a bool pointer to
/// bool.
///
/// Example:
///
/// \code
/// bool *p;
/// if (p) {
/// // Never used in a pointer-specific way.
/// }
/// \endcode
class BoolPointerImplicitConversionCheck : public ClangTidyCheck {
public:
BoolPointerImplicitConversionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H

View File

@@ -0,0 +1,86 @@
//===--- BugproneTidyModule.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 "ArgumentCommentCheck.h"
#include "AssertSideEffectCheck.h"
#include "BoolPointerImplicitConversionCheck.h"
#include "CopyConstructorInitCheck.h"
#include "DanglingHandleCheck.h"
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
#include "InaccurateEraseCheck.h"
#include "IntegerDivisionCheck.h"
#include "MisplacedOperatorInStrlenInAllocCheck.h"
#include "MoveForwardingReferenceCheck.h"
#include "MultipleStatementMacroCheck.h"
#include "StringConstructorCheck.h"
#include "SuspiciousMemsetUsageCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
#include "UseAfterMoveCheck.h"
#include "VirtualNearMissCheck.h"
namespace clang {
namespace tidy {
namespace bugprone {
class BugproneModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ArgumentCommentCheck>(
"bugprone-argument-comment");
CheckFactories.registerCheck<AssertSideEffectCheck>(
"bugprone-assert-side-effect");
CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
"bugprone-bool-pointer-implicit-conversion");
CheckFactories.registerCheck<CopyConstructorInitCheck>(
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
CheckFactories.registerCheck<FoldInitTypeCheck>(
"bugprone-fold-init-type");
CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
"bugprone-forward-declaration-namespace");
CheckFactories.registerCheck<InaccurateEraseCheck>(
"bugprone-inaccurate-erase");
CheckFactories.registerCheck<IntegerDivisionCheck>(
"bugprone-integer-division");
CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
"bugprone-misplaced-operator-in-strlen-in-alloc");
CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
"bugprone-move-forwarding-reference");
CheckFactories.registerCheck<MultipleStatementMacroCheck>(
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<StringConstructorCheck>(
"bugprone-string-constructor");
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
"bugprone-suspicious-memset-usage");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
CheckFactories.registerCheck<UseAfterMoveCheck>(
"bugprone-use-after-move");
CheckFactories.registerCheck<VirtualNearMissCheck>(
"bugprone-virtual-near-miss");
}
};
} // namespace bugprone
// Register the BugproneTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<bugprone::BugproneModule>
X("bugprone-module", "Adds checks for bugprone code constructs.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the BugproneModule.
volatile int BugproneModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,32 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyBugproneModule
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
BoolPointerImplicitConversionCheck.cpp
BugproneTidyModule.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
FoldInitTypeCheck.cpp
ForwardDeclarationNamespaceCheck.cpp
InaccurateEraseCheck.cpp
IntegerDivisionCheck.cpp
MisplacedOperatorInStrlenInAllocCheck.cpp
MoveForwardingReferenceCheck.cpp
MultipleStatementMacroCheck.cpp
StringConstructorCheck.cpp
SuspiciousMemsetUsageCheck.cpp
UndefinedMemoryManipulationCheck.cpp
UseAfterMoveCheck.cpp
VirtualNearMissCheck.cpp
LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
clangTooling
)

View File

@@ -0,0 +1,121 @@
//===--- CopyConstructorInitCheck.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 "CopyConstructorInitCheck.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 bugprone {
void CopyConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// In the future this might be extended to move constructors?
Finder->addMatcher(
cxxConstructorDecl(
isCopyConstructor(),
hasAnyConstructorInitializer(cxxCtorInitializer(
isBaseInitializer(),
withInitializer(cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(isDefaultConstructor())))))),
unless(isInstantiated()))
.bind("ctor"),
this);
}
void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
std::string ParamName = Ctor->getParamDecl(0)->getNameAsString();
// We want only one warning (and FixIt) for each ctor.
std::string FixItInitList;
bool HasRelevantBaseInit = false;
bool ShouldNotDoFixit = false;
bool HasWrittenInitializer = false;
SmallVector<FixItHint, 2> SafeFixIts;
for (const auto *Init : Ctor->inits()) {
bool CtorInitIsWritten = Init->isWritten();
HasWrittenInitializer = HasWrittenInitializer || CtorInitIsWritten;
if (!Init->isBaseInitializer())
continue;
const Type *BaseType = Init->getBaseClass();
// Do not do fixits if there is a type alias involved or one of the bases
// are explicitly initialized. In the latter case we not do fixits to avoid
// -Wreorder warnings.
if (const auto *TempSpecTy = dyn_cast<TemplateSpecializationType>(BaseType))
ShouldNotDoFixit = ShouldNotDoFixit || TempSpecTy->isTypeAlias();
ShouldNotDoFixit = ShouldNotDoFixit || isa<TypedefType>(BaseType);
ShouldNotDoFixit = ShouldNotDoFixit || CtorInitIsWritten;
const CXXRecordDecl *BaseClass =
BaseType->getAsCXXRecordDecl()->getDefinition();
if (BaseClass->field_empty() &&
BaseClass->forallBases(
[](const CXXRecordDecl *Class) { return Class->field_empty(); }))
continue;
bool NonCopyableBase = false;
for (const auto *Ctor : BaseClass->ctors()) {
if (Ctor->isCopyConstructor() &&
(Ctor->getAccess() == AS_private || Ctor->isDeleted())) {
NonCopyableBase = true;
break;
}
}
if (NonCopyableBase)
continue;
const auto *CExpr = dyn_cast<CXXConstructExpr>(Init->getInit());
if (!CExpr || !CExpr->getConstructor()->isDefaultConstructor())
continue;
HasRelevantBaseInit = true;
if (CtorInitIsWritten) {
if (!ParamName.empty())
SafeFixIts.push_back(
FixItHint::CreateInsertion(CExpr->getLocEnd(), ParamName));
} else {
if (Init->getSourceLocation().isMacroID() ||
Ctor->getLocation().isMacroID() || ShouldNotDoFixit)
break;
FixItInitList += BaseClass->getNameAsString();
FixItInitList += "(" + ParamName + "), ";
}
}
if (!HasRelevantBaseInit)
return;
auto Diag = diag(Ctor->getLocation(),
"calling a base constructor other than the copy constructor")
<< SafeFixIts;
if (FixItInitList.empty() || ParamName.empty() || ShouldNotDoFixit)
return;
std::string FixItMsg{FixItInitList.substr(0, FixItInitList.size() - 2)};
SourceLocation FixItLoc;
// There is no initialization list in this constructor.
if (!HasWrittenInitializer) {
FixItLoc = Ctor->getBody()->getLocStart();
FixItMsg = " : " + FixItMsg;
} else {
// We apply the missing ctors at the beginning of the initialization list.
FixItLoc = (*Ctor->init_begin())->getSourceLocation();
FixItMsg += ',';
}
FixItMsg += ' ';
Diag << FixItHint::CreateInsertion(FixItLoc, FixItMsg);
} // namespace misc
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,36 @@
//===--- CopyConstructorInitCheck.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_BUGPRONE_COPY_CONSTRUCTOR_INIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_COPY_CONSTRUCTOR_INIT_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds copy constructors where the ctor don't call the copy constructor of
/// the base class.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-copy-constructor-init.html
class CopyConstructorInitCheck : public ClangTidyCheck {
public:
CopyConstructorInitCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_COPY_CONSTRUCTOR_INIT_H

View File

@@ -0,0 +1,184 @@
//===--- DanglingHandleCheck.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 "DanglingHandleCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
using namespace clang::tidy::matchers;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
ast_matchers::internal::BindableMatcher<Stmt>
handleFrom(const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle,
const ast_matchers::internal::Matcher<Expr> &Arg) {
return cxxConstructExpr(hasDeclaration(cxxMethodDecl(ofClass(IsAHandle))),
hasArgument(0, Arg));
}
ast_matchers::internal::Matcher<Stmt> handleFromTemporaryValue(
const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
// If a ternary operator returns a temporary value, then both branches hold a
// temporary value. If one of them is not a temporary then it must be copied
// into one to satisfy the type of the operator.
const auto TemporaryTernary =
conditionalOperator(hasTrueExpression(cxxBindTemporaryExpr()),
hasFalseExpression(cxxBindTemporaryExpr()));
return handleFrom(IsAHandle, anyOf(cxxBindTemporaryExpr(), TemporaryTernary));
}
ast_matchers::internal::Matcher<RecordDecl> isASequence() {
return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
"::std::vector");
}
ast_matchers::internal::Matcher<RecordDecl> isASet() {
return hasAnyName("::std::set", "::std::multiset", "::std::unordered_set",
"::std::unordered_multiset");
}
ast_matchers::internal::Matcher<RecordDecl> isAMap() {
return hasAnyName("::std::map", "::std::multimap", "::std::unordered_map",
"::std::unordered_multimap");
}
ast_matchers::internal::BindableMatcher<Stmt> makeContainerMatcher(
const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
// This matcher could be expanded to detect:
// - Constructors: eg. vector<string_view>(3, string("A"));
// - emplace*(): This requires a different logic to determine that
// the conversion will happen inside the container.
// - map's insert: This requires detecting that the pair conversion triggers
// the bug. A little more complicated than what we have now.
return callExpr(
hasAnyArgument(
ignoringParenImpCasts(handleFromTemporaryValue(IsAHandle))),
anyOf(
// For sequences: assign, push_back, resize.
cxxMemberCallExpr(
callee(functionDecl(hasAnyName("assign", "push_back", "resize"))),
on(expr(hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(isASequence())))))))),
// For sequences and sets: insert.
cxxMemberCallExpr(callee(functionDecl(hasName("insert"))),
on(expr(hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(
anyOf(isASequence(), isASet()))))))))),
// For maps: operator[].
cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(isAMap()))),
hasOverloadedOperatorName("[]"))));
}
} // anonymous namespace
DanglingHandleCheck::DanglingHandleCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
HandleClasses(utils::options::parseStringList(Options.get(
"HandleClasses",
"std::basic_string_view;std::experimental::basic_string_view"))),
IsAHandle(cxxRecordDecl(hasAnyName(std::vector<StringRef>(
HandleClasses.begin(), HandleClasses.end())))
.bind("handle")) {}
void DanglingHandleCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HandleClasses",
utils::options::serializeStringList(HandleClasses));
}
void DanglingHandleCheck::registerMatchersForVariables(MatchFinder *Finder) {
const auto ConvertedHandle = handleFromTemporaryValue(IsAHandle);
// Find 'Handle foo(ReturnsAValue());'
Finder->addMatcher(
varDecl(hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(IsAHandle))))),
hasInitializer(
exprWithCleanups(has(ignoringParenImpCasts(ConvertedHandle)))
.bind("bad_stmt"))),
this);
// Find 'Handle foo = ReturnsAValue();'
Finder->addMatcher(
varDecl(
hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(IsAHandle))))),
unless(parmVarDecl()),
hasInitializer(exprWithCleanups(has(ignoringParenImpCasts(handleFrom(
IsAHandle, ConvertedHandle))))
.bind("bad_stmt"))),
this);
// Find 'foo = ReturnsAValue(); // foo is Handle'
Finder->addMatcher(
cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(IsAHandle))),
hasOverloadedOperatorName("="),
hasArgument(1, ConvertedHandle))
.bind("bad_stmt"),
this);
// Container insertions that will dangle.
Finder->addMatcher(makeContainerMatcher(IsAHandle).bind("bad_stmt"), this);
}
void DanglingHandleCheck::registerMatchersForReturn(MatchFinder *Finder) {
// Return a local.
Finder->addMatcher(
returnStmt(
// The AST contains two constructor calls:
// 1. Value to Handle conversion.
// 2. Handle copy construction.
// We have to match both.
has(ignoringImplicit(handleFrom(
IsAHandle,
handleFrom(IsAHandle,
declRefExpr(to(varDecl(
// Is function scope ...
hasAutomaticStorageDuration(),
// ... and it is a local array or Value.
anyOf(hasType(arrayType()),
hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(
unless(IsAHandle)))))))))))))),
// Temporary fix for false positives inside lambdas.
unless(hasAncestor(lambdaExpr())))
.bind("bad_stmt"),
this);
// Return a temporary.
Finder->addMatcher(
returnStmt(
has(ignoringParenImpCasts(exprWithCleanups(has(ignoringParenImpCasts(
handleFrom(IsAHandle, handleFromTemporaryValue(IsAHandle))))))))
.bind("bad_stmt"),
this);
}
void DanglingHandleCheck::registerMatchers(MatchFinder *Finder) {
registerMatchersForVariables(Finder);
registerMatchersForReturn(Finder);
}
void DanglingHandleCheck::check(const MatchFinder::MatchResult &Result) {
auto *Handle = Result.Nodes.getNodeAs<CXXRecordDecl>("handle");
diag(Result.Nodes.getNodeAs<Stmt>("bad_stmt")->getLocStart(),
"%0 outlives its value")
<< Handle->getQualifiedNameAsString();
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,43 @@
//===--- DanglingHandleCheck.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_BUGPRONE_DANGLING_HANDLE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DANGLING_HANDLE_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Detect dangling references in value handlers like
/// std::experimental::string_view.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-dangling-handle.html
class DanglingHandleCheck : public ClangTidyCheck {
public:
DanglingHandleCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
void registerMatchersForVariables(ast_matchers::MatchFinder *Finder);
void registerMatchersForReturn(ast_matchers::MatchFinder *Finder);
const std::vector<std::string> HandleClasses;
const ast_matchers::internal::Matcher<RecordDecl> IsAHandle;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DANGLING_HANDLE_H

View File

@@ -0,0 +1,140 @@
//===--- FoldInitTypeCheck.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 "FoldInitTypeCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) {
// We match functions of interest and bind the iterator and init value types.
// Note: Right now we check only builtin types.
const auto BuiltinTypeWithId = [](const char *ID) {
return hasCanonicalType(builtinType().bind(ID));
};
const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) {
return anyOf(
// Pointer types.
pointsTo(BuiltinTypeWithId(ID)),
// Iterator types.
recordType(hasDeclaration(has(typedefNameDecl(
hasName("value_type"), hasType(BuiltinTypeWithId(ID)))))));
};
const auto IteratorParam = parmVarDecl(
hasType(hasCanonicalType(IteratorWithValueType("IterValueType"))));
const auto Iterator2Param = parmVarDecl(
hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType"))));
const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType")));
// std::accumulate, std::reduce.
Finder->addMatcher(
callExpr(callee(functionDecl(
hasAnyName("::std::accumulate", "::std::reduce"),
hasParameter(0, IteratorParam), hasParameter(2, InitParam))),
argumentCountIs(3))
.bind("Call"),
this);
// std::inner_product.
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::std::inner_product"),
hasParameter(0, IteratorParam),
hasParameter(2, Iterator2Param),
hasParameter(3, InitParam))),
argumentCountIs(4))
.bind("Call"),
this);
// std::reduce with a policy.
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::std::reduce"),
hasParameter(1, IteratorParam),
hasParameter(3, InitParam))),
argumentCountIs(4))
.bind("Call"),
this);
// std::inner_product with a policy.
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::std::inner_product"),
hasParameter(1, IteratorParam),
hasParameter(3, Iterator2Param),
hasParameter(4, InitParam))),
argumentCountIs(5))
.bind("Call"),
this);
}
/// Returns true if ValueType is allowed to fold into InitType, i.e. if:
/// static_cast<InitType>(ValueType{some_value})
/// does not result in trucation.
static bool isValidBuiltinFold(const BuiltinType &ValueType,
const BuiltinType &InitType,
const ASTContext &Context) {
const auto ValueTypeSize = Context.getTypeSize(&ValueType);
const auto InitTypeSize = Context.getTypeSize(&InitType);
// It's OK to fold a float into a float of bigger or equal size, but not OK to
// fold into an int.
if (ValueType.isFloatingPoint())
return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize;
// It's OK to fold an int into:
// - an int of the same size and signedness.
// - a bigger int, regardless of signedness.
// - FIXME: should it be a warning to fold into floating point?
if (ValueType.isInteger()) {
if (InitType.isInteger()) {
if (InitType.isSignedInteger() == ValueType.isSignedInteger())
return InitTypeSize >= ValueTypeSize;
return InitTypeSize > ValueTypeSize;
}
if (InitType.isFloatingPoint())
return InitTypeSize >= ValueTypeSize;
}
return false;
}
/// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see
// isValidBuiltinFold for details).
void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType,
const BuiltinType &InitType,
const ASTContext &Context,
const CallExpr &CallNode) {
if (!isValidBuiltinFold(IterValueType, InitType, Context)) {
diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in "
"loss of precision")
<< IterValueType.desugar() << InitType.desugar();
}
}
void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) {
// Given the iterator and init value type retreived by the matchers,
// we check that the ::value_type of the iterator is compatible with
// the init value type.
const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType");
const auto *IterValueType =
Result.Nodes.getNodeAs<BuiltinType>("IterValueType");
assert(InitType != nullptr);
assert(IterValueType != nullptr);
const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call");
assert(CallNode != nullptr);
doCheck(*IterValueType, *InitType, *Result.Context, *CallNode);
if (const auto *Iter2ValueType =
Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType"))
doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode);
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,44 @@
//===--- FoldInitTypeCheck.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_BUGPRONE_FOLD_INIT_TYPE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FOLD_INIT_TYPE_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Find and flag invalid initializer values in folds, e.g. std::accumulate.
/// Example:
/// \code
/// auto v = {65536L * 65536 * 65536};
/// std::accumulate(begin(v), end(v), 0 /* int type is too small */);
/// \endcode
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-fold-init-type.html
class FoldInitTypeCheck : public ClangTidyCheck {
public:
FoldInitTypeCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
void doCheck(const BuiltinType &IterValueType, const BuiltinType &InitType,
const ASTContext &Context, const CallExpr &CallNode);
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FOLD_INIT_TYPE_H

View File

@@ -0,0 +1,174 @@
//===--- ForwardDeclarationNamespaceCheck.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 "ForwardDeclarationNamespaceCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include <stack>
#include <string>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
// Match all class declarations/definitions *EXCEPT*
// 1. implicit classes, e.g. `class A {};` has implicit `class A` inside `A`.
// 2. nested classes declared/defined inside another class.
// 3. template class declaration, template instantiation or
// specialization (NOTE: extern specialization is filtered out by
// `unless(hasAncestor(cxxRecordDecl()))`).
auto IsInSpecialization = hasAncestor(
decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
functionDecl(isExplicitTemplateSpecialization()))));
Finder->addMatcher(
cxxRecordDecl(
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
unless(isInstantiated()), unless(IsInSpecialization),
unless(classTemplateSpecializationDecl()))
.bind("record_decl"),
this);
// Match all friend declarations. Classes used in friend declarations are not
// marked as referenced in AST. We need to record all record classes used in
// friend declarations.
Finder->addMatcher(friendDecl().bind("friend_decl"), this);
}
void ForwardDeclarationNamespaceCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *RecordDecl =
Result.Nodes.getNodeAs<CXXRecordDecl>("record_decl")) {
StringRef DeclName = RecordDecl->getName();
if (RecordDecl->isThisDeclarationADefinition()) {
DeclNameToDefinitions[DeclName].push_back(RecordDecl);
} else {
// If a declaration has no definition, the definition could be in another
// namespace (a wrong namespace).
// NOTE: even a declaration does have definition, we still need it to
// compare with other declarations.
DeclNameToDeclarations[DeclName].push_back(RecordDecl);
}
} else {
const auto *Decl = Result.Nodes.getNodeAs<FriendDecl>("friend_decl");
assert(Decl && "Decl is neither record_decl nor friend decl!");
// Classes used in friend delarations are not marked referenced in AST,
// so we need to check classes used in friend declarations manually to
// reduce the rate of false positive.
// For example, in
// \code
// struct A;
// struct B { friend A; };
// \endcode
// `A` will not be marked as "referenced" in the AST.
if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
FriendTypes.insert(Desugared.getTypePtr());
}
}
}
static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1,
const CXXRecordDecl *Decl2) {
const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
// Since we only matched declarations whose parent is Namespace or
// TranslationUnit declaration, the parent should be either a translation unit
// or namespace.
if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
return ParentDecl1 == ParentDecl2;
}
assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
"ParentDecl1 declaration must be a namespace");
assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
"ParentDecl2 declaration must be a namespace");
auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
}
static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
const auto *ParentDecl = Decl->getLexicalParent();
if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
return "(global)";
}
const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
std::string Ns;
llvm::raw_string_ostream OStream(Ns);
NsDecl->printQualifiedName(OStream);
OStream.flush();
return Ns.empty() ? "(global)" : Ns;
}
void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
// Iterate each group of declarations by name.
for (const auto &KeyValuePair : DeclNameToDeclarations) {
const auto &Declarations = KeyValuePair.second;
// If more than 1 declaration exists, we check if all are in the same
// namespace.
for (const auto *CurDecl : Declarations) {
if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
continue; // Skip forward declarations that are used/referenced.
}
if (FriendTypes.count(CurDecl->getTypeForDecl()) != 0) {
continue; // Skip forward declarations referenced as friend.
}
if (CurDecl->getLocation().isMacroID() ||
CurDecl->getLocation().isInvalid()) {
continue;
}
// Compare with all other declarations with the same name.
for (const auto *Decl : Declarations) {
if (Decl == CurDecl) {
continue; // Don't compare with self.
}
if (!CurDecl->hasDefinition() &&
!haveSameNamespaceOrTranslationUnit(CurDecl, Decl)) {
diag(CurDecl->getLocation(),
"declaration %0 is never referenced, but a declaration with "
"the same name found in another namespace '%1'")
<< CurDecl << getNameOfNamespace(Decl);
diag(Decl->getLocation(), "a declaration of %0 is found here",
DiagnosticIDs::Note)
<< Decl;
break; // FIXME: We only generate one warning for each declaration.
}
}
// Check if a definition in another namespace exists.
const auto DeclName = CurDecl->getName();
if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
continue; // No definition in this translation unit, we can skip it.
}
// Make a warning for each definition with the same name (in other
// namespaces).
const auto &Definitions = DeclNameToDefinitions[DeclName];
for (const auto *Def : Definitions) {
diag(CurDecl->getLocation(),
"no definition found for %0, but a definition with "
"the same name %1 found in another namespace '%2'")
<< CurDecl << Def << getNameOfNamespace(Def);
diag(Def->getLocation(), "a definition of %0 is found here",
DiagnosticIDs::Note)
<< Def;
}
}
}
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,59 @@
//===--- ForwardDeclarationNamespaceCheck.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_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H
#include "../ClangTidy.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <set>
#include <vector>
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks if an unused forward declaration is in a wrong namespace.
///
/// The check inspects all unused forward declarations and checks if there is
/// any declaration/definition with the same name, which could indicate
/// that the forward declaration is potentially in a wrong namespace.
///
/// \code
/// namespace na { struct A; }
/// namespace nb { struct A {} };
/// nb::A a;
/// // warning : no definition found for 'A', but a definition with the same
/// name 'A' found in another namespace 'nb::'
/// \endcode
///
/// This check can only generate warnings, but it can't suggest fixes at this
/// point.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-forward-declaration-namespace.html
class ForwardDeclarationNamespaceCheck : public ClangTidyCheck {
public:
ForwardDeclarationNamespaceCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void onEndOfTranslationUnit() override;
private:
llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDefinitions;
llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDeclarations;
llvm::SmallPtrSet<const Type *, 16> FriendTypes;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H

View File

@@ -0,0 +1,81 @@
//===--- InaccurateEraseCheck.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 "InaccurateEraseCheck.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 bugprone {
namespace {
AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
}
void InaccurateEraseCheck::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;
const auto EndCall =
callExpr(
callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))),
hasArgument(
1,
anyOf(cxxConstructExpr(has(ignoringImplicit(
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
.bind("end")))),
anything())))
.bind("alg");
const auto DeclInStd = type(hasUnqualifiedDesugaredType(
tagType(hasDeclaration(decl(isInStdNamespace())))));
Finder->addMatcher(
cxxMemberCallExpr(
on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))),
callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
hasArgument(0, has(ignoringImplicit(
anyOf(EndCall, has(ignoringImplicit(EndCall)))))),
unless(isInTemplateInstantiation()))
.bind("erase"),
this);
}
void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MemberCall =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
const auto *EndExpr =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("end");
const SourceLocation Loc = MemberCall->getLocStart();
FixItHint Hint;
if (!Loc.isMacroID() && EndExpr) {
const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("alg");
std::string ReplacementText = Lexer::getSourceText(
CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
*Result.SourceManager, getLangOpts());
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
AlgCall->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
Hint = FixItHint::CreateInsertion(EndLoc, ", " + ReplacementText);
}
diag(Loc, "this call will remove at most one item even when multiple items "
"should be removed")
<< Hint;
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,38 @@
//===--- InaccurateEraseCheck.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_BUGPRONE_INACCURATEERASECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INACCURATEERASECHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks for inaccurate use of the `erase()` method.
///
/// Algorithms like `remove()` do not actually remove any element from the
/// container but return an iterator to the first redundant element at the end
/// of the container. These redundant elements must be removed using the
/// `erase()` method. This check warns when not all of the elements will be
/// removed due to using an inappropriate overload.
class InaccurateEraseCheck : public ClangTidyCheck {
public:
InaccurateEraseCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INACCURATEERASECHECK_H

View File

@@ -0,0 +1,57 @@
//===--- IntegerDivisionCheck.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 "IntegerDivisionCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void IntegerDivisionCheck::registerMatchers(MatchFinder *Finder) {
const auto IntType = hasType(isInteger());
const auto BinaryOperators = binaryOperator(anyOf(
hasOperatorName("%"), hasOperatorName("<<"), hasOperatorName(">>"),
hasOperatorName("<<"), hasOperatorName("^"), hasOperatorName("|"),
hasOperatorName("&"), hasOperatorName("||"), hasOperatorName("&&"),
hasOperatorName("<"), hasOperatorName(">"), hasOperatorName("<="),
hasOperatorName(">="), hasOperatorName("=="), hasOperatorName("!=")));
const auto UnaryOperators =
unaryOperator(anyOf(hasOperatorName("~"), hasOperatorName("!")));
const auto Exceptions =
anyOf(BinaryOperators, conditionalOperator(), binaryConditionalOperator(),
callExpr(IntType), explicitCastExpr(IntType), UnaryOperators);
Finder->addMatcher(
binaryOperator(
hasOperatorName("/"), hasLHS(expr(IntType)), hasRHS(expr(IntType)),
hasAncestor(
castExpr(hasCastKind(CK_IntegralToFloating)).bind("FloatCast")),
unless(hasAncestor(
expr(Exceptions,
hasAncestor(castExpr(equalsBoundNode("FloatCast")))))))
.bind("IntDiv"),
this);
}
void IntegerDivisionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *IntDiv = Result.Nodes.getNodeAs<BinaryOperator>("IntDiv");
diag(IntDiv->getLocStart(), "result of integer division used in a floating "
"point context; possible loss of precision");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,36 @@
//===--- IntegerDivisionCheck.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_BUGPRONE_INTEGER_DIVISION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INTEGER_DIVISION_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds cases where integer division in a floating point context is likely to
/// cause unintended loss of precision.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-integer-division.html
class IntegerDivisionCheck : public ClangTidyCheck {
public:
IntegerDivisionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INTEGER_DIVISION_H

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