diff --git a/gui/cppchecklibrarydata.cpp b/gui/cppchecklibrarydata.cpp index 377753ebe..b21ce8ee6 100644 --- a/gui/cppchecklibrarydata.cpp +++ b/gui/cppchecklibrarydata.cpp @@ -22,6 +22,7 @@ #include #include +#include const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U; const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U; @@ -321,6 +322,108 @@ static CppcheckLibraryData::Reflection loadReflection(QXmlStreamReader &xmlReade return reflection; } +static CppcheckLibraryData::Markup loadMarkup(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Markup markup; + + QXmlStreamReader::TokenType type; + if (xmlReader.attributes().hasAttribute("ext")) { + markup.ext = xmlReader.attributes().value("ext").toString(); + } + else { + mandatoryAttibuteMissing(xmlReader, "ext"); + } + if (xmlReader.attributes().hasAttribute("aftercode")) { + markup.afterCode = (xmlReader.attributes().value("aftercode") == "true") ? true : false; + } + else { + mandatoryAttibuteMissing(xmlReader, "aftercode"); + } + if (xmlReader.attributes().hasAttribute("reporterrors")) { + markup.reportErrors = (xmlReader.attributes().value("reporterrors") == "true") ? true : false; + } + else { + mandatoryAttibuteMissing(xmlReader, "reporterrors"); + } + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "markup") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "keywords") { + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "keywords") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "keyword") { + markup.keywords.append(xmlReader.attributes().value("name").toString()); + } + else { + unhandledElement(xmlReader); + } + } + } else if (elementName == "codeblocks") { + CppcheckLibraryData::Markup::CodeBlocks codeBlock; + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "codeblocks") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "block") { + codeBlock.blocks.append(xmlReader.attributes().value("name").toString()); + } + else if (xmlReader.name().toString() == "structure") { + codeBlock.offset = xmlReader.attributes().value("offset").toInt(); + codeBlock.start = xmlReader.attributes().value("start").toString(); + codeBlock.end = xmlReader.attributes().value("end").toString(); + } + else { + unhandledElement(xmlReader); + } + } + markup.codeBlocks.append(codeBlock); + } else if (elementName == "exported") { + CppcheckLibraryData::Markup::Exporter exporter; + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "exported") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "exporter") { + exporter.prefix = xmlReader.attributes().value("prefix").toString(); + } + else if (xmlReader.name().toString() == "prefix") { + exporter.prefixList.append(xmlReader.readElementText()); + } + else if (xmlReader.name().toString() == "suffix") { + exporter.suffixList.append(xmlReader.readElementText()); + } + else { + unhandledElement(xmlReader); + } + } + markup.exporter.append(exporter); + } else if (elementName == "imported") { + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "imported") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "importer") { + markup.importer.append(xmlReader.readElementText()); + } + else { + unhandledElement(xmlReader); + } + } + } else { + unhandledElement(xmlReader); + } + } + + return markup; +} + QString CppcheckLibraryData::open(QIODevice &file) { clear(); @@ -359,6 +462,8 @@ QString CppcheckLibraryData::open(QIODevice &file) platformTypes.append(loadPlatformType(xmlReader)); else if (elementName == "reflection") reflections.append(loadReflection(xmlReader)); + else if (elementName == "markup") + markups.append(loadMarkup(xmlReader)); else unhandledElement(xmlReader); } catch (std::runtime_error &e) { @@ -630,6 +735,68 @@ static void writeReflection(QXmlStreamWriter &xmlWriter, const CppcheckLibraryDa xmlWriter.writeEndElement(); } +static void writeMarkup(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Markup &mup) +{ + xmlWriter.writeStartElement("markup"); + xmlWriter.writeAttribute("ext", mup.ext); + xmlWriter.writeAttribute("aftercode", QVariant(mup.afterCode).toString()); + xmlWriter.writeAttribute("reporterrors", QVariant(mup.reportErrors).toString()); + if (!mup.keywords.isEmpty()) { + xmlWriter.writeStartElement("keywords"); + foreach (const QString &keyword, mup.keywords) { + xmlWriter.writeStartElement("keyword"); + xmlWriter.writeAttribute("name", keyword); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.importer.isEmpty()) { + xmlWriter.writeStartElement("imported"); + foreach (const QString &import, mup.importer) { + xmlWriter.writeStartElement("importer"); + xmlWriter.writeCharacters(import); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.exporter.isEmpty()) { + xmlWriter.writeStartElement("exported"); + foreach (const CppcheckLibraryData::Markup::Exporter exporter, mup.exporter) { + xmlWriter.writeStartElement("exporter"); + xmlWriter.writeAttribute("prefix", exporter.prefix); + foreach (const QString &prefix, exporter.prefixList) { + xmlWriter.writeStartElement("prefix"); + xmlWriter.writeCharacters(prefix); + xmlWriter.writeEndElement(); + } + foreach (const QString &suffix, exporter.suffixList) { + xmlWriter.writeStartElement("suffix"); + xmlWriter.writeCharacters(suffix); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.codeBlocks.isEmpty()) { + foreach (const CppcheckLibraryData::Markup::CodeBlocks codeblock, mup.codeBlocks) { + xmlWriter.writeStartElement("codeblocks"); + foreach (const QString &block, codeblock.blocks) { + xmlWriter.writeStartElement("block"); + xmlWriter.writeAttribute("name", block); + xmlWriter.writeEndElement(); + } + xmlWriter.writeStartElement("structure"); + xmlWriter.writeAttribute("offset", QString("%1").arg(codeblock.offset)); + xmlWriter.writeAttribute("start", codeblock.start); + xmlWriter.writeAttribute("end", codeblock.end); + xmlWriter.writeEndElement(); + xmlWriter.writeEndElement(); + } + } + xmlWriter.writeEndElement(); +} + QString CppcheckLibraryData::toString() const { QString outputString; @@ -695,6 +862,10 @@ QString CppcheckLibraryData::toString() const writeReflection(xmlWriter, refl); } + foreach (const Markup &mup, markups) { + writeMarkup(xmlWriter, mup); + } + xmlWriter.writeEndElement(); return outputString; diff --git a/gui/cppchecklibrarydata.h b/gui/cppchecklibrarydata.h index 9dee77acb..eb69553b9 100644 --- a/gui/cppchecklibrarydata.h +++ b/gui/cppchecklibrarydata.h @@ -193,6 +193,33 @@ public: QList calls; }; + struct Markup { + struct CodeBlocks { + CodeBlocks() : + offset {-1} + {} + + QStringList blocks; + int offset; + QString start; + QString end; + }; + + struct Exporter { + QString prefix; + QStringList prefixList; + QStringList suffixList; + }; + + QString ext; + bool afterCode; + bool reportErrors; + QStringList keywords; + QStringList importer; + QList codeBlocks; + QList exporter; + }; + void clear() { containers.clear(); defines.clear(); @@ -204,6 +231,7 @@ public: typeChecks.clear(); platformTypes.clear(); reflections.clear(); + markups.clear(); } void swap(CppcheckLibraryData &other) { @@ -217,6 +245,7 @@ public: typeChecks.swap(other.typeChecks); platformTypes.swap(other.platformTypes); reflections.swap(other.reflections); + markups.swap(other.markups); } QString open(QIODevice &file); @@ -232,6 +261,7 @@ public: QStringList undefines; QStringList smartPointers; QList reflections; + QList markups; }; #endif // CPPCHECKLIBRARYDATA_H diff --git a/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg b/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg new file mode 100644 index 000000000..f3a2ff4dd --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg @@ -0,0 +1,6 @@ + + + + + + diff --git a/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg b/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg new file mode 100644 index 000000000..5bfb80c9b --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + READ + READ + WRITE + NOTIFY + + + + connect + + + diff --git a/gui/test/cppchecklibrarydata/files/markup_valid.cfg b/gui/test/cppchecklibrarydata/files/markup_valid.cfg new file mode 100644 index 000000000..286c0c4f3 --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/markup_valid.cfg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + READ + READ + WRITE + NOTIFY + + + + connect + + + diff --git a/gui/test/cppchecklibrarydata/resources.qrc b/gui/test/cppchecklibrarydata/resources.qrc index 64119adb4..c6394211e 100644 --- a/gui/test/cppchecklibrarydata/resources.qrc +++ b/gui/test/cppchecklibrarydata/resources.qrc @@ -16,5 +16,8 @@ files/reflection_unhandled_element.cfg files/reflection_valid.cfg files/reflection_mandatory_attribute_missing.cfg + files/markup_mandatory_attribute_missing.cfg + files/markup_valid.cfg + files/markup_unhandled_element.cfg diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp index 8f34679aa..59c1403f0 100644 --- a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp @@ -55,6 +55,10 @@ void TestCppcheckLibraryData::unhandledElement() loadCfgFile(":/files/reflection_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; + + loadCfgFile(":/files/markup_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; } void TestCppcheckLibraryData::mandatoryAttributeMissing() @@ -66,6 +70,10 @@ void TestCppcheckLibraryData::mandatoryAttributeMissing() loadCfgFile(":/files/reflection_mandatory_attribute_missing.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; + + loadCfgFile(":/files/markup_mandatory_attribute_missing.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; } void TestCppcheckLibraryData::podtypeValid() @@ -457,6 +465,81 @@ void TestCppcheckLibraryData::reflectionValid() } } +void TestCppcheckLibraryData::markupValid() +{ + // Load library data from file + loadCfgFile(":/files/markup_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.markups.size(), 1); + QCOMPARE(libraryData.markups[0].ext, ".qml"); + QCOMPARE(libraryData.markups[0].reportErrors, false); + QCOMPARE(libraryData.markups[0].afterCode, true); + + QCOMPARE(libraryData.markups[0].keywords.size(), 4); + QCOMPARE(libraryData.markups[0].keywords, QStringList( {"if", "while", "typeof", "for"} )); + + QCOMPARE(libraryData.markups[0].importer.size(), 1); + QCOMPARE(libraryData.markups[0].importer, QStringList("connect")); + + QCOMPARE(libraryData.markups[0].exporter.size(), 1); + QCOMPARE(libraryData.markups[0].exporter[0].prefix, "Q_PROPERTY"); + QCOMPARE(libraryData.markups[0].exporter[0].suffixList.size(), 1); + QCOMPARE(libraryData.markups[0].exporter[0].suffixList, QStringList("READ")); + QCOMPARE(libraryData.markups[0].exporter[0].prefixList.size(), 3); + QCOMPARE(libraryData.markups[0].exporter[0].prefixList, QStringList( {"READ", "WRITE", "NOTIFY"} )); + + QCOMPARE(libraryData.markups[0].codeBlocks.size(), 2); + QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks.size(), 5); + QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks, QStringList( {"onClicked", "onFinished", "onTriggered", "onPressed", "onTouch"} )); + QCOMPARE(libraryData.markups[0].codeBlocks[0].offset, 3); + QCOMPARE(libraryData.markups[0].codeBlocks[0].start, "{"); + QCOMPARE(libraryData.markups[0].codeBlocks[0].end, "}"); + QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks.size(), 1); + QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks, QStringList("function")); + QCOMPARE(libraryData.markups[0].codeBlocks[1].offset, 2); + QCOMPARE(libraryData.markups[0].codeBlocks[1].start, "{"); + QCOMPARE(libraryData.markups[0].codeBlocks[1].end, "}"); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.markups.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.markups.size(), fileLibraryData.markups.size()); + for (int idx=0; idx < libraryData.markups.size(); idx++) { + CppcheckLibraryData::Markup lhs = libraryData.markups[idx]; + CppcheckLibraryData::Markup rhs = fileLibraryData.markups[idx]; + + QCOMPARE(lhs.ext, rhs.ext); + QCOMPARE(lhs.reportErrors, rhs.reportErrors); + QCOMPARE(lhs.afterCode, rhs.afterCode); + QCOMPARE(lhs.keywords, rhs.keywords); + QCOMPARE(lhs.importer, rhs.importer); + for (int num=0; num < lhs.exporter.size(); num++) { + QCOMPARE(lhs.exporter[num].prefix, rhs.exporter[num].prefix); + QCOMPARE(lhs.exporter[num].suffixList, rhs.exporter[num].suffixList); + QCOMPARE(lhs.exporter[num].prefixList, rhs.exporter[num].prefixList); + } + for (int num=0; num < lhs.codeBlocks.size(); num++) { + QCOMPARE(lhs.codeBlocks[num].blocks, rhs.codeBlocks[num].blocks); + QCOMPARE(lhs.codeBlocks[num].offset, rhs.codeBlocks[num].offset); + QCOMPARE(lhs.codeBlocks[num].start, rhs.codeBlocks[num].start); + QCOMPARE(lhs.codeBlocks[num].end, rhs.codeBlocks[num].end); + } + } +} + void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile) { QFile file(filename); diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h index 7448dae26..8cee20d7f 100644 --- a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h @@ -37,6 +37,7 @@ private slots: void defineValid(); void undefineValid(); void reflectionValid(); + void markupValid(); private: void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile = false);