1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2021 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "projectfile.h"
20 
21 #include <QFile>
22 #include <QDir>
23 #include <QXmlStreamReader>
24 #include "common.h"
25 #include "importproject.h"
26 
27 #include "settings.h"
28 
29 ProjectFile *ProjectFile::mActiveProject;
30 
ProjectFile(QObject * parent)31 ProjectFile::ProjectFile(QObject *parent) :
32     QObject(parent)
33 {
34     clear();
35 }
36 
ProjectFile(const QString & filename,QObject * parent)37 ProjectFile::ProjectFile(const QString &filename, QObject *parent) :
38     QObject(parent),
39     mFilename(filename)
40 {
41     clear();
42     read();
43 }
44 
clear()45 void ProjectFile::clear()
46 {
47     const Settings settings;
48     clangParser = false;
49     bugHunting = false;
50     mRootPath.clear();
51     mBuildDir.clear();
52     mImportProject.clear();
53     mAnalyzeAllVsConfigs = true;
54     mIncludeDirs.clear();
55     mDefines.clear();
56     mUndefines.clear();
57     mPaths.clear();
58     mExcludedPaths.clear();
59     mFunctionContracts.clear();
60     mVariableContracts.clear();
61     mLibraries.clear();
62     mPlatform.clear();
63     mSuppressions.clear();
64     mAddons.clear();
65     mClangAnalyzer = mClangTidy = false;
66     mAnalyzeAllVsConfigs = false;
67     mCheckHeaders = true;
68     mCheckUnusedTemplates = true;
69     mMaxCtuDepth = settings.maxCtuDepth;
70     mMaxTemplateRecursion = settings.maxTemplateRecursion;
71     mCheckUnknownFunctionReturn.clear();
72     safeChecks.clear();
73     mVsConfigurations.clear();
74     mTags.clear();
75     mWarningTags.clear();
76 }
77 
read(const QString & filename)78 bool ProjectFile::read(const QString &filename)
79 {
80     if (!filename.isEmpty())
81         mFilename = filename;
82 
83     QFile file(mFilename);
84     if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
85         return false;
86 
87     clear();
88 
89     QXmlStreamReader xmlReader(&file);
90     bool insideProject = false;
91     bool projectTagFound = false;
92     while (!xmlReader.atEnd()) {
93         switch (xmlReader.readNext()) {
94         case QXmlStreamReader::StartElement:
95             if (xmlReader.name() == CppcheckXml::ProjectElementName) {
96                 insideProject = true;
97                 projectTagFound = true;
98                 break;
99             }
100             if (!insideProject)
101                 break;
102 
103             // Read root path from inside project element
104             if (xmlReader.name() == CppcheckXml::RootPathName)
105                 readRootPath(xmlReader);
106 
107             // Read root path from inside project element
108             if (xmlReader.name() == CppcheckXml::BuildDirElementName)
109                 readBuildDir(xmlReader);
110 
111             // Find paths to check from inside project element
112             if (xmlReader.name() == CppcheckXml::PathsElementName)
113                 readCheckPaths(xmlReader);
114 
115             if (xmlReader.name() == CppcheckXml::ImportProjectElementName)
116                 readImportProject(xmlReader);
117 
118             if (xmlReader.name() == CppcheckXml::AnalyzeAllVsConfigsElementName)
119                 mAnalyzeAllVsConfigs = readBool(xmlReader);
120 
121             if (xmlReader.name() == CppcheckXml::Parser)
122                 clangParser = true;
123 
124             if (xmlReader.name() == CppcheckXml::BugHunting)
125                 bugHunting = true;
126 
127             if (xmlReader.name() == CppcheckXml::CheckHeadersElementName)
128                 mCheckHeaders = readBool(xmlReader);
129 
130             if (xmlReader.name() == CppcheckXml::CheckUnusedTemplatesElementName)
131                 mCheckUnusedTemplates = readBool(xmlReader);
132 
133             // Find include directory from inside project element
134             if (xmlReader.name() == CppcheckXml::IncludeDirElementName)
135                 readIncludeDirs(xmlReader);
136 
137             // Find preprocessor define from inside project element
138             if (xmlReader.name() == CppcheckXml::DefinesElementName)
139                 readDefines(xmlReader);
140 
141             // Find preprocessor define from inside project element
142             if (xmlReader.name() == CppcheckXml::UndefinesElementName)
143                 readStringList(mUndefines, xmlReader, CppcheckXml::UndefineName);
144 
145             // Find exclude list from inside project element
146             if (xmlReader.name() == CppcheckXml::ExcludeElementName)
147                 readExcludes(xmlReader);
148 
149             // Find ignore list from inside project element
150             // These are read for compatibility
151             if (xmlReader.name() == CppcheckXml::IgnoreElementName)
152                 readExcludes(xmlReader);
153 
154             // Function contracts
155             if (xmlReader.name() == CppcheckXml::FunctionContracts)
156                 readFunctionContracts(xmlReader);
157 
158             // Variable constraints
159             if (xmlReader.name() == CppcheckXml::VariableContractsElementName)
160                 readVariableContracts(xmlReader);
161 
162             // Find libraries list from inside project element
163             if (xmlReader.name() == CppcheckXml::LibrariesElementName)
164                 readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName);
165 
166             if (xmlReader.name() == CppcheckXml::PlatformElementName)
167                 readPlatform(xmlReader);
168 
169             // Find suppressions list from inside project element
170             if (xmlReader.name() == CppcheckXml::SuppressionsElementName)
171                 readSuppressions(xmlReader);
172 
173             // Unknown function return values
174             if (xmlReader.name() == CppcheckXml::CheckUnknownFunctionReturn)
175                 readStringList(mCheckUnknownFunctionReturn, xmlReader, CppcheckXml::Name);
176 
177             // check all function parameter values
178             if (xmlReader.name() == Settings::SafeChecks::XmlRootName)
179                 safeChecks.loadFromXml(xmlReader);
180 
181             // Addons
182             if (xmlReader.name() == CppcheckXml::AddonsElementName)
183                 readStringList(mAddons, xmlReader, CppcheckXml::AddonElementName);
184 
185             // Tools
186             if (xmlReader.name() == CppcheckXml::ToolsElementName) {
187                 QStringList tools;
188                 readStringList(tools, xmlReader, CppcheckXml::ToolElementName);
189                 mClangAnalyzer = tools.contains(CLANG_ANALYZER);
190                 mClangTidy = tools.contains(CLANG_TIDY);
191             }
192 
193             if (xmlReader.name() == CppcheckXml::TagsElementName)
194                 readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
195 
196             if (xmlReader.name() == CppcheckXml::TagWarningsElementName)
197                 readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString());
198 
199             if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName)
200                 mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
201 
202             if (xmlReader.name() == CppcheckXml::MaxTemplateRecursionElementName)
203                 mMaxTemplateRecursion = readInt(xmlReader, mMaxTemplateRecursion);
204 
205             // VSConfiguration
206             if (xmlReader.name() == CppcheckXml::VSConfigurationElementName)
207                 readVsConfigurations(xmlReader);
208             break;
209 
210         case QXmlStreamReader::EndElement:
211             if (xmlReader.name() == CppcheckXml::ProjectElementName)
212                 insideProject = false;
213             break;
214 
215         // Not handled
216         case QXmlStreamReader::NoToken:
217         case QXmlStreamReader::Invalid:
218         case QXmlStreamReader::StartDocument:
219         case QXmlStreamReader::EndDocument:
220         case QXmlStreamReader::Characters:
221         case QXmlStreamReader::Comment:
222         case QXmlStreamReader::DTD:
223         case QXmlStreamReader::EntityReference:
224         case QXmlStreamReader::ProcessingInstruction:
225             break;
226         }
227     }
228 
229     file.close();
230     return projectTagFound;
231 }
232 
readRootPath(QXmlStreamReader & reader)233 void ProjectFile::readRootPath(QXmlStreamReader &reader)
234 {
235     QXmlStreamAttributes attribs = reader.attributes();
236     QString name = attribs.value(QString(), CppcheckXml::RootPathNameAttrib).toString();
237     if (!name.isEmpty())
238         mRootPath = name;
239 }
240 
readBuildDir(QXmlStreamReader & reader)241 void ProjectFile::readBuildDir(QXmlStreamReader &reader)
242 {
243     mBuildDir.clear();
244     do {
245         const QXmlStreamReader::TokenType type = reader.readNext();
246         switch (type) {
247         case QXmlStreamReader::Characters:
248             mBuildDir = reader.text().toString();
249             FALLTHROUGH;
250         case QXmlStreamReader::EndElement:
251             return;
252         // Not handled
253         case QXmlStreamReader::StartElement:
254         case QXmlStreamReader::NoToken:
255         case QXmlStreamReader::Invalid:
256         case QXmlStreamReader::StartDocument:
257         case QXmlStreamReader::EndDocument:
258         case QXmlStreamReader::Comment:
259         case QXmlStreamReader::DTD:
260         case QXmlStreamReader::EntityReference:
261         case QXmlStreamReader::ProcessingInstruction:
262             break;
263         }
264     } while (true);
265 }
266 
readImportProject(QXmlStreamReader & reader)267 void ProjectFile::readImportProject(QXmlStreamReader &reader)
268 {
269     mImportProject.clear();
270     do {
271         const QXmlStreamReader::TokenType type = reader.readNext();
272         switch (type) {
273         case QXmlStreamReader::Characters:
274             mImportProject = reader.text().toString();
275             FALLTHROUGH;
276         case QXmlStreamReader::EndElement:
277             return;
278         // Not handled
279         case QXmlStreamReader::StartElement:
280         case QXmlStreamReader::NoToken:
281         case QXmlStreamReader::Invalid:
282         case QXmlStreamReader::StartDocument:
283         case QXmlStreamReader::EndDocument:
284         case QXmlStreamReader::Comment:
285         case QXmlStreamReader::DTD:
286         case QXmlStreamReader::EntityReference:
287         case QXmlStreamReader::ProcessingInstruction:
288             break;
289         }
290     } while (true);
291 }
292 
readBool(QXmlStreamReader & reader)293 bool ProjectFile::readBool(QXmlStreamReader &reader)
294 {
295     bool ret = false;
296     do {
297         const QXmlStreamReader::TokenType type = reader.readNext();
298         switch (type) {
299         case QXmlStreamReader::Characters:
300             ret = (reader.text().toString() == "true");
301             FALLTHROUGH;
302         case QXmlStreamReader::EndElement:
303             return ret;
304         // Not handled
305         case QXmlStreamReader::StartElement:
306         case QXmlStreamReader::NoToken:
307         case QXmlStreamReader::Invalid:
308         case QXmlStreamReader::StartDocument:
309         case QXmlStreamReader::EndDocument:
310         case QXmlStreamReader::Comment:
311         case QXmlStreamReader::DTD:
312         case QXmlStreamReader::EntityReference:
313         case QXmlStreamReader::ProcessingInstruction:
314             break;
315         }
316     } while (true);
317 }
318 
readInt(QXmlStreamReader & reader,int defaultValue)319 int ProjectFile::readInt(QXmlStreamReader &reader, int defaultValue)
320 {
321     int ret = defaultValue;
322     do {
323         const QXmlStreamReader::TokenType type = reader.readNext();
324         switch (type) {
325         case QXmlStreamReader::Characters:
326             ret = reader.text().toString().toInt();
327             FALLTHROUGH;
328         case QXmlStreamReader::EndElement:
329             return ret;
330         // Not handled
331         case QXmlStreamReader::StartElement:
332         case QXmlStreamReader::NoToken:
333         case QXmlStreamReader::Invalid:
334         case QXmlStreamReader::StartDocument:
335         case QXmlStreamReader::EndDocument:
336         case QXmlStreamReader::Comment:
337         case QXmlStreamReader::DTD:
338         case QXmlStreamReader::EntityReference:
339         case QXmlStreamReader::ProcessingInstruction:
340             break;
341         }
342     } while (true);
343 }
344 
readIncludeDirs(QXmlStreamReader & reader)345 void ProjectFile::readIncludeDirs(QXmlStreamReader &reader)
346 {
347     QXmlStreamReader::TokenType type;
348     bool allRead = false;
349     do {
350         type = reader.readNext();
351         switch (type) {
352         case QXmlStreamReader::StartElement:
353 
354             // Read dir-elements
355             if (reader.name().toString() == CppcheckXml::DirElementName) {
356                 QXmlStreamAttributes attribs = reader.attributes();
357                 QString name = attribs.value(QString(), CppcheckXml::DirNameAttrib).toString();
358                 if (!name.isEmpty())
359                     mIncludeDirs << name;
360             }
361             break;
362 
363         case QXmlStreamReader::EndElement:
364             if (reader.name().toString() == CppcheckXml::IncludeDirElementName)
365                 allRead = true;
366             break;
367 
368         // Not handled
369         case QXmlStreamReader::NoToken:
370         case QXmlStreamReader::Invalid:
371         case QXmlStreamReader::StartDocument:
372         case QXmlStreamReader::EndDocument:
373         case QXmlStreamReader::Characters:
374         case QXmlStreamReader::Comment:
375         case QXmlStreamReader::DTD:
376         case QXmlStreamReader::EntityReference:
377         case QXmlStreamReader::ProcessingInstruction:
378             break;
379         }
380     } while (!allRead);
381 }
382 
readDefines(QXmlStreamReader & reader)383 void ProjectFile::readDefines(QXmlStreamReader &reader)
384 {
385     QXmlStreamReader::TokenType type;
386     bool allRead = false;
387     do {
388         type = reader.readNext();
389         switch (type) {
390         case QXmlStreamReader::StartElement:
391             // Read define-elements
392             if (reader.name().toString() == CppcheckXml::DefineName) {
393                 QXmlStreamAttributes attribs = reader.attributes();
394                 QString name = attribs.value(QString(), CppcheckXml::DefineNameAttrib).toString();
395                 if (!name.isEmpty())
396                     mDefines << name;
397             }
398             break;
399 
400         case QXmlStreamReader::EndElement:
401             if (reader.name().toString() == CppcheckXml::DefinesElementName)
402                 allRead = true;
403             break;
404 
405         // Not handled
406         case QXmlStreamReader::NoToken:
407         case QXmlStreamReader::Invalid:
408         case QXmlStreamReader::StartDocument:
409         case QXmlStreamReader::EndDocument:
410         case QXmlStreamReader::Characters:
411         case QXmlStreamReader::Comment:
412         case QXmlStreamReader::DTD:
413         case QXmlStreamReader::EntityReference:
414         case QXmlStreamReader::ProcessingInstruction:
415             break;
416         }
417     } while (!allRead);
418 }
419 
readCheckPaths(QXmlStreamReader & reader)420 void ProjectFile::readCheckPaths(QXmlStreamReader &reader)
421 {
422     QXmlStreamReader::TokenType type;
423     bool allRead = false;
424     do {
425         type = reader.readNext();
426         switch (type) {
427         case QXmlStreamReader::StartElement:
428 
429             // Read dir-elements
430             if (reader.name().toString() == CppcheckXml::PathName) {
431                 QXmlStreamAttributes attribs = reader.attributes();
432                 QString name = attribs.value(QString(), CppcheckXml::PathNameAttrib).toString();
433                 if (!name.isEmpty())
434                     mPaths << name;
435             }
436             break;
437 
438         case QXmlStreamReader::EndElement:
439             if (reader.name().toString() == CppcheckXml::PathsElementName)
440                 allRead = true;
441             break;
442 
443         // Not handled
444         case QXmlStreamReader::NoToken:
445         case QXmlStreamReader::Invalid:
446         case QXmlStreamReader::StartDocument:
447         case QXmlStreamReader::EndDocument:
448         case QXmlStreamReader::Characters:
449         case QXmlStreamReader::Comment:
450         case QXmlStreamReader::DTD:
451         case QXmlStreamReader::EntityReference:
452         case QXmlStreamReader::ProcessingInstruction:
453             break;
454         }
455     } while (!allRead);
456 }
457 
readExcludes(QXmlStreamReader & reader)458 void ProjectFile::readExcludes(QXmlStreamReader &reader)
459 {
460     QXmlStreamReader::TokenType type;
461     bool allRead = false;
462     do {
463         type = reader.readNext();
464         switch (type) {
465         case QXmlStreamReader::StartElement:
466             // Read exclude-elements
467             if (reader.name().toString() == CppcheckXml::ExcludePathName) {
468                 QXmlStreamAttributes attribs = reader.attributes();
469                 QString name = attribs.value(QString(), CppcheckXml::ExcludePathNameAttrib).toString();
470                 if (!name.isEmpty())
471                     mExcludedPaths << name;
472             }
473             // Read ignore-elements - deprecated but support reading them
474             else if (reader.name().toString() == CppcheckXml::IgnorePathName) {
475                 QXmlStreamAttributes attribs = reader.attributes();
476                 QString name = attribs.value(QString(), CppcheckXml::IgnorePathNameAttrib).toString();
477                 if (!name.isEmpty())
478                     mExcludedPaths << name;
479             }
480             break;
481 
482         case QXmlStreamReader::EndElement:
483             if (reader.name().toString() == CppcheckXml::IgnoreElementName)
484                 allRead = true;
485             if (reader.name().toString() == CppcheckXml::ExcludeElementName)
486                 allRead = true;
487             break;
488 
489         // Not handled
490         case QXmlStreamReader::NoToken:
491         case QXmlStreamReader::Invalid:
492         case QXmlStreamReader::StartDocument:
493         case QXmlStreamReader::EndDocument:
494         case QXmlStreamReader::Characters:
495         case QXmlStreamReader::Comment:
496         case QXmlStreamReader::DTD:
497         case QXmlStreamReader::EntityReference:
498         case QXmlStreamReader::ProcessingInstruction:
499             break;
500         }
501     } while (!allRead);
502 }
503 
readFunctionContracts(QXmlStreamReader & reader)504 void ProjectFile::readFunctionContracts(QXmlStreamReader &reader)
505 {
506     QXmlStreamReader::TokenType type;
507     bool allRead = false;
508     do {
509         type = reader.readNext();
510         switch (type) {
511         case QXmlStreamReader::StartElement:
512             if (reader.name().toString() == CppcheckXml::FunctionContract) {
513                 QXmlStreamAttributes attribs = reader.attributes();
514                 QString function = attribs.value(QString(), CppcheckXml::ContractFunction).toString();
515                 QString expects = attribs.value(QString(), CppcheckXml::ContractExpects).toString();
516                 if (!function.isEmpty() && !expects.isEmpty())
517                     mFunctionContracts[function.toStdString()] = expects.toStdString();
518             }
519             break;
520 
521         case QXmlStreamReader::EndElement:
522             if (reader.name().toString() == CppcheckXml::FunctionContracts)
523                 allRead = true;
524             break;
525 
526         // Not handled
527         case QXmlStreamReader::NoToken:
528         case QXmlStreamReader::Invalid:
529         case QXmlStreamReader::StartDocument:
530         case QXmlStreamReader::EndDocument:
531         case QXmlStreamReader::Characters:
532         case QXmlStreamReader::Comment:
533         case QXmlStreamReader::DTD:
534         case QXmlStreamReader::EntityReference:
535         case QXmlStreamReader::ProcessingInstruction:
536             break;
537         }
538     } while (!allRead);
539 }
540 
readVariableContracts(QXmlStreamReader & reader)541 void ProjectFile::readVariableContracts(QXmlStreamReader &reader)
542 {
543     QXmlStreamReader::TokenType type;
544     while (true) {
545         type = reader.readNext();
546         switch (type) {
547         case QXmlStreamReader::StartElement:
548             if (reader.name().toString() == CppcheckXml::VariableContractItemElementName) {
549                 QXmlStreamAttributes attribs = reader.attributes();
550                 QString varname = attribs.value(QString(), CppcheckXml::VariableContractVarName).toString();
551                 QString minValue = attribs.value(QString(), CppcheckXml::VariableContractMin).toString();
552                 QString maxValue = attribs.value(QString(), CppcheckXml::VariableContractMax).toString();
553                 setVariableContracts(varname, minValue, maxValue);
554             }
555             break;
556 
557         case QXmlStreamReader::EndElement:
558             if (reader.name().toString() == CppcheckXml::VariableContractsElementName)
559                 return;
560             break;
561 
562         // Not handled
563         case QXmlStreamReader::NoToken:
564         case QXmlStreamReader::Invalid:
565         case QXmlStreamReader::StartDocument:
566         case QXmlStreamReader::EndDocument:
567         case QXmlStreamReader::Characters:
568         case QXmlStreamReader::Comment:
569         case QXmlStreamReader::DTD:
570         case QXmlStreamReader::EntityReference:
571         case QXmlStreamReader::ProcessingInstruction:
572             break;
573         }
574     }
575 }
576 
readVsConfigurations(QXmlStreamReader & reader)577 void ProjectFile::readVsConfigurations(QXmlStreamReader &reader)
578 {
579     QXmlStreamReader::TokenType type;
580     do {
581         type = reader.readNext();
582         switch (type) {
583         case QXmlStreamReader::StartElement:
584             // Read library-elements
585             if (reader.name().toString() == CppcheckXml::VSConfigurationName) {
586                 QString config;
587                 type = reader.readNext();
588                 if (type == QXmlStreamReader::Characters) {
589                     config = reader.text().toString();
590                 }
591                 mVsConfigurations << config;
592             }
593             break;
594 
595         case QXmlStreamReader::EndElement:
596             if (reader.name().toString() != CppcheckXml::VSConfigurationName)
597                 return;
598             break;
599 
600         // Not handled
601         case QXmlStreamReader::NoToken:
602         case QXmlStreamReader::Invalid:
603         case QXmlStreamReader::StartDocument:
604         case QXmlStreamReader::EndDocument:
605         case QXmlStreamReader::Characters:
606         case QXmlStreamReader::Comment:
607         case QXmlStreamReader::DTD:
608         case QXmlStreamReader::EntityReference:
609         case QXmlStreamReader::ProcessingInstruction:
610             break;
611         }
612     } while (true);
613 }
614 
readPlatform(QXmlStreamReader & reader)615 void ProjectFile::readPlatform(QXmlStreamReader &reader)
616 {
617     do {
618         const QXmlStreamReader::TokenType type = reader.readNext();
619         switch (type) {
620         case QXmlStreamReader::Characters:
621             mPlatform = reader.text().toString();
622             FALLTHROUGH;
623         case QXmlStreamReader::EndElement:
624             return;
625         // Not handled
626         case QXmlStreamReader::StartElement:
627         case QXmlStreamReader::NoToken:
628         case QXmlStreamReader::Invalid:
629         case QXmlStreamReader::StartDocument:
630         case QXmlStreamReader::EndDocument:
631         case QXmlStreamReader::Comment:
632         case QXmlStreamReader::DTD:
633         case QXmlStreamReader::EntityReference:
634         case QXmlStreamReader::ProcessingInstruction:
635             break;
636         }
637     } while (true);
638 }
639 
640 
readSuppressions(QXmlStreamReader & reader)641 void ProjectFile::readSuppressions(QXmlStreamReader &reader)
642 {
643     QXmlStreamReader::TokenType type;
644     do {
645         type = reader.readNext();
646         switch (type) {
647         case QXmlStreamReader::StartElement:
648             // Read library-elements
649             if (reader.name().toString() == CppcheckXml::SuppressionElementName) {
650                 Suppressions::Suppression suppression;
651                 if (reader.attributes().hasAttribute(QString(),"fileName"))
652                     suppression.fileName = reader.attributes().value(QString(),"fileName").toString().toStdString();
653                 if (reader.attributes().hasAttribute(QString(),"lineNumber"))
654                     suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
655                 if (reader.attributes().hasAttribute(QString(),"symbolName"))
656                     suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString();
657                 if (reader.attributes().hasAttribute(QString(),"hash"))
658                     suppression.hash = reader.attributes().value(QString(),"hash").toULongLong();
659                 type = reader.readNext();
660                 if (type == QXmlStreamReader::Characters) {
661                     suppression.errorId = reader.text().toString().toStdString();
662                 }
663                 mSuppressions << suppression;
664             }
665             break;
666 
667         case QXmlStreamReader::EndElement:
668             if (reader.name().toString() != CppcheckXml::SuppressionElementName)
669                 return;
670             break;
671 
672         // Not handled
673         case QXmlStreamReader::NoToken:
674         case QXmlStreamReader::Invalid:
675         case QXmlStreamReader::StartDocument:
676         case QXmlStreamReader::EndDocument:
677         case QXmlStreamReader::Characters:
678         case QXmlStreamReader::Comment:
679         case QXmlStreamReader::DTD:
680         case QXmlStreamReader::EntityReference:
681         case QXmlStreamReader::ProcessingInstruction:
682             break;
683         }
684     } while (true);
685 }
686 
687 
readTagWarnings(QXmlStreamReader & reader,const QString & tag)688 void ProjectFile::readTagWarnings(QXmlStreamReader &reader, const QString &tag)
689 {
690     QXmlStreamReader::TokenType type;
691     do {
692         type = reader.readNext();
693         switch (type) {
694         case QXmlStreamReader::StartElement:
695             // Read library-elements
696             if (reader.name().toString() == CppcheckXml::WarningElementName) {
697                 std::size_t hash = reader.attributes().value(QString(), CppcheckXml::HashAttributeName).toULongLong();
698                 mWarningTags[hash] = tag;
699             }
700             break;
701 
702         case QXmlStreamReader::EndElement:
703             if (reader.name().toString() != CppcheckXml::WarningElementName)
704                 return;
705             break;
706 
707         // Not handled
708         case QXmlStreamReader::NoToken:
709         case QXmlStreamReader::Invalid:
710         case QXmlStreamReader::StartDocument:
711         case QXmlStreamReader::EndDocument:
712         case QXmlStreamReader::Characters:
713         case QXmlStreamReader::Comment:
714         case QXmlStreamReader::DTD:
715         case QXmlStreamReader::EntityReference:
716         case QXmlStreamReader::ProcessingInstruction:
717             break;
718         }
719     } while (true);
720 }
721 
722 
readStringList(QStringList & stringlist,QXmlStreamReader & reader,const char elementname[])723 void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[])
724 {
725     QXmlStreamReader::TokenType type;
726     bool allRead = false;
727     do {
728         type = reader.readNext();
729         switch (type) {
730         case QXmlStreamReader::StartElement:
731             // Read library-elements
732             if (reader.name().toString() == elementname) {
733                 type = reader.readNext();
734                 if (type == QXmlStreamReader::Characters) {
735                     QString text = reader.text().toString();
736                     stringlist << text;
737                 }
738             }
739             break;
740 
741         case QXmlStreamReader::EndElement:
742             if (reader.name().toString() != elementname)
743                 allRead = true;
744             break;
745 
746         // Not handled
747         case QXmlStreamReader::NoToken:
748         case QXmlStreamReader::Invalid:
749         case QXmlStreamReader::StartDocument:
750         case QXmlStreamReader::EndDocument:
751         case QXmlStreamReader::Characters:
752         case QXmlStreamReader::Comment:
753         case QXmlStreamReader::DTD:
754         case QXmlStreamReader::EntityReference:
755         case QXmlStreamReader::ProcessingInstruction:
756             break;
757         }
758     } while (!allRead);
759 }
760 
setIncludes(const QStringList & includes)761 void ProjectFile::setIncludes(const QStringList &includes)
762 {
763     mIncludeDirs = includes;
764 }
765 
setDefines(const QStringList & defines)766 void ProjectFile::setDefines(const QStringList &defines)
767 {
768     mDefines = defines;
769 }
770 
setUndefines(const QStringList & undefines)771 void ProjectFile::setUndefines(const QStringList &undefines)
772 {
773     mUndefines = undefines;
774 }
775 
setCheckPaths(const QStringList & paths)776 void ProjectFile::setCheckPaths(const QStringList &paths)
777 {
778     mPaths = paths;
779 }
780 
setExcludedPaths(const QStringList & paths)781 void ProjectFile::setExcludedPaths(const QStringList &paths)
782 {
783     mExcludedPaths = paths;
784 }
785 
setLibraries(const QStringList & libraries)786 void ProjectFile::setLibraries(const QStringList &libraries)
787 {
788     mLibraries = libraries;
789 }
790 
setFunctionContract(QString function,QString expects)791 void ProjectFile::setFunctionContract(QString function, QString expects)
792 {
793     mFunctionContracts[function.toStdString()] = expects.toStdString();
794 }
795 
setPlatform(const QString & platform)796 void ProjectFile::setPlatform(const QString &platform)
797 {
798     mPlatform = platform;
799 }
800 
setSuppressions(const QList<Suppressions::Suppression> & suppressions)801 void ProjectFile::setSuppressions(const QList<Suppressions::Suppression> &suppressions)
802 {
803     mSuppressions = suppressions;
804 }
805 
addSuppression(const Suppressions::Suppression & suppression)806 void ProjectFile::addSuppression(const Suppressions::Suppression &suppression)
807 {
808     mSuppressions.append(suppression);
809 }
810 
setAddons(const QStringList & addons)811 void ProjectFile::setAddons(const QStringList &addons)
812 {
813     mAddons = addons;
814 }
815 
setVSConfigurations(const QStringList & vsConfigs)816 void ProjectFile::setVSConfigurations(const QStringList &vsConfigs)
817 {
818     mVsConfigurations = vsConfigs;
819 }
820 
setWarningTags(std::size_t hash,QString tags)821 void ProjectFile::setWarningTags(std::size_t hash, QString tags)
822 {
823     if (tags.isEmpty())
824         mWarningTags.erase(hash);
825     else if (hash > 0)
826         mWarningTags[hash] = tags;
827 }
828 
getWarningTags(std::size_t hash) const829 QString ProjectFile::getWarningTags(std::size_t hash) const
830 {
831     auto it = mWarningTags.find(hash);
832     return (it != mWarningTags.end()) ? it->second : QString();
833 }
834 
write(const QString & filename)835 bool ProjectFile::write(const QString &filename)
836 {
837     if (!filename.isEmpty())
838         mFilename = filename;
839 
840     QFile file(mFilename);
841     if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
842         return false;
843 
844     QXmlStreamWriter xmlWriter(&file);
845     xmlWriter.setAutoFormatting(true);
846     xmlWriter.writeStartDocument("1.0");
847     xmlWriter.writeStartElement(CppcheckXml::ProjectElementName);
848     xmlWriter.writeAttribute(CppcheckXml::ProjectVersionAttrib, CppcheckXml::ProjectFileVersion);
849 
850     if (!mRootPath.isEmpty()) {
851         xmlWriter.writeStartElement(CppcheckXml::RootPathName);
852         xmlWriter.writeAttribute(CppcheckXml::RootPathNameAttrib, mRootPath);
853         xmlWriter.writeEndElement();
854     }
855 
856     if (!mBuildDir.isEmpty()) {
857         xmlWriter.writeStartElement(CppcheckXml::BuildDirElementName);
858         xmlWriter.writeCharacters(mBuildDir);
859         xmlWriter.writeEndElement();
860     }
861 
862     if (!mPlatform.isEmpty()) {
863         xmlWriter.writeStartElement(CppcheckXml::PlatformElementName);
864         xmlWriter.writeCharacters(mPlatform);
865         xmlWriter.writeEndElement();
866     }
867 
868     if (!mImportProject.isEmpty()) {
869         xmlWriter.writeStartElement(CppcheckXml::ImportProjectElementName);
870         xmlWriter.writeCharacters(mImportProject);
871         xmlWriter.writeEndElement();
872     }
873 
874     xmlWriter.writeStartElement(CppcheckXml::AnalyzeAllVsConfigsElementName);
875     xmlWriter.writeCharacters(mAnalyzeAllVsConfigs ? "true" : "false");
876     xmlWriter.writeEndElement();
877 
878     if (clangParser) {
879         xmlWriter.writeStartElement(CppcheckXml::Parser);
880         xmlWriter.writeCharacters("clang");
881         xmlWriter.writeEndElement();
882     }
883 
884     if (bugHunting) {
885         xmlWriter.writeStartElement(CppcheckXml::BugHunting);
886         xmlWriter.writeEndElement();
887     }
888 
889     xmlWriter.writeStartElement(CppcheckXml::CheckHeadersElementName);
890     xmlWriter.writeCharacters(mCheckHeaders ? "true" : "false");
891     xmlWriter.writeEndElement();
892 
893     xmlWriter.writeStartElement(CppcheckXml::CheckUnusedTemplatesElementName);
894     xmlWriter.writeCharacters(mCheckUnusedTemplates ? "true" : "false");
895     xmlWriter.writeEndElement();
896 
897     xmlWriter.writeStartElement(CppcheckXml::MaxCtuDepthElementName);
898     xmlWriter.writeCharacters(QString::number(mMaxCtuDepth));
899     xmlWriter.writeEndElement();
900 
901     xmlWriter.writeStartElement(CppcheckXml::MaxTemplateRecursionElementName);
902     xmlWriter.writeCharacters(QString::number(mMaxTemplateRecursion));
903     xmlWriter.writeEndElement();
904 
905     if (!mIncludeDirs.isEmpty()) {
906         xmlWriter.writeStartElement(CppcheckXml::IncludeDirElementName);
907         foreach (QString incdir, mIncludeDirs) {
908             xmlWriter.writeStartElement(CppcheckXml::DirElementName);
909             xmlWriter.writeAttribute(CppcheckXml::DirNameAttrib, incdir);
910             xmlWriter.writeEndElement();
911         }
912         xmlWriter.writeEndElement();
913     }
914 
915     if (!mDefines.isEmpty()) {
916         xmlWriter.writeStartElement(CppcheckXml::DefinesElementName);
917         foreach (QString define, mDefines) {
918             xmlWriter.writeStartElement(CppcheckXml::DefineName);
919             xmlWriter.writeAttribute(CppcheckXml::DefineNameAttrib, define);
920             xmlWriter.writeEndElement();
921         }
922         xmlWriter.writeEndElement();
923     }
924 
925     if (!mVsConfigurations.isEmpty()) {
926         writeStringList(xmlWriter,
927                         mVsConfigurations,
928                         CppcheckXml::VSConfigurationElementName,
929                         CppcheckXml::VSConfigurationName);
930     }
931 
932     writeStringList(xmlWriter,
933                     mUndefines,
934                     CppcheckXml::UndefinesElementName,
935                     CppcheckXml::UndefineName);
936 
937     if (!mPaths.isEmpty()) {
938         xmlWriter.writeStartElement(CppcheckXml::PathsElementName);
939         foreach (QString path, mPaths) {
940             xmlWriter.writeStartElement(CppcheckXml::PathName);
941             xmlWriter.writeAttribute(CppcheckXml::PathNameAttrib, path);
942             xmlWriter.writeEndElement();
943         }
944         xmlWriter.writeEndElement();
945     }
946 
947     if (!mExcludedPaths.isEmpty()) {
948         xmlWriter.writeStartElement(CppcheckXml::ExcludeElementName);
949         foreach (QString path, mExcludedPaths) {
950             xmlWriter.writeStartElement(CppcheckXml::ExcludePathName);
951             xmlWriter.writeAttribute(CppcheckXml::ExcludePathNameAttrib, path);
952             xmlWriter.writeEndElement();
953         }
954         xmlWriter.writeEndElement();
955     }
956 
957     writeStringList(xmlWriter,
958                     mLibraries,
959                     CppcheckXml::LibrariesElementName,
960                     CppcheckXml::LibraryElementName);
961 
962     if (!mFunctionContracts.empty()) {
963         xmlWriter.writeStartElement(CppcheckXml::FunctionContracts);
964         for (const auto& contract: mFunctionContracts) {
965             xmlWriter.writeStartElement(CppcheckXml::FunctionContract);
966             xmlWriter.writeAttribute(CppcheckXml::ContractFunction, QString::fromStdString(contract.first));
967             xmlWriter.writeAttribute(CppcheckXml::ContractExpects, QString::fromStdString(contract.second));
968             xmlWriter.writeEndElement();
969         }
970         xmlWriter.writeEndElement();
971     }
972 
973     if (!mVariableContracts.empty()) {
974         xmlWriter.writeStartElement(CppcheckXml::VariableContractsElementName);
975 
976         for (auto vc: mVariableContracts) {
977             xmlWriter.writeStartElement(CppcheckXml::VariableContractItemElementName);
978             xmlWriter.writeAttribute(CppcheckXml::VariableContractVarName, vc.first);
979             xmlWriter.writeAttribute(CppcheckXml::VariableContractMin, QString::fromStdString(vc.second.minValue));
980             xmlWriter.writeAttribute(CppcheckXml::VariableContractMax, QString::fromStdString(vc.second.maxValue));
981             xmlWriter.writeEndElement();
982         }
983 
984         xmlWriter.writeEndElement();
985     }
986 
987     if (!mSuppressions.isEmpty()) {
988         xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName);
989         foreach (const Suppressions::Suppression &suppression, mSuppressions) {
990             xmlWriter.writeStartElement(CppcheckXml::SuppressionElementName);
991             if (!suppression.fileName.empty())
992                 xmlWriter.writeAttribute("fileName", QString::fromStdString(suppression.fileName));
993             if (suppression.lineNumber > 0)
994                 xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
995             if (!suppression.symbolName.empty())
996                 xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
997             if (suppression.hash > 0)
998                 xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash));
999             if (!suppression.errorId.empty())
1000                 xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
1001             xmlWriter.writeEndElement();
1002         }
1003         xmlWriter.writeEndElement();
1004     }
1005 
1006     writeStringList(xmlWriter,
1007                     mCheckUnknownFunctionReturn,
1008                     CppcheckXml::CheckUnknownFunctionReturn,
1009                     CppcheckXml::Name);
1010 
1011     safeChecks.saveToXml(xmlWriter);
1012 
1013     writeStringList(xmlWriter,
1014                     mAddons,
1015                     CppcheckXml::AddonsElementName,
1016                     CppcheckXml::AddonElementName);
1017 
1018     QStringList tools;
1019     if (mClangAnalyzer)
1020         tools << CLANG_ANALYZER;
1021     if (mClangTidy)
1022         tools << CLANG_TIDY;
1023     writeStringList(xmlWriter,
1024                     tools,
1025                     CppcheckXml::ToolsElementName,
1026                     CppcheckXml::ToolElementName);
1027 
1028     writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName);
1029     if (!mWarningTags.empty()) {
1030         QStringList tags;
1031         for (const auto& wt: mWarningTags) {
1032             if (!tags.contains(wt.second))
1033                 tags.append(wt.second);
1034         }
1035         for (const QString &tag: tags) {
1036             xmlWriter.writeStartElement(CppcheckXml::TagWarningsElementName);
1037             xmlWriter.writeAttribute(CppcheckXml::TagAttributeName, tag);
1038             QStringList warnings;
1039             for (const auto& wt: mWarningTags) {
1040                 if (wt.second == tag) {
1041                     xmlWriter.writeStartElement(CppcheckXml::WarningElementName);
1042                     xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(wt.first));
1043                     xmlWriter.writeEndElement();
1044                 }
1045             }
1046             xmlWriter.writeEndElement();
1047         }
1048     }
1049 
1050     xmlWriter.writeEndDocument();
1051     file.close();
1052     return true;
1053 }
1054 
writeStringList(QXmlStreamWriter & xmlWriter,const QStringList & stringlist,const char startelementname[],const char stringelementname[])1055 void ProjectFile::writeStringList(QXmlStreamWriter &xmlWriter, const QStringList &stringlist, const char startelementname[], const char stringelementname[])
1056 {
1057     if (stringlist.isEmpty())
1058         return;
1059 
1060     xmlWriter.writeStartElement(startelementname);
1061     foreach (QString str, stringlist) {
1062         xmlWriter.writeStartElement(stringelementname);
1063         xmlWriter.writeCharacters(str);
1064         xmlWriter.writeEndElement();
1065     }
1066     xmlWriter.writeEndElement();
1067 }
1068 
fromNativeSeparators(const QStringList & paths)1069 QStringList ProjectFile::fromNativeSeparators(const QStringList &paths)
1070 {
1071     QStringList ret;
1072     foreach (const QString &path, paths)
1073     ret << QDir::fromNativeSeparators(path);
1074     return ret;
1075 }
1076 
getAddonsAndTools() const1077 QStringList ProjectFile::getAddonsAndTools() const
1078 {
1079     QStringList ret(mAddons);
1080     if (mClangAnalyzer)
1081         ret << CLANG_ANALYZER;
1082     if (mClangTidy)
1083         ret << CLANG_TIDY;
1084     return ret;
1085 }
1086 
loadFromXml(QXmlStreamReader & xmlReader)1087 void ProjectFile::SafeChecks::loadFromXml(QXmlStreamReader &xmlReader)
1088 {
1089     classes = externalFunctions = internalFunctions = externalVariables = false;
1090 
1091     int level = 0;
1092 
1093     do {
1094         const QXmlStreamReader::TokenType type = xmlReader.readNext();
1095         switch (type) {
1096         case QXmlStreamReader::StartElement:
1097             ++level;
1098             if (xmlReader.name() == Settings::SafeChecks::XmlClasses)
1099                 classes = true;
1100             else if (xmlReader.name() == Settings::SafeChecks::XmlExternalFunctions)
1101                 externalFunctions = true;
1102             else if (xmlReader.name() == Settings::SafeChecks::XmlInternalFunctions)
1103                 internalFunctions = true;
1104             else if (xmlReader.name() == Settings::SafeChecks::XmlExternalVariables)
1105                 externalVariables = true;
1106             break;
1107         case QXmlStreamReader::EndElement:
1108             if (level <= 0)
1109                 return;
1110             level--;
1111             break;
1112         // Not handled
1113         case QXmlStreamReader::Characters:
1114         case QXmlStreamReader::NoToken:
1115         case QXmlStreamReader::Invalid:
1116         case QXmlStreamReader::StartDocument:
1117         case QXmlStreamReader::EndDocument:
1118         case QXmlStreamReader::Comment:
1119         case QXmlStreamReader::DTD:
1120         case QXmlStreamReader::EntityReference:
1121         case QXmlStreamReader::ProcessingInstruction:
1122             break;
1123         }
1124     } while (true);
1125 }
1126 
saveToXml(QXmlStreamWriter & xmlWriter) const1127 void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const
1128 {
1129     if (!classes && !externalFunctions && !internalFunctions && !externalVariables)
1130         return;
1131     xmlWriter.writeStartElement(Settings::SafeChecks::XmlRootName);
1132     if (classes) {
1133         xmlWriter.writeStartElement(Settings::SafeChecks::XmlClasses);
1134         xmlWriter.writeEndElement();
1135     }
1136     if (externalFunctions) {
1137         xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalFunctions);
1138         xmlWriter.writeEndElement();
1139     }
1140     if (internalFunctions) {
1141         xmlWriter.writeStartElement(Settings::SafeChecks::XmlInternalFunctions);
1142         xmlWriter.writeEndElement();
1143     }
1144     if (externalVariables) {
1145         xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalVariables);
1146         xmlWriter.writeEndElement();
1147     }
1148     xmlWriter.writeEndElement();
1149 }
1150 
getAddonFilePath(QString filesDir,const QString & addon)1151 QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon)
1152 {
1153     if (!filesDir.endsWith("/"))
1154         filesDir += "/";
1155 
1156     QStringList searchPaths;
1157     searchPaths << filesDir << (filesDir + "addons/") << (filesDir + "../addons/")
1158 #ifdef FILESDIR
1159         << (QLatin1String(FILESDIR) + "/addons/")
1160 #endif
1161     ;
1162 
1163     foreach (QString path, searchPaths) {
1164         QString f = path + addon + ".py";
1165         if (QFile(f).exists())
1166             return f;
1167     }
1168 
1169     return QString();
1170 }
1171