/*
* 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 "projectfile.h"
#include
#include
#include
#include "common.h"
#include "importproject.h"
#include "settings.h"
ProjectFile *ProjectFile::mActiveProject;
ProjectFile::ProjectFile(QObject *parent) :
QObject(parent)
{
clear();
}
ProjectFile::ProjectFile(const QString &filename, QObject *parent) :
QObject(parent),
mFilename(filename)
{
clear();
read();
}
void ProjectFile::clear()
{
const Settings settings;
clangParser = false;
bugHunting = false;
mRootPath.clear();
mBuildDir.clear();
mImportProject.clear();
mAnalyzeAllVsConfigs = true;
mIncludeDirs.clear();
mDefines.clear();
mUndefines.clear();
mPaths.clear();
mExcludedPaths.clear();
mFunctionContracts.clear();
mVariableContracts.clear();
mLibraries.clear();
mPlatform.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();
}
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() == CppcheckXml::ProjectElementName) {
insideProject = true;
projectTagFound = true;
break;
}
if (!insideProject)
break;
// Read root path from inside project element
if (xmlReader.name() == CppcheckXml::RootPathName)
readRootPath(xmlReader);
// Read root path from inside project element
if (xmlReader.name() == CppcheckXml::BuildDirElementName)
readBuildDir(xmlReader);
// Find paths to check from inside project element
if (xmlReader.name() == CppcheckXml::PathsElementName)
readCheckPaths(xmlReader);
if (xmlReader.name() == CppcheckXml::ImportProjectElementName)
readImportProject(xmlReader);
if (xmlReader.name() == CppcheckXml::AnalyzeAllVsConfigsElementName)
mAnalyzeAllVsConfigs = readBool(xmlReader);
if (xmlReader.name() == CppcheckXml::Parser)
clangParser = true;
if (xmlReader.name() == CppcheckXml::BugHunting)
bugHunting = true;
if (xmlReader.name() == CppcheckXml::CheckHeadersElementName)
mCheckHeaders = readBool(xmlReader);
if (xmlReader.name() == CppcheckXml::CheckUnusedTemplatesElementName)
mCheckUnusedTemplates = readBool(xmlReader);
// Find include directory from inside project element
if (xmlReader.name() == CppcheckXml::IncludeDirElementName)
readIncludeDirs(xmlReader);
// Find preprocessor define from inside project element
if (xmlReader.name() == CppcheckXml::DefinesElementName)
readDefines(xmlReader);
// Find preprocessor define from inside project element
if (xmlReader.name() == CppcheckXml::UndefinesElementName)
readStringList(mUndefines, xmlReader, CppcheckXml::UndefineName);
// Find exclude list from inside project element
if (xmlReader.name() == CppcheckXml::ExcludeElementName)
readExcludes(xmlReader);
// Find ignore list from inside project element
// These are read for compatibility
if (xmlReader.name() == CppcheckXml::IgnoreElementName)
readExcludes(xmlReader);
// Function contracts
if (xmlReader.name() == CppcheckXml::FunctionContracts)
readFunctionContracts(xmlReader);
// Variable constraints
if (xmlReader.name() == CppcheckXml::VariableContractsElementName)
readVariableContracts(xmlReader);
// Find libraries list from inside project element
if (xmlReader.name() == CppcheckXml::LibrariesElementName)
readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName);
if (xmlReader.name() == CppcheckXml::PlatformElementName)
readPlatform(xmlReader);
// Find suppressions list from inside project element
if (xmlReader.name() == CppcheckXml::SuppressionsElementName)
readSuppressions(xmlReader);
// Unknown function return values
if (xmlReader.name() == CppcheckXml::CheckUnknownFunctionReturn)
readStringList(mCheckUnknownFunctionReturn, xmlReader, CppcheckXml::Name);
// check all function parameter values
if (xmlReader.name() == Settings::SafeChecks::XmlRootName)
safeChecks.loadFromXml(xmlReader);
// Addons
if (xmlReader.name() == CppcheckXml::AddonsElementName)
readStringList(mAddons, xmlReader, CppcheckXml::AddonElementName);
// Tools
if (xmlReader.name() == CppcheckXml::ToolsElementName) {
QStringList tools;
readStringList(tools, xmlReader, CppcheckXml::ToolElementName);
mClangAnalyzer = tools.contains(CLANG_ANALYZER);
mClangTidy = tools.contains(CLANG_TIDY);
}
if (xmlReader.name() == CppcheckXml::TagsElementName)
readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
if (xmlReader.name() == CppcheckXml::TagWarningsElementName)
readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString());
if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName)
mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
if (xmlReader.name() == CppcheckXml::MaxTemplateRecursionElementName)
mMaxTemplateRecursion = readInt(xmlReader, mMaxTemplateRecursion);
// VSConfiguration
if (xmlReader.name() == CppcheckXml::VSConfigurationElementName)
readVsConfigurations(xmlReader);
break;
case QXmlStreamReader::EndElement:
if (xmlReader.name() == 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);
}
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