diff --git a/src/Utils.h b/src/Utils.h index ad0a240..877c21c 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,292 +1,300 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015-2016 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOREWARNINGS_UTILS_H #define MOREWARNINGS_UTILS_H #include "clazy_export.h" #include #include #include #include #include #include #include #include #include // TODO: this is a dumping ground, most of these functions should be moved to the other *Utils classes namespace clang { class CXXNamedCastExpr; class CXXRecordDecl; class CXXMemberCallExpr; class CXXConstructExpr; class CompilerInstance; class ClassTemplateSpecializationDecl; class Decl; class ParentMap; class SourceManager; class Stmt; class SourceLocation; class ExprWithCleanups; class ValueDecl; class ConditionalOperator; class CXXMethodDecl; class BinaryOperator; } struct StmtBodyRange; namespace Utils { /// Returns true if the class has at least one constexpr ctor CLAZYLIB_EXPORT bool hasConstexprCtor(clang::CXXRecordDecl *decl); /// Returns the type we're casting *from* CLAZYLIB_EXPORT clang::CXXRecordDecl * namedCastInnerDecl(clang::CXXNamedCastExpr *staticOrDynamicCast); /// Returns the type we're casting *to* CLAZYLIB_EXPORT clang::CXXRecordDecl * namedCastOuterDecl(clang::CXXNamedCastExpr *staticOrDynamicCast); /// Returns the class declaration from a variable declaration // So, if the var decl is "Foo f"; it returns the declaration of Foo CLAZYLIB_EXPORT clang::CXXRecordDecl * recordFromVarDecl(clang::Decl *); /// Returns the template specialization from a variable declaration // So, if the var decl is "QList f;", returns the template specialization QList CLAZYLIB_EXPORT clang::ClassTemplateSpecializationDecl * templateSpecializationFromVarDecl(clang::Decl *); /// Returns true if all the child member function calls are const functions. CLAZYLIB_EXPORT bool allChildrenMemberCallsConst(clang::Stmt *stm); /// Returns true if at least a UnaryOperator, BinaryOperator or non-const function call is found CLAZYLIB_EXPORT bool childsHaveSideEffects(clang::Stmt *stm); /// Receives a member call, such as "list.reserve()" and returns the declaration of the variable list // such as "QList list" CLAZYLIB_EXPORT clang::ValueDecl * valueDeclForMemberCall(clang::CXXMemberCallExpr *); /// Receives an operator call, such as "list << fooo" and returns the declaration of the variable list // such as "QList list" CLAZYLIB_EXPORT clang::ValueDecl * valueDeclForOperatorCall(clang::CXXOperatorCallExpr *); // overload CLAZYLIB_EXPORT clang::ValueDecl * valueDeclForCallExpr(clang::CallExpr *); // Returns true of this value decl is a member variable of a class or struct // returns null if not CLAZYLIB_EXPORT clang::CXXRecordDecl* isMemberVariable(clang::ValueDecl *); // Returns true if a body of statements contains a non const member call on object declared by varDecl // For example: // Foo foo; // this is the varDecl // while (bar) { foo.setValue(); // non-const call } CLAZYLIB_EXPORT bool containsNonConstMemberCall(clang::ParentMap *map, clang::Stmt *body, const clang::VarDecl *varDecl); // Returns true if there's an assignment to varDecl in body // Example: our_var = something_else CLAZYLIB_EXPORT bool isAssignedTo(clang::Stmt *body, const clang::VarDecl *varDecl); // Returns true if a body of statements contains a function call that takes our variable (varDecl) // By ref or pointer CLAZYLIB_EXPORT bool isPassedToFunction(const StmtBodyRange &bodyRange, const clang::VarDecl *varDecl, bool byRefOrPtrOnly); // Returns true if we take the address of varDecl, such as: &foo CLAZYLIB_EXPORT bool addressIsTaken(const clang::CompilerInstance &ci, clang::Stmt *body, const clang::ValueDecl *valDecl); // QString::fromLatin1("foo") -> true // QString::fromLatin1("foo", 1) -> false CLAZYLIB_EXPORT bool callHasDefaultArguments(clang::CallExpr *expr); // If there's a child of type StringLiteral, returns true // if allowEmpty is false, "" will be ignored CLAZYLIB_EXPORT bool containsStringLiteral(clang::Stmt *, bool allowEmpty = true, int depth = -1); CLAZYLIB_EXPORT bool isInsideOperatorCall(clang::ParentMap *map, clang::Stmt *s, const std::vector &anyOf); CLAZYLIB_EXPORT bool insideCTORCall(clang::ParentMap *map, clang::Stmt *s, const std::vector &anyOf); // returns true if the ternary operator has two string literal arguments, such as: // foo ? "bar" : "baz" CLAZYLIB_EXPORT bool ternaryOperatorIsOfStringLiteral(clang::ConditionalOperator*); CLAZYLIB_EXPORT bool isAssignOperator(clang::CXXOperatorCallExpr *op, llvm::StringRef className, llvm::StringRef argumentType, const clang::LangOptions &lo); CLAZYLIB_EXPORT bool isImplicitCastTo(clang::Stmt *, const std::string &); CLAZYLIB_EXPORT bool presumedLocationsEqual(const clang::PresumedLoc &l1, const clang::PresumedLoc &l2); // Returns the list of methods with name methodName that the class/struct record contains CLAZYLIB_EXPORT std::vector methodsFromString(const clang::CXXRecordDecl *record, const std::string &methodName); // Returns the most derived class. (CXXMemberCallExpr::getRecordDecl() return the first base class with the method) // The returned callee is the name of the variable on which the member call was made: // o1->foo() => "o1" // foo() => "this" CLAZYLIB_EXPORT const clang::CXXRecordDecl* recordForMemberCall(clang::CXXMemberCallExpr *call, std::string &implicitCallee); CLAZYLIB_EXPORT bool isAscii(clang::StringLiteral *lt); // Checks if Statement s inside an operator* call CLAZYLIB_EXPORT bool isInDerefExpression(clang::Stmt *s, clang::ParentMap *map); // For a a chain called expression like foo().bar().baz() returns a list of calls // {baz(), bar(), foo()} // the parameter lastCallExpr to pass would be baz() // No need to specify the other callexprs, they are children of the first one, since the AST looks like: // - baz // -- bar // --- foo CLAZYLIB_EXPORT std::vector callListForChain(clang::CallExpr *lastCallExpr); // Returns the first base class CLAZYLIB_EXPORT clang::CXXRecordDecl * rootBaseClass(clang::CXXRecordDecl *derived); // Returns the copy ctor for this class CLAZYLIB_EXPORT clang::CXXConstructorDecl *copyCtor(clang::CXXRecordDecl *); // Returns the copy-assignment operator for this class CLAZYLIB_EXPORT clang::CXXMethodDecl *copyAssign(clang::CXXRecordDecl *); CLAZYLIB_EXPORT bool hasMember(clang::CXXRecordDecl *record, const std::string &memberTypeName); /** * Returns true if record is a shared pointer (boost, Qt or stl only). */ CLAZYLIB_EXPORT bool isSharedPointer(clang::CXXRecordDecl *record); /** * Returns true if varDecl is initialized externally. * Example: * QList list = getList(); // true * QList list = list2; // true * QList list = {1, 2, 3}; // false * QList list; // false */ CLAZYLIB_EXPORT bool isInitializedExternally(clang::VarDecl *varDecl); /** * Returns true if declStmt refers to varDecl */ CLAZYLIB_EXPORT bool referencesVarDecl(clang::DeclStmt *declStmt, clang::VarDecl *varDecl); /** * Returns true if the body of a function is empty. * Returns false if either function or it's body are null. */ CLAZYLIB_EXPORT bool functionHasEmptyBody(clang::FunctionDecl *func); /** * If stm is an UnaryOperator or BinaryOperator that writes to the variable it returns the expression * that represents the variable (Usually a MemberExpr or DeclRefExpr for local variables). * * Otherwise returns nullptr. * * The operators that write to the variable are operator=, operator+=, operator++, etc. */ CLAZYLIB_EXPORT clang::Expr* isWriteOperator(clang::Stmt *stm); /** * Gets the UserDefinedLiteral of type @p type which is somewhere in the ast of @p stm. * Returns nullptr if there's no such UserDefinedLiteral. */ CLAZYLIB_EXPORT clang::UserDefinedLiteral* userDefinedLiteral(clang::Stmt *stm, const std::string &type, const clang::LangOptions &lo); /** * Returns the function parameters fom @p func * This should be used instead of calling FunctionDecl::params() since it changed signature in * clang 3.9. */ CLAZYLIB_EXPORT #if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 8 clang::FunctionDecl::param_range #else clang::ArrayRef #endif functionParameters(clang::FunctionDecl *func); /** * For the given ctor, and ctor param, returns the ctor member initializers that used that param. * Example: * MyCtor(int a, int b) : c(a), d(b) {} * auto result = Utils::ctorInitializer(MyCtor, b); // Result is the statement "d(b)" */ CLAZYLIB_EXPORT std::vector ctorInitializer(clang::CXXConstructorDecl *ctor, clang::ParmVarDecl *param); /** * Returns true if a ctor initializer contains a std::move() * Example * MyCtor(Foo a) : c(move(a)) {} // Would return true for this init list */ CLAZYLIB_EXPORT bool ctorInitializerContainsMove(clang::CXXCtorInitializer*); // Overload that recieves a vector and returns true if any ctor initializer contains a move() CLAZYLIB_EXPORT bool ctorInitializerContainsMove(const std::vector &); /** * Returns the filename for the source location loc */ CLAZYLIB_EXPORT std::string filenameForLoc(clang::SourceLocation loc, const clang::SourceManager &sm); /** * Returns the location after the lexer token that is at loc. * For example: * emit sig(); * If loc refers to the location of 'emit', then this function will return the source location if * the sig() call. */ CLAZYLIB_EXPORT clang::SourceLocation locForNextToken(clang::SourceLocation loc, const clang::SourceManager &sm, const clang::LangOptions &lo); inline bool isMainFile(const clang::SourceManager &sm, clang::SourceLocation loc) { if (loc.isMacroID()) loc = sm.getExpansionLoc(loc); return sm.isInFileID(loc, sm.getMainFileID()); } /** * Returns true if the string literal contains escaped bytes, such as \x12, \123, \u00F6. */ bool literalContainsEscapedBytes(clang::StringLiteral *lt, const clang::SourceManager &sm, const clang::LangOptions &lo); + + /** + * Returns true if this method overrides one from the base class + */ + inline bool methodOverrides(clang::CXXMethodDecl *method) + { + return method && method->isVirtual() && method->size_overridden_methods() > 0; + } } #endif diff --git a/src/checks/level2/function-args-by-ref.cpp b/src/checks/level2/function-args-by-ref.cpp index 4146aad..3a8b2be 100644 --- a/src/checks/level2/function-args-by-ref.cpp +++ b/src/checks/level2/function-args-by-ref.cpp @@ -1,164 +1,169 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "function-args-by-ref.h" #include "Utils.h" #include "FixItUtils.h" #include "TypeUtils.h" #include "ClazyContext.h" #include "StringUtils.h" #include #include using namespace clang; using namespace std; enum Fixit { FixitNone = 0, FixitAll = 0x1 // More granularity isn't needed I guess }; static bool shouldIgnoreClass(CXXRecordDecl *record) { if (!record) return false; if (Utils::isSharedPointer(record)) return true; static const vector ignoreList = {"QDebug", // Too many warnings "QGenericReturnArgument", "QColor", // TODO: Remove in Qt6 "QStringRef", // TODO: Remove in Qt6 "QList::const_iterator", // TODO: Remove in Qt6 "QJsonArray::const_iterator", // TODO: Remove in Qt6 "QList::const_iterator", // TODO: Remove in Qt6 "QtMetaTypePrivate::QSequentialIterableImpl", "QtMetaTypePrivate::QAssociativeIterableImpl", "QVariantComparisonHelper", "QHashDummyValue", "QCharRef", "QString::Null" }; return clazy::contains(ignoreList, record->getQualifiedNameAsString()); } static bool shouldIgnoreOperator(FunctionDecl *function) { // Too many warnings in operator<< static const vector ignoreList = { "operator<<" }; return clazy::contains(ignoreList, clazy::name(function)); } static bool shouldIgnoreFunction(clang::FunctionDecl *function) { static const vector qualifiedIgnoreList = {"QDBusMessage::createErrorReply", // Fixed in Qt6 "QMenu::exec", // Fixed in Qt6 "QTextFrame::iterator", // Fixed in Qt6 "QGraphicsWidget::addActions", // Fixed in Qt6 "QListWidget::mimeData", // Fixed in Qt6 "QTableWidget::mimeData", // Fixed in Qt6 "QTreeWidget::mimeData", // Fixed in Qt6 "QWidget::addActions", // Fixed in Qt6 "QSslCertificate::verify", // Fixed in Qt6 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6 }; return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); } FunctionArgsByRef::FunctionArgsByRef(const std::string &name, ClazyContext *context) : CheckBase(name, context, Option_CanIgnoreIncludes) { } static std::string warningMsgForSmallType(int sizeOf, const std::string &typeName) { std::string sizeStr = std::to_string(sizeOf); return "Missing reference on large type (sizeof " + typeName + " is " + sizeStr + " bytes)"; } void FunctionArgsByRef::processFunction(FunctionDecl *func) { if (!func || !func->isThisDeclarationADefinition() || func->isDeleted() || shouldIgnoreOperator(func)) return; if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) return; + if (Utils::methodOverrides(dyn_cast(func))) { + // When overriding you can't change the signature. You should fix the base classes first + return; + } + Stmt *body = func->getBody(); int i = -1; for (auto param : Utils::functionParameters(func)) { i++; QualType paramQt = TypeUtils::unrefQualType(param->getType()); const Type *paramType = paramQt.getTypePtrOrNull(); if (!paramType || paramType->isIncompleteType() || paramType->isDependentType()) continue; if (shouldIgnoreClass(paramType->getAsCXXRecordDecl())) continue; TypeUtils::QualTypeClassification classif; bool success = TypeUtils::classifyQualType(m_context, param, classif, body); if (!success) continue; vector ctorInits = Utils::ctorInitializer(dyn_cast(func), param); if (Utils::ctorInitializerContainsMove(ctorInits)) continue; if (classif.passBigTypeByConstRef || classif.passNonTriviallyCopyableByConstRef) { string error; std::vector fixits; const string paramStr = param->getType().getAsString(); if (classif.passBigTypeByConstRef) { error = warningMsgForSmallType(classif.size_of_T, paramStr); } else if (classif.passNonTriviallyCopyableByConstRef) { error = "Missing reference on non-trivial type (" + paramStr + ')'; } emitWarning(param->getLocStart(), error.c_str(), fixits); } } } void FunctionArgsByRef::VisitDecl(Decl *decl) { processFunction(dyn_cast(decl)); } void FunctionArgsByRef::VisitStmt(Stmt *stmt) { if (auto lambda = dyn_cast(stmt)) { if (!shouldIgnoreFile(stmt->getLocStart())) processFunction(lambda->getCallOperator()); } } clang::FixItHint FunctionArgsByRef::fixit(const ParmVarDecl *, TypeUtils::QualTypeClassification) { FixItHint fixit; return fixit; } diff --git a/src/checks/level2/function-args-by-value.cpp b/src/checks/level2/function-args-by-value.cpp index 577e022..050caf0 100644 --- a/src/checks/level2/function-args-by-value.cpp +++ b/src/checks/level2/function-args-by-value.cpp @@ -1,205 +1,209 @@ /* This file is part of the clazy static checker. - Copyright (C) 2016-2017 Sergio Martins + Copyright (C) 2016-2018 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "function-args-by-value.h" #include "Utils.h" #include "StringUtils.h" #include "TypeUtils.h" #include "FixItUtils.h" #include "ClazyContext.h" #include #include using namespace clang; using namespace std; enum Fixit { FixitNone = 0, FixitAll = 0x1 // More granularity isn't needed I guess }; // TODO, go over all these static bool shouldIgnoreClass(CXXRecordDecl *record) { if (!record) return false; if (Utils::isSharedPointer(record)) return true; static const vector ignoreList = {"QDebug", // Too many warnings "QGenericReturnArgument", "QColor", // TODO: Remove in Qt6 "QStringRef", // TODO: Remove in Qt6 "QList::const_iterator", // TODO: Remove in Qt6 "QJsonArray::const_iterator", // TODO: Remove in Qt6 "QList::const_iterator", // TODO: Remove in Qt6 "QtMetaTypePrivate::QSequentialIterableImpl", "QtMetaTypePrivate::QAssociativeIterableImpl", "QVariantComparisonHelper", "QHashDummyValue", "QCharRef", "QString::Null" }; return clazy::contains(ignoreList, record->getQualifiedNameAsString()); } static bool shouldIgnoreOperator(FunctionDecl *function) { // Too many warnings in operator<< static const vector ignoreList = { "operator<<" }; return clazy::contains(ignoreList, clazy::name(function)); } static bool shouldIgnoreFunction(clang::FunctionDecl *function) { static const vector qualifiedIgnoreList = {"QDBusMessage::createErrorReply", // Fixed in Qt6 "QMenu::exec", // Fixed in Qt6 "QTextFrame::iterator", // Fixed in Qt6 "QGraphicsWidget::addActions", // Fixed in Qt6 "QListWidget::mimeData", // Fixed in Qt6 "QTableWidget::mimeData", // Fixed in Qt6 "QTreeWidget::mimeData", // Fixed in Qt6 "QWidget::addActions", // Fixed in Qt6 "QSslCertificate::verify", // Fixed in Qt6 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6 }; return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); } FunctionArgsByValue::FunctionArgsByValue(const std::string &name, ClazyContext *context) : CheckBase(name, context, Option_CanIgnoreIncludes) { } void FunctionArgsByValue::VisitDecl(Decl *decl) { processFunction(dyn_cast(decl)); } void FunctionArgsByValue::VisitStmt(Stmt *stmt) { - if (LambdaExpr *lambda = dyn_cast(stmt)) + if (auto lambda = dyn_cast(stmt)) processFunction(lambda->getCallOperator()); } void FunctionArgsByValue::processFunction(FunctionDecl *func) { - if (!func || !func->isThisDeclarationADefinition() || - func->isDeleted()) + if (!func || !func->isThisDeclarationADefinition() || func->isDeleted()) return; auto ctor = dyn_cast(func); - if (ctor) { - if (ctor->isCopyConstructor()) - return; // copy-ctor must take by ref + if (ctor && ctor->isCopyConstructor()) + return; // copy-ctor must take by ref + + if (Utils::methodOverrides(dyn_cast(func))) { + // When overriding you can't change the signature. You should fix the base classes first + return; } if (shouldIgnoreOperator(func)) return; if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) return; Stmt *body = func->getBody(); int i = -1; for (auto param : Utils::functionParameters(func)) { i++; QualType paramQt = TypeUtils::unrefQualType(param->getType()); const Type *paramType = paramQt.getTypePtrOrNull(); if (!paramType || paramType->isIncompleteType() || paramType->isDependentType()) continue; if (shouldIgnoreClass(paramType->getAsCXXRecordDecl())) continue; TypeUtils::QualTypeClassification classif; bool success = TypeUtils::classifyQualType(m_context, param, classif, body); if (!success) continue; if (classif.passSmallTrivialByValue) { if (ctor) { // Implements fix for Bug #379342 vector initializers = Utils::ctorInitializer(ctor, param); bool found_by_ref_member_init = false; for (auto initializer : initializers) { if (!initializer->isMemberInitializer()) continue; // skip base class initializer FieldDecl *field = initializer->getMember(); if (!field) continue; QualType type = field->getType(); if (type.isNull() || type->isReferenceType()) { found_by_ref_member_init = true; break; } } if (found_by_ref_member_init) continue; } std::vector fixits; - if (isFixitEnabled(FixitAll)) { + auto method = dyn_cast(func); + const bool isVirtualMethod = method && method->isVirtual(); + if (!isVirtualMethod && isFixitEnabled(FixitAll)) { // Don't try to fix virtual methods, as build can fail for (auto redecl : func->redecls()) { // Fix in both header and .cpp - FunctionDecl *fdecl = dyn_cast(redecl); + auto fdecl = dyn_cast(redecl); const ParmVarDecl *param = fdecl->getParamDecl(i); fixits.push_back(fixit(fdecl, param, classif)); } } const string paramStr = param->getType().getAsString(); string error = "Pass small and trivially-copyable type by value (" + paramStr + ')'; emitWarning(param->getLocStart(), error.c_str(), fixits); } } } FixItHint FunctionArgsByValue::fixit(FunctionDecl *func, const ParmVarDecl *param, TypeUtils::QualTypeClassification) { QualType qt = TypeUtils::unrefQualType(param->getType()); qt.removeLocalConst(); const string typeName = qt.getAsString(PrintingPolicy(lo())); string replacement = typeName + ' ' + string(param->getName()); SourceLocation startLoc = param->getLocStart(); SourceLocation endLoc = param->getLocEnd(); const int numRedeclarations = std::distance(func->redecls_begin(), func->redecls_end()); const bool definitionIsAlsoDeclaration = numRedeclarations == 1; const bool isDeclarationButNotDefinition = !func->doesThisDeclarationHaveABody(); if (param->hasDefaultArg() && (isDeclarationButNotDefinition || definitionIsAlsoDeclaration)) { endLoc = param->getDefaultArg()->getLocStart().getLocWithOffset(-1); replacement += " ="; } if (!startLoc.isValid() || !endLoc.isValid()) { llvm::errs() << "Internal error could not apply fixit " << startLoc.printToString(sm()) << ';' << endLoc.printToString(sm()) << "\n"; return {}; } return clazy::createReplacement({ startLoc, endLoc }, replacement); } diff --git a/tests/function-args-by-ref/main.cpp b/tests/function-args-by-ref/main.cpp index fdd9d8b..7fb7d5b 100644 --- a/tests/function-args-by-ref/main.cpp +++ b/tests/function-args-by-ref/main.cpp @@ -1,230 +1,247 @@ #include #include struct Trivial { int a; }; struct NonTrivial { NonTrivial() {} NonTrivial(const NonTrivial &) {} void constFunction() const {}; void nonConstFunction() {}; int a; }; extern void by_pointer(NonTrivial*); extern void by_const_pointer(const NonTrivial*); extern void by_ref(NonTrivial&); extern void by_constRef(const NonTrivial&); int foo1(const Trivial nt) // Test #1: No warning { return nt.a; } int foo2(const NonTrivial nt) // Test #2: Warning { nt.constFunction(); return nt.a; } int foo3(NonTrivial nt) // Test #3: Warning { return nt.a; } int foo4(NonTrivial nt) // Test #4: Warning { return nt.a; } int foo5(NonTrivial nt) // Test #5: No warning { by_pointer(&nt); return nt.a; } int foo6(NonTrivial nt) // Test #6: No warning { by_ref(nt); return nt.a; } int foo7(NonTrivial nt) // Test #7: No warning { nt.nonConstFunction(); return nt.a; } int foo8(NonTrivial nt) // Test #8: Warning { nt.constFunction(); return nt.a; } int foo9(NonTrivial nt) // Test #9: Warning { by_constRef(nt); return nt.a; } int foo10(NonTrivial nt) // Test #10: Warning { by_const_pointer(&nt); return nt.a; } int foo11(QColor) // Test #11: No warning { return 1; } int foo12(NonTrivial nt) // Test #12: No warning { nt = NonTrivial(); return 1; } void func(QString text) { QTextStream a(&text); // OK, by-pointer to a CTOR } struct StringConstPtr { StringConstPtr(const QString *s) {} }; void func2(QString text) { StringConstPtr s(&text); // Warning, it's a const ptr } enum Option { FooOption, FooOption2 }; Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_OPERATORS_FOR_FLAGS(Options) struct NoUserCtorsButNotTrivial { NonTrivial t; }; void testNoUserCtorsButNotTrivial(NoUserCtorsButNotTrivial) { } struct TrivialWithDefaultCopyCtorAndDtor { TrivialWithDefaultCopyCtorAndDtor(const TrivialWithDefaultCopyCtorAndDtor &) = default; ~TrivialWithDefaultCopyCtorAndDtor() = default; int m; }; void testTrivialWithDefaultCopyCtorAndDtor(TrivialWithDefaultCopyCtorAndDtor) { } struct DefaultButNotTrivialCopyable { DefaultButNotTrivialCopyable(const DefaultButNotTrivialCopyable & ) = default; ~DefaultButNotTrivialCopyable() = default; NonTrivial t; }; void testDefaultButNotTrivialCopyable(DefaultButNotTrivialCopyable) { } void shouldBeByValue(const Trivial &nt) { } void isOK(const NonTrivial &&) { } void foo(const Trivial &t = Trivial()); void foo1(const Trivial & = Trivial()); void foo2(const Trivial &t); void foo3(const Trivial&); void foo4(const Trivial &t); void foo4(const Trivial &t) { } void foo5(const Trivial &t) { } void foo6(const Trivial &t = {}) { } struct Base // Test that fixits fix both Base and Derived { void foo(const Trivial &); }; void Base::foo(const Trivial &) { } struct Derived : public Base { void foo(const Trivial &); }; void Derived::foo(const Trivial &) { } struct DeletedCtor // bug #360112 { Q_DISABLE_COPY(DeletedCtor) }; struct CopyCtor // bug #360112 { CopyCtor(const CopyCtor &) {} }; struct Ctors { Ctors (NonTrivial) {} }; struct Ctors2 { Ctors2(NonTrivial n) : m(std::move(n)), i(0) {} // Ok NonTrivial m; int i; }; struct Ctors3 { Ctors3(NonTrivial n) : m(n), i(0) {} // Warning NonTrivial m; int i; }; + + +// #381812 +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(NonTrivial) {}; // Warn + virtual void virtualMethod2(NonTrivial) {}; // Warn + void nonVirtualMethod(NonTrivial) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(NonTrivial) override {}; // OK + void virtualMethod2(NonTrivial) {}; // OK + void nonVirtualMethod(NonTrivial) {}; // Warn +}; diff --git a/tests/function-args-by-ref/main.cpp.expected b/tests/function-args-by-ref/main.cpp.expected index d4a2e8f..de6c8fb 100644 --- a/tests/function-args-by-ref/main.cpp.expected +++ b/tests/function-args-by-ref/main.cpp.expected @@ -1,11 +1,16 @@ +function-args-by-ref/main.cpp:245:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] function-args-by-ref/main.cpp:28:10: warning: Missing reference on non-trivial type (const struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:34:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:39:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:63:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:69:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:75:11: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:102:12: warning: Missing reference on non-trivial type (class QString) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:121:35: warning: Missing reference on non-trivial type (struct NoUserCtorsButNotTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:145:39: warning: Missing reference on non-trivial type (struct DefaultButNotTrivialCopyable) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:214:12: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:227:12: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:237:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:238:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:239:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:246:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] diff --git a/tests/function-args-by-value/main.cpp b/tests/function-args-by-value/main.cpp index f4ab78c..b8e8508 100644 --- a/tests/function-args-by-value/main.cpp +++ b/tests/function-args-by-value/main.cpp @@ -1,218 +1,235 @@ #include #include struct Trivial { int a; }; struct NonTrivial { NonTrivial() {} NonTrivial(const NonTrivial &) {} void constFunction() const {}; void nonConstFunction() {}; int a; }; void by_pointer(NonTrivial*) {} void by_const_pointer(const NonTrivial*) {} void by_ref(NonTrivial&) {} void by_constRef(const NonTrivial&) {} int foo1(const Trivial nt) // Test #1: No warning { return nt.a; } int foo2(const NonTrivial nt) // Test #2: Warning { nt.constFunction(); return nt.a; } int foo3(NonTrivial nt) // Test #3: Warning { return nt.a; } int foo4(NonTrivial nt) // Test #4: Warning { return nt.a; } int foo5(NonTrivial nt) // Test #5: No warning { by_pointer(&nt); return nt.a; } int foo6(NonTrivial nt) // Test #6: No warning { by_ref(nt); return nt.a; } int foo7(NonTrivial nt) // Test #7: No warning { nt.nonConstFunction(); return nt.a; } int foo8(NonTrivial nt) // Test #8: Warning { nt.constFunction(); return nt.a; } int foo9(NonTrivial nt) // Test #9: Warning { by_constRef(nt); return nt.a; } int foo10(NonTrivial nt) // Test #10: Warning { by_const_pointer(&nt); return nt.a; } int foo11(QColor) // Test #11: No warning { return 1; } int foo12(NonTrivial nt) // Test #12: No warning { nt = NonTrivial(); return 1; } void func(QString text) { QTextStream a(&text); // OK, by-pointer to a CTOR } struct StringConstPtr { StringConstPtr(const QString *s) {} }; void func2(QString text) { StringConstPtr s(&text); // Warning, it's a const ptr } enum Option { FooOption, FooOption2 }; Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_OPERATORS_FOR_FLAGS(Options) struct NoUserCtorsButNotTrivial { NonTrivial t; }; void testNoUserCtorsButNotTrivial(NoUserCtorsButNotTrivial) { } struct TrivialWithDefaultCopyCtorAndDtor { TrivialWithDefaultCopyCtorAndDtor(const TrivialWithDefaultCopyCtorAndDtor &) = default; ~TrivialWithDefaultCopyCtorAndDtor() = default; int m; }; void testTrivialWithDefaultCopyCtorAndDtor(TrivialWithDefaultCopyCtorAndDtor) { } struct DefaultButNotTrivialCopyable { DefaultButNotTrivialCopyable(const DefaultButNotTrivialCopyable & ) = default; ~DefaultButNotTrivialCopyable() = default; NonTrivial t; }; void testDefaultButNotTrivialCopyable(DefaultButNotTrivialCopyable) { } void shouldBeByValue(const Trivial &nt) { } void isOK(const NonTrivial &&) { } void foo(const Trivial &t = Trivial()); void foo1(const Trivial & = Trivial()); void foo2(const Trivial &t); void foo3(const Trivial&); void foo4(const Trivial &t); void foo4(const Trivial &t) { } void foo5(const Trivial &t) { } void foo6(const Trivial &t = {}) { } struct Base // Test that fixits fix both Base and Derived { void foo(const Trivial &); }; void Base::foo(const Trivial &) { } struct Derived : public Base { void foo(const Trivial &); }; void Derived::foo(const Trivial &) { } struct QDBusMessage { void createErrorReply(QString) {} }; struct DeletedCtor // bug #360112 { Q_DISABLE_COPY(DeletedCtor) }; struct CopyCtor // bug #360112 { CopyCtor(const CopyCtor &) {} }; struct Ctors { Ctors (NonTrivial) {} }; void trivialByConstRef(const int &t) {} // Warn void trivialByRef(int &t) {} // OK + +// #381812 + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(const Trivial &) {}; // Warn + virtual void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(const Trivial &) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(const Trivial &) override {}; // OK + void virtualMethod2(const Trivial &) {}; // OK + void nonVirtualMethod(const Trivial &) {}; // Warn +}; diff --git a/tests/function-args-by-value/main.cpp.expected b/tests/function-args-by-value/main.cpp.expected index f30cec0..735e75c 100644 --- a/tests/function-args-by-value/main.cpp.expected +++ b/tests/function-args-by-value/main.cpp.expected @@ -1,7 +1,12 @@ +function-args-by-value/main.cpp:233:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] function-args-by-value/main.cpp:150:22: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:166:11: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:170:11: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:174:11: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:184:16: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:193:19: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:217:24: warning: Pass small and trivially-copyable type by value (const int &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:225:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:226:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:227:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:234:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] diff --git a/tests/function-args-by-value/main.cpp_fixed.cpp.expected b/tests/function-args-by-value/main.cpp_fixed.cpp.expected index ffa772e..a76ae55 100644 --- a/tests/function-args-by-value/main.cpp_fixed.cpp.expected +++ b/tests/function-args-by-value/main.cpp_fixed.cpp.expected @@ -1,218 +1,235 @@ #include #include struct Trivial { int a; }; struct NonTrivial { NonTrivial() {} NonTrivial(const NonTrivial &) {} void constFunction() const {}; void nonConstFunction() {}; int a; }; void by_pointer(NonTrivial*) {} void by_const_pointer(const NonTrivial*) {} void by_ref(NonTrivial&) {} void by_constRef(const NonTrivial&) {} int foo1(const Trivial nt) // Test #1: No warning { return nt.a; } int foo2(const NonTrivial nt) // Test #2: Warning { nt.constFunction(); return nt.a; } int foo3(NonTrivial nt) // Test #3: Warning { return nt.a; } int foo4(NonTrivial nt) // Test #4: Warning { return nt.a; } int foo5(NonTrivial nt) // Test #5: No warning { by_pointer(&nt); return nt.a; } int foo6(NonTrivial nt) // Test #6: No warning { by_ref(nt); return nt.a; } int foo7(NonTrivial nt) // Test #7: No warning { nt.nonConstFunction(); return nt.a; } int foo8(NonTrivial nt) // Test #8: Warning { nt.constFunction(); return nt.a; } int foo9(NonTrivial nt) // Test #9: Warning { by_constRef(nt); return nt.a; } int foo10(NonTrivial nt) // Test #10: Warning { by_const_pointer(&nt); return nt.a; } int foo11(QColor) // Test #11: No warning { return 1; } int foo12(NonTrivial nt) // Test #12: No warning { nt = NonTrivial(); return 1; } void func(QString text) { QTextStream a(&text); // OK, by-pointer to a CTOR } struct StringConstPtr { StringConstPtr(const QString *s) {} }; void func2(QString text) { StringConstPtr s(&text); // Warning, it's a const ptr } enum Option { FooOption, FooOption2 }; Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_OPERATORS_FOR_FLAGS(Options) struct NoUserCtorsButNotTrivial { NonTrivial t; }; void testNoUserCtorsButNotTrivial(NoUserCtorsButNotTrivial) { } struct TrivialWithDefaultCopyCtorAndDtor { TrivialWithDefaultCopyCtorAndDtor(const TrivialWithDefaultCopyCtorAndDtor &) = default; ~TrivialWithDefaultCopyCtorAndDtor() = default; int m; }; void testTrivialWithDefaultCopyCtorAndDtor(TrivialWithDefaultCopyCtorAndDtor) { } struct DefaultButNotTrivialCopyable { DefaultButNotTrivialCopyable(const DefaultButNotTrivialCopyable & ) = default; ~DefaultButNotTrivialCopyable() = default; NonTrivial t; }; void testDefaultButNotTrivialCopyable(DefaultButNotTrivialCopyable) { } void shouldBeByValue(Trivial nt) { } void isOK(const NonTrivial &&) { } void foo(const Trivial &t = Trivial()); void foo1(const Trivial & = Trivial()); void foo2(const Trivial &t); void foo3(const Trivial&); void foo4(Trivial t); void foo4(Trivial t) { } void foo5(Trivial t) { } void foo6(Trivial t = {}) { } struct Base // Test that fixits fix both Base and Derived { void foo(Trivial ); }; void Base::foo(Trivial ) { } struct Derived : public Base { void foo(Trivial ); }; void Derived::foo(Trivial ) { } struct QDBusMessage { void createErrorReply(QString) {} }; struct DeletedCtor // bug #360112 { Q_DISABLE_COPY(DeletedCtor) }; struct CopyCtor // bug #360112 { CopyCtor(const CopyCtor &) {} }; struct Ctors { Ctors (NonTrivial) {} }; void trivialByConstRef(int t) {} // Warn void trivialByRef(int &t) {} // OK + +// #381812 + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(const Trivial &) {}; // Warn + virtual void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(Trivial ) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(const Trivial &) override {}; // OK + void virtualMethod2(const Trivial &) {}; // OK + void nonVirtualMethod(Trivial ) {}; // Warn +};