1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 11509 $
6  * $Id: nativeparser_base.cpp 11509 2018-11-04 02:49:39Z ollydbg $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/codecompletion/nativeparser_base.cpp $
8  */
9 
10 #include <sdk.h>
11 
12 #ifndef CB_PRECOMP
13 #endif
14 
15 #include "nativeparser_base.h"
16 #include "parser/tokenizer.h"
17 
18 #include "parser/cclogger.h"
19 
20 #define CC_NATIVEPARSERBASE_DEBUG_OUTPUT 0
21 
22 #if defined(CC_GLOBAL_DEBUG_OUTPUT)
23     #if CC_GLOBAL_DEBUG_OUTPUT == 1
24         #undef CC_NATIVEPARSERBASE_DEBUG_OUTPUT
25         #define CC_NATIVEPARSERBASE_DEBUG_OUTPUT 1
26     #elif CC_GLOBAL_DEBUG_OUTPUT == 2
27         #undef CC_NATIVEPARSERBASE_DEBUG_OUTPUT
28         #define CC_NATIVEPARSERBASE_DEBUG_OUTPUT 2
29     #endif
30 #endif
31 
32 #ifdef CC_PARSER_TEST
33     #define ADDTOKEN(format, args...) \
34             CCLogger::Get()->AddToken(F(format, ##args))
35     #define TRACE(format, args...) \
36             CCLogger::Get()->DebugLog(F(format, ##args))
37     #define TRACE2(format, args...) \
38             CCLogger::Get()->DebugLog(F(format, ##args))
39 #else
40     #if CC_NATIVEPARSERBASE_DEBUG_OUTPUT == 1
41         #define ADDTOKEN(format, args...) \
42                 CCLogger::Get()->AddToken(F(format, ##args))
43         #define TRACE(format, args...) \
44             CCLogger::Get()->DebugLog(F(format, ##args))
45         #define TRACE2(format, args...)
46     #elif CC_NATIVEPARSERBASE_DEBUG_OUTPUT == 2
47         #define ADDTOKEN(format, args...) \
48                 CCLogger::Get()->AddToken(F(format, ##args))
49         #define TRACE(format, args...)                                              \
50             do                                                                      \
51             {                                                                       \
52                 if (g_EnableDebugTrace)                                             \
53                     CCLogger::Get()->DebugLog(F(format, ##args));                   \
54             }                                                                       \
55             while (false)
56         #define TRACE2(format, args...) \
57             CCLogger::Get()->DebugLog(F(format, ##args))
58     #else
59         #define ADDTOKEN(format, args...)
60         #define TRACE(format, args...)
61         #define TRACE2(format, args...)
62     #endif
63 #endif
64 
NativeParserBase()65 NativeParserBase::NativeParserBase()
66 {
67 }
68 
~NativeParserBase()69 NativeParserBase::~NativeParserBase()
70 {
71 }
72 
Reset()73 void NativeParserBase::Reset()
74 {
75     m_LastComponent.Clear();
76 }
77 
78 // Here's the meat of code-completion :)
79 // This function decides most of what gets included in the auto-completion
80 // list presented to the user.
81 // It's called recursively for each component of the std::queue argument.
82 // for example: objA.objB.function()
83 // The queue is like: 'objA' 'objB' 'function'. We deal with objA first.
84 //
85 // No critical section needed in this recursive function!
86 // All functions that call this recursive function, should already entered a critical section.
FindAIMatches(TokenTree * tree,std::queue<ParserComponent> components,TokenIdxSet & result,int parentTokenIdx,bool isPrefix,bool caseSensitive,bool use_inheritance,short int kindMask,TokenIdxSet * search_scope)87 size_t NativeParserBase::FindAIMatches(TokenTree*                  tree,
88                                        std::queue<ParserComponent> components,
89                                        TokenIdxSet&                result,
90                                        int                         parentTokenIdx,
91                                        bool                        isPrefix,
92                                        bool                        caseSensitive,
93                                        bool                        use_inheritance,
94                                        short int                   kindMask,
95                                        TokenIdxSet*                search_scope)
96 {
97     if (components.empty())
98         return 0;
99 
100     if (s_DebugSmartSense)
101         CCLogger::Get()->DebugLog(_T("FindAIMatches() ----- FindAIMatches - enter -----"));
102 
103     TRACE(_T("NativeParser::FindAIMatches()"));
104 
105     // pop top component
106     ParserComponent parser_component = components.front();
107     components.pop();
108 
109     // handle the special keyword "this".
110     if ((parentTokenIdx != -1) && (parser_component.component == _T("this")))
111     {
112         // this will make the AI behave like it's the previous scope (or the current if no previous scope)
113 
114         // move on please, nothing to see here...
115         // All functions that call the recursive FindAIMatches should already entered a critical section.
116         return FindAIMatches(tree, components, result, parentTokenIdx,
117                              isPrefix, caseSensitive, use_inheritance,
118                              kindMask, search_scope);
119     }
120 
121     // we 'll only add tokens in the result set if we get matches for the last token
122     bool isLastComponent = components.empty();
123     wxString searchtext = parser_component.component;
124 
125     if (s_DebugSmartSense)
126         CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Search for %s, isLast = %d"),
127                                     searchtext.wx_str(), isLastComponent?1:0));
128 
129     // get a set of matches for the current token
130     TokenIdxSet local_result;
131     // All functions that call the recursive GenerateResultSet should already entered a critical section.
132     GenerateResultSet(tree, searchtext, parentTokenIdx, local_result,
133                       (caseSensitive || !isLastComponent),
134                       (isLastComponent && !isPrefix), kindMask);
135 
136     if (s_DebugSmartSense)
137         CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Looping %lu results"),
138                                     static_cast<unsigned long>(local_result.size())));
139 
140     // loop all matches, and recurse
141     for (TokenIdxSet::const_iterator it = local_result.begin(); it != local_result.end(); it++)
142     {
143         int id = *it;
144         const Token* token = tree->at(id);
145 
146         // sanity check
147         if (!token)
148         {
149             if (s_DebugSmartSense)
150                 CCLogger::Get()->DebugLog(_T("FindAIMatches() Token is NULL?!"));
151             continue;
152         }
153 
154         // ignore operators
155         if (token->m_IsOperator)
156             continue;
157 
158         // enums children (enumerators), are added by default
159         if (token->m_TokenKind == tkEnum)
160         {
161             // insert enum type
162             result.insert(id);
163 
164             // insert enumerators
165             for (TokenIdxSet::const_iterator tis_it = token->m_Children.begin();
166                  tis_it != token->m_Children.end();
167                  tis_it++)
168                 result.insert(*tis_it);
169 
170             continue; // done with this token
171         }
172 
173         if (s_DebugSmartSense)
174             CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Match: '%s' (ID='%d') : type='%s'"),
175                                         token->m_Name.wx_str(), id, token->m_BaseType.wx_str()));
176 
177 
178         // is the token a function or variable (i.e. is not a type)
179         if (    !searchtext.IsEmpty()
180              && (parser_component.tokenType != pttSearchText)
181              && !token->m_BaseType.IsEmpty() )
182         {
183             // the token is not a type
184             // find its type's ID and use this as parent instead of (*it)
185             TokenIdxSet type_result;
186             std::queue<ParserComponent> type_components;
187             wxString actual = token->m_BaseType;
188 
189             // TODO: ignore builtin types (void, int, etc)
190             BreakUpComponents(actual, type_components);
191             // the parent to search under is a bit troubling, because of namespaces
192             // what we 'll do is search under current parent and traverse up the parentship
193             // until we find a result, or reach -1...
194 
195             if (s_DebugSmartSense)
196                 CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Looking for type: '%s' (%lu components)"),
197                                             actual.wx_str(),
198                                             static_cast<unsigned long>(type_components.size())));
199 
200             // search under all search-scope namespaces too
201             TokenIdxSet temp_search_scope;
202             if (search_scope)
203                 temp_search_scope = *search_scope;
204 
205             // add grand-parent as search scope (if none defined)
206             // this helps with namespaces when the token's type doesn't contain
207             // namespace info. In that case (with the code here) we 're searching in
208             // the parent's namespace too
209             if (parentTokenIdx != -1)
210             {
211                 const Token* parentToken = tree->at(parentTokenIdx);
212                 if (parentToken)
213                 {
214                     const Token* parent = tree->at(parentToken->m_ParentIndex);
215                     if (parent)
216                     {
217                         temp_search_scope.insert(parent->m_Index);
218                         if (s_DebugSmartSense)
219                             CCLogger::Get()->DebugLog(_T("FindAIMatches() Implicit search scope added:") + parent->m_Name);
220                     }
221                 }
222             }
223 
224             TokenIdxSet::const_iterator itsearch;
225             itsearch = temp_search_scope.begin();
226             while (!search_scope || itsearch != temp_search_scope.end())
227             {
228                 const Token* parent = tree->at(*itsearch);
229 
230                 if (s_DebugSmartSense)
231                     CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Now looking under '%s'"),
232                                                 parent ? parent->m_Name.wx_str() : wxString(_("Global namespace")).wx_str()));
233 
234                 do
235                 {
236                     // types are searched as whole words, case sensitive and only classes/namespaces
237                     // All functions that call the recursive FindAIMatches should already entered a critical section.
238                     if (FindAIMatches(tree,
239                                       type_components,
240                                       type_result,
241                                       parent ? parent->m_Index : -1,
242                                       true,
243                                       false,
244                                       false,
245                                       tkClass | tkNamespace | tkTypedef | tkEnum,
246                                       &temp_search_scope) != 0)
247                         break;
248                     if (!parent)
249                         break;
250                     parent = tree->at(parent->m_ParentIndex);
251                 } while (true);
252                 ++itsearch;
253             }
254 
255             // we got all possible types (hopefully should be just one)
256             if (!type_result.empty())
257             {
258                 // this is the first result
259                 id = *(type_result.begin());
260                 if (type_result.size() > 1)
261                 {
262                     // if we have more than one result, recurse for all of them
263                     TokenIdxSet::const_iterator tis_it = type_result.begin();
264                     ++tis_it;
265                     while (tis_it != type_result.end())
266                     {
267                         std::queue<ParserComponent> lcomp = components;
268                         // All functions that call the recursive FindAIMatches should already entered a critical section.
269                         FindAIMatches(tree, lcomp, result, *tis_it, isPrefix,
270                                       caseSensitive, use_inheritance,
271                                       kindMask, search_scope);
272                         ++tis_it;
273                     }
274                 }
275 
276                 if (s_DebugSmartSense)
277                 {
278                     CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Type: '%s' (%d)"), tree->at(id)->m_Name.wx_str(), id));
279                     if (type_result.size() > 1)
280                         CCLogger::Get()->DebugLog(F(_T("FindAIMatches() Multiple types matched for '%s': %lu results"),
281                                                     token->m_BaseType.wx_str(),
282                                                     static_cast<unsigned long>(type_result.size())));
283                 }
284             }
285             else if (s_DebugSmartSense)
286                 CCLogger::Get()->DebugLog(F(_T("FindAIMatches() No types matched '%s'."), token->m_BaseType.wx_str()));
287         }
288 
289         // if no more components, add to result set
290         if (isLastComponent)
291             result.insert(id);
292         // else recurse this function using id as a parent
293         else
294             // All functions that call the recursive FindAIMatches should already entered a critical section.
295             FindAIMatches(tree, components, result, id, isPrefix,
296                           caseSensitive, use_inheritance, kindMask,
297                           search_scope);
298     }
299 
300     if (s_DebugSmartSense)
301         CCLogger::Get()->DebugLog(_T("FindAIMatches() ----- FindAIMatches - leave -----"));
302 
303     return result.size();
304 }
305 
FindCurrentFunctionScope(TokenTree * tree,const TokenIdxSet & procResult,TokenIdxSet & scopeResult)306 void NativeParserBase::FindCurrentFunctionScope(TokenTree*        tree,
307                                                 const TokenIdxSet& procResult,
308                                                 TokenIdxSet&       scopeResult)
309 {
310     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
311     // loop on the input parameter procResult, and only record some container tokens, such as
312     // class token, function token.
313     for (TokenIdxSet::const_iterator it = procResult.begin(); it != procResult.end(); ++it)
314     {
315         const Token* token = tree->at(*it);
316         if (!token)
317             continue;
318 
319         if (token->m_TokenKind == tkClass)
320             scopeResult.insert(*it);
321         else
322         {
323             if (token->m_TokenKind & tkAnyFunction && token->HasChildren()) // for local variable
324                 scopeResult.insert(*it);
325             scopeResult.insert(token->m_ParentIndex);
326         }
327 
328         if (s_DebugSmartSense)
329         {
330             const Token* parent = tree->at(token->m_ParentIndex);
331             CCLogger::Get()->DebugLog(_T("AI() Adding search namespace: ") +
332                                       (parent ? parent->m_Name : _T("Global namespace")));
333         }
334     }
335 
336     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
337 }
338 
CleanupSearchScope(TokenTree * tree,TokenIdxSet * searchScope)339 void NativeParserBase::CleanupSearchScope(TokenTree*   tree,
340                                           TokenIdxSet* searchScope)
341 {
342     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
343     // remove all the container tokens in the token index set
344     for (TokenIdxSet::const_iterator it = searchScope->begin(); it != searchScope->end();)
345     {
346         const Token* token = tree->at(*it);
347         if (!token || !(token->m_TokenKind & (tkNamespace | tkClass | tkTypedef | tkAnyFunction)))
348             searchScope->erase(it++);
349         else
350             ++it;
351     }
352 
353     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
354 
355     // ...but always search the global scope.
356     searchScope->insert(-1);
357 }
358 
359 // Set start and end for the calltip highlight region.
GetCallTipHighlight(const wxString & calltip,int * start,int * end,int typedCommas)360 void NativeParserBase::GetCallTipHighlight(const wxString& calltip,
361                                            int*            start,
362                                            int*            end,
363                                            int             typedCommas)
364 {
365     TRACE(_T("NativeParserBase::GetCallTipHighlight()"));
366 
367     int pos = 0;
368     int paramsCloseBracket = calltip.length() - 1;
369     int nest = 0;
370     int commas = 0;
371     *start = FindFunctionOpenParenthesis(calltip) + 1;
372     *end = 0;
373 
374     // for a function call tip string like below
375     // void f(int a, map<int, float>b)
376     // we have to take care the <> pair
377     while (true)
378     {
379         wxChar c = calltip.GetChar(pos++);
380         if (c == '\0')
381             break;
382         else if (c == '(')
383             ++nest;
384         else if (c == ')')
385         {
386             --nest;
387             if (nest == 0)
388                 paramsCloseBracket = pos - 1;
389         }
390         else if (c == ',' && nest == 1)
391         {
392             ++commas;
393             if (commas == typedCommas + 1)
394             {
395                 *end = pos - 1;
396                 return;
397             }
398             *start = pos;
399         }
400         else if (c =='<')
401             ++nest;
402         else if (c == '>')
403             --nest;
404     }
405     if (*end == 0)
406         *end = paramsCloseBracket;
407 }
408 
FindFunctionOpenParenthesis(const wxString & calltip)409 int NativeParserBase::FindFunctionOpenParenthesis(const wxString& calltip)
410 {
411     int nest = 0;
412     for (size_t i = calltip.length(); i > 0; --i)
413     {
414         wxChar c = calltip[i - 1];
415         if (c == wxT('('))
416         {
417             --nest;
418             if (nest == 0)
419             return i - 1;
420         }
421         else if (c == wxT(')'))
422             ++nest;
423     }
424     return -1;
425 }
426 
GetCCToken(wxString & line,ParserTokenType & tokenType,OperatorType & tokenOperatorType)427 wxString NativeParserBase::GetCCToken(wxString&        line,
428                                       ParserTokenType& tokenType,
429                                       OperatorType&    tokenOperatorType)
430 {
431     tokenType         = pttSearchText;
432     tokenOperatorType = otOperatorUndefined;
433     if (line.IsEmpty())
434         return wxEmptyString;
435 
436     tokenOperatorType = otOperatorUndefined;
437     unsigned int startAt = FindCCTokenStart(line);
438     wxString res = GetNextCCToken(line, startAt, tokenOperatorType);
439 
440     TRACE(_T("GetCCToken() : FindCCTokenStart returned %u \"%s\""), startAt, line.wx_str());
441     TRACE(_T("GetCCToken() : GetNextCCToken returned %u \"%s\""), startAt, res.wx_str());
442 
443 
444     if (startAt == line.Len())
445         line.Clear();
446     else
447     {
448         // skip whitespace
449         startAt = AfterWhitespace(startAt, line);
450 
451         // Check for [Class]. ('.' pressed)
452         if (IsOperatorDot(startAt, line))
453         {
454             tokenType = pttClass;
455             line.Remove(0, startAt + 1);
456         }
457         // Check for
458         // (1) "AAA->" ('>' pressed)
459         // (2) "AAA::" (':' pressed)
460         // If (1) and tokenOperatorType == otOperatorSquare, then we have
461         // special case "AAA[]->" and should not change tokenOperatorType.
462         else if (IsOperatorEnd(startAt, line))
463         {
464             if (    IsOperatorPointer(startAt, line)
465                  && !res.IsEmpty()
466                  && tokenOperatorType != otOperatorSquare)
467                 tokenOperatorType = otOperatorPointer;
468             if (line.GetChar(startAt) == ':')
469                 tokenType = pttNamespace;
470             else
471                 tokenType = pttClass;
472             line.Remove(0, startAt + 1);
473         }
474         else
475             line.Clear();
476     }
477 
478     TRACE(_T("GetCCToken() : Left \"%s\""), line.wx_str());
479 
480     if (tokenOperatorType == otOperatorParentheses)
481         tokenType = pttFunction;
482 
483     return res;
484 }
485 
486 // skip nest braces in the expression, e.g.
487 //  SomeObject->SomeMethod(arg1, arg2)->Method2()
488 //              ^end                 ^begin
489 // note we skip the nest brace (arg1, arg2).
FindCCTokenStart(const wxString & line)490 unsigned int NativeParserBase::FindCCTokenStart(const wxString& line)
491 {
492     // Careful: startAt can become negative, so it's defined as integer here!
493     int startAt = line.Len() - 1;
494     int nest    = 0;
495 
496     bool repeat = true;
497     while (repeat)
498     {
499         repeat = false;
500         // Go back to the beginning of the function/variable (token)
501         startAt = BeginOfToken(startAt, line);
502 
503         // Check for [Class]. ('.' pressed)
504         if (IsOperatorDot(startAt, line))
505         {
506             --startAt;
507             repeat = true; // yes -> repeat.
508         }
509         // Check for [Class]-> ('>' pressed)
510         // Check for [Class]:: (':' pressed)
511         else if (IsOperatorEnd(startAt, line))
512         {
513             startAt -= 2;
514             repeat = true; // yes -> repeat.
515         }
516 
517         if (repeat)
518         {
519             // now we're just before the "." or "->" or "::"
520             // skip any whitespace
521             startAt = BeforeWhitespace(startAt, line);
522 
523             // check for function/array/cast ()
524             if (IsClosingBracket(startAt, line))
525             {
526                 ++nest;
527                 while (   (--startAt >= 0)
528                        && (nest != 0) )
529                 {
530                     #if wxCHECK_VERSION(3, 0, 0)
531                     switch (line.GetChar(startAt).GetValue())
532                     #else
533                     switch (line.GetChar(startAt))
534                     #endif
535                     {
536                         case ']':
537                         case ')': ++nest; --startAt; break;
538 
539                         case '[':
540                         case '(': --nest; --startAt; break;
541                         default:
542                             break;
543                     }
544 
545                     startAt = BeforeWhitespace(startAt, line);
546 
547                     if (IsClosingBracket(startAt, line))
548                         ++nest;
549                     if (IsOpeningBracket(startAt, line))
550                         --nest;
551                 }
552 
553                 startAt = BeforeToken(startAt, line);
554             }
555         }
556     }
557     ++startAt;
558 
559     startAt = AfterWhitespace(startAt, line);
560 
561     TRACE(_T("FindCCTokenStart() : Starting at %u \"%s\""), startAt, line.Mid(startAt).wx_str());
562 
563     return startAt;
564 }
565 
GetNextCCToken(const wxString & line,unsigned int & startAt,OperatorType & tokenOperatorType)566 wxString NativeParserBase::GetNextCCToken(const wxString& line,
567                                           unsigned int&   startAt,
568                                           OperatorType&   tokenOperatorType)
569 {
570     wxString res;
571     int nest = 0;
572 
573     if (   (startAt < line.Len())
574         && (line.GetChar(startAt) == '(') )
575     {
576         while (   (startAt < line.Len())
577                && (   (line.GetChar(startAt) == '*')
578                    || (line.GetChar(startAt) == '&')
579                    || (line.GetChar(startAt) == '(') ) )
580         {
581             if (line.GetChar(startAt) == '(')
582                 ++nest;
583             if (line.GetChar(startAt) == _T('*'))
584                 tokenOperatorType = otOperatorStar;
585             ++startAt;
586         }
587     }
588 
589     TRACE(_T("GetNextCCToken() : at %u (%c): res=%s"), startAt, line.GetChar(startAt), res.wx_str());
590 
591     while (InsideToken(startAt, line))
592     {
593         res << line.GetChar(startAt);
594         ++startAt;
595     }
596     while (   (nest > 0)
597            && (startAt < line.Len()) )
598     {
599         // TODO: handle nested scope resolution (A::getC()).|
600         if (line.GetChar(startAt) == '(')
601             ++nest;
602         else if (line.GetChar(startAt) == ')')
603             --nest;
604         ++startAt;
605     }
606 
607     TRACE(_T("GetNextCCToken() : Done nest: at %u (%c): res=%s"), startAt, line.GetChar(startAt), res.wx_str());
608 
609     startAt = AfterWhitespace(startAt, line);
610     if (IsOpeningBracket(startAt, line))
611     {
612         if (line.GetChar(startAt) == _T('('))
613             tokenOperatorType = otOperatorParentheses;
614         else if (line.GetChar(startAt) == _T('['))
615             tokenOperatorType = otOperatorSquare;
616         ++nest;
617         while (   (startAt < line.Len()-1)
618                && (nest != 0) )
619         {
620             ++startAt;
621             #if wxCHECK_VERSION(3, 0, 0)
622             switch (line.GetChar(startAt).GetValue())
623             #else
624             switch (line.GetChar(startAt))
625             #endif
626             {
627                 case ']':
628                 case ')': --nest; ++startAt; break;
629 
630                 case '[':tokenOperatorType = otOperatorSquare;
631                 case '(': ++nest; ++startAt; break;
632                 default:
633                     break;
634             }
635 
636             startAt = AfterWhitespace(startAt, line);
637 
638             if (IsOpeningBracket(startAt, line))
639                 ++nest;
640             //NOTE: do not skip successive closing brackets. Eg,
641             // "GetConfigManager(_T("code_completion"))->ReadBool"
642             //                                        ^
643             if (IsClosingBracket(startAt, line))
644             {
645                 --nest;
646                 if (nest == 0)
647                     ++startAt;
648             }
649         }
650     }
651     if (IsOperatorBegin(startAt, line))
652         ++startAt;
653 
654     TRACE(_T("GetNextCCToken() : Return at %u (%c): res=%s"), startAt, line.GetChar(startAt), res.wx_str());
655 
656     return res;
657 }
658 
RemoveLastFunctionChildren(TokenTree * tree,int & lastFuncTokenIdx)659 void NativeParserBase::RemoveLastFunctionChildren(TokenTree* tree,
660                                                   int&       lastFuncTokenIdx)
661 {
662     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
663 
664     Token* token = tree->at(lastFuncTokenIdx);
665     if (token)
666     {
667         lastFuncTokenIdx = -1;
668         if (token->m_TokenKind & tkAnyFunction)
669             token->DeleteAllChildren();
670     }
671 
672     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
673 }
674 
675 // Breaks up the phrase for code-completion.
676 // Suppose the user has invoked code-completion in this piece of code:
677 //
678 //   Ogre::Root::getSingleton().|
679 //
680 // This function will break this up into an std::queue (FIFO) containing
681 // the following items (top is first-out):
682 //
683 // Ogre             [pttNamespace]
684 // Root             [pttClass]
685 // getSingleton     [pttFunction]
686 // (empty space)    [pttSearchText]
687 //
688 // It also classifies each component as a pttClass, pttNamespace, pttFunction, pttSearchText
BreakUpComponents(const wxString & actual,std::queue<ParserComponent> & components)689 size_t NativeParserBase::BreakUpComponents(const wxString&              actual,
690                                            std::queue<ParserComponent>& components)
691 {
692     ParserTokenType tokenType;
693     wxString statement = actual;
694     OperatorType tokenOperatorType;
695     // break up components of phrase
696     if (s_DebugSmartSense)
697         CCLogger::Get()->DebugLog(F(_T("BreakUpComponents() Breaking up '%s'"), statement.wx_str()));
698     TRACE(_T("NativeParserBase::BreakUpComponents()"));
699 
700     while (true)
701     {
702         wxString tok = GetCCToken(statement, tokenType, tokenOperatorType);
703 
704         ParserComponent pc;
705         pc.component         = tok;
706         pc.tokenType         = tokenType;
707         pc.tokenOperatorType = tokenOperatorType;
708         // Debug smart sense: output the component's name and type.
709         if (s_DebugSmartSense)
710         {
711             wxString tokenTypeString;
712             switch (tokenType)
713             {
714                 case (pttFunction):
715                 {   tokenTypeString = _T("Function");   break; }
716                 case (pttClass):
717                 {   tokenTypeString = _T("Class");      break; }
718                 case (pttNamespace):
719                 {   tokenTypeString = _T("Namespace");  break; }
720                 case (pttSearchText):
721                 {   tokenTypeString = _T("SearchText"); break; }
722                 case (pttUndefined):
723                 default:
724                 {   tokenTypeString = _T("Undefined");         }
725             }
726             CCLogger::Get()->DebugLog(F(_T("BreakUpComponents() Found component: '%s' (%s)"),
727                                         tok.wx_str(), tokenTypeString.wx_str()));
728         }
729 
730         // Support global namespace like ::MessageBoxA
731         // Break up into "", type is pttNameSpace and "MessageBoxA", type is pttSearchText.
732         // for pttNameSpace  type, if its text (tok) is empty -> ignore this component.
733         // for pttSearchText type, don't do this because for ss:: we need this, too.
734         if (!tok.IsEmpty() || (tokenType == pttSearchText && !components.empty()))
735         {
736             if (s_DebugSmartSense)
737                 CCLogger::Get()->DebugLog(F(_T("BreakUpComponents() Adding component: '%s'."), tok.wx_str()));
738             components.push(pc);
739         }
740 
741         if (tokenType == pttSearchText)
742             break;
743     }
744 
745     return 0;
746 }
747 
ResolveExpression(TokenTree * tree,std::queue<ParserComponent> components,const TokenIdxSet & searchScope,TokenIdxSet & result,bool caseSense,bool isPrefix)748 size_t NativeParserBase::ResolveExpression(TokenTree*                  tree,
749                                            std::queue<ParserComponent> components,
750                                            const TokenIdxSet&          searchScope,
751                                            TokenIdxSet&                result,
752                                            bool                        caseSense,
753                                            bool                        isPrefix)
754 {
755     m_TemplateMap.clear();
756     if (components.empty())
757         return 0;
758 
759     TokenIdxSet initialScope;
760     if (!searchScope.empty())
761         initialScope = searchScope;
762     else
763         initialScope.insert(-1);
764 
765     while (!components.empty())
766     {
767         TokenIdxSet initialResult;
768         ParserComponent subComponent = components.front();
769         components.pop();
770         wxString searchText = subComponent.component;
771         if (searchText == _T("this"))
772         {
773             initialScope.erase(-1);
774             TokenIdxSet tempInitialScope = initialScope;
775 
776             CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
777 
778             for (TokenIdxSet::const_iterator it = tempInitialScope.begin();
779                  it != tempInitialScope.end(); ++it)
780             {
781                 const Token* token = tree->at(*it);
782                 if (token && (token->m_TokenKind != tkClass))
783                     initialScope.erase(*it);
784             }
785 
786             CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
787 
788             if (!initialScope.empty())
789                 continue;
790             else
791             {
792                 CCLogger::Get()->DebugLog(F(_T("ResolveExpression() Error to find initial search scope.")));
793                 break; // error happened.
794             }
795         }
796 
797         if (s_DebugSmartSense)
798         {
799             CCLogger::Get()->DebugLog(F(_T("ResolveExpression() Search scope with %lu result:"),
800                                         static_cast<unsigned long>(initialScope.size())));
801             for (TokenIdxSet::const_iterator tt = initialScope.begin(); tt != initialScope.end(); ++tt)
802                 CCLogger::Get()->DebugLog(F(_T("- Search scope: %d"), (*tt)));
803         }
804 
805         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
806 
807         // All functions that call the recursive GenerateResultSet should already entered a critical section.
808 
809         // e.g. A.BB.CCC.DDDD|
810         if (components.empty()) // is the last component (DDDD)
811             GenerateResultSet(tree, searchText, initialScope, initialResult, caseSense, isPrefix);
812         else // case sensitive and full-match always (A / BB / CCC)
813             GenerateResultSet(tree, searchText, initialScope, initialResult, true, false);
814 
815         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
816 
817         // now we should clear the initialScope.
818         initialScope.clear();
819 
820         //-------------------------------------
821 
822         if (s_DebugSmartSense)
823             CCLogger::Get()->DebugLog(F(_T("ResolveExpression() Looping %lu result."),
824                                         static_cast<unsigned long>(initialResult.size())));
825 
826         //------------------------------------
827         if (!initialResult.empty())
828         {
829             bool locked = false;
830 
831             // loop all matches.
832             for (TokenIdxSet::const_iterator it = initialResult.begin(); it != initialResult.end(); ++it)
833             {
834                 const size_t id = (*it);
835                 wxString actualTypeStr;
836                 int parentIndex = -1;
837                 bool isFuncOrVar = false;
838 
839                 if (locked)
840                     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
841                 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
842                 locked = true;
843 
844                 const Token* token = tree->at(id);
845                 if (!token)
846                 {
847                     if (s_DebugSmartSense)
848                         CCLogger::Get()->DebugLog(F(_T("ResolveExpression() token is NULL?!")));
849                     continue;
850                 }
851 
852                 // TODO: we should deal with operators carefully.
853                 // it should work for class::/namespace::
854                 if (token->m_IsOperator && (m_LastComponent.tokenType != pttNamespace))
855                     continue;
856 
857                 if (s_DebugSmartSense)
858                     CCLogger::Get()->DebugLog(F(_T("ResolvExpression() Match:'%s(ID=%lu) : type='%s'"),
859                                                 token->m_Name.wx_str(),
860                                                 static_cast<unsigned long>(id),
861                                                 token->m_BaseType.wx_str()));
862 
863                 // recond the template map message here. hope it will work.
864                 // wxString tkname = token->m_Name;
865                 // wxArrayString tks = token->m_TemplateType;
866                 if (!token->m_TemplateMap.empty())
867                     m_TemplateMap = token->m_TemplateMap;
868 
869                 // if the token is a function/variable(i.e. is not a type)
870                 isFuncOrVar =   !searchText.IsEmpty()
871                              && (subComponent.tokenType != pttSearchText)
872                              && !token->m_BaseType.IsEmpty();
873                 if (isFuncOrVar)
874                 {
875                     actualTypeStr = token->m_BaseType;
876                     parentIndex = token->m_Index;
877                 }
878 
879                 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
880                 locked = false;
881 
882                 // handle it if the token is a function/variable(i.e. is not a type)
883                 if (isFuncOrVar)
884                 {
885                     TokenIdxSet actualTypeScope;
886                     if (searchScope.empty())
887                         actualTypeScope.insert(-1);
888                     else
889                     {
890                         // now collect the search scope for actual type of function/variable.
891                         CollectSearchScopes(searchScope, actualTypeScope, tree);
892 
893                         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
894 
895                         // now add the current token's parent scope;
896                         const Token* currentTokenParent = tree->at(parentIndex);
897                         while (true)
898                         {
899                             if (!currentTokenParent)
900                                 break;
901                             actualTypeScope.insert(currentTokenParent->m_Index);
902                             currentTokenParent = tree->at(currentTokenParent->m_ParentIndex);
903                         }
904 
905                         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
906                     }
907 
908                     // now get the tokens of variable/function.
909                     TokenIdxSet actualTypeResult;
910                     ResolveActualType(tree, actualTypeStr, actualTypeScope, actualTypeResult);
911                     if (!actualTypeResult.empty())
912                     {
913                         for (TokenIdxSet::const_iterator it2 = actualTypeResult.begin();
914                              it2 != actualTypeResult.end();
915                              ++it2)
916                         {
917                             initialScope.insert(*it2);
918 
919                             CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
920 
921                             const Token* typeToken = tree->at(*it2);
922                             if (typeToken && !typeToken->m_TemplateMap.empty())
923                                 m_TemplateMap = typeToken->m_TemplateMap;
924 
925                             CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
926 
927                             // and we need to add the template argument alias too.
928                             AddTemplateAlias(tree, *it2, actualTypeScope, initialScope);
929                         }
930                     }
931                     else // ok ,we search template container to check if type is template formal.
932                         ResolveTemplateMap(tree, actualTypeStr, actualTypeScope, initialScope);
933 
934                     continue;
935                 }
936 
937                 initialScope.insert(id);
938             }// for
939 
940             if (locked)
941                 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
942         }
943         else
944         {
945             initialScope.clear();
946             break;
947         }
948 
949         if (subComponent.tokenOperatorType != otOperatorUndefined)
950         {
951             TokenIdxSet operatorResult;
952             ResolveOperator(tree, subComponent.tokenOperatorType, initialScope, searchScope, operatorResult);
953             if (!operatorResult.empty())
954                 initialScope = operatorResult;
955         }
956         if (subComponent.tokenType != pttSearchText)
957             m_LastComponent = subComponent;
958     }// while
959 
960     // initialScope contains all the matching tokens after the cascade matching algorithm
961     if (!initialScope.empty())
962     {
963         // normally, tokens have hierarchies. E.g. a constructor token is a child of a class token.
964         // but here we promote (expose) the constructor tokens to the user. if a Token in initialScope
965         // is a class, we add all its public constructors to the results, this give us a chance to let
966         // CC jump to the declaration of a constructor, see
967         // http://forums.codeblocks.org/index.php/topic,13753.msg92654.html#msg92654
968         AddConstructors(tree, initialScope, result);
969     }
970 
971     return result.size();
972 }
973 
AddConstructors(TokenTree * tree,const TokenIdxSet & source,TokenIdxSet & dest)974 void NativeParserBase::AddConstructors(TokenTree *tree, const TokenIdxSet& source, TokenIdxSet& dest)
975 {
976     for (TokenIdxSet::iterator It = source.begin(); It != source.end(); ++It)
977     {
978         const Token* token = tree->at(*It);
979         if (!token)
980             continue;
981         dest.insert(*It);
982 
983         // add constructors of the class type token
984         if (token->m_TokenKind == tkClass)
985         {
986             // loop on its children, add its public constructors
987             for (TokenIdxSet::iterator chIt = token->m_Children.begin();
988                  chIt != token->m_Children.end();
989                  ++chIt)
990             {
991                 const Token* tk = tree->at(*chIt);
992                 if (   tk && (   tk->m_TokenKind == tkConstructor
993                               || (tk->m_IsOperator && tk->m_Name.EndsWith(wxT("()"))) )
994                     && (tk->m_Scope == tsPublic || tk->m_Scope == tsUndefined) )
995                 {
996                     dest.insert(*chIt);
997                 }
998             }
999         }
1000     }
1001 }
1002 
ResolveOperator(TokenTree * tree,const OperatorType & tokenOperatorType,const TokenIdxSet & tokens,const TokenIdxSet & searchScope,TokenIdxSet & result)1003 void NativeParserBase::ResolveOperator(TokenTree*          tree,
1004                                        const OperatorType& tokenOperatorType,
1005                                        const TokenIdxSet&  tokens,
1006                                        const TokenIdxSet&  searchScope,
1007                                        TokenIdxSet&        result)
1008 {
1009     if (!tree || searchScope.empty())
1010         return;
1011 
1012     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1013 
1014     // first,we need to eliminate the tokens which are not tokens.
1015     TokenIdxSet opInitialScope;
1016     for (TokenIdxSet::const_iterator it=tokens.begin(); it!=tokens.end(); ++it)
1017     {
1018         int id = (*it);
1019         const Token* token = tree->at(id);
1020         if (token && (token->m_TokenKind == tkClass || token->m_TokenKind == tkTypedef))
1021             opInitialScope.insert(id);
1022     }
1023 
1024     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1025 
1026     // if we get nothing, just return.
1027     if (opInitialScope.empty())
1028         return;
1029 
1030     wxString operatorStr;
1031     switch(tokenOperatorType)
1032     {
1033         case otOperatorParentheses:
1034             operatorStr = _T("operator()"); break;
1035         case otOperatorSquare:
1036             operatorStr = _T("operator[]"); break;
1037         case otOperatorPointer:
1038             operatorStr = _T("operator->"); break;
1039         case otOperatorStar:
1040             operatorStr = _T("operator*"); break;
1041         case otOperatorUndefined:
1042         default:
1043             break;
1044     }
1045     if (operatorStr.IsEmpty())
1046         return;
1047 
1048     //s tart to parse the operator overload actual type.
1049     TokenIdxSet opInitialResult;
1050 
1051     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1052 
1053     // All functions that call the recursive GenerateResultSet should already entered a critical section.
1054     GenerateResultSet(tree, operatorStr, opInitialScope, opInitialResult);
1055 
1056     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1057 
1058     CollectSearchScopes(searchScope, opInitialScope, tree);
1059 
1060     if (opInitialResult.empty())
1061         return;
1062 
1063     for (TokenIdxSet::const_iterator it=opInitialResult.begin(); it!=opInitialResult.end(); ++it)
1064     {
1065         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1066 
1067         wxString type;
1068         const Token* token = tree->at((*it));
1069         if (token)
1070             type = token->m_BaseType;
1071 
1072         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1073 
1074         if (type.IsEmpty())
1075             continue;
1076 
1077         TokenIdxSet typeResult;
1078         ResolveActualType(tree, type, opInitialScope, typeResult);
1079         if (!typeResult.empty())
1080         {
1081             for (TokenIdxSet::const_iterator pTypeResult = typeResult.begin();
1082                  pTypeResult!=typeResult.end();
1083                  ++pTypeResult)
1084             {
1085                 result.insert(*pTypeResult);
1086                 AddTemplateAlias(tree, *pTypeResult, opInitialScope, result);
1087             }
1088         }
1089         else
1090             ResolveTemplateMap(tree, type, opInitialScope, result);
1091     }
1092 }
1093 
ResolveActualType(TokenTree * tree,wxString searchText,const TokenIdxSet & searchScope,TokenIdxSet & result)1094 size_t NativeParserBase::ResolveActualType(TokenTree*         tree,
1095                                            wxString           searchText,
1096                                            const TokenIdxSet& searchScope,
1097                                            TokenIdxSet&       result)
1098 {
1099     // break up the search text for next analysis.
1100     std::queue<ParserComponent> typeComponents;
1101     BreakUpComponents(searchText, typeComponents);
1102     if (!typeComponents.empty())
1103     {
1104         TokenIdxSet initialScope;
1105         if (!searchScope.empty())
1106             initialScope = searchScope;
1107         else
1108             initialScope.insert(-1);
1109 
1110         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1111 
1112         while (!typeComponents.empty())
1113         {
1114             TokenIdxSet initialResult;
1115             ParserComponent component = typeComponents.front();
1116             typeComponents.pop();
1117             wxString actualTypeStr = component.component;
1118 
1119             // All functions that call the recursive GenerateResultSet should already entered a critical section.
1120             GenerateResultSet(tree, actualTypeStr, initialScope, initialResult, true, false, 0xFFFF);
1121 
1122             if (!initialResult.empty())
1123             {
1124                 initialScope.clear();
1125                 for (TokenIdxSet::const_iterator it = initialResult.begin(); it != initialResult.end(); ++it)
1126                     // TODO (Morten#1#): eliminate the variable/function
1127                     initialScope.insert(*it);
1128             }
1129             else
1130             {
1131                 initialScope.clear();
1132                 break;
1133             }
1134         }
1135 
1136         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1137 
1138         if (!initialScope.empty())
1139             result = initialScope;
1140     }
1141 
1142     return result.size();
1143 }
1144 
ResolveTemplateMap(TokenTree * tree,const wxString & searchStr,const TokenIdxSet & actualTypeScope,TokenIdxSet & initialScope)1145 void NativeParserBase::ResolveTemplateMap(TokenTree*         tree,
1146                                           const wxString&    searchStr,
1147                                           const TokenIdxSet& actualTypeScope,
1148                                           TokenIdxSet&       initialScope)
1149 {
1150     if (actualTypeScope.empty())
1151         return;
1152 
1153     wxString actualTypeStr = searchStr;
1154     std::map<wxString, wxString>::const_iterator it = m_TemplateMap.find(actualTypeStr);
1155     if (it != m_TemplateMap.end())
1156     {
1157         actualTypeStr = it->second;
1158         TokenIdxSet actualTypeResult;
1159         ResolveActualType(tree, actualTypeStr, actualTypeScope, actualTypeResult);
1160         if (!actualTypeResult.empty())
1161         {
1162             for (TokenIdxSet::const_iterator it2=actualTypeResult.begin(); it2!=actualTypeResult.end(); ++it2)
1163                 initialScope.insert(*it2);
1164         }
1165     }
1166 }
1167 
AddTemplateAlias(TokenTree * tree,const int & id,const TokenIdxSet & actualTypeScope,TokenIdxSet & initialScope)1168 void NativeParserBase::AddTemplateAlias(TokenTree*         tree,
1169                                         const int&         id,
1170                                         const TokenIdxSet& actualTypeScope,
1171                                         TokenIdxSet&       initialScope)
1172 {
1173     if (!tree || actualTypeScope.empty())
1174         return;
1175 
1176     // and we need to add the template argument alias too.
1177     wxString actualTypeStr;
1178 
1179     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1180 
1181     const Token* typeToken = tree->at(id);
1182     if (typeToken &&  typeToken->m_TokenKind == tkTypedef
1183                   && !typeToken->m_TemplateAlias.IsEmpty() )
1184         actualTypeStr = typeToken->m_TemplateAlias;
1185 
1186     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1187 
1188     std::map<wxString, wxString>::const_iterator it = m_TemplateMap.find(actualTypeStr);
1189     if (it != m_TemplateMap.end())
1190     {
1191         actualTypeStr = it->second;
1192 
1193         if (actualTypeStr.Last() == _T('&') || actualTypeStr.Last() == _T('*'))
1194             actualTypeStr.RemoveLast();
1195 
1196         TokenIdxSet actualTypeResult;
1197         ResolveActualType(tree, actualTypeStr, actualTypeScope, actualTypeResult);
1198         if (!actualTypeResult.empty())
1199         {
1200             for (TokenIdxSet::const_iterator it2 = actualTypeResult.begin(); it2 != actualTypeResult.end(); ++it2)
1201                 initialScope.insert(*it2);
1202         }
1203     }
1204 }
1205 
1206 // No critical section needed in this recursive function!
1207 // All functions that call this recursive function, should already entered a critical section.
1208 //
1209 // Here are sample algorithm, if we have code snippet
1210 //
1211 // class AAA { public: void f1(); int m_aaa;};
1212 // class BBB : public AAA {public: void f2(); int m_bbb;};
1213 // BBB obj;
1214 // obj.f|-------CC Here, we expect "f1" and "f2" be prompt
1215 //
1216 // Here, we do a search with the following states:
1217 // the search text target = "f", and isPrefix = true
1218 // parentIdx points to the class type Token "BBB"
1219 // caseSens could be true, kindMask could be to list any function kind tokens.
1220 //
1221 // First, we first list the children of BBB, there are two "f1", and "m_aaa", and text match should
1222 // only find one "f1".
1223 // Second, we try to calculate all the ancestors of BBB, which we see a class type Token "AAA"
1224 // list the children of AAA, and do a text match, we find "f2"
1225 // Last, we return a Token set which contains two elements ["f1", "f2"]
1226 //
1227 // Note that parentIdx could be the global namespace
1228 // Some kinds of Token types, such as Enum or Namespaces could be handled specially
GenerateResultSet(TokenTree * tree,const wxString & target,int parentIdx,TokenIdxSet & result,bool caseSens,bool isPrefix,short int kindMask)1229 size_t NativeParserBase::GenerateResultSet(TokenTree*      tree,
1230                                            const wxString& target,
1231                                            int             parentIdx,
1232                                            TokenIdxSet&    result,
1233                                            bool            caseSens,
1234                                            bool            isPrefix,
1235                                            short int       kindMask)
1236 {
1237     TRACE(_T("NativeParserBase::GenerateResultSet_1()"));
1238 
1239     Token* parent = tree->at(parentIdx);
1240     if (s_DebugSmartSense)
1241         CCLogger::Get()->DebugLog(F(_("GenerateResultSet() search '%s', parent='%s (id:%d, type:%s), isPrefix=%d'"),
1242                                     target.wx_str(),
1243                                     parent ? parent->m_Name.wx_str() : wxString(_T("Global namespace")).wx_str(),
1244                                     parent ? parent->m_Index : 0,
1245                                     parent ? parent->GetTokenKindString().wx_str() : 0,
1246                                     isPrefix ? 1 : 0));
1247 
1248     // parent == null means we are searching in the global scope
1249     if (parent)
1250     {
1251         // we got a parent; add its children
1252         for (TokenIdxSet::const_iterator it = parent->m_Children.begin(); it != parent->m_Children.end(); ++it)
1253         {
1254             const Token* token = tree->at(*it);
1255             if (token && MatchType(token->m_TokenKind, kindMask))
1256             {
1257                 if (MatchText(token->m_Name, target, caseSens, isPrefix))
1258                     result.insert(*it);
1259                 else if (token && token->m_TokenKind == tkNamespace && token->m_Aliases.size()) // handle namespace aliases
1260                 {
1261                     for (size_t i = 0; i < token->m_Aliases.size(); ++i)
1262                     {
1263                         if (MatchText(token->m_Aliases[i], target, caseSens, isPrefix))
1264                         {
1265                             result.insert(*it);
1266                             // break; ?
1267                         }
1268                     }
1269                 }
1270                 else if (token && token->m_TokenKind == tkEnum) // check enumerators for match too
1271                     // All functions that call the recursive GenerateResultSet should already entered a critical section.
1272                     GenerateResultSet(tree, target, *it, result, caseSens, isPrefix, kindMask);
1273             }
1274         }
1275         // now go up the inheritance chain and add all ancestors' children too
1276         tree->RecalcInheritanceChain(parent);
1277         for (TokenIdxSet::const_iterator it = parent->m_Ancestors.begin(); it != parent->m_Ancestors.end(); ++it)
1278         {
1279             const Token* ancestor = tree->at(*it);
1280             if (!ancestor)
1281                 continue;
1282             for (TokenIdxSet::const_iterator it2 = ancestor->m_Children.begin(); it2 != ancestor->m_Children.end(); ++it2)
1283             {
1284                 const Token* token = tree->at(*it2);
1285                 if (token && MatchType(token->m_TokenKind, kindMask))
1286                 {
1287                     if (MatchText(token->m_Name, target, caseSens, isPrefix))
1288                         result.insert(*it2);
1289                     else if (token && token->m_TokenKind == tkNamespace && token->m_Aliases.size()) // handle namespace aliases
1290                     {
1291                         for (size_t i = 0; i < token->m_Aliases.size(); ++i)
1292                         {
1293                             if (MatchText(token->m_Aliases[i], target, caseSens, isPrefix))
1294                             {
1295                                 result.insert(*it2);
1296                                 // break; ?
1297                             }
1298                         }
1299                     }
1300                     else if (token && token->m_TokenKind == tkEnum) // check enumerators for match too
1301                         // All functions that call the recursive GenerateResultSet should already entered a critical section.
1302                         GenerateResultSet(tree, target, *it2, result, caseSens, isPrefix, kindMask);
1303                 }
1304             }
1305         }
1306     }
1307     else
1308     {
1309         // all global tokens
1310         const TokenList* tl = tree->GetTokens();
1311         for (TokenList::const_iterator it = tl->begin(); it != tl->end(); ++it)
1312         {
1313             const Token* token = *it;
1314             if (token && token->m_ParentIndex == -1)
1315             {
1316                 if (token && MatchType(token->m_TokenKind, kindMask))
1317                 {
1318                     if (MatchText(token->m_Name, target, caseSens, isPrefix))
1319                         result.insert(token->m_Index);
1320                     else if (token && token->m_TokenKind == tkNamespace && token->m_Aliases.size()) // handle namespace aliases
1321                     {
1322                         for (size_t i = 0; i < token->m_Aliases.size(); ++i)
1323                         {
1324                             if (MatchText(token->m_Aliases[i], target, caseSens, isPrefix))
1325                             {
1326                                 result.insert(token->m_Index);
1327                                 // break; ?
1328                             }
1329                         }
1330                     }
1331                     else if (token && token->m_TokenKind == tkEnum) // check enumerators for match too
1332                         // All functions that call the recursive GenerateResultSet should already entered a critical section.
1333                         GenerateResultSet(tree, target, token->m_Index, result, caseSens, isPrefix, kindMask);
1334                 }
1335             }
1336         }
1337     }
1338 
1339     // done
1340     return result.size();
1341 }
1342 
1343 // No critical section needed in this recursive function!
1344 // All functions that call this recursive function, should already entered a critical section.
1345 /** detailed description
1346  * we have special handling of the c++ stl container with some template related code:
1347  * example:
1348  *
1349  * vector<string> AAA;
1350  * AAA.back().     // should display tokens of string members
1351  *
1352  * Cause of problem:
1353  *
1354  * The root of the problem is that CC can't parse some pieces of the C++ library. STL containers have
1355  * return types like "const_reference", which users of the library can treat as typedef aliases of
1356  * their template arguments.
1357  *
1358  * E.g. "back()" returns "const_reference", which we can assume to
1359  *     be a typedef alias of "const string&"
1360  *
1361  * However, these return types are actually defined through a complicated chain of typedefs,
1362  * templates, and inheritance. For example, the C++ library defines "const_reference" in vector
1363  * as a typedef alias of "_Alloc_traits::const_reference", which is a typedef alias of
1364  * "__gnu_cxx::_alloc_traits<_Tp_alloc_type>::const_reference". This chain actually continues, but
1365  * it would be too much to list here. The main thing to understand is that CC will not be able to
1366  * figure out what "const_reference" is supposed to be.
1367  *
1368  * Solution:
1369  *
1370  * Trying get CC to understand this chain would have made the template code even more complicated
1371  * and error-prone, so I used a trick. STL containers are based on the allocator class. And the
1372  * allocator class contains all the simple typedefs we need. For example, in the allocator class we
1373  * find the definition of const_reference:
1374  *
1375  * typedef const _Tp&   const_reference
1376  *
1377  * Where _Tp is a template parameter. Here's another trick - because most STL containers use the
1378  * name "_Tp" for their template parameter, the above definition can be directly applied to these
1379  * containers.
1380  *
1381  * E.g. vector is defined as:
1382  *         template <typename _Tp>
1383  *         vector { ... }
1384  * So AAA's template map will connect "_Tp" to "string".
1385  *
1386  * So we can look up "const_reference" in allocator, see that it returns "_Tp", look up "_Tp" in
1387  * the template map and add its actual value to the search scope.
1388  *
1389  * Walking through the example:
1390  *
1391  * [in NativeParserBase::GenerateResultSet()]
1392  * CC sees that back() returns const_reference. It searches the TokenTree for const_reference and
1393  * finds the typedef belonging to allocator:
1394  *
1395  * "typedef const _Tp&   const_reference"
1396  *
1397  * CC then checks that back()'s parent, AAA, is an STL container which relies on allocator. Since it
1398  * is, this typedef is added to the search scope.
1399  *
1400  * [in NativeParserBase::AddTemplateAlias()]
1401  * CC sees the typedef in the search scope. It searches AAA's template map for the actual type of
1402  * "_Tp" and finds "string". So "string" is added to the scope.
1403  *
1404  * That's the big picture. The negative of this patch is that it relies on how the C++ STL library
1405  * is written. If the library is ever changed significantly, then this patch will need to be updated.
1406  * It was an ok sacrifice to make for cleaner, maintainable code.
1407  */
GenerateResultSet(TokenTree * tree,const wxString & target,const TokenIdxSet & parentSet,TokenIdxSet & result,bool caseSens,bool isPrefix,cb_unused short int kindMask)1408 size_t NativeParserBase::GenerateResultSet(TokenTree*          tree,
1409                                            const wxString&     target,
1410                                            const TokenIdxSet&  parentSet,
1411                                            TokenIdxSet&        result,
1412                                            bool                caseSens,
1413                                            bool                isPrefix,
1414                                            cb_unused short int kindMask)
1415 {
1416     if (!tree) return 0;
1417 
1418     TRACE(_T("NativeParserBase::GenerateResultSet_2()"));
1419 
1420     if (target.IsEmpty())
1421     {
1422         for (TokenIdxSet::const_iterator ptr = parentSet.begin(); ptr != parentSet.end(); ++ptr)
1423         {
1424             size_t parentIdx = (*ptr);
1425             Token* parent = tree->at(parentIdx);
1426             if (!parent)
1427                 continue;
1428 
1429             for (TokenIdxSet::const_iterator it = parent->m_Children.begin();
1430                  it != parent->m_Children.end();
1431                  ++it)
1432             {
1433                 const Token* token = tree->at(*it);
1434                 if (!token)
1435                     continue;
1436                 if ( !AddChildrenOfUnnamed(tree, token, result) )
1437                 {
1438                     result.insert(*it);
1439                     AddChildrenOfEnum(tree, token, result);
1440                 }
1441             }
1442 
1443             tree->RecalcInheritanceChain(parent);
1444 
1445             for (TokenIdxSet::const_iterator it = parent->m_Ancestors.begin();
1446                  it != parent->m_Ancestors.end();
1447                  ++it)
1448             {
1449                 const Token* ancestor = tree->at(*it);
1450                 if (!ancestor)
1451                     continue;
1452                 for (TokenIdxSet::const_iterator it2 = ancestor->m_Children.begin();
1453                      it2 != ancestor->m_Children.end();
1454                      ++it2)
1455                 {
1456                     const Token* token = tree->at(*it2);
1457                     if (!token)
1458                         continue;
1459                     if ( !AddChildrenOfUnnamed(tree, token, result) )
1460                     {
1461                         result.insert(*it2);
1462                         AddChildrenOfEnum(tree, token, result);
1463                     }
1464                 }
1465             }
1466         }
1467     }
1468     else
1469     {
1470         // we use FindMatches to get the items from tree directly and eliminate the
1471         // items which are not under the search scope.
1472         TokenIdxSet textMatchSet, tmpMatches;
1473         if (tree->FindMatches(target, tmpMatches, caseSens, isPrefix))
1474         {
1475             TokenIdxSet::const_iterator it;
1476             for (it = tmpMatches.begin(); it != tmpMatches.end(); ++it)
1477             {
1478                 const Token* token = tree->at(*it);
1479                 if (token)
1480                     textMatchSet.insert(*it);
1481             }
1482         }
1483         // eliminate the tokens.
1484         if (!textMatchSet.empty())
1485         {
1486             TRACE(_T("Find %lu valid text matched tokens from the tree."),
1487                   static_cast<unsigned long>(textMatchSet.size()));
1488 
1489             // get the tokens under the search scope. Note: tokens can have the same names, but we are
1490             // only interests those under the search scope, here the search scope is the parentSet,
1491             // So, the outer loop is the parentSet, which is the search scope
1492             for (TokenIdxSet::const_iterator parentIterator = parentSet.begin();
1493                  parentIterator != parentSet.end();
1494                  ++parentIterator)
1495             {
1496                 // to make it clear, parentIdx stands for search scope. (Token Idx)
1497                 // (*it) stand for matched item id.
1498                 int parentIdx = (*parentIterator);
1499 
1500                 // The inner loop is the textMatchSet
1501                 for (TokenIdxSet::const_iterator it = textMatchSet.begin();
1502                      it != textMatchSet.end();
1503                      ++it)
1504                 {
1505                     const Token* token = tree->at(*it);
1506                     // check whether its under the parentIdx
1507                     // NOTE: check for unnamed or enum inside class.
1508                     // eg, 'ParserCommon::ParserState::ptCreateParser' should be accessed as
1509                     // 'ParserCommon::ptCreateParser'.
1510                     // Here, token is ptCreateParser and parentIdx is ParserCommon, so
1511                     // 'token->m_ParentIndex == parentIdx' is false. Now, we iterate over the
1512                     // children of parentIdx and check if any of them is unnamed or enum
1513                     // and match with token->m_ParentIndex. Thus if we confirm that 'token' is a
1514                     // child of unnamed or enum(i.e., m_ParentIndex), we add the token to result.
1515                     if (token && ((token->m_ParentIndex == parentIdx)
1516                               || IsChildOfUnnamedOrEnum(tree, token->m_ParentIndex, parentIdx)))
1517                         result.insert(*it);
1518 
1519                     // "result" will become the search scope for the next loop, so
1520                     // if the parentIdx has ancestors, we need to add them too.
1521                     if (parentIdx != -1) //global namespace does not have ancestors
1522                     {
1523                         Token* tokenParent = tree->at(parentIdx);
1524                         if (tokenParent)
1525                         {
1526                             // Here, we are going to calculate all tk's ancestors
1527                             // Finally we will add them in the "result".
1528                             tree->RecalcInheritanceChain(tokenParent);
1529 
1530                             // This is somewhat tricky, for example, we have one tk, which has the
1531                             // tk->m_AncestorsString == wxNavigationEnabled<wxWindow>
1532                             // Normally, the wxNavigationEnabled will be added, but if we have:
1533                             // template <class W>
1534                             // class wxNavigationEnabled : public W
1535                             // Shall we add the "W" as tk's ancestors? W is a formalTemplateArgument
1536 
1537                             // Add tk's ancestors
1538                             for ( TokenIdxSet::const_iterator ancestorIterator = tokenParent->m_Ancestors.begin();
1539                                   ancestorIterator != tokenParent->m_Ancestors.end();
1540                                   ++ancestorIterator )
1541                             {
1542                                 // NOTE: check for unnamed or enum inside class (see note above).
1543                                 if (token && ((token->m_ParentIndex == (*ancestorIterator)) //matched
1544                                           || IsChildOfUnnamedOrEnum(tree, token->m_ParentIndex, (*ancestorIterator))))
1545                                     result.insert(*it);
1546                             }
1547                         }
1548                     }
1549                     else if (-1 == parentIdx)
1550                     {
1551                         //if the search scope is global,and the token's parent token kind is tkEnum ,we add them too.
1552                         const Token* parentToken = tree->at(token->m_ParentIndex);
1553                         if (parentToken && parentToken->m_TokenKind == tkEnum)
1554                             result.insert(*it);
1555                     }
1556 
1557                     // Check if allocator class tokens should be added to the search scope.
1558                     // allocator holds the typedefs that CC needs to handle STL containers.
1559                     // An allocator token will be added if parentIdx is a child of an allocator dependent class.
1560                     // Most STL containers are dependent on allocator.
1561                     //
1562                     // For example, suppose we are completing:
1563                     //     vector<string> AAA;
1564                     //     AAA.back().
1565                     //
1566                     // Here, parentIdx == "back()", which is a child of the allocator dependent class, vector.
1567                     // So we add (*it) to the search scope if it is an allocator token.
1568                     if (token && IsAllocator(tree, token->m_ParentIndex)
1569                               && DependsOnAllocator(tree, parentIdx))
1570                         result.insert(*it);
1571                 }
1572             }
1573         }
1574         else
1575         {
1576             // We need to handle namespace aliases too. I hope we can find a good way to do this.
1577             // TODO: Handle template class here.
1578             if (parentSet.count(-1))
1579             {
1580                 const TokenList* tl = tree->GetTokens();
1581                 for (TokenList::const_iterator it = tl->begin(); it != tl->end(); ++it)
1582                 {
1583                     const Token* token = (*it);
1584                     if (token && token->m_TokenKind == tkNamespace && token->m_Aliases.size())
1585                     {
1586                         for (size_t i = 0; i < token->m_Aliases.size(); ++i)
1587                         {
1588                             if (token->m_Aliases[i] == target)
1589                             {
1590                                 result.insert(token->m_Index);
1591                                 // break; ?
1592                             }
1593                         }
1594                     }
1595                 }
1596             }
1597         }
1598     }
1599 
1600     return result.size();
1601 }
1602 
1603 // No critical section needed in this function!
1604 // All functions that call this function, should already entered a critical section.
IsAllocator(TokenTree * tree,const int & id)1605 bool NativeParserBase::IsAllocator(TokenTree*   tree,
1606                                    const int&   id)
1607 {
1608     if (!tree)
1609         return false;
1610 
1611     const Token* token = tree->at(id);
1612     return (token && token->m_Name.IsSameAs(_T("allocator")));
1613 }
1614 
1615 // No critical section needed in this recursive function!
1616 // All functions that call this recursive function, should already entered a critical section.
1617 //
1618 // Currently, this function only identifies STL containers dependent on allocator.
DependsOnAllocator(TokenTree * tree,const int & id)1619 bool NativeParserBase::DependsOnAllocator(TokenTree*    tree,
1620                                           const int&    id)
1621 {
1622     if (!tree)
1623         return false;
1624 
1625     const Token* token = tree->at(id);
1626     if (!token)
1627         return false;
1628 
1629     // If the STL class depends on allocator, it will have the form:
1630     // template <typename T, typename _Alloc = std::allocator<T> > class AAA { ... };
1631     if (token->m_TemplateArgument.Find(_T("_Alloc")) != wxNOT_FOUND)
1632         return true;
1633 
1634     // The STL class could also be a container adapter:
1635     // template <typename T, typename _Sequence = AAA<T> > class BBB { ... };
1636     // where AAA depends on allocator.
1637     if (token->m_TemplateArgument.Find(_T("_Sequence")) != wxNOT_FOUND)
1638         return true;
1639 
1640     return DependsOnAllocator(tree, token->m_ParentIndex);
1641 }
1642 
CollectSearchScopes(const TokenIdxSet & searchScope,TokenIdxSet & actualTypeScope,TokenTree * tree)1643 void NativeParserBase::CollectSearchScopes(const TokenIdxSet& searchScope,
1644                                            TokenIdxSet&       actualTypeScope,
1645                                            TokenTree*         tree)
1646 {
1647     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1648 
1649     for (TokenIdxSet::const_iterator pScope=searchScope.begin(); pScope!=searchScope.end(); ++pScope)
1650     {
1651         actualTypeScope.insert(*pScope);
1652         // we need to pScope's parent scope too.
1653         if ((*pScope) != -1)
1654         {
1655             const Token* token = tree->at(*pScope);
1656             if (!token)
1657                 continue;
1658             const Token* parent = tree->at(token->m_ParentIndex);
1659             while (true)
1660             {
1661                 if (!parent)
1662                     break;
1663                 actualTypeScope.insert(parent->m_Index);
1664                 parent = tree->at(parent->m_ParentIndex);
1665             }
1666         }
1667     }
1668 
1669     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1670 }
1671 
1672 // No critical section needed in this recursive function!
1673 // All functions that call this function, should already entered a critical section.
GetTokenFromCurrentLine(TokenTree * tree,const TokenIdxSet & tokens,size_t curLine,const wxString & file)1674 int NativeParserBase::GetTokenFromCurrentLine(TokenTree*         tree,
1675                                               const TokenIdxSet& tokens,
1676                                               size_t             curLine,
1677                                               const wxString&    file)
1678 {
1679     TRACE(_T("NativeParserBase::GetTokenFromCurrentLine()"));
1680 
1681     int result = -1;
1682     bool found = false;
1683     if (!tree)
1684         return result;
1685 
1686     const size_t fileIdx = tree->InsertFileOrGetIndex(file);
1687     const Token* classToken = nullptr;
1688     for (TokenIdxSet::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
1689     {
1690         const Token* token = tree->at(*it);
1691         if (!token)
1692             continue;
1693 
1694         TRACE(_T("GetTokenFromCurrentLine() Iterating: tN='%s', tF='%s', tStart=%u, tEnd=%u"),
1695               token->DisplayName().wx_str(), token->GetFilename().wx_str(),
1696               token->m_ImplLineStart, token->m_ImplLineEnd);
1697 
1698         if (   token->m_TokenKind & tkAnyFunction
1699             && token->m_ImplFileIdx == fileIdx
1700             && token->m_ImplLine    <= curLine
1701             && token->m_ImplLineEnd >= curLine)
1702         {
1703             TRACE(_T("GetTokenFromCurrentLine() tkAnyFunction : tN='%s', tF='%s', tStart=%u, tEnd=%u"),
1704                    token->DisplayName().wx_str(), token->GetFilename().wx_str(),
1705                    token->m_ImplLineStart, token->m_ImplLineEnd);
1706             result = token->m_Index;
1707             found = true;
1708         }
1709         else if (   token->m_TokenKind == tkConstructor
1710                  && token->m_ImplFileIdx == fileIdx
1711                  && token->m_ImplLine <= curLine
1712                  && token->m_ImplLineStart >= curLine)
1713         {
1714             TRACE(_T("GetTokenFromCurrentLine() tkConstructor : tN='%s', tF='%s', tStart=%u, tEnd=%u"),
1715                   token->DisplayName().wx_str(), token->GetFilename().wx_str(),
1716                   token->m_ImplLineStart, token->m_ImplLineEnd);
1717             result = token->m_Index;
1718             found = true;
1719         }
1720         else if (   token->m_TokenKind == tkClass
1721                  && token->m_ImplLineStart <= curLine
1722                  && token->m_ImplLineEnd >= curLine)
1723         {
1724             TRACE(_T("GetTokenFromCurrentLine() tkClass : tN='%s', tF='%s', tStart=%u, tEnd=%u"),
1725                   token->DisplayName().wx_str(), token->GetFilename().wx_str(),
1726                   token->m_ImplLineStart, token->m_ImplLineEnd);
1727             classToken = token;
1728             continue;
1729         }
1730 
1731         if (found) break; // exit for-loop
1732 
1733         TRACE(_T("GetTokenFromCurrentLine() Function out of bounds: tN='%s', tF='%s', tStart=%u, ")
1734               _T("tEnd=%u, line=%lu (size_t)line=%lu"), token->DisplayName().wx_str(),
1735               token->GetFilename().wx_str(), token->m_ImplLineStart, token->m_ImplLineEnd,
1736               static_cast<unsigned long>(curLine), static_cast<unsigned long>(curLine));
1737     }
1738 
1739     if (classToken)
1740         result = classToken->m_Index;
1741 
1742     return result;
1743 }
1744 
ComputeCallTip(TokenTree * tree,const TokenIdxSet & tokens,wxArrayString & items)1745 void NativeParserBase::ComputeCallTip(TokenTree*         tree,
1746                                       const TokenIdxSet& tokens,
1747                                       wxArrayString&     items)
1748 {
1749     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1750 
1751     for (TokenIdxSet::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
1752     {
1753         const Token* token = tree->at(*it);
1754         if (!token)
1755             continue;
1756 
1757         // support constructor call tips
1758         if (token->m_TokenKind == tkVariable)
1759         {
1760             TokenIdxSet classes;
1761             tree->FindMatches(token->m_BaseType, classes, true, false, tkClass);
1762             for (TokenIdxSet::const_iterator clIt = classes.begin(); clIt != classes.end(); ++clIt)
1763             {
1764                 const Token* tk = tree->at(*clIt);
1765                 if (tk)
1766                 {
1767                     token = tk;
1768                     break;
1769                 }
1770             }
1771         }
1772         if (token->m_TokenKind == tkClass)
1773         {
1774             for (TokenIdxSet::iterator chIt = token->m_Children.begin();
1775                  chIt != token->m_Children.end();
1776                  ++chIt)
1777             {
1778                 const Token* tk = tree->at(*chIt);
1779                 if (   tk && (   tk->m_TokenKind == tkConstructor
1780                               || (tk->m_IsOperator && tk->m_Name.EndsWith(wxT("()"))) )
1781                     && (tk->m_Scope == tsPublic || tk->m_Scope == tsUndefined) )
1782                 {
1783                     wxString tkTip;
1784                     if (PrettyPrintToken(tree, tk, tkTip))
1785                         items.Add(tkTip);
1786                 }
1787             }
1788             continue;
1789         }
1790 
1791         // support macro call tips
1792         // NOTE: improved to support more advanced cases: preprocessor token mapped to
1793         // function / macro name or variable name (for typedef'd function ptr). Eg,
1794         // #define __MINGW_NAME_AW(func) func##A
1795         // #define MessageBox __MINGW_NAME_AW(MessageBox)
1796         // MessageBox(  // --> Use calltip for MessageBoxA().
1797         // see details in
1798         // http://forums.codeblocks.org/index.php/topic,19278.msg133989.html#msg133989
1799 
1800         // only handle variable like macro definitions
1801         if (token->m_TokenKind == tkMacroDef && token->m_Args.empty())
1802         {
1803             // NOTE: we use m_FullType for our search so that we accept function / macro NAMES only,
1804             // any actual calls will be rejected (i.e., allow "#define MessageBox MessageBoxA", but
1805             // not "#define MessageBox MessageBoxA(...)"
1806             const Token* tk = tree->at(tree->TokenExists(token->m_FullType, -1,
1807                                        tkFunction|tkMacroDef|tkVariable));
1808 
1809             // either a function or a variable, but it is OK if a macro with not empty m_Args.
1810             if (tk && ((tk->m_TokenKind ^ tkMacroDef) || !tk->m_Args.empty()))
1811                 token = tk; // tkVariable could be a typedef'd function ptr (checked in PrettyPrintToken())
1812             else
1813             {
1814                 // a variable like macro, this token don't have m_Args(call tip information), but
1815                 // if we try to expand the token, and finally find some one who do have m_Args, then
1816                 // the expanded token's m_Args can used as call tips.
1817                 Tokenizer smallTokenizer(tree);
1818                 smallTokenizer.InitFromBuffer(token->m_FullType + _T('\n'));
1819                 tk = tree->at(tree->TokenExists(smallTokenizer.GetToken(), -1, tkFunction|tkMacroDef|tkVariable));
1820                 // only if the expanded result is a single token
1821                 if (tk && smallTokenizer.PeekToken().empty())
1822                     token = tk;
1823             }
1824         }
1825 
1826         wxString tkTip;
1827         if ( !PrettyPrintToken(tree, token, tkTip) )
1828             tkTip = wxT("Error while pretty printing token!");
1829         items.Add(tkTip);
1830 
1831     }// for
1832 
1833     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1834 }
1835 
PrettyPrintToken(TokenTree * tree,const Token * token,wxString & result,bool isRoot)1836 bool NativeParserBase::PrettyPrintToken(TokenTree*   tree,
1837                                         const Token* token,
1838                                         wxString&    result,
1839                                         bool         isRoot)
1840 {
1841     wxString name = token->m_Name;
1842     // a variable basically don't have call tips, but if it's type is a typedef'd function
1843     // pointer, we can still have call tips (which is the typedef function's arguments)
1844     if (token->m_TokenKind == tkVariable)
1845     {
1846         const Token* tk = tree->at(tree->TokenExists(token->m_BaseType, token->m_ParentIndex, tkTypedef));
1847         if (!tk && token->m_ParentIndex != -1)
1848             tk = tree->at(tree->TokenExists(token->m_BaseType, -1, tkTypedef));
1849         if (tk && !tk->m_Args.empty()) // typedef'd function pointer
1850         {
1851             name = token->m_Name;
1852             token = tk;
1853         }
1854     }
1855 
1856     // if the token has parents and the token is a container or a function,
1857     // then pretty print the parent of the token->
1858     if (   (token->m_ParentIndex != -1)
1859         && (token->m_TokenKind & (tkAnyContainer | tkAnyFunction)) )
1860     {
1861         const Token* parentToken = tree->at(token->m_ParentIndex);
1862         if (!parentToken || !PrettyPrintToken(tree, parentToken, result, false))
1863             return false;
1864     }
1865 
1866     switch (token->m_TokenKind)
1867     {
1868         case tkConstructor:
1869             result = result + token->m_Name + token->GetFormattedArgs();
1870             return true;
1871 
1872         case tkFunction:
1873             result = token->m_FullType + wxT(" ") + result + token->m_Name + token->GetFormattedArgs();
1874             if (token->m_IsConst)
1875                 result += wxT(" const");
1876             if (token->m_IsNoExcept)
1877                 result += wxT(" noexcept");
1878             return true;
1879 
1880         case tkClass:
1881         case tkNamespace:
1882             if (isRoot)
1883                 result += token->m_Name;
1884             else
1885                 result += token->m_Name + wxT("::");
1886             return true;
1887 
1888         case tkMacroDef:
1889             if (!token->GetFormattedArgs().IsEmpty())
1890                 result = wxT("#define ") + token->m_Name + token->GetFormattedArgs();
1891             return true;
1892 
1893         case tkTypedef:
1894             result = token->m_BaseType + wxT(" ") + result + name + token->GetFormattedArgs();
1895             return true;
1896 
1897         case tkEnum:
1898         case tkDestructor:
1899         case tkVariable:
1900         case tkEnumerator:
1901         case tkMacroUse:
1902         case tkAnyContainer:
1903         case tkAnyFunction:
1904         case tkUndefined:
1905         default:
1906             break;
1907     }
1908     return true;
1909 }
1910