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();
138 // copied from stringconcat.cxx
139 Expr
const * stripConstructor(Expr
const * expr
) {
141 auto const e1
= dyn_cast
<CXXFunctionalCastExpr
>(e0
);
143 e0
= e1
->getSubExpr()->IgnoreParenImpCasts();
145 auto const e2
= dyn_cast
<CXXBindTemporaryExpr
>(e0
);
149 auto const e3
= dyn_cast
<CXXConstructExpr
>(
150 e2
->getSubExpr()->IgnoreParenImpCasts());
154 auto qt
= loplugin::DeclCheck(e3
->getConstructor());
155 if (!((qt
.MemberFunction().Class("OString").Namespace("rtl")
157 || (qt
.MemberFunction().Class("OUString").Namespace("rtl")
158 .GlobalNamespace())))
162 if (e3
->getNumArgs() != 2) {
165 return e3
->getArg(0)->IgnoreParenImpCasts();
168 bool CppunitAssertEquals::isCompileTimeConstant(Expr
const * expr
)
170 if (expr
->isIntegerConstantExpr(compiler
.getASTContext()))
172 // is string literal ?
173 expr
= expr
->IgnoreParenImpCasts();
174 expr
= stripConstructor(expr
);
175 return isa
<clang::StringLiteral
>(expr
);
178 void CppunitAssertEquals::checkExpr(
179 SourceRange range
, StringRef name
, Expr
const * expr
, bool negated
)
181 if (auto const e
= dyn_cast
<UnaryOperator
>(expr
)) {
182 if (e
->getOpcode() == UO_LNot
) {
184 range
, name
, e
->getSubExpr()->IgnoreParenImpCasts(), !negated
);
188 if (auto const e
= dyn_cast
<BinaryOperator
>(expr
)) {
189 auto const op
= e
->getOpcode();
190 if ((!negated
&& op
== BO_EQ
) || (negated
&& op
== BO_NE
)) {
191 reportEquals(range
, name
, op
== BO_NE
, e
->getLHS(), e
->getRHS());
194 #if 0 // TODO: enable later
195 if ((!negated
&& op
== BO_LAnd
) || (negated
&& op
== BO_LOr
)) {
197 DiagnosticsEngine::Warning
,
198 "rather split into two %0", e
->getExprLoc())
205 if (auto const e
= dyn_cast
<CXXOperatorCallExpr
>(expr
)) {
206 auto const op
= e
->getOperator();
207 if ((!negated
&& op
== OO_EqualEqual
)
208 || (negated
&& op
== OO_ExclaimEqual
))
210 reportEquals(range
, name
, op
== OO_ExclaimEqual
, e
->getArg(0), e
->getArg(1));
217 void CppunitAssertEquals::reportEquals(
218 SourceRange range
, StringRef name
, bool negative
, Expr
const * lhs
, Expr
const * rhs
)
220 if (lhs
->IgnoreImpCasts()->getType()->isNullPtrType()
221 != rhs
->IgnoreImpCasts()->getType()->isNullPtrType())
226 DiagnosticsEngine::Warning
,
228 " %select{CPPUNIT_ASSERT_EQUAL|CPPUNIT_ASSERT_EQUAL_MESSAGE}0 (or"
229 " rewrite as an explicit operator %select{==|!=}1 call when the"
230 " operator itself is the topic)"),
232 << (name
== "CPPUNIT_ASSERT_MESSAGE") << negative
<< range
;
235 loplugin::Plugin::Registration
< CppunitAssertEquals
> cppunitassertequals("cppunitassertequals");
239 #endif // LO_CLANG_SHARED_PLUGINS
241 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */