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
29static QAtomicInt cppParserCount(0);
30CppParser::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
63CppParser::~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
80void 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
90void CppParser::addIncludePath(const QString &value)
91{
92 QMutexLocker locker(&mMutex);
93 mPreprocessor.addIncludePath(includeTrailingPathDelimiter(value));
94}
95
96void CppParser::addProjectIncludePath(const QString &value)
97{
98 QMutexLocker locker(&mMutex);
99 mPreprocessor.addProjectIncludePath(includeTrailingPathDelimiter(value));
100}
101
102void CppParser::clearIncludePaths()
103{
104 QMutexLocker locker(&mMutex);
105 mPreprocessor.clearIncludePaths();
106}
107
108void CppParser::clearProjectIncludePaths()
109{
110 QMutexLocker locker(&mMutex);
111 mPreprocessor.clearProjectIncludePaths();
112}
113
114void CppParser::clearProjectFiles()
115{
116 QMutexLocker locker(&mMutex);
117 mProjectFiles.clear();
118}
119
120QList<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
150PStatement 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
164PFileIncludes 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}
172QString 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
198PStatement 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
215int 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
234PStatementList CppParser::findNamespace(const QString &name)
235{
236 QMutexLocker locker(&mMutex);
237 return mNamespaces.value(name,PStatementList());
238}
239
240PStatement 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
268PStatement 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
276PStatement 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
420PEvalStatement CppParser::evalExpression(
421 const QString &fileName,
422 const QStringList &phraseExpression,
423 const PStatement &currentScope)
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
438PStatement 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
444PStatement CppParser::findStatementOf(const QString &fileName, const QStringList &expression, const PStatement &currentScope)
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
486PStatement 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
494PStatement 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
520PStatement 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
557PStatement 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
592PStatement 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
601bool CppParser::freeze()
602{
603 QMutexLocker locker(&mMutex);
604 if (mParsing)
605 return false;
606 mLockCount++;
607 return true;
608}
609
610bool 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
621QStringList 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
643QStringList 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
659QSet<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
678QSet<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
703QString CppParser::getHeaderFileName(const QString &relativeTo, const QString &line)
704{
705 QMutexLocker locker(&mMutex);
706 return ::getHeaderFilename(relativeTo, line, mPreprocessor.includePathList(),
707 mPreprocessor.projectIncludePathList());
708}
709
710void 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
726bool 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
737bool CppParser::isProjectHeaderFile(const QString &fileName)
738{
739 QMutexLocker locker(&mMutex);
740 return ::isSystemHeaderFile(fileName,mPreprocessor.projectIncludePaths());
741}
742
743bool CppParser::isSystemHeaderFile(const QString &fileName)
744{
745 QMutexLocker locker(&mMutex);
746 return ::isSystemHeaderFile(fileName,mPreprocessor.includePaths());
747}
748
749void 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
812void 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
861void CppParser::parseHardDefines()
862{
863 QMutexLocker locker(&mMutex);
864 if (mParsing)
865 return;
866 int oldIsSystemHeader = 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
892bool CppParser::parsing() const
893{
894 return mParsing;
895}
896
897void 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
951void CppParser::unFreeze()
952{
953 QMutexLocker locker(&mMutex);
954 mLockCount--;
955}
956
957QSet<QString> CppParser::scannedFiles()
958{
959 return mPreprocessor.scannedFiles();
960}
961
962bool CppParser::isFileParsed(const QString &filename)
963{
964 return mPreprocessor.scannedFiles().contains(filename);
965}
966
967QString 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
980QString 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
1054QString 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
1068int 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
1091void 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
1107PStatement 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
1128PStatement 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
1150PStatement 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
1253void 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
1291bool 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
1305void 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
1343void 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
1379int 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
1397int 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
1415void 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
1427bool CppParser::checkForCatchBlock()
1428{
1429// return mIndex < mTokenizer.tokenCount() &&
1430// mTokenizer[mIndex]->text == "catch";
1431 return mTokenizer[mIndex]->text == "catch";
1432}
1433
1434bool CppParser::checkForEnum()
1435{
1436// return mIndex < mTokenizer.tokenCount() &&
1437// mTokenizer[mIndex]->text == "enum";
1438 return mTokenizer[mIndex]->text == "enum";
1439}
1440
1441bool CppParser::checkForForBlock()
1442{
1443// return mIndex < mTokenizer.tokenCount() &&
1444// mTokenizer[mIndex]->text == "for";
1445 return mTokenizer[mIndex]->text == "for";
1446}
1447
1448bool CppParser::checkForKeyword()
1449{
1450 SkipType st = mCppKeywords.value(mTokenizer[mIndex]->text,SkipType::skNone);
1451 return st!=SkipType::skNone;
1452}
1453
1454bool 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
1554bool 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
1564bool CppParser::checkForPreprocessor()
1565{
1566// return (mIndex < mTokenizer.tokenCount())
1567// && ( "#" == mTokenizer[mIndex]->text);
1568 return (mTokenizer[mIndex]->text.startsWith('#'));
1569}
1570
1571bool 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
1582void 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
1593bool 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
1644bool CppParser::checkForTypedef()
1645{
1646 return mTokenizer[mIndex]->text == "typedef";
1647}
1648
1649bool 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
1657bool 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
1670bool CppParser::checkForUsing()
1671{
1672 return (mIndex < mTokenizer.tokenCount()-1) && mTokenizer[mIndex]->text == "using";
1673
1674}
1675
1676bool 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
1740int CppParser::getCurrentBlockEndSkip()
1741{
1742 if (mBlockEndSkips.isEmpty())
1743 return mTokenizer.tokenCount()+1;
1744 return mBlockEndSkips.back();
1745}
1746
1747int CppParser::getCurrentBlockBeginSkip()
1748{
1749 if (mBlockBeginSkips.isEmpty())
1750 return mTokenizer.tokenCount()+1;
1751 return mBlockBeginSkips.back();
1752}
1753
1754int CppParser::getCurrentInlineNamespaceEndSkip()
1755{
1756 if (mInlineNamespaceEndSkips.isEmpty())
1757 return mTokenizer.tokenCount()+1;
1758 return mInlineNamespaceEndSkips.back();
1759}
1760
1761PStatement CppParser::getCurrentScope()
1762{
1763 if (mCurrentScope.isEmpty()) {
1764 return PStatement();
1765 }
1766 return mCurrentScope.back();
1767}
1768
1769void 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
1805QString 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
1816PStatement 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
1830StatementScope 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
1844QString CppParser::getStatementKey(const QString &sName, const QString &sType, const QString &sNoNameArgs)
1845{
1846 return sName + "--" + sType + "--" + sNoNameArgs;
1847}
1848
1849PStatement 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
1875void 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
1918void 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
2074void 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
2119void 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
2161void 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
2320void 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
2403void 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
2531void 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)
2580handlePreprocessorEnd:
2581 mIndex++;
2582}
2583
2584StatementClassScope 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
2595void CppParser::handleScope()
2596{
2597 mClassScope = getClassScope(mIndex);
2598 mIndex+=2; // the scope is followed by a ':'
2599}
2600
2601bool 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
2690void 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
2962void 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
3055void 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
3218void 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
3279void 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
3320QString CppParser::expandMacroType(const QString &name)
3321{
3322 //its done in the preprocessor
3323 return name;
3324}
3325
3326void 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
3345QList<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
3364PStatement 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
3389QList<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
3413PStatement 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
3431PStatement 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
3442PStatement 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
3455PEvalStatement 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
3471PEvalStatement 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
3520PEvalStatement 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
3564PEvalStatement 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
3679PEvalStatement 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
3811PEvalStatement 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
3866PEvalStatement 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
4011PEvalStatement 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
4022PEvalStatement 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
4051PEvalStatement CppParser::doCreateEvalType(const QString &primitiveType)
4052{
4053 return std::make_shared<EvalStatement>(
4054 primitiveType,
4055 EvalStatementKind::Type,
4056 PStatement(),
4057 PStatement());
4058}
4059
4060PEvalStatement 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
4082PEvalStatement 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
4103PEvalStatement CppParser::doCreateEvalLiteral(const QString &type)
4104{
4105 return std::make_shared<EvalStatement>(
4106 type,
4107 EvalStatementKind::Literal,
4108 PStatement(),
4109 PStatement());
4110}
4111
4112void 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
4130PStatement 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
4192int 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
4211PStatement 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
4226void 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
4277void CppParser::internalInvalidateFiles(const QSet<QString> &files)
4278{
4279 for (const QString& file:files)
4280 internalInvalidateFile(file);
4281}
4282
4283QSet<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
4305int 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
4315void 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
4398QString 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
4466static bool isIdentChar(const QChar& ch) {
4467 return ch.isLetter()
4468 || ch == '_'
4469 || ch.isDigit();
4470}
4471
4472static 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}
4485QString 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
4576bool 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
4623bool 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
4635bool 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
4648void CppParser::updateSerialId()
4649{
4650 mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount);
4651}
4652
4653ParserLanguage CppParser::language() const
4654{
4655 return mLanguage;
4656}
4657
4658void CppParser::setLanguage(ParserLanguage newLanguage)
4659{
4660 mLanguage = newLanguage;
4661}
4662
4663
4664
4665const StatementModel &CppParser::statementList() const
4666{
4667 return mStatementList;
4668}
4669
4670bool CppParser::parseGlobalHeaders() const
4671{
4672 return mParseGlobalHeaders;
4673}
4674
4675void CppParser::setParseGlobalHeaders(bool newParseGlobalHeaders)
4676{
4677 mParseGlobalHeaders = newParseGlobalHeaders;
4678}
4679
4680const QSet<QString> &CppParser::includePaths()
4681{
4682 return mPreprocessor.includePaths();
4683}
4684
4685const QSet<QString> &CppParser::projectIncludePaths()
4686{
4687 return mPreprocessor.projectIncludePaths();
4688}
4689
4690bool CppParser::parseLocalHeaders() const
4691{
4692 return mParseLocalHeaders;
4693}
4694
4695void CppParser::setParseLocalHeaders(bool newParseLocalHeaders)
4696{
4697 mParseLocalHeaders = newParseLocalHeaders;
4698}
4699
4700const QString &CppParser::serialId() const
4701{
4702 return mSerialId;
4703}
4704
4705int CppParser::parserId() const
4706{
4707 return mParserId;
4708}
4709
4710void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream)
4711{
4712 mOnGetFileStream = newOnGetFileStream;
4713}
4714
4715const QSet<QString> &CppParser::filesToScan() const
4716{
4717 return mFilesToScan;
4718}
4719
4720void CppParser::setFilesToScan(const QSet<QString> &newFilesToScan)
4721{
4722 mFilesToScan = newFilesToScan;
4723}
4724
4725bool CppParser::enabled() const
4726{
4727 return mEnabled;
4728}
4729
4730void CppParser::setEnabled(bool newEnabled)
4731{
4732 if (mEnabled!=newEnabled) {
4733 mEnabled = newEnabled;
4734 if (!mEnabled) {
4735 this->reset();
4736 }
4737 }
4738}
4739
4740CppFileParserThread::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
4756void CppFileParserThread::run()
4757{
4758 if (mParser && !mParser->parsing()) {
4759 mParser->parseFile(mFileName,mInProject,mOnlyIfNotParsed,mUpdateView);
4760 }
4761}
4762
4763CppFileListParserThread::CppFileListParserThread(PCppParser parser,
4764 bool updateView, QObject *parent):
4765 QThread(parent),
4766 mParser(parser),
4767 mUpdateView(updateView)
4768{
4769
4770}
4771
4772void CppFileListParserThread::run()
4773{
4774 if (mParser && !mParser->parsing()) {
4775 mParser->parseFileList(mUpdateView);
4776 }
4777}
4778
4779void 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
4791void 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