adapt to kdevplatform changes, use DeclarationPointer when the duchain might get...
[kdevelop:php.git] / completion / context.cpp
1 /*
2    Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
3    Copyright 2008 Hamish Rodda <rodda@kde.org>
4    Copyright 2008 Niko Sams <niko.sams@gmail.com>
5    Copyright 2009 Milian Wolff <mail@milianw.de>
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License version 2 as published by the Free Software Foundation.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public License
17    along with this library; see the file COPYING.LIB.  If not, write to
18    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19    Boston, MA 02110-1301, USA.
20 */
21
22 #include "context.h"
23
24 #include "duchain/expressionparser.h"
25 #include "helpers.h"
26
27 #include "../duchain/helper.h"
28 #include "../duchain/declarations/variabledeclaration.h"
29
30 #include "implementationitem.h"
31 #include "keyworditem.h"
32
33 #include "phpparser.h"
34 #include "phptokentext.h"
35
36 #include <ktexteditor/view.h>
37 #include <ktexteditor/document.h>
38 #include <klocalizedstring.h>
39 #include <language/duchain/ducontext.h>
40 #include <language/duchain/duchain.h>
41 #include <language/duchain/duchainlock.h>
42 #include <language/duchain/types/identifiedtype.h>
43 #include <language/duchain/types/functiontype.h>
44 #include <language/interfaces/iproblem.h>
45 #include <util/pushvalue.h>
46 #include <language/duchain/codemodel.h>
47 #include <language/duchain/classdeclaration.h>
48 #include <language/codecompletion/codecompletion.h>
49
50 #include "declarations/classmethoddeclaration.h"
51 #include "types/structuretype.h"
52
53 #include <interfaces/icore.h>
54 #include <interfaces/iprojectcontroller.h>
55 #include <interfaces/iproject.h>
56 #include <project/projectmodel.h>
57 #include <language/duchain/types/unsuretype.h>
58 #include <language/duchain/parsingenvironment.h>
59 #include <language/util/includeitem.h>
60
61 #include "includefileitem.h"
62 #include "../duchain/completioncodemodel.h"
63 #include "codemodelitem.h"
64
65 #define LOCKDUCHAIN     DUChainReadLocker lock(DUChain::lock())
66
67 #define ifDebug(x)
68
69 using namespace KDevelop;
70
71 namespace Php
72 {
73
74 typedef QList<Parser::TokenType> TokenList;
75
76 /**
77  * Utility class which makes it easier to access the relevant parts
78  * of the token stream for code completion.
79  *
80  * TODO: This class should be reviewed imo - I just hacked it together, quick'n'dirty
81  */
82 class TokenAccess {
83 public:
84     /// Setup the token stream from the input code
85     TokenAccess(const QString &code)
86         : m_code(code)
87     {
88
89         Lexer lexer(&m_stream, code);
90         int token;
91         while ((token = lexer.nextTokenKind())) {
92             Parser::Token &t = m_stream.next();
93             t.begin = lexer.tokenBegin();
94             t.end = lexer.tokenEnd();
95             t.kind = token;
96         }
97         // move to last token
98         m_pos = m_stream.size() - 1;
99     }
100
101     /// returns Token_INVALID if the position is invalid
102     /// else returns the type of the current token
103     Parser::TokenType type() const {
104         if ( m_pos == -1 ) {
105             return Parser::Token_INVALID;
106         } else {
107             return (Parser::TokenType) m_stream.token(m_pos).kind;
108         }
109     }
110
111     /// convenience comparison to a tokentype
112     bool operator==(const Parser::TokenType& other) const {
113         return other == type();
114     }
115
116     /// move to previous token
117     void pop() {
118         if ( m_pos >= 0 ) {
119             --m_pos;
120         }
121     }
122
123     /// move relative to current token
124     /// NOTE: make sure you honor the boundaries.
125     void moveTo(const qint64 &relPos) {
126         m_pos += relPos;
127         Q_ASSERT(m_pos > 0);
128         Q_ASSERT(m_pos < m_stream.size());
129     }
130
131     /// get type of token relative to current position
132     /// returns Token_INVALID if the position goes out of the boundaries
133     int typeAt(const qint64 &relPos) const {
134         const qint64 pos = m_pos + relPos;
135         if ( pos >= 0 && pos < m_stream.size() ) {
136             return m_stream.token(pos).kind;
137         } else {
138             return Parser::Token_INVALID;
139         }
140     }
141
142     /// Get string for token at a given position relative to the current one.
143     /// NOTE: Make sure you honor the boundaries.
144     QString stringAt(const qint64 &relPos) const {
145         Parser::Token token = tokenAt(relPos);
146         return m_code.mid(token.begin, token.end - token.begin + 1);
147     }
148
149     /// check whether the current token is prepended by the list of tokens
150     /// @return -1 when not prepended by the list, else the relative index-position
151     qint64 prependedBy(const TokenList &list, bool skipWhitespace = false ) const {
152         // this would be useless, hence forbid it
153         Q_ASSERT ( !list.isEmpty() );
154
155         if ( m_pos < list.count() - 1 ) {
156             // not enough tokens
157             return -1;
158         } else {
159             uint pos = 1;
160             foreach ( const Parser::TokenType& type, list ) {
161                 if ( skipWhitespace && m_stream.token( m_pos - pos).kind == Parser::Token_WHITESPACE ) {
162                     ++pos;
163                 }
164                 if ( m_stream.token( m_pos - pos).kind == type ) {
165                     ++pos;
166                     continue;
167                 } else {
168                     return -1;
169                 }
170             }
171             return pos;
172         }
173     }
174
175     /// Get the token relative to the current one.
176     /// NOTE: Make sure you honor the boundaries.
177     Parser::Token tokenAt(const qint64 &relPos) const {
178         const qint64 pos = m_pos + relPos;
179         Q_ASSERT(pos >= 0);
180         Q_ASSERT(pos < m_stream.size());
181         return m_stream.token(pos);
182     }
183
184 private:
185     const QString m_code;
186     TokenStream m_stream;
187     qint64 m_pos;
188 };
189
190 /**
191  * Pops all tokens from the @p lastToken and stops at the LPAREN.
192  */
193 void removeOtherArguments(TokenAccess &lastToken)
194 {
195     Q_ASSERT(lastToken.type() == Parser::Token_COMMA);
196
197     // remove all other arguments
198     int openLParen = 0;
199     do {
200         lastToken.pop();
201         if ( lastToken.type() == Parser::Token_RPAREN ) {
202             ++openLParen;
203         } else if ( lastToken.type() == Parser::Token_LPAREN ) {
204             if ( openLParen == 0 ) {
205                 return;
206             } else {
207                 --openLParen;
208             }
209         }
210     } while ( lastToken.type() != Parser::Token_INVALID );
211 }
212
213 /**
214  * if token at @p pos is whitespace, decrease pos by one.
215  */
216 inline void skipWhiteSpace(const TokenAccess &lastToken, qint64 &pos)
217 {
218     if ( lastToken.typeAt(pos) == Parser::Token_WHITESPACE ) {
219         --pos;
220     }
221 }
222
223 /// add keyword to list of completion items
224 #define ADD_KEYWORD(x) items << CompletionTreeItemPointer( new KeywordItem( x, KDevelop::CodeCompletionContext::Ptr(this) ) )
225 #define ADD_KEYWORD2(x, y) items << CompletionTreeItemPointer( new KeywordItem( x, KDevelop::CodeCompletionContext::Ptr(this), y ) )
226
227 int completionRecursionDepth = 0;
228
229 CodeCompletionContext::CodeCompletionContext(KDevelop::DUContextPointer context, const QString& text, const QString& followingText, const KDevelop::CursorInRevision& position, int depth)
230         : KDevelop::CodeCompletionContext(context, text, position, depth)
231         , m_memberAccessOperation(NoMemberAccess), m_parentAccess(false), m_isFileCompletionAfterDirname(false)
232 {
233     // use other ctor for parents
234     Q_ASSERT(depth == 0);
235
236     ifDebug(kDebug() << "non-processed text: " + text;)
237
238     if ( context->type() == DUContext::Class || context->type() == DUContext::Function || context->type() == DUContext::Other
239         || context->type() == DUContext::Namespace )
240     {
241         if ( !m_parentContext && !m_text.startsWith(QLatin1String("<?php ")) ) {
242             ifDebug(kDebug() << "added start tag: " + m_text;)
243             m_text.prepend("<?php ");
244         }
245     }
246
247     m_valid = !m_text.isEmpty();
248
249     if (!m_valid) {
250         kDebug() << "empty completion text";
251         return;
252     }
253
254     TokenAccess lastToken(m_text);
255 //     ifDebug(kDebug() << "clearing completion text");)
256 //     m_text.clear();
257
258     /// even when we skip to some more meaning ful token, this will
259     /// always be the end position of the last token
260     const qint64 lastTokenEnd = lastToken.tokenAt(0).end + 1;
261
262     bool lastWasWhitespace = lastToken == Parser::Token_WHITESPACE;
263     if ( lastWasWhitespace ) {
264         ifDebug(kDebug() << "skipping whitespace token";)
265         lastToken.pop();
266     }
267
268     // when the text after the current token starts with /* we are inside
269     // a multi line comment => don't offer completion
270     if ( m_text.mid( lastTokenEnd, 2 ) == "/*" ) {
271         ifDebug(kDebug() << "no completion in comments");
272         m_valid = false;
273         return;
274     }
275
276     ifDebug(kDebug() << tokenText(lastToken.type());)
277
278     ///TODO: REFACTOR: push some stuff into its own methods
279     ///                and call them from inside the big switch.
280     ///                Then we can forget about having additional checks
281     ///                beforehand and can handle it all in one place.
282
283     // The following tokens require a whitespace after them for code-completion:
284     if ( !lastWasWhitespace ) {
285         switch ( lastToken.type() ) {
286             case Parser::Token_EXTENDS:
287             case Parser::Token_IMPLEMENTS:
288             case Parser::Token_NEW:
289             case Parser::Token_THROW:
290                 ifDebug(kDebug() << "need whitespace after token for completion";)
291                 m_valid = false;
292                 return;
293             default:
294                 break;
295         }
296     }
297
298     ifDebug(kDebug() << tokenText(lastToken.type());)
299
300     switch ( lastToken.type() ) {
301         case Parser::Token_COMMENT:
302             // don't offer code completion in comments, i.e. single line comments that don't end on \n
303             // multi-line comments are handled above
304             if ( !lastWasWhitespace && !lastToken.stringAt(0).endsWith('\n')
305                     && !lastToken.stringAt(0).startsWith(QLatin1String("/*")) ) {
306                 ifDebug(kDebug() << "no completion in comments";)
307                 m_valid = false;
308             }
309             break;
310         case Parser::Token_EXTENDS:
311             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
312                                                    << Parser::Token_WHITESPACE << Parser::Token_CLASS) != -1 ) {
313                 m_memberAccessOperation = ClassExtendsChoose;
314                 forbidIdentifier(lastToken.stringAt(-2));
315             } else if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
316                                                    << Parser::Token_WHITESPACE << Parser::Token_INTERFACE) != -1 ) {
317                 m_memberAccessOperation = InterfaceChoose;
318                 forbidIdentifier(lastToken.stringAt(-2));
319             } else {
320                 ifDebug(kDebug() << "token prepended by bad tokens, don't do completion";)
321                 m_valid = false;
322             }
323             break;
324         case Parser::Token_IMPLEMENTS:
325             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
326                                                    << Parser::Token_WHITESPACE << Parser::Token_CLASS) != -1 ) {
327                 m_memberAccessOperation = InterfaceChoose;
328                 forbidIdentifier(lastToken.stringAt(-2));
329             } else {
330                 ifDebug(kDebug() << "token prepended by bad tokens, don't do completion";)
331                 m_valid = false;
332             }
333             break;
334         case Parser::Token_COMMA:
335             {
336             // check if we are in the list after Token_IMPLEMENTS:
337             qint64 relPos = -1;
338             QList<qint64> identifierPositions;
339             while ( true ) {
340                 skipWhiteSpace(lastToken, relPos);
341                 if ( lastToken.typeAt(relPos) == Parser::Token_STRING ) {
342                     identifierPositions << relPos;
343                     --relPos;
344                     skipWhiteSpace(lastToken, relPos);
345                             // interfaces may extend more than one interface
346                     if ( ( lastToken.typeAt(relPos) == Parser::Token_EXTENDS &&
347                             lastToken.typeAt(relPos - 1) == Parser::Token_WHITESPACE &&
348                             lastToken.typeAt(relPos - 2) == Parser::Token_STRING &&
349                             lastToken.typeAt(relPos - 3) == Parser::Token_WHITESPACE &&
350                             lastToken.typeAt(relPos - 4) == Parser::Token_INTERFACE )
351                         || // classes may implement more than one interface
352                          ( lastToken.typeAt(relPos) == Parser::Token_IMPLEMENTS &&
353                             lastToken.typeAt(relPos - 1) == Parser::Token_WHITESPACE &&
354                             lastToken.typeAt(relPos - 2) == Parser::Token_STRING &&
355                             lastToken.typeAt(relPos - 3) == Parser::Token_WHITESPACE &&
356                             lastToken.typeAt(relPos - 4) == Parser::Token_CLASS ) )
357                     {
358                         identifierPositions << (relPos - 2);
359                         m_memberAccessOperation = InterfaceChoose;
360                         break;
361                     } else if ( lastToken.typeAt(relPos) == Parser::Token_COMMA ) {
362                         // skip to next entry
363                         --relPos;
364                         continue;
365                     }
366                 } else {
367                     break;
368                 }
369             }
370             if ( m_memberAccessOperation == InterfaceChoose ) {
371                 ifDebug(kDebug() << "in implementation list";)
372                 m_memberAccessOperation = InterfaceChoose;
373                 foreach ( const qint64& pos, identifierPositions ) {
374                     forbidIdentifier(lastToken.stringAt(pos));
375                 }
376             } else {
377                 // else do function call completion
378                 m_memberAccessOperation = FunctionCallAccess;
379
380                 ///TODO: global, static etc. enumerations.
381                 removeOtherArguments(lastToken);
382
383                 if ( lastToken.type() == Parser::Token_INVALID ) {
384                     m_valid = false;
385                 }
386             }
387             }
388             break;
389         case Parser::Token_OPEN_TAG:
390             // don't do completion if no whitespace is given and there is some text following,
391             // esp. for stuff like <?php <?ph <?p
392             if ( !lastWasWhitespace && !followingText.isEmpty() ) {
393                 ifDebug(kDebug() << "no completion because <? is followed by" + followingText;)
394                 m_valid = false;
395             } else {
396                 // else just do normal completion
397                 m_memberAccessOperation = NoMemberAccess;
398             }
399             break;
400         case Parser::Token_OBJECT_OPERATOR:
401             m_memberAccessOperation = MemberAccess;
402             lastToken.pop();
403             break;
404         case Parser::Token_PAAMAYIM_NEKUDOTAYIM:
405             m_memberAccessOperation = StaticMemberAccess;
406             lastToken.pop();
407             break;
408         case Parser::Token_LPAREN:
409             {
410             qint64 pos = -1;
411             skipWhiteSpace(lastToken, pos);
412             if ( lastToken.typeAt(pos) == Parser::Token_CATCH ) {
413                 m_memberAccessOperation = ExceptionChoose;
414             } else if ( lastToken.typeAt(pos) == Parser::Token_ARRAY ) {
415                 m_memberAccessOperation = NoMemberAccess;
416                 ifDebug(kDebug() << "NoMemberAccess";)
417                 ifDebug(kDebug() << "returning early";)
418                 return;
419             } else {
420                 m_memberAccessOperation = FunctionCallAccess;
421             }
422             }
423             break;
424         case Parser::Token_NEW:
425             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_THROW) != -1 ) {
426                 m_memberAccessOperation = ExceptionChoose;
427             } else {
428                 m_memberAccessOperation = NewClassChoose;
429             }
430             break;
431         case Parser::Token_THROW:
432             m_memberAccessOperation = ExceptionInstanceChoose;
433             break;
434         case Parser::Token_CONSTANT_ENCAPSED_STRING:
435             {
436                 // support something like `include dirname(__FILE__) . "/...`
437                 ///TODO: include __DIR__ . "/ (php 5.3)
438                 bool isAfterDirname = false;
439                 //NOTE: prependedBy will return -1 on failure, this is what we need in these cases
440                 //      on success it will return a positive number, we'll need to switch it's sign in that case
441                 qint64 relPos = lastToken.prependedBy(TokenList() << Parser::Token_CONCAT << Parser::Token_RPAREN << Parser::Token_FILE
442                                                    << Parser::Token_LPAREN << Parser::Token_STRING, true);
443                 if ( relPos != -1 ) {
444                     // switch sign
445                     relPos = -relPos;
446                     if ( lastToken.stringAt(relPos + 1).toLower() == "dirname" ) {
447                         isAfterDirname = true;
448                     }
449                 }
450                 skipWhiteSpace(lastToken, relPos);
451                 if ( lastToken.typeAt(relPos) == Parser::Token_LPAREN ) {
452                     --relPos;
453                 }
454                 skipWhiteSpace(lastToken, relPos);
455                 switch ( lastToken.typeAt(relPos) ) {
456                     case Parser::Token_REQUIRE:
457                     case Parser::Token_REQUIRE_ONCE:
458                     case Parser::Token_INCLUDE:
459                     case Parser::Token_INCLUDE_ONCE:
460                         m_memberAccessOperation = FileChoose;
461                         m_expression = m_text.mid( lastToken.tokenAt(0).begin + 1 ).append(followingText).trimmed();
462                         m_isFileCompletionAfterDirname = isAfterDirname;
463                         break;
464                     default:
465                         if ( m_text.at( lastToken.tokenAt(0).begin ).unicode() == '"' ) {
466                             ///TODO: only offer variable completion
467                             m_valid = false;
468                         } else {
469                             // in or after constant strings ('...') don't offer completion at all
470                             m_valid = false;
471                         }
472                         break;
473                 }
474                 break;
475             }
476             break;
477         case Parser::Token_INSTANCEOF:
478             m_memberAccessOperation = InstanceOfChoose;
479             break;
480         case Parser::Token_AND_ASSIGN:
481         case Parser::Token_ARRAY_CAST:
482         case Parser::Token_ASSIGN:
483         case Parser::Token_AT:
484         case Parser::Token_BANG:
485         case Parser::Token_BIT_AND:
486         case Parser::Token_BIT_OR:
487         case Parser::Token_BIT_XOR:
488         case Parser::Token_BOOLEAN_AND:
489         case Parser::Token_BOOLEAN_OR:
490         case Parser::Token_BOOL_CAST:
491         case Parser::Token_COLON:
492         case Parser::Token_CONCAT:
493         case Parser::Token_CONCAT_ASSIGN:
494         case Parser::Token_CURLY_OPEN:
495         case Parser::Token_DEC:
496         case Parser::Token_DIV:
497         case Parser::Token_DIV_ASSIGN:
498         case Parser::Token_DOC_COMMENT:
499         case Parser::Token_DOLLAR_OPEN_CURLY_BRACES:
500         case Parser::Token_DOUBLE_ARROW:
501         case Parser::Token_DOUBLE_CAST:
502         case Parser::Token_DOUBLE_QUOTE:
503         case Parser::Token_ECHO:
504         case Parser::Token_ENCAPSED_AND_WHITESPACE:
505         case Parser::Token_EXIT:
506         case Parser::Token_INC:
507         case Parser::Token_INT_CAST:
508         case Parser::Token_IS_EQUAL:
509         case Parser::Token_IS_GREATER:
510         case Parser::Token_IS_GREATER_OR_EQUAL:
511         case Parser::Token_IS_IDENTICAL:
512         case Parser::Token_IS_NOT_EQUAL:
513         case Parser::Token_IS_NOT_IDENTICAL:
514         case Parser::Token_IS_SMALLER:
515         case Parser::Token_IS_SMALLER_OR_EQUAL:
516         case Parser::Token_LBRACE:
517         case Parser::Token_LBRACKET:
518         case Parser::Token_LOGICAL_AND:
519         case Parser::Token_LOGICAL_OR:
520         case Parser::Token_LOGICAL_XOR:
521         case Parser::Token_MINUS:
522         case Parser::Token_MINUS_ASSIGN:
523         case Parser::Token_MOD:
524         case Parser::Token_MOD_ASSIGN:
525         case Parser::Token_MUL:
526         case Parser::Token_MUL_ASSIGN:
527         case Parser::Token_OBJECT_CAST:
528         case Parser::Token_OPEN_TAG_WITH_ECHO:
529         case Parser::Token_OR_ASSIGN:
530         case Parser::Token_PLUS:
531         case Parser::Token_PLUS_ASSIGN:
532         case Parser::Token_PRINT:
533         case Parser::Token_QUESTION:
534         case Parser::Token_RBRACE:
535         case Parser::Token_RETURN:
536         case Parser::Token_SEMICOLON:
537         case Parser::Token_SL:
538         case Parser::Token_SL_ASSIGN:
539         case Parser::Token_SR:
540         case Parser::Token_SR_ASSIGN:
541         case Parser::Token_START_HEREDOC:
542         case Parser::Token_START_NOWDOC:
543         case Parser::Token_STRING:
544         case Parser::Token_STRING_CAST:
545         case Parser::Token_TILDE:
546         case Parser::Token_UNSET_CAST:
547         case Parser::Token_XOR_ASSIGN:
548             // normal completion is valid
549             if ( duContext() && duContext()->type() == DUContext::Class ) {
550                 // when we are inside a class context, give overloadable members as completion
551                 m_memberAccessOperation = ClassMemberChoose;
552             } else {
553                 m_memberAccessOperation = NoMemberAccess;
554             }
555             break;
556         case Parser::Token_ABSTRACT:
557         case Parser::Token_CONST:
558         case Parser::Token_FINAL:
559         case Parser::Token_PUBLIC:
560         case Parser::Token_PRIVATE:
561         case Parser::Token_PROTECTED:
562         case Parser::Token_STATIC:
563         case Parser::Token_VAR:
564             if ( duContext() && duContext()->type() == DUContext::Class ) {
565                 // when we are inside a class context, give overloadable members as completion
566                 m_memberAccessOperation = ClassMemberChoose;
567             } else {
568                 m_valid = false;
569             }
570             break;
571         case Parser::Token_NAMESPACE:
572         case Parser::Token_BACKSLASH:
573         {
574             QString identifier;
575             qint64 relPos = 0;
576             while (lastToken.typeAt(relPos) == Parser::Token_STRING || lastToken.typeAt(relPos) == Parser::Token_BACKSLASH) {
577                 if (lastToken.typeAt(relPos) == Parser::Token_BACKSLASH) {
578                     identifier.prepend("::");
579                 } else {
580                     identifier.prepend(lastToken.stringAt(relPos));
581                 }
582                 --relPos;
583             }
584             if ( lastToken.typeAt(relPos) == Parser::Token_NAMESPACE ) {
585                 m_memberAccessOperation = NamespaceChoose;
586             } else {
587                 m_memberAccessOperation = BackslashAccess;
588             }
589             m_namespace = QualifiedIdentifier(identifier);
590             break;
591         }
592         case Parser::Token_ARRAY:
593         case Parser::Token_AS:
594         case Parser::Token_BACKTICK:
595         case Parser::Token_BREAK:
596         case Parser::Token_CASE:
597         case Parser::Token_CATCH:
598         case Parser::Token_CLASS:
599         case Parser::Token_CLASS_C:
600         case Parser::Token_CLONE:
601         case Parser::Token_CLOSE_TAG:
602         case Parser::Token_CONTINUE:
603         case Parser::Token_DECLARE:
604         case Parser::Token_DEFAULT:
605         case Parser::Token_DNUMBER:
606         case Parser::Token_DO:
607         case Parser::Token_DOLLAR:
608         case Parser::Token_ELSE:
609         case Parser::Token_ELSEIF:
610         case Parser::Token_EMPTY:
611         case Parser::Token_ENDDECLARE:
612         case Parser::Token_ENDFOR:
613         case Parser::Token_ENDFOREACH:
614         case Parser::Token_ENDIF:
615         case Parser::Token_ENDSWITCH:
616         case Parser::Token_ENDWHILE:
617         case Parser::Token_END_HEREDOC:
618         case Parser::Token_END_NOWDOC:
619         case Parser::Token_EOF:
620         case Parser::Token_EVAL:
621         case Parser::Token_FILE:
622         case Parser::Token_FOR:
623         case Parser::Token_FOREACH:
624         case Parser::Token_FUNCTION:
625         case Parser::Token_FUNC_C:
626         case Parser::Token_GLOBAL:
627         case Parser::Token_HALT_COMPILER:
628         case Parser::Token_IF:
629         case Parser::Token_INCLUDE:
630         case Parser::Token_INCLUDE_ONCE:
631         case Parser::Token_INLINE_HTML:
632         case Parser::Token_INTERFACE:
633         case Parser::Token_INVALID:
634         case Parser::Token_ISSET:
635         case Parser::Token_LINE:
636         case Parser::Token_LIST:
637         case Parser::Token_LNUMBER:
638         case Parser::Token_METHOD_C:
639         case Parser::Token_NAMESPACE_C:
640         case Parser::Token_NUM_STRING:
641         case Parser::Token_REQUIRE:
642         case Parser::Token_REQUIRE_ONCE:
643         case Parser::Token_RBRACKET:
644         case Parser::Token_RPAREN:
645         case Parser::Token_STRING_VARNAME:
646         case Parser::Token_SWITCH:
647         case Parser::Token_TRY:
648         case Parser::Token_UNSET:
649         case Parser::Token_USE:
650         case Parser::Token_VARIABLE:
651         case Parser::Token_WHILE:
652         case Parser::Token_WHITESPACE:
653         case Parser::TokenTypeSize:
654             ifDebug(kDebug() << "no completion after this token";)
655             m_valid = false;
656             break;
657     }
658
659     ifDebug(
660         switch ( m_memberAccessOperation ) {
661             case FileChoose:
662                 kDebug() << "FileChoose";
663                 break;
664             case ExceptionInstanceChoose:
665                 kDebug() << "ExceptionInstanceChoose";
666                 break;
667             case ExceptionChoose:
668                 kDebug() << "ExceptionChoose";
669                 break;
670             case ClassMemberChoose:
671                 kDebug() << "ClassMemberChoose";
672                 break;
673             case NoMemberAccess:
674                 kDebug() << "NoMemberAccess";
675                 break;
676             case NewClassChoose:
677                 kDebug() << "NewClassChoose";
678                 break;
679             case FunctionCallAccess:
680                 kDebug() << "FunctionCallAccess";
681                 break;
682             case InterfaceChoose:
683                 kDebug() << "InterfaceChoose";
684                 break;
685             case ClassExtendsChoose:
686                 kDebug() << "ClassExtendsChoose";
687                 break;
688             case MemberAccess:
689                 kDebug() << "MemberAccess";
690                 break;
691             case StaticMemberAccess:
692                 kDebug() << "StaticMemberAccess";
693                 break;
694             case InstanceOfChoose:
695                 kDebug() << "InstanceOfChoose";
696                 break;
697             case NamespaceChoose:
698                 kDebug() << "NamespaceChoose";
699                 break;
700             case BackslashAccess:
701                 kDebug() << "BackslashAccess";
702                 break;
703         }
704     )
705
706     ifDebug(kDebug() << tokenText(lastToken.type());)
707
708     // if it's not valid, we should return early
709     if ( !m_valid ) {
710         ifDebug(kDebug() << "invalid completion";)
711         return;
712     }
713
714     // trim the text to the end position of the current token
715     m_text = m_text.left(lastToken.tokenAt(0).end + 1).trimmed();
716     ifDebug(kDebug() << "trimmed text: " << m_text;)
717
718     // check whether we need the expression or have everything we need and can return early
719     switch ( m_memberAccessOperation ) {
720         // these access operations don't need the previous expression evaluated
721         case FileChoose:
722         case ClassMemberChoose:
723         case InterfaceChoose:
724         case NewClassChoose:
725         case ExceptionChoose:
726         case ExceptionInstanceChoose:
727         case ClassExtendsChoose:
728         case NoMemberAccess:
729         case InstanceOfChoose:
730         case NamespaceChoose:
731         case BackslashAccess:
732             ifDebug(kDebug() << "returning early";)
733             return;
734         case FunctionCallAccess:
735             m_memberAccessOperation = NoMemberAccess;
736             Q_ASSERT(lastToken.type() == Parser::Token_LPAREN);
737             if ( lastToken.prependedBy(TokenList() << Parser::Token_STRING, true) == -1 &&
738                  lastToken.prependedBy(TokenList() << Parser::Token_VARIABLE, true) == -1 )
739             {
740                 // handle for, foreach, while, etc.
741                 ifDebug(kDebug() << "NoMemberAccess (no function call)";)
742             } else {
743                 //The first context should never be a function-call context,
744                 //so make this a NoMemberAccess context and the parent a function-call context.
745                 ifDebug(kDebug() << "NoMemberAccess (creating parentContext for function call)";)
746                 m_parentContext = new CodeCompletionContext(m_duContext, m_position, lastToken, depth + 1);
747             }
748             return;
749         case MemberAccess:
750         case StaticMemberAccess:
751             // these types need the expression evaluated
752             break;
753     }
754
755     evaluateExpression(lastToken);
756 }
757
758 CodeCompletionContext::CodeCompletionContext(KDevelop::DUContextPointer context, const KDevelop::CursorInRevision& position,
759                                              TokenAccess& lastToken,  int depth)
760         : KDevelop::CodeCompletionContext(context, QString(), position, depth)
761         , m_memberAccessOperation(NoMemberAccess), m_parentAccess(false), m_isFileCompletionAfterDirname(false)
762 {
763     switch ( lastToken.type() ) {
764         case Parser::Token_LPAREN:
765             m_memberAccessOperation = FunctionCallAccess;
766             break;
767         default:
768             kDebug() << "unhandled token type for parent context" << tokenText(lastToken.typeAt(0));
769             Q_ASSERT(false);
770             m_valid = false;
771             return;
772     }
773
774     evaluateExpression(lastToken);
775 }
776
777 void CodeCompletionContext::evaluateExpression(TokenAccess& lastToken)
778 {
779     /// token pos
780     qint64 startPos = 0;
781     int openLParen = 0;
782
783     if ( m_memberAccessOperation == FunctionCallAccess ) {
784         Q_ASSERT(lastToken.type() == Parser::Token_LPAREN);
785         // check ctor call
786         qint64 pos = lastToken.prependedBy(TokenList() << Parser::Token_STRING << Parser::Token_NEW, true);
787         if ( pos != -1 ) {
788             startPos = -pos;
789             ifDebug(kDebug() << "ctor call";)
790         } else {
791             // simple function call, get it's expression
792             startPos = -1;
793             ifDebug(kDebug() << "simple function call";)
794         }
795     }
796
797     static const QList<int> defaultStopTokens = QList<int>()
798             << Parser::Token_SEMICOLON << Parser::Token_INVALID << Parser::Token_OPEN_TAG
799             << Parser::Token_OPEN_TAG_WITH_ECHO << Parser::Token_LBRACE << Parser::Token_RBRACE
800             << Parser::Token_IF << Parser::Token_WHILE << Parser::Token_FOR << Parser::Token_FOREACH
801             << Parser::Token_SWITCH << Parser::Token_ELSEIF;
802
803
804     // find expression start
805     while ( !defaultStopTokens.contains(lastToken.typeAt(startPos)) &&
806             (m_memberAccessOperation == FunctionCallAccess || lastToken.typeAt(startPos) != Parser::Token_COMMA) )
807     {
808         if ( lastToken.typeAt(startPos) == Parser::Token_LPAREN ) {
809             ++openLParen;
810             if ( openLParen > 0 ) {
811                 break;
812             }
813         } else if ( lastToken.typeAt(startPos) == Parser::Token_RPAREN ) {
814             --openLParen;
815         }
816         --startPos;
817     }
818
819     if ( openLParen < 0 ) {
820         ifDebug(kDebug() << "too many closed parenthesis";)
821         m_valid = false;
822         return;
823     }
824
825     // we actually incorporate the not-wanted token, hence move forward
826     ++startPos;
827
828     if ( lastToken.typeAt(startPos) == Parser::Token_WHITESPACE ) {
829         ++startPos;
830     }
831
832     if ( lastToken.typeAt(startPos) == Parser::Token_RETURN ) {
833         ///TODO: match against function return type
834         ++startPos;
835
836         if ( lastToken.typeAt(startPos) == Parser::Token_WHITESPACE ) {
837             ++startPos;
838         }
839     }
840
841     if ( m_memberAccessOperation == StaticMemberAccess ) {
842         if ( lastToken.typeAt(startPos) != Parser::Token_STRING ) {
843             ifDebug(kDebug() << "unsupported token for start member access:" << tokenText(lastToken.typeAt(startPos));)
844             m_valid = false;
845             return;
846         }
847
848         const QString identifier(lastToken.stringAt(startPos).toLower());
849
850         if ( identifier == "self" || identifier == "parent" || identifier == "static" ) {
851             // self and parent are only accessible from within a member function of a class
852             if (DUContext* parent = m_duContext->parentContext()) {
853                 LOCKDUCHAIN;
854                 ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(parent->owner());
855                 if (classDec) {
856                     if (identifier == "parent") {
857                         FOREACH_FUNCTION(const BaseClassInstance& base, classDec->baseClasses) {
858                             if (StructureType::Ptr classType = base.baseClass.type<StructureType>()) {
859                                 if (ClassDeclaration* baseClass = dynamic_cast<ClassDeclaration*>(classType->declaration(m_duContext->topContext()))) {
860                                     if (baseClass->classType() == ClassDeclarationData::Class
861                                             && baseClass->classModifier() != ClassDeclarationData::Abstract) {
862                                         ifDebug(kDebug() << "correction: parent can do MemberAccess");
863                                         m_parentAccess = true;
864                                         m_memberAccessOperation = MemberAccess;
865                                         m_expressionResult.setDeclaration(baseClass);
866                                         break;
867                                     }
868                                 }
869                             }
870                         }
871                         if (!m_parentAccess) {
872                             ifDebug(kDebug() << "class has no accessible parent class");
873                             m_valid = false;
874                             return;
875                         }
876                     } else {
877                         m_expressionResult.setDeclaration(parent->owner());
878                     }
879                 }
880             }
881         } else {
882             QualifiedIdentifier id(identifier);
883
884             m_expressionResult.setDeclaration(findDeclarationImportHelper(duContext(), id, ClassDeclarationType, 0, 0));
885         }
886     } else {
887         // Now get the string of the expression and evaluate it
888         Q_ASSERT(m_expression.isEmpty());
889
890         for (qint64 i = startPos; i <= 0; ++i ) {
891             m_expression += lastToken.stringAt(i);
892         }
893
894         m_expression = m_expression.trimmed();
895
896         // make sure the expression is valid
897         if (m_memberAccessOperation == FunctionCallAccess) {
898             m_expression.append(')');
899         }
900         for ( int i = openLParen; i > 0; --i ) {
901             m_expression.append(')');
902         }
903
904         ifDebug(kDebug() << "expression: " << m_expression;)
905
906         if ( !m_expression.isEmpty() ) {
907             ExpressionParser expressionParser;
908             m_expressionResult = expressionParser.evaluateType(m_expression.toUtf8(), m_duContext, m_position);
909         }
910
911         if (m_expressionResult.type()) {
912             LOCKDUCHAIN;
913             ifDebug(kDebug() << "expression type: " << m_expressionResult.type()->toString();)
914         } else {
915             ifDebug(kDebug() << QString("expression could not be evaluated"));
916             if ( m_memberAccessOperation == FunctionCallAccess ) {
917                 ifDebug(kDebug() << "function not found";)
918                 return;
919             }
920             m_valid = false;
921             return;
922         }
923     }
924
925     lastToken.moveTo(startPos);
926
927     // Handle recursive contexts (Example: "ret = function1(param1, function2(" )
928     if ( lastToken.typeAt(-1) == Parser::Token_LPAREN ||
929          lastToken.typeAt(-1) == Parser::Token_COMMA ) {
930         //Our expression is within a function-call. We need to find out the possible argument-types we need to match, and show an argument-hint.
931
932         lastToken.moveTo(-1);
933         if ( lastToken.type() == Parser::Token_COMMA ) {
934             removeOtherArguments(lastToken);
935             if ( lastToken.type() == Parser::Token_INVALID ) {
936                 ifDebug(kDebug() << QString("Could not find start position for parent function-call. Aborting.");)
937                 m_valid = false;
938                 return;
939             }
940         }
941
942         if ( lastToken.prependedBy(TokenList() << Parser::Token_STRING, true) == -1 ) {
943             // handle for, foreach, while, etc.
944             ifDebug(kDebug() << "No real function call";)
945             return;
946         }
947
948         ifDebug(kDebug() << QString("Recursive function-call: creating parent context"));
949         m_parentContext = new CodeCompletionContext(m_duContext, m_position, lastToken, m_depth + 1);
950
951         if (!m_parentContext->isValid()) {
952             m_parentContext = 0;
953             m_valid = false;
954             return;
955         }
956     }
957 }
958
959 void CodeCompletionContext::forbidIdentifier(const QString& identifier)
960 {
961     QualifiedIdentifier id(identifier.toLower());
962
963     ClassDeclaration *dec = dynamic_cast<ClassDeclaration*>(
964                                 findDeclarationImportHelper(m_duContext.data(), id,
965                                                             ClassDeclarationType, 0, 0).data()
966                             );
967     if (dec) {
968         forbidIdentifier(dec);
969     } else {
970         // might be a class we are currently writing, i.e. without a proper declaration
971         m_forbiddenIdentifiers << id.index();
972     }
973 }
974
975 void CodeCompletionContext::forbidIdentifier(ClassDeclaration* klass)
976 {
977     uint id;
978     {
979         LOCKDUCHAIN;
980         // TODO: qualifiedIdentifier is marked as expensive - any other way
981         //       we can do what we are doing here?
982         // TODO: maybe we should clar the m_fobiddenIdentifiers after we got
983         //       our list of items...
984         id = klass->qualifiedIdentifier().index();
985     }
986     if (m_forbiddenIdentifiers.contains(id)) {
987         // nothing to do
988         return;
989     }
990
991     m_forbiddenIdentifiers << id;
992
993     // add parents so those are excluded from the completion items as well
994     if (klass->baseClassesSize() > 0) {
995         FOREACH_FUNCTION(const BaseClassInstance& base, klass->baseClasses) {
996             StructureType::Ptr type = base.baseClass.type<StructureType>();
997             if (! type.isNull()) {
998                 ClassDeclaration* parent;
999                 {
1000                     LOCKDUCHAIN;
1001                     parent = dynamic_cast<ClassDeclaration*>(
1002                                                type->declaration(m_duContext->topContext())
1003                                            );
1004                 }
1005                 if (parent) {
1006                     forbidIdentifier(parent);
1007                 }
1008             }
1009         }
1010     }
1011 }
1012
1013 CodeCompletionContext::~CodeCompletionContext()
1014 {
1015 }
1016
1017 CodeCompletionContext::MemberAccessOperation CodeCompletionContext::memberAccessOperation() const
1018 {
1019     return m_memberAccessOperation;
1020 }
1021
1022 ExpressionEvaluationResult CodeCompletionContext::memberAccessContainer() const
1023 {
1024     return m_expressionResult;
1025 }
1026
1027 CodeCompletionContext* CodeCompletionContext::parentContext()
1028 {
1029     return static_cast<CodeCompletionContext*>(KDevelop::CodeCompletionContext::parentContext());
1030 }
1031
1032 QList<DUContext*> CodeCompletionContext::memberAccessContainers() const
1033 {
1034     QList<DUContext*> ret;
1035     QList<AbstractType::Ptr> types;
1036     AbstractType::Ptr expressionTarget = m_expressionResult.type();
1037     if (UnsureType::Ptr unsureType = UnsureType::Ptr::dynamicCast(m_expressionResult.type())) {
1038         FOREACH_FUNCTION(const IndexedType& t, unsureType->types) {
1039             types << t.abstractType();
1040         }
1041     } else if (ReferenceType::Ptr referencedType = ReferenceType::Ptr::dynamicCast(m_expressionResult.type()) ) {
1042         types << referencedType->baseType();
1043     } else {
1044         types << expressionTarget;
1045     }
1046     foreach (const AbstractType::Ptr &type, types) {
1047         const IdentifiedType* idType = dynamic_cast<const IdentifiedType*>(type.unsafeData());
1048         Declaration* declaration = 0;
1049         if (idType) {
1050             declaration = idType->declaration(m_duContext->topContext());
1051         }
1052         DUContext* ctx = 0;
1053         if (declaration) {
1054             ctx = declaration->logicalInternalContext(m_duContext->topContext());
1055         }
1056         if (ctx) {
1057             ret << ctx;
1058         } else if (declaration) {
1059             //Print some debug-output
1060             kDebug() << "Could not get internal context from" << declaration->toString();
1061         } else {
1062             //Print some debug-output
1063             kDebug() << "Could not get declaration";
1064         }
1065     }
1066     return ret;
1067 }
1068
1069 QList<CompletionTreeItemPointer> CodeCompletionContext::completionItems(bool& abort, bool fullCompletion)
1070 {
1071     /// Indexed string for 'Php', identifies environment files from this language plugin
1072     static const IndexedString phpLangString("Php");
1073
1074     LOCKDUCHAIN;
1075
1076     QList<CompletionTreeItemPointer> items;
1077
1078     if (!m_duContext)
1079         return items;
1080
1081     typedef QPair<Declaration*, int> DeclarationDepthPair;
1082
1083     if ( memberAccessOperation() == FileChoose ) {
1084         if ( !ICore::self() ) {
1085             // in unit tests we can't do anything
1086             kDebug() << "no core found";
1087             return items;
1088         }
1089         // file completion
1090         KUrl path;
1091         KUrl base;
1092         if ( !m_isFileCompletionAfterDirname ) {
1093             path = getUrlForBase(m_expression, m_duContext->url().toUrl().upUrl());
1094             base = path;
1095             if ( !m_expression.isEmpty() && !m_expression.endsWith('/') ) {
1096                 base = base.upUrl();
1097             }
1098         } else {
1099             if ( m_expression.startsWith('/') ) {
1100                 path = getUrlForBase(m_expression.mid(1), m_duContext->url().toUrl().upUrl());
1101             } else {
1102                 path = m_duContext->url().toUrl().upUrl();
1103             }
1104             base = path;
1105             if ( !m_expression.isEmpty() && !m_expression.endsWith('/') && m_expression != "/" ) {
1106                 base = base.upUrl();
1107             }
1108         }
1109         base.cleanPath();
1110         IProject* project = ICore::self()->projectController()->findProjectForUrl(base);
1111         if ( project && !abort ) {
1112             QList<KUrl> addedUrls;
1113             bool addedParentDir = false;
1114             foreach ( ProjectFolderItem* folder, project->foldersForUrl(base) ) {
1115                 if ( abort ) {
1116                     break;
1117                 }
1118                 foreach ( ProjectFileItem* subFile, folder->fileList() ) {
1119                     if ( abort ) {
1120                         break;
1121                     }
1122                     if ( addedUrls.contains(subFile->url()) ) {
1123                         continue;
1124                     } else {
1125                         addedUrls << subFile->url();
1126                     }
1127                     IncludeItem item;
1128                     item.isDirectory = false;
1129                     item.basePath = base;
1130                     item.name = subFile->fileName();
1131                     if ( m_isFileCompletionAfterDirname && !m_expression.startsWith('/') ) {
1132                         item.name.prepend('/');
1133                     }
1134                     items << CompletionTreeItemPointer(new IncludeFileItem(item));
1135                 }
1136                 foreach ( ProjectFolderItem* subFolder, folder->folderList() ) {
1137                     if ( abort ) {
1138                         break;
1139                     }
1140                     if ( addedUrls.contains(subFolder->url()) ) {
1141                         continue;
1142                     } else {
1143                         addedUrls << subFolder->url();
1144                     }
1145                     IncludeItem item;
1146                     item.isDirectory = true;
1147                     item.basePath = base;
1148                     item.name = subFolder->folderName();
1149                     if ( m_isFileCompletionAfterDirname && !m_expression.startsWith('/') ) {
1150                         item.name.prepend('/');
1151                     }
1152                     items << CompletionTreeItemPointer(new IncludeFileItem(item));
1153                 }
1154                 if ( !folder->parent() && !addedParentDir && m_expression.isEmpty() ) {
1155                     // expect a parent dir
1156                     IncludeItem item;
1157                     item.isDirectory = true;
1158                     item.basePath = base;
1159                     item.name = "..";
1160                     items << CompletionTreeItemPointer(new IncludeFileItem(item));
1161                 }
1162             }
1163         }
1164
1165         return items;
1166     } else if (memberAccessOperation() == ClassMemberChoose) {
1167         // get current class
1168         if (ClassDeclaration * currentClass = dynamic_cast<ClassDeclaration*>(m_duContext->owner())) {
1169             // whether we want to show a list of overloadable functions
1170             // i.e. not after we have typed one of the keywords var,const or abstract
1171             bool showOverloadable = true;
1172             // whether we want to remove static functions from the overloadable list
1173             // i.e. after we have typed "public function"
1174             bool filterStatic = false;
1175             // whether we want to remove non-static functions from the overloadable list
1176             // i.e. after we have typed "public static function"
1177             bool filterNonStatic = false;
1178             // private functions are always removed from the overloadable list
1179             // but when we type "protected function" only protected functions may be shown
1180             bool filterPublic = false;
1181
1182             {
1183                 // add typical keywords for class member definitions
1184                 QStringList modifiers = getMethodTokens(m_text);
1185
1186                 // don't add keywords when "function" was already typed
1187                 bool addKeywords = !modifiers.contains("function");
1188
1189                 if (currentClass->classModifier() == ClassDeclarationData::Abstract) {
1190                     // abstract is only allowed in abstract classes
1191                     if (modifiers.contains("abstract")) {
1192                         // don't show overloadable functions when we are defining an abstract function
1193                         showOverloadable = false;
1194                     } else if (addKeywords) {
1195                         ADD_KEYWORD("abstract");
1196                     }
1197                 } else {
1198                     // final is only allowed in non-abstract classes
1199                     if (addKeywords && !modifiers.contains("final")) {
1200                         ADD_KEYWORD("final");
1201                     }
1202                 }
1203
1204                 if (modifiers.contains("private")) {
1205                     // overloadable functions must not be declared private
1206                     showOverloadable = false;
1207                 } else if (modifiers.contains("protected")) {
1208                     // only show protected overloadable methods
1209                     filterPublic = true;
1210                 } else if (addKeywords && !modifiers.contains("public")) {
1211                     ADD_KEYWORD("public");
1212                     ADD_KEYWORD("protected");
1213                     ADD_KEYWORD("private");
1214                 }
1215
1216                 if (modifiers.contains("static")) {
1217                     filterNonStatic = true;
1218                 } else {
1219                     if (addKeywords) {
1220                         ADD_KEYWORD("static");
1221                     } else {
1222                         filterStatic = true;
1223                     }
1224                 }
1225
1226                 if (addKeywords) {
1227                     ADD_KEYWORD("function");
1228                 }
1229
1230                 if (modifiers.isEmpty()) {
1231                     // var and const may not have any modifiers
1232                     ADD_KEYWORD("var");
1233                     ADD_KEYWORD("const");
1234                 }
1235             }
1236             // complete overloadable methods from parents
1237             if (showOverloadable) {
1238                 // TODO: use m_duContext instead of ctx
1239                 // overloadable choose is only possible inside classes which extend/implement others
1240                 if (currentClass->baseClassesSize()) {
1241                     DUContext* ctx = currentClass->internalContext();
1242                     if (!ctx) {
1243                         kDebug() << "invalid class context";
1244                         return items;
1245                     }
1246                     QList<uint> alreadyImplemented;
1247                     //TODO: use the token stream here as well
1248                     //TODO: always add __construct, __destruct and maby other magic functions
1249                     // get all visible declarations and add inherited to the completion items
1250                     foreach(const DeclarationDepthPair& decl, ctx->allDeclarations(ctx->range().end, m_duContext->topContext(), false)) {
1251                         if (decl.first->isFunctionDeclaration()) {
1252                             ClassFunctionDeclaration *method = dynamic_cast<ClassFunctionDeclaration*>(decl.first);
1253                             if (method) {
1254                                 if (decl.second == 0) {
1255                                     // this function is already implemented
1256                                     alreadyImplemented << decl.first->indexedIdentifier().getIndex();
1257                                     continue;
1258                                 }
1259                                 // skip already implemented functions
1260                                 if (alreadyImplemented.contains(decl.first->indexedIdentifier().getIndex())) {
1261                                     continue;
1262                                 }
1263                                 // skip non-static functions when requested
1264                                 if (filterNonStatic && !method->isStatic()) {
1265                                     continue;
1266                                 }
1267                                 // skip static functions when requested
1268                                 if (filterStatic && method->isStatic()) {
1269                                     continue;
1270                                 }
1271                                 // always skip private functions
1272                                 if (method->accessPolicy() == Declaration::Private) {
1273                                     continue;
1274                                 }
1275                                 // skip public functions when requested
1276                                 if (filterPublic && method->accessPolicy() == Declaration::Public) {
1277                                     // make sure no non-public base methods are added
1278                                     alreadyImplemented << decl.first->indexedIdentifier().getIndex();
1279                                     continue;
1280                                 }
1281                                 // skip final methods
1282                                 if (method->isFinal()) {
1283                                     // make sure no non-final base methods are added
1284                                     alreadyImplemented << decl.first->indexedIdentifier().getIndex();
1285                                     continue;
1286                                 }
1287                                 // make sure we inherit or implement the base class of this method
1288                                 if (!method->context() || !method->context()->owner()) {
1289                                     kDebug() << "invalid parent context/owner:" << method->toString();
1290                                     continue;
1291                                 }
1292                                 if (!currentClass->isPublicBaseClass(dynamic_cast<ClassDeclaration*>(method->context()->owner()),
1293                                                                         m_duContext->topContext())) {
1294                                     continue;
1295                                 }
1296
1297                                 ImplementationItem::HelperType itype;
1298                                 if (method->isAbstract()) {
1299                                     itype = ImplementationItem::Implement;
1300                                 } else {
1301                                     itype = ImplementationItem::Override;
1302                                 }
1303
1304                                 items << CompletionTreeItemPointer(new ImplementationItem(itype, DeclarationPointer(decl.first),
1305                                                                     KDevelop::CodeCompletionContext::Ptr(this), decl.second));
1306                                 // don't add identical items twice to the completion choices
1307                                 alreadyImplemented << decl.first->indexedIdentifier().getIndex();
1308                             }
1309                         }
1310                     }
1311                 }
1312             } else {
1313                 kDebug() << "invalid owner declaration for overloadable completion";
1314             }
1315         }
1316     } else if (m_memberAccessOperation == BackslashAccess || m_memberAccessOperation == NamespaceChoose) {
1317         DUContext* ctx = 0;
1318         if (m_namespace.isEmpty()) {
1319             ctx = m_duContext->topContext();
1320         } else {
1321             foreach(Declaration* dec, m_duContext->topContext()->findDeclarations(m_namespace)) {
1322                 if (dec->kind() == Declaration::Namespace) {
1323                     ctx = dec->internalContext();
1324                     break;
1325                 }
1326             }
1327         }
1328         if (!ctx) {
1329             kDebug() << "could not find namespace:" << m_namespace.toString();
1330             return items;
1331         }
1332         foreach(Declaration* dec, ctx->localDeclarations()) {
1333             if (!isValidCompletionItem(dec)) {
1334                 continue;
1335             } else {
1336                 items << CompletionTreeItemPointer(
1337                             new NormalDeclarationCompletionItem(
1338                                     DeclarationPointer(dec),
1339                                     KDevelop::CodeCompletionContext::Ptr(this), depth()
1340                                 )
1341                          );
1342             }
1343         }
1344     } else if (m_expressionResult.type()) {
1345         QList<DUContext*> containers = memberAccessContainers();
1346         kDebug() << "containers: " << containers.count();
1347         if (!containers.isEmpty()) {
1348             // get the parent class when we are inside a method declaration
1349             ClassDeclaration* currentClass = 0;
1350             if (m_duContext->owner() && m_duContext->owner()->isFunctionDeclaration() &&
1351                     m_duContext->parentContext() && m_duContext->parentContext()->owner()) {
1352                 currentClass = dynamic_cast<ClassDeclaration*>(m_duContext->parentContext()->owner());
1353             }
1354
1355             bool filterAbstract = memberAccessOperation() == StaticMemberAccess || memberAccessOperation() == MemberAccess;
1356
1357             foreach(DUContext* ctx, containers) {
1358                 ClassDeclaration* accessedClass = dynamic_cast<ClassDeclaration*>(ctx->owner());
1359                 if (abort)
1360                     return items;
1361
1362                 foreach(const DeclarationDepthPair& decl, ctx->allDeclarations(
1363                                                             ctx->range().end, m_duContext->topContext(), false))
1364                 {
1365                     //If we have StaticMemberAccess, which means A::Bla, show only static members,
1366                     //except if we're within a class that derives from the container
1367                     ClassMemberDeclaration* classMember = dynamic_cast<ClassMemberDeclaration*>(decl.first);
1368                     if (memberAccessOperation() != StaticMemberAccess) {
1369                         if (classMember && classMember->isStatic())
1370                             continue; //Skip static class members when not doing static access
1371                     } else {
1372                         if (!classMember || !classMember->isStatic())
1373                             continue; //Skip static class members when not doing static access
1374                     }
1375
1376                     // check access policy
1377                     if (classMember && accessedClass) {
1378                         // by default only show public declarations
1379                         Declaration::AccessPolicy ap = Declaration::Public;
1380                         if (currentClass) {
1381                             // if we are inside a class, we might want to show protected or private members
1382                             ClassDeclaration* memberClass = dynamic_cast<ClassDeclaration*>(classMember->context()->owner());
1383                             if (memberClass) {
1384                                 if (currentClass == accessedClass) {
1385                                     if (currentClass == memberClass) {
1386                                         // we can show all members of the current class
1387                                         ap = Declaration::Private;
1388                                     } else if (currentClass->isPublicBaseClass(memberClass, m_duContext->topContext())) {
1389                                         // we can show all but private members of ancestors of the current class
1390                                         ap = Declaration::Protected;
1391                                     }
1392                                 } else if (currentClass->isPublicBaseClass(accessedClass, m_duContext->topContext())
1393                                             && (accessedClass == memberClass ||
1394                                                 accessedClass->isPublicBaseClass(memberClass, m_duContext->topContext()))) {
1395                                     // we can show all but private members of ancestors of the current class
1396                                     ap = Declaration::Protected;
1397                                 }
1398                             }
1399                         }
1400                         if (ap < classMember->accessPolicy()) {
1401                             continue;
1402                         }
1403                     }
1404
1405                     // filter abstract methods
1406                     if (filterAbstract) {
1407                         ClassFunctionDeclaration* method = dynamic_cast<ClassFunctionDeclaration*>(decl.first);
1408                         if (method && method->isAbstract()) {
1409                             continue;
1410                         }
1411                     }
1412
1413                     if (!decl.first->identifier().isEmpty())
1414                         items << CompletionTreeItemPointer(
1415                                     new NormalDeclarationCompletionItem(
1416                                             DeclarationPointer(
1417                                                 decl.first),
1418                                                 KDevelop::CodeCompletionContext::Ptr(this),
1419                                                 decl.second
1420                                             )
1421                                     );
1422                 }
1423             }
1424         } else {
1425             kDebug() << "setContext: no container-type";
1426         }
1427
1428     } else {
1429         //Show all visible declarations
1430         QSet<uint> existingIdentifiers;
1431         QList<DeclarationDepthPair> decls = m_duContext->allDeclarations(
1432             CursorInRevision::invalid(),
1433             m_duContext->topContext()
1434         );
1435
1436         kDebug() << "setContext: using all declarations visible:" << decls.size();
1437
1438         QListIterator<DeclarationDepthPair> i(decls);
1439         i.toBack();
1440         while (i.hasPrevious()) {
1441             DeclarationDepthPair decl = i.previous();
1442             Declaration* dec = decl.first;
1443             if (dec->kind() == Declaration::Instance) {
1444                 // filter non-superglobal vars of other contexts
1445                 if (dec->context() != m_duContext.data() && !m_duContext->imports(dec->context())) {
1446                     VariableDeclaration* vDec = dynamic_cast<VariableDeclaration*>(dec);
1447                     if ( vDec && !vDec->isSuperglobal() ) {
1448                         continue;
1449                     }
1450                 }
1451
1452                 if (existingIdentifiers.contains(dec->indexedIdentifier().getIndex())) continue;
1453                 existingIdentifiers.insert(dec->indexedIdentifier().getIndex());
1454             }
1455             if (abort)
1456                 return items;
1457             if (!isValidCompletionItem(dec)) continue;
1458             items << CompletionTreeItemPointer(
1459                         new NormalDeclarationCompletionItem(
1460                                 DeclarationPointer(dec),
1461                                 KDevelop::CodeCompletionContext::Ptr(this),
1462                                 decl.second
1463                         )
1464                     );
1465         }
1466
1467         foreach(QSet<IndexedString> urlSets, completionFiles()) {
1468             foreach(const IndexedString &url, urlSets) {
1469                 if (url == m_duContext->url()) {
1470                     continue;
1471                 }
1472                 uint count = 0;
1473                 const CodeModelItem* foundItems = 0;
1474                 CodeModel::self().items(url, count, foundItems);
1475                 for (uint i = 0; i < count; ++i) {
1476                     CodeModelItem::Kind k = foundItems[i].kind;
1477                     if (((k & CodeModelItem::Function) || (k & CodeModelItem::Variable)) && !(k & CodeModelItem::ClassMember)) {
1478                         foreach(const ParsingEnvironmentFilePointer &env, DUChain::self()->allEnvironmentFiles(url)) {
1479                             if (env->language() != phpLangString) continue;
1480                             TopDUContext* top = env->topContext();
1481                             if(!top) continue;
1482                             if (m_duContext->imports(top)) continue;
1483                             QList<Declaration*> decls = top->findDeclarations(foundItems[i].id);
1484                             foreach(Declaration* decl, decls) {
1485                                 if (abort) return items;
1486                                 // we don't want to have class methods/properties, just normal functions
1487                                 // and other global stuff
1488                                 if ( decl->context() && decl->context()->type() == DUContext::Class ) {
1489                                     continue;
1490                                 }
1491                                 if (!isValidCompletionItem(decl)) continue;
1492                                 if ( VariableDeclaration* vDec = dynamic_cast<VariableDeclaration*>(decl) ) {
1493                                     if ( !vDec->isSuperglobal() ) {
1494                                         continue;
1495                                     }
1496                                 }
1497                                 items << CompletionTreeItemPointer(
1498                                             new NormalDeclarationCompletionItem(
1499                                                     DeclarationPointer(decl),
1500                                                     KDevelop::CodeCompletionContext::Ptr(this)
1501                                             )
1502                                         );
1503                             }
1504                         }
1505                     }
1506                 }
1507             }
1508         }
1509
1510         foreach(QSet<IndexedString> urlSets, completionFiles()) {
1511             foreach(const IndexedString &url, urlSets) {
1512                 uint count = 0;
1513                 const CompletionCodeModelItem* foundItems = 0;
1514                 CompletionCodeModel::self().items(url, count, foundItems);
1515                 for (uint i = 0; i < count; ++i) {
1516                     if (abort) return items;
1517                     if (m_memberAccessOperation == ExceptionChoose) {
1518                         if (!(foundItems[i].kind & CompletionCodeModelItem::Exception)) continue;
1519                     }
1520                     foreach(const ParsingEnvironmentFilePointer &env, DUChain::self()->allEnvironmentFiles(url)) {
1521                         Q_ASSERT(env->language() == phpLangString);
1522                         items << CompletionTreeItemPointer ( new CodeModelCompletionItem(env, foundItems[i]));
1523                     }
1524                 }
1525             }
1526         }
1527     }
1528
1529     ///Find all recursive function-calls that should be shown as call-tips
1530     CodeCompletionContext::Ptr parentContext(this);
1531     do {
1532         if (abort)
1533             return items;
1534
1535         parentContext = parentContext->parentContext();
1536         if (parentContext) {
1537             if (parentContext->memberAccessOperation() == CodeCompletionContext::FunctionCallAccess) {
1538                 if (!parentContext->memberAccessContainer().allDeclarationIds().isEmpty()) {
1539                     Declaration* decl = parentContext->memberAccessContainer().allDeclarationIds().first()
1540                                             .getDeclaration(m_duContext->topContext());
1541
1542                     if (!isValidCompletionItem(decl)) {
1543                         continue;
1544                     }
1545                     if ( !decl->isFunctionDeclaration() ) {
1546                         if ( ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(decl) ) {
1547                             // search for ctor
1548                             decl = 0;
1549                             foreach ( Declaration* dec, classDec->internalContext()->findDeclarations(Identifier("__construct")) ) {
1550                                 if ( dec->isFunctionDeclaration() ) {
1551                                     decl = dec;
1552                                     break;
1553                                 }
1554                             }
1555                             if ( !decl ) {
1556                                 foreach ( Declaration* dec, classDec->internalContext()->findDeclarations(classDec->identifier()) ) {
1557                                     if ( dec->isFunctionDeclaration() ) {
1558                                         decl = dec;
1559                                         break;
1560                                     }
1561                                 }
1562                             }
1563                             if ( !decl ) {
1564                                 continue;
1565                             }
1566                         } else if ( !decl->type<FunctionType>() ) {
1567                             kDebug() << "parent decl is neither function nor class nor closure, skipping" << decl->toString();
1568                             continue;
1569                         }
1570                     }
1571                     items << CompletionTreeItemPointer(
1572                                 new NormalDeclarationCompletionItem(
1573                                         DeclarationPointer(decl),
1574                                         KDevelop::CodeCompletionContext::Ptr::staticCast(parentContext)
1575                                 )
1576                             );
1577                 }
1578             } else {
1579                 kDebug() << "parent-context has non function-call access type";
1580             }
1581         }
1582     } while (parentContext);
1583
1584     if ( m_memberAccessOperation == NoMemberAccess ) {
1585         ///TODO: function-like statements should just be handled as a function with declaration etc.
1586         ///      e.g.: empty, eval, die, exit, isset, unset
1587         ///      but _not_ echo, print, catch, include*, require*
1588         ///TODO: use user's style for indendation etc.
1589         ADD_KEYWORD2("abstract class", "abstract class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1590         ADD_KEYWORD2("final class", "final class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1591         ADD_KEYWORD2("class", "class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1592         ADD_KEYWORD2("interface", "interface %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1593         ADD_KEYWORD2("array", "array(\n%INDENT%%CURSOR%\n)");
1594         ADD_KEYWORD2("break", "break;\n");
1595         ADD_KEYWORD2("case", "case %SELECT%CASE%ENDSELECT%:\n%INDENT%\n%INDENT%break;\n");
1596         ADD_KEYWORD2("throw", "throw %CURSOR%;\n");
1597         ADD_KEYWORD2("try", "try {\n%INDENT%%CURSOR%\n} catch() {\n$%INDENT%\n}\n");
1598         ADD_KEYWORD2("catch", "catch(%CURSOR%) {\n%INDENT%\n}\n");
1599         ADD_KEYWORD2("clone", "clone %CURSOR%;\n");
1600         ADD_KEYWORD2("continue", "continue;\n");
1601         ADD_KEYWORD2("declare", "declare(%CURSOR%);\n");
1602         ADD_KEYWORD2("default", "default:\n%INDENT%%CURSOR%\n%INDENT%break;\n");
1603         ADD_KEYWORD2("do", "do {\n%INDENT%%CURSOR%\n} while();\n");
1604         ADD_KEYWORD2("echo", "echo %CURSOR%;\n");
1605         ADD_KEYWORD2("else", "else {\n%INDENT%%CURSOR%\n}\n");
1606         ADD_KEYWORD2("elseif", "elseif (%CURSOR%) {\n%INDENT%\n}\n");
1607         ADD_KEYWORD2("endif", "endif;");
1608         ADD_KEYWORD2("endforeach", "endforeach;");
1609         ADD_KEYWORD2("endswitch", "endswitch;");
1610         ADD_KEYWORD2("endwhile", "endwhile;");
1611         ADD_KEYWORD2("endfor", "endfor;");
1612         ADD_KEYWORD2("enddeclare", "enddeclare;");
1613         ADD_KEYWORD2("empty", "empty(%CURSOR%)");
1614         ADD_KEYWORD2("eval", "eval(%CURSOR%)");
1615         ADD_KEYWORD2("die", "die(%CURSOR%);\n");
1616         ADD_KEYWORD2("exit", "exit(%CURSOR%);\n");
1617         ///TODO: only activate when after "class NAME "
1618         ADD_KEYWORD("extends");
1619         ADD_KEYWORD("implements");
1620         ADD_KEYWORD2("for", "for(%CURSOR%;;) {\n%INDENT%\n}\n");
1621         ADD_KEYWORD2("foreach", "foreach(%CURSOR%) {\n%INDENT%\n}\n");
1622         ADD_KEYWORD2("function", "function %SELECT%NAME%ENDSELECT%() {\n%INDENT%\n}\n");
1623         ADD_KEYWORD2("global", "global $%CURSOR%;");
1624         ADD_KEYWORD2("if", "if (%CURSOR%) {\n%INDENT%\n}\n");
1625         ADD_KEYWORD2("include", "include '%CURSOR%';\n");
1626         ADD_KEYWORD2("include_once", "include_once '%CURSOR%';\n");
1627         ADD_KEYWORD2("require", "require '%CURSOR%';\n");
1628         ADD_KEYWORD2("require_once", "require_once '%CURSOR%';\n");
1629         ADD_KEYWORD2("isset", "isset(%CURSOR%)");
1630         ADD_KEYWORD2("list", "list(%CURSOR%)");
1631         ADD_KEYWORD2("print", "print %CURSOR%;\n");
1632         ADD_KEYWORD2("return", "return %CURSOR%;\n");
1633         ADD_KEYWORD2("static", "static $%CURSOR%%;\n");
1634         ADD_KEYWORD2("unset", "unset(%CURSOR%);\n");
1635         ADD_KEYWORD2("while", "while (%CURSOR%) {\n%INDENT%\n}\n");
1636         ADD_KEYWORD2("switch", "switch (%CURSOR%) {\n%INDENT%\n}\n");
1637     }
1638
1639     return items;
1640 }
1641
1642 inline bool CodeCompletionContext::isValidCompletionItem(Declaration* dec)
1643 {
1644     if ( dec->range().isEmpty() ) {
1645         // hack for included files
1646         return false;
1647     }
1648     if ( dec->kind() == Declaration::Type && dec->qualifiedIdentifier().isEmpty() ) {
1649         // filter closures
1650         return false;
1651     }
1652
1653     static DUChainPointer<ClassDeclaration> exceptionDecl;
1654     if (!exceptionDecl) {
1655         /// Qualified identifier for 'exception'
1656         static const KDevelop::QualifiedIdentifier exceptionQId("exception");
1657         QList<Declaration*> decs = dec->context()->findDeclarations(exceptionQId);
1658         Q_ASSERT(decs.count());
1659         exceptionDecl = dynamic_cast<ClassDeclaration*>(decs.first());
1660         Q_ASSERT(exceptionDecl);
1661     }
1662
1663     if (m_memberAccessOperation == ExceptionChoose
1664             || m_memberAccessOperation == NewClassChoose
1665             || m_memberAccessOperation == InterfaceChoose
1666             || m_memberAccessOperation == ClassExtendsChoose
1667             || m_memberAccessOperation == InstanceOfChoose) {
1668         // filter current class
1669         if (!m_forbiddenIdentifiers.isEmpty() && m_forbiddenIdentifiers.contains(dec->qualifiedIdentifier().index())) {
1670             return false;
1671         }
1672         ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
1673
1674         // filter non-classes
1675         if (!classDec) {
1676             return false;
1677         }
1678         // show non-interface and non-abstract
1679         else if (m_memberAccessOperation == NewClassChoose) {
1680             return !(classDec->classModifier() & ClassDeclarationData::Abstract)
1681                    && classDec->classType() == ClassDeclarationData::Class;
1682         }
1683         // filter non-exception classes
1684         else if (m_memberAccessOperation == ExceptionChoose) {
1685             return classDec->equalQualifiedIdentifier(exceptionDecl.data())
1686                    || classDec->isPublicBaseClass(exceptionDecl.data(), m_duContext->topContext());
1687         }
1688         // show interfaces
1689         else if (m_memberAccessOperation == InterfaceChoose) {
1690             return classDec->classType() == ClassDeclarationData::Interface;
1691         }
1692         // show anything but final classes and interfaces
1693         else if (m_memberAccessOperation == ClassExtendsChoose) {
1694             return !(classDec->classModifier() & ClassDeclarationData::Final)
1695                    && classDec->classType() == ClassDeclarationData::Class;
1696         }
1697         else if (m_memberAccessOperation == InstanceOfChoose) {
1698             return true;
1699         }
1700     }
1701
1702     if (m_memberAccessOperation == ExceptionInstanceChoose) {
1703         if (dec->kind() != Declaration::Instance) return false;
1704         StructureType::Ptr structType = dec->type<StructureType>();
1705         if (!structType) return false;
1706         ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(structType->declaration(dec->topContext()));
1707         if (!classDec) return false;
1708         return classDec->isPublicBaseClass(exceptionDecl.data(), m_duContext->topContext());
1709     }
1710
1711     if (m_memberAccessOperation == NoMemberAccess) {
1712         // filter private methods and class members when doing a global completion
1713         // when we are inside a class function, don't filter the private stuff
1714         // of the current class
1715         // NOTE: ClassFunctionDeclaration inherits ClassMemberDeclaration
1716         // NOTE: both have to have a parent context with type class
1717         if ( dec->context() && dec->context()->type() == DUContext::Class
1718                 && m_duContext->parentContext() != dec->context() )
1719         {
1720             if ( ClassMemberDeclaration* memberDec = dynamic_cast<ClassMemberDeclaration*>(dec) ) {
1721                 if ( memberDec->accessPolicy() == Declaration::Private ) {
1722                     return false;
1723                 }
1724             }
1725         }
1726         if ( !dec->isFunctionDeclaration() && m_duContext.data() == dec->context() && m_position < dec->range().start ) {
1727             return false;
1728         }
1729     }
1730
1731     if (m_memberAccessOperation == NamespaceChoose) {
1732         return dec->kind() == Declaration::Namespace;
1733     }
1734
1735     return true;
1736 }
1737
1738 QList<QSet<IndexedString> > CodeCompletionContext::completionFiles()
1739 {
1740     QList<QSet<IndexedString> > ret;
1741     if (ICore::self()) {
1742         foreach(IProject* project, ICore::self()->projectController()->projects()) {
1743             ret << project->fileSet();
1744         }
1745     }
1746     return ret;
1747 }
1748
1749 }