Fix library data markup (#3118)

This commit is contained in:
Mathias Schmid 2021-02-09 13:53:32 +01:00 committed by GitHub
parent f48e195c31
commit 25fa7c55d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 365 additions and 0 deletions

View File

@ -22,6 +22,7 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include <QVariant>
const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U; const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U;
const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U; const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U;
@ -321,6 +322,108 @@ static CppcheckLibraryData::Reflection loadReflection(QXmlStreamReader &xmlReade
return reflection; 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) QString CppcheckLibraryData::open(QIODevice &file)
{ {
clear(); clear();
@ -359,6 +462,8 @@ QString CppcheckLibraryData::open(QIODevice &file)
platformTypes.append(loadPlatformType(xmlReader)); platformTypes.append(loadPlatformType(xmlReader));
else if (elementName == "reflection") else if (elementName == "reflection")
reflections.append(loadReflection(xmlReader)); reflections.append(loadReflection(xmlReader));
else if (elementName == "markup")
markups.append(loadMarkup(xmlReader));
else else
unhandledElement(xmlReader); unhandledElement(xmlReader);
} catch (std::runtime_error &e) { } catch (std::runtime_error &e) {
@ -630,6 +735,68 @@ static void writeReflection(QXmlStreamWriter &xmlWriter, const CppcheckLibraryDa
xmlWriter.writeEndElement(); 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 CppcheckLibraryData::toString() const
{ {
QString outputString; QString outputString;
@ -695,6 +862,10 @@ QString CppcheckLibraryData::toString() const
writeReflection(xmlWriter, refl); writeReflection(xmlWriter, refl);
} }
foreach (const Markup &mup, markups) {
writeMarkup(xmlWriter, mup);
}
xmlWriter.writeEndElement(); xmlWriter.writeEndElement();
return outputString; return outputString;

View File

@ -193,6 +193,33 @@ public:
QList<struct Call> calls; QList<struct Call> 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> codeBlocks;
QList<Exporter> exporter;
};
void clear() { void clear() {
containers.clear(); containers.clear();
defines.clear(); defines.clear();
@ -204,6 +231,7 @@ public:
typeChecks.clear(); typeChecks.clear();
platformTypes.clear(); platformTypes.clear();
reflections.clear(); reflections.clear();
markups.clear();
} }
void swap(CppcheckLibraryData &other) { void swap(CppcheckLibraryData &other) {
@ -217,6 +245,7 @@ public:
typeChecks.swap(other.typeChecks); typeChecks.swap(other.typeChecks);
platformTypes.swap(other.platformTypes); platformTypes.swap(other.platformTypes);
reflections.swap(other.reflections); reflections.swap(other.reflections);
markups.swap(other.markups);
} }
QString open(QIODevice &file); QString open(QIODevice &file);
@ -232,6 +261,7 @@ public:
QStringList undefines; QStringList undefines;
QStringList smartPointers; QStringList smartPointers;
QList<struct Reflection> reflections; QList<struct Reflection> reflections;
QList<struct Markup> markups;
}; };
#endif // CPPCHECKLIBRARYDATA_H #endif // CPPCHECKLIBRARYDATA_H

View File

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<def format="2">
<!-- error: invalid attribute reporterror -->
<markup ext=".qml" reporterror="false" aftercode="true">
</markup>
</def>

View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<def format="2">
<!-- Test data from qt.cfg -->
<markup ext=".qml" reporterrors="false" aftercode="true">
<keywords>
<keyword name="if"/>
<keyword name="while"/>
<keyword name="typeof"/>
<keyword name="for"/>
</keywords>
<codeblocks>
<block name="onClicked"/>
<block name="onFinished"/>
<block name="onTriggered"/>
<block name="onPressed"/>
<block name="onTouch"/>
<structure offset="3" start="{" end="}"/>
</codeblocks>
<codeblocks>
<block name="function"/>
<structure offset="2" start="{" end="}"/>
</codeblocks>
<!-- error: invalid element export -->
<export>
<exporter prefix="Q_PROPERTY">
<suffix>READ</suffix>
<prefix>READ</prefix>
<prefix>WRITE</prefix>
<prefix>NOTIFY</prefix>
</exporter>
</export>
<imported>
<importer>connect</importer>
</imported>
</markup>
</def>

View File

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<def format="2">
<!-- Test data from qt.cfg -->
<markup ext=".qml" reporterrors="false" aftercode="true">
<keywords>
<keyword name="if"/>
<keyword name="while"/>
<keyword name="typeof"/>
<keyword name="for"/>
</keywords>
<codeblocks>
<block name="onClicked"/>
<block name="onFinished"/>
<block name="onTriggered"/>
<block name="onPressed"/>
<block name="onTouch"/>
<structure offset="3" start="{" end="}"/>
</codeblocks>
<codeblocks>
<block name="function"/>
<structure offset="2" start="{" end="}"/>
</codeblocks>
<exported>
<exporter prefix="Q_PROPERTY">
<suffix>READ</suffix>
<prefix>READ</prefix>
<prefix>WRITE</prefix>
<prefix>NOTIFY</prefix>
</exporter>
</exported>
<imported>
<importer>connect</importer>
</imported>
</markup>
</def>

View File

@ -16,5 +16,8 @@
<file>files/reflection_unhandled_element.cfg</file> <file>files/reflection_unhandled_element.cfg</file>
<file>files/reflection_valid.cfg</file> <file>files/reflection_valid.cfg</file>
<file>files/reflection_mandatory_attribute_missing.cfg</file> <file>files/reflection_mandatory_attribute_missing.cfg</file>
<file>files/markup_mandatory_attribute_missing.cfg</file>
<file>files/markup_valid.cfg</file>
<file>files/markup_unhandled_element.cfg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -55,6 +55,10 @@ void TestCppcheckLibraryData::unhandledElement()
loadCfgFile(":/files/reflection_unhandled_element.cfg", fileLibraryData, result); loadCfgFile(":/files/reflection_unhandled_element.cfg", fileLibraryData, result);
QCOMPARE(result.isNull(), false); QCOMPARE(result.isNull(), false);
qDebug() << result; qDebug() << result;
loadCfgFile(":/files/markup_unhandled_element.cfg", fileLibraryData, result);
QCOMPARE(result.isNull(), false);
qDebug() << result;
} }
void TestCppcheckLibraryData::mandatoryAttributeMissing() void TestCppcheckLibraryData::mandatoryAttributeMissing()
@ -66,6 +70,10 @@ void TestCppcheckLibraryData::mandatoryAttributeMissing()
loadCfgFile(":/files/reflection_mandatory_attribute_missing.cfg", fileLibraryData, result); loadCfgFile(":/files/reflection_mandatory_attribute_missing.cfg", fileLibraryData, result);
QCOMPARE(result.isNull(), false); QCOMPARE(result.isNull(), false);
qDebug() << result; qDebug() << result;
loadCfgFile(":/files/markup_mandatory_attribute_missing.cfg", fileLibraryData, result);
QCOMPARE(result.isNull(), false);
qDebug() << result;
} }
void TestCppcheckLibraryData::podtypeValid() 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) void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile)
{ {
QFile file(filename); QFile file(filename);

View File

@ -37,6 +37,7 @@ private slots:
void defineValid(); void defineValid();
void undefineValid(); void undefineValid();
void reflectionValid(); void reflectionValid();
void markupValid();
private: private:
void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile = false); void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile = false);