1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
3 | * |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation, either version 3 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "cppparser.h" |
18 | #include "parserutils.h" |
19 | #include "../utils.h" |
20 | #include "qsynedit/highlighter/cpp.h" |
21 | |
22 | #include <QApplication> |
23 | #include <QDate> |
24 | #include <QHash> |
25 | #include <QQueue> |
26 | #include <QThread> |
27 | #include <QTime> |
28 | |
29 | static QAtomicInt cppParserCount(0); |
30 | CppParser::CppParser(QObject *parent) : QObject(parent), |
31 | mMutex(QMutex::Recursive) |
32 | { |
33 | mParserId = cppParserCount.fetchAndAddRelaxed(1); |
34 | mLanguage = ParserLanguage::CPlusPlus; |
35 | mSerialCount = 0; |
36 | updateSerialId(); |
37 | mUniqId = 0; |
38 | mParsing = false; |
39 | //mStatementList ; // owns the objects |
40 | //mFilesToScan; |
41 | //mIncludePaths; |
42 | //mProjectIncludePaths; |
43 | //mProjectFiles; |
44 | // mCurrentScope; |
45 | //mCurrentClassScope; |
46 | //mSkipList; |
47 | mParseLocalHeaders = true; |
48 | mParseGlobalHeaders = true; |
49 | mLockCount = 0; |
50 | mIsSystemHeader = false; |
51 | mIsHeader = false; |
52 | mIsProjectFile = false; |
53 | |
54 | mCppKeywords = CppKeywords; |
55 | mCppTypeKeywords = CppTypeKeywords; |
56 | mEnabled = true; |
57 | //mNamespaces; |
58 | //mBlockBeginSkips; |
59 | //mBlockEndSkips; |
60 | //mInlineNamespaceEndSkips; |
61 | } |
62 | |
63 | CppParser::~CppParser() |
64 | { |
65 | while (true) { |
66 | //wait for all methods finishes running |
67 | { |
68 | QMutexLocker locker(&mMutex); |
69 | if (!mParsing && (mLockCount == 0)) { |
70 | mParsing = true; |
71 | break; |
72 | } |
73 | } |
74 | QThread::msleep(50); |
75 | QCoreApplication* app = QApplication::instance(); |
76 | app->processEvents(); |
77 | } |
78 | } |
79 | |
80 | void CppParser::addHardDefineByLine(const QString &line) |
81 | { |
82 | QMutexLocker locker(&mMutex); |
83 | if (line.startsWith('#')) { |
84 | mPreprocessor.addHardDefineByLine(line.mid(1).trimmed()); |
85 | } else { |
86 | mPreprocessor.addHardDefineByLine(line); |
87 | } |
88 | } |
89 | |
90 | void CppParser::addIncludePath(const QString &value) |
91 | { |
92 | QMutexLocker locker(&mMutex); |
93 | mPreprocessor.addIncludePath(includeTrailingPathDelimiter(value)); |
94 | } |
95 | |
96 | void CppParser::addProjectIncludePath(const QString &value) |
97 | { |
98 | QMutexLocker locker(&mMutex); |
99 | mPreprocessor.addProjectIncludePath(includeTrailingPathDelimiter(value)); |
100 | } |
101 | |
102 | void CppParser::clearIncludePaths() |
103 | { |
104 | QMutexLocker locker(&mMutex); |
105 | mPreprocessor.clearIncludePaths(); |
106 | } |
107 | |
108 | void CppParser::clearProjectIncludePaths() |
109 | { |
110 | QMutexLocker locker(&mMutex); |
111 | mPreprocessor.clearProjectIncludePaths(); |
112 | } |
113 | |
114 | void CppParser::clearProjectFiles() |
115 | { |
116 | QMutexLocker locker(&mMutex); |
117 | mProjectFiles.clear(); |
118 | } |
119 | |
120 | QList<PStatement> CppParser::getListOfFunctions(const QString &fileName, const QString &phrase, int line) |
121 | { |
122 | QMutexLocker locker(&mMutex); |
123 | QList<PStatement> result; |
124 | if (mParsing) |
125 | return result; |
126 | |
127 | PStatement statement = findStatementOf(fileName,phrase, line); |
128 | if (!statement) |
129 | return result; |
130 | PStatement parentScope; |
131 | if (statement->kind == StatementKind::skClass) { |
132 | parentScope = statement; |
133 | } else |
134 | parentScope = statement->parentScope.lock(); |
135 | if (parentScope && parentScope->kind == StatementKind::skNamespace) { |
136 | PStatementList namespaceStatementsList = findNamespace(parentScope->command); |
137 | if (namespaceStatementsList) { |
138 | for (PStatement& namespaceStatement : *namespaceStatementsList) { |
139 | result.append( |
140 | getListOfFunctions(fileName,line,statement,namespaceStatement)); |
141 | } |
142 | } |
143 | } else |
144 | result.append( |
145 | getListOfFunctions(fileName,line,statement,parentScope) |
146 | ); |
147 | return result; |
148 | } |
149 | |
150 | PStatement CppParser::findAndScanBlockAt(const QString &filename, int line) |
151 | { |
152 | QMutexLocker locker(&mMutex); |
153 | if (mParsing) { |
154 | return PStatement(); |
155 | } |
156 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(filename); |
157 | if (!fileIncludes) |
158 | return PStatement(); |
159 | |
160 | PStatement statement = fileIncludes->scopes.findScopeAtLine(line); |
161 | return statement; |
162 | } |
163 | |
164 | PFileIncludes CppParser::findFileIncludes(const QString &filename, bool deleteIt) |
165 | { |
166 | QMutexLocker locker(&mMutex); |
167 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(filename,PFileIncludes()); |
168 | if (deleteIt && fileIncludes) |
169 | mPreprocessor.includesList().remove(filename); |
170 | return fileIncludes; |
171 | } |
172 | QString CppParser::findFirstTemplateParamOf(const QString &fileName, const QString &phrase, const PStatement& currentScope) |
173 | { |
174 | QMutexLocker locker(&mMutex); |
175 | if (mParsing) |
176 | return "" ; |
177 | // Remove pointer stuff from type |
178 | QString s = phrase; // 'Type' is a keyword |
179 | int i = s.indexOf('<'); |
180 | if (i>=0) { |
181 | int t=getFirstTemplateParamEnd(s,i); |
182 | return s.mid(i+1,t-i-1); |
183 | } |
184 | int position = s.length()-1; |
185 | while ((position >= 0) && (s[position] == '*' |
186 | || s[position] == ' ' |
187 | || s[position] == '&')) |
188 | position--; |
189 | if (position != s.length()-1) |
190 | s.truncate(position+1); |
191 | |
192 | PStatement scopeStatement = currentScope; |
193 | |
194 | PStatement statement = findStatementOf(fileName,s,currentScope); |
195 | return getFirstTemplateParam(statement,fileName, phrase, currentScope); |
196 | } |
197 | |
198 | PStatement CppParser::findFunctionAt(const QString &fileName, int line) |
199 | { |
200 | QMutexLocker locker(&mMutex); |
201 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(fileName); |
202 | if (!fileIncludes) |
203 | return PStatement(); |
204 | for (PStatement& statement : fileIncludes->statements) { |
205 | if (statement->kind != StatementKind::skFunction |
206 | && statement->kind != StatementKind::skConstructor |
207 | && statement->kind != StatementKind::skDestructor) |
208 | continue; |
209 | if (statement->line == line || statement->definitionLine == line) |
210 | return statement; |
211 | } |
212 | return PStatement(); |
213 | } |
214 | |
215 | int CppParser::findLastOperator(const QString &phrase) const |
216 | { |
217 | int i = phrase.length()-1; |
218 | |
219 | // Obtain stuff after first operator |
220 | while (i>=0) { |
221 | if ((i+1<phrase.length()) && |
222 | (phrase[i + 1] == '>') && (phrase[i] == '-')) |
223 | return i; |
224 | else if ((i+1<phrase.length()) && |
225 | (phrase[i + 1] == ':') && (phrase[i] == ':')) |
226 | return i; |
227 | else if (phrase[i] == '.') |
228 | return i; |
229 | i--; |
230 | } |
231 | return -1; |
232 | } |
233 | |
234 | PStatementList CppParser::findNamespace(const QString &name) |
235 | { |
236 | QMutexLocker locker(&mMutex); |
237 | return mNamespaces.value(name,PStatementList()); |
238 | } |
239 | |
240 | PStatement CppParser::findStatement(const QString &fullname) |
241 | { |
242 | QMutexLocker locker(&mMutex); |
243 | if (fullname.isEmpty()) |
244 | return PStatement(); |
245 | QStringList phrases = fullname.split("::" ); |
246 | if (phrases.isEmpty()) |
247 | return PStatement(); |
248 | PStatement parentStatement; |
249 | PStatement statement; |
250 | foreach (const QString& phrase, phrases) { |
251 | if (parentStatement && parentStatement->kind == StatementKind::skNamespace) { |
252 | PStatementList lst = findNamespace(parentStatement->fullName); |
253 | foreach (const PStatement& namespaceStatement, *lst) { |
254 | statement = findMemberOfStatement(phrase,namespaceStatement); |
255 | if (statement) |
256 | break; |
257 | } |
258 | } else { |
259 | statement = findMemberOfStatement(phrase,parentStatement); |
260 | } |
261 | if (!statement) |
262 | return PStatement(); |
263 | parentStatement = statement; |
264 | } |
265 | return statement; |
266 | } |
267 | |
268 | PStatement CppParser::findStatementOf(const QString &fileName, const QString &phrase, int line) |
269 | { |
270 | QMutexLocker locker(&mMutex); |
271 | if (mParsing) |
272 | return PStatement(); |
273 | return findStatementOf(fileName,phrase,findAndScanBlockAt(fileName,line)); |
274 | } |
275 | |
276 | PStatement CppParser::findStatementOf(const QString &fileName, |
277 | const QString &phrase, |
278 | const PStatement& currentScope, |
279 | PStatement &parentScopeType, |
280 | bool force) |
281 | { |
282 | QMutexLocker locker(&mMutex); |
283 | PStatement result; |
284 | parentScopeType = currentScope; |
285 | if (mParsing && !force) |
286 | return PStatement(); |
287 | |
288 | //find the start scope statement |
289 | QString namespaceName, remainder; |
290 | QString nextScopeWord,operatorToken,memberName; |
291 | PStatement statement; |
292 | getFullNamespace(phrase, namespaceName, remainder); |
293 | if (!namespaceName.isEmpty()) { // (namespace )qualified Name |
294 | PStatementList namespaceList = mNamespaces.value(namespaceName); |
295 | |
296 | if (!namespaceList || namespaceList->isEmpty()) |
297 | return PStatement(); |
298 | |
299 | if (remainder.isEmpty()) |
300 | return namespaceList->front(); |
301 | |
302 | remainder = splitPhrase(remainder,nextScopeWord,operatorToken,memberName); |
303 | |
304 | for (PStatement& currentNamespace: *namespaceList) { |
305 | statement = findMemberOfStatement(nextScopeWord,currentNamespace); |
306 | if (statement) |
307 | break; |
308 | } |
309 | |
310 | //not found in namespaces; |
311 | if (!statement) |
312 | return PStatement(); |
313 | // found in namespace |
314 | } else if ((phrase.length()>2) && |
315 | (phrase[0]==':') && (phrase[1]==':')) { |
316 | //global |
317 | remainder= phrase.mid(2); |
318 | remainder= splitPhrase(remainder,nextScopeWord,operatorToken,memberName); |
319 | statement= findMemberOfStatement(nextScopeWord,PStatement()); |
320 | if (!statement) |
321 | return PStatement(); |
322 | } else { |
323 | //unqualified name |
324 | parentScopeType = currentScope; |
325 | remainder = splitPhrase(remainder,nextScopeWord,operatorToken,memberName); |
326 | statement = findStatementStartingFrom(fileName,nextScopeWord,parentScopeType); |
327 | if (!statement) |
328 | return PStatement(); |
329 | } |
330 | parentScopeType = currentScope; |
331 | |
332 | if (!memberName.isEmpty() && (statement->kind == StatementKind::skTypedef)) { |
333 | PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType); |
334 | if (typeStatement) |
335 | statement = typeStatement; |
336 | } |
337 | |
338 | //using alias like 'using std::vector;' |
339 | if (statement->kind == StatementKind::skAlias) { |
340 | statement = findAliasedStatement(statement); |
341 | if (!statement) |
342 | return PStatement(); |
343 | } |
344 | |
345 | if (statement->kind == StatementKind::skConstructor) { |
346 | // we need the class, not the construtor |
347 | statement = statement->parentScope.lock(); |
348 | if (!statement) |
349 | return PStatement(); |
350 | } |
351 | PStatement lastScopeStatement; |
352 | QString typeName; |
353 | PStatement typeStatement; |
354 | while (!memberName.isEmpty()) { |
355 | if (statement->kind!=StatementKind::skClass |
356 | && operatorToken == "::" ) { |
357 | return PStatement(); |
358 | } |
359 | if (statement->kind == StatementKind::skVariable |
360 | || statement->kind == StatementKind::skParameter |
361 | || statement->kind == StatementKind::skFunction) { |
362 | |
363 | bool isSTLContainerFunctions = false; |
364 | |
365 | if (statement->kind == StatementKind::skFunction){ |
366 | PStatement parentScope = statement->parentScope.lock(); |
367 | if (parentScope |
368 | && STLContainers.contains(parentScope->fullName) |
369 | && STLElementMethods.contains(statement->command) |
370 | && lastScopeStatement) { |
371 | isSTLContainerFunctions = true; |
372 | PStatement lastScopeParent = lastScopeStatement->parentScope.lock(); |
373 | typeName=findFirstTemplateParamOf(fileName,lastScopeStatement->type, |
374 | lastScopeParent ); |
375 | typeStatement=findTypeDefinitionOf(fileName, typeName, |
376 | lastScopeParent ); |
377 | } |
378 | } |
379 | if (!isSTLContainerFunctions) |
380 | typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType); |
381 | |
382 | //it's stl smart pointer |
383 | if ((typeStatement) |
384 | && STLPointers.contains(typeStatement->fullName) |
385 | && (operatorToken == "->" )) { |
386 | PStatement parentScope = statement->parentScope.lock(); |
387 | typeName=findFirstTemplateParamOf(fileName,statement->type, parentScope); |
388 | typeStatement=findTypeDefinitionOf(fileName, typeName,parentScope); |
389 | } else if ((typeStatement) |
390 | && STLContainers.contains(typeStatement->fullName) |
391 | && nextScopeWord.endsWith(']')) { |
392 | //it's a std container |
393 | PStatement parentScope = statement->parentScope.lock(); |
394 | typeName = findFirstTemplateParamOf(fileName,statement->type, |
395 | parentScope); |
396 | typeStatement = findTypeDefinitionOf(fileName, typeName, |
397 | parentScope); |
398 | } |
399 | lastScopeStatement = statement; |
400 | if (typeStatement) |
401 | statement = typeStatement; |
402 | } else |
403 | lastScopeStatement = statement; |
404 | remainder = splitPhrase(remainder,nextScopeWord,operatorToken,memberName); |
405 | PStatement memberStatement = findMemberOfStatement(nextScopeWord,statement); |
406 | if (!memberStatement) |
407 | return PStatement(); |
408 | |
409 | parentScopeType=statement; |
410 | statement = memberStatement; |
411 | if (!memberName.isEmpty() && (statement->kind == StatementKind::skTypedef)) { |
412 | PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType); |
413 | if (typeStatement) |
414 | statement = typeStatement; |
415 | } |
416 | } |
417 | return statement; |
418 | } |
419 | |
420 | PEvalStatement CppParser::evalExpression( |
421 | const QString &fileName, |
422 | const QStringList &phraseExpression, |
423 | const PStatement ¤tScope) |
424 | { |
425 | QMutexLocker locker(&mMutex); |
426 | if (mParsing) |
427 | return PEvalStatement(); |
428 | // qDebug()<<phraseExpression; |
429 | int pos = 0; |
430 | return doEvalExpression(fileName, |
431 | phraseExpression, |
432 | pos, |
433 | currentScope, |
434 | PEvalStatement(), |
435 | true); |
436 | } |
437 | |
438 | PStatement CppParser::findStatementOf(const QString &fileName, const QString &phrase, const PStatement& currentClass, bool force) |
439 | { |
440 | PStatement statementParentType; |
441 | return findStatementOf(fileName,phrase,currentClass,statementParentType,force); |
442 | } |
443 | |
444 | PStatement CppParser::findStatementOf(const QString &fileName, const QStringList &expression, const PStatement ¤tScope) |
445 | { |
446 | QMutexLocker locker(&mMutex); |
447 | if (mParsing) |
448 | return PStatement(); |
449 | QString memberOperator; |
450 | QStringList memberExpression; |
451 | QStringList ownerExpression = getOwnerExpressionAndMember(expression,memberOperator,memberExpression); |
452 | if (memberExpression.isEmpty()) { |
453 | return PStatement(); |
454 | } |
455 | QString phrase = memberExpression[0]; |
456 | if (memberOperator.isEmpty()) { |
457 | return findStatementStartingFrom(fileName,phrase,currentScope); |
458 | } else if (ownerExpression.isEmpty()) { |
459 | return findMemberOfStatement(phrase,PStatement()); |
460 | } else { |
461 | int pos = 0; |
462 | PEvalStatement ownerEvalStatement = doEvalExpression(fileName, |
463 | ownerExpression, |
464 | pos, |
465 | currentScope, |
466 | PEvalStatement(), |
467 | true); |
468 | if (!ownerEvalStatement) { |
469 | return PStatement(); |
470 | } |
471 | if (ownerEvalStatement->effectiveTypeStatement && |
472 | ownerEvalStatement->effectiveTypeStatement->kind == StatementKind::skNamespace) { |
473 | PStatementList lst = findNamespace(ownerEvalStatement->effectiveTypeStatement->fullName); |
474 | foreach (const PStatement& namespaceStatement, *lst) { |
475 | PStatement statement = findMemberOfStatement(phrase,namespaceStatement); |
476 | if (statement) |
477 | return statement; |
478 | } |
479 | return PStatement(); |
480 | } |
481 | return findMemberOfStatement(phrase, ownerEvalStatement->effectiveTypeStatement); |
482 | } |
483 | |
484 | } |
485 | |
486 | PStatement CppParser::findStatementOf(const QString &fileName, const QStringList &expression, int line) |
487 | { |
488 | QMutexLocker locker(&mMutex); |
489 | if (mParsing) |
490 | return PStatement(); |
491 | return findStatementOf(fileName,expression,findAndScanBlockAt(fileName,line)); |
492 | } |
493 | |
494 | PStatement CppParser::findAliasedStatement(const PStatement &statement) |
495 | { |
496 | QMutexLocker locker(&mMutex); |
497 | if (mParsing) |
498 | return PStatement(); |
499 | if (!statement) |
500 | return PStatement(); |
501 | QString alias = statement->type; |
502 | int pos = statement->type.lastIndexOf("::" ); |
503 | if (pos<0) |
504 | return PStatement(); |
505 | QString nsName=statement->type.mid(0,pos); |
506 | QString name = statement->type.mid(pos+2); |
507 | PStatementList namespaceStatements = findNamespace(nsName); |
508 | if (!namespaceStatements) |
509 | return PStatement(); |
510 | foreach (const PStatement& namespaceStatement, *namespaceStatements) { |
511 | QList<PStatement> resultList = findMembersOfStatement(name,namespaceStatement); |
512 | foreach(const PStatement& resultStatement,resultList) { |
513 | if (resultStatement->kind != StatementKind::skAlias) |
514 | return resultStatement; |
515 | } |
516 | } |
517 | return PStatement(); |
518 | } |
519 | |
520 | PStatement CppParser::findStatementStartingFrom(const QString &fileName, const QString &phrase, const PStatement& startScope) |
521 | { |
522 | PStatement scopeStatement = startScope; |
523 | |
524 | // repeat until reach global |
525 | PStatement result; |
526 | while (scopeStatement) { |
527 | //search members of current scope |
528 | result = findStatementInScope(phrase, scopeStatement); |
529 | if (result) |
530 | return result; |
531 | // not found |
532 | // search members of all usings (in current scope ) |
533 | foreach (const QString& namespaceName, scopeStatement->usingList) { |
534 | result = findStatementInNamespace(phrase,namespaceName); |
535 | if (result) |
536 | return result; |
537 | } |
538 | scopeStatement = scopeStatement->parentScope.lock(); |
539 | } |
540 | |
541 | // Search all global members |
542 | result = findMemberOfStatement(phrase,PStatement()); |
543 | if (result) |
544 | return result; |
545 | |
546 | //Find in all global usings |
547 | const QSet<QString>& fileUsings = getFileUsings(fileName); |
548 | // add members of all fusings |
549 | for (const QString& namespaceName:fileUsings) { |
550 | result = findStatementInNamespace(phrase,namespaceName); |
551 | if (result) |
552 | return result; |
553 | } |
554 | return PStatement(); |
555 | } |
556 | |
557 | PStatement CppParser::findTypeDefinitionOf(const QString &fileName, const QString &aType, const PStatement& currentClass) |
558 | { |
559 | QMutexLocker locker(&mMutex); |
560 | |
561 | if (mParsing) |
562 | return PStatement(); |
563 | |
564 | // Remove pointer stuff from type |
565 | QString s = aType; // 'Type' is a keyword |
566 | int position = s.length()-1; |
567 | while ((position >= 0) && (s[position] == '*' |
568 | || s[position] == ' ' |
569 | || s[position] == '&')) |
570 | position--; |
571 | if (position != s.length()-1) |
572 | s.truncate(position+1); |
573 | |
574 | // Strip template stuff |
575 | position = s.indexOf('<'); |
576 | if (position >= 0) { |
577 | int endPos = getBracketEnd(s,position); |
578 | s.remove(position,endPos-position+1); |
579 | } |
580 | |
581 | // Use last word only (strip 'const', 'static', etc) |
582 | position = s.lastIndexOf(' '); |
583 | if (position >= 0) |
584 | s = s.mid(position+1); |
585 | |
586 | PStatement scopeStatement = currentClass; |
587 | |
588 | PStatement statement = findStatementOf(fileName,s,currentClass); |
589 | return getTypeDef(statement,fileName,aType); |
590 | } |
591 | |
592 | PStatement CppParser::findTypeDef(const PStatement &statement, const QString &fileName) |
593 | { |
594 | QMutexLocker locker(&mMutex); |
595 | |
596 | if (mParsing) |
597 | return PStatement(); |
598 | return getTypeDef(statement, fileName, "" ); |
599 | } |
600 | |
601 | bool CppParser::freeze() |
602 | { |
603 | QMutexLocker locker(&mMutex); |
604 | if (mParsing) |
605 | return false; |
606 | mLockCount++; |
607 | return true; |
608 | } |
609 | |
610 | bool CppParser::freeze(const QString &serialId) |
611 | { |
612 | QMutexLocker locker(&mMutex); |
613 | if (mParsing) |
614 | return false; |
615 | if (mSerialId!=serialId) |
616 | return false; |
617 | mLockCount++; |
618 | return true; |
619 | } |
620 | |
621 | QStringList CppParser::getClassesList() |
622 | { |
623 | QMutexLocker locker(&mMutex); |
624 | |
625 | QStringList list; |
626 | return list; |
627 | // fills List with a list of all the known classes |
628 | QQueue<PStatement> queue; |
629 | queue.enqueue(PStatement()); |
630 | while (!queue.isEmpty()) { |
631 | PStatement statement = queue.dequeue(); |
632 | StatementMap statementMap = mStatementList.childrenStatements(statement); |
633 | for (PStatement& child:statementMap) { |
634 | if (child->kind == StatementKind::skClass) |
635 | list.append(child->command); |
636 | if (!child->children.isEmpty()) |
637 | queue.enqueue(child); |
638 | } |
639 | } |
640 | return list; |
641 | } |
642 | |
643 | QStringList CppParser::getFileDirectIncludes(const QString &filename) |
644 | { |
645 | QMutexLocker locker(&mMutex); |
646 | if (mParsing) |
647 | return QStringList(); |
648 | if (filename.isEmpty()) |
649 | return QStringList(); |
650 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(filename,PFileIncludes()); |
651 | |
652 | if (fileIncludes) { |
653 | return fileIncludes->directIncludes; |
654 | } |
655 | return QStringList(); |
656 | |
657 | } |
658 | |
659 | QSet<QString> CppParser::getFileIncludes(const QString &filename) |
660 | { |
661 | QMutexLocker locker(&mMutex); |
662 | QSet<QString> list; |
663 | if (mParsing) |
664 | return list; |
665 | if (filename.isEmpty()) |
666 | return list; |
667 | list.insert(filename); |
668 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(filename,PFileIncludes()); |
669 | |
670 | if (fileIncludes) { |
671 | foreach (const QString& file, fileIncludes->includeFiles.keys()) { |
672 | list.insert(file); |
673 | } |
674 | } |
675 | return list; |
676 | } |
677 | |
678 | QSet<QString> CppParser::getFileUsings(const QString &filename) |
679 | { |
680 | QMutexLocker locker(&mMutex); |
681 | QSet<QString> result; |
682 | if (filename.isEmpty()) |
683 | return result; |
684 | if (mParsing) |
685 | return result; |
686 | PFileIncludes fileIncludes= mPreprocessor.includesList().value(filename,PFileIncludes()); |
687 | if (fileIncludes) { |
688 | foreach (const QString& usingName, fileIncludes->usings) { |
689 | result.insert(usingName); |
690 | } |
691 | foreach (const QString& subFile,fileIncludes->includeFiles.keys()){ |
692 | PFileIncludes subIncludes = mPreprocessor.includesList().value(subFile,PFileIncludes()); |
693 | if (subIncludes) { |
694 | foreach (const QString& usingName, subIncludes->usings) { |
695 | result.insert(usingName); |
696 | } |
697 | } |
698 | } |
699 | } |
700 | return result; |
701 | } |
702 | |
703 | QString CppParser::(const QString &relativeTo, const QString &line) |
704 | { |
705 | QMutexLocker locker(&mMutex); |
706 | return ::getHeaderFilename(relativeTo, line, mPreprocessor.includePathList(), |
707 | mPreprocessor.projectIncludePathList()); |
708 | } |
709 | |
710 | void CppParser::invalidateFile(const QString &fileName) |
711 | { |
712 | if (!mEnabled) |
713 | return; |
714 | { |
715 | QMutexLocker locker(&mMutex); |
716 | if (mParsing || mLockCount>0) |
717 | return; |
718 | updateSerialId(); |
719 | mParsing = true; |
720 | } |
721 | QSet<QString> files = calculateFilesToBeReparsed(fileName); |
722 | internalInvalidateFiles(files); |
723 | mParsing = false; |
724 | } |
725 | |
726 | bool CppParser::isIncludeLine(const QString &line) |
727 | { |
728 | QString trimmedLine = line.trimmed(); |
729 | if ((trimmedLine.length() > 0) |
730 | && trimmedLine.startsWith('#')) { // it's a preprocessor line |
731 | if (trimmedLine.mid(1).trimmed().startsWith("include" )) |
732 | return true; |
733 | } |
734 | return false; |
735 | } |
736 | |
737 | bool CppParser::(const QString &fileName) |
738 | { |
739 | QMutexLocker locker(&mMutex); |
740 | return ::isSystemHeaderFile(fileName,mPreprocessor.projectIncludePaths()); |
741 | } |
742 | |
743 | bool CppParser::(const QString &fileName) |
744 | { |
745 | QMutexLocker locker(&mMutex); |
746 | return ::isSystemHeaderFile(fileName,mPreprocessor.includePaths()); |
747 | } |
748 | |
749 | void CppParser::parseFile(const QString &fileName, bool inProject, bool onlyIfNotParsed, bool updateView) |
750 | { |
751 | if (!mEnabled) |
752 | return; |
753 | { |
754 | QMutexLocker locker(&mMutex); |
755 | if (mParsing || mLockCount>0) |
756 | return; |
757 | updateSerialId(); |
758 | mParsing = true; |
759 | if (updateView) |
760 | emit onBusy(); |
761 | emit onStartParsing(); |
762 | } |
763 | { |
764 | auto action = finally([&,this]{ |
765 | mParsing = false; |
766 | |
767 | if (updateView) |
768 | emit onEndParsing(mFilesScannedCount,1); |
769 | else |
770 | emit onEndParsing(mFilesScannedCount,0); |
771 | }); |
772 | QString fName = fileName; |
773 | if (onlyIfNotParsed && mPreprocessor.scannedFiles().contains(fName)) |
774 | return; |
775 | |
776 | QSet<QString> files = calculateFilesToBeReparsed(fileName); |
777 | internalInvalidateFiles(files); |
778 | |
779 | if (inProject) |
780 | mProjectFiles.insert(fileName); |
781 | else { |
782 | mProjectFiles.remove(fileName); |
783 | } |
784 | |
785 | // Parse from disk or stream |
786 | mFilesToScanCount = files.count(); |
787 | mFilesScannedCount = 0; |
788 | |
789 | // parse header files in the first parse |
790 | foreach (const QString& file,files) { |
791 | if (isHFile(file)) { |
792 | mFilesScannedCount++; |
793 | emit onProgress(file,mFilesToScanCount,mFilesScannedCount); |
794 | if (!mPreprocessor.scannedFiles().contains(file)) { |
795 | internalParse(file); |
796 | } |
797 | } |
798 | } |
799 | //we only parse CFile in the second parse |
800 | foreach (const QString& file,files) { |
801 | if (!isHFile(file)) { |
802 | mFilesScannedCount++; |
803 | emit onProgress(file,mFilesToScanCount,mFilesScannedCount); |
804 | if (!mPreprocessor.scannedFiles().contains(file)) { |
805 | internalParse(file); |
806 | } |
807 | } |
808 | } |
809 | } |
810 | } |
811 | |
812 | void CppParser::parseFileList(bool updateView) |
813 | { |
814 | if (!mEnabled) |
815 | return; |
816 | { |
817 | QMutexLocker locker(&mMutex); |
818 | if (mParsing || mLockCount>0) |
819 | return; |
820 | updateSerialId(); |
821 | mParsing = true; |
822 | if (updateView) |
823 | emit onBusy(); |
824 | emit onStartParsing(); |
825 | } |
826 | { |
827 | auto action = finally([&,this]{ |
828 | mParsing = false; |
829 | if (updateView) |
830 | emit onEndParsing(mFilesScannedCount,1); |
831 | else |
832 | emit onEndParsing(mFilesScannedCount,0); |
833 | }); |
834 | // Support stopping of parsing when files closes unexpectedly |
835 | mFilesScannedCount = 0; |
836 | mFilesToScanCount = mFilesToScan.count(); |
837 | // parse header files in the first parse |
838 | foreach (const QString& file, mFilesToScan) { |
839 | if (isHFile(file)) { |
840 | mFilesScannedCount++; |
841 | emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount); |
842 | if (!mPreprocessor.scannedFiles().contains(file)) { |
843 | internalParse(file); |
844 | } |
845 | } |
846 | } |
847 | //we only parse CFile in the second parse |
848 | foreach (const QString& file,mFilesToScan) { |
849 | if (isCFile(file)) { |
850 | mFilesScannedCount++; |
851 | emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount); |
852 | if (!mPreprocessor.scannedFiles().contains(file)) { |
853 | internalParse(file); |
854 | } |
855 | } |
856 | } |
857 | mFilesToScan.clear(); |
858 | } |
859 | } |
860 | |
861 | void CppParser::parseHardDefines() |
862 | { |
863 | QMutexLocker locker(&mMutex); |
864 | if (mParsing) |
865 | return; |
866 | int = mIsSystemHeader; |
867 | mIsSystemHeader = true; |
868 | mParsing=true; |
869 | { |
870 | auto action = finally([&,this]{ |
871 | mParsing = false; |
872 | mIsSystemHeader=oldIsSystemHeader; |
873 | }); |
874 | for (const PDefine& define:mPreprocessor.hardDefines()) { |
875 | addStatement( |
876 | PStatement(), // defines don't belong to any scope |
877 | "" , |
878 | "" , // define has no type |
879 | define->name, |
880 | define->value, |
881 | define->args, |
882 | -1, |
883 | StatementKind::skPreprocessor, |
884 | StatementScope::ssGlobal, |
885 | StatementClassScope::scsNone, |
886 | true, |
887 | false); |
888 | } |
889 | } |
890 | } |
891 | |
892 | bool CppParser::parsing() const |
893 | { |
894 | return mParsing; |
895 | } |
896 | |
897 | void CppParser::reset() |
898 | { |
899 | while (true) { |
900 | { |
901 | QMutexLocker locker(&mMutex); |
902 | if (!mParsing && mLockCount ==0) { |
903 | mParsing = true; |
904 | break; |
905 | } |
906 | } |
907 | QThread::msleep(50); |
908 | QCoreApplication* app = QApplication::instance(); |
909 | app->processEvents(); |
910 | } |
911 | { |
912 | auto action = finally([this]{ |
913 | mParsing = false; |
914 | }); |
915 | emit onBusy(); |
916 | mPreprocessor.clear(); |
917 | mUniqId = 0; |
918 | mSkipList.clear(); |
919 | mBlockBeginSkips.clear(); |
920 | mBlockEndSkips.clear(); |
921 | mInlineNamespaceEndSkips.clear(); |
922 | mParseLocalHeaders = true; |
923 | mParseGlobalHeaders = true; |
924 | mIsSystemHeader = false; |
925 | mIsHeader = false; |
926 | mIsProjectFile = false; |
927 | |
928 | mCurrentScope.clear(); |
929 | mCurrentClassScope.clear(); |
930 | mProjectFiles.clear(); |
931 | mFilesToScan.clear(); |
932 | mTokenizer.reset(); |
933 | // Remove all statements |
934 | mStatementList.clear(); |
935 | |
936 | // We haven't scanned anything anymore |
937 | mPreprocessor.scannedFiles().clear(); |
938 | |
939 | // We don't include anything anymore |
940 | mPreprocessor.includesList().clear(); |
941 | |
942 | mNamespaces.clear(); |
943 | mInlineNamespaces.clear(); |
944 | |
945 | mPreprocessor.clearProjectIncludePaths(); |
946 | mPreprocessor.clearIncludePaths(); |
947 | mProjectFiles.clear(); |
948 | } |
949 | } |
950 | |
951 | void CppParser::unFreeze() |
952 | { |
953 | QMutexLocker locker(&mMutex); |
954 | mLockCount--; |
955 | } |
956 | |
957 | QSet<QString> CppParser::scannedFiles() |
958 | { |
959 | return mPreprocessor.scannedFiles(); |
960 | } |
961 | |
962 | bool CppParser::isFileParsed(const QString &filename) |
963 | { |
964 | return mPreprocessor.scannedFiles().contains(filename); |
965 | } |
966 | |
967 | QString CppParser::getScopePrefix(const PStatement& statement){ |
968 | switch (statement->classScope) { |
969 | case StatementClassScope::scsPublic: |
970 | return "public" ; |
971 | case StatementClassScope::scsPrivate: |
972 | return "private" ; |
973 | case StatementClassScope::scsProtected: |
974 | return "protected" ; |
975 | default: |
976 | return "" ; |
977 | } |
978 | } |
979 | |
980 | QString CppParser::prettyPrintStatement(const PStatement& statement, const QString& filename, int line) |
981 | { |
982 | QString result; |
983 | switch(statement->kind) { |
984 | case StatementKind::skPreprocessor: |
985 | if (statement->command == "__FILE__" ) |
986 | result = '"'+filename+'"'; |
987 | else if (statement->command == "__LINE__" ) |
988 | result = QString("\"%1\"" ).arg(line); |
989 | else if (statement->command == "__DATE__" ) |
990 | result = QString("\"%1\"" ).arg(QDate::currentDate().toString(Qt::ISODate)); |
991 | else if (statement->command == "__TIME__" ) |
992 | result = QString("\"%1\"" ).arg(QTime::currentTime().toString(Qt::ISODate)); |
993 | else { |
994 | QString hintText = "#define" ; |
995 | if (statement->command != "" ) |
996 | hintText += ' ' + statement->command; |
997 | if (statement->args != "" ) |
998 | hintText += ' ' + statement->args; |
999 | if (statement->value != "" ) |
1000 | hintText += ' ' + statement->value; |
1001 | result = hintText; |
1002 | } |
1003 | break; |
1004 | case StatementKind::skEnumClassType: |
1005 | result = "enum class " +statement->command; |
1006 | break; |
1007 | case StatementKind::skEnumType: |
1008 | result = "enum " +statement->command; |
1009 | break; |
1010 | case StatementKind::skEnum: |
1011 | result = statement->type + "::" + statement->command; |
1012 | break; |
1013 | case StatementKind::skTypedef: |
1014 | result = "typedef " +statement->type+" " +statement->command; |
1015 | if (!statement->args.isEmpty()) |
1016 | result += " " +statement->args; |
1017 | break; |
1018 | case StatementKind::skAlias: |
1019 | result = "using " +statement->type; |
1020 | break; |
1021 | case StatementKind::skFunction: |
1022 | case StatementKind::skVariable: |
1023 | case StatementKind::skParameter: |
1024 | case StatementKind::skClass: |
1025 | if (statement->scope!= StatementScope::ssLocal) |
1026 | result = getScopePrefix(statement)+ ' '; // public |
1027 | result += statement->type + ' '; // void |
1028 | result += statement->fullName; // A::B::C::Bar |
1029 | result += statement->args; // (int a) |
1030 | break; |
1031 | case StatementKind::skNamespace: |
1032 | result = statement->fullName; // Bar |
1033 | break; |
1034 | case StatementKind::skConstructor: |
1035 | result = getScopePrefix(statement); // public |
1036 | result += QObject::tr("constructor" ) + ' '; // constructor |
1037 | result += statement->type + ' '; // void |
1038 | result += statement->fullName; // A::B::C::Bar |
1039 | result += statement->args; // (int a) |
1040 | break; |
1041 | case StatementKind::skDestructor: |
1042 | result = getScopePrefix(statement); // public |
1043 | result += QObject::tr("destructor" ) + ' '; // constructor |
1044 | result += statement->type + ' '; // void |
1045 | result += statement->fullName; // A::B::C::Bar |
1046 | result += statement->args; // (int a) |
1047 | break; |
1048 | default: |
1049 | break; |
1050 | } |
1051 | return result; |
1052 | } |
1053 | |
1054 | QString CppParser::getFirstTemplateParam(const PStatement& statement, |
1055 | const QString& filename, |
1056 | const QString& phrase, |
1057 | const PStatement& currentScope) |
1058 | { |
1059 | if (!statement) |
1060 | return "" ; |
1061 | if (statement->kind != StatementKind::skTypedef) |
1062 | return "" ; |
1063 | if (statement->type == phrase) // prevent infinite loop |
1064 | return "" ; |
1065 | return findFirstTemplateParamOf(filename,statement->type, currentScope); |
1066 | } |
1067 | |
1068 | int CppParser::getFirstTemplateParamEnd(const QString &s, int startAt) |
1069 | { |
1070 | int i = startAt; |
1071 | int level = 0; // assume we start on top of '<' |
1072 | while (i < s.length()) { |
1073 | switch (s[i].unicode()) { |
1074 | case '<': |
1075 | level++; |
1076 | break; |
1077 | case ',': |
1078 | if (level == 1) |
1079 | return i; |
1080 | break; |
1081 | case '>': |
1082 | level--; |
1083 | if (level==0) |
1084 | return i; |
1085 | } |
1086 | i++; |
1087 | } |
1088 | return startAt; |
1089 | } |
1090 | |
1091 | void CppParser::addFileToScan(const QString& value, bool inProject) |
1092 | { |
1093 | QMutexLocker locker(&mMutex); |
1094 | //value.replace('/','\\'); // only accept full file names |
1095 | |
1096 | // Update project listing |
1097 | if (inProject) |
1098 | mProjectFiles.insert(value); |
1099 | |
1100 | // Only parse given file |
1101 | if (!mPreprocessor.scannedFiles().contains(value)) { |
1102 | mFilesToScan.insert(value); |
1103 | } |
1104 | |
1105 | } |
1106 | |
1107 | PStatement CppParser::addInheritedStatement(const PStatement& derived, const PStatement& inherit, StatementClassScope access) |
1108 | { |
1109 | |
1110 | PStatement statement = addStatement( |
1111 | derived, |
1112 | inherit->fileName, |
1113 | inherit->type, // "Type" is already in use |
1114 | inherit->command, |
1115 | inherit->args, |
1116 | inherit->value, |
1117 | inherit->line, |
1118 | inherit->kind, |
1119 | inherit->scope, |
1120 | access, |
1121 | true, |
1122 | inherit->isStatic); |
1123 | statement->inheritanceList.append(inherit->inheritanceList), |
1124 | statement->isInherited = true; |
1125 | return statement; |
1126 | } |
1127 | |
1128 | PStatement CppParser::addChildStatement(const PStatement& parent, const QString &fileName, |
1129 | const QString &aType, |
1130 | const QString &command, const QString &args, |
1131 | const QString &value, int line, StatementKind kind, |
1132 | const StatementScope& scope, const StatementClassScope& classScope, |
1133 | bool isDefinition, bool isStatic) |
1134 | { |
1135 | return addStatement( |
1136 | parent, |
1137 | fileName, |
1138 | aType, |
1139 | command, |
1140 | args, |
1141 | value, |
1142 | line, |
1143 | kind, |
1144 | scope, |
1145 | classScope, |
1146 | isDefinition, |
1147 | isStatic); |
1148 | } |
1149 | |
1150 | PStatement CppParser::addStatement(const PStatement& parent, |
1151 | const QString &fileName, |
1152 | const QString &aType, |
1153 | const QString &command, |
1154 | const QString &args, |
1155 | const QString &value, |
1156 | int line, StatementKind kind, |
1157 | const StatementScope& scope, |
1158 | const StatementClassScope& classScope, bool isDefinition, bool isStatic) |
1159 | { |
1160 | // Move '*', '&' to type rather than cmd (it's in the way for code-completion) |
1161 | QString newType = aType; |
1162 | QString newCommand = command; |
1163 | while (!newCommand.isEmpty() && (newCommand.front() == '*' || newCommand.front() == '&')) { |
1164 | newType += newCommand.front(); |
1165 | newCommand.remove(0,1); // remove first |
1166 | } |
1167 | |
1168 | QString noNameArgs = "" ; |
1169 | if (kind == StatementKind::skConstructor |
1170 | || kind == StatementKind::skFunction |
1171 | || kind == StatementKind::skDestructor |
1172 | || kind == StatementKind::skVariable |
1173 | ) { |
1174 | noNameArgs = removeArgNames(args); |
1175 | //find |
1176 | if (isDefinition) { |
1177 | PStatement oldStatement = findStatementInScope(newCommand,noNameArgs,kind,parent); |
1178 | if (oldStatement && !oldStatement->hasDefinition) { |
1179 | oldStatement->hasDefinition = true; |
1180 | if (oldStatement->fileName!=fileName) { |
1181 | PFileIncludes fileIncludes1=mPreprocessor.includesList().value(fileName); |
1182 | if (fileIncludes1) { |
1183 | fileIncludes1->statements.insert(oldStatement->fullName, |
1184 | oldStatement); |
1185 | fileIncludes1->dependingFiles.insert(oldStatement->fileName); |
1186 | PFileIncludes fileIncludes2=mPreprocessor.includesList().value(oldStatement->fileName); |
1187 | if (fileIncludes2) { |
1188 | fileIncludes2->dependedFiles.insert(fileName); |
1189 | } |
1190 | } |
1191 | } |
1192 | oldStatement->definitionLine = line; |
1193 | oldStatement->definitionFileName = fileName; |
1194 | return oldStatement; |
1195 | } |
1196 | } |
1197 | } |
1198 | PStatement result = std::make_shared<Statement>(); |
1199 | result->parentScope = parent; |
1200 | result->type = newType; |
1201 | if (!newCommand.isEmpty()) |
1202 | result->command = newCommand; |
1203 | else { |
1204 | mUniqId++; |
1205 | result->command = QString("__STATEMENT__%1" ).arg(mUniqId); |
1206 | } |
1207 | result->args = args; |
1208 | result->noNameArgs = noNameArgs; |
1209 | result->value = value; |
1210 | result->kind = kind; |
1211 | //result->inheritanceList; |
1212 | result->scope = scope; |
1213 | result->classScope = classScope; |
1214 | result->hasDefinition = isDefinition; |
1215 | result->line = line; |
1216 | result->definitionLine = line; |
1217 | result->fileName = fileName; |
1218 | result->definitionFileName = fileName; |
1219 | if (!fileName.isEmpty()) |
1220 | result->inProject = mIsProjectFile; |
1221 | else |
1222 | result->inProject = false; |
1223 | result->inSystemHeader = mIsSystemHeader; |
1224 | //result->children; |
1225 | //result->friends; |
1226 | result->isStatic = isStatic; |
1227 | result->isInherited = false; |
1228 | if (scope == StatementScope::ssLocal) |
1229 | result->fullName = newCommand; |
1230 | else |
1231 | result->fullName = getFullStatementName(newCommand, parent); |
1232 | result->usageCount = -1; |
1233 | mStatementList.add(result); |
1234 | if (result->kind == StatementKind::skNamespace) { |
1235 | PStatementList namespaceList = mNamespaces.value(result->fullName,PStatementList()); |
1236 | if (!namespaceList) { |
1237 | namespaceList=std::make_shared<StatementList>(); |
1238 | mNamespaces.insert(result->fullName,namespaceList); |
1239 | } |
1240 | namespaceList->append(result); |
1241 | } |
1242 | |
1243 | if (result->kind!= StatementKind::skBlock) { |
1244 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(fileName); |
1245 | if (fileIncludes) { |
1246 | fileIncludes->statements.insert(result->fullName,result); |
1247 | fileIncludes->declaredStatements.insert(result->fullName,result); |
1248 | } |
1249 | } |
1250 | return result; |
1251 | } |
1252 | |
1253 | void CppParser::setInheritance(int index, const PStatement& classStatement, bool isStruct) |
1254 | { |
1255 | // Clear it. Assume it is assigned |
1256 | classStatement->inheritanceList.clear(); |
1257 | StatementClassScope lastInheritScopeType = StatementClassScope::scsNone; |
1258 | // Assemble a list of statements in text form we inherit from |
1259 | while (true) { |
1260 | StatementClassScope inheritScopeType = getClassScope(index); |
1261 | if (inheritScopeType == StatementClassScope::scsNone) { |
1262 | if (mTokenizer[index]->text.front()!=',' |
1263 | && mTokenizer[index]->text.front()!=':' |
1264 | && mTokenizer[index]->text.front()!='(') { |
1265 | QString basename = mTokenizer[index]->text; |
1266 | //remove template staff |
1267 | if (basename.endsWith('>')) { |
1268 | int pBegin = basename.indexOf('<'); |
1269 | if (pBegin>=0) |
1270 | basename.truncate(pBegin); |
1271 | } |
1272 | // Find the corresponding PStatement |
1273 | PStatement statement = findStatementOf(mCurrentFile,basename, |
1274 | classStatement->parentScope.lock(),true); |
1275 | if (statement && statement->kind == StatementKind::skClass) { |
1276 | classStatement->inheritanceList.append(statement); |
1277 | inheritClassStatement(classStatement,isStruct,statement,lastInheritScopeType); |
1278 | } |
1279 | } |
1280 | } |
1281 | index++; |
1282 | lastInheritScopeType = inheritScopeType; |
1283 | if (index >= mTokenizer.tokenCount()) |
1284 | break; |
1285 | if (mTokenizer[index]->text.front() == '{' |
1286 | || mTokenizer[index]->text.front() == ';') |
1287 | break; |
1288 | } |
1289 | } |
1290 | |
1291 | bool CppParser::isCurrentScope(const QString &command) |
1292 | { |
1293 | PStatement statement = getCurrentScope(); |
1294 | if (!statement) |
1295 | return false; |
1296 | QString s = command; |
1297 | // remove template staff |
1298 | int i= command.indexOf('<'); |
1299 | if (i>=0) { |
1300 | s.truncate(i); |
1301 | } |
1302 | return (statement->command == s); |
1303 | } |
1304 | |
1305 | void CppParser::addSoloScopeLevel(PStatement& statement, int line, bool shouldResetBlock) |
1306 | { |
1307 | // Add class list |
1308 | |
1309 | PStatement parentScope; |
1310 | if (shouldResetBlock && statement && (statement->kind == StatementKind::skBlock)) { |
1311 | parentScope = statement->parentScope.lock(); |
1312 | while (parentScope && (parentScope->kind == StatementKind::skBlock)) { |
1313 | parentScope = parentScope->parentScope.lock(); |
1314 | } |
1315 | if (!parentScope) |
1316 | statement.reset(); |
1317 | } |
1318 | |
1319 | if (mCurrentClassScope.count()>0) { |
1320 | mCurrentClassScope.back() = mClassScope; |
1321 | } |
1322 | |
1323 | mCurrentScope.append(statement); |
1324 | |
1325 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(mCurrentFile); |
1326 | |
1327 | if (fileIncludes) { |
1328 | fileIncludes->scopes.addScope(line,statement); |
1329 | } |
1330 | |
1331 | // Set new scope |
1332 | if (!statement) |
1333 | mClassScope = StatementClassScope::scsNone; // {}, namespace or class that doesn't exist |
1334 | else if (statement->kind == StatementKind::skNamespace) |
1335 | mClassScope = StatementClassScope::scsNone; |
1336 | else if (statement->type == "class" ) |
1337 | mClassScope = StatementClassScope::scsPrivate; // classes are private by default |
1338 | else |
1339 | mClassScope = StatementClassScope::scsPublic; // structs are public by default |
1340 | mCurrentClassScope.append(mClassScope); |
1341 | } |
1342 | |
1343 | void CppParser::removeScopeLevel(int line) |
1344 | { |
1345 | // Remove class list |
1346 | if (mCurrentScope.isEmpty()) |
1347 | return; // TODO: should be an exception |
1348 | PStatement currentScope = mCurrentScope.back();; |
1349 | PFileIncludes fileIncludes = mPreprocessor.includesList().value(mCurrentFile); |
1350 | if (currentScope && (currentScope->kind == StatementKind::skBlock)) { |
1351 | if (currentScope->children.isEmpty()) { |
1352 | // remove no children block |
1353 | if (fileIncludes) { |
1354 | fileIncludes->scopes.removeLastScope(); |
1355 | } |
1356 | mStatementList.deleteStatement(currentScope); |
1357 | } else { |
1358 | fileIncludes->statements.insert(currentScope->fullName,currentScope); |
1359 | fileIncludes->declaredStatements.insert(currentScope->fullName,currentScope); |
1360 | } |
1361 | } |
1362 | mCurrentScope.pop_back(); |
1363 | mCurrentClassScope.pop_back(); |
1364 | |
1365 | // Set new scope |
1366 | currentScope = getCurrentScope(); |
1367 | // fileIncludes:=FindFileIncludes(fCurrentFile); |
1368 | if (fileIncludes && fileIncludes->scopes.lastScope()!=currentScope) { |
1369 | fileIncludes->scopes.addScope(line,currentScope); |
1370 | } |
1371 | |
1372 | if (!currentScope) { |
1373 | mClassScope = StatementClassScope::scsNone; |
1374 | } else { |
1375 | mClassScope = mCurrentClassScope.back(); |
1376 | } |
1377 | } |
1378 | |
1379 | int CppParser::skipBraces(int startAt) |
1380 | { |
1381 | int i = startAt; |
1382 | int level = 0; // assume we start on top of { |
1383 | while (i < mTokenizer.tokenCount()) { |
1384 | switch(mTokenizer[i]->text.front().unicode()) { |
1385 | case '{': level++; |
1386 | break; |
1387 | case '}': |
1388 | level--; |
1389 | if (level==0) |
1390 | return i; |
1391 | } |
1392 | i++; |
1393 | } |
1394 | return startAt; |
1395 | } |
1396 | |
1397 | int CppParser::skipBracket(int startAt) |
1398 | { |
1399 | int i = startAt; |
1400 | int level = 0; // assume we start on top of { |
1401 | while (i < mTokenizer.tokenCount()) { |
1402 | switch(mTokenizer[i]->text.front().unicode()) { |
1403 | case '[': level++; |
1404 | break; |
1405 | case ']': |
1406 | level--; |
1407 | if (level==0) |
1408 | return i; |
1409 | } |
1410 | i++; |
1411 | } |
1412 | return startAt; |
1413 | } |
1414 | |
1415 | void CppParser::internalClear() |
1416 | { |
1417 | mCurrentScope.clear(); |
1418 | mCurrentClassScope.clear(); |
1419 | mIndex = 0; |
1420 | mClassScope = StatementClassScope::scsNone; |
1421 | mSkipList.clear(); |
1422 | mBlockBeginSkips.clear(); |
1423 | mBlockEndSkips.clear(); |
1424 | mInlineNamespaceEndSkips.clear(); |
1425 | } |
1426 | |
1427 | bool CppParser::checkForCatchBlock() |
1428 | { |
1429 | // return mIndex < mTokenizer.tokenCount() && |
1430 | // mTokenizer[mIndex]->text == "catch"; |
1431 | return mTokenizer[mIndex]->text == "catch" ; |
1432 | } |
1433 | |
1434 | bool CppParser::checkForEnum() |
1435 | { |
1436 | // return mIndex < mTokenizer.tokenCount() && |
1437 | // mTokenizer[mIndex]->text == "enum"; |
1438 | return mTokenizer[mIndex]->text == "enum" ; |
1439 | } |
1440 | |
1441 | bool CppParser::checkForForBlock() |
1442 | { |
1443 | // return mIndex < mTokenizer.tokenCount() && |
1444 | // mTokenizer[mIndex]->text == "for"; |
1445 | return mTokenizer[mIndex]->text == "for" ; |
1446 | } |
1447 | |
1448 | bool CppParser::checkForKeyword() |
1449 | { |
1450 | SkipType st = mCppKeywords.value(mTokenizer[mIndex]->text,SkipType::skNone); |
1451 | return st!=SkipType::skNone; |
1452 | } |
1453 | |
1454 | bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, bool &isStatic, bool &isFriend) |
1455 | { |
1456 | PStatement scope = getCurrentScope(); |
1457 | |
1458 | if (scope && !(scope->kind == StatementKind::skNamespace |
1459 | || scope->kind == StatementKind::skClass)) { //don't care function declaration in the function's |
1460 | return false; |
1461 | } |
1462 | |
1463 | // Function template: |
1464 | // compiler directives (>= 0 words), added to type |
1465 | // type (>= 1 words) |
1466 | // name (1 word) |
1467 | // (argument list) |
1468 | // ; or { |
1469 | |
1470 | isStatic = false; |
1471 | isFriend = false; |
1472 | |
1473 | sType = "" ; // should contain type "int" |
1474 | sName = "" ; // should contain function name "foo::function" |
1475 | sArgs = "" ; // should contain argument list "(int a)" |
1476 | |
1477 | bool bTypeOK = false; |
1478 | bool bNameOK = false; |
1479 | bool bArgsOK = false; |
1480 | |
1481 | // Don't modify index |
1482 | int indexBackup = mIndex; |
1483 | |
1484 | // Gather data for the string parts |
1485 | while ((mIndex < mTokenizer.tokenCount()) && !isSeperator(mTokenizer[mIndex]->text[0])) { |
1486 | if ((mIndex + 1 < mTokenizer.tokenCount()) |
1487 | && (mTokenizer[mIndex + 1]->text[0] == '(')) { // and start of a function |
1488 | //it's not a function define |
1489 | if ((mIndex+2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 2]->text[0] == ',')) |
1490 | break; |
1491 | |
1492 | if ((mIndex+2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 2]->text[0] == ';')) { |
1493 | if (isNotFuncArgs(mTokenizer[mIndex + 1]->text)) |
1494 | break; |
1495 | } |
1496 | sName = mTokenizer[mIndex]->text; |
1497 | sArgs = mTokenizer[mIndex + 1]->text; |
1498 | bTypeOK = !sType.isEmpty(); |
1499 | bNameOK = !sName.isEmpty(); |
1500 | bArgsOK = !sArgs.isEmpty(); |
1501 | |
1502 | // Allow constructor/destructor too |
1503 | if (!bTypeOK) { |
1504 | // Check for constructor/destructor outside class body |
1505 | int delimPos = sName.indexOf("::" ); |
1506 | if (delimPos >= 0) { |
1507 | bTypeOK = true; |
1508 | sType = sName.mid(0, delimPos); |
1509 | |
1510 | // remove template staff |
1511 | int pos1 = sType.indexOf('<'); |
1512 | if (pos1>=0) { |
1513 | sType.truncate(pos1); |
1514 | sName = sType+sName.mid(delimPos); |
1515 | } |
1516 | } |
1517 | } |
1518 | |
1519 | // Are we inside a class body? |
1520 | if (!bTypeOK) { |
1521 | sType = mTokenizer[mIndex]->text; |
1522 | if (sType[0] == '~') |
1523 | sType.remove(0,1); |
1524 | bTypeOK = isCurrentScope(sType); // constructor/destructor |
1525 | } |
1526 | break; |
1527 | } else { |
1528 | //if IsValidIdentifier(mTokenizer[mIndex]->text) then |
1529 | // Still walking through type |
1530 | QString s = expandMacroType(mTokenizer[mIndex]->text); //todo: do we really need expand macro? it should be done in preprocessor |
1531 | if (s == "static" ) |
1532 | isStatic = true; |
1533 | if (s == "friend" ) |
1534 | isFriend = true; |
1535 | if (!s.isEmpty() && !(s=="extern" )) |
1536 | sType = sType + ' '+ s; |
1537 | bTypeOK = !sType.isEmpty(); |
1538 | } |
1539 | mIndex++; |
1540 | } |
1541 | |
1542 | mIndex = indexBackup; |
1543 | |
1544 | // Correct function, don't jump over |
1545 | if (bTypeOK && bNameOK && bArgsOK) { |
1546 | sType = sType.trimmed(); // should contain type "int" |
1547 | sName = sName.trimmed(); // should contain function name "foo::function" |
1548 | sArgs = sArgs.trimmed(); // should contain argument list "(int a)" |
1549 | return true; |
1550 | } else |
1551 | return false; |
1552 | } |
1553 | |
1554 | bool CppParser::checkForNamespace() |
1555 | { |
1556 | return ((mIndex < mTokenizer.tokenCount()-1) |
1557 | && (mTokenizer[mIndex]->text == "namespace" )) |
1558 | || ( |
1559 | (mIndex+1 < mTokenizer.tokenCount()-1) |
1560 | && (mTokenizer[mIndex]->text == "inline" ) |
1561 | && (mTokenizer[mIndex+1]->text == "namespace" )); |
1562 | } |
1563 | |
1564 | bool CppParser::checkForPreprocessor() |
1565 | { |
1566 | // return (mIndex < mTokenizer.tokenCount()) |
1567 | // && ( "#" == mTokenizer[mIndex]->text); |
1568 | return (mTokenizer[mIndex]->text.startsWith('#')); |
1569 | } |
1570 | |
1571 | bool CppParser::checkForScope() |
1572 | { |
1573 | return (mIndex < mTokenizer.tokenCount() - 1) |
1574 | && (mTokenizer[mIndex + 1]->text == ':') |
1575 | && ( |
1576 | (mTokenizer[mIndex]->text == "public" ) |
1577 | || (mTokenizer[mIndex]->text == "protected" ) |
1578 | || (mTokenizer[mIndex]->text == "private" ) |
1579 | ); |
1580 | } |
1581 | |
1582 | void CppParser::checkForSkipStatement() |
1583 | { |
1584 | if ((mSkipList.count()>0) && (mIndex == mSkipList.back())) { // skip to next ';' |
1585 | do { |
1586 | mIndex++; |
1587 | } while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text[0] != ';')); |
1588 | mIndex++; //skip ';' |
1589 | mSkipList.pop_back(); |
1590 | } |
1591 | } |
1592 | |
1593 | bool CppParser::checkForStructs() |
1594 | { |
1595 | int dis = 0; |
1596 | if ((mTokenizer[mIndex]->text == "friend" ) |
1597 | || (mTokenizer[mIndex]->text == "public" ) |
1598 | || (mTokenizer[mIndex]->text == "private" )) |
1599 | dis = 1; |
1600 | if (mIndex >= mTokenizer.tokenCount() - 2 - dis) |
1601 | return false; |
1602 | QString word = mTokenizer[mIndex+dis]->text; |
1603 | int keyLen = calcKeyLenForStruct(word); |
1604 | if (keyLen<0) |
1605 | return false; |
1606 | bool result = (word.length() == keyLen) || isSpaceChar(word[keyLen]) |
1607 | || (word[keyLen] == '['); |
1608 | |
1609 | if (result) { |
1610 | if (mTokenizer[mIndex + 2+dis]->text[0] != ';') { // not: class something; |
1611 | int i = mIndex+dis +1; |
1612 | // the check for ']' was added because of this example: |
1613 | // struct option long_options[] = { |
1614 | // {"debug", 1, 0, 'D'}, |
1615 | // {"info", 0, 0, 'i'}, |
1616 | // ... |
1617 | // }; |
1618 | while (i < mTokenizer.tokenCount()) { |
1619 | QChar ch = mTokenizer[i]->text.back(); |
1620 | if (ch=='{' || ch == ':') |
1621 | break; |
1622 | switch(ch.unicode()) { |
1623 | case ';': |
1624 | case '}': |
1625 | case ',': |
1626 | case ')': |
1627 | case ']': |
1628 | case '=': |
1629 | case '*': |
1630 | case '&': |
1631 | case '%': |
1632 | case '+': |
1633 | case '-': |
1634 | case '~': |
1635 | return false; |
1636 | } |
1637 | i++; |
1638 | } |
1639 | } |
1640 | } |
1641 | return result; |
1642 | } |
1643 | |
1644 | bool CppParser::checkForTypedef() |
1645 | { |
1646 | return mTokenizer[mIndex]->text == "typedef" ; |
1647 | } |
1648 | |
1649 | bool CppParser::checkForTypedefEnum() |
1650 | { |
1651 | //we assume that typedef is the current index, so we check the next |
1652 | //should call CheckForTypedef first!!! |
1653 | return (mIndex < mTokenizer.tokenCount() - 1) && |
1654 | (mTokenizer[mIndex + 1]->text == "enum" ); |
1655 | } |
1656 | |
1657 | bool CppParser::checkForTypedefStruct() |
1658 | { |
1659 | //we assume that typedef is the current index, so we check the next |
1660 | //should call CheckForTypedef first!!! |
1661 | if (mIndex+1 >= mTokenizer.tokenCount()) |
1662 | return false; |
1663 | QString word = mTokenizer[mIndex + 1]->text; |
1664 | int keyLen = calcKeyLenForStruct(word); |
1665 | if (keyLen<0) |
1666 | return false; |
1667 | return (word.length() == keyLen) || isSpaceChar(word[keyLen]) || word[keyLen]=='['; |
1668 | } |
1669 | |
1670 | bool CppParser::checkForUsing() |
1671 | { |
1672 | return (mIndex < mTokenizer.tokenCount()-1) && mTokenizer[mIndex]->text == "using" ; |
1673 | |
1674 | } |
1675 | |
1676 | bool CppParser::checkForVar() |
1677 | { |
1678 | // Be pessimistic |
1679 | bool result = false; |
1680 | |
1681 | // Store old index |
1682 | int indexBackup = mIndex; |
1683 | |
1684 | // Use mIndex so we can reuse checking functions |
1685 | if (mIndex + 1 < mTokenizer.tokenCount()) { |
1686 | // Check the current and the next token |
1687 | for (int i = 0; i<=1; i++) { |
1688 | if (checkForKeyword() |
1689 | || isInvalidVarPrefixChar(mTokenizer[mIndex]->text.front()) |
1690 | || (mTokenizer[mIndex]->text.back() == '.') |
1691 | || ( |
1692 | (mTokenizer[mIndex]->text.length() > 1) && |
1693 | (mTokenizer[mIndex]->text[mTokenizer[mIndex]->text.length() - 2] == '-') && |
1694 | (mTokenizer[mIndex]->text[mTokenizer[mIndex]->text.length() - 1] == '>')) |
1695 | ) { |
1696 | // Reset index and fail |
1697 | mIndex = indexBackup; |
1698 | return false; |
1699 | } // Could be a function pointer? |
1700 | else if (mTokenizer[mIndex]->text.front() == '(') { |
1701 | // Quick fix: there must be a pointer operator in the first tiken |
1702 | if ( (mIndex + 1 >= mTokenizer.tokenCount()) |
1703 | || (mTokenizer[mIndex + 1]->text.front() != '(') |
1704 | || mTokenizer[mIndex]->text.indexOf('*')<0) { |
1705 | // Reset index and fail |
1706 | mIndex = indexBackup; |
1707 | return false; |
1708 | } |
1709 | } |
1710 | mIndex++; |
1711 | } |
1712 | } |
1713 | |
1714 | // Revert to the point we started at |
1715 | mIndex = indexBackup; |
1716 | |
1717 | // Fail if we do not find a comma or a semicolon or a ( (inline constructor) |
1718 | while (mIndex < mTokenizer.tokenCount()) { |
1719 | if (mTokenizer[mIndex]->text.front() == '#' |
1720 | || mTokenizer[mIndex]->text.front() == '}' |
1721 | || checkForKeyword()) { |
1722 | break; // fail |
1723 | // } else if ((mTokenizer[mIndex]->text.length()>1) && (mTokenizer[mIndex]->text[0] == '(') |
1724 | // && (mTokenizer[mIndex]->text[1] == '(')) { // TODO: is this used to remove __attribute stuff? |
1725 | // break; |
1726 | } else if (mTokenizer[mIndex]->text.front() == ',' |
1727 | || mTokenizer[mIndex]->text.front() == ';' |
1728 | || mTokenizer[mIndex]->text.front() == '{') { |
1729 | result = true; |
1730 | break; |
1731 | } |
1732 | mIndex++; |
1733 | } |
1734 | |
1735 | // Revert to the point we started at |
1736 | mIndex = indexBackup; |
1737 | return result; |
1738 | } |
1739 | |
1740 | int CppParser::getCurrentBlockEndSkip() |
1741 | { |
1742 | if (mBlockEndSkips.isEmpty()) |
1743 | return mTokenizer.tokenCount()+1; |
1744 | return mBlockEndSkips.back(); |
1745 | } |
1746 | |
1747 | int CppParser::getCurrentBlockBeginSkip() |
1748 | { |
1749 | if (mBlockBeginSkips.isEmpty()) |
1750 | return mTokenizer.tokenCount()+1; |
1751 | return mBlockBeginSkips.back(); |
1752 | } |
1753 | |
1754 | int CppParser::getCurrentInlineNamespaceEndSkip() |
1755 | { |
1756 | if (mInlineNamespaceEndSkips.isEmpty()) |
1757 | return mTokenizer.tokenCount()+1; |
1758 | return mInlineNamespaceEndSkips.back(); |
1759 | } |
1760 | |
1761 | PStatement CppParser::getCurrentScope() |
1762 | { |
1763 | if (mCurrentScope.isEmpty()) { |
1764 | return PStatement(); |
1765 | } |
1766 | return mCurrentScope.back(); |
1767 | } |
1768 | |
1769 | void CppParser::getFullNamespace(const QString &phrase, QString &sNamespace, QString &member) |
1770 | { |
1771 | sNamespace = "" ; |
1772 | member = phrase; |
1773 | int strLen = phrase.length(); |
1774 | if (strLen==0) |
1775 | return; |
1776 | int lastI =-1; |
1777 | int i=0; |
1778 | while (i<strLen) { |
1779 | if ((i+1<strLen) && (phrase[i]==':') && (phrase[i+1]==':') ) { |
1780 | if (!mNamespaces.contains(sNamespace)) { |
1781 | break; |
1782 | } else { |
1783 | lastI = i; |
1784 | } |
1785 | } |
1786 | sNamespace += phrase[i]; |
1787 | i++; |
1788 | } |
1789 | if (i>=strLen) { |
1790 | if (mNamespaces.contains(sNamespace)) { |
1791 | sNamespace = phrase; |
1792 | member = "" ; |
1793 | return; |
1794 | } |
1795 | } |
1796 | if (lastI >= 0) { |
1797 | sNamespace = phrase.mid(0,lastI); |
1798 | member = phrase.mid(lastI+2); |
1799 | } else { |
1800 | sNamespace = "" ; |
1801 | member = phrase; |
1802 | } |
1803 | } |
1804 | |
1805 | QString CppParser::getFullStatementName(const QString &command, const PStatement& parent) |
1806 | { |
1807 | PStatement scopeStatement=parent; |
1808 | while (scopeStatement && !isNamedScope(scopeStatement->kind)) |
1809 | scopeStatement = scopeStatement->parentScope.lock(); |
1810 | if (scopeStatement) |
1811 | return scopeStatement->fullName + "::" + command; |
1812 | else |
1813 | return command; |
1814 | } |
1815 | |
1816 | PStatement CppParser::getIncompleteClass(const QString &command, const PStatement& parentScope) |
1817 | { |
1818 | QString s=command; |
1819 | //remove template parameter |
1820 | int p = s.indexOf('<'); |
1821 | if (p>=0) { |
1822 | s.truncate(p); |
1823 | } |
1824 | PStatement result = findStatementOf(mCurrentFile,s,parentScope,true); |
1825 | if (result && result->kind!=StatementKind::skClass) |
1826 | return PStatement(); |
1827 | return result; |
1828 | } |
1829 | |
1830 | StatementScope CppParser::getScope() |
1831 | { |
1832 | // Don't blindly trust levels. Namespaces and externs can have levels too |
1833 | PStatement currentScope = getCurrentScope(); |
1834 | |
1835 | // Invalid class or namespace/extern |
1836 | if (!currentScope || (currentScope->kind == StatementKind::skNamespace)) |
1837 | return StatementScope::ssGlobal; |
1838 | else if (currentScope->kind == StatementKind::skClass) |
1839 | return StatementScope::ssClassLocal; |
1840 | else |
1841 | return StatementScope::ssLocal; |
1842 | } |
1843 | |
1844 | QString CppParser::getStatementKey(const QString &sName, const QString &sType, const QString &sNoNameArgs) |
1845 | { |
1846 | return sName + "--" + sType + "--" + sNoNameArgs; |
1847 | } |
1848 | |
1849 | PStatement CppParser::getTypeDef(const PStatement& statement, |
1850 | const QString& fileName, const QString& aType) |
1851 | { |
1852 | if (!statement) { |
1853 | return PStatement(); |
1854 | } |
1855 | if (statement->kind == StatementKind::skClass |
1856 | || statement->kind == StatementKind::skEnumType |
1857 | || statement->kind == StatementKind::skEnumClassType) { |
1858 | return statement; |
1859 | } else if (statement->kind == StatementKind::skTypedef) { |
1860 | if (statement->type == aType) // prevent infinite loop |
1861 | return statement; |
1862 | PStatement result = findTypeDefinitionOf(fileName,statement->type, statement->parentScope.lock()); |
1863 | if (!result) // found end of typedef trail, return result |
1864 | return statement; |
1865 | return result; |
1866 | } else if (statement->kind == StatementKind::skAlias) { |
1867 | PStatement result = findAliasedStatement(statement); |
1868 | if (!result) // found end of typedef trail, return result |
1869 | return statement; |
1870 | return result; |
1871 | } else |
1872 | return PStatement(); |
1873 | } |
1874 | |
1875 | void CppParser::handleCatchBlock() |
1876 | { |
1877 | int startLine= mTokenizer[mIndex]->line; |
1878 | mIndex++; // skip for/catch; |
1879 | if (!((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.startsWith('(')))) |
1880 | return; |
1881 | //skip params |
1882 | int i2=mIndex+1; |
1883 | if (i2>=mTokenizer.tokenCount()) |
1884 | return; |
1885 | if (mTokenizer[i2]->text.startsWith('{')) { |
1886 | mBlockBeginSkips.append(i2); |
1887 | int i = skipBraces(i2); |
1888 | if (i==i2) { |
1889 | mBlockEndSkips.append(mTokenizer.tokenCount()); |
1890 | } else { |
1891 | mBlockEndSkips.append(i); |
1892 | } |
1893 | } else { |
1894 | int i=i2; |
1895 | while ((i<mTokenizer.tokenCount()) && !mTokenizer[i]->text.startsWith(';')) |
1896 | i++; |
1897 | mBlockEndSkips.append(i); |
1898 | } |
1899 | // add a block |
1900 | PStatement block = addStatement( |
1901 | getCurrentScope(), |
1902 | mCurrentFile, |
1903 | "" , |
1904 | "" , |
1905 | "" , |
1906 | "" , |
1907 | startLine, |
1908 | StatementKind::skBlock, |
1909 | getScope(), |
1910 | mClassScope, |
1911 | true, |
1912 | false); |
1913 | addSoloScopeLevel(block,startLine,false); |
1914 | if (!mTokenizer[mIndex]->text.contains("..." )) |
1915 | scanMethodArgs(block,mTokenizer[mIndex]->text); |
1916 | } |
1917 | |
1918 | void CppParser::handleEnum() |
1919 | { |
1920 | //todo : handle enum class |
1921 | QString enumName = "" ; |
1922 | bool isEnumClass = false; |
1923 | int startLine = mTokenizer[mIndex]->line; |
1924 | mIndex++; //skip 'enum' |
1925 | |
1926 | if (mIndex < mTokenizer.tokenCount() && mTokenizer[mIndex]->text == "class" ) { |
1927 | //enum class |
1928 | isEnumClass = true; |
1929 | mIndex++; //skip class |
1930 | |
1931 | } |
1932 | if ((mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('{')) { // enum {...} NAME |
1933 | // Skip to the closing brace |
1934 | int i = skipBraces(mIndex); |
1935 | // Have we found the name? |
1936 | if ((i + 1 < mTokenizer.tokenCount()) && mTokenizer[i]->text.startsWith('}')) { |
1937 | if (!mTokenizer[i + 1]->text.startsWith(';')) |
1938 | enumName = mTokenizer[i + 1]->text.trimmed(); |
1939 | } |
1940 | } else if (mIndex+1< mTokenizer.tokenCount() && mTokenizer[mIndex+1]->text.startsWith('{')){ // enum NAME {...}; |
1941 | if ( (mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text == "class" ) { |
1942 | //enum class {...} NAME |
1943 | isEnumClass = true; |
1944 | mIndex++; |
1945 | } |
1946 | while ((mIndex < mTokenizer.tokenCount()) && |
1947 | !(mTokenizer[mIndex]->text.startsWith('{') |
1948 | || mTokenizer[mIndex]->text.startsWith(';'))) { |
1949 | enumName += mTokenizer[mIndex]->text + ' '; |
1950 | mIndex++; |
1951 | } |
1952 | enumName = enumName.trimmed(); |
1953 | // An opening brace must be present after NAME |
1954 | if ((mIndex >= mTokenizer.tokenCount()) || !mTokenizer[mIndex]->text.startsWith('{')) |
1955 | return; |
1956 | } else { |
1957 | // enum NAME blahblah |
1958 | // it's an old c-style enum variable definition |
1959 | return; |
1960 | } |
1961 | |
1962 | // Add statement for enum name too |
1963 | PStatement enumStatement; |
1964 | if (!enumName.isEmpty()) { |
1965 | if (isEnumClass) { |
1966 | enumStatement=addStatement( |
1967 | getCurrentScope(), |
1968 | mCurrentFile, |
1969 | "enum class" , |
1970 | enumName, |
1971 | "" , |
1972 | "" , |
1973 | startLine, |
1974 | StatementKind::skEnumClassType, |
1975 | getScope(), |
1976 | mClassScope, |
1977 | true, |
1978 | false); |
1979 | } else { |
1980 | enumStatement=addStatement( |
1981 | getCurrentScope(), |
1982 | mCurrentFile, |
1983 | "enum" , |
1984 | enumName, |
1985 | "" , |
1986 | "" , |
1987 | startLine, |
1988 | StatementKind::skEnumType, |
1989 | getScope(), |
1990 | mClassScope, |
1991 | true, |
1992 | false); |
1993 | } |
1994 | } else { |
1995 | enumStatement = getCurrentScope(); |
1996 | } |
1997 | |
1998 | // Skip opening brace |
1999 | mIndex++; |
2000 | |
2001 | // Call every member "enum NAME ITEMNAME" |
2002 | QString lastType("enum" ); |
2003 | if (!enumName.isEmpty()) |
2004 | lastType += ' ' + enumName; |
2005 | QString cmd; |
2006 | QString args; |
2007 | if (!mTokenizer[mIndex]->text.startsWith('}')) { |
2008 | while ((mIndex < mTokenizer.tokenCount()) && |
2009 | !isblockChar(mTokenizer[mIndex]->text[0])) { |
2010 | if (!mTokenizer[mIndex]->text.startsWith(',')) { |
2011 | if (mTokenizer[mIndex]->text.endsWith(']')) { //array; break args |
2012 | int p = mTokenizer[mIndex]->text.indexOf('['); |
2013 | cmd = mTokenizer[mIndex]->text.mid(0,p); |
2014 | args = mTokenizer[mIndex]->text.mid(p); |
2015 | } else { |
2016 | cmd = mTokenizer[mIndex]->text; |
2017 | args = "" ; |
2018 | } |
2019 | if (isEnumClass) { |
2020 | if (enumStatement) { |
2021 | addStatement( |
2022 | enumStatement, |
2023 | mCurrentFile, |
2024 | lastType, |
2025 | cmd, |
2026 | args, |
2027 | "" , |
2028 | mTokenizer[mIndex]->line, |
2029 | StatementKind::skEnum, |
2030 | getScope(), |
2031 | mClassScope, |
2032 | true, |
2033 | false); |
2034 | } |
2035 | } else { |
2036 | if (enumStatement) { |
2037 | addStatement( |
2038 | enumStatement, |
2039 | mCurrentFile, |
2040 | lastType, |
2041 | cmd, |
2042 | args, |
2043 | "" , |
2044 | mTokenizer[mIndex]->line, |
2045 | StatementKind::skEnum, |
2046 | getScope(), |
2047 | mClassScope, |
2048 | true, |
2049 | false); |
2050 | } |
2051 | addStatement( |
2052 | getCurrentScope(), |
2053 | mCurrentFile, |
2054 | lastType, |
2055 | cmd, |
2056 | args, |
2057 | "" , |
2058 | mTokenizer[mIndex]->line, |
2059 | StatementKind::skEnum, |
2060 | getScope(), |
2061 | mClassScope, |
2062 | true, |
2063 | false); |
2064 | } |
2065 | } |
2066 | mIndex ++ ; |
2067 | } |
2068 | } |
2069 | // Step over closing brace |
2070 | if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('}')) |
2071 | mIndex++; |
2072 | } |
2073 | |
2074 | void CppParser::handleForBlock() |
2075 | { |
2076 | int startLine = mTokenizer[mIndex]->line; |
2077 | mIndex++; // skip for/catch; |
2078 | if (!(mIndex < mTokenizer.tokenCount())) |
2079 | return; |
2080 | int i=mIndex; |
2081 | while ((i<mTokenizer.tokenCount()) && !mTokenizer[i]->text.startsWith(';')) |
2082 | i++; |
2083 | if (i>=mTokenizer.tokenCount()) |
2084 | return; |
2085 | int i2 = i+1; //skip over ';' (tokenizer have change for(;;) to for(;) |
2086 | if (i2>=mTokenizer.tokenCount()) |
2087 | return; |
2088 | if (mTokenizer[i2]->text.startsWith('{')) { |
2089 | mBlockBeginSkips.append(i2); |
2090 | i=skipBraces(i2); |
2091 | if (i==i2) |
2092 | mBlockEndSkips.append(mTokenizer.tokenCount()); |
2093 | else |
2094 | mBlockEndSkips.append(i); |
2095 | } else { |
2096 | i=i2; |
2097 | while ((i<mTokenizer.tokenCount()) && !mTokenizer[i]->text.startsWith(';')) |
2098 | i++; |
2099 | mBlockEndSkips.append(i); |
2100 | } |
2101 | // add a block |
2102 | PStatement block = addStatement( |
2103 | getCurrentScope(), |
2104 | mCurrentFile, |
2105 | "" , |
2106 | "" , |
2107 | "" , |
2108 | "" , |
2109 | startLine, |
2110 | StatementKind::skBlock, |
2111 | getScope(), |
2112 | mClassScope, |
2113 | true, |
2114 | false); |
2115 | |
2116 | addSoloScopeLevel(block,startLine); |
2117 | } |
2118 | |
2119 | void CppParser::handleKeyword() |
2120 | { |
2121 | // Skip |
2122 | SkipType skipType = mCppKeywords.value(mTokenizer[mIndex]->text,SkipType::skNone); |
2123 | switch (skipType) { |
2124 | case SkipType::skItself: |
2125 | // skip it; |
2126 | mIndex++; |
2127 | break; |
2128 | case SkipType::skToSemicolon: |
2129 | // Skip to ; |
2130 | while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith(';')) |
2131 | mIndex++; |
2132 | mIndex++;// step over |
2133 | break; |
2134 | case SkipType::skToColon: |
2135 | // Skip to : |
2136 | while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith(':')) |
2137 | mIndex++; |
2138 | break; |
2139 | case SkipType::skToRightParenthesis: |
2140 | // skip to ) |
2141 | while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.endsWith(')')) |
2142 | mIndex++; |
2143 | mIndex++; // step over |
2144 | break; |
2145 | case SkipType::skToLeftBrace: |
2146 | // Skip to { |
2147 | while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith('{')) |
2148 | mIndex++; |
2149 | break; |
2150 | case SkipType::skToRightBrace: |
2151 | // Skip to } |
2152 | while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith('}')) |
2153 | mIndex++; |
2154 | mIndex++; // step over |
2155 | break; |
2156 | default: |
2157 | break; |
2158 | } |
2159 | } |
2160 | |
2161 | void CppParser::handleMethod(const QString &sType, const QString &sName, const QString &sArgs, bool isStatic, bool isFriend) |
2162 | { |
2163 | bool isValid = true; |
2164 | bool isDeclaration = false; // assume it's not a prototype |
2165 | int i = mIndex; |
2166 | int startLine = mTokenizer[mIndex]->line; |
2167 | |
2168 | // Skip over argument list |
2169 | while ((mIndex < mTokenizer.tokenCount()) && ! ( |
2170 | isblockChar(mTokenizer[mIndex]->text.front()) |
2171 | || mTokenizer[mIndex]->text.startsWith(':'))) |
2172 | mIndex++; |
2173 | |
2174 | if (mIndex >= mTokenizer.tokenCount()) // not finished define, just skip it; |
2175 | return; |
2176 | |
2177 | PStatement functionClass = getCurrentScope(); |
2178 | // Check if this is a prototype |
2179 | if (mTokenizer[mIndex]->text.startsWith(';') |
2180 | || mTokenizer[mIndex]->text.startsWith('}')) {// prototype |
2181 | isDeclaration = true; |
2182 | } else { |
2183 | // Find the function body start after the inherited constructor |
2184 | if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith(':')) { |
2185 | while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text.front())) |
2186 | mIndex++; |
2187 | } |
2188 | |
2189 | // Still a prototype |
2190 | if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.startsWith(';') |
2191 | || mTokenizer[mIndex]->text.startsWith('}'))) {// prototype |
2192 | isDeclaration = true; |
2193 | } |
2194 | } |
2195 | |
2196 | QString scopelessName; |
2197 | PStatement functionStatement; |
2198 | if (isFriend && isDeclaration && functionClass) { |
2199 | int delimPos = sName.indexOf("::" ); |
2200 | if (delimPos >= 0) { |
2201 | scopelessName = sName.mid(delimPos+2); |
2202 | } else |
2203 | scopelessName = sName; |
2204 | //TODO : we should check namespace |
2205 | functionClass->friends.insert(scopelessName); |
2206 | } else if (isValid) { |
2207 | // Use the class the function belongs to as the parent ID if the function is declared outside of the class body |
2208 | int delimPos = sName.indexOf("::" ); |
2209 | QString scopelessName; |
2210 | QString parentClassName; |
2211 | if (delimPos >= 0) { |
2212 | // Provide Bar instead of Foo::Bar |
2213 | scopelessName = sName.mid(delimPos+2); |
2214 | |
2215 | // Check what class this function belongs to |
2216 | parentClassName = sName.mid(0, delimPos); |
2217 | functionClass = getIncompleteClass(parentClassName,getCurrentScope()); |
2218 | } else |
2219 | scopelessName = sName; |
2220 | |
2221 | StatementKind functionKind; |
2222 | // Determine function type |
2223 | if (scopelessName == sType) { |
2224 | functionKind = StatementKind::skConstructor; |
2225 | } else if (scopelessName == '~' + sType) { |
2226 | functionKind = StatementKind::skDestructor; |
2227 | } else { |
2228 | functionKind = StatementKind::skFunction; |
2229 | } |
2230 | |
2231 | // For function definitions, the parent class is given. Only use that as a parent |
2232 | if (!isDeclaration) { |
2233 | functionStatement=addStatement( |
2234 | functionClass, |
2235 | mCurrentFile, |
2236 | sType, |
2237 | scopelessName, |
2238 | sArgs, |
2239 | "" , |
2240 | //mTokenizer[mIndex - 1]^.Line, |
2241 | startLine, |
2242 | functionKind, |
2243 | getScope(), |
2244 | mClassScope, |
2245 | true, |
2246 | isStatic); |
2247 | scanMethodArgs(functionStatement, sArgs); |
2248 | // add variable this to the class function |
2249 | if (functionClass && functionClass->kind == StatementKind::skClass && |
2250 | !isStatic) { |
2251 | //add this to non-static class member function |
2252 | addStatement( |
2253 | functionStatement, |
2254 | mCurrentFile, |
2255 | functionClass->command, |
2256 | "this" , |
2257 | "" , |
2258 | "" , |
2259 | startLine, |
2260 | StatementKind::skVariable, |
2261 | StatementScope::ssLocal, |
2262 | StatementClassScope::scsNone, |
2263 | true, |
2264 | false); |
2265 | } |
2266 | // add "__func__ variable" |
2267 | addStatement( |
2268 | functionStatement, |
2269 | mCurrentFile, |
2270 | "static const char " , |
2271 | "__func__" , |
2272 | "[]" , |
2273 | "\"" +scopelessName+"\"" , |
2274 | startLine+1, |
2275 | StatementKind::skVariable, |
2276 | StatementScope::ssLocal, |
2277 | StatementClassScope::scsNone, |
2278 | true, |
2279 | false); |
2280 | } else { |
2281 | functionStatement = addStatement( |
2282 | functionClass, |
2283 | mCurrentFile, |
2284 | sType, |
2285 | scopelessName, |
2286 | sArgs, |
2287 | "" , |
2288 | //mTokenizer[mIndex - 1]^.Line, |
2289 | startLine, |
2290 | functionKind, |
2291 | getScope(), |
2292 | mClassScope, |
2293 | false, |
2294 | isStatic); |
2295 | } |
2296 | |
2297 | } |
2298 | |
2299 | |
2300 | if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('{')) { |
2301 | addSoloScopeLevel(functionStatement,startLine); |
2302 | mIndex++; //skip '{' |
2303 | } else if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith(';')) { |
2304 | addSoloScopeLevel(functionStatement,startLine); |
2305 | if (mTokenizer[mIndex]->line != startLine) |
2306 | removeScopeLevel(mTokenizer[mIndex]->line+1); |
2307 | else |
2308 | removeScopeLevel(startLine+1); |
2309 | mIndex++; |
2310 | } |
2311 | |
2312 | if (i == mIndex) { // if not moved ahead, something is wrong but don't get stuck ;) |
2313 | if ( (mIndex < mTokenizer.tokenCount()) && |
2314 | ! isBraceChar(mTokenizer[mIndex]->text.front())) { |
2315 | mIndex++; |
2316 | } |
2317 | } |
2318 | } |
2319 | |
2320 | void CppParser::handleNamespace() |
2321 | { |
2322 | bool isInline=false; |
2323 | if (mTokenizer[mIndex]->text == "inline" ) { |
2324 | isInline = true; |
2325 | mIndex++; //skip 'inline' |
2326 | } |
2327 | |
2328 | int startLine = mTokenizer[mIndex]->line; |
2329 | mIndex++; //skip 'namespace' |
2330 | |
2331 | if (!isLetterChar(mTokenizer[mIndex]->text.front())) |
2332 | //wrong namespace define, stop handling |
2333 | return; |
2334 | QString command = mTokenizer[mIndex]->text; |
2335 | |
2336 | QString fullName = getFullStatementName(command,getCurrentScope()); |
2337 | if (isInline) { |
2338 | mInlineNamespaces.insert(fullName); |
2339 | } else if (mInlineNamespaces.contains(fullName)) { |
2340 | isInline = true; |
2341 | } |
2342 | // if (command.startsWith("__")) // hack for inline namespaces |
2343 | // isInline = true; |
2344 | mIndex++; |
2345 | if (mIndex>=mTokenizer.tokenCount()) |
2346 | return; |
2347 | QString aliasName; |
2348 | if ((mIndex+2<mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == '=')) { |
2349 | aliasName=mTokenizer[mIndex+1]->text; |
2350 | //namespace alias |
2351 | addStatement( |
2352 | getCurrentScope(), |
2353 | mCurrentFile, |
2354 | aliasName, // name of the alias namespace |
2355 | command, // command |
2356 | "" , // args |
2357 | "" , // values |
2358 | //mTokenizer[mIndex]^.Line, |
2359 | startLine, |
2360 | StatementKind::skNamespaceAlias, |
2361 | getScope(), |
2362 | mClassScope, |
2363 | true, |
2364 | false); |
2365 | mIndex+=2; //skip ; |
2366 | return; |
2367 | } else if (isInline) { |
2368 | //inline namespace , just skip it |
2369 | // Skip to '{' |
2370 | while ((mIndex<mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() != '{')) |
2371 | mIndex++; |
2372 | int i =skipBraces(mIndex); //skip '}' |
2373 | if (i==mIndex) |
2374 | mInlineNamespaceEndSkips.append(mTokenizer.tokenCount()); |
2375 | else |
2376 | mInlineNamespaceEndSkips.append(i); |
2377 | if (mIndex<mTokenizer.tokenCount()) |
2378 | mIndex++; //skip '{' |
2379 | } else { |
2380 | PStatement namespaceStatement = addStatement( |
2381 | getCurrentScope(), |
2382 | mCurrentFile, |
2383 | "" , // type |
2384 | command, // command |
2385 | "" , // args |
2386 | "" , // values |
2387 | startLine, |
2388 | StatementKind::skNamespace, |
2389 | getScope(), |
2390 | mClassScope, |
2391 | true, |
2392 | false); |
2393 | addSoloScopeLevel(namespaceStatement,startLine); |
2394 | |
2395 | // Skip to '{' |
2396 | while ((mIndex<mTokenizer.tokenCount()) && !mTokenizer[mIndex]->text.startsWith('{')) |
2397 | mIndex++; |
2398 | if (mIndex<mTokenizer.tokenCount()) |
2399 | mIndex++; //skip '{' |
2400 | } |
2401 | } |
2402 | |
2403 | void CppParser::handleOtherTypedefs() |
2404 | { |
2405 | int startLine = mTokenizer[mIndex]->line; |
2406 | // Skip typedef word |
2407 | mIndex++; |
2408 | |
2409 | if (mIndex>=mTokenizer.tokenCount()) |
2410 | return; |
2411 | |
2412 | if (mTokenizer[mIndex]->text.front() == '(' |
2413 | || mTokenizer[mIndex]->text.front() == ',' |
2414 | || mTokenizer[mIndex]->text.front() == ';') { // error typedef |
2415 | //skip to ; |
2416 | while ((mIndex< mTokenizer.tokenCount()) && !mTokenizer[mIndex]->text.startsWith(';')) |
2417 | mIndex++; |
2418 | //skip ; |
2419 | if ((mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith(';')) |
2420 | mIndex++; |
2421 | return; |
2422 | } |
2423 | if ((mIndex+1<mTokenizer.tokenCount()) |
2424 | && (mTokenizer[mIndex+1]->text == ';')) { |
2425 | //no old type |
2426 | QString newType = mTokenizer[mIndex]->text.trimmed(); |
2427 | addStatement( |
2428 | getCurrentScope(), |
2429 | mCurrentFile, |
2430 | "" , |
2431 | newType, |
2432 | "" , |
2433 | "" , |
2434 | startLine, |
2435 | StatementKind::skTypedef, |
2436 | getScope(), |
2437 | mClassScope, |
2438 | true, |
2439 | false); |
2440 | mIndex+=2; //skip ; |
2441 | return; |
2442 | } |
2443 | QString oldType; |
2444 | |
2445 | // Walk up to first new word (before first comma or ;) |
2446 | while(true) { |
2447 | oldType += mTokenizer[mIndex]->text + ' '; |
2448 | mIndex++; |
2449 | if (mIndex+1>=mTokenizer.tokenCount()) |
2450 | break; |
2451 | if (mTokenizer[mIndex + 1]->text.front() == ',' |
2452 | || mTokenizer[mIndex + 1]->text.front() == ';') |
2453 | break; |
2454 | if ((mIndex + 2 < mTokenizer.tokenCount()) |
2455 | && (mTokenizer[mIndex + 2]->text.front() == ',' |
2456 | || mTokenizer[mIndex + 2]->text.front() == ';') |
2457 | && (mTokenizer[mIndex + 1]->text.front() == '(')) |
2458 | break; |
2459 | } |
2460 | oldType = oldType.trimmed(); |
2461 | |
2462 | // Add synonyms for old |
2463 | if ((mIndex+1 < mTokenizer.tokenCount()) && !oldType.isEmpty()) { |
2464 | QString newType; |
2465 | while(true) { |
2466 | // Support multiword typedefs |
2467 | if ((mIndex + 2 < mTokenizer.tokenCount()) |
2468 | && (mTokenizer[mIndex + 2]->text.front() == ',' |
2469 | || mTokenizer[mIndex + 2]->text.front() == ';') |
2470 | && (mTokenizer[mIndex + 1]->text.front() == '(')) { |
2471 | //valid function define |
2472 | newType = mTokenizer[mIndex]->text.trimmed(); |
2473 | newType = newType.mid(1,newType.length()-2); //remove '(' and ')'; |
2474 | newType = newType.trimmed(); |
2475 | int p = newType.lastIndexOf(' '); |
2476 | if (p>=0) |
2477 | newType.truncate(p+1); |
2478 | addStatement( |
2479 | getCurrentScope(), |
2480 | mCurrentFile, |
2481 | oldType, |
2482 | newType, |
2483 | mTokenizer[mIndex + 1]->text, |
2484 | "" , |
2485 | startLine, |
2486 | StatementKind::skTypedef, |
2487 | getScope(), |
2488 | mClassScope, |
2489 | true, |
2490 | false); |
2491 | newType = "" ; |
2492 | //skip to ',' or ';' |
2493 | mIndex+=2; |
2494 | } else if (mTokenizer[mIndex+1]->text.front() ==',' |
2495 | || mTokenizer[mIndex+1]->text.front() ==';' |
2496 | || mTokenizer[mIndex+1]->text.front() =='(') { |
2497 | newType += mTokenizer[mIndex]->text; |
2498 | newType = newType.trimmed(); |
2499 | addStatement( |
2500 | getCurrentScope(), |
2501 | mCurrentFile, |
2502 | oldType, |
2503 | newType, |
2504 | "" , |
2505 | "" , |
2506 | startLine, |
2507 | StatementKind::skTypedef, |
2508 | getScope(), |
2509 | mClassScope, |
2510 | true, |
2511 | false); |
2512 | newType = "" ; |
2513 | mIndex++; |
2514 | } else { |
2515 | newType += mTokenizer[mIndex]->text + ' '; |
2516 | mIndex++; |
2517 | } |
2518 | if (mIndex < mTokenizer.tokenCount() && mTokenizer[mIndex]->text[0] == ',' ) |
2519 | mIndex++; |
2520 | if ((mIndex>= mTokenizer.tokenCount()) || (mTokenizer[mIndex]->text[0] == ';')) |
2521 | break; |
2522 | if (mIndex+1 >= mTokenizer.tokenCount()) |
2523 | break; |
2524 | } |
2525 | } |
2526 | |
2527 | // Step over semicolon (saves one HandleStatement loop) |
2528 | mIndex++; |
2529 | } |
2530 | |
2531 | void CppParser::handlePreprocessor() |
2532 | { |
2533 | QString text = mTokenizer[mIndex]->text.mid(1).trimmed(); |
2534 | if (text.startsWith("include" )) { // start of new file |
2535 | // format: #include fullfilename:line |
2536 | // Strip keyword |
2537 | QString s = text.mid(QString("include" ).length()); |
2538 | if (!s.startsWith(" " ) && !s.startsWith("\t" )) |
2539 | goto handlePreprocessorEnd; |
2540 | int delimPos = s.lastIndexOf(':'); |
2541 | if (delimPos>=0) { |
2542 | mCurrentFile = s.mid(0,delimPos).trimmed(); |
2543 | mIsSystemHeader = isSystemHeaderFile(mCurrentFile) || isProjectHeaderFile(mCurrentFile); |
2544 | mIsProjectFile = mProjectFiles.contains(mCurrentFile); mIsHeader = isHFile(mCurrentFile); |
2545 | |
2546 | // Mention progress to user if we enter a NEW file |
2547 | bool ok; |
2548 | int line = s.midRef(delimPos+1).toInt(&ok); |
2549 | if (line == 1) { |
2550 | mFilesScannedCount++; |
2551 | mFilesToScanCount++; |
2552 | emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount); |
2553 | } |
2554 | } |
2555 | } else if (text.startsWith("define" ) || text.startsWith("define" )) { |
2556 | |
2557 | // format: #define A B, remove define keyword |
2558 | QString s = text.mid(QString("define" ).length()); |
2559 | if (!s.startsWith(" " ) && !s.startsWith("\t" )) |
2560 | goto handlePreprocessorEnd; |
2561 | s = s.trimmed(); |
2562 | // Ask the preprocessor to cut parts up |
2563 | QString name,args,value; |
2564 | mPreprocessor.getDefineParts(s,name,args,value); |
2565 | |
2566 | addStatement( |
2567 | nullptr, // defines don't belong to any scope |
2568 | mCurrentFile, |
2569 | "" , // define has no type |
2570 | name, |
2571 | args, |
2572 | value, |
2573 | mTokenizer[mIndex]->line, |
2574 | StatementKind::skPreprocessor, |
2575 | StatementScope::ssGlobal, |
2576 | StatementClassScope::scsNone, |
2577 | true, |
2578 | false); |
2579 | } // TODO: undef ( define has limited scope) |
2580 | handlePreprocessorEnd: |
2581 | mIndex++; |
2582 | } |
2583 | |
2584 | StatementClassScope CppParser::getClassScope(int index) { |
2585 | if (mTokenizer[index]->text=="public" ) |
2586 | return StatementClassScope::scsPublic; |
2587 | else if (mTokenizer[index]->text=="private" ) |
2588 | return StatementClassScope::scsPrivate; |
2589 | else if (mTokenizer[index]->text=="protected" ) |
2590 | return StatementClassScope::scsProtected; |
2591 | else |
2592 | return StatementClassScope::scsNone; |
2593 | } |
2594 | |
2595 | void CppParser::handleScope() |
2596 | { |
2597 | mClassScope = getClassScope(mIndex); |
2598 | mIndex+=2; // the scope is followed by a ':' |
2599 | } |
2600 | |
2601 | bool CppParser::handleStatement() |
2602 | { |
2603 | QString S1,S2,S3; |
2604 | bool isStatic, isFriend; |
2605 | int idx=getCurrentBlockEndSkip(); |
2606 | int idx2=getCurrentBlockBeginSkip(); |
2607 | int idx3=getCurrentInlineNamespaceEndSkip(); |
2608 | if (mIndex >= idx2) { |
2609 | //skip (previous handled) block begin |
2610 | mBlockBeginSkips.pop_back(); |
2611 | if (mIndex == idx2) |
2612 | mIndex++; |
2613 | else if (mIndex<mTokenizer.tokenCount()) //error happens, but we must remove an (error) added scope |
2614 | removeScopeLevel(mTokenizer[mIndex]->line); |
2615 | } else if (mIndex >= idx) { |
2616 | //skip (previous handled) block end |
2617 | mBlockEndSkips.pop_back(); |
2618 | if (idx+1 < mTokenizer.tokenCount()) |
2619 | removeScopeLevel(mTokenizer[idx+1]->line); |
2620 | if (mIndex == idx) |
2621 | mIndex++; |
2622 | } else if (mIndex >= idx3) { |
2623 | //skip (previous handled) inline name space end |
2624 | mInlineNamespaceEndSkips.pop_back(); |
2625 | if (mIndex == idx3) |
2626 | mIndex++; |
2627 | } else if (mTokenizer[mIndex]->text.startsWith('{')) { |
2628 | PStatement block = addStatement( |
2629 | getCurrentScope(), |
2630 | mCurrentFile, |
2631 | "" , |
2632 | "" , |
2633 | "" , |
2634 | "" , |
2635 | //mTokenizer[mIndex]^.Line, |
2636 | mTokenizer[mIndex]->line, |
2637 | StatementKind::skBlock, |
2638 | getScope(), |
2639 | mClassScope, |
2640 | true, |
2641 | false); |
2642 | addSoloScopeLevel(block,mTokenizer[mIndex]->line); |
2643 | mIndex++; |
2644 | } else if (mTokenizer[mIndex]->text[0] == '}') { |
2645 | removeScopeLevel(mTokenizer[mIndex]->line); |
2646 | mIndex++; |
2647 | } else if (checkForPreprocessor()) { |
2648 | handlePreprocessor(); |
2649 | } else if (checkForKeyword()) { // includes template now |
2650 | handleKeyword(); |
2651 | } else if (checkForForBlock()) { // (for/catch) |
2652 | handleForBlock(); |
2653 | } else if (checkForCatchBlock()) { // (for/catch) |
2654 | handleCatchBlock(); |
2655 | } else if (checkForScope()) { // public /private/proteced |
2656 | handleScope(); |
2657 | } else if (checkForEnum()) { |
2658 | handleEnum(); |
2659 | } else if (checkForTypedef()) { |
2660 | if (mIndex+1 < mTokenizer.tokenCount()) { |
2661 | if (checkForTypedefStruct()) { // typedef struct something |
2662 | mIndex++; // skip 'typedef' |
2663 | handleStructs(true); |
2664 | } else if (checkForTypedefEnum()) { // typedef enum something |
2665 | mIndex++; // skip 'typedef' |
2666 | handleEnum(); |
2667 | } else |
2668 | handleOtherTypedefs(); // typedef Foo Bar |
2669 | } else |
2670 | mIndex++; |
2671 | } else if (checkForNamespace()) { |
2672 | handleNamespace(); |
2673 | } else if (checkForUsing()) { |
2674 | handleUsing(); |
2675 | } else if (checkForStructs()) { |
2676 | handleStructs(false); |
2677 | } else if (checkForMethod(S1, S2, S3, isStatic, isFriend)) { |
2678 | handleMethod(S1, S2, S3, isStatic, isFriend); // don't recalculate parts |
2679 | } else if (checkForVar()) { |
2680 | handleVar(); |
2681 | } else |
2682 | mIndex++; |
2683 | |
2684 | checkForSkipStatement(); |
2685 | |
2686 | return mIndex < mTokenizer.tokenCount(); |
2687 | |
2688 | } |
2689 | |
2690 | void CppParser::handleStructs(bool isTypedef) |
2691 | { |
2692 | bool isFriend = false; |
2693 | QString prefix = mTokenizer[mIndex]->text; |
2694 | if (prefix == "friend" ) { |
2695 | isFriend = true; |
2696 | mIndex++; |
2697 | } |
2698 | // Check if were dealing with a struct or union |
2699 | prefix = mTokenizer[mIndex]->text; |
2700 | bool isStruct = ("struct" == prefix) || ("union" ==prefix); |
2701 | int startLine = mTokenizer[mIndex]->line; |
2702 | |
2703 | mIndex++; //skip struct/class/union |
2704 | |
2705 | if (mIndex>=mTokenizer.tokenCount()) |
2706 | return; |
2707 | |
2708 | // Do not modifiy index initially |
2709 | int i = mIndex; |
2710 | |
2711 | // Skip until the struct body starts |
2712 | while ((i < mTokenizer.tokenCount()) && ! ( |
2713 | mTokenizer[i]->text.front() ==';' |
2714 | || mTokenizer[i]->text.front() =='{')) |
2715 | i++; |
2716 | |
2717 | // Forward class/struct decl *or* typedef, e.g. typedef struct some_struct synonym1, synonym2; |
2718 | if ((i < mTokenizer.tokenCount()) && (mTokenizer[i]->text.front() == ';')) { |
2719 | // typdef struct Foo Bar |
2720 | if (isTypedef) { |
2721 | QString oldType = mTokenizer[mIndex]->text; |
2722 | while(true) { |
2723 | // Add definition statement for the synonym |
2724 | if ((mIndex + 1 < mTokenizer.tokenCount()) |
2725 | && (mTokenizer[mIndex + 1]->text.front()==',' |
2726 | || mTokenizer[mIndex + 1]->text.front()==';')) { |
2727 | QString newType = mTokenizer[mIndex]->text; |
2728 | addStatement( |
2729 | getCurrentScope(), |
2730 | mCurrentFile, |
2731 | oldType, |
2732 | newType, |
2733 | "" , |
2734 | "" , |
2735 | startLine, |
2736 | StatementKind::skTypedef, |
2737 | getScope(), |
2738 | mClassScope, |
2739 | true, |
2740 | false); |
2741 | } |
2742 | mIndex++; |
2743 | if (mIndex >= mTokenizer.tokenCount()) |
2744 | break; |
2745 | if (mTokenizer[mIndex]->text.front() == ';') |
2746 | break; |
2747 | } |
2748 | } else { |
2749 | if (isFriend) { // friend class |
2750 | PStatement parentStatement = getCurrentScope(); |
2751 | if (parentStatement) { |
2752 | parentStatement->friends.insert(mTokenizer[mIndex]->text); |
2753 | } |
2754 | } else { |
2755 | // todo: Forward declaration, struct Foo. Don't mention in class browser |
2756 | } |
2757 | i++; // step over ; |
2758 | mIndex = i; |
2759 | } |
2760 | |
2761 | // normal class/struct decl |
2762 | } else { |
2763 | PStatement firstSynonym; |
2764 | // Add class/struct name BEFORE opening brace |
2765 | if (mTokenizer[mIndex]->text.front() != '{') { |
2766 | while(true) { |
2767 | if ((mIndex + 1 < mTokenizer.tokenCount()) |
2768 | && (mTokenizer[mIndex + 1]->text.front() == ',' |
2769 | || mTokenizer[mIndex + 1]->text.front() == ';' |
2770 | || mTokenizer[mIndex + 1]->text.front() == '{' |
2771 | || mTokenizer[mIndex + 1]->text.front() == ':')) { |
2772 | QString command = mTokenizer[mIndex]->text; |
2773 | if (!command.isEmpty()) { |
2774 | firstSynonym = addStatement( |
2775 | getCurrentScope(), |
2776 | mCurrentFile, |
2777 | prefix, // type |
2778 | command, // command |
2779 | "" , // args |
2780 | "" , // values |
2781 | startLine, |
2782 | StatementKind::skClass, |
2783 | getScope(), |
2784 | mClassScope, |
2785 | true, |
2786 | false); |
2787 | command = "" ; |
2788 | } |
2789 | mIndex++; |
2790 | } else if ((mIndex + 2 < mTokenizer.tokenCount()) |
2791 | && (mTokenizer[mIndex + 1]->text == "final" ) |
2792 | && (mTokenizer[mIndex + 2]->text.front()==',' |
2793 | || mTokenizer[mIndex + 2]->text.front()==':' |
2794 | || isblockChar(mTokenizer[mIndex + 2]->text.front()))) { |
2795 | QString command = mTokenizer[mIndex]->text; |
2796 | if (!command.isEmpty()) { |
2797 | firstSynonym = addStatement( |
2798 | getCurrentScope(), |
2799 | mCurrentFile, |
2800 | prefix, // type |
2801 | command, // command |
2802 | "" , // args |
2803 | "" , // values |
2804 | startLine, |
2805 | StatementKind::skClass, |
2806 | getScope(), |
2807 | mClassScope, |
2808 | true, |
2809 | false); |
2810 | command="" ; |
2811 | } |
2812 | mIndex+=2; |
2813 | } else |
2814 | mIndex++; |
2815 | if (mIndex >= mTokenizer.tokenCount()) |
2816 | break; |
2817 | if (mTokenizer[mIndex]->text.front() == ':' |
2818 | || mTokenizer[mIndex]->text.front() == '{' |
2819 | || mTokenizer[mIndex]->text.front() == ';') |
2820 | break; |
2821 | } |
2822 | } |
2823 | |
2824 | // Walk to opening brace if we encountered inheritance statements |
2825 | if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == ':')) { |
2826 | if (firstSynonym) |
2827 | setInheritance(mIndex, firstSynonym, isStruct); // set the _InheritanceList value |
2828 | while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() != '{')) |
2829 | mIndex++; // skip decl after ':' |
2830 | } |
2831 | |
2832 | // Check for struct synonyms after close brace |
2833 | if (isStruct) { |
2834 | |
2835 | // Walk to closing brace |
2836 | i = skipBraces(mIndex); // step onto closing brace |
2837 | |
2838 | if ((i + 1 < mTokenizer.tokenCount()) && !( |
2839 | mTokenizer[i + 1]->text.front() == ';' |
2840 | || mTokenizer[i + 1]->text.front() == '}')) { |
2841 | // When encountering names again after struct body scanning, skip it |
2842 | mSkipList.append(i+1); // add first name to skip statement so that we can skip it until the next ; |
2843 | QString command = "" ; |
2844 | QString args = "" ; |
2845 | |
2846 | // Add synonym before opening brace |
2847 | while(true) { |
2848 | i++; |
2849 | |
2850 | if (!(mTokenizer[i]->text.front() == '{' |
2851 | || mTokenizer[i]->text.front() == ',' |
2852 | || mTokenizer[i]->text.front() == ';')) { |
2853 | // if ((mTokenizer[i]->text.front() == '_') |
2854 | // && (mTokenizer[i]->text.back() == '_')) { |
2855 | // // skip possible gcc attributes |
2856 | // // start and end with 2 underscores (i.e. __attribute__) |
2857 | // // so, to avoid slow checks of strings, we just check the first and last letter of the token |
2858 | // // if both are underscores, we split |
2859 | // break; |
2860 | // } else { |
2861 | if (mTokenizer[i]->text.endsWith(']')) { // cut-off array brackets |
2862 | int pos = mTokenizer[i]->text.indexOf('['); |
2863 | command += mTokenizer[i]->text.mid(0,pos) + ' '; |
2864 | args = mTokenizer[i]->text.mid(pos); |
2865 | } else if (mTokenizer[i]->text.front() == '*' |
2866 | || mTokenizer[i]->text.front() == '&') { // do not add spaces after pointer operator |
2867 | command += mTokenizer[i]->text; |
2868 | } else { |
2869 | command += mTokenizer[i]->text + ' '; |
2870 | } |
2871 | // } |
2872 | } else { |
2873 | command = command.trimmed(); |
2874 | if (!command.isEmpty() && |
2875 | ( !firstSynonym |
2876 | || command!=firstSynonym->command )) { |
2877 | //not define the struct yet, we define a unamed struct |
2878 | if (!firstSynonym) { |
2879 | firstSynonym = addStatement( |
2880 | getCurrentScope(), |
2881 | mCurrentFile, |
2882 | prefix, |
2883 | "__" +command, |
2884 | "" , |
2885 | "" , |
2886 | startLine, |
2887 | StatementKind::skClass, |
2888 | getScope(), |
2889 | mClassScope, |
2890 | true, |
2891 | false); |
2892 | } |
2893 | if (isTypedef) { |
2894 | //typedef |
2895 | addStatement( |
2896 | getCurrentScope(), |
2897 | mCurrentFile, |
2898 | firstSynonym->command, |
2899 | command, |
2900 | "" , |
2901 | "" , |
2902 | mTokenizer[mIndex]->line, |
2903 | StatementKind::skTypedef, |
2904 | getScope(), |
2905 | mClassScope, |
2906 | true, |
2907 | false); // typedef |
2908 | } else { |
2909 | //variable define |
2910 | addStatement( |
2911 | getCurrentScope(), |
2912 | mCurrentFile, |
2913 | firstSynonym->command, |
2914 | command, |
2915 | args, |
2916 | "" , |
2917 | mTokenizer[i]->line, |
2918 | StatementKind::skVariable, |
2919 | getScope(), |
2920 | mClassScope, |
2921 | true, |
2922 | false); // TODO: not supported to pass list |
2923 | } |
2924 | } |
2925 | command = "" ; |
2926 | } |
2927 | if (i >= mTokenizer.tokenCount() - 1) |
2928 | break; |
2929 | if (mTokenizer[i]->text.front()=='{' |
2930 | || mTokenizer[i]->text.front()== ';') |
2931 | break; |
2932 | } |
2933 | |
2934 | // Nothing worth mentioning after closing brace |
2935 | // Proceed to set first synonym as current class |
2936 | } |
2937 | } |
2938 | if (!firstSynonym) { |
2939 | //anonymous union/struct/class, add ast a block |
2940 | firstSynonym=addStatement( |
2941 | getCurrentScope(), |
2942 | mCurrentFile, |
2943 | "" , |
2944 | "" , |
2945 | "" , |
2946 | "" , |
2947 | startLine, |
2948 | StatementKind::skBlock, |
2949 | getScope(), |
2950 | mClassScope, |
2951 | true, |
2952 | false); |
2953 | } |
2954 | addSoloScopeLevel(firstSynonym,startLine); |
2955 | |
2956 | // Step over { |
2957 | if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == '{')) |
2958 | mIndex++; |
2959 | } |
2960 | } |
2961 | |
2962 | void CppParser::handleUsing() |
2963 | { |
2964 | int startLine = mTokenizer[mIndex]->line; |
2965 | if (mCurrentFile.isEmpty()) { |
2966 | //skip to ; |
2967 | while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text!=';')) |
2968 | mIndex++; |
2969 | mIndex++; //skip ; |
2970 | return; |
2971 | } |
2972 | |
2973 | mIndex++; //skip 'using' |
2974 | |
2975 | //handle things like 'using vec = std::vector; ' |
2976 | if (mIndex+1 < mTokenizer.tokenCount() |
2977 | && mTokenizer[mIndex+1]->text == "=" ) { |
2978 | QString fullName = mTokenizer[mIndex]->text; |
2979 | QString aliasName; |
2980 | mIndex+=2; |
2981 | while (mIndex<mTokenizer.tokenCount() && |
2982 | mTokenizer[mIndex]->text!=';') { |
2983 | aliasName += mTokenizer[mIndex]->text; |
2984 | mIndex++; |
2985 | } |
2986 | addStatement( |
2987 | getCurrentScope(), |
2988 | mCurrentFile, |
2989 | aliasName, // name of the alias (type) |
2990 | fullName, // command |
2991 | "" , // args |
2992 | "" , // values |
2993 | startLine, |
2994 | StatementKind::skTypedef, |
2995 | getScope(), |
2996 | mClassScope, |
2997 | true, |
2998 | false); |
2999 | // skip ; |
3000 | mIndex++; |
3001 | return; |
3002 | } |
3003 | //handle things like 'using std::vector;' |
3004 | if ((mIndex+2>=mTokenizer.tokenCount()) |
3005 | || (mTokenizer[mIndex]->text != "namespace" )) { |
3006 | int i= mTokenizer[mIndex]->text.lastIndexOf("::" ); |
3007 | if (i>=0) { |
3008 | QString fullName = mTokenizer[mIndex]->text; |
3009 | QString usingName = fullName.mid(i+2); |
3010 | addStatement( |
3011 | getCurrentScope(), |
3012 | mCurrentFile, |
3013 | fullName, // name of the alias (type) |
3014 | usingName, // command |
3015 | "" , // args |
3016 | "" , // values |
3017 | startLine, |
3018 | StatementKind::skAlias, |
3019 | getScope(), |
3020 | mClassScope, |
3021 | true, |
3022 | false); |
3023 | } |
3024 | //skip to ; |
3025 | while ((mIndex<mTokenizer.tokenCount()) && |
3026 | (mTokenizer[mIndex]->text!=";" )) |
3027 | mIndex++; |
3028 | mIndex++; //and skip it |
3029 | return; |
3030 | } |
3031 | mIndex++; // skip 'namespace' |
3032 | PStatement scopeStatement = getCurrentScope(); |
3033 | |
3034 | QString usingName = mTokenizer[mIndex]->text; |
3035 | mIndex++; |
3036 | |
3037 | if (scopeStatement) { |
3038 | QString fullName = scopeStatement->fullName + "::" + usingName; |
3039 | if (!mNamespaces.contains(fullName)) { |
3040 | fullName = usingName; |
3041 | } |
3042 | if (mNamespaces.contains(fullName)) { |
3043 | scopeStatement->usingList.insert(fullName); |
3044 | } |
3045 | } else { |
3046 | PFileIncludes fileInfo = mPreprocessor.includesList().value(mCurrentFile); |
3047 | if (!fileInfo) |
3048 | return; |
3049 | if (mNamespaces.contains(usingName)) { |
3050 | fileInfo->usings.insert(usingName); |
3051 | } |
3052 | } |
3053 | } |
3054 | |
3055 | void CppParser::handleVar() |
3056 | { |
3057 | // Keep going and stop on top of the variable name |
3058 | QString lastType = "" ; |
3059 | bool isFunctionPointer = false; |
3060 | bool isExtern = false; |
3061 | bool isStatic = false; |
3062 | bool varAdded = false; |
3063 | while (true) { |
3064 | if ((mIndex + 2 < mTokenizer.tokenCount()) |
3065 | && (mTokenizer[mIndex + 1]->text.front() == '(') |
3066 | && (mTokenizer[mIndex + 2]->text.front() == '(')) { |
3067 | isFunctionPointer = mTokenizer[mIndex + 1]->text.indexOf('*') >= 0; |
3068 | if (!isFunctionPointer) |
3069 | break; // inline constructor |
3070 | } else if ((mIndex + 1 < mTokenizer.tokenCount()) |
3071 | && (mTokenizer[mIndex + 1]->text.front()=='(' |
3072 | || mTokenizer[mIndex + 1]->text.front()==',' |
3073 | || mTokenizer[mIndex + 1]->text.front()==';' |
3074 | || mTokenizer[mIndex + 1]->text.front()==':' |
3075 | || mTokenizer[mIndex + 1]->text.front()=='}' |
3076 | || mTokenizer[mIndex + 1]->text.front()=='#' |
3077 | || mTokenizer[mIndex + 1]->text.front()=='{')) { |
3078 | break; |
3079 | } |
3080 | |
3081 | |
3082 | // we've made a mistake, this is a typedef , not a variable definition. |
3083 | if (mTokenizer[mIndex]->text == "typedef" ) |
3084 | return; |
3085 | |
3086 | // struct/class/union is part of the type signature |
3087 | // but we dont store it in the type cache, so must trim it to find the type info |
3088 | if (mTokenizer[mIndex]->text!="struct" |
3089 | && mTokenizer[mIndex]->text!="class" |
3090 | && mTokenizer[mIndex]->text!="union" ) { |
3091 | if (mTokenizer[mIndex]->text == ':') { |
3092 | lastType += ':'; |
3093 | } else { |
3094 | QString s=expandMacroType(mTokenizer[mIndex]->text); |
3095 | if (s == "extern" ) { |
3096 | isExtern = true; |
3097 | } else { |
3098 | if (!s.isEmpty()) |
3099 | lastType += ' '+s; |
3100 | if (s == "static" ) |
3101 | isStatic = true; |
3102 | } |
3103 | } |
3104 | } |
3105 | mIndex++; |
3106 | if(mIndex >= mTokenizer.tokenCount()) |
3107 | break; |
3108 | if (isFunctionPointer) |
3109 | break; |
3110 | } |
3111 | lastType = lastType.trimmed(); |
3112 | |
3113 | // Don't bother entering the scanning loop when we have failed |
3114 | if (mIndex >= mTokenizer.tokenCount()) |
3115 | return; |
3116 | |
3117 | // Find the variable name |
3118 | while (true) { |
3119 | |
3120 | // Skip bit identifiers, |
3121 | // e.g.: |
3122 | // handle |
3123 | // unsigned short bAppReturnCode:8,reserved:6,fBusy:1,fAck:1 |
3124 | // as |
3125 | // unsigned short bAppReturnCode,reserved,fBusy,fAck |
3126 | if ( (mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == ':')) { |
3127 | while ( (mIndex < mTokenizer.tokenCount()) |
3128 | && !( |
3129 | mTokenizer[mIndex]->text.front() == ',' |
3130 | || isblockChar(';') |
3131 | )) |
3132 | mIndex++; |
3133 | } |
3134 | |
3135 | // Skip inline constructors, |
3136 | // e.g.: |
3137 | // handle |
3138 | // int a(3) |
3139 | // as |
3140 | // int a |
3141 | if (!isFunctionPointer && |
3142 | mIndex < mTokenizer.tokenCount() && |
3143 | mTokenizer[mIndex]->text.front() == '(') { |
3144 | while ((mIndex < mTokenizer.tokenCount()) |
3145 | && !( |
3146 | mTokenizer[mIndex]->text.front() == ',' |
3147 | || isblockChar(mTokenizer[mIndex]->text.front()) |
3148 | )) |
3149 | mIndex++; |
3150 | } |
3151 | |
3152 | // Did we stop on top of the variable name? |
3153 | if (mIndex < mTokenizer.tokenCount()) { |
3154 | if (mTokenizer[mIndex]->text.front()!=',' |
3155 | && mTokenizer[mIndex]->text.front()!=';') { |
3156 | QString cmd; |
3157 | QString args; |
3158 | if (isFunctionPointer && (mIndex + 1 < mTokenizer.tokenCount())) { |
3159 | QString s = mTokenizer[mIndex]->text; |
3160 | cmd = s.mid(2,s.length()-3).trimmed(); // (*foo) -> foo |
3161 | args = mTokenizer[mIndex + 1]->text; // (int a,int b) |
3162 | lastType += "(*)" + args; // void(int a,int b) |
3163 | mIndex++; |
3164 | } else if (mTokenizer[mIndex]->text.back() == ']') { //array; break args |
3165 | int pos = mTokenizer[mIndex]->text.indexOf('['); |
3166 | cmd = mTokenizer[mIndex]->text.mid(0,pos); |
3167 | args = mTokenizer[mIndex]->text.mid(pos); |
3168 | } else { |
3169 | cmd = mTokenizer[mIndex]->text; |
3170 | args = "" ; |
3171 | } |
3172 | |
3173 | // Add a statement for every struct we are in |
3174 | if (!lastType.isEmpty()) { |
3175 | addChildStatement( |
3176 | getCurrentScope(), |
3177 | mCurrentFile, |
3178 | lastType, |
3179 | cmd, |
3180 | args, |
3181 | "" , |
3182 | mTokenizer[mIndex]->line, |
3183 | StatementKind::skVariable, |
3184 | getScope(), |
3185 | mClassScope, |
3186 | //True, |
3187 | !isExtern, |
3188 | isStatic); // TODO: not supported to pass list |
3189 | varAdded = true; |
3190 | } |
3191 | } |
3192 | |
3193 | // Step over the variable name |
3194 | if (isblockChar(mTokenizer[mIndex]->text.front())) { |
3195 | break; |
3196 | } |
3197 | mIndex++; |
3198 | } |
3199 | if (mIndex >= mTokenizer.tokenCount()) |
3200 | break; |
3201 | if (isblockChar(mTokenizer[mIndex]->text.front())) |
3202 | break; |
3203 | } |
3204 | if (varAdded && (mIndex < mTokenizer.tokenCount()) |
3205 | && (mTokenizer[mIndex]->text == '{')) { |
3206 | // skip { } like A x {new A}; |
3207 | int i=skipBraces(mIndex); |
3208 | if (i!=mIndex) |
3209 | mIndex = i+1; |
3210 | } |
3211 | // Skip ; and , |
3212 | if ( (mIndex < mTokenizer.tokenCount()) && |
3213 | (mTokenizer[mIndex]->text.front() == ';' |
3214 | || mTokenizer[mIndex]->text.front() == ',')) |
3215 | mIndex++; |
3216 | } |
3217 | |
3218 | void CppParser::internalParse(const QString &fileName) |
3219 | { |
3220 | // Perform some validation before we start |
3221 | if (!mEnabled) |
3222 | return; |
3223 | // if (!isCfile(fileName) && !isHfile(fileName)) // support only known C/C++ files |
3224 | // return; |
3225 | |
3226 | QStringList buffer; |
3227 | if (mOnGetFileStream) { |
3228 | mOnGetFileStream(fileName,buffer); |
3229 | } |
3230 | |
3231 | // Preprocess the file... |
3232 | { |
3233 | auto action = finally([this]{ |
3234 | mPreprocessor.reset(); |
3235 | mTokenizer.reset(); |
3236 | }); |
3237 | // Let the preprocessor augment the include records |
3238 | // mPreprocessor.setIncludesList(mIncludesList); |
3239 | // mPreprocessor.setScannedFileList(mScannedFiles); |
3240 | // mPreprocessor.setIncludePaths(mIncludePaths); |
3241 | // mPreprocessor.setProjectIncludePaths(mProjectIncludePaths); |
3242 | mPreprocessor.setScanOptions(mParseGlobalHeaders, mParseLocalHeaders); |
3243 | mPreprocessor.preprocess(fileName, buffer); |
3244 | |
3245 | QStringList preprocessResult = mPreprocessor.result(); |
3246 | //reduce memory usage |
3247 | mPreprocessor.clearResult(); |
3248 | #ifdef QT_DEBUG |
3249 | // stringsToFile(mPreprocessor.result(),"r:\\preprocess.txt"); |
3250 | // mPreprocessor.dumpDefinesTo("r:\\defines.txt"); |
3251 | // mPreprocessor.dumpIncludesListTo("r:\\includes.txt"); |
3252 | #endif |
3253 | |
3254 | // Tokenize the preprocessed buffer file |
3255 | mTokenizer.tokenize(preprocessResult); |
3256 | //reduce memory usage |
3257 | preprocessResult.clear(); |
3258 | if (mTokenizer.tokenCount() == 0) |
3259 | return; |
3260 | |
3261 | // Process the token list |
3262 | internalClear(); |
3263 | while(true) { |
3264 | if (!handleStatement()) |
3265 | break; |
3266 | } |
3267 | //reduce memory usage |
3268 | internalClear(); |
3269 | #ifdef QT_DEBUG |
3270 | // mTokenizer.dumpTokens("r:\\tokens.txt"); |
3271 | // |
3272 | // mStatementList.dumpAll("r:\\all-stats.txt"); |
3273 | #endif |
3274 | //reduce memory usage |
3275 | mTokenizer.reset(); |
3276 | } |
3277 | } |
3278 | |
3279 | void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct, |
3280 | const PStatement& base, StatementClassScope access) |
3281 | { |
3282 | PFileIncludes fileIncludes1=mPreprocessor.includesList().value(derived->fileName); |
3283 | PFileIncludes fileIncludes2=mPreprocessor.includesList().value(base->fileName); |
3284 | if (fileIncludes1 && fileIncludes2) { |
3285 | //derived class depeneds on base class |
3286 | fileIncludes1->dependingFiles.insert(base->fileName); |
3287 | fileIncludes2->dependedFiles.insert(derived->fileName); |
3288 | } |
3289 | //differentiate class and struct |
3290 | if (access == StatementClassScope::scsNone) { |
3291 | if (isStruct) |
3292 | access = StatementClassScope::scsPublic; |
3293 | else |
3294 | access = StatementClassScope::scsPrivate; |
3295 | } |
3296 | foreach (const PStatement& statement, base->children) { |
3297 | if (statement->classScope == StatementClassScope::scsPrivate |
3298 | || statement->kind == StatementKind::skConstructor |
3299 | || statement->kind == StatementKind::skDestructor) |
3300 | continue; |
3301 | StatementClassScope m_acc; |
3302 | switch(access) { |
3303 | case StatementClassScope::scsPublic: |
3304 | m_acc = statement->classScope; |
3305 | break; |
3306 | case StatementClassScope::scsProtected: |
3307 | m_acc = StatementClassScope::scsProtected; |
3308 | break; |
3309 | case StatementClassScope::scsPrivate: |
3310 | m_acc = StatementClassScope::scsPrivate; |
3311 | break; |
3312 | default: |
3313 | m_acc = StatementClassScope::scsPrivate; |
3314 | } |
3315 | //inherit |
3316 | addInheritedStatement(derived,statement,m_acc); |
3317 | } |
3318 | } |
3319 | |
3320 | QString CppParser::expandMacroType(const QString &name) |
3321 | { |
3322 | //its done in the preprocessor |
3323 | return name; |
3324 | } |
3325 | |
3326 | void CppParser::fillListOfFunctions(const QString& fileName, int line, |
3327 | const PStatement& statement, |
3328 | const PStatement& scopeStatement, QStringList &list) |
3329 | { |
3330 | StatementMap children = mStatementList.childrenStatements(scopeStatement); |
3331 | for (const PStatement& child:children) { |
3332 | if ((statement->command == child->command) |
3333 | #ifdef Q_OS_WIN |
3334 | || (statement->command +'A' == child->command) |
3335 | || (statement->command +'W' == child->command) |
3336 | #endif |
3337 | ) { |
3338 | if (line < child->line && (child->fileName == fileName)) |
3339 | continue; |
3340 | list.append(prettyPrintStatement(child,child->fileName,child->line)); |
3341 | } |
3342 | } |
3343 | } |
3344 | |
3345 | QList<PStatement> CppParser::getListOfFunctions(const QString &fileName, int line, const PStatement &statement, const PStatement &scopeStatement) |
3346 | { |
3347 | QList<PStatement> result; |
3348 | StatementMap children = mStatementList.childrenStatements(scopeStatement); |
3349 | for (const PStatement& child:children) { |
3350 | if (( (statement->command == child->command) |
3351 | #ifdef Q_OS_WIN |
3352 | || (statement->command +'A' == child->command) |
3353 | || (statement->command +'W' == child->command) |
3354 | #endif |
3355 | ) ) { |
3356 | if (line < child->line && (child->fileName == fileName)) |
3357 | continue; |
3358 | result.append(child); |
3359 | } |
3360 | } |
3361 | return result; |
3362 | } |
3363 | |
3364 | PStatement CppParser::findMemberOfStatement(const QString &phrase, |
3365 | const PStatement& scopeStatement) |
3366 | { |
3367 | const StatementMap& statementMap =mStatementList.childrenStatements(scopeStatement); |
3368 | if (statementMap.isEmpty()) |
3369 | return PStatement(); |
3370 | |
3371 | QString s = phrase; |
3372 | //remove [] |
3373 | int p = phrase.indexOf('['); |
3374 | if (p>=0) |
3375 | s.truncate(p); |
3376 | //remove () |
3377 | p = phrase.indexOf('('); |
3378 | if (p>=0) |
3379 | s.truncate(p); |
3380 | |
3381 | //remove <> |
3382 | p =s.indexOf('<'); |
3383 | if (p>=0) |
3384 | s.truncate(p); |
3385 | |
3386 | return statementMap.value(s,PStatement()); |
3387 | } |
3388 | |
3389 | QList<PStatement> CppParser::findMembersOfStatement(const QString &phrase, const PStatement &scopeStatement) |
3390 | { |
3391 | const StatementMap& statementMap =mStatementList.childrenStatements(scopeStatement); |
3392 | if (statementMap.isEmpty()) |
3393 | return QList<PStatement>(); |
3394 | |
3395 | QString s = phrase; |
3396 | //remove [] |
3397 | int p = phrase.indexOf('['); |
3398 | if (p>=0) |
3399 | s.truncate(p); |
3400 | //remove () |
3401 | p = phrase.indexOf('('); |
3402 | if (p>=0) |
3403 | s.truncate(p); |
3404 | |
3405 | //remove <> |
3406 | p =s.indexOf('<'); |
3407 | if (p>=0) |
3408 | s.truncate(p); |
3409 | |
3410 | return statementMap.values(s); |
3411 | } |
3412 | |
3413 | PStatement CppParser::findStatementInScope(const QString &name, const QString &noNameArgs, |
3414 | StatementKind kind, const PStatement& scope) |
3415 | { |
3416 | if (scope && scope->kind == StatementKind::skNamespace) { |
3417 | PStatementList namespaceStatementsList = findNamespace(scope->command); |
3418 | if (!namespaceStatementsList) |
3419 | return PStatement(); |
3420 | foreach (const PStatement& namespaceStatement, *namespaceStatementsList) { |
3421 | PStatement result=doFindStatementInScope(name,noNameArgs,kind,namespaceStatement); |
3422 | if (result) |
3423 | return result; |
3424 | } |
3425 | } else { |
3426 | return doFindStatementInScope(name,noNameArgs,kind,scope); |
3427 | } |
3428 | return PStatement(); |
3429 | } |
3430 | |
3431 | PStatement CppParser::findStatementInScope(const QString &name, const PStatement &scope) |
3432 | { |
3433 | if (!scope) |
3434 | return findMemberOfStatement(name,scope); |
3435 | if (scope->kind == StatementKind::skNamespace) { |
3436 | return findStatementInNamespace(name, scope->fullName); |
3437 | } else { |
3438 | return findMemberOfStatement(name,scope); |
3439 | } |
3440 | } |
3441 | |
3442 | PStatement CppParser::findStatementInNamespace(const QString &name, const QString &namespaceName) |
3443 | { |
3444 | PStatementList namespaceStatementsList=findNamespace(namespaceName); |
3445 | if (!namespaceStatementsList) |
3446 | return PStatement(); |
3447 | foreach (const PStatement& namespaceStatement,*namespaceStatementsList) { |
3448 | PStatement result = findMemberOfStatement(name,namespaceStatement); |
3449 | if (result) |
3450 | return result; |
3451 | } |
3452 | return PStatement(); |
3453 | } |
3454 | |
3455 | PEvalStatement CppParser::doEvalExpression(const QString& fileName, |
3456 | const QStringList& phraseExpression, |
3457 | int &pos, |
3458 | const PStatement& scope, |
3459 | const PEvalStatement& previousResult, |
3460 | bool freeScoped) |
3461 | { |
3462 | //dummy function to easy later upgrades |
3463 | return doEvalPointerArithmetic(fileName, |
3464 | phraseExpression, |
3465 | pos, |
3466 | scope, |
3467 | previousResult, |
3468 | freeScoped); |
3469 | } |
3470 | |
3471 | PEvalStatement CppParser::doEvalPointerArithmetic(const QString &fileName, const QStringList &phraseExpression, int &pos, const PStatement &scope, const PEvalStatement &previousResult, bool freeScoped) |
3472 | { |
3473 | if (pos>=phraseExpression.length()) |
3474 | return PEvalStatement(); |
3475 | //find the start scope statement |
3476 | PEvalStatement currentResult = doEvalPointerToMembers( |
3477 | fileName, |
3478 | phraseExpression, |
3479 | pos, |
3480 | scope, |
3481 | previousResult, |
3482 | freeScoped); |
3483 | while (pos < phraseExpression.length()) { |
3484 | if (!currentResult) |
3485 | break; |
3486 | if (currentResult && |
3487 | (phraseExpression[pos]=="+" |
3488 | || phraseExpression[pos]=="-" )) { |
3489 | if (currentResult->kind == EvalStatementKind::Variable) { |
3490 | pos++; |
3491 | PEvalStatement op2=doEvalPointerToMembers( |
3492 | fileName, |
3493 | phraseExpression, |
3494 | pos, |
3495 | scope, |
3496 | currentResult, |
3497 | false); |
3498 | //todo operator+/- overload |
3499 | } else if (currentResult->kind == EvalStatementKind::Literal |
3500 | && currentResult->baseType == "int" ) { |
3501 | pos++; |
3502 | PEvalStatement op2=doEvalPointerToMembers( |
3503 | fileName, |
3504 | phraseExpression, |
3505 | pos, |
3506 | scope, |
3507 | currentResult, |
3508 | false); |
3509 | currentResult = op2; |
3510 | } else |
3511 | break; |
3512 | } else |
3513 | break; |
3514 | } |
3515 | // qDebug()<<pos<<"pointer add member end"; |
3516 | return currentResult; |
3517 | |
3518 | } |
3519 | |
3520 | PEvalStatement CppParser::doEvalPointerToMembers( |
3521 | const QString &fileName, |
3522 | const QStringList &phraseExpression, |
3523 | int &pos, |
3524 | const PStatement &scope, |
3525 | const PEvalStatement &previousResult, |
3526 | bool freeScoped) |
3527 | { |
3528 | if (pos>=phraseExpression.length()) |
3529 | return PEvalStatement(); |
3530 | //find the start scope statement |
3531 | PEvalStatement currentResult = doEvalCCast( |
3532 | fileName, |
3533 | phraseExpression, |
3534 | pos, |
3535 | scope, |
3536 | previousResult, |
3537 | freeScoped); |
3538 | while (pos < phraseExpression.length()) { |
3539 | if (!currentResult) |
3540 | break; |
3541 | if (currentResult && |
3542 | (currentResult->kind == EvalStatementKind::Variable) |
3543 | && (phraseExpression[pos]==".*" |
3544 | || phraseExpression[pos]=="->*" )) { |
3545 | pos++; |
3546 | currentResult = |
3547 | doEvalCCast( |
3548 | fileName, |
3549 | phraseExpression, |
3550 | pos, |
3551 | scope, |
3552 | currentResult, |
3553 | false); |
3554 | if (currentResult) { |
3555 | currentResult->pointerLevel++; |
3556 | } |
3557 | } else |
3558 | break; |
3559 | } |
3560 | // qDebug()<<pos<<"pointer member end"; |
3561 | return currentResult; |
3562 | } |
3563 | |
3564 | PEvalStatement CppParser::doEvalCCast(const QString &fileName, |
3565 | const QStringList &phraseExpression, |
3566 | int &pos, |
3567 | const PStatement& scope, |
3568 | const PEvalStatement& previousResult, |
3569 | bool freeScoped) |
3570 | { |
3571 | if (pos>=phraseExpression.length()) |
3572 | return PEvalStatement(); |
3573 | PEvalStatement result; |
3574 | if (phraseExpression[pos]=="*" ) { |
3575 | pos++; //skip "*" |
3576 | result = doEvalCCast( |
3577 | fileName, |
3578 | phraseExpression, |
3579 | pos, |
3580 | scope, |
3581 | previousResult, |
3582 | freeScoped); |
3583 | if (result) { |
3584 | //todo: STL container; |
3585 | if (result->pointerLevel==0) { |
3586 | PStatement typeStatement = result->effectiveTypeStatement; |
3587 | if ((typeStatement) |
3588 | && STLPointers.contains(typeStatement->fullName) |
3589 | && result->kind == EvalStatementKind::Variable |
3590 | && result->baseStatement) { |
3591 | PStatement parentScope = result->baseStatement->parentScope.lock(); |
3592 | QString typeName=findFirstTemplateParamOf(fileName,result->baseStatement->type, parentScope); |
3593 | // qDebug()<<"typeName"<<typeName; |
3594 | typeStatement=findTypeDefinitionOf(fileName, typeName,parentScope); |
3595 | if (typeStatement) { |
3596 | result = doCreateEvalType(fileName,typeStatement); |
3597 | result->kind = EvalStatementKind::Variable; |
3598 | } |
3599 | } |
3600 | } else |
3601 | result->pointerLevel--; |
3602 | } |
3603 | } else if (phraseExpression[pos]=="&" ) { |
3604 | pos++; //skip "&" |
3605 | result = doEvalCCast( |
3606 | fileName, |
3607 | phraseExpression, |
3608 | pos, |
3609 | scope, |
3610 | previousResult, |
3611 | freeScoped); |
3612 | if (result) { |
3613 | result->pointerLevel++; |
3614 | } |
3615 | } else if (phraseExpression[pos]=="++" |
3616 | || phraseExpression[pos]=="--" ) { |
3617 | pos++; //skip "++" or "--" |
3618 | result = doEvalCCast( |
3619 | fileName, |
3620 | phraseExpression, |
3621 | pos, |
3622 | scope, |
3623 | previousResult, |
3624 | freeScoped); |
3625 | } else if (phraseExpression[pos]=="(" ) { |
3626 | //parse |
3627 | int startPos = pos; |
3628 | pos++; |
3629 | // qDebug()<<"parse type cast ()"; |
3630 | PEvalStatement evalType = doEvalExpression( |
3631 | fileName, |
3632 | phraseExpression, |
3633 | pos, |
3634 | scope, |
3635 | PEvalStatement(), |
3636 | true); |
3637 | // qDebug()<<pos; |
3638 | if (pos >= phraseExpression.length() || phraseExpression[pos]!=")" ) { |
3639 | return PEvalStatement(); |
3640 | } else if (evalType && |
3641 | (evalType->kind == EvalStatementKind::Type)) { |
3642 | pos++; // skip ")" |
3643 | // qDebug()<<"parse type cast exp"; |
3644 | //it's a type cast |
3645 | result = doEvalCCast(fileName, |
3646 | phraseExpression, |
3647 | pos, |
3648 | scope, |
3649 | previousResult, |
3650 | freeScoped); |
3651 | if (result) { |
3652 | // qDebug()<<"type cast"; |
3653 | result->assignType(evalType); |
3654 | } |
3655 | } else //it's not a type cast |
3656 | result = doEvalMemberAccess( |
3657 | fileName, |
3658 | phraseExpression, |
3659 | startPos, //we must reparse it |
3660 | scope, |
3661 | previousResult, |
3662 | freeScoped); |
3663 | } else |
3664 | result = doEvalMemberAccess( |
3665 | fileName, |
3666 | phraseExpression, |
3667 | pos, |
3668 | scope, |
3669 | previousResult, |
3670 | freeScoped); |
3671 | // if (result) { |
3672 | // qDebug()<<pos<<(int)result->kind<<result->baseType; |
3673 | // } else { |
3674 | // qDebug()<<"!!!!!!!!!!!not found"; |
3675 | // } |
3676 | return result; |
3677 | } |
3678 | |
3679 | PEvalStatement CppParser::doEvalMemberAccess(const QString &fileName, |
3680 | const QStringList &phraseExpression, |
3681 | int &pos, |
3682 | const PStatement& scope, |
3683 | const PEvalStatement& previousResult, |
3684 | bool freeScoped) |
3685 | |
3686 | { |
3687 | // qDebug()<<"eval member access "<<pos<<phraseExpression; |
3688 | PEvalStatement result; |
3689 | if (pos>=phraseExpression.length()) |
3690 | return result; |
3691 | PEvalStatement lastResult = previousResult; |
3692 | result = doEvalScopeResolution( |
3693 | fileName, |
3694 | phraseExpression, |
3695 | pos, |
3696 | scope, |
3697 | previousResult, |
3698 | freeScoped); |
3699 | if (!result) |
3700 | return PEvalStatement(); |
3701 | while (pos<phraseExpression.length()) { |
3702 | if (!result) |
3703 | break; |
3704 | if (phraseExpression[pos]=="++" || phraseExpression[pos]=="--" ) { |
3705 | pos++; //just skip it |
3706 | } else if (phraseExpression[pos] == "(" ) { |
3707 | if (result->kind == EvalStatementKind::Type) { |
3708 | pos++; // skip "(" |
3709 | PEvalStatement newResult = doEvalExpression( |
3710 | fileName, |
3711 | phraseExpression, |
3712 | pos, |
3713 | scope, |
3714 | PEvalStatement(), |
3715 | true); |
3716 | if (newResult) |
3717 | newResult->assignType(result); |
3718 | pos++; // skip ")" |
3719 | result = newResult; |
3720 | } else if (result->kind == EvalStatementKind::Function) { |
3721 | doSkipInExpression(phraseExpression,pos,"(" ,")" ); |
3722 | // qDebug()<<"????"<<(result->baseStatement!=nullptr)<<(lastResult!=nullptr); |
3723 | if (result->baseStatement && lastResult && lastResult->baseStatement) { |
3724 | PStatement parentScope = result->baseStatement->parentScope.lock(); |
3725 | if (parentScope |
3726 | && STLContainers.contains(parentScope->fullName) |
3727 | && STLElementMethods.contains(result->baseStatement->command) |
3728 | ) { |
3729 | //stl container methods |
3730 | PStatement typeStatement = result->effectiveTypeStatement; |
3731 | QString typeName=findFirstTemplateParamOf(fileName,lastResult->baseStatement->type, parentScope); |
3732 | // qDebug()<<"typeName"<<typeName<<lastResult->baseStatement->type<<lastResult->baseStatement->command; |
3733 | typeStatement=findTypeDefinitionOf(fileName, typeName,parentScope); |
3734 | if (typeStatement) { |
3735 | result = doCreateEvalType(fileName,typeStatement); |
3736 | result->kind = EvalStatementKind::Variable; |
3737 | } |
3738 | } |
3739 | } |
3740 | result->kind = EvalStatementKind::Variable; |
3741 | } else |
3742 | result = PEvalStatement(); |
3743 | } else if (phraseExpression[pos] == "[" ) { |
3744 | //skip to "]" |
3745 | doSkipInExpression(phraseExpression,pos,"[" ,"]" ); |
3746 | if (result->pointerLevel>0) |
3747 | result->pointerLevel--; |
3748 | else { |
3749 | PStatement typeStatement = result->effectiveTypeStatement; |
3750 | if (typeStatement |
3751 | && STLContainers.contains(typeStatement->fullName) |
3752 | && result->kind == EvalStatementKind::Variable |
3753 | && result->baseStatement) { |
3754 | PStatement parentScope = result->baseStatement->parentScope.lock(); |
3755 | QString typeName = findFirstTemplateParamOf(fileName,result->baseStatement->type, |
3756 | parentScope); |
3757 | typeStatement = findTypeDefinitionOf(fileName, typeName, |
3758 | parentScope); |
3759 | if (typeStatement) { |
3760 | result = doCreateEvalType(fileName,typeStatement); |
3761 | result->kind = EvalStatementKind::Variable; |
3762 | } |
3763 | } |
3764 | } |
3765 | } else if (phraseExpression[pos] == "." ) { |
3766 | pos++; |
3767 | lastResult = result; |
3768 | result = doEvalScopeResolution( |
3769 | fileName, |
3770 | phraseExpression, |
3771 | pos, |
3772 | scope, |
3773 | result, |
3774 | false); |
3775 | // qDebug()<<(result!=nullptr)<<pos<<"after ."; |
3776 | } else if (phraseExpression[pos] == "->" ) { |
3777 | pos++; |
3778 | // qDebug()<<"pointer level"<<result->pointerLevel; |
3779 | if (result->pointerLevel==0) { |
3780 | PStatement typeStatement = result->effectiveTypeStatement; |
3781 | if ((typeStatement) |
3782 | && STLPointers.contains(typeStatement->fullName) |
3783 | && result->kind == EvalStatementKind::Variable |
3784 | && result->baseStatement) { |
3785 | PStatement parentScope = result->baseStatement->parentScope.lock(); |
3786 | QString typeName=findFirstTemplateParamOf(fileName,result->baseStatement->type, parentScope); |
3787 | // qDebug()<<"typeName"<<typeName; |
3788 | typeStatement=findTypeDefinitionOf(fileName, typeName,parentScope); |
3789 | if (typeStatement) { |
3790 | result = doCreateEvalType(fileName,typeStatement); |
3791 | result->kind = EvalStatementKind::Variable; |
3792 | } |
3793 | } |
3794 | } else { |
3795 | result->pointerLevel--; |
3796 | } |
3797 | lastResult = result; |
3798 | result = doEvalScopeResolution( |
3799 | fileName, |
3800 | phraseExpression, |
3801 | pos, |
3802 | scope, |
3803 | result, |
3804 | false); |
3805 | } else |
3806 | break; |
3807 | } |
3808 | return result; |
3809 | } |
3810 | |
3811 | PEvalStatement CppParser::doEvalScopeResolution(const QString &fileName, |
3812 | const QStringList &phraseExpression, |
3813 | int &pos, |
3814 | const PStatement& scope, |
3815 | const PEvalStatement& previousResult, |
3816 | bool freeScoped) |
3817 | { |
3818 | // qDebug()<<"eval scope res "<<pos<<phraseExpression; |
3819 | PEvalStatement result; |
3820 | if (pos>=phraseExpression.length()) |
3821 | return result; |
3822 | result = doEvalTerm( |
3823 | fileName, |
3824 | phraseExpression, |
3825 | pos, |
3826 | scope, |
3827 | previousResult, |
3828 | freeScoped); |
3829 | while (pos<phraseExpression.length()) { |
3830 | if (phraseExpression[pos]=="::" ) { |
3831 | pos++; |
3832 | if (!result) { |
3833 | //global |
3834 | result = doEvalTerm(fileName, |
3835 | phraseExpression, |
3836 | pos, |
3837 | PStatement(), |
3838 | PEvalStatement(), |
3839 | false); |
3840 | } else if (result->kind == EvalStatementKind::Type) { |
3841 | //class static member |
3842 | result = doEvalTerm(fileName, |
3843 | phraseExpression, |
3844 | pos, |
3845 | scope, |
3846 | result, |
3847 | false); |
3848 | } else if (result->kind == EvalStatementKind::Namespace) { |
3849 | //namespace |
3850 | result = doEvalTerm(fileName, |
3851 | phraseExpression, |
3852 | pos, |
3853 | scope, |
3854 | result, |
3855 | false); |
3856 | } |
3857 | if (!result) |
3858 | break; |
3859 | } else |
3860 | break; |
3861 | } |
3862 | // qDebug()<<pos<<"scope end"; |
3863 | return result; |
3864 | } |
3865 | |
3866 | PEvalStatement CppParser::doEvalTerm(const QString &fileName, |
3867 | const QStringList &phraseExpression, |
3868 | int &pos, |
3869 | const PStatement& scope, |
3870 | const PEvalStatement& previousResult, |
3871 | bool freeScoped) |
3872 | { |
3873 | // if (previousResult) { |
3874 | // qDebug()<<"eval term "<<pos<<phraseExpression<<previousResult->baseType<<freeScoped; |
3875 | // } else { |
3876 | // qDebug()<<"eval term "<<pos<<phraseExpression<<"no type"<<freeScoped; |
3877 | // } |
3878 | PEvalStatement result; |
3879 | if (pos>=phraseExpression.length()) |
3880 | return result; |
3881 | if (phraseExpression[pos]=="(" ) { |
3882 | pos++; |
3883 | result = doEvalExpression(fileName,phraseExpression,pos,scope,PEvalStatement(),freeScoped); |
3884 | if (pos >= phraseExpression.length() || phraseExpression[pos]!=")" ) |
3885 | return PEvalStatement(); |
3886 | else { |
3887 | pos++; // skip ")"; |
3888 | return result; |
3889 | } |
3890 | } else { |
3891 | int pointerLevel = 0; |
3892 | //skip "struct", "const", "static", etc |
3893 | while(pos < phraseExpression.length()) { |
3894 | QString token = phraseExpression[pos]; |
3895 | if (token=="*" ) // for expression like (const * char)? |
3896 | pointerLevel++; |
3897 | else if (mCppTypeKeywords.contains(token) |
3898 | || !mCppKeywords.contains(token)) |
3899 | break; |
3900 | pos++; |
3901 | } |
3902 | |
3903 | if (pos>=phraseExpression.length() || phraseExpression[pos]==")" ) |
3904 | return result; |
3905 | if (mCppKeywords.contains(phraseExpression[pos])) { |
3906 | result = doCreateEvalType(phraseExpression[pos]); |
3907 | pos++; |
3908 | } else if (isIdentifier(phraseExpression[pos])) { |
3909 | PStatement statement; |
3910 | if (freeScoped) { |
3911 | if (!previousResult) { |
3912 | statement = findStatementStartingFrom( |
3913 | fileName, |
3914 | phraseExpression[pos], |
3915 | scope); |
3916 | } else { |
3917 | statement = findStatementStartingFrom( |
3918 | fileName, |
3919 | phraseExpression[pos], |
3920 | previousResult->effectiveTypeStatement); |
3921 | } |
3922 | } else { |
3923 | if (!previousResult) { |
3924 | statement = findStatementInScope(phraseExpression[pos],PStatement()); |
3925 | } else { |
3926 | // if (previousResult->effectiveTypeStatement) { |
3927 | // qDebug()<<phraseExpression[pos]<<previousResult->effectiveTypeStatement->fullName; |
3928 | // } else { |
3929 | // qDebug()<<phraseExpression[pos]<<"no type"; |
3930 | // } |
3931 | statement = findStatementInScope(phraseExpression[pos],previousResult->effectiveTypeStatement); |
3932 | // if (!statement) { |
3933 | // qDebug()<<"not found!"; |
3934 | // } else { |
3935 | // qDebug()<<statement->fullName; |
3936 | // qDebug()<<statement->kind; |
3937 | // } |
3938 | } |
3939 | } |
3940 | pos++; |
3941 | if (statement && statement->kind == StatementKind::skConstructor) { |
3942 | statement = statement->parentScope.lock(); |
3943 | } |
3944 | if (statement) { |
3945 | switch (statement->kind) { |
3946 | case StatementKind::skNamespace: |
3947 | result = doCreateEvalNamespace(statement); |
3948 | break; |
3949 | case StatementKind::skAlias: { |
3950 | statement = findAliasedStatement(statement); |
3951 | if (statement) |
3952 | result = doCreateEvalNamespace(statement); |
3953 | } |
3954 | break; |
3955 | case StatementKind::skVariable: |
3956 | case StatementKind::skParameter: |
3957 | result = doCreateEvalVariable(fileName,statement); |
3958 | break; |
3959 | case StatementKind::skEnumType: |
3960 | case StatementKind::skClass: |
3961 | case StatementKind::skEnumClassType: |
3962 | case StatementKind::skTypedef: |
3963 | result = doCreateEvalType(fileName,statement); |
3964 | break; |
3965 | case StatementKind::skFunction: |
3966 | result = doCreateEvalFunction(fileName,statement); |
3967 | break; |
3968 | default: |
3969 | result = PEvalStatement(); |
3970 | } |
3971 | } |
3972 | } else if (isIntegerLiteral(phraseExpression[pos])) { |
3973 | result = doCreateEvalLiteral("int" ); |
3974 | pos++; |
3975 | } else if (isFloatLiteral(phraseExpression[pos])) { |
3976 | result = doCreateEvalLiteral("double" ); |
3977 | pos++; |
3978 | } else if (isStringLiteral(phraseExpression[pos])) { |
3979 | result = doCreateEvalLiteral("char" ); |
3980 | result->pointerLevel = 1; |
3981 | pos++; |
3982 | } else if (isCharLiteral(phraseExpression[pos])) { |
3983 | result = doCreateEvalLiteral("char" ); |
3984 | pos++; |
3985 | } else |
3986 | return result; |
3987 | // if (result) { |
3988 | // qDebug()<<"term kind:"<<(int)result->kind; |
3989 | // } |
3990 | if (result && result->kind == EvalStatementKind::Type) { |
3991 | //skip "struct", "const", "static", etc |
3992 | while(pos < phraseExpression.length()) { |
3993 | QString token = phraseExpression[pos]; |
3994 | if (token=="*" ) // for expression like (const * char)? |
3995 | pointerLevel++; |
3996 | else if (mCppTypeKeywords.contains(token) |
3997 | || !mCppKeywords.contains(token)) |
3998 | break; |
3999 | pos++; |
4000 | } |
4001 | result->pointerLevel = pointerLevel; |
4002 | } |
4003 | } |
4004 | // qDebug()<<pos<<" term end"; |
4005 | // if (!result) { |
4006 | // qDebug()<<"not found !!!!"; |
4007 | // } |
4008 | return result; |
4009 | } |
4010 | |
4011 | PEvalStatement CppParser::doCreateEvalNamespace(const PStatement &namespaceStatement) |
4012 | { |
4013 | if (!namespaceStatement) |
4014 | return PEvalStatement(); |
4015 | return std::make_shared<EvalStatement>( |
4016 | namespaceStatement->fullName, |
4017 | EvalStatementKind::Namespace, |
4018 | PStatement(), |
4019 | namespaceStatement); |
4020 | } |
4021 | |
4022 | PEvalStatement CppParser::doCreateEvalType(const QString& fileName,const PStatement &typeStatement) |
4023 | { |
4024 | if (!typeStatement) |
4025 | return PEvalStatement(); |
4026 | if (typeStatement->kind == StatementKind::skTypedef) { |
4027 | QString baseType; |
4028 | int pointerLevel=0; |
4029 | PStatement statement = doParseEvalTypeInfo( |
4030 | fileName, |
4031 | typeStatement->parentScope.lock(), |
4032 | typeStatement->type + typeStatement->args, |
4033 | baseType, |
4034 | pointerLevel); |
4035 | return std::make_shared<EvalStatement>( |
4036 | baseType, |
4037 | EvalStatementKind::Type, |
4038 | PStatement(), |
4039 | typeStatement, |
4040 | pointerLevel |
4041 | ); |
4042 | } else { |
4043 | return std::make_shared<EvalStatement>( |
4044 | typeStatement->fullName, |
4045 | EvalStatementKind::Type, |
4046 | PStatement(), |
4047 | typeStatement); |
4048 | } |
4049 | } |
4050 | |
4051 | PEvalStatement CppParser::doCreateEvalType(const QString &primitiveType) |
4052 | { |
4053 | return std::make_shared<EvalStatement>( |
4054 | primitiveType, |
4055 | EvalStatementKind::Type, |
4056 | PStatement(), |
4057 | PStatement()); |
4058 | } |
4059 | |
4060 | PEvalStatement CppParser::doCreateEvalVariable(const QString &fileName, PStatement varStatement) |
4061 | { |
4062 | if (!varStatement) |
4063 | return PEvalStatement(); |
4064 | QString baseType; |
4065 | int pointerLevel=0; |
4066 | PStatement typeStatement = doParseEvalTypeInfo( |
4067 | fileName, |
4068 | varStatement->parentScope.lock(), |
4069 | varStatement->type+ varStatement->args, |
4070 | baseType, |
4071 | pointerLevel); |
4072 | // qDebug()<<"parse ..."<<baseType<<pointerLevel; |
4073 | return std::make_shared<EvalStatement>( |
4074 | baseType, |
4075 | EvalStatementKind::Variable, |
4076 | varStatement, |
4077 | typeStatement, |
4078 | pointerLevel |
4079 | ); |
4080 | } |
4081 | |
4082 | PEvalStatement CppParser::doCreateEvalFunction(const QString &fileName, PStatement funcStatement) |
4083 | { |
4084 | if (!funcStatement) |
4085 | return PEvalStatement(); |
4086 | QString baseType; |
4087 | int pointerLevel=0; |
4088 | PStatement typeStatement = doParseEvalTypeInfo( |
4089 | fileName, |
4090 | funcStatement->parentScope.lock(), |
4091 | funcStatement->type, |
4092 | baseType, |
4093 | pointerLevel); |
4094 | return std::make_shared<EvalStatement>( |
4095 | baseType, |
4096 | EvalStatementKind::Function, |
4097 | funcStatement, |
4098 | typeStatement, |
4099 | pointerLevel |
4100 | ); |
4101 | } |
4102 | |
4103 | PEvalStatement CppParser::doCreateEvalLiteral(const QString &type) |
4104 | { |
4105 | return std::make_shared<EvalStatement>( |
4106 | type, |
4107 | EvalStatementKind::Literal, |
4108 | PStatement(), |
4109 | PStatement()); |
4110 | } |
4111 | |
4112 | void CppParser::doSkipInExpression(const QStringList &expression, int &pos, const QString &startSymbol, const QString &endSymbol) |
4113 | { |
4114 | int level = 0; |
4115 | while (pos<expression.length()) { |
4116 | QString token = expression[pos]; |
4117 | if (token == startSymbol) { |
4118 | level++; |
4119 | } else if (token == endSymbol) { |
4120 | level--; |
4121 | if (level==0) { |
4122 | pos++; |
4123 | return; |
4124 | } |
4125 | } |
4126 | pos++; |
4127 | } |
4128 | } |
4129 | |
4130 | PStatement CppParser::doParseEvalTypeInfo( |
4131 | const QString &fileName, |
4132 | const PStatement &scope, |
4133 | const QString &type, |
4134 | QString &baseType, |
4135 | int &pointerLevel) |
4136 | { |
4137 | // Remove pointer stuff from type |
4138 | QString s = type; |
4139 | // qDebug()<<"eval type info"<<type; |
4140 | int position = s.length()-1; |
4141 | QSynedit::CppHighlighter highlighter; |
4142 | highlighter.resetState(); |
4143 | highlighter.setLine(type,0); |
4144 | int bracketLevel = 0; |
4145 | int templateLevel = 0; |
4146 | while(!highlighter.eol()) { |
4147 | QString token = highlighter.getToken(); |
4148 | if (bracketLevel == 0 && templateLevel ==0) { |
4149 | if (token == "*" ) |
4150 | pointerLevel++; |
4151 | else if (token == "&" ) |
4152 | pointerLevel--; |
4153 | else if (highlighter.getTokenAttribute() == highlighter.identifierAttribute()) { |
4154 | if (token!= "const" ) |
4155 | baseType += token; |
4156 | } else if (token == "[" ) { |
4157 | pointerLevel++; |
4158 | bracketLevel++; |
4159 | } else if (token == "<" ) { |
4160 | templateLevel++; |
4161 | } else if (token == "::" ) { |
4162 | baseType += token; |
4163 | } |
4164 | } else if (bracketLevel > 0) { |
4165 | if (token == "[" ) { |
4166 | bracketLevel++; |
4167 | } else if (token == "]" ) { |
4168 | bracketLevel--; |
4169 | } |
4170 | } else if (templateLevel > 0) { |
4171 | if (token == "<" ) { |
4172 | templateLevel++; |
4173 | } else if (token == ">" ) { |
4174 | templateLevel--; |
4175 | } |
4176 | } |
4177 | highlighter.next(); |
4178 | } |
4179 | while ((position >= 0) && (s[position] == '*' |
4180 | || s[position] == ' ' |
4181 | || s[position] == '&')) { |
4182 | if (s[position]=='*') { |
4183 | |
4184 | } |
4185 | position--; |
4186 | } |
4187 | PStatement statement = findStatementOf(fileName,baseType,scope); |
4188 | return getTypeDef(statement,fileName,baseType); |
4189 | |
4190 | } |
4191 | |
4192 | int CppParser::getBracketEnd(const QString &s, int startAt) |
4193 | { |
4194 | int i = startAt; |
4195 | int level = 0; // assume we start on top of [ |
4196 | while (i < s.length()) { |
4197 | switch(s[i].unicode()) { |
4198 | case '<': |
4199 | level++; |
4200 | break; |
4201 | case '>': |
4202 | level--; |
4203 | if (level == 0) |
4204 | return i; |
4205 | } |
4206 | i++; |
4207 | } |
4208 | return startAt; |
4209 | } |
4210 | |
4211 | PStatement CppParser::doFindStatementInScope(const QString &name, |
4212 | const QString &noNameArgs, |
4213 | StatementKind kind, |
4214 | const PStatement& scope) |
4215 | { |
4216 | const StatementMap& statementMap =mStatementList.childrenStatements(scope); |
4217 | |
4218 | foreach (const PStatement& statement, statementMap.values(name)) { |
4219 | if (statement->kind == kind && statement->noNameArgs == noNameArgs) { |
4220 | return statement; |
4221 | } |
4222 | } |
4223 | return PStatement(); |
4224 | } |
4225 | |
4226 | void CppParser::internalInvalidateFile(const QString &fileName) |
4227 | { |
4228 | if (fileName.isEmpty()) |
4229 | return; |
4230 | |
4231 | //remove all statements in the file |
4232 | const QList<QString>& keys=mNamespaces.keys(); |
4233 | for (const QString& key:keys) { |
4234 | PStatementList statements = mNamespaces.value(key); |
4235 | for (int i=statements->size()-1;i>=0;i--) { |
4236 | PStatement statement = statements->at(i); |
4237 | if (statement->fileName == fileName |
4238 | || statement->definitionFileName == fileName) { |
4239 | statements->removeAt(i); |
4240 | } |
4241 | } |
4242 | if (statements->isEmpty()) { |
4243 | mNamespaces.remove(key); |
4244 | } |
4245 | } |
4246 | // delete it from scannedfiles |
4247 | mPreprocessor.scannedFiles().remove(fileName); |
4248 | |
4249 | // remove its include files list |
4250 | PFileIncludes p = findFileIncludes(fileName, true); |
4251 | if (p) { |
4252 | //fPreprocessor.InvalidDefinesInFile(FileName); //we don't need this, since we reset defines after each parse |
4253 | //p->includeFiles.clear(); |
4254 | //p->usings.clear(); |
4255 | for (PStatement& statement:p->statements) { |
4256 | if ((statement->kind == StatementKind::skFunction |
4257 | || statement->kind == StatementKind::skConstructor |
4258 | || statement->kind == StatementKind::skDestructor |
4259 | || statement->kind == StatementKind::skVariable) |
4260 | && (fileName != statement->fileName)) { |
4261 | statement->hasDefinition = false; |
4262 | } |
4263 | } |
4264 | |
4265 | for (PStatement& statement:p->declaredStatements) { |
4266 | mStatementList.deleteStatement(statement); |
4267 | } |
4268 | |
4269 | //p->declaredStatements.clear(); |
4270 | //p->statements.clear(); |
4271 | //p->scopes.clear(); |
4272 | //p->dependedFiles.clear(); |
4273 | //p->dependingFiles.clear(); |
4274 | } |
4275 | } |
4276 | |
4277 | void CppParser::internalInvalidateFiles(const QSet<QString> &files) |
4278 | { |
4279 | for (const QString& file:files) |
4280 | internalInvalidateFile(file); |
4281 | } |
4282 | |
4283 | QSet<QString> CppParser::calculateFilesToBeReparsed(const QString &fileName) |
4284 | { |
4285 | if (fileName.isEmpty()) |
4286 | return QSet<QString>(); |
4287 | QQueue<QString> queue; |
4288 | QSet<QString> processed; |
4289 | queue.enqueue(fileName); |
4290 | while (!queue.isEmpty()) { |
4291 | QString name = queue.dequeue(); |
4292 | processed.insert(name); |
4293 | PFileIncludes p=mPreprocessor.includesList().value(name); |
4294 | if (!p) |
4295 | continue; |
4296 | foreach (const QString& s,p->dependedFiles) { |
4297 | if (!processed.contains(s)) { |
4298 | queue.enqueue(s); |
4299 | } |
4300 | } |
4301 | } |
4302 | return processed; |
4303 | } |
4304 | |
4305 | int CppParser::calcKeyLenForStruct(const QString &word) |
4306 | { |
4307 | if (word.startsWith("struct" )) |
4308 | return 6; |
4309 | else if (word.startsWith("class" ) |
4310 | || word.startsWith("union" )) |
4311 | return 5; |
4312 | return -1; |
4313 | } |
4314 | |
4315 | void CppParser::scanMethodArgs(const PStatement& functionStatement, const QString &argStr) |
4316 | { |
4317 | // Split up argument string by , |
4318 | int i = 1; // assume it starts with ( and ends with ) |
4319 | int paramStart = i; |
4320 | |
4321 | QString args; |
4322 | while (i < argStr.length()) { |
4323 | if ((argStr[i] == ',') || |
4324 | ((i == argStr.length()-1) && (argStr[i] == ')'))) { |
4325 | // We've found "int* a" for example |
4326 | QString s = argStr.mid(paramStart,i-paramStart); |
4327 | |
4328 | //remove default value |
4329 | int assignPos = s.indexOf('='); |
4330 | if (assignPos >= 0) { |
4331 | s.truncate(assignPos); |
4332 | s = s.trimmed(); |
4333 | } |
4334 | // we don't support function pointer parameters now, till we can tokenize function parameters |
4335 | // { |
4336 | // // Can be a function pointer. If so, scan after last ) |
4337 | // BracePos := LastPos(')', S); |
4338 | // if (BracePos > 0) then // it's a function pointer... begin |
4339 | // SpacePos := LastPos(' ', Copy(S, BracePos, MaxInt)) // start search at brace |
4340 | // end else begin |
4341 | // } |
4342 | //skip [] |
4343 | int varEndPos = s.length()-1; |
4344 | int bracketLevel = 0; |
4345 | while (varEndPos>=0) { |
4346 | switch(s[varEndPos].unicode()) { |
4347 | case ']': |
4348 | bracketLevel++; |
4349 | break; |
4350 | case '[': |
4351 | bracketLevel--; |
4352 | varEndPos--; |
4353 | break; |
4354 | } |
4355 | if (bracketLevel==0) |
4356 | break; |
4357 | varEndPos--; |
4358 | } |
4359 | int varStartPos = varEndPos; |
4360 | if (varEndPos>=0) { |
4361 | while (varStartPos-1>=0) { |
4362 | if (!mTokenizer.isIdentChar(s[varStartPos-1])) |
4363 | break; |
4364 | varStartPos--; |
4365 | } |
4366 | } |
4367 | if (varStartPos>=0) { |
4368 | if (varEndPos+1<s.length()) |
4369 | args=s.mid(varEndPos+1); |
4370 | addStatement( |
4371 | functionStatement, |
4372 | mCurrentFile, |
4373 | s.mid(0,varStartPos), // 'int*' |
4374 | s.mid(varStartPos,varEndPos-varStartPos+1), // a |
4375 | args, |
4376 | "" , |
4377 | functionStatement->definitionLine, |
4378 | StatementKind::skParameter, |
4379 | StatementScope::ssLocal, |
4380 | StatementClassScope::scsNone, |
4381 | true, |
4382 | false); |
4383 | } |
4384 | if (varStartPos<s.length()) { |
4385 | args = "" ; |
4386 | int bracketPos = s.indexOf('['); |
4387 | if (bracketPos >= 0) { |
4388 | args = s.mid(bracketPos); |
4389 | s.truncate(bracketPos); |
4390 | } |
4391 | } |
4392 | paramStart = i + 1; // step over , |
4393 | } |
4394 | i++; |
4395 | } |
4396 | } |
4397 | |
4398 | QString CppParser::splitPhrase(const QString &phrase, QString &sClazz, |
4399 | QString &sOperator, QString &sMember) |
4400 | { |
4401 | sClazz="" ; |
4402 | sMember="" ; |
4403 | sOperator="" ; |
4404 | QString result="" ; |
4405 | int bracketLevel = 0; |
4406 | // Obtain stuff before first operator |
4407 | int firstOpStart = phrase.length() + 1; |
4408 | int firstOpEnd = phrase.length() + 1; |
4409 | for (int i = 0; i<phrase.length();i++) { |
4410 | if ((i+1<phrase.length()) && (phrase[i] == '-') && (phrase[i + 1] == '>') && (bracketLevel==0)) { |
4411 | firstOpStart = i; |
4412 | firstOpEnd = i+2; |
4413 | sOperator = "->" ; |
4414 | break; |
4415 | } else if ((i+1<phrase.length()) && (phrase[i] == ':') && (phrase[i + 1] == ':') && (bracketLevel==0)) { |
4416 | firstOpStart = i; |
4417 | firstOpEnd = i+2; |
4418 | sOperator = "::" ; |
4419 | break; |
4420 | } else if ((phrase[i] == '.') && (bracketLevel==0)) { |
4421 | firstOpStart = i; |
4422 | firstOpEnd = i+1; |
4423 | sOperator = "." ; |
4424 | break; |
4425 | } else if (phrase[i] == '[') { |
4426 | bracketLevel++; |
4427 | } else if (phrase[i] == ']') { |
4428 | bracketLevel--; |
4429 | } |
4430 | } |
4431 | sClazz = phrase.mid(0, firstOpStart); |
4432 | if (firstOpStart == 0) { |
4433 | sMember = "" ; |
4434 | return "" ; |
4435 | } |
4436 | |
4437 | result = phrase.mid(firstOpEnd); |
4438 | |
4439 | // ... and before second op, if there is one |
4440 | int secondOp = 0; |
4441 | bracketLevel = 0; |
4442 | for (int i = firstOpEnd; i<phrase.length();i++) { |
4443 | if ((i+1<phrase.length()) && (phrase[i] == '-') && (phrase[i + 1] == '>') && (bracketLevel=0)) { |
4444 | secondOp = i; |
4445 | break; |
4446 | } else if ((i+1<phrase.length()) && (phrase[i] == ':') && (phrase[i + 1] == ':') && (bracketLevel=0)) { |
4447 | secondOp = i; |
4448 | break; |
4449 | } else if ((phrase[i] == '.') && (bracketLevel=0)) { |
4450 | secondOp = i; |
4451 | break; |
4452 | } else if (phrase[i] == '[') { |
4453 | bracketLevel++; |
4454 | } else if (phrase[i] == ']') { |
4455 | bracketLevel--; |
4456 | } |
4457 | } |
4458 | if (secondOp == 0) { |
4459 | sMember = phrase.mid(firstOpEnd); |
4460 | } else { |
4461 | sMember = phrase.mid(firstOpEnd,secondOp-firstOpEnd); |
4462 | } |
4463 | return result; |
4464 | } |
4465 | |
4466 | static bool isIdentChar(const QChar& ch) { |
4467 | return ch.isLetter() |
4468 | || ch == '_' |
4469 | || ch.isDigit(); |
4470 | } |
4471 | |
4472 | static void appendArgWord(QString& args, const QString& word) { |
4473 | QString s=word.trimmed(); |
4474 | if (s.isEmpty()) |
4475 | return; |
4476 | if (args.isEmpty()) |
4477 | args.append(s); |
4478 | else if (isIdentChar(args.back()) && isIdentChar(word.front()) ) { |
4479 | args+=" " ; |
4480 | args+=s; |
4481 | } else { |
4482 | args+=s; |
4483 | } |
4484 | } |
4485 | QString CppParser::removeArgNames(const QString &args) |
4486 | { |
4487 | QString result = "" ; |
4488 | int argsLen = args.length(); |
4489 | if (argsLen < 2) |
4490 | return "" ; |
4491 | int i=1; // skip start '(' |
4492 | QString currentArg; |
4493 | QString word; |
4494 | int brackLevel = 0; |
4495 | bool typeGetted = false; |
4496 | while (i<argsLen-1) { //skip end ')' |
4497 | switch(args[i].unicode()) { |
4498 | case ',': |
4499 | if (brackLevel >0) { |
4500 | word+=args[i]; |
4501 | } else { |
4502 | if (!typeGetted) { |
4503 | appendArgWord(currentArg,word); |
4504 | } else { |
4505 | if (isCppKeyword(word)) { |
4506 | appendArgWord(currentArg,word); |
4507 | } |
4508 | } |
4509 | word = "" ; |
4510 | result += currentArg.trimmed() + ','; |
4511 | currentArg = "" ; |
4512 | typeGetted = false; |
4513 | } |
4514 | break; |
4515 | case '<': |
4516 | case '[': |
4517 | case '(': |
4518 | brackLevel++; |
4519 | word+=args[i]; |
4520 | break; |
4521 | case '>': |
4522 | case ']': |
4523 | case ')': |
4524 | brackLevel--; |
4525 | word+=args[i]; |
4526 | break; |
4527 | case ' ': |
4528 | case '\t': |
4529 | if ((brackLevel >0) && !isSpaceChar(args[i-1])) { |
4530 | word+=args[i]; |
4531 | } else if (!word.isEmpty()) { |
4532 | if (!typeGetted) { |
4533 | appendArgWord(currentArg,word); |
4534 | if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) |
4535 | typeGetted = true; |
4536 | } else { |
4537 | if (isCppKeyword(word)) |
4538 | appendArgWord(currentArg,word); |
4539 | } |
4540 | word = "" ; |
4541 | } |
4542 | break; |
4543 | case '&': |
4544 | case '*': |
4545 | if (!word.isEmpty()) { |
4546 | if (!typeGetted) { |
4547 | appendArgWord(currentArg,word); |
4548 | if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) |
4549 | typeGetted = true; |
4550 | } else { |
4551 | if (isCppKeyword(word)) |
4552 | appendArgWord(currentArg,word); |
4553 | } |
4554 | word = "" ; |
4555 | } |
4556 | currentArg+=args[i]; |
4557 | break; |
4558 | default: |
4559 | if (isIdentChar(args[i])) { |
4560 | word+=args[i]; |
4561 | } |
4562 | } |
4563 | i++; |
4564 | } |
4565 | if (!typeGetted) { |
4566 | appendArgWord(currentArg,word); |
4567 | } else { |
4568 | if (isCppKeyword(word)) { |
4569 | appendArgWord(currentArg,word); |
4570 | } |
4571 | } |
4572 | result += currentArg.trimmed(); |
4573 | return result; |
4574 | } |
4575 | |
4576 | bool CppParser::isNotFuncArgs(const QString &args) |
4577 | { |
4578 | int i=1; //skip '(' |
4579 | int endPos = args.length()-1;//skip ')' |
4580 | bool lastCharIsId=false; |
4581 | QString word = "" ; |
4582 | while (i<endPos) { |
4583 | if (args[i] == '"' || args[i]=='\'') { |
4584 | // args contains a string/char, can't be a func define |
4585 | return true; |
4586 | } else if ( isLetterChar(args[i])) { |
4587 | word += args[i]; |
4588 | lastCharIsId = true; |
4589 | i++; |
4590 | } else if ((args[i] == ':') && (args[i+1] == ':')) { |
4591 | lastCharIsId = false; |
4592 | word += "::" ; |
4593 | i+=2; |
4594 | } else if (isDigitChar(args[i])) { |
4595 | if (!lastCharIsId) |
4596 | return true; |
4597 | word+=args[i]; |
4598 | i++; |
4599 | } else if (isSpaceChar(args[i]) || isLineChar(args[i])) { |
4600 | if (!word.isEmpty()) |
4601 | break; |
4602 | i++; |
4603 | } else if (word.isEmpty()) { |
4604 | return true; |
4605 | } else |
4606 | break; |
4607 | } |
4608 | //function with no args |
4609 | if (i>endPos && word.isEmpty()) { |
4610 | return false; |
4611 | } |
4612 | |
4613 | if (isCppKeyword(word)) { |
4614 | return word == "true" || word == "false" || word == "nullptr" ; |
4615 | } |
4616 | PStatement statement =findStatementOf(mCurrentFile,word,getCurrentScope(),true); |
4617 | if (statement && |
4618 | !isTypeStatement(statement->kind)) |
4619 | return true; |
4620 | return false; |
4621 | } |
4622 | |
4623 | bool CppParser::isNamedScope(StatementKind kind) const |
4624 | { |
4625 | switch(kind) { |
4626 | case StatementKind::skClass: |
4627 | case StatementKind::skNamespace: |
4628 | case StatementKind::skFunction: |
4629 | return true; |
4630 | default: |
4631 | return false; |
4632 | } |
4633 | } |
4634 | |
4635 | bool CppParser::isTypeStatement(StatementKind kind) const |
4636 | { |
4637 | switch(kind) { |
4638 | case StatementKind::skClass: |
4639 | case StatementKind::skTypedef: |
4640 | case StatementKind::skEnumClassType: |
4641 | case StatementKind::skEnumType: |
4642 | return true; |
4643 | default: |
4644 | return false; |
4645 | } |
4646 | } |
4647 | |
4648 | void CppParser::updateSerialId() |
4649 | { |
4650 | mSerialId = QString("%1 %2" ).arg(mParserId).arg(mSerialCount); |
4651 | } |
4652 | |
4653 | ParserLanguage CppParser::language() const |
4654 | { |
4655 | return mLanguage; |
4656 | } |
4657 | |
4658 | void CppParser::setLanguage(ParserLanguage newLanguage) |
4659 | { |
4660 | mLanguage = newLanguage; |
4661 | } |
4662 | |
4663 | |
4664 | |
4665 | const StatementModel &CppParser::statementList() const |
4666 | { |
4667 | return mStatementList; |
4668 | } |
4669 | |
4670 | bool CppParser::() const |
4671 | { |
4672 | return mParseGlobalHeaders; |
4673 | } |
4674 | |
4675 | void CppParser::(bool ) |
4676 | { |
4677 | mParseGlobalHeaders = newParseGlobalHeaders; |
4678 | } |
4679 | |
4680 | const QSet<QString> &CppParser::includePaths() |
4681 | { |
4682 | return mPreprocessor.includePaths(); |
4683 | } |
4684 | |
4685 | const QSet<QString> &CppParser::projectIncludePaths() |
4686 | { |
4687 | return mPreprocessor.projectIncludePaths(); |
4688 | } |
4689 | |
4690 | bool CppParser::() const |
4691 | { |
4692 | return mParseLocalHeaders; |
4693 | } |
4694 | |
4695 | void CppParser::(bool ) |
4696 | { |
4697 | mParseLocalHeaders = newParseLocalHeaders; |
4698 | } |
4699 | |
4700 | const QString &CppParser::serialId() const |
4701 | { |
4702 | return mSerialId; |
4703 | } |
4704 | |
4705 | int CppParser::parserId() const |
4706 | { |
4707 | return mParserId; |
4708 | } |
4709 | |
4710 | void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream) |
4711 | { |
4712 | mOnGetFileStream = newOnGetFileStream; |
4713 | } |
4714 | |
4715 | const QSet<QString> &CppParser::filesToScan() const |
4716 | { |
4717 | return mFilesToScan; |
4718 | } |
4719 | |
4720 | void CppParser::setFilesToScan(const QSet<QString> &newFilesToScan) |
4721 | { |
4722 | mFilesToScan = newFilesToScan; |
4723 | } |
4724 | |
4725 | bool CppParser::enabled() const |
4726 | { |
4727 | return mEnabled; |
4728 | } |
4729 | |
4730 | void CppParser::setEnabled(bool newEnabled) |
4731 | { |
4732 | if (mEnabled!=newEnabled) { |
4733 | mEnabled = newEnabled; |
4734 | if (!mEnabled) { |
4735 | this->reset(); |
4736 | } |
4737 | } |
4738 | } |
4739 | |
4740 | CppFileParserThread::CppFileParserThread( |
4741 | PCppParser parser, |
4742 | QString fileName, |
4743 | bool inProject, |
4744 | bool onlyIfNotParsed, |
4745 | bool updateView, |
4746 | QObject *parent):QThread(parent), |
4747 | mParser(parser), |
4748 | mFileName(fileName), |
4749 | mInProject(inProject), |
4750 | mOnlyIfNotParsed(onlyIfNotParsed), |
4751 | mUpdateView(updateView) |
4752 | { |
4753 | |
4754 | } |
4755 | |
4756 | void CppFileParserThread::run() |
4757 | { |
4758 | if (mParser && !mParser->parsing()) { |
4759 | mParser->parseFile(mFileName,mInProject,mOnlyIfNotParsed,mUpdateView); |
4760 | } |
4761 | } |
4762 | |
4763 | CppFileListParserThread::CppFileListParserThread(PCppParser parser, |
4764 | bool updateView, QObject *parent): |
4765 | QThread(parent), |
4766 | mParser(parser), |
4767 | mUpdateView(updateView) |
4768 | { |
4769 | |
4770 | } |
4771 | |
4772 | void CppFileListParserThread::run() |
4773 | { |
4774 | if (mParser && !mParser->parsing()) { |
4775 | mParser->parseFileList(mUpdateView); |
4776 | } |
4777 | } |
4778 | |
4779 | void parseFile(PCppParser parser, const QString& fileName, bool inProject, bool onlyIfNotParsed, bool updateView) |
4780 | { |
4781 | if (!parser) |
4782 | return; |
4783 | CppFileParserThread* thread = new CppFileParserThread(parser,fileName,inProject,onlyIfNotParsed,updateView); |
4784 | thread->connect(thread, |
4785 | &QThread::finished, |
4786 | thread, |
4787 | &QThread::deleteLater); |
4788 | thread->start(); |
4789 | } |
4790 | |
4791 | void parseFileList(PCppParser parser, bool updateView) |
4792 | { |
4793 | if (!parser) |
4794 | return; |
4795 | CppFileListParserThread *thread = new CppFileListParserThread(parser,updateView); |
4796 | thread->connect(thread, |
4797 | &QThread::finished, |
4798 | thread, |
4799 | &QThread::deleteLater); |
4800 | thread->start(); |
4801 | } |
4802 | |