1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 #ifndef LO_CLANG_SHARED_PLUGINS
18 (*) calls to CPPUNIT_ASSERT when it should be using CPPUNIT_ASSERT_EQUALS
19 (*) calls to CPPUNIT_ASSERT_EQUALS where the constant is the second param
24 class CppunitAssertEquals
:
25 public loplugin::FilteringPlugin
<CppunitAssertEquals
>
28 explicit CppunitAssertEquals(loplugin::InstantiationData
const & data
):
29 FilteringPlugin(data
) {}
31 virtual bool preRun() override
33 return compiler
.getLangOpts().CPlusPlus
;
36 virtual void run() override
39 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
43 bool VisitCallExpr(const CallExpr
*);
47 SourceRange range
, StringRef name
, Expr
const * expr
, bool negated
);
50 SourceRange range
, StringRef name
, bool negative
, Expr
const * lhs
, Expr
const * rhs
);
52 bool isCompileTimeConstant(Expr
const * expr
);
55 bool CppunitAssertEquals::VisitCallExpr(const CallExpr
* callExpr
)
57 auto const decl
= callExpr
->getDirectCallee();
61 calls to CPPUNIT_ASSERT when it should be using CPPUNIT_ASSERT_EQUALS
63 if (loplugin::DeclCheck(decl
).Function("failIf").Struct("Asserter")
64 .Namespace("CppUnit").GlobalNamespace())
66 // Don't use callExpr->getLocStart() or callExpr->getExprLoc(), as those
67 // fall into a nested use of the CPPUNIT_NS macro; CallExpr::getRParenLoc
68 // happens to be readily available and cause good results:
69 auto loc
= callExpr
->getRParenLoc();
70 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
71 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
73 if (!compiler
.getSourceManager().isMacroBodyExpansion(loc
)
75 compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
)))
79 auto name
= Lexer::getImmediateMacroName(
80 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
81 if (name
!= "CPPUNIT_ASSERT" && name
!= "CPPUNIT_ASSERT_MESSAGE") {
84 if (decl
->getNumParams() != 3) {
86 DiagnosticsEngine::Warning
,
87 ("TODO: suspicious CppUnit::Asserter::failIf call with %0"
89 callExpr
->getExprLoc())
90 << decl
->getNumParams() << callExpr
->getSourceRange();
93 auto const e1
= callExpr
->getArg(0)->IgnoreParenImpCasts();
94 Expr
const * e2
= nullptr;
95 if (auto const e3
= dyn_cast
<UnaryOperator
>(e1
)) {
96 if (e3
->getOpcode() == UO_LNot
) {
97 e2
= e3
->getSubExpr();
99 } else if (auto const e4
= dyn_cast
<CXXOperatorCallExpr
>(e1
)) {
100 if (e4
->getOperator() == OO_Exclaim
) {
106 DiagnosticsEngine::Warning
,
107 ("TODO: suspicious CppUnit::Asserter::failIf call not wrapping"
109 callExpr
->getExprLoc())
110 << callExpr
->getSourceRange();
113 auto range
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), loc
);
115 SourceRange(range
.first
, range
.second
), name
,
116 e2
->IgnoreParenImpCasts(), false);
120 Check for calls to CPPUNIT_ASSERT_EQUALS where the constant is the second param
122 if (loplugin::DeclCheck(decl
).Function("assertEquals").
123 Namespace("CppUnit").GlobalNamespace())
125 // can happen in template test code that both params are compile time constants
126 if (isCompileTimeConstant(callExpr
->getArg(0)))
128 if (isCompileTimeConstant(callExpr
->getArg(1)))
130 DiagnosticsEngine::Warning
,
131 "CPPUNIT_ASSERT_EQUALS parameters look switched, expected value should be first param",
132 callExpr
->getExprLoc())
133 << callExpr
->getSourceRange();
135 if (loplugin::DeclCheck(decl
).Function("assertDoubleEquals").
136 Namespace("CppUnit").GlobalNamespace())
138 // can happen in template test code that both params are compile time constants
139 if (isCompileTimeConstant(callExpr
->getArg(0)))
141 if (isCompileTimeConstant(callExpr
->getArg(1)))
143 DiagnosticsEngine::Warning
,
144 "CPPUNIT_ASSERT_DOUBLES_EQUALS parameters look switched, expected value should be first param",
145 callExpr
->getExprLoc())
146 << callExpr
->getSourceRange();
148 if (loplugin::DeclCheck(decl
).Function("assertLess").
149 Namespace("CppUnit").GlobalNamespace())
151 // can happen in template test code that both params are compile time constants
152 if (isCompileTimeConstant(callExpr
->getArg(0)))
154 if (isCompileTimeConstant(callExpr
->getArg(1)))
156 DiagnosticsEngine::Warning
,
157 "CPPUNIT_ASSERT_LESS parameters look switched, expected value should be first param",
158 callExpr
->getExprLoc())
159 << callExpr
->getSourceRange();
161 if (loplugin::DeclCheck(decl
).Function("assertLessEqual").
162 Namespace("CppUnit").GlobalNamespace())
164 // can happen in template test code that both params are compile time constants
165 if (isCompileTimeConstant(callExpr
->getArg(0)))
167 if (isCompileTimeConstant(callExpr
->getArg(1)))
169 DiagnosticsEngine::Warning
,
170 "CPPUNIT_ASSERT_LESSEQUAL parameters look switched, expected value should be first param",
171 callExpr
->getExprLoc())
172 << callExpr
->getSourceRange();
174 if (loplugin::DeclCheck(decl
).Function("assertGreater").
175 Namespace("CppUnit").GlobalNamespace())
177 // can happen in template test code that both params are compile time constants
178 if (isCompileTimeConstant(callExpr
->getArg(0)))
180 if (isCompileTimeConstant(callExpr
->getArg(1)))
182 DiagnosticsEngine::Warning
,
183 "CPPUNIT_ASSERT_GREATER parameters look switched, expected value should be first param",
184 callExpr
->getExprLoc())
185 << callExpr
->getSourceRange();
187 if (loplugin::DeclCheck(decl
).Function("assertGreaterEqual").
188 Namespace("CppUnit").GlobalNamespace())
190 // can happen in template test code that both params are compile time constants
191 if (isCompileTimeConstant(callExpr
->getArg(0)))
193 if (isCompileTimeConstant(callExpr
->getArg(1)))
195 DiagnosticsEngine::Warning
,
196 "CPPUNIT_ASSERT_GREATEREQUAL parameters look switched, expected value should be first param",
197 callExpr
->getExprLoc())
198 << callExpr
->getSourceRange();
203 // copied from stringconcat.cxx
204 Expr
const * stripConstructor(Expr
const * expr
) {
206 auto const e1
= dyn_cast
<CXXFunctionalCastExpr
>(e0
);
208 e0
= e1
->getSubExpr()->IgnoreParenImpCasts();
210 auto const e2
= dyn_cast
<CXXBindTemporaryExpr
>(e0
);
214 auto const e3
= dyn_cast
<CXXConstructExpr
>(
215 e2
->getSubExpr()->IgnoreParenImpCasts());
219 auto qt
= loplugin::DeclCheck(e3
->getConstructor());
220 if (!((qt
.MemberFunction().Class("OString").Namespace("rtl")
222 || (qt
.MemberFunction().Class("OUString").Namespace("rtl")
223 .GlobalNamespace())))
227 if (e3
->getNumArgs() != 2) {
230 return e3
->getArg(0)->IgnoreParenImpCasts();
233 bool CppunitAssertEquals::isCompileTimeConstant(Expr
const * expr
)
235 if (expr
->isCXX11ConstantExpr(compiler
.getASTContext()))
237 // is string literal ?
238 expr
= expr
->IgnoreParenImpCasts();
239 expr
= stripConstructor(expr
);
240 return isa
<clang::StringLiteral
>(expr
);
243 void CppunitAssertEquals::checkExpr(
244 SourceRange range
, StringRef name
, Expr
const * expr
, bool negated
)
246 if (auto const e
= dyn_cast
<UnaryOperator
>(expr
)) {
247 if (e
->getOpcode() == UO_LNot
) {
249 range
, name
, e
->getSubExpr()->IgnoreParenImpCasts(), !negated
);
253 if (auto const e
= dyn_cast
<BinaryOperator
>(expr
)) {
254 auto const op
= e
->getOpcode();
255 if ((!negated
&& op
== BO_EQ
) || (negated
&& op
== BO_NE
)) {
256 reportEquals(range
, name
, op
== BO_NE
, e
->getLHS(), e
->getRHS());
259 if ((!negated
&& op
== BO_LAnd
) || (negated
&& op
== BO_LOr
)) {
261 DiagnosticsEngine::Warning
,
262 "rather split into two %0", e
->getExprLoc())
268 if (auto const e
= dyn_cast
<CXXOperatorCallExpr
>(expr
)) {
269 auto const op
= e
->getOperator();
270 if ((!negated
&& op
== OO_EqualEqual
)
271 || (negated
&& op
== OO_ExclaimEqual
))
273 reportEquals(range
, name
, op
== OO_ExclaimEqual
, e
->getArg(0), e
->getArg(1));
280 void CppunitAssertEquals::reportEquals(
281 SourceRange range
, StringRef name
, bool negative
, Expr
const * lhs
, Expr
const * rhs
)
283 if (lhs
->IgnoreImpCasts()->getType()->isNullPtrType()
284 != rhs
->IgnoreImpCasts()->getType()->isNullPtrType())
289 DiagnosticsEngine::Warning
,
291 " %select{CPPUNIT_ASSERT_EQUAL|CPPUNIT_ASSERT_EQUAL_MESSAGE}0 when comparing %1 and %2 (or"
292 " rewrite as an explicit operator %select{==|!=}3 call when the"
293 " operator itself is the topic)"),
295 << (name
== "CPPUNIT_ASSERT_MESSAGE") << lhs
->IgnoreImpCasts()->getType()
296 << rhs
->IgnoreImpCasts()->getType() << negative
<< range
;
299 loplugin::Plugin::Registration
< CppunitAssertEquals
> cppunitassertequals("cppunitassertequals");
303 #endif // LO_CLANG_SHARED_PLUGINS
305 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */