Skip to content

Commit f6cb7b6

Browse files
committed
Merge branch 'main' into stl-find-insert-msg-use-it
2 parents 10bd8fa + e52d652 commit f6cb7b6

16 files changed

Lines changed: 365 additions & 80 deletions

‎addons/cppcheckdata.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838

3939
current_dumpfile_suppressions = []
4040

41+
def _iterparse(filename, events):
42+
# Open the file here so it is closed even when the caller stops iterating
43+
# early. Python 3.15 warns about unclosed iterparse iterators otherwise.
44+
with open(filename, 'rb') as f:
45+
yield from ElementTree.iterparse(f, events=events)
46+
47+
4148
def _load_location(location, element):
4249
"""Load location from element/dict"""
4350
location.file = element.get('file')
@@ -1237,7 +1244,7 @@ def __init__(self, filename):
12371244
# Parse general configuration options from <dumps> node
12381245
# We intentionally don't clean node resources here because we
12391246
# want to serialize in memory only small part of the XML tree.
1240-
for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')):
1247+
for event, node in _iterparse(self.filename, events=('start', 'end')):
12411248
if platform_done and rawtokens_done and suppressions_done:
12421249
break
12431250
if node.tag == 'dumps':
@@ -1296,7 +1303,7 @@ def iterconfigurations(self):
12961303
# Use iterable objects to traverse XML tree for dump files incrementally.
12971304
# Iterative approach is required to avoid large memory consumption.
12981305
# Calling .clear() is necessary to let the element be garbage collected.
1299-
for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')):
1306+
for event, node in _iterparse(self.filename, events=('start', 'end')):
13001307
# Serialize new configuration node
13011308
if node.tag == 'dump':
13021309
if event == 'start':

‎gui/test/data/xmlfiles/xmlreport_v2.xml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
<results version="2">
33
<cppcheck version="1.47"/>
44
<errors>
5-
<error id="unreadVariable" severity="style" msg="Variable &amp;#039;a&amp;#039; is assigned a value that is never used" verbose="Variable &amp;#039;a&amp;#039; is assigned a value that is never used">
5+
<error id="unreadVariable" severity="style" msg="Variable 'a' is assigned a value that is never used" verbose="Variable 'a' is assigned a value that is never used">
66
<location file="test.cxx" line="11"/>
77
</error>
8-
<error id="unreadVariable" severity="style" msg="Variable &amp;#039;b&amp;#039; is assigned a value that is never used" verbose="Variable &amp;#039;b&amp;#039; is assigned a value that is never used">
8+
<error id="unreadVariable" severity="style" msg="Variable 'b' is assigned a value that is never used" verbose="Variable 'b' is assigned a value that is never used">
99
<location file="test.cxx" line="28"/>
1010
</error>
1111
<error id="memleak" severity="error" msg="Memory leak: b" verbose="Memory leak: b">
@@ -15,7 +15,7 @@
1515
<location file="test.cxx" line="32"/>
1616
<location file="test.cxx" line="16"/>
1717
</error>
18-
<error id="obsoleteFunctionsgets" severity="style" msg="Found obsolescent function &amp;#039;gets&amp;#039;. It is recommended to use the function &amp;#039;fgets&amp;#039; instead" verbose="Found obsolescent function &amp;#039;gets&amp;#039;. With gets you&amp;#039;ll get buffer overruns if the input data too big for the buffer. It is recommended to use the function &amp;#039;fgets&amp;#039; instead.">
18+
<error id="obsoleteFunctionsgets" severity="style" msg="Found obsolescent function 'gets'. It is recommended to use the function 'fgets' instead" verbose="Found obsolescent function 'gets'. With gets you'll get buffer overruns if the input data too big for the buffer. It is recommended to use the function 'fgets' instead.">
1919
<location file="test.cxx" line="31"/>
2020
</error>
2121
<error id="postfixOperator" severity="performance" msg="Prefer prefix ++/-- operators for non-primitive types." verbose="Prefix ++/-- operators should be preferred for non-primitive types. Pre-increment/decrement can be more efficient than post-increment/decrement. Post-increment/decrement usually involves keeping a copy of the previous value around and adds a little extra code.">

‎gui/test/resultstree/testresultstree.cpp‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,6 @@ Application& ApplicationList::getApplication(const int /*unused*/) {
106106
const Application& ApplicationList::getApplication(const int index) const {
107107
return mApplications.at(index);
108108
}
109-
QString XmlReport::quoteMessage(const QString &message) {
110-
return message;
111-
}
112-
QString XmlReport::unquoteMessage(const QString &message) {
113-
return message;
114-
}
115109
XmlReport::XmlReport(const QString& filename) : Report(filename) {}
116110
// NOLINTNEXTLINE(performance-unnecessary-value-param)
117111
void ThreadResult::finishCheck(CheckThread::Details /*unused*/) {

‎gui/test/xmlreportv2/testxmlreportv2.cpp‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include "erroritem.h"
2222
#include "xmlreportv2.h"
2323

24+
#include <QFile>
2425
#include <QList>
26+
#include <QTemporaryDir>
2527
#include <QtTest>
2628

2729
void TestXmlReportV2::readXml() const
@@ -53,4 +55,39 @@ void TestXmlReportV2::readXml() const
5355
QCOMPARE(item2.message, QString("Mismatching allocation and deallocation: k"));
5456
}
5557

58+
void TestXmlReportV2::writeXmlEscaping() const
59+
{
60+
QTemporaryDir dir;
61+
const QString path = dir.path() + "/results.xml";
62+
63+
ErrorItem item;
64+
item.errorId = "id";
65+
item.severity = Severity::style;
66+
item.summary = "\"x\" & <y> &quot;";
67+
item.message = item.summary;
68+
QErrorPathItem loc;
69+
loc.file = "a&b.cpp";
70+
loc.line = 1;
71+
item.errorPath << loc;
72+
{
73+
XmlReportV2 report(path, QString());
74+
QVERIFY(report.create());
75+
report.writeHeader();
76+
report.writeError(item);
77+
report.writeFooter();
78+
}
79+
80+
QFile file(path);
81+
QVERIFY(file.open(QIODevice::ReadOnly));
82+
QVERIFY(file.readAll().contains("msg=\"&quot;x&quot; &amp; &lt;y&gt; &amp;quot;\""));
83+
84+
XmlReportV2 report(path, QString());
85+
QVERIFY(report.open());
86+
const QList<ErrorItem> errors = report.read();
87+
QCOMPARE(errors.size(), 1);
88+
QCOMPARE(errors[0].summary, item.summary);
89+
QCOMPARE(errors[0].message, item.message);
90+
QCOMPARE(errors[0].errorPath[0].file, loc.file);
91+
}
92+
5693
QTEST_MAIN(TestXmlReportV2)

‎gui/test/xmlreportv2/testxmlreportv2.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ class TestXmlReportV2 : public QObject {
2323

2424
private slots:
2525
void readXml() const;
26+
void writeXmlEscaping() const;
2627
};

‎gui/xmlreport.cpp‎

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,6 @@ XmlReport::XmlReport(const QString &filename) :
3232
Report(filename)
3333
{}
3434

35-
QString XmlReport::quoteMessage(const QString &message)
36-
{
37-
QString quotedMessage(message);
38-
quotedMessage.replace("&", "&amp;");
39-
quotedMessage.replace("\"", "&quot;");
40-
quotedMessage.replace("'", "&#039;");
41-
quotedMessage.replace("<", "&lt;");
42-
quotedMessage.replace(">", "&gt;");
43-
return quotedMessage;
44-
}
45-
46-
QString XmlReport::unquoteMessage(const QString &message)
47-
{
48-
QString quotedMessage(message);
49-
quotedMessage.replace("&amp;", "&");
50-
quotedMessage.replace("&quot;", "\"");
51-
quotedMessage.replace("&#039;", "'");
52-
quotedMessage.replace("&lt;", "<");
53-
quotedMessage.replace("&gt;", ">");
54-
return quotedMessage;
55-
}
56-
5735
int XmlReport::determineVersion(const QString &filename)
5836
{
5937
QFile file;

‎gui/xmlreport.h‎

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,6 @@ class XmlReport : public Report {
4242
*/
4343
virtual QList<ErrorItem> read() = 0;
4444

45-
/**
46-
* @brief Quote the message.
47-
* @param message Message to quote.
48-
* @return quoted message.
49-
*/
50-
static QString quoteMessage(const QString &message);
51-
52-
/**
53-
* @brief Unquote the message.
54-
* @param message Message to quote.
55-
* @return quoted message.
56-
*/
57-
static QString unquoteMessage(const QString &message);
58-
5945
/**
6046
* @brief Get the XML report format version from the file.
6147
* @param filename Filename of the report file.

‎gui/xmlreportv2.cpp‎

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,8 @@ void XmlReportV2::writeError(const ErrorItem &error)
132132

133133
// Don't localize severity so we can read these files
134134
mXmlWriter->writeAttribute(SeverityAttribute, GuiSeverity::toString(error.severity));
135-
const QString summary = XmlReport::quoteMessage(error.summary);
136-
mXmlWriter->writeAttribute(MsgAttribute, summary);
137-
const QString message = XmlReport::quoteMessage(error.message);
138-
mXmlWriter->writeAttribute(VerboseAttribute, message);
135+
mXmlWriter->writeAttribute(MsgAttribute, error.summary);
136+
mXmlWriter->writeAttribute(VerboseAttribute, error.message);
139137
if (error.inconclusive)
140138
mXmlWriter->writeAttribute(InconclusiveAttribute, "true");
141139
if (!error.remark.isEmpty())
@@ -145,7 +143,7 @@ void XmlReportV2::writeError(const ErrorItem &error)
145143
if (error.hash > 0)
146144
mXmlWriter->writeAttribute(HashAttribute, QString::number(error.hash));
147145
if (!error.file0.isEmpty())
148-
mXmlWriter->writeAttribute(IncludedFromFilenameAttribute, quoteMessage(error.file0));
146+
mXmlWriter->writeAttribute(IncludedFromFilenameAttribute, error.file0);
149147
if (!error.sinceDate.isEmpty())
150148
mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate);
151149
if (!error.tags.isEmpty())
@@ -155,12 +153,12 @@ void XmlReportV2::writeError(const ErrorItem &error)
155153
mXmlWriter->writeStartElement(LocationElementName);
156154

157155
QString file = QDir::toNativeSeparators(error.errorPath[i].file);
158-
mXmlWriter->writeAttribute(FilenameAttribute, XmlReport::quoteMessage(file));
156+
mXmlWriter->writeAttribute(FilenameAttribute, file);
159157
mXmlWriter->writeAttribute(LineAttribute, QString::number(error.errorPath[i].line));
160158
if (error.errorPath[i].column > 0)
161159
mXmlWriter->writeAttribute(ColumnAttribute, QString::number(error.errorPath[i].column));
162160
if (error.errorPath.count() > 1)
163-
mXmlWriter->writeAttribute(InfoAttribute, XmlReport::quoteMessage(error.errorPath[i].info));
161+
mXmlWriter->writeAttribute(InfoAttribute, error.errorPath[i].info);
164162

165163
mXmlWriter->writeEndElement();
166164
}
@@ -228,10 +226,8 @@ ErrorItem XmlReportV2::readError(const QXmlStreamReader *reader)
228226
QXmlStreamAttributes attribs = reader->attributes();
229227
item.errorId = attribs.value(QString(), IdAttribute).toString();
230228
item.severity = GuiSeverity::fromString(attribs.value(QString(), SeverityAttribute).toString());
231-
const QString summary = attribs.value(QString(), MsgAttribute).toString();
232-
item.summary = XmlReport::unquoteMessage(summary);
233-
const QString message = attribs.value(QString(), VerboseAttribute).toString();
234-
item.message = XmlReport::unquoteMessage(message);
229+
item.summary = attribs.value(QString(), MsgAttribute).toString();
230+
item.message = attribs.value(QString(), VerboseAttribute).toString();
235231
if (attribs.hasAttribute(QString(), InconclusiveAttribute))
236232
item.inconclusive = true;
237233
if (attribs.hasAttribute(QString(), RemarkAttribute))
@@ -258,14 +254,14 @@ ErrorItem XmlReportV2::readError(const QXmlStreamReader *reader)
258254
QXmlStreamAttributes attribs = mXmlReader->attributes();
259255
QString file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString();
260256
if (!file0.isEmpty())
261-
item.file0 = XmlReport::unquoteMessage(file0);
257+
item.file0 = file0;
262258
QErrorPathItem loc;
263-
loc.file = XmlReport::unquoteMessage(attribs.value(QString(), FilenameAttribute).toString());
259+
loc.file = attribs.value(QString(), FilenameAttribute).toString();
264260
loc.line = attribs.value(QString(), LineAttribute).toString().toUInt();
265261
if (attribs.hasAttribute(QString(), ColumnAttribute))
266262
loc.column = attribs.value(QString(), ColumnAttribute).toString().toInt();
267263
if (attribs.hasAttribute(QString(), InfoAttribute))
268-
loc.info = XmlReport::unquoteMessage(attribs.value(QString(), InfoAttribute).toString());
264+
loc.info = attribs.value(QString(), InfoAttribute).toString();
269265
item.errorPath.push_front(loc);
270266
}
271267
break;

‎lib/checkclass.cpp‎

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,65 @@ void CheckClassImpl::handleUnionMembers(std::vector<Usage>& usageList)
195195
}
196196
}
197197

198+
static std::vector<const Token *> getInheritedConstructors(const Scope *scope)
199+
{
200+
std::vector<const Token *> result;
201+
202+
if (!scope || !scope->bodyStart || !scope->definedType)
203+
return result;
204+
205+
const auto isInherited = [](const Function &baseCtor, const Scope *scope) {
206+
if (baseCtor.type != FunctionType::eConstructor || baseCtor.argCount() == 0)
207+
return false;
208+
if (baseCtor.isDelete() || baseCtor.access == AccessControl::Private)
209+
return false;
210+
return std::none_of(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function &func) {
211+
return func.isConstructor() && func.argCount() == baseCtor.argCount() &&
212+
func.argsMatch(baseCtor.nestedIn, baseCtor.argDef, func.argDef, "", 0);
213+
});
214+
};
215+
216+
const std::vector<Type::BaseInfo> &bases = scope->definedType->derivedFrom;
217+
const Token *baseName = nullptr;
218+
const std::list<Function> *baseFuncs = nullptr;
219+
for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
220+
if (Token::Match(tok, "using %name%")) {
221+
const auto base = std::find_if(
222+
bases.cbegin(),
223+
bases.cend(),
224+
[&tok](const Type::BaseInfo &info) {
225+
return info.name == tok->strAt(1);
226+
}
227+
);
228+
if (base != bases.cend() && base->type && base->type->classScope) {
229+
baseName = tok->next();
230+
baseFuncs = &base->type->classScope->functionList;
231+
}
232+
continue;
233+
}
234+
235+
if (tok->str() == ";") {
236+
baseName = nullptr;
237+
continue;
238+
}
239+
240+
if (tok->str() == "{") {
241+
baseName = nullptr;
242+
tok = tok->link();
243+
continue;
244+
}
245+
246+
if (baseName && Token::Match(tok, "%name% :: %name% ;") &&
247+
tok->str() == baseName->str() && tok->strAt(2) == baseName->str() &&
248+
std::any_of(baseFuncs->cbegin(), baseFuncs->cend(), [&](const Function &func) {
249+
return isInherited(func, scope);
250+
}))
251+
result.push_back(tok->tokAt(2));
252+
}
253+
254+
return result;
255+
}
256+
198257
//---------------------------------------------------------------------------
199258
// ClassCheck: Check that all class constructors are ok.
200259
//---------------------------------------------------------------------------
@@ -223,9 +282,11 @@ void CheckClassImpl::constructors()
223282
});
224283
});
225284

285+
const std::vector<const Token *> inheritedCtors = getInheritedConstructors(scope);
286+
226287
// There are no constructors.
227288
std::set<const Variable*> diagVars;
228-
if (scope->numConstructors == 0 && printStyle && !usedInUnion) {
289+
if (scope->numConstructors == 0 && inheritedCtors.empty() && printStyle && !usedInUnion) {
229290
// If there is a private variable, there should be a constructor..
230291
int needInit = 0, haveInit = 0;
231292
std::vector<const Variable*> uninitVars;
@@ -339,6 +400,30 @@ void CheckClassImpl::constructors()
339400
}
340401
}
341402

403+
if (!inheritedCtors.empty()) {
404+
clearAllVar(usageList);
405+
406+
for (Usage &usage : usageList) {
407+
if (usage.var->hasDefault())
408+
usage.init = true;
409+
}
410+
411+
handleUnionMembers(usageList);
412+
413+
// Inherited constructors don't initialize members of the derived class
414+
for (const Token *baseCtorTok : inheritedCtors) {
415+
for (const Usage &usage : usageList) {
416+
if (isInitialized(usage, FunctionType::eConstructor))
417+
continue;
418+
const Variable& var = *usage.var;
419+
if (var.typeScope() && var.typeScope()->numConstructors > 0)
420+
continue;
421+
uninitVarError(baseCtorTok, false, FunctionType::eConstructor, var.scope()->className, var.name(), scope != var.scope(), false, false, true);
422+
diagVars.emplace(&var);
423+
}
424+
}
425+
}
426+
342427
if (scope->numConstructors == 0) {
343428

344429
// Mark all variables not used
@@ -1182,12 +1267,15 @@ void CheckClassImpl::noExplicitConstructorError(const Token *tok, const std::str
11821267
reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal);
11831268
}
11841269

1185-
void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor)
1270+
void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor, bool inheritedCtor)
11861271
{
11871272
std::string message("Member variable '$symbol' ");
11881273
if (noCtor)
11891274
message += "has no initializer.";
1190-
else {
1275+
else if (inheritedCtor) {
1276+
message += tok ? "is not initialized in the constructor inherited from '" + tok->str() + "'." :
1277+
"is not initialized in the inherited constructor.";
1278+
} else {
11911279
message += "is not initialized in the ";
11921280
if (functionType == FunctionType::eCopyConstructor)
11931281
message += "copy ";

‎lib/checkclass.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class CPPCHECKLIB CheckClassImpl : public CheckImpl {
182182
void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive);
183183
void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive);
184184
void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc);
185-
void uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor = false);
185+
void uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor = false, bool inheritedCtor = false);
186186
void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname);
187187
void missingMemberCopyError(const Token *tok, FunctionType functionType, const std::string& classname, const std::string& varname);
188188
void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive);

0 commit comments

Comments
 (0)