1 //===--- AssertSideEffectCheck.cpp - clang-tidy ---------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "AssertSideEffectCheck.h"
10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Casting.h"
22 using namespace clang::ast_matchers
;
24 namespace clang::tidy::bugprone
{
28 AST_MATCHER_P2(Expr
, hasSideEffect
, bool, CheckFunctionCalls
,
29 clang::ast_matchers::internal::Matcher
<NamedDecl
>,
30 IgnoredFunctionsMatcher
) {
31 const Expr
*E
= &Node
;
33 if (const auto *Op
= dyn_cast
<UnaryOperator
>(E
)) {
34 UnaryOperator::Opcode OC
= Op
->getOpcode();
35 return OC
== UO_PostInc
|| OC
== UO_PostDec
|| OC
== UO_PreInc
||
39 if (const auto *Op
= dyn_cast
<BinaryOperator
>(E
)) {
40 return Op
->isAssignmentOp();
43 if (const auto *OpCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(E
)) {
44 if (const auto *MethodDecl
=
45 dyn_cast_or_null
<CXXMethodDecl
>(OpCallExpr
->getDirectCallee()))
46 if (MethodDecl
->isConst())
49 OverloadedOperatorKind OpKind
= OpCallExpr
->getOperator();
50 return OpKind
== OO_Equal
|| OpKind
== OO_PlusEqual
||
51 OpKind
== OO_MinusEqual
|| OpKind
== OO_StarEqual
||
52 OpKind
== OO_SlashEqual
|| OpKind
== OO_AmpEqual
||
53 OpKind
== OO_PipeEqual
|| OpKind
== OO_CaretEqual
||
54 OpKind
== OO_LessLessEqual
|| OpKind
== OO_GreaterGreaterEqual
||
55 OpKind
== OO_LessLess
|| OpKind
== OO_GreaterGreater
||
56 OpKind
== OO_PlusPlus
|| OpKind
== OO_MinusMinus
||
57 OpKind
== OO_PercentEqual
|| OpKind
== OO_New
||
58 OpKind
== OO_Delete
|| OpKind
== OO_Array_New
||
59 OpKind
== OO_Array_Delete
;
62 if (const auto *CExpr
= dyn_cast
<CallExpr
>(E
)) {
63 if (!CheckFunctionCalls
)
65 if (const auto *FuncDecl
= CExpr
->getDirectCallee()) {
66 if (FuncDecl
->getDeclName().isIdentifier() &&
67 IgnoredFunctionsMatcher
.matches(*FuncDecl
, Finder
,
68 Builder
)) // exceptions come here
70 for (size_t I
= 0; I
< FuncDecl
->getNumParams(); I
++) {
71 const ParmVarDecl
*P
= FuncDecl
->getParamDecl(I
);
73 I
< CExpr
->getNumArgs() ? CExpr
->getArg(I
) : nullptr;
74 const QualType PT
= P
->getType().getCanonicalType();
75 if (ArgExpr
&& !ArgExpr
->isXValue() && PT
->isReferenceType() &&
76 !PT
.getNonReferenceType().isConstQualified())
79 if (const auto *MethodDecl
= dyn_cast
<CXXMethodDecl
>(FuncDecl
))
80 return !MethodDecl
->isConst();
85 return isa
<CXXNewExpr
>(E
) || isa
<CXXDeleteExpr
>(E
) || isa
<CXXThrowExpr
>(E
);
90 AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name
,
91 ClangTidyContext
*Context
)
92 : ClangTidyCheck(Name
, Context
),
93 CheckFunctionCalls(Options
.get("CheckFunctionCalls", false)),
94 RawAssertList(Options
.get("AssertMacros", "assert,NSAssert,NSCAssert")),
95 IgnoredFunctions(utils::options::parseListPair(
96 "__builtin_expect;", Options
.get("IgnoredFunctions", ""))) {
97 StringRef(RawAssertList
).split(AssertMacros
, ",", -1, false);
100 // The options are explained in AssertSideEffectCheck.h.
101 void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
102 Options
.store(Opts
, "CheckFunctionCalls", CheckFunctionCalls
);
103 Options
.store(Opts
, "AssertMacros", RawAssertList
);
104 Options
.store(Opts
, "IgnoredFunctions",
105 utils::options::serializeStringList(IgnoredFunctions
));
108 void AssertSideEffectCheck::registerMatchers(MatchFinder
*Finder
) {
109 auto IgnoredFunctionsMatcher
=
110 matchers::matchesAnyListedName(IgnoredFunctions
);
112 auto DescendantWithSideEffect
=
113 traverse(TK_AsIs
, hasDescendant(expr(hasSideEffect(
114 CheckFunctionCalls
, IgnoredFunctionsMatcher
))));
115 auto ConditionWithSideEffect
= hasCondition(DescendantWithSideEffect
);
118 anyOf(conditionalOperator(ConditionWithSideEffect
),
119 ifStmt(ConditionWithSideEffect
),
120 unaryOperator(hasOperatorName("!"),
121 hasUnaryOperand(unaryOperator(
122 hasOperatorName("!"),
123 hasUnaryOperand(DescendantWithSideEffect
))))))
128 void AssertSideEffectCheck::check(const MatchFinder::MatchResult
&Result
) {
129 const SourceManager
&SM
= *Result
.SourceManager
;
130 const LangOptions LangOpts
= getLangOpts();
131 SourceLocation Loc
= Result
.Nodes
.getNodeAs
<Stmt
>("condStmt")->getBeginLoc();
133 StringRef AssertMacroName
;
134 while (Loc
.isValid() && Loc
.isMacroID()) {
135 StringRef MacroName
= Lexer::getImmediateMacroName(Loc
, SM
, LangOpts
);
136 Loc
= SM
.getImmediateMacroCallerLoc(Loc
);
138 // Check if this macro is an assert.
139 if (llvm::is_contained(AssertMacros
, MacroName
)) {
140 AssertMacroName
= MacroName
;
144 if (AssertMacroName
.empty())
147 diag(Loc
, "side effect in %0() condition discarded in release builds")
151 } // namespace clang::tidy::bugprone