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,45 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyMiscModule
ForwardingReferenceOverloadCheck.cpp
LambdaFunctionNameCheck.cpp
MisplacedConstCheck.cpp
UnconventionalAssignOperatorCheck.cpp
DefinitionsInHeadersCheck.cpp
IncorrectRoundings.cpp
MacroParenthesesCheck.cpp
MacroRepeatedSideEffectsCheck.cpp
MiscTidyModule.cpp
MisplacedWideningCastCheck.cpp
NewDeleteOverloadsCheck.cpp
NonCopyableObjects.cpp
RedundantExpressionCheck.cpp
SizeofContainerCheck.cpp
SizeofExpressionCheck.cpp
StaticAssertCheck.cpp
StringCompareCheck.cpp
StringIntegerAssignmentCheck.cpp
StringLiteralWithEmbeddedNulCheck.cpp
SuspiciousEnumUsageCheck.cpp
SuspiciousMissingCommaCheck.cpp
SuspiciousSemicolonCheck.cpp
SuspiciousStringCompareCheck.cpp
SwappedArgumentsCheck.cpp
ThrowByValueCatchByReferenceCheck.cpp
UndelegatedConstructor.cpp
UniqueptrResetReleaseCheck.cpp
UnusedAliasDeclsCheck.cpp
UnusedParametersCheck.cpp
UnusedRAIICheck.cpp
UnusedUsingDeclsCheck.cpp
LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
clangTooling
)

View File

@ -0,0 +1,158 @@
//===--- DefinitionsInHeadersCheck.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 "DefinitionsInHeadersCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
utils::HeaderFileExtensionsSet, HeaderFileExtensions) {
return utils::isExpansionLocInHeaderFile(
Node.getLocStart(), Finder->getASTContext().getSourceManager(),
HeaderFileExtensions);
}
} // namespace
DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
"HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions, ',')) {
// FIXME: Find a more suitable way to handle invalid configuration
// options.
llvm::errs() << "Invalid header file extension: "
<< RawStringHeaderFileExtensions << "\n";
}
}
void DefinitionsInHeadersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
auto DefinitionMatcher =
anyOf(functionDecl(isDefinition(), unless(isDeleted())),
varDecl(isDefinition()));
if (UseHeaderFileExtension) {
Finder->addMatcher(namedDecl(DefinitionMatcher,
usesHeaderFileExtension(HeaderFileExtensions))
.bind("name-decl"),
this);
} else {
Finder->addMatcher(
namedDecl(DefinitionMatcher,
anyOf(usesHeaderFileExtension(HeaderFileExtensions),
unless(isExpansionInMainFile())))
.bind("name-decl"),
this);
}
}
void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
// Don't run the check in failing TUs.
if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
return;
// C++ [basic.def.odr] p6:
// There can be more than one definition of a class type, enumeration type,
// inline function with external linkage, class template, non-static function
// template, static data member of a class template, member function of a
// class template, or template specialization for which some template
// parameters are not specifiedin a program provided that each definition
// appears in a different translation unit, and provided the definitions
// satisfy the following requirements.
const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
assert(ND);
if (ND->isInvalidDecl())
return;
// Internal linkage variable definitions are ignored for now:
// const int a = 1;
// static int b = 1;
//
// Although these might also cause ODR violations, we can be less certain and
// should try to keep the false-positive rate down.
//
// FIXME: Should declarations in anonymous namespaces get the same treatment
// as static / const declarations?
if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
return;
if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
// Inline functions are allowed.
if (FD->isInlined())
return;
// Function templates are allowed.
if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
return;
// Ignore instantiated functions.
if (FD->isTemplateInstantiation())
return;
// Member function of a class template and member function of a nested class
// in a class template are allowed.
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
const auto *DC = MD->getDeclContext();
while (DC->isRecord()) {
if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
if (isa<ClassTemplatePartialSpecializationDecl>(RD))
return;
if (RD->getDescribedClassTemplate())
return;
}
DC = DC->getParent();
}
}
bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
diag(FD->getLocation(),
"%select{function|full function template specialization}0 %1 defined "
"in a header file; function definitions in header files can lead to "
"ODR violations")
<< is_full_spec << FD << FixItHint::CreateInsertion(
FD->getReturnTypeSourceRange().getBegin(), "inline ");
} else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
// Static data members of a class template are allowed.
if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
return;
// Ignore instantiated static data members of classes.
if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
return;
// Ignore variable definition within function scope.
if (VD->hasLocalStorage() || VD->isStaticLocal())
return;
// Ignore inline variables.
if (VD->isInline())
return;
diag(VD->getLocation(),
"variable %0 defined in a header file; "
"variable definitions in header files can lead to ODR violations")
<< VD;
}
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,51 @@
//===--- DefinitionsInHeadersCheck.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_MISC_DEFINITIONS_IN_HEADERS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
#include "../ClangTidy.h"
#include "../utils/HeaderFileExtensionsUtils.h"
namespace clang {
namespace tidy {
namespace misc {
/// Finds non-extern non-inline function and variable definitions in header
/// files, which can lead to potential ODR violations.
///
/// The check supports these options:
/// - `UseHeaderFileExtension`: Whether to use file extension to distinguish
/// header files. True by default.
/// - `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.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-definitions-in-headers.html
class DefinitionsInHeadersCheck : public ClangTidyCheck {
public:
DefinitionsInHeadersCheck(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 UseHeaderFileExtension;
const std::string RawStringHeaderFileExtensions;
utils::HeaderFileExtensionsSet HeaderFileExtensions;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H

View File

@ -0,0 +1,148 @@
//===--- ForwardingReferenceOverloadCheck.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 "ForwardingReferenceOverloadCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <algorithm>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
// Check if the given type is related to std::enable_if.
AST_MATCHER(QualType, isEnableIf) {
auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
return false;
}
const NamedDecl *TypeDecl =
Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
return TypeDecl->isInStdNamespace() &&
(TypeDecl->getName().equals("enable_if") ||
TypeDecl->getName().equals("enable_if_t"));
};
const Type *BaseType = Node.getTypePtr();
// Case: pointer or reference to enable_if.
while (BaseType->isPointerType() || BaseType->isReferenceType()) {
BaseType = BaseType->getPointeeType().getTypePtr();
}
// Case: type parameter dependent (enable_if<is_integral<T>>).
if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
BaseType = Dependent->getQualifier()->getAsType();
}
if (!BaseType)
return false;
if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
return true; // Case: enable_if_t< >.
} else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
return true; // Case: enable_if< >::type.
}
}
}
return false;
}
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
return Node.hasDefaultArgument() &&
TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
}
} // namespace
void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
// Forwarding references require C++11 or later.
if (!getLangOpts().CPlusPlus11)
return;
auto ForwardingRefParm =
parmVarDecl(
hasType(qualType(rValueReferenceType(),
references(templateTypeParmType(hasDeclaration(
templateTypeParmDecl().bind("type-parm-decl")))),
unless(references(isConstQualified())))))
.bind("parm-var");
DeclarationMatcher findOverload =
cxxConstructorDecl(
hasParameter(0, ForwardingRefParm),
unless(hasAnyParameter(
// No warning: enable_if as constructor parameter.
parmVarDecl(hasType(isEnableIf())))),
unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
// No warning: enable_if as type parameter.
hasDefaultArgument(isEnableIf())))))))
.bind("ctor");
Finder->addMatcher(findOverload, this);
}
void ForwardingReferenceOverloadCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
const auto *TypeParmDecl =
Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
// Get the FunctionDecl and FunctionTemplateDecl containing the function
// parameter.
const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
if (!FuncForParam)
return;
const FunctionTemplateDecl *FuncTemplate =
FuncForParam->getDescribedFunctionTemplate();
if (!FuncTemplate)
return;
// Check that the template type parameter belongs to the same function
// template as the function parameter of that type. (This implies that type
// deduction will happen on the type.)
const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
return;
// Every parameter after the first must have a default value.
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
if (!(*Iter)->hasDefaultArg())
return;
}
bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
DisabledMove = false;
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
if (OtherCtor->isCopyOrMoveConstructor()) {
if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
(OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
else
(OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
}
}
bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
bool Move = !DisabledMove || EnabledMove;
if (!Copy && !Move)
return;
diag(Ctor->getLocation(),
"constructor accepting a forwarding reference can "
"hide the %select{copy|move|copy and move}0 constructor%s1")
<< (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
OtherCtor->getAccess() != AS_private) {
diag(OtherCtor->getLocation(),
"%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
<< OtherCtor->isMoveConstructor();
}
}
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,42 @@
//===--- ForwardingReferenceOverloadCheck.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_MISC_FORWARDING_REFERENCE_OVERLOAD_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// The checker looks for constructors that can act as copy or move constructors
/// through their forwarding reference parameters. If a non const lvalue
/// reference is passed to the constructor, the forwarding reference parameter
/// can be a perfect match while the const reference parameter of the copy
/// constructor can't. The forwarding reference constructor will be called,
/// which can lead to confusion.
/// For detailed description of this problem see: Scott Meyers, Effective Modern
/// C++ Design, item 26.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html
class ForwardingReferenceOverloadCheck : public ClangTidyCheck {
public:
ForwardingReferenceOverloadCheck(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_MISC_FORWARDING_REFERENCE_OVERLOAD_H

View File

@ -0,0 +1,71 @@
//===--- IncorrectRoundings.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 "IncorrectRoundings.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
AST_MATCHER(FloatingLiteral, floatHalf) {
const auto &literal = Node.getValue();
if ((&Node.getSemantics()) == &llvm::APFloat::IEEEsingle())
return literal.convertToFloat() == 0.5f;
if ((&Node.getSemantics()) == &llvm::APFloat::IEEEdouble())
return literal.convertToDouble() == 0.5;
return false;
}
} // namespace
void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) {
// Match a floating literal with value 0.5.
auto FloatHalf = floatLiteral(floatHalf());
// Match a floating point expression.
auto FloatType = expr(hasType(realFloatingPointType()));
// Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
// cast to floating type.
auto FloatOrCastHalf =
anyOf(FloatHalf,
implicitCastExpr(FloatType, has(ignoringParenImpCasts(FloatHalf))));
// Match if either the LHS or RHS is a floating literal of 0.5 or a floating
// literal of 0.5 and the other is of type double or vice versa.
auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));
// Find expressions of cast to int of the sum of a floating point expression
// and 0.5.
MatchFinder->addMatcher(
implicitCastExpr(
hasImplicitDestinationType(isInteger()),
ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
.bind("CastExpr"),
this);
}
void IncorrectRoundings::check(const MatchFinder::MatchResult &Result) {
const auto *CastExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("CastExpr");
diag(CastExpr->getLocStart(),
"casting (double + 0.5) to integer leads to incorrect rounding; "
"consider using lround (#include <cmath>) instead");
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,39 @@
//===--- IncorrectRoundings.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_MISC_INCORRECTROUNDINGS_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// \brief Checks the usage of patterns known to produce incorrect rounding.
/// Programmers often use
/// (int)(double_expression + 0.5)
/// to round the double expression to an integer. The problem with this
/// 1. It is unnecessarily slow.
/// 2. It is incorrect. The number 0.499999975 (smallest representable float
/// number below 0.5) rounds to 1.0. Even worse behavior for negative
/// numbers where both -0.5f and -1.4f both round to 0.0.
class IncorrectRoundings : public ClangTidyCheck {
public:
IncorrectRoundings(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_MISC_INCORRECTROUNDINGS_H_

View File

@ -0,0 +1,99 @@
//===--- LambdaFunctionNameCheck.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 "LambdaFunctionNameCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
// Keep track of macro expansions that contain both __FILE__ and __LINE__. If
// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
// warning because __FILE__ and __LINE__ may be useful even if __func__ or
// __FUNCTION__ is not, especially if the macro could be used in the context of
// either a function body or a lambda body.
class MacroExpansionsWithFileAndLine : public PPCallbacks {
public:
explicit MacroExpansionsWithFileAndLine(
LambdaFunctionNameCheck::SourceRangeSet *SME)
: SuppressMacroExpansions(SME) {}
void MacroExpands(const Token &MacroNameTok,
const MacroDefinition &MD, SourceRange Range,
const MacroArgs *Args) override {
bool has_file = false;
bool has_line = false;
for (const auto& T : MD.getMacroInfo()->tokens()) {
if (T.is(tok::identifier)) {
StringRef IdentName = T.getIdentifierInfo()->getName();
if (IdentName == "__FILE__") {
has_file = true;
} else if (IdentName == "__LINE__") {
has_line = true;
}
}
}
if (has_file && has_line) {
SuppressMacroExpansions->insert(Range);
}
}
private:
LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
};
} // namespace
void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
// Match on PredefinedExprs inside a lambda.
Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
this);
}
void LambdaFunctionNameCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Compiler.getPreprocessor().addPPCallbacks(
llvm::make_unique<MacroExpansionsWithFileAndLine>(
&SuppressMacroExpansions));
}
void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {
const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");
if (E->getIdentType() != PredefinedExpr::Func &&
E->getIdentType() != PredefinedExpr::Function) {
// We don't care about other PredefinedExprs.
return;
}
if (E->getLocation().isMacroID()) {
auto ER =
Result.SourceManager->getImmediateExpansionRange(E->getLocation());
if (SuppressMacroExpansions.find(SourceRange(ER.first, ER.second)) !=
SuppressMacroExpansions.end()) {
// This is a macro expansion for which we should not warn.
return;
}
}
diag(E->getLocation(),
"inside a lambda, '%0' expands to the name of the function call "
"operator; consider capturing the name of the enclosing function "
"explicitly")
<< PredefinedExpr::getIdentTypeName(E->getIdentType());
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,51 @@
//===--- LambdaFunctionNameCheck.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_MISC_LAMBDA_FUNCTION_NAME_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// Detect when __func__ or __FUNCTION__ is being used from within a lambda. In
/// that context, those expressions expand to the name of the call operator
/// (i.e., `operator()`).
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html
class LambdaFunctionNameCheck : public ClangTidyCheck {
public:
struct SourceRangeLessThan {
bool operator()(const SourceRange &L, const SourceRange &R) const {
if (L.getBegin() == R.getBegin()) {
return L.getEnd() < R.getEnd();
}
return L.getBegin() < R.getBegin();
}
};
using SourceRangeSet = std::set<SourceRange, SourceRangeLessThan>;
LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void registerPPCallbacks(CompilerInstance &Compiler) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
SourceRangeSet SuppressMacroExpansions;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H

View File

@ -0,0 +1,260 @@
//===--- MacroParenthesesCheck.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 "MacroParenthesesCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
namespace clang {
namespace tidy {
namespace misc {
namespace {
class MacroParenthesesPPCallbacks : public PPCallbacks {
public:
MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check)
: PP(PP), Check(Check) {}
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override {
replacementList(MacroNameTok, MD->getMacroInfo());
argument(MacroNameTok, MD->getMacroInfo());
}
private:
/// Replacement list with calculations should be enclosed in parentheses.
void replacementList(const Token &MacroNameTok, const MacroInfo *MI);
/// Arguments should be enclosed in parentheses.
void argument(const Token &MacroNameTok, const MacroInfo *MI);
Preprocessor *PP;
MacroParenthesesCheck *Check;
};
} // namespace
/// Is argument surrounded properly with parentheses/braces/squares/commas?
static bool isSurroundedLeft(const Token &T) {
return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
tok::semi);
}
/// Is argument surrounded properly with parentheses/braces/squares/commas?
static bool isSurroundedRight(const Token &T) {
return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
tok::semi);
}
/// Is given TokenKind a keyword?
static bool isKeyword(const Token &T) {
// FIXME: better matching of keywords to avoid false positives.
return T.isOneOf(tok::kw_case, tok::kw_const, tok::kw_struct);
}
/// Warning is written when one of these operators are not within parentheses.
static bool isWarnOp(const Token &T) {
// FIXME: This is an initial list of operators. It can be tweaked later to
// get more positives or perhaps avoid some false positive.
return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
tok::amp, tok::pipe, tok::caret);
}
/// Is given Token a keyword that is used in variable declarations?
static bool isVarDeclKeyword(const Token &T) {
return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int,
tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const,
tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct,
tok::kw_signed, tok::kw_unsigned);
}
/// Is there a possible variable declaration at Tok?
static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok) {
if (Tok == MI->tokens_end())
return false;
// If we see int/short/struct/etc., just assume this is a variable
// declaration.
if (isVarDeclKeyword(*Tok))
return true;
// Variable declarations start with identifier or coloncolon.
if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
return false;
// Skip possible types, etc
while (Tok != MI->tokens_end() &&
Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
tok::star, tok::amp, tok::ampamp, tok::less,
tok::greater))
Tok++;
// Return true for possible variable declarations.
return Tok == MI->tokens_end() ||
Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
isVarDeclKeyword(*Tok);
}
void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok,
const MacroInfo *MI) {
// Make sure macro replacement isn't a variable declaration.
if (possibleVarDecl(MI, MI->tokens_begin()))
return;
// Count how deep we are in parentheses/braces/squares.
int Count = 0;
// SourceLocation for error
SourceLocation Loc;
for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
const Token &Tok = *TI;
// Replacement list contains keywords, don't warn about it.
if (isKeyword(Tok))
return;
// When replacement list contains comma/semi don't warn about it.
if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
return;
if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
++Count;
} else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
--Count;
// If there are unbalanced parentheses don't write any warning
if (Count < 0)
return;
} else if (Count == 0 && isWarnOp(Tok)) {
// Heuristic for macros that are clearly not intended to be enclosed in
// parentheses, macro starts with operator. For example:
// #define X *10
if (TI == MI->tokens_begin() && (TI + 1) != TE &&
!Tok.isOneOf(tok::plus, tok::minus))
return;
// Don't warn about this macro if the last token is a star. For example:
// #define X void *
if ((TE - 1)->is(tok::star))
return;
Loc = Tok.getLocation();
}
}
if (Loc.isValid()) {
const Token &Last = *(MI->tokens_end() - 1);
Check->diag(Loc, "macro replacement list should be enclosed in parentheses")
<< FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(), "(")
<< FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
PP->getSpelling(Last).length()),
")");
}
}
void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
const MacroInfo *MI) {
// Skip variable declaration.
bool VarDecl = possibleVarDecl(MI, MI->tokens_begin());
for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
// First token.
if (TI == MI->tokens_begin())
continue;
// Last token.
if ((TI + 1) == MI->tokens_end())
continue;
const Token &Prev = *(TI - 1);
const Token &Next = *(TI + 1);
const Token &Tok = *TI;
// There should not be extra parentheses in possible variable declaration.
if (VarDecl) {
if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
VarDecl = false;
continue;
}
// Only interested in identifiers.
if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
continue;
// Only interested in macro arguments.
if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
continue;
// Argument is surrounded with parentheses/squares/braces/commas.
if (isSurroundedLeft(Prev) && isSurroundedRight(Next))
continue;
// Don't warn after hash/hashhash or before hashhash.
if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
continue;
// Argument is a struct member.
if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
tok::periodstar))
continue;
// Argument is a namespace or class.
if (Next.is(tok::coloncolon))
continue;
// String concatenation.
if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
continue;
// Type/Var.
if (isAnyIdentifier(Prev.getKind()) || isKeyword(Prev) ||
isAnyIdentifier(Next.getKind()) || isKeyword(Next))
continue;
// Initialization.
if (Next.is(tok::l_paren))
continue;
// Cast.
if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
continue;
// Assignment/return, i.e. '=x;' or 'return x;'.
if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
continue;
// C++ template parameters.
if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
Next.isOneOf(tok::comma, tok::greater))
continue;
// Namespaces.
if (Prev.is(tok::kw_namespace))
continue;
// Variadic templates
if (MI->isVariadic())
continue;
Check->diag(Tok.getLocation(), "macro argument should be enclosed in "
"parentheses")
<< FixItHint::CreateInsertion(Tok.getLocation(), "(")
<< FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
PP->getSpelling(Tok).length()),
")");
}
}
void MacroParenthesesCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Compiler.getPreprocessor().addPPCallbacks(
llvm::make_unique<MacroParenthesesPPCallbacks>(
&Compiler.getPreprocessor(), this));
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,43 @@
//===--- MacroParenthesesCheck.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_MISC_MACRO_PARENTHESES_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// Finds macros that can have unexpected behaviour due to missing parentheses.
///
/// Macros are expanded by the preprocessor as-is. As a result, there can be
/// unexpected behaviour; operators may be evaluated in unexpected order and
/// unary operators may become binary operators, etc.
///
/// When the replacement list has an expression, it is recommended to surround
/// it with parentheses. This ensures that the macro result is evaluated
/// completely before it is used.
///
/// It is also recommended to surround macro arguments in the replacement list
/// with parentheses. This ensures that the argument value is calculated
/// properly.
class MacroParenthesesCheck : public ClangTidyCheck {
public:
MacroParenthesesCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H

View File

@ -0,0 +1,184 @@
//===--- MacroRepeatedSideEffectsCheck.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 "MacroRepeatedSideEffectsCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
namespace clang {
namespace tidy {
namespace misc {
namespace {
class MacroRepeatedPPCallbacks : public PPCallbacks {
public:
MacroRepeatedPPCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
: Check(Check), PP(PP) {}
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range, const MacroArgs *Args) override;
private:
ClangTidyCheck &Check;
Preprocessor &PP;
unsigned countArgumentExpansions(const MacroInfo *MI,
const IdentifierInfo *Arg) const;
bool hasSideEffects(const Token *ResultArgToks) const;
};
} // End of anonymous namespace.
void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,
const MacroDefinition &MD,
SourceRange Range,
const MacroArgs *Args) {
// Ignore macro argument expansions.
if (!Range.getBegin().isFileID())
return;
const MacroInfo *MI = MD.getMacroInfo();
// Bail out if the contents of the macro are containing keywords that are
// making the macro too complex.
if (std::find_if(
MI->tokens().begin(), MI->tokens().end(), [](const Token &T) {
return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch,
tok::kw_case, tok::kw_break, tok::kw_while,
tok::kw_do, tok::kw_for, tok::kw_continue,
tok::kw_goto, tok::kw_return);
}) != MI->tokens().end())
return;
for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
if (hasSideEffects(ResultArgToks) &&
countArgumentExpansions(MI, Arg) >= 2) {
Check.diag(ResultArgToks->getLocation(),
"side effects in the %ordinal0 macro argument %1 are "
"repeated in macro expansion")
<< (ArgNo + 1) << Arg;
Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",
DiagnosticIDs::Note)
<< MacroNameTok.getIdentifierInfo();
}
}
}
unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
const MacroInfo *MI, const IdentifierInfo *Arg) const {
// Current argument count. When moving forward to a different control-flow
// path this can decrease.
unsigned Current = 0;
// Max argument count.
unsigned Max = 0;
bool SkipParen = false;
int SkipParenCount = 0;
// Has a __builtin_constant_p been found?
bool FoundBuiltin = false;
bool PrevTokenIsHash = false;
// Count when "?" is reached. The "Current" will get this value when the ":"
// is reached.
std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
for (const auto &T : MI->tokens()) {
// The result of __builtin_constant_p(x) is 0 if x is a macro argument
// with side effects. If we see a __builtin_constant_p(x) followed by a
// "?" "&&" or "||", then we need to reason about control flow to report
// warnings correctly. Until such reasoning is added, bail out when this
// happens.
if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
return Max;
// Skip stringified tokens.
if (T.is(tok::hash)) {
PrevTokenIsHash = true;
continue;
}
if (PrevTokenIsHash) {
PrevTokenIsHash = false;
continue;
}
// Handling of ? and :.
if (T.is(tok::question)) {
CountAtQuestion.push(Current);
} else if (T.is(tok::colon)) {
if (CountAtQuestion.empty())
return 0;
Current = CountAtQuestion.top();
CountAtQuestion.pop();
}
// If current token is a parenthesis, skip it.
if (SkipParen) {
if (T.is(tok::l_paren))
SkipParenCount++;
else if (T.is(tok::r_paren))
SkipParenCount--;
SkipParen = (SkipParenCount != 0);
if (SkipParen)
continue;
}
IdentifierInfo *TII = T.getIdentifierInfo();
// If not existent, skip it.
if (TII == nullptr)
continue;
// If a __builtin_constant_p is found within the macro definition, don't
// count arguments inside the parentheses and remember that it has been
// seen in case there are "?", "&&" or "||" operators later.
if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
FoundBuiltin = true;
SkipParen = true;
continue;
}
// If another macro is found within the macro definition, skip the macro
// and the eventual arguments.
if (TII->hasMacroDefinition()) {
const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();
if (M != nullptr && M->isFunctionLike())
SkipParen = true;
continue;
}
// Count argument.
if (TII == Arg) {
Current++;
if (Current > Max)
Max = Current;
}
}
return Max;
}
bool MacroRepeatedPPCallbacks::hasSideEffects(
const Token *ResultArgToks) const {
for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
return true;
}
return false;
}
void MacroRepeatedSideEffectsCheck::registerPPCallbacks(
CompilerInstance &Compiler) {
Compiler.getPreprocessor().addPPCallbacks(
::llvm::make_unique<MacroRepeatedPPCallbacks>(
*this, Compiler.getPreprocessor()));
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,31 @@
//===--- MacroRepeatedSideEffectsCheck.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_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// Checks for repeated argument with side effects in macros.
class MacroRepeatedSideEffectsCheck : public ClangTidyCheck {
public:
MacroRepeatedSideEffectsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H

View File

@ -0,0 +1,120 @@
//===--- MiscTidyModule.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 "DefinitionsInHeadersCheck.h"
#include "ForwardingReferenceOverloadCheck.h"
#include "IncorrectRoundings.h"
#include "LambdaFunctionNameCheck.h"
#include "MacroParenthesesCheck.h"
#include "MacroRepeatedSideEffectsCheck.h"
#include "MisplacedConstCheck.h"
#include "MisplacedWideningCastCheck.h"
#include "NewDeleteOverloadsCheck.h"
#include "NonCopyableObjects.h"
#include "RedundantExpressionCheck.h"
#include "SizeofContainerCheck.h"
#include "SizeofExpressionCheck.h"
#include "StaticAssertCheck.h"
#include "StringCompareCheck.h"
#include "StringIntegerAssignmentCheck.h"
#include "StringLiteralWithEmbeddedNulCheck.h"
#include "SuspiciousEnumUsageCheck.h"
#include "SuspiciousMissingCommaCheck.h"
#include "SuspiciousSemicolonCheck.h"
#include "SuspiciousStringCompareCheck.h"
#include "SwappedArgumentsCheck.h"
#include "ThrowByValueCatchByReferenceCheck.h"
#include "UnconventionalAssignOperatorCheck.h"
#include "UndelegatedConstructor.h"
#include "UniqueptrResetReleaseCheck.h"
#include "UnusedAliasDeclsCheck.h"
#include "UnusedParametersCheck.h"
#include "UnusedRAIICheck.h"
#include "UnusedUsingDeclsCheck.h"
namespace clang {
namespace tidy {
namespace misc {
class MiscModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
"misc-forwarding-reference-overload");
CheckFactories.registerCheck<LambdaFunctionNameCheck>(
"misc-lambda-function-name");
CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
"misc-unconventional-assign-operator");
CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
"misc-definitions-in-headers");
CheckFactories.registerCheck<IncorrectRoundings>(
"misc-incorrect-roundings");
CheckFactories.registerCheck<MacroParenthesesCheck>(
"misc-macro-parentheses");
CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
"misc-macro-repeated-side-effects");
CheckFactories.registerCheck<MisplacedWideningCastCheck>(
"misc-misplaced-widening-cast");
CheckFactories.registerCheck<NewDeleteOverloadsCheck>(
"misc-new-delete-overloads");
CheckFactories.registerCheck<NonCopyableObjectsCheck>(
"misc-non-copyable-objects");
CheckFactories.registerCheck<RedundantExpressionCheck>(
"misc-redundant-expression");
CheckFactories.registerCheck<SizeofContainerCheck>("misc-sizeof-container");
CheckFactories.registerCheck<SizeofExpressionCheck>(
"misc-sizeof-expression");
CheckFactories.registerCheck<StaticAssertCheck>("misc-static-assert");
CheckFactories.registerCheck<StringCompareCheck>("misc-string-compare");
CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
"misc-string-integer-assignment");
CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
"misc-string-literal-with-embedded-nul");
CheckFactories.registerCheck<SuspiciousEnumUsageCheck>(
"misc-suspicious-enum-usage");
CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
"misc-suspicious-missing-comma");
CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
"misc-suspicious-semicolon");
CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
"misc-suspicious-string-compare");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"misc-swapped-arguments");
CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
"misc-throw-by-value-catch-by-reference");
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
"misc-undelegated-constructor");
CheckFactories.registerCheck<UniqueptrResetReleaseCheck>(
"misc-uniqueptr-reset-release");
CheckFactories.registerCheck<UnusedAliasDeclsCheck>(
"misc-unused-alias-decls");
CheckFactories.registerCheck<UnusedParametersCheck>(
"misc-unused-parameters");
CheckFactories.registerCheck<UnusedRAIICheck>("misc-unused-raii");
CheckFactories.registerCheck<UnusedUsingDeclsCheck>(
"misc-unused-using-decls");
}
};
} // namespace misc
// Register the MiscTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<misc::MiscModule>
X("misc-module", "Adds miscellaneous lint checks.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the MiscModule.
volatile int MiscModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,63 @@
//===--- MisplacedConstCheck.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 "MisplacedConstCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
valueDecl(hasType(isConstQualified()),
hasType(typedefType(hasDeclaration(
typedefDecl(hasType(pointerType(unless(pointee(
anyOf(isConstQualified(),
ignoringParens(functionType())))))))
.bind("typedef")))))
.bind("decl"),
this);
}
static QualType guessAlternateQualification(ASTContext &Context, QualType QT) {
// We're given a QualType from a typedef where the qualifiers apply to the
// pointer instead of the pointee. Strip the const qualifier from the pointer
// type and add it to the pointee instead.
if (!QT->isPointerType())
return QT;
Qualifiers Quals = QT.getLocalQualifiers();
Quals.removeConst();
QualType NewQT = Context.getPointerType(
QualType(QT->getPointeeType().getTypePtr(), Qualifiers::Const));
return NewQT.withCVRQualifiers(Quals.getCVRQualifiers());
}
void MisplacedConstCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Var = Result.Nodes.getNodeAs<ValueDecl>("decl");
const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
ASTContext &Ctx = *Result.Context;
QualType CanQT = Var->getType().getCanonicalType();
diag(Var->getLocation(), "%0 declared with a const-qualified typedef type; "
"results in the type being '%1' instead of '%2'")
<< Var << CanQT.getAsString(Ctx.getPrintingPolicy())
<< guessAlternateQualification(Ctx, CanQT)
.getAsString(Ctx.getPrintingPolicy());
diag(Typedef->getLocation(), "typedef declared here", DiagnosticIDs::Note);
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,36 @@
//===--- MisplacedConstCheck.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_MISC_MISPLACED_CONST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// This check diagnoses when a const qualifier is applied to a typedef to a
/// pointer type rather than to the pointee.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-const.html
class MisplacedConstCheck : public ClangTidyCheck {
public:
MisplacedConstCheck(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_MISC_MISPLACED_CONST_H

View File

@ -0,0 +1,233 @@
//===--- MisplacedWideningCastCheck.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 "MisplacedWideningCastCheck.h"
#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
MisplacedWideningCastCheck::MisplacedWideningCastCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
void MisplacedWideningCastCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
}
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
const auto Calc =
expr(anyOf(binaryOperator(
anyOf(hasOperatorName("+"), hasOperatorName("-"),
hasOperatorName("*"), hasOperatorName("<<"))),
unaryOperator(hasOperatorName("~"))),
hasType(isInteger()))
.bind("Calc");
const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
has(ignoringParenImpCasts(Calc)));
const auto ImplicitCast =
implicitCastExpr(hasImplicitDestinationType(isInteger()),
has(ignoringParenImpCasts(Calc)));
const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
Finder->addMatcher(
binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
this);
}
static unsigned getMaxCalculationWidth(const ASTContext &Context,
const Expr *E) {
E = E->IgnoreParenImpCasts();
if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
if (Bop->getOpcode() == BO_Mul)
return LHSWidth + RHSWidth;
if (Bop->getOpcode() == BO_Add)
return std::max(LHSWidth, RHSWidth) + 1;
if (Bop->getOpcode() == BO_Rem) {
llvm::APSInt Val;
if (Bop->getRHS()->EvaluateAsInt(Val, Context))
return Val.getActiveBits();
} else if (Bop->getOpcode() == BO_Shl) {
llvm::APSInt Bits;
if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
// We don't handle negative values and large values well. It is assumed
// that compiler warnings are written for such values so the user will
// fix that.
return LHSWidth + Bits.getExtValue();
}
// Unknown bitcount, assume there is truncation.
return 1024U;
}
} else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
// There is truncation when ~ is used.
if (Uop->getOpcode() == UO_Not)
return 1024U;
QualType T = Uop->getType();
return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
} else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
return I->getValue().getActiveBits();
}
return Context.getIntWidth(E->getType());
}
static int relativeIntSizes(BuiltinType::Kind Kind) {
switch (Kind) {
case BuiltinType::UChar:
return 1;
case BuiltinType::SChar:
return 1;
case BuiltinType::Char_U:
return 1;
case BuiltinType::Char_S:
return 1;
case BuiltinType::UShort:
return 2;
case BuiltinType::Short:
return 2;
case BuiltinType::UInt:
return 3;
case BuiltinType::Int:
return 3;
case BuiltinType::ULong:
return 4;
case BuiltinType::Long:
return 4;
case BuiltinType::ULongLong:
return 5;
case BuiltinType::LongLong:
return 5;
case BuiltinType::UInt128:
return 6;
case BuiltinType::Int128:
return 6;
default:
return 0;
}
}
static int relativeCharSizes(BuiltinType::Kind Kind) {
switch (Kind) {
case BuiltinType::UChar:
return 1;
case BuiltinType::SChar:
return 1;
case BuiltinType::Char_U:
return 1;
case BuiltinType::Char_S:
return 1;
case BuiltinType::Char16:
return 2;
case BuiltinType::Char32:
return 3;
default:
return 0;
}
}
static int relativeCharSizesW(BuiltinType::Kind Kind) {
switch (Kind) {
case BuiltinType::UChar:
return 1;
case BuiltinType::SChar:
return 1;
case BuiltinType::Char_U:
return 1;
case BuiltinType::Char_S:
return 1;
case BuiltinType::WChar_U:
return 2;
case BuiltinType::WChar_S:
return 2;
default:
return 0;
}
}
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
int FirstSize, SecondSize;
if ((FirstSize = relativeIntSizes(First)) != 0 &&
(SecondSize = relativeIntSizes(Second)) != 0)
return FirstSize > SecondSize;
if ((FirstSize = relativeCharSizes(First)) != 0 &&
(SecondSize = relativeCharSizes(Second)) != 0)
return FirstSize > SecondSize;
if ((FirstSize = relativeCharSizesW(First)) != 0 &&
(SecondSize = relativeCharSizesW(Second)) != 0)
return FirstSize > SecondSize;
return false;
}
void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
return;
if (Cast->getLocStart().isMacroID())
return;
const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
if (Calc->getLocStart().isMacroID())
return;
if (Cast->isTypeDependent() || Cast->isValueDependent() ||
Calc->isTypeDependent() || Calc->isValueDependent())
return;
ASTContext &Context = *Result.Context;
QualType CastType = Cast->getType();
QualType CalcType = Calc->getType();
// Explicit truncation using cast.
if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
return;
// If CalcType and CastType have same size then there is no real danger, but
// there can be a portability problem.
if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
const auto *CastBuiltinType =
dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
const auto *CalcBuiltinType =
dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
if (CastBuiltinType && CalcBuiltinType &&
!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
return;
}
// Don't write a warning if we can easily see that the result is not
// truncated.
if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
return;
diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or "
"there is loss of precision before the conversion")
<< CalcType << CastType;
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,46 @@
//===--- MisplacedWideningCastCheck.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_MISC_MISPLACED_WIDENING_CAST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// Find casts of calculation results to bigger type. Typically from int to
/// long. If the intention of the cast is to avoid loss of precision then
/// the cast is misplaced, and there can be loss of precision. Otherwise
/// such cast is ineffective.
///
/// There is one option:
///
/// - `CheckImplicitCasts`: Whether to check implicit casts as well which may
// be the most common case. Enabled by default.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-widening-cast.html
class MisplacedWideningCastCheck : public ClangTidyCheck {
public:
MisplacedWideningCastCheck(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 CheckImplicitCasts;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif

View File

@ -0,0 +1,213 @@
//===--- NewDeleteOverloadsCheck.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 "NewDeleteOverloadsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
AST_MATCHER(FunctionDecl, isPlacementOverload) {
bool New;
switch (Node.getOverloadedOperator()) {
default:
return false;
case OO_New:
case OO_Array_New:
New = true;
break;
case OO_Delete:
case OO_Array_Delete:
New = false;
break;
}
// Variadic functions are always placement functions.
if (Node.isVariadic())
return true;
// Placement new is easy: it always has more than one parameter (the first
// parameter is always the size). If it's an overload of delete or delete[]
// that has only one parameter, it's never a placement delete.
if (New)
return Node.getNumParams() > 1;
if (Node.getNumParams() == 1)
return false;
// Placement delete is a little more challenging. They always have more than
// one parameter with the first parameter being a pointer. However, the
// second parameter can be a size_t for sized deallocation, and that is never
// a placement delete operator.
if (Node.getNumParams() <= 1 || Node.getNumParams() > 2)
return true;
const auto *FPT = Node.getType()->castAs<FunctionProtoType>();
ASTContext &Ctx = Node.getASTContext();
if (Ctx.getLangOpts().SizedDeallocation &&
Ctx.hasSameType(FPT->getParamType(1), Ctx.getSizeType()))
return false;
return true;
}
OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
switch (FD->getOverloadedOperator()) {
default:
break;
case OO_New:
return OO_Delete;
case OO_Delete:
return OO_New;
case OO_Array_New:
return OO_Array_Delete;
case OO_Array_Delete:
return OO_Array_New;
}
llvm_unreachable("Not an overloaded allocation operator");
}
const char *getOperatorName(OverloadedOperatorKind K) {
switch (K) {
default:
break;
case OO_New:
return "operator new";
case OO_Delete:
return "operator delete";
case OO_Array_New:
return "operator new[]";
case OO_Array_Delete:
return "operator delete[]";
}
llvm_unreachable("Not an overloaded allocation operator");
}
bool areCorrespondingOverloads(const FunctionDecl *LHS,
const FunctionDecl *RHS) {
return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
}
bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
const CXXRecordDecl *RD = nullptr) {
if (RD) {
// Check the methods in the given class and accessible to derived classes.
for (const auto *BMD : RD->methods())
if (BMD->isOverloadedOperator() && BMD->getAccess() != AS_private &&
areCorrespondingOverloads(MD, BMD))
return true;
} else {
// Get the parent class of the method; we do not need to care about checking
// the methods in this class as the caller has already done that by looking
// at the declaration contexts.
RD = MD->getParent();
}
for (const auto &BS : RD->bases()) {
// We can't say much about a dependent base class, but to avoid false
// positives assume it can have a corresponding overload.
if (BS.getType()->isDependentType())
return true;
if (const auto *BaseRD = BS.getType()->getAsCXXRecordDecl())
if (hasCorrespondingOverloadInBaseClass(MD, BaseRD))
return true;
}
return false;
}
} // anonymous namespace
void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Match all operator new and operator delete overloads (including the array
// forms). Do not match implicit operators, placement operators, or
// deleted/private operators.
//
// Technically, trivially-defined operator delete seems like a reasonable
// thing to also skip. e.g., void operator delete(void *) {}
// However, I think it's more reasonable to warn in this case as the user
// should really be writing that as a deleted function.
Finder->addMatcher(
functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(),
isDeleted(), cxxMethodDecl(isPrivate()))),
anyOf(hasOverloadedOperatorName("new"),
hasOverloadedOperatorName("new[]"),
hasOverloadedOperatorName("delete"),
hasOverloadedOperatorName("delete[]")))
.bind("func"),
this);
}
void NewDeleteOverloadsCheck::check(const MatchFinder::MatchResult &Result) {
// Add any matches we locate to the list of things to be checked at the
// end of the translation unit.
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
const CXXRecordDecl *RD = nullptr;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
RD = MD->getParent();
Overloads[RD].push_back(FD);
}
void NewDeleteOverloadsCheck::onEndOfTranslationUnit() {
// Walk over the list of declarations we've found to see if there is a
// corresponding overload at the same declaration context or within a base
// class. If there is not, add the element to the list of declarations to
// diagnose.
SmallVector<const FunctionDecl *, 4> Diagnose;
for (const auto &RP : Overloads) {
// We don't care about the CXXRecordDecl key in the map; we use it as a way
// to shard the overloads by declaration context to reduce the algorithmic
// complexity when searching for corresponding free store functions.
for (const auto *Overload : RP.second) {
const auto *Match =
std::find_if(RP.second.begin(), RP.second.end(),
[&Overload](const FunctionDecl *FD) {
if (FD == Overload)
return false;
// If the declaration contexts don't match, we don't
// need to check any further.
if (FD->getDeclContext() != Overload->getDeclContext())
return false;
// Since the declaration contexts match, see whether
// the current element is the corresponding operator.
if (!areCorrespondingOverloads(Overload, FD))
return false;
return true;
});
if (Match == RP.second.end()) {
// Check to see if there is a corresponding overload in a base class
// context. If there isn't, or if the overload is not a class member
// function, then we should diagnose.
const auto *MD = dyn_cast<CXXMethodDecl>(Overload);
if (!MD || !hasCorrespondingOverloadInBaseClass(MD))
Diagnose.push_back(Overload);
}
}
}
for (const auto *FD : Diagnose)
diag(FD->getLocation(), "declaration of %0 has no matching declaration "
"of '%1' at the same scope")
<< FD << getOperatorName(getCorrespondingOverload(FD));
}
} // namespace misc
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,38 @@
//===--- NewDeleteOverloadsCheck.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_MISC_NEWDELETEOVERLOADS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
#include "../ClangTidy.h"
#include "llvm/ADT/SmallVector.h"
#include <map>
namespace clang {
namespace tidy {
namespace misc {
class NewDeleteOverloadsCheck : public ClangTidyCheck {
std::map<const clang::CXXRecordDecl *,
llvm::SmallVector<const clang::FunctionDecl *, 4>>
Overloads;
public:
NewDeleteOverloadsCheck(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;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H

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