Skip to content

Commit 1bdb713

Browse files
Fix #9684 New check: find unnecessary copy in range loop (#5738)
1 parent 7ac824f commit 1bdb713

5 files changed

Lines changed: 34 additions & 14 deletions

File tree

gui/checkthread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ void CheckThread::runAddonsAndTools(const FileSettings *fileSettings, const QStr
177177
const QString clangPath = CheckThread::clangTidyCmd();
178178
if (!clangPath.isEmpty()) {
179179
QDir dir(clangPath + "/../lib/clang");
180-
for (QString ver : dir.entryList()) {
180+
for (const QString& ver : dir.entryList()) {
181181
QString includePath = dir.absolutePath() + '/' + ver + "/include";
182182
if (ver[0] != '.' && QDir(includePath).exists()) {
183183
args << "-isystem" << includePath;

gui/cppchecklibrarydata.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,14 +697,14 @@ static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData
697697
}
698698
if (!function.notOverlappingDataArgs.isEmpty()) {
699699
xmlWriter.writeStartElement("not-overlapping-data");
700-
foreach (const QString value, function.notOverlappingDataArgs) {
700+
foreach (const QString& value, function.notOverlappingDataArgs) {
701701
xmlWriter.writeAttribute(function.notOverlappingDataArgs.key(value), value);
702702
}
703703
xmlWriter.writeEndElement();
704704
}
705705
if (!function.containerAttributes.isEmpty()) {
706706
xmlWriter.writeStartElement("container");
707-
foreach (const QString value, function.containerAttributes) {
707+
foreach (const QString& value, function.containerAttributes) {
708708
xmlWriter.writeAttribute(function.containerAttributes.key(value), value);
709709
}
710710
xmlWriter.writeEndElement();

lib/checkother.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,10 +1244,14 @@ void CheckOther::checkPassByReference()
12441244
const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
12451245

12461246
for (const Variable* var : symbolDatabase->variableList()) {
1247-
if (!var || !var->isArgument() || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType())
1247+
if (!var || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType())
12481248
continue;
12491249

1250-
if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...")
1250+
const bool isRangeBasedFor = astIsRangeBasedForDecl(var->nameToken());
1251+
if (!var->isArgument() && !isRangeBasedFor)
1252+
continue;
1253+
1254+
if (!isRangeBasedFor && var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...")
12511255
continue; // references could not be used as va_start parameters (#5824)
12521256

12531257
const Token * const varDeclEndToken = var->declEndToken();
@@ -1275,25 +1279,27 @@ void CheckOther::checkPassByReference()
12751279

12761280
const bool isConst = var->isConst();
12771281
if (isConst) {
1278-
passedByValueError(var, inconclusive);
1282+
passedByValueError(var, inconclusive, isRangeBasedFor);
12791283
continue;
12801284
}
12811285

12821286
// Check if variable could be const
1283-
if (!var->scope() || var->scope()->function->isImplicitlyVirtual())
1287+
if (!isRangeBasedFor && (!var->scope() || var->scope()->function->isImplicitlyVirtual()))
12841288
continue;
12851289

12861290
if (!isVariableChanged(var, mSettings, mTokenizer->isCPP())) {
1287-
passedByValueError(var, inconclusive);
1291+
passedByValueError(var, inconclusive, isRangeBasedFor);
12881292
}
12891293
}
12901294
}
12911295

1292-
void CheckOther::passedByValueError(const Variable* var, bool inconclusive)
1296+
void CheckOther::passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor)
12931297
{
1294-
std::string id = "passedByValue";
1295-
std::string msg = "$symbol:" + (var ? var->name() : "") + "\n"
1296-
"Function parameter '$symbol' should be passed by const reference.";
1298+
std::string id = isRangeBasedFor ? "iterateByValue" : "passedByValue";
1299+
const std::string action = isRangeBasedFor ? "declared as": "passed by";
1300+
const std::string type = isRangeBasedFor ? "Range variable" : "Function parameter";
1301+
std::string msg = "$symbol:" + (var ? var->name() : "") + "\n" +
1302+
type + " '$symbol' should be " + action + " const reference.";
12971303
ErrorPath errorPath;
12981304
if (var && var->scope() && var->scope()->function && var->scope()->function->functionPointerUsage) {
12991305
id += "Callback";
@@ -1302,7 +1308,10 @@ void CheckOther::passedByValueError(const Variable* var, bool inconclusive)
13021308
}
13031309
if (var)
13041310
errorPath.emplace_back(var->nameToken(), msg);
1305-
msg += "\nParameter '$symbol' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++.";
1311+
if (isRangeBasedFor)
1312+
msg += "\nVariable '$symbol' is used to iterate by value. It could be declared as a const reference which is usually faster and recommended in C++.";
1313+
else
1314+
msg += "\nParameter '$symbol' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++.";
13061315
reportError(errorPath, Severity::performance, id.c_str(), msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
13071316
}
13081317

lib/checkother.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ class CPPCHECKLIB CheckOther : public Check {
241241
void clarifyStatementError(const Token* tok);
242242
void cstyleCastError(const Token *tok);
243243
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt);
244-
void passedByValueError(const Variable* var, bool inconclusive);
244+
void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false);
245245
void constVariableError(const Variable *var, const Function *function);
246246
void constStatementError(const Token *tok, const std::string &type, bool inconclusive);
247247
void signedCharArrayIndexError(const Token *tok);

test/testother.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ class TestOther : public TestFixture {
289289
TEST_CASE(constVariableArrayMember); // #10371
290290

291291
TEST_CASE(knownPointerToBool);
292+
TEST_CASE(iterateByValue);
292293
}
293294

294295
#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
@@ -11707,6 +11708,16 @@ class TestOther : public TestFixture {
1170711708
"}\n");
1170811709
ASSERT_EQUALS("", errout.str());
1170911710
}
11711+
11712+
void iterateByValue() {
11713+
check("void f() {\n" // #9684
11714+
" const std::set<std::string> ss = { \"a\", \"b\", \"c\" };\n"
11715+
" for (auto s : ss)\n"
11716+
" (void)s.size();\n"
11717+
"}\n");
11718+
ASSERT_EQUALS("[test.cpp:3]: (performance) Range variable 's' should be declared as const reference.\n",
11719+
errout.str());
11720+
}
1171011721
};
1171111722

1171211723
REGISTER_TEST(TestOther)

0 commit comments

Comments
 (0)