1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "builtineditordocumentparser.h"
27 #include "cppsourceprocessor.h"
28
29 #include <projectexplorer/projectmacro.h>
30 #include <projectexplorer/projectexplorerconstants.h>
31
32 #include <utils/algorithm.h>
33 #include <utils/qtcassert.h>
34
35 using namespace CPlusPlus;
36 using namespace CppTools;
37 using namespace CppTools::Internal;
38
overwrittenToolchainDefines(const ProjectPart & projectPart)39 static QByteArray overwrittenToolchainDefines(const ProjectPart &projectPart)
40 {
41 QByteArray defines;
42
43 // MSVC's predefined macros like __FUNCSIG__ expand to itself.
44 // We can't parse this, so redefine to the empty string literal.
45 if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
46 defines += "#define __FUNCSIG__ \"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"\n"
47 "#define __FUNCDNAME__ \"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"\n"
48 "#define __FUNCTION__ \"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"\n";
49 }
50
51 return defines;
52 }
53
BuiltinEditorDocumentParser(const QString & filePath,int fileSizeLimitInMb)54 BuiltinEditorDocumentParser::BuiltinEditorDocumentParser(const QString &filePath,
55 int fileSizeLimitInMb)
56 : BaseEditorDocumentParser(filePath)
57 , m_fileSizeLimitInMb(fileSizeLimitInMb)
58 {
59 qRegisterMetaType<CPlusPlus::Snapshot>("CPlusPlus::Snapshot");
60 }
61
updateImpl(const QFutureInterface<void> & future,const UpdateParams & updateParams)62 void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &future,
63 const UpdateParams &updateParams)
64 {
65 if (filePath().isEmpty())
66 return;
67
68 const Configuration baseConfig = configuration();
69 const bool releaseSourceAndAST_ = releaseSourceAndAST();
70
71 State baseState = state();
72 ExtraState state = extraState();
73 WorkingCopy workingCopy = updateParams.workingCopy;
74
75 bool invalidateSnapshot = false, invalidateConfig = false, editorDefinesChanged_ = false;
76
77 CppModelManager *modelManager = CppModelManager::instance();
78 QByteArray configFile = modelManager->codeModelConfiguration();
79 ProjectExplorer::HeaderPaths headerPaths;
80 QStringList includedFiles;
81 QStringList precompiledHeaders;
82 QString projectConfigFile;
83 LanguageFeatures features = LanguageFeatures::defaultFeatures();
84
85 baseState.projectPartInfo = determineProjectPart(filePath(),
86 baseConfig.preferredProjectPartId,
87 baseState.projectPartInfo,
88 updateParams.activeProject,
89 updateParams.languagePreference,
90 updateParams.projectsUpdated);
91 emit projectPartInfoUpdated(baseState.projectPartInfo);
92
93 if (state.forceSnapshotInvalidation) {
94 invalidateSnapshot = true;
95 state.forceSnapshotInvalidation = false;
96 }
97
98 if (const ProjectPart::Ptr part = baseState.projectPartInfo.projectPart) {
99 configFile += ProjectExplorer::Macro::toByteArray(part->toolChainMacros);
100 configFile += overwrittenToolchainDefines(*part.data());
101 configFile += ProjectExplorer::Macro::toByteArray(part->projectMacros);
102 if (!part->projectConfigFile.isEmpty())
103 configFile += ProjectPart::readProjectConfigFile(part);
104 headerPaths = part->headerPaths;
105 projectConfigFile = part->projectConfigFile;
106 includedFiles = part->includedFiles;
107 if (baseConfig.usePrecompiledHeaders)
108 precompiledHeaders = part->precompiledHeaders;
109 features = part->languageFeatures;
110 }
111
112 if (configFile != state.configFile) {
113 state.configFile = configFile;
114 invalidateSnapshot = true;
115 invalidateConfig = true;
116 }
117
118 if (baseConfig.editorDefines != baseState.editorDefines) {
119 baseState.editorDefines = baseConfig.editorDefines;
120 invalidateSnapshot = true;
121 editorDefinesChanged_ = true;
122 }
123
124 if (headerPaths != state.headerPaths) {
125 state.headerPaths = headerPaths;
126 invalidateSnapshot = true;
127 }
128
129 if (projectConfigFile != state.projectConfigFile) {
130 state.projectConfigFile = projectConfigFile;
131 invalidateSnapshot = true;
132 }
133
134 if (includedFiles != state.includedFiles) {
135 state.includedFiles = includedFiles;
136 invalidateSnapshot = true;
137 }
138
139 if (precompiledHeaders != state.precompiledHeaders) {
140 state.precompiledHeaders = precompiledHeaders;
141 invalidateSnapshot = true;
142 }
143
144 unsigned rev = 0;
145 if (Document::Ptr doc = state.snapshot.document(filePath()))
146 rev = doc->revision();
147 else
148 invalidateSnapshot = true;
149
150 Snapshot globalSnapshot = modelManager->snapshot();
151
152 if (invalidateSnapshot) {
153 state.snapshot = Snapshot();
154 } else {
155 // Remove changed files from the snapshot
156 QSet<Utils::FilePath> toRemove;
157 foreach (const Document::Ptr &doc, state.snapshot) {
158 const Utils::FilePath fileName = Utils::FilePath::fromString(doc->fileName());
159 if (workingCopy.contains(fileName)) {
160 if (workingCopy.get(fileName).second != doc->editorRevision())
161 addFileAndDependencies(&state.snapshot, &toRemove, fileName);
162 continue;
163 }
164 Document::Ptr otherDoc = globalSnapshot.document(fileName);
165 if (!otherDoc.isNull() && otherDoc->revision() != doc->revision())
166 addFileAndDependencies(&state.snapshot, &toRemove, fileName);
167 }
168
169 if (!toRemove.isEmpty()) {
170 invalidateSnapshot = true;
171 foreach (const Utils::FilePath &fileName, toRemove)
172 state.snapshot.remove(fileName);
173 }
174 }
175
176 // Update the snapshot
177 if (invalidateSnapshot) {
178 const QString configurationFileName = CppModelManager::configurationFileName();
179 if (invalidateConfig)
180 state.snapshot.remove(configurationFileName);
181 if (!state.snapshot.contains(configurationFileName))
182 workingCopy.insert(configurationFileName, state.configFile);
183 state.snapshot.remove(filePath());
184
185 static const QString editorDefinesFileName
186 = CppModelManager::editorConfigurationFileName();
187 if (editorDefinesChanged_) {
188 state.snapshot.remove(editorDefinesFileName);
189 workingCopy.insert(editorDefinesFileName, baseState.editorDefines);
190 }
191
192 CppSourceProcessor sourceProcessor(state.snapshot, [&](const Document::Ptr &doc) {
193 const QString fileName = doc->fileName();
194 const bool isInEditor = fileName == filePath();
195 Document::Ptr otherDoc = modelManager->document(fileName);
196 unsigned newRev = otherDoc.isNull() ? 1U : otherDoc->revision() + 1;
197 if (isInEditor)
198 newRev = qMax(rev + 1, newRev);
199 doc->setRevision(newRev);
200 modelManager->emitDocumentUpdated(doc);
201 if (releaseSourceAndAST_)
202 doc->releaseSourceAndAST();
203 });
204 sourceProcessor.setFileSizeLimitInMb(m_fileSizeLimitInMb);
205 sourceProcessor.setCancelChecker([future]() {
206 return future.isCanceled();
207 });
208
209 Snapshot globalSnapshot = modelManager->snapshot();
210 globalSnapshot.remove(filePath());
211 sourceProcessor.setGlobalSnapshot(globalSnapshot);
212 sourceProcessor.setWorkingCopy(workingCopy);
213 sourceProcessor.setHeaderPaths(state.headerPaths);
214 sourceProcessor.setLanguageFeatures(features);
215 sourceProcessor.run(configurationFileName);
216 if (baseConfig.usePrecompiledHeaders) {
217 foreach (const QString &precompiledHeader, state.precompiledHeaders)
218 sourceProcessor.run(precompiledHeader);
219 }
220 if (!baseState.editorDefines.isEmpty())
221 sourceProcessor.run(editorDefinesFileName);
222 QStringList includedFiles = state.includedFiles;
223 if (baseConfig.usePrecompiledHeaders)
224 includedFiles << state.precompiledHeaders;
225 includedFiles.removeDuplicates();
226 sourceProcessor.run(filePath(), includedFiles);
227 state.snapshot = sourceProcessor.snapshot();
228 Snapshot newSnapshot = state.snapshot.simplified(state.snapshot.document(filePath()));
229 for (Snapshot::const_iterator i = state.snapshot.begin(), ei = state.snapshot.end(); i != ei; ++i) {
230 if (Client::isInjectedFile(i.key().toString()))
231 newSnapshot.insert(i.value());
232 }
233 state.snapshot = newSnapshot;
234 state.snapshot.updateDependencyTable();
235 }
236
237 setState(baseState);
238 setExtraState(state);
239
240 if (invalidateSnapshot)
241 emit finished(state.snapshot.document(filePath()), state.snapshot);
242 }
243
releaseResources()244 void BuiltinEditorDocumentParser::releaseResources()
245 {
246 ExtraState s = extraState();
247 s.snapshot = Snapshot();
248 s.forceSnapshotInvalidation = true;
249 setExtraState(s);
250 }
251
document() const252 Document::Ptr BuiltinEditorDocumentParser::document() const
253 {
254 return extraState().snapshot.document(filePath());
255 }
256
snapshot() const257 Snapshot BuiltinEditorDocumentParser::snapshot() const
258 {
259 return extraState().snapshot;
260 }
261
headerPaths() const262 ProjectExplorer::HeaderPaths BuiltinEditorDocumentParser::headerPaths() const
263 {
264 return extraState().headerPaths;
265 }
266
get(const QString & filePath)267 BuiltinEditorDocumentParser::Ptr BuiltinEditorDocumentParser::get(const QString &filePath)
268 {
269 if (BaseEditorDocumentParser::Ptr b = BaseEditorDocumentParser::get(filePath))
270 return b.objectCast<BuiltinEditorDocumentParser>();
271 return BuiltinEditorDocumentParser::Ptr();
272 }
273
addFileAndDependencies(Snapshot * snapshot,QSet<Utils::FilePath> * toRemove,const Utils::FilePath & fileName) const274 void BuiltinEditorDocumentParser::addFileAndDependencies(Snapshot *snapshot,
275 QSet<Utils::FilePath> *toRemove,
276 const Utils::FilePath &fileName) const
277 {
278 QTC_ASSERT(snapshot, return);
279
280 toRemove->insert(fileName);
281 if (fileName != Utils::FilePath::fromString(filePath())) {
282 Utils::FilePaths deps = snapshot->filesDependingOn(fileName);
283 toRemove->unite(Utils::toSet(deps));
284 }
285 }
286
extraState() const287 BuiltinEditorDocumentParser::ExtraState BuiltinEditorDocumentParser::extraState() const
288 {
289 QMutexLocker locker(&m_stateAndConfigurationMutex);
290 return m_extraState;
291 }
292
setExtraState(const ExtraState & extraState)293 void BuiltinEditorDocumentParser::setExtraState(const ExtraState &extraState)
294 {
295 QMutexLocker locker(&m_stateAndConfigurationMutex);
296 m_extraState = extraState;
297 }
298
releaseSourceAndAST() const299 bool BuiltinEditorDocumentParser::releaseSourceAndAST() const
300 {
301 QMutexLocker locker(&m_stateAndConfigurationMutex);
302 return m_releaseSourceAndAST;
303 }
304
setReleaseSourceAndAST(bool release)305 void BuiltinEditorDocumentParser::setReleaseSourceAndAST(bool release)
306 {
307 QMutexLocker locker(&m_stateAndConfigurationMutex);
308 m_releaseSourceAndAST = release;
309 }
310