1 /*
2 SPDX-FileCopyrightText: 2016 Anton Anikin <anton.anikin@htower.ru>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "parameters.h"
8
9 #include "globalsettings.h"
10 #include "projectsettings.h"
11
12 #include <interfaces/iplugin.h>
13 #include <interfaces/iproject.h>
14 #include <project/interfaces/ibuildsystemmanager.h>
15 #include <project/projectmodel.h>
16
17 #include <KShell>
18 #include <KLocalizedString>
19
20 #include <QFile>
21 #include <QRegularExpression>
22
23 namespace cppcheck
24 {
25
includesForItem(KDevelop::ProjectBaseItem * parent,QSet<KDevelop::Path> & includes)26 void includesForItem(KDevelop::ProjectBaseItem* parent, QSet<KDevelop::Path>& includes)
27 {
28 const auto children = parent->children();
29 for (auto* child : children) {
30 if (child->type() == KDevelop::ProjectBaseItem::ProjectItemType::File) {
31 continue;
32 }
33
34 else if (child->type() == KDevelop::ProjectBaseItem::ProjectItemType::ExecutableTarget ||
35 child->type() == KDevelop::ProjectBaseItem::ProjectItemType::LibraryTarget ||
36 child->type() == KDevelop::ProjectBaseItem::ProjectItemType::Target) {
37
38 if (auto buildSystemManager = child->project()->buildSystemManager()) {
39 const auto includeDirectories = buildSystemManager->includeDirectories(child);
40 for (auto& dir : includeDirectories) {
41 includes.insert(dir);
42 }
43 }
44 }
45
46 includesForItem(child, includes);
47 }
48 }
49
includesForProject(KDevelop::IProject * project)50 QList<KDevelop::Path> includesForProject(KDevelop::IProject* project)
51 {
52 QSet<KDevelop::Path> includesSet;
53 includesForItem(project->projectItem(), includesSet);
54
55 return includesSet.values();
56 }
57
Parameters(KDevelop::IProject * project)58 Parameters::Parameters(KDevelop::IProject* project)
59 : m_project(project)
60 {
61 executablePath = KDevelop::Path(GlobalSettings::executablePath()).toLocalFile();
62 hideOutputView = GlobalSettings::hideOutputView();
63 showXmlOutput = GlobalSettings::showXmlOutput();
64
65 if (!project) {
66 checkStyle = defaults::checkStyle;
67 checkPerformance = defaults::checkPerformance;
68 checkPortability = defaults::checkPortability;
69 checkInformation = defaults::checkInformation;
70 checkUnusedFunction = defaults::checkUnusedFunction;
71 checkMissingInclude = defaults::checkMissingInclude;
72 inconclusiveAnalysis = defaults::inconclusiveAnalysis;
73 forceCheck = defaults::forceCheck;
74 checkConfig = defaults::checkConfig;
75
76 useProjectIncludes = defaults::useProjectIncludes;
77 useSystemIncludes = defaults::useSystemIncludes;
78
79 return;
80 }
81
82 ProjectSettings projectSettings;
83 projectSettings.setSharedConfig(project->projectConfiguration());
84 projectSettings.load();
85
86 checkStyle = projectSettings.checkStyle();
87 checkPerformance = projectSettings.checkPerformance();
88 checkPortability = projectSettings.checkPortability();
89 checkInformation = projectSettings.checkInformation();
90 checkUnusedFunction = projectSettings.checkUnusedFunction();
91 checkMissingInclude = projectSettings.checkMissingInclude();
92 inconclusiveAnalysis = projectSettings.inconclusiveAnalysis();
93 forceCheck = projectSettings.forceCheck();
94 checkConfig = projectSettings.checkConfig();
95
96 useProjectIncludes = projectSettings.useProjectIncludes();
97 useSystemIncludes = projectSettings.useSystemIncludes();
98 ignoredIncludes = projectSettings.ignoredIncludes();
99
100 extraParameters = projectSettings.extraParameters();
101
102 m_projectRootPath = m_project->path();
103
104 if (auto buildSystemManager = m_project->buildSystemManager()) {
105 m_projectBuildPath = buildSystemManager->buildDirectory(m_project->projectItem());
106 }
107 m_includeDirectories = includesForProject(project);
108 }
109
commandLine() const110 QStringList Parameters::commandLine() const
111 {
112 QString temp;
113 return commandLine(temp);
114 }
115
commandLine(QString & infoMessage) const116 QStringList Parameters::commandLine(QString& infoMessage) const
117 {
118 static const auto mocHeaderRegex = QRegularExpression(QStringLiteral("#define\\s+Q_MOC_OUTPUT_REVISION\\s+(.+)"));
119 static const auto mocParametersRegex = QRegularExpression(QStringLiteral("-DQ_MOC_OUTPUT_REVISION=\\d{2,}"));
120
121 const QString mocMessage = i18n(
122 "It seems that this project uses Qt library. For correctly work of cppcheck "
123 "the value for define Q_MOC_OUTPUT_REVISION must be set. Unfortunately, the plugin is unable "
124 "to find this value automatically - you should set it manually by adding "
125 "'-DQ_MOC_OUTPUT_REVISION=XX' to extra parameters. The 'XX' value can be found in any project's "
126 "moc-generated file or in the <QtCore/qobjectdefs.h> header file.");
127
128 QStringList result;
129
130 result << executablePath;
131 result << QStringLiteral("--xml-version=2");
132
133 if (checkStyle) {
134 result << QStringLiteral("--enable=style");
135 }
136
137 if (checkPerformance) {
138 result << QStringLiteral("--enable=performance");
139 }
140
141 if (checkPortability) {
142 result << QStringLiteral("--enable=portability");
143 }
144
145 if (checkInformation) {
146 result << QStringLiteral("--enable=information");
147 }
148
149 if (checkUnusedFunction) {
150 result << QStringLiteral("--enable=unusedFunction");
151 }
152
153 if (checkMissingInclude) {
154 result << QStringLiteral("--enable=missingInclude");
155 }
156
157 if (inconclusiveAnalysis) {
158 result << QStringLiteral("--inconclusive");
159 }
160
161 if (forceCheck) {
162 result << QStringLiteral("--force");
163 }
164
165 if (checkConfig) {
166 result << QStringLiteral("--check-config");
167 }
168
169 // Try to automatically get value of Q_MOC_OUTPUT_REVISION for Qt-projects.
170 // If such define is not correctly set, cppcheck 'fails' on files with moc-includes
171 // and not return any errors, even if the file contains them.
172 if (!mocParametersRegex.match(extraParameters).hasMatch()) {
173 bool qtUsed = false;
174 bool mocDefineFound = false;
175 for (const auto& dir : m_includeDirectories) {
176 if (dir.path().endsWith(QLatin1String("QtCore"))) {
177 qtUsed = true;
178
179 QFile qtHeader(dir.path() + QStringLiteral("/qobjectdefs.h"));
180 if (!qtHeader.open(QIODevice::ReadOnly)) {
181 break;
182 }
183
184 while(!qtHeader.atEnd()) {
185 auto match = mocHeaderRegex.match(QString::fromUtf8(qtHeader.readLine()));
186 if (match.hasMatch()) {
187 mocDefineFound = true;
188 result << QLatin1String("-DQ_MOC_OUTPUT_REVISION=") + match.capturedRef(1);
189 break;
190 }
191 }
192 break;
193 }
194 }
195
196 if (qtUsed && !mocDefineFound) {
197 infoMessage = mocMessage;
198 }
199 }
200
201 if (!extraParameters.isEmpty()) {
202 result << KShell::splitArgs(applyPlaceholders(extraParameters));
203 }
204
205 if (m_project && useProjectIncludes) {
206 QList<KDevelop::Path> ignored;
207
208 const auto elements = applyPlaceholders(ignoredIncludes).split(QLatin1Char(';'));
209 for (const QString& element : elements) {
210 if (!element.trimmed().isEmpty()) {
211 ignored.append(KDevelop::Path(element));
212 }
213 }
214
215 for (const auto& dir : m_includeDirectories) {
216 if (ignored.contains(dir)) {
217 continue;
218 }
219
220 else if (useSystemIncludes ||
221 dir == m_projectRootPath || m_projectRootPath.isParentOf(dir) ||
222 dir == m_projectBuildPath || m_projectBuildPath.isParentOf(dir)) {
223
224 result << QStringLiteral("-I");
225 result << dir.toLocalFile();
226 }
227 }
228 }
229
230 if (m_project && m_project->managerPlugin()) {
231 if (m_project->managerPlugin()->componentName() == QLatin1String("kdevcmakemanager")) {
232 result << QStringLiteral("-i")
233 << m_projectBuildPath.toLocalFile() + QLatin1String("/CMakeFiles");
234 }
235 }
236
237 result << checkPath;
238
239 return result;
240 }
241
projectRootPath() const242 KDevelop::Path Parameters::projectRootPath() const
243 {
244 return m_projectRootPath;
245 }
246
applyPlaceholders(const QString & text) const247 QString Parameters::applyPlaceholders(const QString& text) const
248 {
249 QString result(text);
250
251 if (m_project) {
252 result.replace(QLatin1String("%p"), m_projectRootPath.toLocalFile());
253 result.replace(QLatin1String("%b"), m_projectBuildPath.toLocalFile());
254 }
255
256 return result;
257 }
258
259 }
260