Kaydet (Commit) a528392e authored tarafından Stephan Bergmann's avatar Stephan Bergmann

Fixed/improved loplugin:cppunitassertequals

* 994e38e3 "loplugin: use TypeCheck instead of
  getQualifiedNameAsString" had effectively disabled this plugin (Asserter is a
  struct, not a namespace).  Fixed that.

* Also improved the checks, for one removing the---expensive---use of
  Plugin::parentStmt, for another making the plugin look into (...), !..., and
  ...&&... expressions.

* However, as the plugin had effectively already been disabled (see above) when
  it was switched on generally with 839e53b9
  "compilerplugins: enable loplugin:cppunitassertequals by default", it now hit
  way more places than I had initially anticipated.  To keep the amount of work
  manageable, midway-through I disabled looking into ...&&... expressions for
  now.  That will be enabled (and resulting warnings fixed) in follow-up
  commits.

* Checks like

    CPPUNIT_ASSERT(a == b)

  that actually want to check a specific overloaded operator == implementation,
  rather than using such an operator == to actually check that a and b are
  equal, can be rewritten as

    CPPUNIT_ASSERT(operator ==(a, b))

  to avoid false warnings from this plugin.

Change-Id: If3501020e2d150ad0f2454a65a39081e31470c0f
üst 29c09916
...@@ -7,11 +7,6 @@ ...@@ -7,11 +7,6 @@
* 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 <cassert>
#include <string>
#include <iostream>
#include <fstream>
#include <set>
#include "plugin.hxx" #include "plugin.hxx"
#include "check.hxx" #include "check.hxx"
...@@ -29,74 +24,133 @@ public: ...@@ -29,74 +24,133 @@ public:
virtual void run() override virtual void run() override
{ {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
} }
bool VisitCallExpr(const CallExpr*); bool VisitCallExpr(const CallExpr*);
bool VisitBinaryOperator(const BinaryOperator*);
private: private:
void checkExpr(const Stmt* stmt); void checkExpr(
SourceRange range, StringRef name, Expr const * expr, bool negated);
void reportEquals(SourceRange range, StringRef name, bool negative);
}; };
bool CppunitAssertEquals::VisitCallExpr(const CallExpr* callExpr) bool CppunitAssertEquals::VisitCallExpr(const CallExpr* callExpr)
{ {
if (ignoreLocation(callExpr)) { auto const decl = callExpr->getDirectCallee();
if (decl == nullptr
|| !(loplugin::DeclCheck(decl).Function("failIf").Struct("Asserter")
.Namespace("CppUnit").GlobalNamespace()))
{
return true; return true;
} }
if (callExpr->getDirectCallee() == nullptr) { // Don't use callExpr->getLocStart() or callExpr->getExprLoc(), as those
// fall into a nested use of the CPPUNIT_NS macro; CallExpr::getRParenLoc
// happens to be readily available and cause good results:
auto loc = callExpr->getRParenLoc();
while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
}
if (!compiler.getSourceManager().isMacroBodyExpansion(loc)
|| ignoreLocation(
compiler.getSourceManager().getImmediateMacroCallerLoc(loc)))
{
return true; return true;
} }
const FunctionDecl* functionDecl = callExpr->getDirectCallee()->getCanonicalDecl(); auto name = Lexer::getImmediateMacroName(
if (functionDecl->getOverloadedOperator() != OO_EqualEqual) { loc, compiler.getSourceManager(), compiler.getLangOpts());
if (name != "CPPUNIT_ASSERT" && name != "CPPUNIT_ASSERT_MESSAGE") {
return true; return true;
} }
checkExpr(callExpr); if (decl->getNumParams() != 3) {
return true; report(
} DiagnosticsEngine::Warning,
("TODO: suspicious CppUnit::Asserter::failIf call with %0"
bool CppunitAssertEquals::VisitBinaryOperator(const BinaryOperator* binaryOp) " parameters"),
{ callExpr->getExprLoc())
if (ignoreLocation(binaryOp)) { << decl->getNumParams() << callExpr->getSourceRange();
return true; return true;
} }
if (binaryOp->getOpcode() != BO_EQ) { auto const e1 = callExpr->getArg(0)->IgnoreParenImpCasts();
Expr const * e2 = nullptr;
if (auto const e3 = dyn_cast<UnaryOperator>(e1)) {
if (e3->getOpcode() == UO_LNot) {
e2 = e3->getSubExpr();
}
} else if (auto const e4 = dyn_cast<CXXOperatorCallExpr>(e1)) {
if (e4->getOperator() == OO_Exclaim) {
e2 = e4->getArg(0);
}
}
if (e2 == nullptr) {
report(
DiagnosticsEngine::Warning,
("TODO: suspicious CppUnit::Asserter::failIf call not wrapping"
" !(...)"),
callExpr->getExprLoc())
<< callExpr->getSourceRange();
return true; return true;
} }
checkExpr(binaryOp); auto range = compiler.getSourceManager().getImmediateExpansionRange(loc);
checkExpr(
SourceRange(range.first, range.second), name,
e2->IgnoreParenImpCasts(), false);
return true; return true;
} }
/* void CppunitAssertEquals::checkExpr(
* check that we are not a compound expression SourceRange range, StringRef name, Expr const * expr, bool negated)
*/
void CppunitAssertEquals::checkExpr(const Stmt* stmt)
{ {
const Stmt* parent = parentStmt(stmt); if (auto const e = dyn_cast<UnaryOperator>(expr)) {
if (!parent || !isa<ParenExpr>(parent)) { if (e->getOpcode() == UO_LNot) {
return; checkExpr(
} range, name, e->getSubExpr()->IgnoreParenImpCasts(), !negated);
parent = parentStmt(parent); }
if (!parent || !isa<UnaryOperator>(parent)) {
return;
}
parent = parentStmt(parent);
if (!parent || !isa<CallExpr>(parent)) {
return; return;
} }
const CallExpr* callExpr = dyn_cast<CallExpr>(parent); if (auto const e = dyn_cast<BinaryOperator>(expr)) {
if (!callExpr || callExpr->getDirectCallee() == nullptr) { auto const op = e->getOpcode();
if ((!negated && op == BO_EQ) || (negated && op == BO_NE)) {
reportEquals(range, name, op == BO_NE);
return;
}
#if 0 // TODO: enable later
if ((!negated && op == BO_LAnd) || (negated && op == BO_LOr)) {
report(
DiagnosticsEngine::Warning,
"rather split into two %0", e->getExprLoc())
<< name << range;
return;
}
#endif
return; return;
} }
const FunctionDecl* functionDecl = callExpr->getDirectCallee()->getCanonicalDecl(); if (auto const e = dyn_cast<CXXOperatorCallExpr>(expr)) {
if (!functionDecl || auto const op = e->getOperator();
!loplugin::DeclCheck(functionDecl).Function("failIf").Namespace("Asserter").Namespace("CppUnit").GlobalNamespace()) if ((!negated && op == OO_EqualEqual)
{ || (negated && op == OO_ExclaimEqual))
{
reportEquals(range, name, op == OO_ExclaimEqual);
return;
}
return; return;
} }
}
void CppunitAssertEquals::reportEquals(
SourceRange range, StringRef name, bool negative)
{
report( report(
DiagnosticsEngine::Warning, "rather call CPPUNIT_ASSERT_EQUALS", DiagnosticsEngine::Warning,
stmt->getSourceRange().getBegin()) ("rather call"
<< stmt->getSourceRange(); " %select{CPPUNIT_ASSERT_EQUAL|CPPUNIT_ASSERT_EQUAL_MESSAGE}0 (or"
" rewrite as an explicit operator %select{==|!=}1 call when the"
" operator itself is the topic)"),
range.getBegin())
<< (name == "CPPUNIT_ASSERT_MESSAGE") << negative << range;
} }
loplugin::Plugin::Registration< CppunitAssertEquals > X("cppunitassertequals"); loplugin::Plugin::Registration< CppunitAssertEquals > X("cppunitassertequals");
......
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*/
#include "sal/config.h"
#include "cppunit/TestAssert.h"
#include "cppunitassertequals.hxx"
#define TEST1 CPPUNIT_ASSERT(b1 == b2)
#define TEST2(x) x
void test(bool b1, bool b2, OUString const & s1, OUString const & s2, T t) {
CppUnit::Asserter::failIf(b1,"");
#if 0 // TODO: enable later
CPPUNIT_ASSERT(b1 && b2); // expected-error {{rather split into two CPPUNIT_ASSERT [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT((b1 && b2)); // expected-error {{rather split into two CPPUNIT_ASSERT [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(b1 || b2)); // expected-error {{rather split into two CPPUNIT_ASSERT [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(b1 && b2));
CPPUNIT_ASSERT(!!(b1 && b2)); // expected-error {{rather split into two CPPUNIT_ASSERT [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT_MESSAGE("", b1 && b2); // expected-error {{rather split into two CPPUNIT_ASSERT_MESSAGE [loplugin:cppunitassertequals]}}
#endif
CPPUNIT_ASSERT(b1 == b2); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(b1 != b2);
CPPUNIT_ASSERT((b1 == b2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(b1 != b2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator != call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(b1 == b2));
CPPUNIT_ASSERT(!!(b1 == b2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT_MESSAGE("", b1 == b2); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL_MESSAGE (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(s1 == s2); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(s1 != s2);
CPPUNIT_ASSERT((s1 == s2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(s1 != s2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator != call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
CPPUNIT_ASSERT(!(s1 == s2));
CPPUNIT_ASSERT(!!(s1 == s2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
TEST1; // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
TEST2(CPPUNIT_ASSERT(b1 == b2)); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
TEST2(TEST1); // expected-error {{rather call CPPUNIT_ASSERT_EQUAL (or rewrite as an explicit operator == call when the operator itself is the topic) [loplugin:cppunitassertequals]}}
// Useful when testing an equality iterator itself:
CPPUNIT_ASSERT(operator ==(s1, s1));
CPPUNIT_ASSERT(t.operator ==(t));
// There might even be good reasons(?) not to warn inside explicit casts:
CPPUNIT_ASSERT(bool(b1 && b2));
CPPUNIT_ASSERT(bool(b1 == b2));
CPPUNIT_ASSERT(bool(s1 == s2));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*/
#ifndef INCLUDED_COMPILERPLUGINS_CLANG_TEST_CPPUNITASSERTEQUALS_HXX
#define INCLUDED_COMPILERPLUGINS_CLANG_TEST_CPPUNITASSERTEQUALS_HXX
#include "sal/config.h"
#include "rtl/ustring.hxx"
struct T { bool operator ==(T); };
void test(bool b1, bool b2, OUString const & s1, OUString const & s2, T t);
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
$(eval $(call gb_CompilerTest_CompilerTest,compilerplugins_clang)) $(eval $(call gb_CompilerTest_CompilerTest,compilerplugins_clang))
$(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \ $(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \
compilerplugins/clang/test/cppunitassertequals \
compilerplugins/clang/test/datamembershadow \ compilerplugins/clang/test/datamembershadow \
compilerplugins/clang/test/externvar \ compilerplugins/clang/test/externvar \
compilerplugins/clang/test/finalprotected \ compilerplugins/clang/test/finalprotected \
...@@ -27,6 +28,10 @@ $(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \ ...@@ -27,6 +28,10 @@ $(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \
compilerplugins/clang/test/vclwidgets \ compilerplugins/clang/test/vclwidgets \
)) ))
$(eval $(call gb_CompilerTest_use_externals,compilerplugins_clang, \
cppunit \
))
$(eval $(call gb_CompilerTest_use_udk_api,compilerplugins_clang)) $(eval $(call gb_CompilerTest_use_udk_api,compilerplugins_clang))
# vim: set noet sw=4 ts=4: # vim: set noet sw=4 ts=4:
...@@ -42,6 +42,7 @@ $(eval $(foreach method, \ ...@@ -42,6 +42,7 @@ $(eval $(foreach method, \
add_objcxxobjects \ add_objcxxobjects \
add_cxxclrobject \ add_cxxclrobject \
add_cxxclrobjects \ add_cxxclrobjects \
use_externals \
use_udk_api \ use_udk_api \
, \ , \
$(call gb_CompilerTest__forward_to_Linktarget,$(method)) \ $(call gb_CompilerTest__forward_to_Linktarget,$(method)) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment