From ab7d72883120d95d641a4f3b7e9208370e7a914c Mon Sep 17 00:00:00 2001 From: Mathias Schmid <50880400+matzeschmid@users.noreply.github.com> Date: Mon, 18 Jan 2021 19:10:53 +0100 Subject: [PATCH] Add missing support for "type-checks" and "smart-pointer" configuration. (#3039) --- .github/workflows/CI-unixish.yml | 5 + gui/cppchecklibrarydata.cpp | 54 +++++ gui/cppchecklibrarydata.h | 9 +- .../cppchecklibrarydata.pro | 23 ++ .../files/mandatory_attribute_missing.cfg | 4 + .../files/podtype_valid.cfg | 5 + .../files/smartptr_valid.cfg | 6 + .../files/typechecks_valid.cfg | 15 ++ .../files/unhandled_element.cfg | 5 + .../files/xml_reader_error.cfg | 5 + gui/test/cppchecklibrarydata/resources.qrc | 10 + .../testcppchecklibrarydata.cpp | 199 ++++++++++++++++++ .../testcppchecklibrarydata.h | 45 ++++ 13 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 gui/test/cppchecklibrarydata/cppchecklibrarydata.pro create mode 100644 gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg create mode 100644 gui/test/cppchecklibrarydata/files/podtype_valid.cfg create mode 100644 gui/test/cppchecklibrarydata/files/smartptr_valid.cfg create mode 100644 gui/test/cppchecklibrarydata/files/typechecks_valid.cfg create mode 100644 gui/test/cppchecklibrarydata/files/unhandled_element.cfg create mode 100644 gui/test/cppchecklibrarydata/files/xml_reader_error.cfg create mode 100644 gui/test/cppchecklibrarydata/resources.qrc create mode 100644 gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp create mode 100644 gui/test/cppchecklibrarydata/testcppchecklibrarydata.h diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 5e342ae0f..41e10462a 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -94,6 +94,11 @@ jobs: qmake make -j$(nproc) ./test-projectfile + popd + pushd gui/test/cppchecklibrarydata + qmake + make -j$(nproc) + ./test-cppchecklibrarydata - name: Generate Qt help file on ubuntu if: contains(matrix.os, 'ubuntu') diff --git a/gui/cppchecklibrarydata.cpp b/gui/cppchecklibrarydata.cpp index 7da428e64..793573347 100644 --- a/gui/cppchecklibrarydata.cpp +++ b/gui/cppchecklibrarydata.cpp @@ -106,6 +106,28 @@ static QString loadUndefine(const QXmlStreamReader &xmlReader) return xmlReader.attributes().value("name").toString(); } +static QString loadSmartPointer(const QXmlStreamReader &xmlReader) +{ + return xmlReader.attributes().value("class-name").toString(); +} + +static CppcheckLibraryData::TypeChecks loadTypeChecks(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::TypeChecks typeChecks; + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "type-checks") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "suppress" || elementName == "check") { + QPair entry(elementName, xmlReader.readElementText()); + typeChecks.append(entry); + } + } + return typeChecks; +} + static CppcheckLibraryData::Function::Arg loadFunctionArg(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Function::Arg arg; @@ -279,6 +301,10 @@ QString CppcheckLibraryData::open(QIODevice &file) memoryresource.append(loadMemoryResource(xmlReader)); else if (elementName == "podtype") podtypes.append(loadPodType(xmlReader)); + else if (elementName == "smart-pointer") + smartPointers.append(loadSmartPointer(xmlReader)); + else if (elementName == "type-checks") + typeChecks.append(loadTypeChecks(xmlReader)); else unhandledElement(xmlReader); } catch (std::runtime_error &e) { @@ -501,6 +527,24 @@ static void writeMemoryResource(QXmlStreamWriter &xmlWriter, const CppcheckLibra xmlWriter.writeEndElement(); } +static void writeTypeChecks(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::TypeChecks &typeChecks) +{ + QPair check; + xmlWriter.writeStartElement("type-checks"); + if (!typeChecks.isEmpty()) { + xmlWriter.writeStartElement("unusedvar"); + } + foreach (check, typeChecks) { + xmlWriter.writeStartElement(check.first); + xmlWriter.writeCharacters(check.second); + xmlWriter.writeEndElement(); + } + if (!typeChecks.isEmpty()) { + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + QString CppcheckLibraryData::toString() const { QString outputString; @@ -548,6 +592,16 @@ QString CppcheckLibraryData::toString() const xmlWriter.writeEndElement(); } + foreach (const TypeChecks check, typeChecks) { + writeTypeChecks(xmlWriter, check); + } + + foreach (const QString &smartPtr, smartPointers) { + xmlWriter.writeStartElement("smart-pointer"); + xmlWriter.writeAttribute("class-name", smartPtr); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); return outputString; diff --git a/gui/cppchecklibrarydata.h b/gui/cppchecklibrarydata.h index 5da837311..52a18964a 100644 --- a/gui/cppchecklibrarydata.h +++ b/gui/cppchecklibrarydata.h @@ -171,6 +171,8 @@ public: QString sign; }; + using TypeChecks = QList>; + void clear() { containers.clear(); defines.clear(); @@ -178,9 +180,10 @@ public: functions.clear(); memoryresource.clear(); podtypes.clear(); + smartPointers.clear(); + typeChecks.clear(); } - void swap(CppcheckLibraryData &other) { containers.swap(other.containers); defines.swap(other.defines); @@ -188,6 +191,8 @@ public: functions.swap(other.functions); memoryresource.swap(other.memoryresource); podtypes.swap(other.podtypes); + smartPointers.swap(other.smartPointers); + typeChecks.swap(other.typeChecks); } QString open(QIODevice &file); @@ -198,7 +203,9 @@ public: QList functions; QList memoryresource; QList podtypes; + QList typeChecks; QStringList undefines; + QStringList smartPointers; }; #endif // CPPCHECKLIBRARYDATA_H diff --git a/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro b/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro new file mode 100644 index 000000000..8eedcaccd --- /dev/null +++ b/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +TARGET = test-cppchecklibrarydata +DEPENDPATH += . +INCLUDEPATH += . +OBJECTS_DIR = ../build +MOC_DIR = ../build + +QT -= gui +QT += core +CONFIG += console + +include(../common.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +SOURCES += testcppchecklibrarydata.cpp \ + ../../cppchecklibrarydata.cpp + +HEADERS += testcppchecklibrarydata.h \ + ../../cppchecklibrarydata.h \ + +RESOURCES += \ + resources.qrc diff --git a/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg b/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg new file mode 100644 index 000000000..12601c935 --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/files/podtype_valid.cfg b/gui/test/cppchecklibrarydata/files/podtype_valid.cfg new file mode 100644 index 000000000..348c12aa9 --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/podtype_valid.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg b/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg new file mode 100644 index 000000000..1ae007ac6 --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg b/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg new file mode 100644 index 000000000..42cc5e736 --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg @@ -0,0 +1,15 @@ + + + + + std::insert_iterator + std::pair + + + + + + std::tuple + + + diff --git a/gui/test/cppchecklibrarydata/files/unhandled_element.cfg b/gui/test/cppchecklibrarydata/files/unhandled_element.cfg new file mode 100644 index 000000000..d9821227b --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/unhandled_element.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg b/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg new file mode 100644 index 000000000..5c397386d --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/resources.qrc b/gui/test/cppchecklibrarydata/resources.qrc new file mode 100644 index 000000000..521edd834 --- /dev/null +++ b/gui/test/cppchecklibrarydata/resources.qrc @@ -0,0 +1,10 @@ + + + files/xml_reader_error.cfg + files/mandatory_attribute_missing.cfg + files/unhandled_element.cfg + files/typechecks_valid.cfg + files/podtype_valid.cfg + files/smartptr_valid.cfg + + diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp new file mode 100644 index 000000000..2a52cf803 --- /dev/null +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp @@ -0,0 +1,199 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testcppchecklibrarydata.h" + +const QString TestCppcheckLibraryData::TempCfgFile = "./tmp.cfg"; + +void TestCppcheckLibraryData::init() +{ + result.clear(); + libraryData.clear(); + fileLibraryData.clear(); +} + +void TestCppcheckLibraryData::xmlReaderError() +{ + loadCfgFile(":/files/xml_reader_error.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::unhandledElement() +{ + loadCfgFile(":/files/unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::mandatoryAttributeMissing() +{ + loadCfgFile(":/files/mandatory_attribute_missing.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::podtypeValid() +{ + // Load library data from file + loadCfgFile(":/files/podtype_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap libray data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.podtypes.size(), 2); + + QCOMPARE(libraryData.podtypes[0].name, "bool"); + QCOMPARE(libraryData.podtypes[0].stdtype.isEmpty(), true); + QCOMPARE(libraryData.podtypes[0].sign.isEmpty(), true); + QCOMPARE(libraryData.podtypes[0].size.isEmpty(), true); + + QCOMPARE(libraryData.podtypes[1].name, "ulong"); + QCOMPARE(libraryData.podtypes[1].stdtype, "uint32_t"); + QCOMPARE(libraryData.podtypes[1].sign, "u"); + QCOMPARE(libraryData.podtypes[1].size, "4"); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.podtypes.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.podtypes.size(), fileLibraryData.podtypes.size()); + QCOMPARE(libraryData.podtypes.size(), 2); + for (int i=0; i < libraryData.podtypes.size(); i++) { + QCOMPARE(libraryData.podtypes[i].name, fileLibraryData.podtypes[i].name); + QCOMPARE(libraryData.podtypes[i].stdtype, fileLibraryData.podtypes[i].stdtype); + QCOMPARE(libraryData.podtypes[i].sign, fileLibraryData.podtypes[i].sign); + QCOMPARE(libraryData.podtypes[i].size, fileLibraryData.podtypes[i].size); + } +} + +void TestCppcheckLibraryData::typechecksValid() +{ + // Load library data from file + loadCfgFile(":/files/typechecks_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap libray data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.typeChecks.size(), 3); + + CppcheckLibraryData::TypeChecks check = libraryData.typeChecks[0]; + QCOMPARE(check.size(), 2); + QCOMPARE(check[0].first, "suppress"); + QCOMPARE(check[0].second, "std::insert_iterator"); + QCOMPARE(check[1].first, "check"); + QCOMPARE(check[1].second, "std::pair"); + + check = libraryData.typeChecks[1]; + QCOMPARE(check.isEmpty(), true); + + check = libraryData.typeChecks[2]; + QCOMPARE(check.size(), 1); + QCOMPARE(check[0].first, "check"); + QCOMPARE(check[0].second, "std::tuple"); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.typeChecks.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.typeChecks.size(), fileLibraryData.typeChecks.size()); + QCOMPARE(libraryData.typeChecks.size(), 3); + for (int idx=0; idx < libraryData.typeChecks.size(); idx++) { + CppcheckLibraryData::TypeChecks lhs = libraryData.typeChecks[idx]; + CppcheckLibraryData::TypeChecks rhs = fileLibraryData.typeChecks[idx]; + QCOMPARE(lhs.size(), lhs.size()); + for (int num=0; num < lhs.size(); num++) { + QCOMPARE(lhs[num].first, rhs[num].first); + QCOMPARE(lhs[num].second, rhs[num].second); + } + } +} + +void TestCppcheckLibraryData::smartPointerValid() +{ + // Load library data from file + loadCfgFile(":/files/smartptr_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap libray data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.smartPointers.size(), 3); + + QCOMPARE(libraryData.smartPointers[0], "wxObjectDataPtr"); + QCOMPARE(libraryData.smartPointers[1], "wxScopedArray"); + QCOMPARE(libraryData.smartPointers[2], "wxScopedPtr"); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.smartPointers.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.smartPointers.size(), fileLibraryData.smartPointers.size()); + QCOMPARE(libraryData.smartPointers.size(), 3); + for (int i=0; i < libraryData.smartPointers.size(); i++) { + QCOMPARE(libraryData.smartPointers[i], fileLibraryData.smartPointers[i]); + } +} + +void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile) +{ + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + result = data.open(file); + file.close(); + if (removeFile) { + file.remove(); + } +} + +void TestCppcheckLibraryData::saveCfgFile(QString filename, CppcheckLibraryData &data) +{ + QFile file(filename); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + QTextStream textStream(&file); + textStream << data.toString() << '\n'; + file.close(); +} + +QTEST_MAIN(TestCppcheckLibraryData) diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h new file mode 100644 index 000000000..f7ea48338 --- /dev/null +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h @@ -0,0 +1,45 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "cppchecklibrarydata.h" + +class TestCppcheckLibraryData: public QObject { + Q_OBJECT + +private slots: + void init(); + + void xmlReaderError(); + void unhandledElement(); + void mandatoryAttributeMissing(); + + void podtypeValid(); + void typechecksValid(); + void smartPointerValid(); + +private: + void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile = false); + void saveCfgFile(QString filename, CppcheckLibraryData &data); + + CppcheckLibraryData libraryData; + CppcheckLibraryData fileLibraryData; + QString result; + + static const QString TempCfgFile; +};