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 "parserutils.h"
18
19#include <QDir>
20#include <QFile>
21#include <QFileInfo>
22#include <QDebug>
23#include <QGlobalStatic>
24#include "../utils.h"
25
26QStringList CppDirectives;
27QStringList JavadocTags;
28QMap<QString,SkipType> CppKeywords;
29QSet<QString> CppControlKeyWords;
30QSet<QString> CppTypeKeywords;
31QSet<QString> CKeywords;
32QSet<QString> STLPointers;
33QSet<QString> STLContainers;
34QSet<QString> STLElementMethods;
35QSet<QString> MemberOperators;
36QSet<QString> IOManipulators;
37
38Q_GLOBAL_STATIC(QSet<QString>,CppHeaderExts)
39Q_GLOBAL_STATIC(QSet<QString>,CppSourceExts)
40
41void initParser()
42{
43 CppHeaderExts->insert("h");
44 CppHeaderExts->insert("hpp");
45 CppHeaderExts->insert("rh");
46 CppHeaderExts->insert("hh");
47 CppHeaderExts->insert("hxx");
48 CppHeaderExts->insert("inl");
49 CppHeaderExts->insert("");
50
51 CppSourceExts->insert("c");
52 CppSourceExts->insert("cpp");
53 CppSourceExts->insert("cc");
54 CppSourceExts->insert("cxx");
55 CppSourceExts->insert("c++");
56 CppSourceExts->insert("cp");
57 // skip itself
58 CppKeywords.insert("and",SkipType::skItself);
59 CppKeywords.insert("and_eq",SkipType::skItself);
60 CppKeywords.insert("bitand",SkipType::skItself);
61 CppKeywords.insert("bitor",SkipType::skItself);
62 CppKeywords.insert("break",SkipType::skItself);
63 CppKeywords.insert("compl",SkipType::skItself);
64 CppKeywords.insert("constexpr",SkipType::skItself);
65 CppKeywords.insert("const_cast",SkipType::skItself);
66 CppKeywords.insert("continue",SkipType::skItself);
67 CppKeywords.insert("dynamic_cast",SkipType::skItself);
68 CppKeywords.insert("else",SkipType::skItself);
69 CppKeywords.insert("explicit",SkipType::skItself);
70 CppKeywords.insert("export",SkipType::skItself);
71 CppKeywords.insert("false",SkipType::skItself);
72 //CppKeywords.insert("for",SkipType::skItself);
73 CppKeywords.insert("mutable",SkipType::skItself);
74 CppKeywords.insert("noexcept",SkipType::skItself);
75 CppKeywords.insert("not",SkipType::skItself);
76 CppKeywords.insert("not_eq",SkipType::skItself);
77 CppKeywords.insert("nullptr",SkipType::skItself);
78 CppKeywords.insert("or",SkipType::skItself);
79 CppKeywords.insert("or_eq",SkipType::skItself);
80 CppKeywords.insert("register",SkipType::skItself);
81 CppKeywords.insert("reinterpret_cast",SkipType::skItself);
82 CppKeywords.insert("static_assert",SkipType::skItself);
83 CppKeywords.insert("static_cast",SkipType::skItself);
84 CppKeywords.insert("template",SkipType::skItself);
85 //CppKeywords.insert("this",SkipType::skItself);
86 CppKeywords.insert("thread_local",SkipType::skItself);
87 CppKeywords.insert("true",SkipType::skItself);
88 CppKeywords.insert("typename",SkipType::skItself);
89 CppKeywords.insert("virtual",SkipType::skItself);
90 CppKeywords.insert("volatile",SkipType::skItself);
91 CppKeywords.insert("xor",SkipType::skItself);
92 CppKeywords.insert("xor_eq",SkipType::skItself);
93
94
95 //CppKeywords.insert("catch",SkipType::skItself);
96 CppKeywords.insert("do",SkipType::skItself);
97 CppKeywords.insert("try",SkipType::skItself);
98
99 // Skip to ;
100 CppKeywords.insert("delete",SkipType::skToSemicolon);
101 CppKeywords.insert("delete[]",SkipType::skToSemicolon);
102 CppKeywords.insert("goto",SkipType::skToSemicolon);
103 CppKeywords.insert("new",SkipType::skToSemicolon);
104 CppKeywords.insert("return",SkipType::skToSemicolon);
105 CppKeywords.insert("throw",SkipType::skToSemicolon);
106 // CppKeywords.insert("using",SkipType::skToSemicolon); //won't use it
107
108 // Skip to :
109 CppKeywords.insert("case",SkipType::skToColon);
110 CppKeywords.insert("default",SkipType::skToColon);
111
112 // Skip to )
113 CppKeywords.insert("__attribute__",SkipType::skToRightParenthesis);
114 CppKeywords.insert("alignas",SkipType::skToRightParenthesis); // not right
115 CppKeywords.insert("alignof",SkipType::skToRightParenthesis); // not right
116 CppKeywords.insert("decltype",SkipType::skToRightParenthesis); // not right
117 CppKeywords.insert("if",SkipType::skToRightParenthesis);
118 CppKeywords.insert("sizeof",SkipType::skToRightParenthesis);
119 CppKeywords.insert("switch",SkipType::skToRightParenthesis);
120 CppKeywords.insert("typeid",SkipType::skToRightParenthesis);
121 CppKeywords.insert("while",SkipType::skToRightParenthesis);
122
123 // Skip to }
124 CppKeywords.insert("asm",SkipType::skToRightBrace);
125 //CppKeywords.insert("namespace",SkipType::skToLeftBrace); // won't process it
126 // Skip to {
127
128 // wont handle
129
130 //Not supported yet
131 CppKeywords.insert("atomic_cancel",SkipType::skNone);
132 CppKeywords.insert("atomic_commit",SkipType::skNone);
133 CppKeywords.insert("atomic_noexcept",SkipType::skNone);
134 CppKeywords.insert("concept",SkipType::skNone);
135 CppKeywords.insert("consteval",SkipType::skNone);
136 CppKeywords.insert("constinit",SkipType::skNone);
137 CppKeywords.insert("co_wait",SkipType::skNone);
138 CppKeywords.insert("co_return",SkipType::skNone);
139 CppKeywords.insert("co_yield",SkipType::skNone);
140 CppKeywords.insert("reflexpr",SkipType::skNone);
141 CppKeywords.insert("requires",SkipType::skNone);
142
143 // its a type
144 CppKeywords.insert("auto",SkipType::skNone);
145 CppKeywords.insert("bool",SkipType::skNone);
146 CppKeywords.insert("char",SkipType::skNone);
147 CppKeywords.insert("char8_t",SkipType::skNone);
148 CppKeywords.insert("char16_t",SkipType::skNone);
149 CppKeywords.insert("char32_t",SkipType::skNone);
150 CppKeywords.insert("double",SkipType::skNone);
151 CppKeywords.insert("float",SkipType::skNone);
152 CppKeywords.insert("int",SkipType::skNone);
153 CppKeywords.insert("long",SkipType::skNone);
154 CppKeywords.insert("short",SkipType::skNone);
155 CppKeywords.insert("signed",SkipType::skNone);
156 CppKeywords.insert("unsigned",SkipType::skNone);
157 CppKeywords.insert("void",SkipType::skNone);
158 CppKeywords.insert("wchar_t",SkipType::skNone);
159
160 // type keywords
161 CppTypeKeywords.insert("auto");
162 CppTypeKeywords.insert("bool");
163 CppTypeKeywords.insert("char");
164 CppTypeKeywords.insert("char8_t");
165 CppTypeKeywords.insert("char16_t");
166 CppTypeKeywords.insert("char32_t");
167 CppTypeKeywords.insert("double");
168 CppTypeKeywords.insert("float");
169 CppTypeKeywords.insert("int");
170 CppTypeKeywords.insert("long");
171 CppTypeKeywords.insert("short");
172 //CppTypeKeywords.insert("signed");
173 //CppTypeKeywords.insert("unsigned");
174 CppTypeKeywords.insert("void");
175 CppTypeKeywords.insert("wchar_t");
176 CppTypeKeywords.insert("signed");
177 CppTypeKeywords.insert("unsigned");
178
179 // it's part of type info
180 CppKeywords.insert("const",SkipType::skNone);
181 CppKeywords.insert("extern",SkipType::skNone);
182 CppKeywords.insert("inline",SkipType::skNone);
183
184 // handled elsewhere
185 CppKeywords.insert("class",SkipType::skNone);
186 CppKeywords.insert("enum",SkipType::skNone);
187 CppKeywords.insert("friend",SkipType::skNone);
188 CppKeywords.insert("operator",SkipType::skNone);
189 CppKeywords.insert("private",SkipType::skNone);
190 CppKeywords.insert("protected",SkipType::skNone);
191 CppKeywords.insert("public",SkipType::skNone);
192 CppKeywords.insert("static",SkipType::skNone);
193 CppKeywords.insert("struct",SkipType::skNone);
194 CppKeywords.insert("typedef",SkipType::skNone);
195 CppKeywords.insert("union",SkipType::skNone);
196 // namespace
197 CppKeywords.insert("namespace",SkipType::skNone);
198 CppKeywords.insert("using",SkipType::skNone);
199
200 CppKeywords.insert("for",SkipType::skNone);
201 CppKeywords.insert("catch",SkipType::skNone);
202
203
204
205 // nullptr is value
206 CppKeywords.insert("nullptr",SkipType::skNone);
207
208 //C Keywords
209 CKeywords.insert("auto");
210 CKeywords.insert("break");
211 CKeywords.insert("case");
212 CKeywords.insert("char");
213 CKeywords.insert("const");
214 CKeywords.insert("continue");
215 CKeywords.insert("default");
216 CKeywords.insert("do");
217 CKeywords.insert("double");
218 CKeywords.insert("else");
219 CKeywords.insert("enum");
220 CKeywords.insert("extern");
221 CKeywords.insert("float");
222 CKeywords.insert("for");
223 CKeywords.insert("goto");
224 CKeywords.insert("if");
225 CKeywords.insert("inline");
226 CKeywords.insert("int");
227 CKeywords.insert("long");
228 CKeywords.insert("register");
229 CKeywords.insert("restrict");
230 CKeywords.insert("return");
231 CKeywords.insert("short");
232 CKeywords.insert("signed");
233 CKeywords.insert("sizeof");
234 CKeywords.insert("static");
235 CKeywords.insert("struct");
236 CKeywords.insert("switch");
237 CKeywords.insert("typedef");
238 CKeywords.insert("union");
239 CKeywords.insert("unsigned");
240 CKeywords.insert("void");
241 CKeywords.insert("volatile");
242 CKeywords.insert("while");
243
244 CppControlKeyWords.insert("for");
245 CppControlKeyWords.insert("if");
246 CppControlKeyWords.insert("catch");
247
248 //STL Containers
249 STLContainers.insert("std::array");
250 STLContainers.insert("std::vector");
251 STLContainers.insert("std::deque");
252 STLContainers.insert("std::forward_list");
253 STLContainers.insert("std::list");
254
255 STLContainers.insert("std::set");
256 STLContainers.insert("std::map");
257 STLContainers.insert("std::multilist");
258 STLContainers.insert("std::multimap");
259
260 STLContainers.insert("std::unordered_set");
261 STLContainers.insert("std::unordered_map");
262 STLContainers.insert("std::unordered_multiset");
263 STLContainers.insert("std::unordered_multimap");
264
265 STLContainers.insert("std::stack");
266 STLContainers.insert("std::queue");
267 STLContainers.insert("std::priority_queue");
268
269 STLContainers.insert("std::span");
270
271 //STL element access methods
272 STLElementMethods.insert("at");
273 STLElementMethods.insert("back");
274 STLElementMethods.insert("front");
275 STLElementMethods.insert("top");
276
277 //STL pointers
278 STLPointers.insert("std::unique_ptr");
279 STLPointers.insert("std::auto_ptr");
280 STLPointers.insert("std::shared_ptr");
281 STLPointers.insert("std::weak_ptr");
282 STLPointers.insert("__gnu_cxx::__normal_iterator");
283 STLPointers.insert("std::reverse_iterator");
284 STLPointers.insert("std::iterator");
285
286 //C/CPP preprocessor directives
287 CppDirectives.append("#include");
288 CppDirectives.append("#if");
289 CppDirectives.append("#ifdef");
290 CppDirectives.append("#ifndef");
291 CppDirectives.append("#else");
292 CppDirectives.append("#elif");
293 CppDirectives.append("#endif");
294 CppDirectives.append("#define");
295 CppDirectives.append("#error");
296 CppDirectives.append("#pragma");
297 CppDirectives.append("#line");
298 CppDirectives.append("#undef");
299
300 // javadoc tags
301 JavadocTags.append("@author");
302 JavadocTags.append("@code");
303 JavadocTags.append("@docRoot");
304 JavadocTags.append("@deprecated");
305 JavadocTags.append("@exception");
306 JavadocTags.append("@inheritDoc");
307 JavadocTags.append("@link");
308 JavadocTags.append("@linkplain");
309 JavadocTags.append("@literal");
310 JavadocTags.append("@param");
311 JavadocTags.append("@return");
312 JavadocTags.append("@see");
313 JavadocTags.append("@serial");
314 JavadocTags.append("@serialData");
315 JavadocTags.append("@serialField");
316 JavadocTags.append("@since");
317 JavadocTags.append("@throws");
318 JavadocTags.append("@value");
319 JavadocTags.append("@version");
320
321 MemberOperators.insert(".");
322 MemberOperators.insert("::");
323 MemberOperators.insert("->");
324 MemberOperators.insert("->*");
325 MemberOperators.insert(".*");
326
327 IOManipulators.insert("std::boolalpha");
328 IOManipulators.insert("std::noboolalpha");
329 IOManipulators.insert("std::showbase");
330 IOManipulators.insert("std::noshowbase");
331 IOManipulators.insert("std::showpoint");
332 IOManipulators.insert("std::noshowpoint");
333 IOManipulators.insert("std::showpos");
334 IOManipulators.insert("std::noshowpos");
335 IOManipulators.insert("std::skipws");
336 IOManipulators.insert("std::noskipws");
337
338 IOManipulators.insert("std::uppercase");
339 IOManipulators.insert("std::nouppercase");
340 IOManipulators.insert("std::unitbuf");
341 IOManipulators.insert("std::nounitbuf");
342 IOManipulators.insert("std::left");
343 IOManipulators.insert("std::right");
344 IOManipulators.insert("std::internal");
345 IOManipulators.insert("std::dec");
346 IOManipulators.insert("std::hex");
347 IOManipulators.insert("std::oct");
348
349 IOManipulators.insert("std::fixed");
350 IOManipulators.insert("std::scientific");
351 IOManipulators.insert("std::hexfloat");
352 IOManipulators.insert("std::defaultfloat");
353 IOManipulators.insert("std::ws");
354 IOManipulators.insert("std::ends");
355 IOManipulators.insert("std::flush");
356 IOManipulators.insert("std::endl");
357
358}
359
360QString getHeaderFilename(const QString &relativeTo, const QString &line,
361 const QStringList& includePaths, const QStringList& projectIncludePaths) {
362 QString result = "";
363
364 // Handle <>
365 int openTokenPos = line.indexOf('<');
366 if (openTokenPos >= 0) {
367 int closeTokenPos = line.indexOf('>',openTokenPos+1);
368 if (closeTokenPos >=0) {
369 QString fileName = line.mid(openTokenPos + 1, closeTokenPos - openTokenPos - 1);
370 //project settings is preferred
371 result = getSystemHeaderFilename(fileName, projectIncludePaths);
372 if (result.isEmpty()) {
373 result = getSystemHeaderFilename(fileName, includePaths);
374 }
375 }
376 } else {
377 // Try ""
378 openTokenPos = line.indexOf('"');
379 if (openTokenPos >= 0) {
380 int closeTokenPos = line.indexOf('"', openTokenPos+1);
381 if (closeTokenPos >= 0) {
382 QString fileName = line.mid(openTokenPos + 1, closeTokenPos - openTokenPos - 1);
383 result = getLocalHeaderFilename(relativeTo, fileName);
384 //project settings is preferred
385 if (result.isEmpty()) {
386 result = getSystemHeaderFilename(fileName, projectIncludePaths);
387 }
388 if (result.isEmpty()) {
389 result = getSystemHeaderFilename(fileName, includePaths);
390 }
391 }
392 }
393 }
394 return result;
395}
396
397QString getLocalHeaderFilename(const QString &relativeTo, const QString &fileName)
398{
399 QFileInfo relativeFile(relativeTo);
400 QDir dir = relativeFile.dir();
401 // Search local directory
402 if (dir.exists(fileName)) {
403 return dir.absoluteFilePath(fileName);
404 }
405 return "";
406}
407
408QString getSystemHeaderFilename(const QString &fileName, const QStringList& includePaths)
409{
410
411 // Search compiler include directories
412 for (const QString& path:includePaths) {
413 QDir dir(path);
414 if (dir.exists(fileName))
415 return dir.absoluteFilePath(fileName);
416 }
417 //not found
418 return "";
419}
420
421bool isSystemHeaderFile(const QString &fileName, const QSet<QString> &includePaths)
422{
423 if (fileName.isEmpty())
424 return false;
425 if (includePaths.isEmpty())
426 return false;
427 bool isFullName = false;
428
429#ifdef Q_OS_WIN
430 isFullName = fileName.startsWith("/") || (fileName.length()>2 && fileName[1]==':');
431#else
432 isFullName = fileName.startsWith("/");
433#endif
434 if (isFullName) {
435 QFileInfo info(fileName);
436 // If it's a full file name, check if its directory is an include path
437 if (info.exists()) { // full file name
438 QDir dir = info.dir();
439 QString absPath = includeTrailingPathDelimiter(dir.absolutePath());
440 foreach (const QString& incPath, includePaths) {
441 if (absPath.startsWith(incPath))
442 return true;
443 }
444 }
445 } else {
446 //check if it's in the include dir
447 for (const QString& includePath: includePaths) {
448 QDir dir(includePath);
449 if (dir.exists(fileName))
450 return true;
451 }
452 }
453 return false;
454}
455
456bool isCppKeyword(const QString &word)
457{
458 return CppKeywords.contains(word);
459}
460
461bool isHFile(const QString& filename)
462{
463 if (filename.isEmpty())
464 return false;
465
466 QFileInfo fileInfo(filename);
467 return CppHeaderExts->contains(fileInfo.suffix().toLower());
468
469}
470
471bool isCFile(const QString& filename)
472{
473 if (filename.isEmpty())
474 return false;
475
476 QFileInfo fileInfo(filename);
477 return CppSourceExts->contains(fileInfo.suffix().toLower());
478}
479
480PStatement CppScopes::findScopeAtLine(int line)
481{
482 if (mScopes.isEmpty())
483 return PStatement();
484 int start = 0;
485 int end = mScopes.size()-1;
486 while (start<=end) {
487 int mid = (start+end)/2;
488 PCppScope midScope = mScopes[mid];
489 if (midScope->startLine == line) {
490 while (mid-1>=0 && (mScopes[mid-1]->startLine == line)) {
491 mid--;
492 }
493 return mScopes[mid]->statement;
494 } else if (midScope->startLine > line) {
495 end = mid-1;
496 } else {
497 start = mid+1;
498 }
499 }
500 if (end>=0)
501 return mScopes[end]->statement;
502 else
503 return PStatement();
504}
505
506void CppScopes::addScope(int line, PStatement scopeStatement)
507{
508 PCppScope scope = std::make_shared<CppScope>();
509 scope->startLine = line;
510 scope->statement = scopeStatement;
511 mScopes.append(scope);
512 if (!mScopes.isEmpty() && mScopes.back()->startLine>line) {
513 qDebug()<<QString("Error: new scope %1 at %2 which is less that last scope %3")
514 .arg(scopeStatement->fullName, line,mScopes.back()->startLine>line);
515 }
516}
517
518PStatement CppScopes::lastScope()
519{
520 if (mScopes.isEmpty())
521 return PStatement();
522 return mScopes.back()->statement;
523}
524
525void CppScopes::removeLastScope()
526{
527 if (!mScopes.isEmpty())
528 mScopes.pop_back();
529}
530
531void CppScopes::clear()
532{
533 mScopes.clear();
534}
535
536MemberOperatorType getOperatorType(const QString &phrase, int index)
537{
538 if (index>=phrase.length())
539 return MemberOperatorType::otOther;
540 if (phrase[index] == '.')
541 return MemberOperatorType::otDot;
542 if (index+1>=phrase.length())
543 return MemberOperatorType::otOther;
544 if ((phrase[index] == '-') && (phrase[index+1] == '>'))
545 return MemberOperatorType::otArrow;
546 if ((phrase[index] == ':') && (phrase[index+1] == ':'))
547 return MemberOperatorType::otDColon;
548 return MemberOperatorType::otOther;
549}
550
551bool isScopeTypeKind(StatementKind kind)
552{
553 switch(kind) {
554 case StatementKind::skClass:
555 case StatementKind::skNamespace:
556 case StatementKind::skEnumType:
557 case StatementKind::skEnumClassType:
558 return true;
559 default:
560 return false;
561 }
562}
563
564EvalStatement::EvalStatement(
565 const QString &baseType,
566 EvalStatementKind kind,
567 const PStatement &baseStatement,
568 const PStatement &typeStatement,
569 int pointerLevel)
570{
571 this->baseType = baseType;
572 this->kind = kind;
573 this->baseStatement = baseStatement;
574 this->effectiveTypeStatement = typeStatement;
575 this->pointerLevel = pointerLevel;
576}
577
578void EvalStatement::assignType(const PEvalStatement &typeStatement)
579{
580 Q_ASSERT(typeStatement && typeStatement->kind==EvalStatementKind::Type);
581 baseType = typeStatement->baseType;
582 pointerLevel = typeStatement->pointerLevel;
583 effectiveTypeStatement = typeStatement->effectiveTypeStatement;
584}
585
586QStringList getOwnerExpressionAndMember(const QStringList expression, QString &memberOperator, QStringList &memberExpression)
587{
588 //find position of the last member operator
589 int lastMemberOperatorPos = -1;
590 int currentMatchingLevel = 0;
591 QString matchingSignLeft;
592 QString matchingSignRight;
593 for (int i=0;i<expression.length();i++) {
594 QString token = expression[i];
595 if (currentMatchingLevel == 0) {
596 if (isMemberOperator(token)) {
597 lastMemberOperatorPos = i;
598 } else if (token == "(") {
599 matchingSignLeft = "(";
600 matchingSignRight = ")";
601 currentMatchingLevel++;
602 } else if (token == "[") {
603 matchingSignLeft = "[";
604 matchingSignRight = "]";
605 currentMatchingLevel++;
606 } else if (token == "<") {
607 matchingSignLeft = "<";
608 matchingSignRight = ">";
609 currentMatchingLevel++;
610 }
611 } else {
612 if (token == matchingSignLeft) {
613 currentMatchingLevel++;
614 } else if (token == matchingSignRight) {
615 currentMatchingLevel--;
616 }
617 }
618 }
619
620 QStringList ownerExpression;
621 if (lastMemberOperatorPos<0) {
622 memberOperator = "";
623 memberExpression = expression;
624 } else {
625 memberOperator = expression[lastMemberOperatorPos];
626 memberExpression = expression.mid(lastMemberOperatorPos+1);
627 ownerExpression = expression.mid(0,lastMemberOperatorPos);
628 }
629 if (memberExpression.length()>1) {
630 memberExpression = memberExpression.mid(memberExpression.length()-1,1);
631 }
632 return ownerExpression;
633
634}
635
636bool isMemberOperator(QString token)
637{
638 return MemberOperators.contains(token);
639}
640
641
642StatementKind getKindOfStatement(const PStatement& statement)
643{
644 if (!statement)
645 return StatementKind::skUnknown;
646 if (statement->kind == StatementKind::skVariable) {
647 if (!statement->parentScope.lock()) {
648 return StatementKind::skGlobalVariable;
649 } else if (statement->scope == StatementScope::ssLocal) {
650 return StatementKind::skLocalVariable;
651 } else {
652 return StatementKind::skVariable;
653 }
654 }
655 return statement->kind;
656}
657
658bool isCppFile(const QString &filename)
659{
660 if (isCFile(filename) && !filename.endsWith(".c"))
661 return true;
662 return false;
663}
664
665bool isCppControlKeyword(const QString &word)
666{
667 return CppControlKeyWords.contains(word);
668}
669