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