Bug 1410472 - clang-plugin follows the LLVM coding style for real r=mystor
MozReview-Commit-ID: AXrQEjWzxvg
This commit is contained in:
@@ -45,14 +45,16 @@ void ArithmeticArgChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArithmeticArgChecker::check(
|
void ArithmeticArgChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
const char *Error =
|
||||||
const char* Error = "cannot pass an arithmetic expression of built-in types to %0";
|
"cannot pass an arithmetic expression of built-in types to %0";
|
||||||
const Expr *Expression = Result.Nodes.getNodeAs<Expr>("node");
|
const Expr *Expression = Result.Nodes.getNodeAs<Expr>("node");
|
||||||
if (const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
|
if (const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
|
||||||
diag(Expression->getLocStart(), Error, DiagnosticIDs::Error) << Call->getDirectCallee();
|
diag(Expression->getLocStart(), Error, DiagnosticIDs::Error)
|
||||||
|
<< Call->getDirectCallee();
|
||||||
} else if (const CXXConstructExpr *Ctr =
|
} else if (const CXXConstructExpr *Ctr =
|
||||||
Result.Nodes.getNodeAs<CXXConstructExpr>("call")) {
|
Result.Nodes.getNodeAs<CXXConstructExpr>("call")) {
|
||||||
diag(Expression->getLocStart(), Error, DiagnosticIDs::Error) << Ctr->getConstructor();
|
diag(Expression->getLocStart(), Error, DiagnosticIDs::Error)
|
||||||
|
<< Ctr->getConstructor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class ArithmeticArgChecker : public BaseCheck {
|
class ArithmeticArgChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
ArithmeticArgChecker(StringRef CheckName,
|
ArithmeticArgChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -7,17 +7,14 @@
|
|||||||
|
|
||||||
void AssertAssignmentChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void AssertAssignmentChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
callExpr(isAssertAssignmentTestFunc()).bind("funcCall"),
|
callExpr(isAssertAssignmentTestFunc()).bind("funcCall"), this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertAssignmentChecker::check(
|
void AssertAssignmentChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
const CallExpr *FuncCall = Result.Nodes.getNodeAs<CallExpr>("funcCall");
|
const CallExpr *FuncCall = Result.Nodes.getNodeAs<CallExpr>("funcCall");
|
||||||
|
|
||||||
if (FuncCall && hasSideEffectAssignment(FuncCall)) {
|
if (FuncCall && hasSideEffectAssignment(FuncCall)) {
|
||||||
diag(FuncCall->getLocStart(),
|
diag(FuncCall->getLocStart(), "Forbidden assignment in assert expression",
|
||||||
"Forbidden assignment in assert expression",
|
|
||||||
DiagnosticIDs::Error);
|
DiagnosticIDs::Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class AssertAssignmentChecker : public BaseCheck {
|
class AssertAssignmentChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
AssertAssignmentChecker(StringRef CheckName,
|
AssertAssignmentChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -19,12 +19,10 @@ void CanRunScriptChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
// and which is not a parameter of the parent function,
|
// and which is not a parameter of the parent function,
|
||||||
unless(declRefExpr(to(parmVarDecl()))),
|
unless(declRefExpr(to(parmVarDecl()))),
|
||||||
// and which is not a MOZ_KnownLive wrapped value.
|
// and which is not a MOZ_KnownLive wrapped value.
|
||||||
unless(callExpr(callee(
|
unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))),
|
||||||
functionDecl(hasName("MOZ_KnownLive"))))),
|
|
||||||
expr().bind("invalidArg")));
|
expr().bind("invalidArg")));
|
||||||
|
|
||||||
auto OptionalInvalidExplicitArg =
|
auto OptionalInvalidExplicitArg = anyOf(
|
||||||
anyOf(
|
|
||||||
// We want to find any argument which is invalid.
|
// We want to find any argument which is invalid.
|
||||||
hasAnyArgument(InvalidArg),
|
hasAnyArgument(InvalidArg),
|
||||||
|
|
||||||
@@ -46,9 +44,7 @@ void CanRunScriptChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
anyOf(
|
anyOf(
|
||||||
// which derefs into an invalid arg,
|
// which derefs into an invalid arg,
|
||||||
on(cxxOperatorCallExpr(
|
on(cxxOperatorCallExpr(
|
||||||
anyOf(
|
anyOf(hasAnyArgument(InvalidArg), anything()))),
|
||||||
hasAnyArgument(InvalidArg),
|
|
||||||
anything()))),
|
|
||||||
// or is an invalid arg.
|
// or is an invalid arg.
|
||||||
on(InvalidArg),
|
on(InvalidArg),
|
||||||
|
|
||||||
@@ -57,13 +53,11 @@ void CanRunScriptChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
// or a regular call expression,
|
// or a regular call expression,
|
||||||
callExpr(
|
callExpr(
|
||||||
// which optionally has an invalid arg.
|
// which optionally has an invalid arg.
|
||||||
OptionalInvalidExplicitArg,
|
OptionalInvalidExplicitArg, expr().bind("callExpr")),
|
||||||
expr().bind("callExpr")),
|
|
||||||
// or a construct expression,
|
// or a construct expression,
|
||||||
cxxConstructExpr(
|
cxxConstructExpr(
|
||||||
// which optionally has an invalid arg.
|
// which optionally has an invalid arg.
|
||||||
OptionalInvalidExplicitArg,
|
OptionalInvalidExplicitArg, expr().bind("constructExpr"))),
|
||||||
expr().bind("constructExpr"))),
|
|
||||||
|
|
||||||
anyOf(
|
anyOf(
|
||||||
// We want to match the parent function.
|
// We want to match the parent function.
|
||||||
@@ -135,16 +129,15 @@ void CanRunScriptChecker::buildFuncSet(ASTContext *Context) {
|
|||||||
// a MOZ_CAN_RUN_SCRIPT annotation.
|
// a MOZ_CAN_RUN_SCRIPT annotation.
|
||||||
FuncSetCallback Callback(CanRunScriptFuncs);
|
FuncSetCallback Callback(CanRunScriptFuncs);
|
||||||
// We add the matcher to the finder, linking it to our callback.
|
// We add the matcher to the finder, linking it to our callback.
|
||||||
Finder.addMatcher(functionDecl(hasCanRunScriptAnnotation())
|
Finder.addMatcher(
|
||||||
.bind("canRunScriptFunction"),
|
functionDecl(hasCanRunScriptAnnotation()).bind("canRunScriptFunction"),
|
||||||
&Callback);
|
&Callback);
|
||||||
|
|
||||||
// We start the analysis, given the ASTContext our main checker is in.
|
// We start the analysis, given the ASTContext our main checker is in.
|
||||||
Finder.matchAST(*Context);
|
Finder.matchAST(*Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanRunScriptChecker::check(
|
void CanRunScriptChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
|
|
||||||
// If the set of functions which can run script is not yet built, then build
|
// If the set of functions which can run script is not yet built, then build
|
||||||
// it.
|
// it.
|
||||||
@@ -161,8 +154,7 @@ void CanRunScriptChecker::check(
|
|||||||
const char *ErrorNonCanRunScriptParent =
|
const char *ErrorNonCanRunScriptParent =
|
||||||
"functions marked as MOZ_CAN_RUN_SCRIPT can only be called from "
|
"functions marked as MOZ_CAN_RUN_SCRIPT can only be called from "
|
||||||
"functions also marked as MOZ_CAN_RUN_SCRIPT";
|
"functions also marked as MOZ_CAN_RUN_SCRIPT";
|
||||||
const char* NoteNonCanRunScriptParent =
|
const char *NoteNonCanRunScriptParent = "parent function declared here";
|
||||||
"parent function declared here";
|
|
||||||
|
|
||||||
const Expr *InvalidArg = Result.Nodes.getNodeAs<Expr>("invalidArg");
|
const Expr *InvalidArg = Result.Nodes.getNodeAs<Expr>("invalidArg");
|
||||||
|
|
||||||
@@ -193,7 +185,6 @@ void CanRunScriptChecker::check(
|
|||||||
ParentFunction = nullptr;
|
ParentFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get the call range from either the CallExpr or the ConstructExpr.
|
// Get the call range from either the CallExpr or the ConstructExpr.
|
||||||
SourceRange CallRange;
|
SourceRange CallRange;
|
||||||
if (Call) {
|
if (Call) {
|
||||||
|
|||||||
@@ -10,8 +10,7 @@
|
|||||||
|
|
||||||
class CanRunScriptChecker : public BaseCheck {
|
class CanRunScriptChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
CanRunScriptChecker(StringRef CheckName,
|
CanRunScriptChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ AST_MATCHER(CXXMethodDecl, isRValueRefQualified) {
|
|||||||
return Node.getRefQualifier() == RQ_RValue;
|
return Node.getRefQualifier() == RQ_RValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_POLYMORPHIC_MATCHER(isFirstParty, \
|
AST_POLYMORPHIC_MATCHER(isFirstParty,
|
||||||
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
|
||||||
return !inThirdPartyPath(&Node, &Finder->getASTContext()) &&
|
return !inThirdPartyPath(&Node, &Finder->getASTContext()) &&
|
||||||
!ASTIsInSystemHeader(Finder->getASTContext(), Node);
|
!ASTIsInSystemHeader(Finder->getASTContext(), Node);
|
||||||
@@ -312,8 +312,8 @@ AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) {
|
} else if (const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) {
|
||||||
if(InnerMatcher.matches(*LambdaExprNode->getCallOperator(),
|
if (InnerMatcher.matches(*LambdaExprNode->getCallOperator(), Finder,
|
||||||
Finder, Builder)) {
|
Builder)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -325,7 +325,7 @@ AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
} // namespace ast_matchers
|
||||||
}
|
} // namespace clang
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,24 +11,19 @@ CustomTypeAnnotation GlobalClass =
|
|||||||
CustomTypeAnnotation("moz_global_class", "global");
|
CustomTypeAnnotation("moz_global_class", "global");
|
||||||
CustomTypeAnnotation NonHeapClass =
|
CustomTypeAnnotation NonHeapClass =
|
||||||
CustomTypeAnnotation("moz_nonheap_class", "non-heap");
|
CustomTypeAnnotation("moz_nonheap_class", "non-heap");
|
||||||
CustomTypeAnnotation HeapClass =
|
CustomTypeAnnotation HeapClass = CustomTypeAnnotation("moz_heap_class", "heap");
|
||||||
CustomTypeAnnotation("moz_heap_class", "heap");
|
|
||||||
CustomTypeAnnotation NonTemporaryClass =
|
CustomTypeAnnotation NonTemporaryClass =
|
||||||
CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
|
CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
|
||||||
|
|
||||||
void CustomTypeAnnotation::dumpAnnotationReason(BaseCheck &Check,
|
void CustomTypeAnnotation::dumpAnnotationReason(BaseCheck &Check, QualType T,
|
||||||
QualType T,
|
|
||||||
SourceLocation Loc) {
|
SourceLocation Loc) {
|
||||||
const char *Inherits =
|
const char *Inherits =
|
||||||
"%1 is a %0 type because it inherits from a %0 type %2";
|
"%1 is a %0 type because it inherits from a %0 type %2";
|
||||||
const char* Member =
|
const char *Member = "%1 is a %0 type because member %2 is a %0 type %3";
|
||||||
"%1 is a %0 type because member %2 is a %0 type %3";
|
const char *Array = "%1 is a %0 type because it is an array of %0 type %2";
|
||||||
const char* Array =
|
|
||||||
"%1 is a %0 type because it is an array of %0 type %2";
|
|
||||||
const char *Templ =
|
const char *Templ =
|
||||||
"%1 is a %0 type because it has a template argument %0 type %2";
|
"%1 is a %0 type because it has a template argument %0 type %2";
|
||||||
const char* Implicit =
|
const char *Implicit = "%1 is a %0 type because %2";
|
||||||
"%1 is a %0 type because %2";
|
|
||||||
|
|
||||||
AnnotationReason Reason = directAnnotationReason(T);
|
AnnotationReason Reason = directAnnotationReason(T);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|||||||
@@ -41,12 +41,10 @@ public:
|
|||||||
bool hasEffectiveAnnotation(QualType T) {
|
bool hasEffectiveAnnotation(QualType T) {
|
||||||
return directAnnotationReason(T).valid();
|
return directAnnotationReason(T).valid();
|
||||||
}
|
}
|
||||||
void dumpAnnotationReason(BaseCheck &Check, QualType T,
|
void dumpAnnotationReason(BaseCheck &Check, QualType T, SourceLocation Loc);
|
||||||
SourceLocation Loc);
|
|
||||||
|
|
||||||
void reportErrorIfPresent(BaseCheck &Check, QualType T,
|
void reportErrorIfPresent(BaseCheck &Check, QualType T, SourceLocation Loc,
|
||||||
SourceLocation Loc, const char* Error,
|
const char *Error, const char *Note) {
|
||||||
const char* Note) {
|
|
||||||
if (hasEffectiveAnnotation(T)) {
|
if (hasEffectiveAnnotation(T)) {
|
||||||
Check.diag(Loc, Error, DiagnosticIDs::Error) << T;
|
Check.diag(Loc, Error, DiagnosticIDs::Error) << T;
|
||||||
Check.diag(Loc, Note, DiagnosticIDs::Note);
|
Check.diag(Loc, Note, DiagnosticIDs::Note);
|
||||||
|
|||||||
@@ -42,9 +42,8 @@ void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) {
|
|||||||
// Main checker //
|
// Main checker //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
auto hasParentCall =
|
auto hasParentCall = hasParent(expr(
|
||||||
hasParent(expr(anyOf(
|
anyOf(cxxOperatorCallExpr(
|
||||||
cxxOperatorCallExpr(
|
|
||||||
// If we're in a lamda, we may have an operator call expression
|
// If we're in a lamda, we may have an operator call expression
|
||||||
// ancestor in the AST, but the temporary we're matching
|
// ancestor in the AST, but the temporary we're matching
|
||||||
// against is not going to have the same lifetime as the
|
// against is not going to have the same lifetime as the
|
||||||
@@ -80,9 +79,7 @@ void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) {
|
|||||||
isFirstParty(),
|
isFirstParty(),
|
||||||
|
|
||||||
// and which is performed on a temporary,
|
// and which is performed on a temporary,
|
||||||
on(allOf(
|
on(allOf(unless(hasType(pointerType())), isTemporary(),
|
||||||
unless(hasType(pointerType())),
|
|
||||||
isTemporary(),
|
|
||||||
// but which is not `this`.
|
// but which is not `this`.
|
||||||
unless(cxxThisExpr()))),
|
unless(cxxThisExpr()))),
|
||||||
|
|
||||||
@@ -101,9 +98,7 @@ void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) {
|
|||||||
|
|
||||||
// This is the case where the call is not the direct parent, so we
|
// This is the case where the call is not the direct parent, so we
|
||||||
// get its child to know in which argument tree we are.
|
// get its child to know in which argument tree we are.
|
||||||
hasAncestor(expr(
|
hasAncestor(expr(hasParentCall, expr().bind("parentCallArg"))),
|
||||||
hasParentCall,
|
|
||||||
expr().bind("parentCallArg"))),
|
|
||||||
// To make it optional.
|
// To make it optional.
|
||||||
anything())),
|
anything())),
|
||||||
this);
|
this);
|
||||||
@@ -163,8 +158,7 @@ void DanglingOnTemporaryChecker::check(const MatchFinder::MatchResult &Result) {
|
|||||||
Result.Nodes.getNodeAs<CXXConstructExpr>("parentConstructExpr");
|
Result.Nodes.getNodeAs<CXXConstructExpr>("parentConstructExpr");
|
||||||
const CXXOperatorCallExpr *ParentOperatorCallExpr =
|
const CXXOperatorCallExpr *ParentOperatorCallExpr =
|
||||||
Result.Nodes.getNodeAs<CXXOperatorCallExpr>("parentOperatorCallExpr");
|
Result.Nodes.getNodeAs<CXXOperatorCallExpr>("parentOperatorCallExpr");
|
||||||
const Expr *ParentCallArg =
|
const Expr *ParentCallArg = Result.Nodes.getNodeAs<Expr>("parentCallArg");
|
||||||
Result.Nodes.getNodeAs<Expr>("parentCallArg");
|
|
||||||
|
|
||||||
// Just in case.
|
// Just in case.
|
||||||
if (!MemberCall) {
|
if (!MemberCall) {
|
||||||
@@ -180,8 +174,8 @@ void DanglingOnTemporaryChecker::check(const MatchFinder::MatchResult &Result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No default constructor so we can't construct it using if/else.
|
// No default constructor so we can't construct it using if/else.
|
||||||
auto FunctionEscapeData
|
auto FunctionEscapeData =
|
||||||
= ParentOperatorCallExpr
|
ParentOperatorCallExpr
|
||||||
? escapesFunction(ParentCallArg, ParentOperatorCallExpr)
|
? escapesFunction(ParentCallArg, ParentOperatorCallExpr)
|
||||||
: ParentCallExpr
|
: ParentCallExpr
|
||||||
? escapesFunction(ParentCallArg, ParentCallExpr)
|
? escapesFunction(ParentCallArg, ParentCallExpr)
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
#include "DiagnosticsMatcher.h"
|
#include "DiagnosticsMatcher.h"
|
||||||
|
|
||||||
DiagnosticsMatcher::DiagnosticsMatcher(CompilerInstance& CI) :
|
DiagnosticsMatcher::DiagnosticsMatcher(CompilerInstance &CI)
|
||||||
|
:
|
||||||
#define CHECK(cls, name) cls##_(name),
|
#define CHECK(cls, name) cls##_(name),
|
||||||
#include "Checks.inc"
|
#include "Checks.inc"
|
||||||
#undef CHECK
|
#undef CHECK
|
||||||
AstMatcher()
|
AstMatcher() {
|
||||||
{
|
#define CHECK(cls, name) \
|
||||||
#define CHECK(cls, name) cls ## _.registerMatchers(&AstMatcher); \
|
cls##_.registerMatchers(&AstMatcher); \
|
||||||
cls##_.registerPPCallbacks(CI);
|
cls##_.registerPPCallbacks(CI);
|
||||||
#include "Checks.inc"
|
#include "Checks.inc"
|
||||||
#undef CHECK
|
#undef CHECK
|
||||||
|
|||||||
@@ -6,16 +6,16 @@
|
|||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void ExplicitImplicitChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void ExplicitImplicitChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(),
|
AstMatcher->addMatcher(
|
||||||
ofClass(allOf(isConcreteClass(),
|
cxxConstructorDecl(
|
||||||
decl().bind("class"))),
|
isInterestingImplicitCtor(),
|
||||||
|
ofClass(allOf(isConcreteClass(), decl().bind("class"))),
|
||||||
unless(isMarkedImplicit()))
|
unless(isMarkedImplicit()))
|
||||||
.bind("ctor"),
|
.bind("ctor"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplicitImplicitChecker::check(
|
void ExplicitImplicitChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
// We've already checked everything in the matcher, so we just have to report
|
// We've already checked everything in the matcher, so we just have to report
|
||||||
// the error.
|
// the error.
|
||||||
|
|
||||||
@@ -25,7 +25,9 @@ void ExplicitImplicitChecker::check(
|
|||||||
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
||||||
|
|
||||||
diag(Ctor->getLocation(), "bad implicit conversion constructor for %0",
|
diag(Ctor->getLocation(), "bad implicit conversion constructor for %0",
|
||||||
DiagnosticIDs::Error) << Declaration->getDeclName();
|
DiagnosticIDs::Error)
|
||||||
diag(Ctor->getLocation(), "consider adding the explicit keyword to the constructor",
|
<< Declaration->getDeclName();
|
||||||
|
diag(Ctor->getLocation(),
|
||||||
|
"consider adding the explicit keyword to the constructor",
|
||||||
DiagnosticIDs::Note);
|
DiagnosticIDs::Note);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class ExplicitImplicitChecker : public BaseCheck {
|
class ExplicitImplicitChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
ExplicitImplicitChecker(StringRef CheckName,
|
ExplicitImplicitChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ void ExplicitOperatorBoolChecker::check(
|
|||||||
!ASTIsInSystemHeader(Method->getASTContext(), *Method) &&
|
!ASTIsInSystemHeader(Method->getASTContext(), *Method) &&
|
||||||
isInterestingDeclForImplicitConversion(Method)) {
|
isInterestingDeclForImplicitConversion(Method)) {
|
||||||
diag(Method->getLocStart(), "bad implicit conversion operator for %0",
|
diag(Method->getLocStart(), "bad implicit conversion operator for %0",
|
||||||
DiagnosticIDs::Error) << Clazz;
|
DiagnosticIDs::Error)
|
||||||
|
<< Clazz;
|
||||||
diag(Method->getLocStart(), "consider adding the explicit keyword to %0",
|
diag(Method->getLocStart(), "consider adding the explicit keyword to %0",
|
||||||
DiagnosticIDs::Note) << "'operator bool'";
|
DiagnosticIDs::Note)
|
||||||
|
<< "'operator bool'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,14 @@
|
|||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void KungFuDeathGripChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void KungFuDeathGripChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(varDecl(hasType(isRefPtr())).bind("decl"),
|
AstMatcher->addMatcher(varDecl(hasType(isRefPtr())).bind("decl"), this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KungFuDeathGripChecker::check(
|
void KungFuDeathGripChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
const char *Error = "Unused \"kungFuDeathGrip\" %0 objects constructed from "
|
||||||
const char* Error =
|
"%1 are prohibited";
|
||||||
"Unused \"kungFuDeathGrip\" %0 objects constructed from %1 are prohibited";
|
const char *Note = "Please switch all accesses to this %0 to go through "
|
||||||
const char* Note =
|
"'%1', or explicitly pass '%1' to `mozilla::Unused`";
|
||||||
"Please switch all accesses to this %0 to go through '%1', or explicitly pass '%1' to `mozilla::Unused`";
|
|
||||||
|
|
||||||
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("decl");
|
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("decl");
|
||||||
if (D->isReferenced() || !D->hasLocalStorage() || !D->hasInit()) {
|
if (D->isReferenced() || !D->hasLocalStorage() || !D->hasInit()) {
|
||||||
@@ -74,7 +72,8 @@ void KungFuDeathGripChecker::check(
|
|||||||
"nsCreateInstanceFromFactory",
|
"nsCreateInstanceFromFactory",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]); ++i) {
|
for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]);
|
||||||
|
++i) {
|
||||||
if (TD->getName() == IgnoreTypes[i]) {
|
if (TD->getName() == IgnoreTypes[i]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,6 +92,8 @@ void KungFuDeathGripChecker::check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We cannot provide the note if we don't have an initializer
|
// We cannot provide the note if we don't have an initializer
|
||||||
diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D->getType() << ErrThing;
|
diag(D->getLocStart(), Error, DiagnosticIDs::Error)
|
||||||
diag(E->getLocStart(), Note, DiagnosticIDs::Note) << NoteThing << getNameChecked(D);
|
<< D->getType() << ErrThing;
|
||||||
|
diag(E->getLocStart(), Note, DiagnosticIDs::Note)
|
||||||
|
<< NoteThing << getNameChecked(D);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class KungFuDeathGripChecker : public BaseCheck {
|
class KungFuDeathGripChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
KungFuDeathGripChecker(StringRef CheckName,
|
KungFuDeathGripChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
#ifndef MemMoveAnnotation_h__
|
#ifndef MemMoveAnnotation_h__
|
||||||
#define MemMoveAnnotation_h__
|
#define MemMoveAnnotation_h__
|
||||||
|
|
||||||
#include "CustomTypeAnnotation.h"
|
|
||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
#include "CustomTypeAnnotation.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
class MemMoveAnnotation final : public CustomTypeAnnotation {
|
class MemMoveAnnotation final : public CustomTypeAnnotation {
|
||||||
@@ -24,30 +24,19 @@ protected:
|
|||||||
// This doesn't check that it's really ::std::pair and not
|
// This doesn't check that it's really ::std::pair and not
|
||||||
// ::std::something_else::pair, but should be good enough.
|
// ::std::something_else::pair, but should be good enough.
|
||||||
StringRef Name = getNameChecked(D);
|
StringRef Name = getNameChecked(D);
|
||||||
if (Name == "pair" ||
|
if (Name == "pair" || Name == "atomic" ||
|
||||||
Name == "atomic" ||
|
|
||||||
// libstdc++ specific names
|
// libstdc++ specific names
|
||||||
Name == "__atomic_base" ||
|
Name == "__atomic_base" || Name == "atomic_bool" ||
|
||||||
Name == "atomic_bool" ||
|
|
||||||
// MSVCRT specific names
|
// MSVCRT specific names
|
||||||
Name == "_Atomic_impl" ||
|
Name == "_Atomic_impl" || Name == "_Atomic_base" ||
|
||||||
Name == "_Atomic_base" ||
|
Name == "_Atomic_bool" || Name == "_Atomic_char" ||
|
||||||
Name == "_Atomic_bool" ||
|
Name == "_Atomic_schar" || Name == "_Atomic_uchar" ||
|
||||||
Name == "_Atomic_char" ||
|
Name == "_Atomic_char16_t" || Name == "_Atomic_char32_t" ||
|
||||||
Name == "_Atomic_schar" ||
|
Name == "_Atomic_wchar_t" || Name == "_Atomic_short" ||
|
||||||
Name == "_Atomic_uchar" ||
|
Name == "_Atomic_ushort" || Name == "_Atomic_int" ||
|
||||||
Name == "_Atomic_char16_t" ||
|
Name == "_Atomic_uint" || Name == "_Atomic_long" ||
|
||||||
Name == "_Atomic_char32_t" ||
|
Name == "_Atomic_ulong" || Name == "_Atomic_llong" ||
|
||||||
Name == "_Atomic_wchar_t" ||
|
Name == "_Atomic_ullong" || Name == "_Atomic_address") {
|
||||||
Name == "_Atomic_short" ||
|
|
||||||
Name == "_Atomic_ushort" ||
|
|
||||||
Name == "_Atomic_int" ||
|
|
||||||
Name == "_Atomic_uint" ||
|
|
||||||
Name == "_Atomic_long" ||
|
|
||||||
Name == "_Atomic_ulong" ||
|
|
||||||
Name == "_Atomic_llong" ||
|
|
||||||
Name == "_Atomic_ullong" ||
|
|
||||||
Name == "_Atomic_address") {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return "it is an stl-provided type not guaranteed to be memmove-able";
|
return "it is an stl-provided type not guaranteed to be memmove-able";
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "plugin.h"
|
|
||||||
#include "DiagnosticsMatcher.h"
|
#include "DiagnosticsMatcher.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "clang/Frontend/FrontendPluginRegistry.h"
|
#include "clang/Frontend/FrontendPluginRegistry.h"
|
||||||
|
|
||||||
class MozCheckAction : public PluginASTAction {
|
class MozCheckAction : public PluginASTAction {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Register the MozillaTidyModule using this statically initialized variable.
|
// Register the MozillaTidyModule using this statically initialized variable.
|
||||||
static ClangTidyModuleRegistry::Add<MozillaModule> X("mozilla-module",
|
static ClangTidyModuleRegistry::Add<MozillaModule>
|
||||||
"Adds Mozilla lint checks.");
|
X("mozilla-module", "Adds Mozilla lint checks.");
|
||||||
|
|
||||||
} // namespace tidy
|
} // namespace tidy
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ void MustOverrideChecker::registerPPCallbacks(CompilerInstance& CI) {
|
|||||||
this->CI = &CI;
|
this->CI = &CI;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MustOverrideChecker::check(
|
void MustOverrideChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
auto D = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
auto D = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
||||||
|
|
||||||
// Look through all of our immediate bases to find methods that need to be
|
// Look through all of our immediate bases to find methods that need to be
|
||||||
@@ -52,9 +51,8 @@ void MustOverrideChecker::check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Overridden) {
|
if (!Overridden) {
|
||||||
diag(D->getLocation(), "%0 must override %1",
|
diag(D->getLocation(), "%0 must override %1", DiagnosticIDs::Error)
|
||||||
DiagnosticIDs::Error) << D->getDeclName()
|
<< D->getDeclName() << O->getDeclName();
|
||||||
<< O->getDeclName();
|
|
||||||
diag(O->getLocation(), "function to override is here",
|
diag(O->getLocation(), "function to override is here",
|
||||||
DiagnosticIDs::Note);
|
DiagnosticIDs::Note);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class MustOverrideChecker : public BaseCheck {
|
class MustOverrideChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
MustOverrideChecker(StringRef CheckName,
|
MustOverrideChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context), CI(nullptr) {}
|
: BaseCheck(CheckName, Context), CI(nullptr) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void registerPPCallbacks(CompilerInstance &CI) override;
|
void registerPPCallbacks(CompilerInstance &CI) override;
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
|
|
||||||
void MustReturnFromCallerChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void MustReturnFromCallerChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
// Look for a call to a MOZ_MUST_RETURN_FROM_CALLER function
|
// Look for a call to a MOZ_MUST_RETURN_FROM_CALLER function
|
||||||
AstMatcher->addMatcher(callExpr(callee(functionDecl(isMozMustReturnFromCaller())),
|
AstMatcher->addMatcher(
|
||||||
|
callExpr(callee(functionDecl(isMozMustReturnFromCaller())),
|
||||||
anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
|
anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
|
||||||
hasAncestor(functionDecl().bind("containing-func")))).bind("call"),
|
hasAncestor(functionDecl().bind("containing-func"))))
|
||||||
|
.bind("call"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +54,8 @@ void MustReturnFromCallerChecker::check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool MustReturnFromCallerChecker::immediatelyReturns(
|
||||||
MustReturnFromCallerChecker::immediatelyReturns(RecurseGuard<const CFGBlock *> Block,
|
RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext,
|
||||||
ASTContext *TheContext,
|
|
||||||
size_t FromIdx) {
|
size_t FromIdx) {
|
||||||
if (Block.isRepeat()) {
|
if (Block.isRepeat()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -73,8 +74,7 @@ MustReturnFromCallerChecker::immediatelyReturns(RecurseGuard<const CFGBlock *> B
|
|||||||
// It is also, of course, OK to look at a ReturnStmt.
|
// It is also, of course, OK to look at a ReturnStmt.
|
||||||
if (isa<ReturnStmt>(AfterTrivials) ||
|
if (isa<ReturnStmt>(AfterTrivials) ||
|
||||||
isa<CXXConstructExpr>(AfterTrivials) ||
|
isa<CXXConstructExpr>(AfterTrivials) ||
|
||||||
isa<DeclRefExpr>(AfterTrivials) ||
|
isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials)) {
|
||||||
isa<MemberExpr>(AfterTrivials)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,8 @@ MustReturnFromCallerChecker::immediatelyReturns(RecurseGuard<const CFGBlock *> B
|
|||||||
// to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
|
// to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
|
||||||
if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
|
if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
|
||||||
auto Callee = CE->getDirectCallee();
|
auto Callee = CE->getDirectCallee();
|
||||||
if (Callee && hasCustomAnnotation(Callee, "moz_may_call_after_must_return")) {
|
if (Callee &&
|
||||||
|
hasCustomAnnotation(Callee, "moz_may_call_after_must_return")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
#ifndef MustReturnFromCallerChecker_h__
|
#ifndef MustReturnFromCallerChecker_h__
|
||||||
#define MustReturnFromCallerChecker_h__
|
#define MustReturnFromCallerChecker_h__
|
||||||
|
|
||||||
#include "plugin.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "RecurseGuard.h"
|
#include "RecurseGuard.h"
|
||||||
#include "StmtToBlockMap.h"
|
#include "StmtToBlockMap.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
class MustReturnFromCallerChecker : public BaseCheck {
|
class MustReturnFromCallerChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
@@ -17,10 +17,10 @@ public:
|
|||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool immediatelyReturns(RecurseGuard<const CFGBlock *> Block,
|
bool immediatelyReturns(RecurseGuard<const CFGBlock *> Block,
|
||||||
ASTContext *TheContext,
|
ASTContext *TheContext, size_t FromIdx);
|
||||||
size_t FromIdx);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ void MustUseChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
AstMatcher->addMatcher(whileStmt().bind("while"), this);
|
AstMatcher->addMatcher(whileStmt().bind("while"), this);
|
||||||
AstMatcher->addMatcher(doStmt().bind("do"), this);
|
AstMatcher->addMatcher(doStmt().bind("do"), this);
|
||||||
AstMatcher->addMatcher(forStmt().bind("for"), this);
|
AstMatcher->addMatcher(forStmt().bind("for"), this);
|
||||||
AstMatcher->addMatcher(binaryOperator(binaryCommaOperator()).bind("bin"), this);
|
AstMatcher->addMatcher(binaryOperator(binaryCommaOperator()).bind("bin"),
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MustUseChecker::check(
|
void MustUseChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
if (auto SC = Result.Nodes.getNodeAs<SwitchCase>("switchcase")) {
|
if (auto SC = Result.Nodes.getNodeAs<SwitchCase>("switchcase")) {
|
||||||
handleUnusedExprResult(SC->getSubStmt());
|
handleUnusedExprResult(SC->getSubStmt());
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,8 @@ void MustUseChecker::handleUnusedExprResult(const Stmt *Statement) {
|
|||||||
QualType T = E->getType();
|
QualType T = E->getType();
|
||||||
if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) {
|
if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) {
|
||||||
diag(E->getLocStart(), "Unused value of must-use type %0",
|
diag(E->getLocStart(), "Unused value of must-use type %0",
|
||||||
DiagnosticIDs::Error) << T;
|
DiagnosticIDs::Error)
|
||||||
|
<< T;
|
||||||
MustUse.dumpAnnotationReason(*this, T, E->getLocStart());
|
MustUse.dumpAnnotationReason(*this, T, E->getLocStart());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class MustUseChecker : public BaseCheck {
|
class MustUseChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
MustUseChecker(StringRef CheckName,
|
MustUseChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -18,22 +18,21 @@ void NaNExprChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NaNExprChecker::check(
|
void NaNExprChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
if (!Result.Context->getLangOpts().CPlusPlus) {
|
if (!Result.Context->getLangOpts().CPlusPlus) {
|
||||||
// mozilla::IsNaN is not usable in C, so there is no point in issuing these
|
// mozilla::IsNaN is not usable in C, so there is no point in issuing these
|
||||||
// warnings.
|
// warnings.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BinaryOperator *Expression = Result.Nodes.getNodeAs<BinaryOperator>(
|
const BinaryOperator *Expression =
|
||||||
"node");
|
Result.Nodes.getNodeAs<BinaryOperator>("node");
|
||||||
const DeclRefExpr *LHS = Result.Nodes.getNodeAs<DeclRefExpr>("lhs");
|
const DeclRefExpr *LHS = Result.Nodes.getNodeAs<DeclRefExpr>("lhs");
|
||||||
const DeclRefExpr *RHS = Result.Nodes.getNodeAs<DeclRefExpr>("rhs");
|
const DeclRefExpr *RHS = Result.Nodes.getNodeAs<DeclRefExpr>("rhs");
|
||||||
const ImplicitCastExpr *LHSExpr = dyn_cast<ImplicitCastExpr>(
|
const ImplicitCastExpr *LHSExpr =
|
||||||
Expression->getLHS());
|
dyn_cast<ImplicitCastExpr>(Expression->getLHS());
|
||||||
const ImplicitCastExpr *RHSExpr = dyn_cast<ImplicitCastExpr>(
|
const ImplicitCastExpr *RHSExpr =
|
||||||
Expression->getRHS());
|
dyn_cast<ImplicitCastExpr>(Expression->getRHS());
|
||||||
// The AST subtree that we are looking for will look like this:
|
// The AST subtree that we are looking for will look like this:
|
||||||
// -BinaryOperator ==/!=
|
// -BinaryOperator ==/!=
|
||||||
// |-ImplicitCastExpr LValueToRValue
|
// |-ImplicitCastExpr LValueToRValue
|
||||||
@@ -47,7 +46,8 @@ void NaNExprChecker::check(
|
|||||||
std::distance(LHSExpr->child_begin(), LHSExpr->child_end()) == 1 &&
|
std::distance(LHSExpr->child_begin(), LHSExpr->child_end()) == 1 &&
|
||||||
std::distance(RHSExpr->child_begin(), RHSExpr->child_end()) == 1 &&
|
std::distance(RHSExpr->child_begin(), RHSExpr->child_end()) == 1 &&
|
||||||
*LHSExpr->child_begin() == LHS && *RHSExpr->child_begin() == RHS) {
|
*LHSExpr->child_begin() == LHS && *RHSExpr->child_begin() == RHS) {
|
||||||
diag(Expression->getLocStart(), "comparing a floating point value to itself for "
|
diag(Expression->getLocStart(),
|
||||||
|
"comparing a floating point value to itself for "
|
||||||
"NaN checking can lead to incorrect results",
|
"NaN checking can lead to incorrect results",
|
||||||
DiagnosticIDs::Error);
|
DiagnosticIDs::Error);
|
||||||
diag(Expression->getLocStart(), "consider using mozilla::IsNaN instead",
|
diag(Expression->getLocStart(), "consider using mozilla::IsNaN instead",
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class NaNExprChecker : public BaseCheck {
|
class NaNExprChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
NaNExprChecker(StringRef CheckName,
|
NaNExprChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ void NeedsNoVTableTypeChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeedsNoVTableTypeChecker::check(
|
void NeedsNoVTableTypeChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
const ClassTemplateSpecializationDecl *Specialization =
|
const ClassTemplateSpecializationDecl *Specialization =
|
||||||
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node");
|
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node");
|
||||||
|
|
||||||
@@ -32,9 +31,9 @@ void NeedsNoVTableTypeChecker::check(
|
|||||||
|
|
||||||
diag(Specialization->getLocStart(),
|
diag(Specialization->getLocStart(),
|
||||||
"%0 cannot be instantiated because %1 has a VTable",
|
"%0 cannot be instantiated because %1 has a VTable",
|
||||||
DiagnosticIDs::Error) << Specialization
|
DiagnosticIDs::Error)
|
||||||
<< Offender;
|
<< Specialization << Offender;
|
||||||
diag(Specialization->getPointOfInstantiation(),
|
diag(Specialization->getPointOfInstantiation(),
|
||||||
"bad instantiation of %0 requested here",
|
"bad instantiation of %0 requested here", DiagnosticIDs::Note)
|
||||||
DiagnosticIDs::Note) << Specialization;
|
<< Specialization;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class NeedsNoVTableTypeChecker : public BaseCheck {
|
class NeedsNoVTableTypeChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
NeedsNoVTableTypeChecker(StringRef CheckName,
|
NeedsNoVTableTypeChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
void NoAddRefReleaseOnReturnChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void NoAddRefReleaseOnReturnChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
// Look for all of the calls to AddRef() or Release()
|
// Look for all of the calls to AddRef() or Release()
|
||||||
AstMatcher->addMatcher(memberExpr(isAddRefOrRelease(), hasParent(callExpr())).bind("member"),
|
AstMatcher->addMatcher(
|
||||||
|
memberExpr(isAddRefOrRelease(), hasParent(callExpr())).bind("member"),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,8 +25,7 @@ void NoAddRefReleaseOnReturnChecker::check(
|
|||||||
diag(Call->getLocStart(),
|
diag(Call->getLocStart(),
|
||||||
"%1 cannot be called on the return value of %0",
|
"%1 cannot be called on the return value of %0",
|
||||||
DiagnosticIDs::Error)
|
DiagnosticIDs::Error)
|
||||||
<< Callee
|
<< Callee << dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
|
||||||
<< dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ void NoAutoTypeChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoAutoTypeChecker::check(
|
void NoAutoTypeChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node");
|
const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node");
|
||||||
|
|
||||||
diag(D->getLocation(),
|
diag(D->getLocation(), "Cannot use auto to declare a variable of type %0",
|
||||||
"Cannot use auto to declare a variable of type %0",
|
DiagnosticIDs::Error)
|
||||||
DiagnosticIDs::Error) << D->getType();
|
<< D->getType();
|
||||||
diag(D->getLocation(),
|
diag(D->getLocation(), "Please write out this type explicitly",
|
||||||
"Please write out this type explicitly",
|
|
||||||
DiagnosticIDs::Note);
|
DiagnosticIDs::Note);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class NoAutoTypeChecker : public BaseCheck {
|
class NoAutoTypeChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
NoAutoTypeChecker(StringRef CheckName,
|
NoAutoTypeChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -34,10 +34,8 @@ void NoDuplicateRefCntMemberChecker::check(
|
|||||||
if (BaseRefCntMember) {
|
if (BaseRefCntMember) {
|
||||||
if (RefCntMember) {
|
if (RefCntMember) {
|
||||||
// We have an mRefCnt, and superclass has an mRefCnt
|
// We have an mRefCnt, and superclass has an mRefCnt
|
||||||
const char* Error =
|
const char *Error = "Refcounted record %0 has multiple mRefCnt members";
|
||||||
"Refcounted record %0 has multiple mRefCnt members";
|
const char *Note1 = "Superclass %0 also has an mRefCnt member";
|
||||||
const char* Note1 =
|
|
||||||
"Superclass %0 also has an mRefCnt member";
|
|
||||||
const char *Note2 =
|
const char *Note2 =
|
||||||
"Consider using the _INHERITED macros for AddRef and Release here";
|
"Consider using the _INHERITED macros for AddRef and Release here";
|
||||||
|
|
||||||
@@ -48,10 +46,9 @@ void NoDuplicateRefCntMemberChecker::check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (FoundRefCntBase) {
|
if (FoundRefCntBase) {
|
||||||
const char* Error =
|
const char *Error = "Refcounted record %0 has multiple superclasses "
|
||||||
"Refcounted record %0 has multiple superclasses with mRefCnt members";
|
"with mRefCnt members";
|
||||||
const char* Note =
|
const char *Note = "Superclass %0 has an mRefCnt member";
|
||||||
"Superclass %0 has an mRefCnt member";
|
|
||||||
|
|
||||||
// superclass has mRefCnt, and another superclass also has an mRefCnt
|
// superclass has mRefCnt, and another superclass also has an mRefCnt
|
||||||
diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D;
|
diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D;
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
#include "NoExplicitMoveConstructorChecker.h"
|
#include "NoExplicitMoveConstructorChecker.h"
|
||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void NoExplicitMoveConstructorChecker::registerMatchers(MatchFinder* AstMatcher) {
|
void NoExplicitMoveConstructorChecker::registerMatchers(
|
||||||
|
MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"),
|
cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"), this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoExplicitMoveConstructorChecker::check(
|
void NoExplicitMoveConstructorChecker::check(
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ MemMoveAnnotation NonMemMovable = MemMoveAnnotation();
|
|||||||
|
|
||||||
void NonMemMovableMemberChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void NonMemMovableMemberChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
// Handle non-mem-movable members
|
// Handle non-mem-movable members
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(cxxRecordDecl(needsMemMovableMembers()).bind("decl"),
|
||||||
cxxRecordDecl(needsMemMovableMembers())
|
|
||||||
.bind("decl"),
|
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NonMemMovableMemberChecker::check(
|
void NonMemMovableMemberChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
const char *Error =
|
const char *Error =
|
||||||
"class %0 cannot have non-memmovable member %1 of type %2";
|
"class %0 cannot have non-memmovable member %1 of type %2";
|
||||||
|
|
||||||
@@ -29,10 +26,9 @@ void NonMemMovableMemberChecker::check(
|
|||||||
QualType Type = Field->getType();
|
QualType Type = Field->getType();
|
||||||
if (NonMemMovable.hasEffectiveAnnotation(Type)) {
|
if (NonMemMovable.hasEffectiveAnnotation(Type)) {
|
||||||
diag(Field->getLocation(), Error, DiagnosticIDs::Error)
|
diag(Field->getLocation(), Error, DiagnosticIDs::Error)
|
||||||
<< Declaration
|
<< Declaration << Field << Type;
|
||||||
<< Field
|
NonMemMovable.dumpAnnotationReason(*this, Type,
|
||||||
<< Type;
|
Declaration->getLocation());
|
||||||
NonMemMovable.dumpAnnotationReason(*this, Type, Declaration->getLocation());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
#include "NonMemMovableTemplateArgChecker.h"
|
#include "NonMemMovableTemplateArgChecker.h"
|
||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void NonMemMovableTemplateArgChecker::registerMatchers(MatchFinder* AstMatcher) {
|
void NonMemMovableTemplateArgChecker::registerMatchers(
|
||||||
|
MatchFinder *AstMatcher) {
|
||||||
// Handle non-mem-movable template specializations
|
// Handle non-mem-movable template specializations
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
classTemplateSpecializationDecl(
|
classTemplateSpecializationDecl(
|
||||||
@@ -19,8 +20,7 @@ void NonMemMovableTemplateArgChecker::check(
|
|||||||
const MatchFinder::MatchResult &Result) {
|
const MatchFinder::MatchResult &Result) {
|
||||||
const char *Error =
|
const char *Error =
|
||||||
"Cannot instantiate %0 with non-memmovable template argument %1";
|
"Cannot instantiate %0 with non-memmovable template argument %1";
|
||||||
const char* Note =
|
const char *Note = "instantiation of %0 requested here";
|
||||||
"instantiation of %0 requested here";
|
|
||||||
|
|
||||||
// Get the specialization
|
// Get the specialization
|
||||||
const ClassTemplateSpecializationDecl *Specialization =
|
const ClassTemplateSpecializationDecl *Specialization =
|
||||||
@@ -33,9 +33,8 @@ void NonMemMovableTemplateArgChecker::check(
|
|||||||
for (unsigned i = 0; i < Args.size(); ++i) {
|
for (unsigned i = 0; i < Args.size(); ++i) {
|
||||||
QualType ArgType = Args[i].getAsType();
|
QualType ArgType = Args[i].getAsType();
|
||||||
if (NonMemMovable.hasEffectiveAnnotation(ArgType)) {
|
if (NonMemMovable.hasEffectiveAnnotation(ArgType)) {
|
||||||
diag(Specialization->getLocation(), Error,
|
diag(Specialization->getLocation(), Error, DiagnosticIDs::Error)
|
||||||
DiagnosticIDs::Error) << Specialization
|
<< Specialization << ArgType;
|
||||||
<< ArgType;
|
|
||||||
// XXX It would be really nice if we could get the instantiation stack
|
// XXX It would be really nice if we could get the instantiation stack
|
||||||
// information
|
// information
|
||||||
// from Sema such that we could print a full template instantiation stack,
|
// from Sema such that we could print a full template instantiation stack,
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
#include "NonParamInsideFunctionDeclChecker.h"
|
#include "NonParamInsideFunctionDeclChecker.h"
|
||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
class NonParamAnnotation : public CustomTypeAnnotation
|
class NonParamAnnotation : public CustomTypeAnnotation {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
NonParamAnnotation() : CustomTypeAnnotation("moz_non_param", "non-param"){};
|
NonParamAnnotation() : CustomTypeAnnotation("moz_non_param", "non-param"){};
|
||||||
|
|
||||||
@@ -28,7 +27,9 @@ protected:
|
|||||||
for (auto F : RD->fields()) {
|
for (auto F : RD->fields()) {
|
||||||
for (auto A : F->attrs()) {
|
for (auto A : F->attrs()) {
|
||||||
if (isa<AlignedAttr>(A)) {
|
if (isa<AlignedAttr>(A)) {
|
||||||
return ("member '" + F->getName() + "' has an alignas(_) annotation").str();
|
return ("member '" + F->getName() +
|
||||||
|
"' has an alignas(_) annotation")
|
||||||
|
.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,17 +42,17 @@ protected:
|
|||||||
};
|
};
|
||||||
NonParamAnnotation NonParam;
|
NonParamAnnotation NonParam;
|
||||||
|
|
||||||
void NonParamInsideFunctionDeclChecker::registerMatchers(MatchFinder* AstMatcher) {
|
void NonParamInsideFunctionDeclChecker::registerMatchers(
|
||||||
|
MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
functionDecl(anyOf(allOf(isDefinition(),
|
functionDecl(
|
||||||
hasAncestor(classTemplateSpecializationDecl()
|
anyOf(allOf(isDefinition(),
|
||||||
.bind("spec"))),
|
hasAncestor(
|
||||||
|
classTemplateSpecializationDecl().bind("spec"))),
|
||||||
isDefinition()))
|
isDefinition()))
|
||||||
.bind("func"),
|
.bind("func"),
|
||||||
this);
|
this);
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(lambdaExpr().bind("lambda"), this);
|
||||||
lambdaExpr().bind("lambda"),
|
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NonParamInsideFunctionDeclChecker::check(
|
void NonParamInsideFunctionDeclChecker::check(
|
||||||
@@ -98,14 +99,15 @@ void NonParamInsideFunctionDeclChecker::check(
|
|||||||
QualType T = p->getType().withoutLocalFastQualifiers();
|
QualType T = p->getType().withoutLocalFastQualifiers();
|
||||||
if (NonParam.hasEffectiveAnnotation(T)) {
|
if (NonParam.hasEffectiveAnnotation(T)) {
|
||||||
diag(p->getLocation(), "Type %0 must not be used as parameter",
|
diag(p->getLocation(), "Type %0 must not be used as parameter",
|
||||||
DiagnosticIDs::Error) << T;
|
DiagnosticIDs::Error)
|
||||||
diag(p->getLocation(), "Please consider passing a const reference instead",
|
<< T;
|
||||||
|
diag(p->getLocation(),
|
||||||
|
"Please consider passing a const reference instead",
|
||||||
DiagnosticIDs::Note);
|
DiagnosticIDs::Note);
|
||||||
|
|
||||||
if (Spec) {
|
if (Spec) {
|
||||||
diag(Spec->getPointOfInstantiation(),
|
diag(Spec->getPointOfInstantiation(),
|
||||||
"The bad argument was passed to %0 here",
|
"The bad argument was passed to %0 here", DiagnosticIDs::Note)
|
||||||
DiagnosticIDs::Note)
|
|
||||||
<< Spec->getSpecializedTemplate();
|
<< Spec->getSpecializedTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void OverrideBaseCallChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void OverrideBaseCallChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"),
|
AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OverrideBaseCallChecker::isRequiredBaseMethod(
|
bool OverrideBaseCallChecker::isRequiredBaseMethod(
|
||||||
@@ -23,8 +22,8 @@ void OverrideBaseCallChecker::evaluateExpression(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) {
|
if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) {
|
||||||
if (auto Method = dyn_cast<CXXMethodDecl>(
|
if (auto Method =
|
||||||
MemberFuncCall->getDirectCallee())) {
|
dyn_cast<CXXMethodDecl>(MemberFuncCall->getDirectCallee())) {
|
||||||
findBaseMethodCall(Method, MethodList);
|
findBaseMethodCall(Method, MethodList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +62,7 @@ void OverrideBaseCallChecker::findBaseMethodCall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverrideBaseCallChecker::check(
|
void OverrideBaseCallChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
const char *Error =
|
const char *Error =
|
||||||
"Method %0 must be called in all overrides, but is not called in "
|
"Method %0 must be called in all overrides, but is not called in "
|
||||||
"this override defined for class %1";
|
"this override defined for class %1";
|
||||||
@@ -105,8 +103,7 @@ void OverrideBaseCallChecker::check(
|
|||||||
BaseMethod->printQualifiedName(OS);
|
BaseMethod->printQualifiedName(OS);
|
||||||
|
|
||||||
diag(Method->getLocation(), Error, DiagnosticIDs::Error)
|
diag(Method->getLocation(), Error, DiagnosticIDs::Error)
|
||||||
<< OS.str()
|
<< OS.str() << Decl->getName();
|
||||||
<< Decl->getName();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
|
|
||||||
class OverrideBaseCallChecker : public BaseCheck {
|
class OverrideBaseCallChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
OverrideBaseCallChecker(StringRef CheckName,
|
OverrideBaseCallChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void evaluateExpression(const Stmt *StmtExpr,
|
void evaluateExpression(const Stmt *StmtExpr,
|
||||||
std::list<const CXXMethodDecl *> &MethodList);
|
std::list<const CXXMethodDecl *> &MethodList);
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
// The RecurseGuard object will unregister its object when it is destroyed, and
|
// The RecurseGuard object will unregister its object when it is destroyed, and
|
||||||
// has a method `isRepeat()` which will return `true` if the item was already
|
// has a method `isRepeat()` which will return `true` if the item was already
|
||||||
// seen.
|
// seen.
|
||||||
template<typename T>
|
template <typename T> class RecurseGuard {
|
||||||
class RecurseGuard {
|
|
||||||
public:
|
public:
|
||||||
RecurseGuard(T Thing) : Thing(Thing), Set(new DenseSet<T>()), Repeat(false) {
|
RecurseGuard(T Thing) : Thing(Thing), Set(new DenseSet<T>()), Repeat(false) {
|
||||||
Set->insert(Thing);
|
Set->insert(Thing);
|
||||||
@@ -42,17 +41,11 @@ public:
|
|||||||
|
|
||||||
T get() { return Thing; }
|
T get() { return Thing; }
|
||||||
|
|
||||||
operator T() {
|
operator T() { return Thing; }
|
||||||
return Thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
T operator ->() {
|
T operator->() { return Thing; }
|
||||||
return Thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecurseGuard recurse(T NewThing) {
|
RecurseGuard recurse(T NewThing) { return RecurseGuard(NewThing, Set); }
|
||||||
return RecurseGuard(NewThing, Set);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T Thing;
|
T Thing;
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
#include "RefCountedCopyConstructorChecker.h"
|
#include "RefCountedCopyConstructorChecker.h"
|
||||||
#include "CustomMatchers.h"
|
#include "CustomMatchers.h"
|
||||||
|
|
||||||
void RefCountedCopyConstructorChecker::registerMatchers(MatchFinder* AstMatcher) {
|
void RefCountedCopyConstructorChecker::registerMatchers(
|
||||||
|
MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
cxxConstructExpr(
|
cxxConstructExpr(
|
||||||
hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
|
hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
|
||||||
@@ -18,8 +19,7 @@ void RefCountedCopyConstructorChecker::check(
|
|||||||
const MatchFinder::MatchResult &Result) {
|
const MatchFinder::MatchResult &Result) {
|
||||||
const char *Error =
|
const char *Error =
|
||||||
"Invalid use of compiler-provided copy constructor on refcounted type";
|
"Invalid use of compiler-provided copy constructor on refcounted type";
|
||||||
const char* Note =
|
const char *Note = "The default copy constructor also copies the "
|
||||||
"The default copy constructor also copies the "
|
|
||||||
"default mRefCnt property, leading to reference "
|
"default mRefCnt property, leading to reference "
|
||||||
"count imbalance issues. Please provide your own "
|
"count imbalance issues. Please provide your own "
|
||||||
"copy constructor which only copies the fields which "
|
"copy constructor which only copies the fields which "
|
||||||
|
|||||||
@@ -15,26 +15,25 @@ void RefCountedInsideLambdaChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
// in a return value (either from lambdas, or in c++14, auto functions).
|
// in a return value (either from lambdas, or in c++14, auto functions).
|
||||||
//
|
//
|
||||||
// We check these lambdas' capture lists for raw pointers to refcounted types.
|
// We check these lambdas' capture lists for raw pointers to refcounted types.
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(functionDecl(returns(recordType(hasDeclaration(
|
||||||
functionDecl(returns(recordType(hasDeclaration(cxxRecordDecl(
|
cxxRecordDecl(isLambdaDecl()).bind("decl"))))),
|
||||||
isLambdaDecl()).bind("decl"))))),
|
|
||||||
this);
|
|
||||||
AstMatcher->addMatcher(lambdaExpr().bind("lambdaExpr"),
|
|
||||||
this);
|
this);
|
||||||
|
AstMatcher->addMatcher(lambdaExpr().bind("lambdaExpr"), this);
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
|
classTemplateSpecializationDecl(
|
||||||
recordType(hasDeclaration(cxxRecordDecl(
|
hasAnyTemplateArgument(refersToType(recordType(
|
||||||
isLambdaDecl()).bind("decl")))))),
|
hasDeclaration(cxxRecordDecl(isLambdaDecl()).bind("decl")))))),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
|
void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
|
||||||
StringRef Name,
|
StringRef Name,
|
||||||
QualType Type) {
|
QualType Type) {
|
||||||
diag(Loc, "Refcounted variable '%0' of type %1 cannot be captured by a lambda",
|
diag(Loc,
|
||||||
DiagnosticIDs::Error) << Name << Type;
|
"Refcounted variable '%0' of type %1 cannot be captured by a lambda",
|
||||||
diag(Loc, "Please consider using a smart pointer",
|
DiagnosticIDs::Error)
|
||||||
DiagnosticIDs::Note);
|
<< Name << Type;
|
||||||
|
diag(Loc, "Please consider using a smart pointer", DiagnosticIDs::Note);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefCountedInsideLambdaChecker::check(
|
void RefCountedInsideLambdaChecker::check(
|
||||||
@@ -66,17 +65,18 @@ void RefCountedInsideLambdaChecker::check(
|
|||||||
|
|
||||||
for (const LambdaCapture &Capture : Lambda->captures()) {
|
for (const LambdaCapture &Capture : Lambda->captures()) {
|
||||||
// Check if any of the captures are ByRef. If they are, we have nothing to
|
// Check if any of the captures are ByRef. If they are, we have nothing to
|
||||||
// report, as it's OK to capture raw pointers to refcounted objects so long as
|
// report, as it's OK to capture raw pointers to refcounted objects so long
|
||||||
// the Lambda doesn't escape the current scope, which is required by ByRef
|
// as the Lambda doesn't escape the current scope, which is required by
|
||||||
// captures already.
|
// ByRef captures already.
|
||||||
if (Capture.getCaptureKind() == LCK_ByRef) {
|
if (Capture.getCaptureKind() == LCK_ByRef) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this capture is byvalue, and captures a strong reference to this.
|
// Check if this capture is byvalue, and captures a strong reference to
|
||||||
// XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow?
|
// this.
|
||||||
if (!StrongRefToThisCaptured &&
|
// XXX: Do we want to make sure that this type which we are capturing is a
|
||||||
Capture.capturesVariable() &&
|
// "Smart Pointer" somehow?
|
||||||
|
if (!StrongRefToThisCaptured && Capture.capturesVariable() &&
|
||||||
Capture.getCaptureKind() == LCK_ByCopy) {
|
Capture.getCaptureKind() == LCK_ByCopy) {
|
||||||
const VarDecl *Var = Capture.getCapturedVar();
|
const VarDecl *Var = Capture.getCapturedVar();
|
||||||
if (Var->hasInit()) {
|
if (Var->hasInit()) {
|
||||||
@@ -103,13 +103,15 @@ void RefCountedInsideLambdaChecker::check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can go through and produce errors for any captured variables or this pointers.
|
// Now we can go through and produce errors for any captured variables or this
|
||||||
|
// pointers.
|
||||||
for (const LambdaCapture &Capture : Lambda->captures()) {
|
for (const LambdaCapture &Capture : Lambda->captures()) {
|
||||||
if (Capture.capturesVariable()) {
|
if (Capture.capturesVariable()) {
|
||||||
QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
|
QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
|
||||||
|
|
||||||
if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
|
if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
|
||||||
emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee);
|
emitDiagnostics(Capture.getLocation(),
|
||||||
|
Capture.getCapturedVar()->getName(), Pointee);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,11 +128,11 @@ void RefCountedInsideLambdaChecker::check(
|
|||||||
// expresses the intent that the lambda won't leave the enclosing scope.
|
// expresses the intent that the lambda won't leave the enclosing scope.
|
||||||
bool ImplicitByRefDefaultedCapture =
|
bool ImplicitByRefDefaultedCapture =
|
||||||
Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
|
Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
|
||||||
if (Capture.capturesThis() &&
|
if (Capture.capturesThis() && !ImplicitByRefDefaultedCapture &&
|
||||||
!ImplicitByRefDefaultedCapture &&
|
|
||||||
!StrongRefToThisCaptured) {
|
!StrongRefToThisCaptured) {
|
||||||
ThisVisitor V(*this);
|
ThisVisitor V(*this);
|
||||||
bool NotAborted = V.TraverseDecl(const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
|
bool NotAborted = V.TraverseDecl(
|
||||||
|
const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
|
||||||
if (!NotAborted) {
|
if (!NotAborted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,7 +140,8 @@ void RefCountedInsideLambdaChecker::check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) {
|
bool RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(
|
||||||
|
CXXThisExpr *This) {
|
||||||
QualType Pointee = This->getType()->getPointeeType();
|
QualType Pointee = This->getType()->getPointeeType();
|
||||||
if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
|
if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
|
||||||
Checker.emitDiagnostics(This->getLocStart(), "this", Pointee);
|
Checker.emitDiagnostics(This->getLocStart(), "this", Pointee);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ private:
|
|||||||
: Checker(Checker) {}
|
: Checker(Checker) {}
|
||||||
|
|
||||||
bool VisitCXXThisExpr(CXXThisExpr *This);
|
bool VisitCXXThisExpr(CXXThisExpr *This);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RefCountedInsideLambdaChecker &Checker;
|
RefCountedInsideLambdaChecker &Checker;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ void ScopeChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
AstMatcher->addMatcher(cxxNewExpr().bind("node"), this);
|
AstMatcher->addMatcher(cxxNewExpr().bind("node"), this);
|
||||||
AstMatcher->addMatcher(materializeTemporaryExpr().bind("node"), this);
|
AstMatcher->addMatcher(materializeTemporaryExpr().bind("node"), this);
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
|
callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this);
|
||||||
this);
|
|
||||||
AstMatcher->addMatcher(parmVarDecl().bind("parm_vardecl"), this);
|
AstMatcher->addMatcher(parmVarDecl().bind("parm_vardecl"), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +30,7 @@ typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *>
|
|||||||
AutomaticTemporaryMap;
|
AutomaticTemporaryMap;
|
||||||
AutomaticTemporaryMap AutomaticTemporaries;
|
AutomaticTemporaryMap AutomaticTemporaries;
|
||||||
|
|
||||||
void ScopeChecker::check(
|
void ScopeChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
// There are a variety of different reasons why something could be allocated
|
// There are a variety of different reasons why something could be allocated
|
||||||
AllocationVariety Variety = AV_None;
|
AllocationVariety Variety = AV_None;
|
||||||
SourceLocation Loc;
|
SourceLocation Loc;
|
||||||
@@ -120,25 +118,17 @@ void ScopeChecker::check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error messages for incorrect allocations.
|
// Error messages for incorrect allocations.
|
||||||
const char* Stack =
|
const char *Stack = "variable of type %0 only valid on the stack";
|
||||||
"variable of type %0 only valid on the stack";
|
const char *Global = "variable of type %0 only valid as global";
|
||||||
const char* Global =
|
const char *Heap = "variable of type %0 only valid on the heap";
|
||||||
"variable of type %0 only valid as global";
|
const char *NonHeap = "variable of type %0 is not valid on the heap";
|
||||||
const char* Heap =
|
const char *NonTemporary = "variable of type %0 is not valid in a temporary";
|
||||||
"variable of type %0 only valid on the heap";
|
|
||||||
const char* NonHeap =
|
|
||||||
"variable of type %0 is not valid on the heap";
|
|
||||||
const char* NonTemporary =
|
|
||||||
"variable of type %0 is not valid in a temporary";
|
|
||||||
|
|
||||||
const char *StackNote =
|
const char *StackNote =
|
||||||
"value incorrectly allocated in an automatic variable";
|
"value incorrectly allocated in an automatic variable";
|
||||||
const char* GlobalNote =
|
const char *GlobalNote = "value incorrectly allocated in a global variable";
|
||||||
"value incorrectly allocated in a global variable";
|
const char *HeapNote = "value incorrectly allocated on the heap";
|
||||||
const char* HeapNote =
|
const char *TemporaryNote = "value incorrectly allocated in a temporary";
|
||||||
"value incorrectly allocated on the heap";
|
|
||||||
const char* TemporaryNote =
|
|
||||||
"value incorrectly allocated in a temporary";
|
|
||||||
|
|
||||||
// Report errors depending on the annotations on the input types.
|
// Report errors depending on the annotations on the input types.
|
||||||
switch (Variety) {
|
switch (Variety) {
|
||||||
|
|||||||
@@ -7,19 +7,22 @@
|
|||||||
|
|
||||||
void SprintfLiteralChecker::registerMatchers(MatchFinder *AstMatcher) {
|
void SprintfLiteralChecker::registerMatchers(MatchFinder *AstMatcher) {
|
||||||
AstMatcher->addMatcher(
|
AstMatcher->addMatcher(
|
||||||
callExpr(isSnprintfLikeFunc(),
|
callExpr(
|
||||||
allOf(hasArgument(0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
|
isSnprintfLikeFunc(),
|
||||||
anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(declRefExpr().bind("size")))),
|
allOf(hasArgument(
|
||||||
|
0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
|
||||||
|
anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(
|
||||||
|
declRefExpr().bind("size")))),
|
||||||
hasArgument(1, integerLiteral().bind("immediate")),
|
hasArgument(1, integerLiteral().bind("immediate")),
|
||||||
hasArgument(1, declRefExpr(to(varDecl(hasType(isConstQualified()),
|
hasArgument(1, declRefExpr(to(varDecl(
|
||||||
hasInitializer(integerLiteral().bind("constant")))))))))
|
hasType(isConstQualified()),
|
||||||
|
hasInitializer(integerLiteral().bind(
|
||||||
|
"constant")))))))))
|
||||||
.bind("funcCall"),
|
.bind("funcCall"),
|
||||||
this
|
this);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SprintfLiteralChecker::check(
|
void SprintfLiteralChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
|
||||||
if (!Result.Context->getLangOpts().CPlusPlus) {
|
if (!Result.Context->getLangOpts().CPlusPlus) {
|
||||||
// SprintfLiteral is not usable in C, so there is no point in issuing these
|
// SprintfLiteral is not usable in C, so there is no point in issuing these
|
||||||
// warnings.
|
// warnings.
|
||||||
@@ -56,21 +59,25 @@ void SprintfLiteralChecker::check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QualType QType = Buffer->getType();
|
const QualType QType = Buffer->getType();
|
||||||
const ConstantArrayType *Type = dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
|
const ConstantArrayType *Type =
|
||||||
|
dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
|
||||||
if (Type) {
|
if (Type) {
|
||||||
// Match calls like snprintf(x, 100, ...), where x is int[100];
|
// Match calls like snprintf(x, 100, ...), where x is int[100];
|
||||||
const IntegerLiteral *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
|
const IntegerLiteral *Literal =
|
||||||
|
Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
|
||||||
if (!Literal) {
|
if (!Literal) {
|
||||||
// Match calls like: const int y = 100; snprintf(x, y, ...);
|
// Match calls like: const int y = 100; snprintf(x, y, ...);
|
||||||
Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
|
Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to assume here that the bitwidth of both of these values fits within 64 bits.
|
// We're going to assume here that the bitwidth of both of these values fits
|
||||||
// and zero-extend both values to 64-bits before comparing them.
|
// within 64 bits. and zero-extend both values to 64-bits before comparing
|
||||||
|
// them.
|
||||||
uint64_t Size = Type->getSize().getZExtValue();
|
uint64_t Size = Type->getSize().getZExtValue();
|
||||||
uint64_t Lit = Literal->getValue().getZExtValue();
|
uint64_t Lit = Literal->getValue().getZExtValue();
|
||||||
if (Size <= Lit) {
|
if (Size <= Lit) {
|
||||||
diag(D->getLocStart(), Error, DiagnosticIDs::Error) << Name << Replacement;
|
diag(D->getLocStart(), Error, DiagnosticIDs::Error)
|
||||||
|
<< Name << Replacement;
|
||||||
diag(D->getLocStart(), Note, DiagnosticIDs::Note) << Name;
|
diag(D->getLocStart(), Note, DiagnosticIDs::Note) << Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class SprintfLiteralChecker : public BaseCheck {
|
class SprintfLiteralChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
SprintfLiteralChecker(StringRef CheckName,
|
SprintfLiteralChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ inline SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class is a modified version of the class from clang-tidy's ExprSequence.cpp
|
// This class is a modified version of the class from clang-tidy's
|
||||||
|
// ExprSequence.cpp
|
||||||
//
|
//
|
||||||
// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be
|
// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be
|
||||||
// contained in more than one `CFGBlock`; in this case, they are mapped to the
|
// contained in more than one `CFGBlock`; in this case, they are mapped to the
|
||||||
@@ -48,7 +49,8 @@ inline SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
|
|||||||
class StmtToBlockMap {
|
class StmtToBlockMap {
|
||||||
public:
|
public:
|
||||||
// Initializes the map for the given `CFG`.
|
// Initializes the map for the given `CFG`.
|
||||||
StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext) : Context(TheContext) {
|
StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
|
||||||
|
: Context(TheContext) {
|
||||||
for (const auto *B : *TheCFG) {
|
for (const auto *B : *TheCFG) {
|
||||||
for (size_t I = 0; I < B->size(); ++I) {
|
for (size_t I = 0; I < B->size(); ++I) {
|
||||||
if (Optional<CFGStmt> S = (*B)[I].getAs<CFGStmt>()) {
|
if (Optional<CFGStmt> S = (*B)[I].getAs<CFGStmt>()) {
|
||||||
@@ -64,7 +66,8 @@ public:
|
|||||||
//
|
//
|
||||||
// The optional outparameter `Index` is set to the index into the block where
|
// The optional outparameter `Index` is set to the index into the block where
|
||||||
// the `Stmt` was found.
|
// the `Stmt` was found.
|
||||||
const CFGBlock *blockContainingStmt(const Stmt *S, size_t *Index = nullptr) const {
|
const CFGBlock *blockContainingStmt(const Stmt *S,
|
||||||
|
size_t *Index = nullptr) const {
|
||||||
while (!Map.count(S)) {
|
while (!Map.count(S)) {
|
||||||
SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
|
SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
|
||||||
if (Parents.empty())
|
if (Parents.empty())
|
||||||
@@ -73,7 +76,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto &E = Map.lookup(S);
|
const auto &E = Map.lookup(S);
|
||||||
if (Index) *Index = E.second;
|
if (Index)
|
||||||
|
*Index = E.second;
|
||||||
return E.first;
|
return E.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ void TrivialCtorDtorChecker::registerMatchers(MatchFinder* AstMatcher) {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrivialCtorDtorChecker::check(
|
void TrivialCtorDtorChecker::check(const MatchFinder::MatchResult &Result) {
|
||||||
const MatchFinder::MatchResult &Result) {
|
const char *Error = "class %0 must have trivial constructors and destructors";
|
||||||
const char* Error =
|
|
||||||
"class %0 must have trivial constructors and destructors";
|
|
||||||
const CXXRecordDecl *Node = Result.Nodes.getNodeAs<CXXRecordDecl>("node");
|
const CXXRecordDecl *Node = Result.Nodes.getNodeAs<CXXRecordDecl>("node");
|
||||||
|
|
||||||
if (!Node->hasDefinition()) {
|
if (!Node->hasDefinition()) {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class TrivialCtorDtorChecker : public BaseCheck {
|
class TrivialCtorDtorChecker : public BaseCheck {
|
||||||
public:
|
public:
|
||||||
TrivialCtorDtorChecker(StringRef CheckName,
|
TrivialCtorDtorChecker(StringRef CheckName, ContextType *Context = nullptr)
|
||||||
ContextType *Context = nullptr)
|
|
||||||
: BaseCheck(CheckName, Context) {}
|
: BaseCheck(CheckName, Context) {}
|
||||||
void registerMatchers(MatchFinder *AstMatcher) override;
|
void registerMatchers(MatchFinder *AstMatcher) override;
|
||||||
void check(const MatchFinder::MatchResult &Result) override;
|
void check(const MatchFinder::MatchResult &Result) override;
|
||||||
|
|||||||
@@ -472,7 +472,8 @@ inline bool isInfixBinaryOp(const CXXOperatorCallExpr* OpCall) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch (OpCall->getOperator()) {
|
switch (OpCall->getOperator()) {
|
||||||
case OO_Call: case OO_Subscript:
|
case OO_Call:
|
||||||
|
case OO_Subscript:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
@@ -480,5 +481,4 @@ inline bool isInfixBinaryOp(const CXXOperatorCallExpr* OpCall) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#include "VariableUsageHelpers.h"
|
#include "VariableUsageHelpers.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
std::vector<const Stmt*>
|
std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration,
|
||||||
getUsageAsRvalue(const ValueDecl* ValueDeclaration,
|
|
||||||
const FunctionDecl *FuncDecl) {
|
const FunctionDecl *FuncDecl) {
|
||||||
std::vector<const Stmt *> UsageStatements;
|
std::vector<const Stmt *> UsageStatements;
|
||||||
|
|
||||||
@@ -14,9 +13,8 @@ getUsageAsRvalue(const ValueDecl* ValueDeclaration,
|
|||||||
|
|
||||||
// We build a Control Flow Graph (CFG) fron the body of the function
|
// We build a Control Flow Graph (CFG) fron the body of the function
|
||||||
// declaration.
|
// declaration.
|
||||||
std::unique_ptr<CFG> StatementCFG
|
std::unique_ptr<CFG> StatementCFG = CFG::buildCFG(
|
||||||
= CFG::buildCFG(FuncDecl, Body, &FuncDecl->getASTContext(),
|
FuncDecl, Body, &FuncDecl->getASTContext(), CFG::BuildOptions());
|
||||||
CFG::BuildOptions());
|
|
||||||
|
|
||||||
// We iterate through all the CFGBlocks, which basically means that we go over
|
// We iterate through all the CFGBlocks, which basically means that we go over
|
||||||
// all the possible branches of the code and therefore cover all statements.
|
// all the possible branches of the code and therefore cover all statements.
|
||||||
@@ -71,30 +69,24 @@ getUsageAsRvalue(const ValueDecl* ValueDeclaration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We declare our EscapesFunctionError enum to be an error code enum.
|
// We declare our EscapesFunctionError enum to be an error code enum.
|
||||||
namespace std
|
namespace std {
|
||||||
{
|
template <> struct is_error_code_enum<EscapesFunctionError> : true_type {};
|
||||||
template <>
|
} // namespace std
|
||||||
struct is_error_code_enum<EscapesFunctionError> : true_type {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// We define the EscapesFunctionErrorCategory which contains the error messages
|
// We define the EscapesFunctionErrorCategory which contains the error messages
|
||||||
// corresponding to each enum variant.
|
// corresponding to each enum variant.
|
||||||
namespace {
|
namespace {
|
||||||
struct EscapesFunctionErrorCategory : std::error_category
|
struct EscapesFunctionErrorCategory : std::error_category {
|
||||||
{
|
|
||||||
const char *name() const noexcept override;
|
const char *name() const noexcept override;
|
||||||
std::string message(int ev) const override;
|
std::string message(int ev) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* EscapesFunctionErrorCategory::name() const noexcept
|
const char *EscapesFunctionErrorCategory::name() const noexcept {
|
||||||
{
|
|
||||||
return "escapes function";
|
return "escapes function";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EscapesFunctionErrorCategory::message(int ev) const
|
std::string EscapesFunctionErrorCategory::message(int ev) const {
|
||||||
{
|
switch (static_cast<EscapesFunctionError>(ev)) {
|
||||||
switch (static_cast<EscapesFunctionError>(ev))
|
|
||||||
{
|
|
||||||
case EscapesFunctionError::ConstructorDeclNotFound:
|
case EscapesFunctionError::ConstructorDeclNotFound:
|
||||||
return "constructor declaration not found";
|
return "constructor declaration not found";
|
||||||
|
|
||||||
@@ -119,10 +111,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{};
|
const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{};
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
std::error_code make_error_code(EscapesFunctionError e)
|
std::error_code make_error_code(EscapesFunctionError e) {
|
||||||
{
|
|
||||||
return {static_cast<int>(e), TheEscapesFunctionErrorCategory};
|
return {static_cast<int>(e), TheEscapesFunctionErrorCategory};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +247,8 @@ escapesFunction(const Expr* Arg, const FunctionDecl *FuncDecl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(Usage, (const Decl *)VarDeclaration);
|
return std::make_tuple(Usage, (const Decl *)VarDeclaration);
|
||||||
} else if (auto FieldDeclaration = dyn_cast<FieldDecl>(DeclRef->getDecl())) {
|
} else if (auto FieldDeclaration =
|
||||||
|
dyn_cast<FieldDecl>(DeclRef->getDecl())) {
|
||||||
// This is the case where the parameter escapes through a field.
|
// This is the case where the parameter escapes through a field.
|
||||||
|
|
||||||
return std::make_tuple(Usage, (const Decl *)FieldDeclaration);
|
return std::make_tuple(Usage, (const Decl *)FieldDeclaration);
|
||||||
@@ -264,8 +256,8 @@ escapesFunction(const Expr* Arg, const FunctionDecl *FuncDecl,
|
|||||||
} else if (isa<ReturnStmt>(Usage)) {
|
} else if (isa<ReturnStmt>(Usage)) {
|
||||||
// This is the case where the parameter escapes through the return value
|
// This is the case where the parameter escapes through the return value
|
||||||
// of the function.
|
// of the function.
|
||||||
if (!FuncDecl->getReturnType()->isPointerType()
|
if (!FuncDecl->getReturnType()->isPointerType() &&
|
||||||
&& !FuncDecl->getReturnType()->isReferenceType()) {
|
!FuncDecl->getReturnType()->isReferenceType()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|
||||||
#ifndef VariableUsageHelpers_h__
|
#ifndef VariableUsageHelpers_h__
|
||||||
#define VariableUsageHelpers_h__
|
#define VariableUsageHelpers_h__
|
||||||
|
|
||||||
@@ -14,8 +13,7 @@
|
|||||||
/// WARNING: incomplete behaviour/implementation for general-purpose use outside
|
/// WARNING: incomplete behaviour/implementation for general-purpose use outside
|
||||||
/// of escapesFunction(). This only detects very basic usages (see
|
/// of escapesFunction(). This only detects very basic usages (see
|
||||||
/// implementation for more details).
|
/// implementation for more details).
|
||||||
std::vector<const Stmt*>
|
std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration,
|
||||||
getUsageAsRvalue(const ValueDecl* ValueDeclaration,
|
|
||||||
const FunctionDecl *FuncDecl);
|
const FunctionDecl *FuncDecl);
|
||||||
|
|
||||||
/// This is the error enumeration for escapesFunction(), describing all the
|
/// This is the error enumeration for escapesFunction(), describing all the
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
#ifndef plugin_h__
|
#ifndef plugin_h__
|
||||||
#define plugin_h__
|
#define plugin_h__
|
||||||
|
|
||||||
#include "clang/Analysis/CFG.h"
|
|
||||||
#include "clang/AST/ASTConsumer.h"
|
#include "clang/AST/ASTConsumer.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/RecursiveASTVisitor.h"
|
#include "clang/AST/RecursiveASTVisitor.h"
|
||||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||||
|
#include "clang/Analysis/CFG.h"
|
||||||
#include "clang/Basic/Version.h"
|
#include "clang/Basic/Version.h"
|
||||||
#include "clang/Frontend/CompilerInstance.h"
|
#include "clang/Frontend/CompilerInstance.h"
|
||||||
#include "clang/Frontend/MultiplexConsumer.h"
|
#include "clang/Frontend/MultiplexConsumer.h"
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include <memory>
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
|
#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
|
||||||
|
|
||||||
@@ -54,9 +54,9 @@ typedef ASTConsumer *ASTConsumerPtr;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// In order to support running our checks using clang-tidy, we implement a source
|
// In order to support running our checks using clang-tidy, we implement a
|
||||||
// compatible base check class called BaseCheck, and we use the preprocessor to
|
// source compatible base check class called BaseCheck, and we use the
|
||||||
// decide which base class to pick.
|
// preprocessor to decide which base class to pick.
|
||||||
#ifdef CLANG_TIDY
|
#ifdef CLANG_TIDY
|
||||||
#include "../ClangTidy.h"
|
#include "../ClangTidy.h"
|
||||||
typedef clang::tidy::ClangTidyCheck BaseCheck;
|
typedef clang::tidy::ClangTidyCheck BaseCheck;
|
||||||
|
|||||||
Reference in New Issue
Block a user