Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / cppunitassertequals.cxx
blobdf50f57537bc6a5d9d69d38c1cf4e70efe8ebaaa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
11 #include "plugin.hxx"
12 #include "check.hxx"
13 #include "compat.hxx"
14 #include <iostream>
16 /**
17 Check for
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
22 namespace {
24 class CppunitAssertEquals:
25 public loplugin::FilteringPlugin<CppunitAssertEquals>
27 public:
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
38 if (preRun()) {
39 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
43 bool VisitCallExpr(const CallExpr*);
45 private:
46 void checkExpr(
47 SourceRange range, StringRef name, Expr const * expr, bool negated);
49 void reportEquals(
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();
58 if (!decl)
59 return true;
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)
74 || ignoreLocation(
75 compiler.getSourceManager().getImmediateMacroCallerLoc(loc)))
77 return true;
79 auto name = Lexer::getImmediateMacroName(
80 loc, compiler.getSourceManager(), compiler.getLangOpts());
81 if (name != "CPPUNIT_ASSERT" && name != "CPPUNIT_ASSERT_MESSAGE") {
82 return true;
84 if (decl->getNumParams() != 3) {
85 report(
86 DiagnosticsEngine::Warning,
87 ("TODO: suspicious CppUnit::Asserter::failIf call with %0"
88 " parameters"),
89 callExpr->getExprLoc())
90 << decl->getNumParams() << callExpr->getSourceRange();
91 return true;
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) {
101 e2 = e4->getArg(0);
104 if (e2 == nullptr) {
105 report(
106 DiagnosticsEngine::Warning,
107 ("TODO: suspicious CppUnit::Asserter::failIf call not wrapping"
108 " !(...)"),
109 callExpr->getExprLoc())
110 << callExpr->getSourceRange();
111 return true;
113 auto range = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc);
114 checkExpr(
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)))
127 return true;
128 if (isCompileTimeConstant(callExpr->getArg(1)))
129 report(
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)))
140 return true;
141 if (isCompileTimeConstant(callExpr->getArg(1)))
142 report(
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)))
153 return true;
154 if (isCompileTimeConstant(callExpr->getArg(1)))
155 report(
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)))
166 return true;
167 if (isCompileTimeConstant(callExpr->getArg(1)))
168 report(
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)))
179 return true;
180 if (isCompileTimeConstant(callExpr->getArg(1)))
181 report(
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)))
192 return true;
193 if (isCompileTimeConstant(callExpr->getArg(1)))
194 report(
195 DiagnosticsEngine::Warning,
196 "CPPUNIT_ASSERT_GREATEREQUAL parameters look switched, expected value should be first param",
197 callExpr->getExprLoc())
198 << callExpr->getSourceRange();
200 return true;
203 // copied from stringconcat.cxx
204 Expr const * stripConstructor(Expr const * expr) {
205 auto e0 = expr;
206 auto const e1 = dyn_cast<CXXFunctionalCastExpr>(e0);
207 if (e1 != nullptr) {
208 e0 = e1->getSubExpr()->IgnoreParenImpCasts();
210 auto const e2 = dyn_cast<CXXBindTemporaryExpr>(e0);
211 if (e2 == nullptr) {
212 return expr;
214 auto const e3 = dyn_cast<CXXConstructExpr>(
215 e2->getSubExpr()->IgnoreParenImpCasts());
216 if (e3 == nullptr) {
217 return expr;
219 auto qt = loplugin::DeclCheck(e3->getConstructor());
220 if (!((qt.MemberFunction().Class("OString").Namespace("rtl")
221 .GlobalNamespace())
222 || (qt.MemberFunction().Class("OUString").Namespace("rtl")
223 .GlobalNamespace())))
225 return expr;
227 if (e3->getNumArgs() != 2) {
228 return expr;
230 return e3->getArg(0)->IgnoreParenImpCasts();
233 bool CppunitAssertEquals::isCompileTimeConstant(Expr const * expr)
235 if (expr->isCXX11ConstantExpr(compiler.getASTContext()))
236 return true;
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) {
248 checkExpr(
249 range, name, e->getSubExpr()->IgnoreParenImpCasts(), !negated);
251 return;
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());
257 return;
259 if ((!negated && op == BO_LAnd) || (negated && op == BO_LOr)) {
260 report(
261 DiagnosticsEngine::Warning,
262 "rather split into two %0", e->getExprLoc())
263 << name << range;
264 return;
266 return;
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));
274 return;
276 return;
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())
286 return;
288 report(
289 DiagnosticsEngine::Warning,
290 ("rather call"
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)"),
294 range.getBegin())
295 << (name == "CPPUNIT_ASSERT_MESSAGE") << lhs->IgnoreImpCasts()->getType()
296 << rhs->IgnoreImpCasts()->getType() << negative << range;
299 loplugin::Plugin::Registration< CppunitAssertEquals > cppunitassertequals("cppunitassertequals");
301 } // namespace
303 #endif // LO_CLANG_SHARED_PLUGINS
305 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */