This is a mess. The version is AUR is still outdated and also doesn't install anymore. Fedora 38 carries the latest version of it so use that now. Keep the old steps in case we need to switch again in the future.
1143 lines
38 KiB
C++
1143 lines
38 KiB
C++
/*
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
|
* Copyright (C) 2007-2023 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "projectfile.h"
|
|
|
|
#include "common.h"
|
|
#include "config.h"
|
|
#include "importproject.h"
|
|
#include "settings.h"
|
|
|
|
#include <utility>
|
|
|
|
#include <QFile>
|
|
#include <QDir>
|
|
#include <QIODevice>
|
|
#include <QLatin1String>
|
|
#include <QXmlStreamAttributes>
|
|
#include <QXmlStreamReader>
|
|
#include <QXmlStreamWriter>
|
|
|
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
|
#include <QStringRef>
|
|
#endif
|
|
|
|
ProjectFile *ProjectFile::mActiveProject;
|
|
|
|
ProjectFile::ProjectFile(QObject *parent) :
|
|
QObject(parent)
|
|
{
|
|
clear();
|
|
}
|
|
|
|
ProjectFile::ProjectFile(QString filename, QObject *parent) :
|
|
QObject(parent),
|
|
mFilename(std::move(filename))
|
|
{
|
|
clear();
|
|
read();
|
|
}
|
|
|
|
void ProjectFile::clear()
|
|
{
|
|
const Settings settings;
|
|
clangParser = false;
|
|
mCheckLevel = CheckLevel::normal;
|
|
mRootPath.clear();
|
|
mBuildDir.clear();
|
|
mImportProject.clear();
|
|
mIncludeDirs.clear();
|
|
mDefines.clear();
|
|
mUndefines.clear();
|
|
mPaths.clear();
|
|
mExcludedPaths.clear();
|
|
mLibraries.clear();
|
|
mPlatform.clear();
|
|
mProjectName.clear();
|
|
mSuppressions.clear();
|
|
mAddons.clear();
|
|
mClangAnalyzer = mClangTidy = false;
|
|
mAnalyzeAllVsConfigs = false;
|
|
mCheckHeaders = true;
|
|
mCheckUnusedTemplates = true;
|
|
mMaxCtuDepth = settings.maxCtuDepth;
|
|
mMaxTemplateRecursion = settings.maxTemplateRecursion;
|
|
mCheckUnknownFunctionReturn.clear();
|
|
safeChecks.clear();
|
|
mVsConfigurations.clear();
|
|
mTags.clear();
|
|
mWarningTags.clear();
|
|
|
|
// Premium
|
|
mBughunting = false;
|
|
mCertIntPrecision = 0;
|
|
mCodingStandards.clear();
|
|
}
|
|
|
|
bool ProjectFile::read(const QString &filename)
|
|
{
|
|
if (!filename.isEmpty())
|
|
mFilename = filename;
|
|
|
|
QFile file(mFilename);
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
return false;
|
|
|
|
clear();
|
|
|
|
QXmlStreamReader xmlReader(&file);
|
|
bool insideProject = false;
|
|
bool projectTagFound = false;
|
|
while (!xmlReader.atEnd()) {
|
|
switch (xmlReader.readNext()) {
|
|
case QXmlStreamReader::StartElement:
|
|
if (xmlReader.name() == QString(CppcheckXml::ProjectElementName)) {
|
|
insideProject = true;
|
|
projectTagFound = true;
|
|
break;
|
|
}
|
|
if (!insideProject)
|
|
break;
|
|
|
|
// Read root path from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::RootPathName))
|
|
readRootPath(xmlReader);
|
|
|
|
// Read root path from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::BuildDirElementName))
|
|
readBuildDir(xmlReader);
|
|
|
|
// Find paths to check from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::PathsElementName))
|
|
readCheckPaths(xmlReader);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::ImportProjectElementName))
|
|
readImportProject(xmlReader);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::AnalyzeAllVsConfigsElementName))
|
|
mAnalyzeAllVsConfigs = readBool(xmlReader);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::Parser))
|
|
clangParser = true;
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::CheckHeadersElementName))
|
|
mCheckHeaders = readBool(xmlReader);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::CheckUnusedTemplatesElementName))
|
|
mCheckUnusedTemplates = readBool(xmlReader);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::CheckLevelExhaustiveElementName))
|
|
mCheckLevel = CheckLevel::exhaustive;
|
|
|
|
// Find include directory from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::IncludeDirElementName))
|
|
readIncludeDirs(xmlReader);
|
|
|
|
// Find preprocessor define from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::DefinesElementName))
|
|
readDefines(xmlReader);
|
|
|
|
// Find preprocessor define from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::UndefinesElementName))
|
|
readStringList(mUndefines, xmlReader, CppcheckXml::UndefineName);
|
|
|
|
// Find exclude list from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::ExcludeElementName))
|
|
readExcludes(xmlReader);
|
|
|
|
// Find ignore list from inside project element
|
|
// These are read for compatibility
|
|
if (xmlReader.name() == QString(CppcheckXml::IgnoreElementName))
|
|
readExcludes(xmlReader);
|
|
|
|
// Find libraries list from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::LibrariesElementName))
|
|
readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::PlatformElementName))
|
|
readPlatform(xmlReader);
|
|
|
|
// Find suppressions list from inside project element
|
|
if (xmlReader.name() == QString(CppcheckXml::SuppressionsElementName))
|
|
readSuppressions(xmlReader);
|
|
|
|
// Unknown function return values
|
|
if (xmlReader.name() == QString(CppcheckXml::CheckUnknownFunctionReturn))
|
|
readStringList(mCheckUnknownFunctionReturn, xmlReader, CppcheckXml::Name);
|
|
|
|
// check all function parameter values
|
|
if (xmlReader.name() == QString(Settings::SafeChecks::XmlRootName))
|
|
safeChecks.loadFromXml(xmlReader);
|
|
|
|
// Addons
|
|
if (xmlReader.name() == QString(CppcheckXml::AddonsElementName))
|
|
readStringList(mAddons, xmlReader, CppcheckXml::AddonElementName);
|
|
|
|
// Tools
|
|
if (xmlReader.name() == QString(CppcheckXml::ToolsElementName)) {
|
|
QStringList tools;
|
|
readStringList(tools, xmlReader, CppcheckXml::ToolElementName);
|
|
mClangAnalyzer = tools.contains(CLANG_ANALYZER);
|
|
mClangTidy = tools.contains(CLANG_TIDY);
|
|
}
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::TagsElementName))
|
|
readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::TagWarningsElementName))
|
|
readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString());
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::MaxCtuDepthElementName))
|
|
mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
|
|
|
|
if (xmlReader.name() == QString(CppcheckXml::MaxTemplateRecursionElementName))
|
|
mMaxTemplateRecursion = readInt(xmlReader, mMaxTemplateRecursion);
|
|
|
|
// VSConfiguration
|
|
if (xmlReader.name() == QString(CppcheckXml::VSConfigurationElementName))
|
|
readVsConfigurations(xmlReader);
|
|
|
|
// Cppcheck Premium
|
|
if (xmlReader.name() == QString(CppcheckXml::BughuntingElementName))
|
|
mBughunting = true;
|
|
if (xmlReader.name() == QString(CppcheckXml::CodingStandardsElementName))
|
|
readStringList(mCodingStandards, xmlReader, CppcheckXml::CodingStandardElementName);
|
|
if (xmlReader.name() == QString(CppcheckXml::CertIntPrecisionElementName))
|
|
mCertIntPrecision = readInt(xmlReader, 0);
|
|
if (xmlReader.name() == QString(CppcheckXml::ProjectNameElementName))
|
|
mProjectName = readString(xmlReader);
|
|
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (xmlReader.name() == QString(CppcheckXml::ProjectElementName))
|
|
insideProject = false;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
return projectTagFound;
|
|
}
|
|
|
|
void ProjectFile::readRootPath(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::RootPathNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mRootPath = name;
|
|
}
|
|
|
|
void ProjectFile::readBuildDir(QXmlStreamReader &reader)
|
|
{
|
|
mBuildDir.clear();
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
mBuildDir = reader.text().toString();
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
void ProjectFile::readImportProject(QXmlStreamReader &reader)
|
|
{
|
|
mImportProject.clear();
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
mImportProject = reader.text().toString();
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
bool ProjectFile::readBool(QXmlStreamReader &reader)
|
|
{
|
|
bool ret = false;
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
ret = (reader.text().toString() == "true");
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return ret;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
int ProjectFile::readInt(QXmlStreamReader &reader, int defaultValue)
|
|
{
|
|
int ret = defaultValue;
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
ret = reader.text().toString().toInt();
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return ret;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
QString ProjectFile::readString(QXmlStreamReader &reader)
|
|
{
|
|
QString ret;
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
ret = reader.text().toString();
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return ret;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
void ProjectFile::readIncludeDirs(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
bool allRead = false;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
|
|
// Read dir-elements
|
|
if (reader.name().toString() == CppcheckXml::DirElementName) {
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::DirNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mIncludeDirs << name;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() == CppcheckXml::IncludeDirElementName)
|
|
allRead = true;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (!allRead);
|
|
}
|
|
|
|
void ProjectFile::readDefines(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
bool allRead = false;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read define-elements
|
|
if (reader.name().toString() == CppcheckXml::DefineName) {
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::DefineNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mDefines << name;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() == CppcheckXml::DefinesElementName)
|
|
allRead = true;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (!allRead);
|
|
}
|
|
|
|
void ProjectFile::readCheckPaths(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
bool allRead = false;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
|
|
// Read dir-elements
|
|
if (reader.name().toString() == CppcheckXml::PathName) {
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::PathNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mPaths << name;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() == CppcheckXml::PathsElementName)
|
|
allRead = true;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (!allRead);
|
|
}
|
|
|
|
void ProjectFile::readExcludes(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
bool allRead = false;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read exclude-elements
|
|
if (reader.name().toString() == CppcheckXml::ExcludePathName) {
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::ExcludePathNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mExcludedPaths << name;
|
|
}
|
|
// Read ignore-elements - deprecated but support reading them
|
|
else if (reader.name().toString() == CppcheckXml::IgnorePathName) {
|
|
QXmlStreamAttributes attribs = reader.attributes();
|
|
QString name = attribs.value(QString(), CppcheckXml::IgnorePathNameAttrib).toString();
|
|
if (!name.isEmpty())
|
|
mExcludedPaths << name;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() == CppcheckXml::IgnoreElementName)
|
|
allRead = true;
|
|
if (reader.name().toString() == CppcheckXml::ExcludeElementName)
|
|
allRead = true;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (!allRead);
|
|
}
|
|
|
|
void ProjectFile::readVsConfigurations(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read library-elements
|
|
if (reader.name().toString() == CppcheckXml::VSConfigurationName) {
|
|
QString config;
|
|
type = reader.readNext();
|
|
if (type == QXmlStreamReader::Characters) {
|
|
config = reader.text().toString();
|
|
}
|
|
mVsConfigurations << config;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() != CppcheckXml::VSConfigurationName)
|
|
return;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
void ProjectFile::readPlatform(QXmlStreamReader &reader)
|
|
{
|
|
do {
|
|
const QXmlStreamReader::TokenType type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::Characters:
|
|
mPlatform = reader.text().toString();
|
|
FALLTHROUGH;
|
|
case QXmlStreamReader::EndElement:
|
|
return;
|
|
// Not handled
|
|
case QXmlStreamReader::StartElement:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
|
|
void ProjectFile::readSuppressions(QXmlStreamReader &reader)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read library-elements
|
|
if (reader.name().toString() == CppcheckXml::SuppressionElementName) {
|
|
Suppressions::Suppression suppression;
|
|
if (reader.attributes().hasAttribute(QString(),"fileName"))
|
|
suppression.fileName = reader.attributes().value(QString(),"fileName").toString().toStdString();
|
|
if (reader.attributes().hasAttribute(QString(),"lineNumber"))
|
|
suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
|
|
if (reader.attributes().hasAttribute(QString(),"symbolName"))
|
|
suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString();
|
|
if (reader.attributes().hasAttribute(QString(),"hash"))
|
|
suppression.hash = reader.attributes().value(QString(),"hash").toULongLong();
|
|
type = reader.readNext();
|
|
if (type == QXmlStreamReader::Characters) {
|
|
suppression.errorId = reader.text().toString().toStdString();
|
|
}
|
|
mSuppressions << suppression;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() != CppcheckXml::SuppressionElementName)
|
|
return;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
|
|
void ProjectFile::readTagWarnings(QXmlStreamReader &reader, const QString &tag)
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read library-elements
|
|
if (reader.name().toString() == CppcheckXml::WarningElementName) {
|
|
const std::size_t hash = reader.attributes().value(QString(), CppcheckXml::HashAttributeName).toULongLong();
|
|
mWarningTags[hash] = tag;
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() != CppcheckXml::WarningElementName)
|
|
return;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
|
|
void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[])
|
|
{
|
|
QXmlStreamReader::TokenType type;
|
|
bool allRead = false;
|
|
do {
|
|
type = reader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
// Read library-elements
|
|
if (reader.name().toString() == elementname) {
|
|
type = reader.readNext();
|
|
if (type == QXmlStreamReader::Characters) {
|
|
QString text = reader.text().toString();
|
|
stringlist << text;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
if (reader.name().toString() != elementname)
|
|
allRead = true;
|
|
break;
|
|
|
|
// Not handled
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (!allRead);
|
|
}
|
|
|
|
void ProjectFile::setIncludes(const QStringList &includes)
|
|
{
|
|
mIncludeDirs = includes;
|
|
}
|
|
|
|
void ProjectFile::setDefines(const QStringList &defines)
|
|
{
|
|
mDefines = defines;
|
|
}
|
|
|
|
void ProjectFile::setUndefines(const QStringList &undefines)
|
|
{
|
|
mUndefines = undefines;
|
|
}
|
|
|
|
void ProjectFile::setCheckPaths(const QStringList &paths)
|
|
{
|
|
mPaths = paths;
|
|
}
|
|
|
|
void ProjectFile::setExcludedPaths(const QStringList &paths)
|
|
{
|
|
mExcludedPaths = paths;
|
|
}
|
|
|
|
void ProjectFile::setLibraries(const QStringList &libraries)
|
|
{
|
|
mLibraries = libraries;
|
|
}
|
|
|
|
void ProjectFile::setPlatform(const QString &platform)
|
|
{
|
|
mPlatform = platform;
|
|
}
|
|
|
|
void ProjectFile::setSuppressions(const QList<Suppressions::Suppression> &suppressions)
|
|
{
|
|
mSuppressions = suppressions;
|
|
}
|
|
|
|
void ProjectFile::addSuppression(const Suppressions::Suppression &suppression)
|
|
{
|
|
mSuppressions.append(suppression);
|
|
}
|
|
|
|
void ProjectFile::setAddons(const QStringList &addons)
|
|
{
|
|
mAddons = addons;
|
|
}
|
|
|
|
void ProjectFile::setVSConfigurations(const QStringList &vsConfigs)
|
|
{
|
|
mVsConfigurations = vsConfigs;
|
|
}
|
|
|
|
void ProjectFile::setCheckLevel(ProjectFile::CheckLevel checkLevel)
|
|
{
|
|
mCheckLevel = checkLevel;
|
|
}
|
|
|
|
bool ProjectFile::isCheckLevelExhaustive() const
|
|
{
|
|
return mCheckLevel == CheckLevel::exhaustive;
|
|
}
|
|
|
|
void ProjectFile::setWarningTags(std::size_t hash, const QString& tags)
|
|
{
|
|
if (tags.isEmpty())
|
|
mWarningTags.erase(hash);
|
|
else if (hash > 0)
|
|
mWarningTags[hash] = tags;
|
|
}
|
|
|
|
QString ProjectFile::getWarningTags(std::size_t hash) const
|
|
{
|
|
auto it = mWarningTags.find(hash);
|
|
return (it != mWarningTags.end()) ? it->second : QString();
|
|
}
|
|
|
|
bool ProjectFile::write(const QString &filename)
|
|
{
|
|
if (!filename.isEmpty())
|
|
mFilename = filename;
|
|
|
|
QFile file(mFilename);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
return false;
|
|
|
|
QXmlStreamWriter xmlWriter(&file);
|
|
xmlWriter.setAutoFormatting(true);
|
|
xmlWriter.writeStartDocument("1.0");
|
|
xmlWriter.writeStartElement(CppcheckXml::ProjectElementName);
|
|
xmlWriter.writeAttribute(CppcheckXml::ProjectVersionAttrib, CppcheckXml::ProjectFileVersion);
|
|
|
|
if (!mRootPath.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::RootPathName);
|
|
xmlWriter.writeAttribute(CppcheckXml::RootPathNameAttrib, mRootPath);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mBuildDir.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::BuildDirElementName);
|
|
xmlWriter.writeCharacters(mBuildDir);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mPlatform.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::PlatformElementName);
|
|
xmlWriter.writeCharacters(mPlatform);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mImportProject.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::ImportProjectElementName);
|
|
xmlWriter.writeCharacters(mImportProject);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
xmlWriter.writeStartElement(CppcheckXml::AnalyzeAllVsConfigsElementName);
|
|
xmlWriter.writeCharacters(mAnalyzeAllVsConfigs ? "true" : "false");
|
|
xmlWriter.writeEndElement();
|
|
|
|
if (clangParser) {
|
|
xmlWriter.writeStartElement(CppcheckXml::Parser);
|
|
xmlWriter.writeCharacters("clang");
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
xmlWriter.writeStartElement(CppcheckXml::CheckHeadersElementName);
|
|
xmlWriter.writeCharacters(mCheckHeaders ? "true" : "false");
|
|
xmlWriter.writeEndElement();
|
|
|
|
xmlWriter.writeStartElement(CppcheckXml::CheckUnusedTemplatesElementName);
|
|
xmlWriter.writeCharacters(mCheckUnusedTemplates ? "true" : "false");
|
|
xmlWriter.writeEndElement();
|
|
|
|
xmlWriter.writeStartElement(CppcheckXml::MaxCtuDepthElementName);
|
|
xmlWriter.writeCharacters(QString::number(mMaxCtuDepth));
|
|
xmlWriter.writeEndElement();
|
|
|
|
xmlWriter.writeStartElement(CppcheckXml::MaxTemplateRecursionElementName);
|
|
xmlWriter.writeCharacters(QString::number(mMaxTemplateRecursion));
|
|
xmlWriter.writeEndElement();
|
|
|
|
if (!mIncludeDirs.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::IncludeDirElementName);
|
|
for (const QString& incdir : mIncludeDirs) {
|
|
xmlWriter.writeStartElement(CppcheckXml::DirElementName);
|
|
xmlWriter.writeAttribute(CppcheckXml::DirNameAttrib, incdir);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mDefines.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::DefinesElementName);
|
|
for (const QString& define : mDefines) {
|
|
xmlWriter.writeStartElement(CppcheckXml::DefineName);
|
|
xmlWriter.writeAttribute(CppcheckXml::DefineNameAttrib, define);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mVsConfigurations.isEmpty()) {
|
|
writeStringList(xmlWriter,
|
|
mVsConfigurations,
|
|
CppcheckXml::VSConfigurationElementName,
|
|
CppcheckXml::VSConfigurationName);
|
|
}
|
|
|
|
writeStringList(xmlWriter,
|
|
mUndefines,
|
|
CppcheckXml::UndefinesElementName,
|
|
CppcheckXml::UndefineName);
|
|
|
|
if (!mPaths.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::PathsElementName);
|
|
for (const QString& path : mPaths) {
|
|
xmlWriter.writeStartElement(CppcheckXml::PathName);
|
|
xmlWriter.writeAttribute(CppcheckXml::PathNameAttrib, path);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mExcludedPaths.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::ExcludeElementName);
|
|
for (const QString& path : mExcludedPaths) {
|
|
xmlWriter.writeStartElement(CppcheckXml::ExcludePathName);
|
|
xmlWriter.writeAttribute(CppcheckXml::ExcludePathNameAttrib, path);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
writeStringList(xmlWriter,
|
|
mLibraries,
|
|
CppcheckXml::LibrariesElementName,
|
|
CppcheckXml::LibraryElementName);
|
|
|
|
if (!mSuppressions.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName);
|
|
for (const Suppressions::Suppression &suppression : mSuppressions) {
|
|
xmlWriter.writeStartElement(CppcheckXml::SuppressionElementName);
|
|
if (!suppression.fileName.empty())
|
|
xmlWriter.writeAttribute("fileName", QString::fromStdString(suppression.fileName));
|
|
if (suppression.lineNumber > 0)
|
|
xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
|
|
if (!suppression.symbolName.empty())
|
|
xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
|
|
if (suppression.hash > 0)
|
|
xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash));
|
|
if (!suppression.errorId.empty())
|
|
xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
writeStringList(xmlWriter,
|
|
mCheckUnknownFunctionReturn,
|
|
CppcheckXml::CheckUnknownFunctionReturn,
|
|
CppcheckXml::Name);
|
|
|
|
safeChecks.saveToXml(xmlWriter);
|
|
|
|
writeStringList(xmlWriter,
|
|
mAddons,
|
|
CppcheckXml::AddonsElementName,
|
|
CppcheckXml::AddonElementName);
|
|
|
|
QStringList tools;
|
|
if (mClangAnalyzer)
|
|
tools << CLANG_ANALYZER;
|
|
if (mClangTidy)
|
|
tools << CLANG_TIDY;
|
|
writeStringList(xmlWriter,
|
|
tools,
|
|
CppcheckXml::ToolsElementName,
|
|
CppcheckXml::ToolElementName);
|
|
|
|
writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName);
|
|
if (!mWarningTags.empty()) {
|
|
QStringList tags;
|
|
for (const auto& wt: mWarningTags) {
|
|
if (!tags.contains(wt.second))
|
|
tags.append(wt.second);
|
|
}
|
|
for (const QString &tag: tags) {
|
|
xmlWriter.writeStartElement(CppcheckXml::TagWarningsElementName);
|
|
xmlWriter.writeAttribute(CppcheckXml::TagAttributeName, tag);
|
|
for (const auto& wt: mWarningTags) {
|
|
if (wt.second == tag) {
|
|
xmlWriter.writeStartElement(CppcheckXml::WarningElementName);
|
|
xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(wt.first));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
}
|
|
|
|
if (mCheckLevel == CheckLevel::exhaustive) {
|
|
xmlWriter.writeStartElement(CppcheckXml::CheckLevelExhaustiveElementName);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
// Cppcheck Premium
|
|
if (mBughunting) {
|
|
xmlWriter.writeStartElement(CppcheckXml::BughuntingElementName);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
writeStringList(xmlWriter,
|
|
mCodingStandards,
|
|
CppcheckXml::CodingStandardsElementName,
|
|
CppcheckXml::CodingStandardElementName);
|
|
|
|
if (mCertIntPrecision > 0) {
|
|
xmlWriter.writeStartElement(CppcheckXml::CertIntPrecisionElementName);
|
|
xmlWriter.writeCharacters(QString::number(mCertIntPrecision));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
if (!mProjectName.isEmpty()) {
|
|
xmlWriter.writeStartElement(CppcheckXml::ProjectNameElementName);
|
|
xmlWriter.writeCharacters(mProjectName);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
xmlWriter.writeEndDocument();
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
void ProjectFile::writeStringList(QXmlStreamWriter &xmlWriter, const QStringList &stringlist, const char startelementname[], const char stringelementname[])
|
|
{
|
|
if (stringlist.isEmpty())
|
|
return;
|
|
|
|
xmlWriter.writeStartElement(startelementname);
|
|
for (const QString& str : stringlist) {
|
|
xmlWriter.writeStartElement(stringelementname);
|
|
xmlWriter.writeCharacters(str);
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
QStringList ProjectFile::fromNativeSeparators(const QStringList &paths)
|
|
{
|
|
QStringList ret;
|
|
for (const QString &path : paths)
|
|
ret << QDir::fromNativeSeparators(path);
|
|
return ret;
|
|
}
|
|
|
|
QStringList ProjectFile::getAddonsAndTools() const
|
|
{
|
|
QStringList ret(mAddons);
|
|
if (mClangAnalyzer)
|
|
ret << CLANG_ANALYZER;
|
|
if (mClangTidy)
|
|
ret << CLANG_TIDY;
|
|
return ret;
|
|
}
|
|
|
|
void ProjectFile::SafeChecks::loadFromXml(QXmlStreamReader &xmlReader)
|
|
{
|
|
classes = externalFunctions = internalFunctions = externalVariables = false;
|
|
|
|
int level = 0;
|
|
|
|
do {
|
|
const QXmlStreamReader::TokenType type = xmlReader.readNext();
|
|
switch (type) {
|
|
case QXmlStreamReader::StartElement:
|
|
++level;
|
|
if (xmlReader.name() == QString(Settings::SafeChecks::XmlClasses))
|
|
classes = true;
|
|
else if (xmlReader.name() == QString(Settings::SafeChecks::XmlExternalFunctions))
|
|
externalFunctions = true;
|
|
else if (xmlReader.name() == QString(Settings::SafeChecks::XmlInternalFunctions))
|
|
internalFunctions = true;
|
|
else if (xmlReader.name() == QString(Settings::SafeChecks::XmlExternalVariables))
|
|
externalVariables = true;
|
|
break;
|
|
case QXmlStreamReader::EndElement:
|
|
if (level <= 0)
|
|
return;
|
|
level--;
|
|
break;
|
|
// Not handled
|
|
case QXmlStreamReader::Characters:
|
|
case QXmlStreamReader::NoToken:
|
|
case QXmlStreamReader::Invalid:
|
|
case QXmlStreamReader::StartDocument:
|
|
case QXmlStreamReader::EndDocument:
|
|
case QXmlStreamReader::Comment:
|
|
case QXmlStreamReader::DTD:
|
|
case QXmlStreamReader::EntityReference:
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const
|
|
{
|
|
if (!classes && !externalFunctions && !internalFunctions && !externalVariables)
|
|
return;
|
|
xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlRootName));
|
|
if (classes) {
|
|
xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlClasses));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
if (externalFunctions) {
|
|
xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlExternalFunctions));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
if (internalFunctions) {
|
|
xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlInternalFunctions));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
if (externalVariables) {
|
|
xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlExternalVariables));
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
xmlWriter.writeEndElement();
|
|
}
|
|
|
|
QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon)
|
|
{
|
|
if (!filesDir.endsWith("/"))
|
|
filesDir += "/";
|
|
|
|
QStringList searchPaths;
|
|
searchPaths << filesDir << (filesDir + "addons/") << (filesDir + "../addons/")
|
|
#ifdef FILESDIR
|
|
<< (QLatin1String(FILESDIR) + "/addons/")
|
|
#endif
|
|
;
|
|
|
|
for (const QString& path : searchPaths) {
|
|
QString f = path + addon + ".py";
|
|
if (QFile(f).exists())
|
|
return f;
|
|
}
|
|
|
|
return QString();
|
|
}
|