File indexing completed on 2024-04-21 15:24:06

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003     SPDX-FileCopyrightText: 2008 Hamish Rodda <rodda@kde.org>
0004     SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
0005     SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "context.h"
0011 
0012 #include <ktexteditor/view.h>
0013 #include <ktexteditor/document.h>
0014 #include <KLocalizedString>
0015 
0016 #include <language/duchain/ducontext.h>
0017 #include <language/duchain/duchain.h>
0018 #include <language/duchain/duchainlock.h>
0019 #include <language/duchain/types/identifiedtype.h>
0020 #include <language/duchain/types/functiontype.h>
0021 #include <language/duchain/codemodel.h>
0022 #include <language/duchain/classdeclaration.h>
0023 #include <language/duchain/types/unsuretype.h>
0024 #include <language/duchain/parsingenvironment.h>
0025 #include <language/util/includeitem.h>
0026 #include <language/codecompletion/codecompletion.h>
0027 
0028 #include <util/pushvalue.h>
0029 #include <util/path.h>
0030 
0031 #include <interfaces/icore.h>
0032 #include <interfaces/iprojectcontroller.h>
0033 #include <interfaces/iproject.h>
0034 
0035 #include <project/projectmodel.h>
0036 
0037 #include "../duchain/completioncodemodel.h"
0038 #include "../duchain/expressionparser.h"
0039 #include "../duchain/helper.h"
0040 #include "../duchain/declarations/variabledeclaration.h"
0041 #include "../duchain/declarations/classmethoddeclaration.h"
0042 #include "../duchain/types/structuretype.h"
0043 
0044 #include "../parser/phpparser.h"
0045 #include "../parser/phptokentext.h"
0046 
0047 #include "includefileitem.h"
0048 #include "codemodelitem.h"
0049 #include "completiondebug.h"
0050 #include "helpers.h"
0051 #include "implementationitem.h"
0052 #include "keyworditem.h"
0053 
0054 #include <KIO/Global>
0055 
0056 #define LOCKDUCHAIN     DUChainReadLocker lock(DUChain::lock())
0057 
0058 #define ifDebug(x)
0059 
0060 using namespace KDevelop;
0061 
0062 namespace Php
0063 {
0064 
0065 typedef QList<Parser::TokenType> TokenList;
0066 
0067 /**
0068  * Utility class which makes it easier to access the relevant parts
0069  * of the token stream for code completion.
0070  *
0071  * TODO: This class should be reviewed imo - I just hacked it together, quick'n'dirty
0072  */
0073 class TokenAccess {
0074 public:
0075     /// Setup the token stream from the input code
0076     TokenAccess(const QString &code)
0077         : m_code(code)
0078     {
0079 
0080         Lexer lexer(&m_stream, code);
0081         int token;
0082         while ((token = lexer.nextTokenKind())) {
0083             Parser::Token &t = m_stream.push();
0084             t.begin = lexer.tokenBegin();
0085             t.end = lexer.tokenEnd();
0086             t.kind = token;
0087         }
0088         // move to last token
0089         m_pos = m_stream.size() - 1;
0090     }
0091 
0092     /// returns Token_INVALID if the position is invalid
0093     /// else returns the type of the current token
0094     Parser::TokenType type() const {
0095         if ( m_pos == -1 ) {
0096             return Parser::Token_INVALID;
0097         } else {
0098             return (Parser::TokenType) m_stream.at(m_pos).kind;
0099         }
0100     }
0101 
0102     /// convenience comparison to a tokentype
0103     bool operator==(const Parser::TokenType& other) const {
0104         return other == type();
0105     }
0106 
0107     /// move to previous token
0108     void pop() {
0109         if ( m_pos >= 0 ) {
0110             --m_pos;
0111         }
0112     }
0113 
0114     /// move relative to current token
0115     /// NOTE: make sure you honor the boundaries.
0116     void moveTo(const qint64 &relPos) {
0117         m_pos += relPos;
0118         Q_ASSERT(m_pos > 0);
0119         Q_ASSERT(m_pos < m_stream.size());
0120     }
0121 
0122     /// get type of token relative to current position
0123     /// returns Token_INVALID if the position goes out of the boundaries
0124     int typeAt(const qint64 &relPos) const {
0125         const qint64 pos = m_pos + relPos;
0126         if ( pos >= 0 && pos < m_stream.size() ) {
0127             return m_stream.at(pos).kind;
0128         } else {
0129             return Parser::Token_INVALID;
0130         }
0131     }
0132 
0133     /// Get string for token at a given position relative to the current one.
0134     /// NOTE: Make sure you honor the boundaries.
0135     QString stringAt(const qint64 &relPos) const {
0136         Parser::Token token = at(relPos);
0137         return m_code.mid(token.begin, token.end - token.begin + 1);
0138     }
0139 
0140     /// check whether the current token is prepended by the list of tokens
0141     /// @return -1 when not prepended by the list, else the relative index-position
0142     qint64 prependedBy(const TokenList &list, bool skipWhitespace = false ) const {
0143         // this would be useless, hence forbid it
0144         Q_ASSERT ( !list.isEmpty() );
0145 
0146         if ( m_pos < list.count() - 1 ) {
0147             // not enough tokens
0148             return -1;
0149         } else {
0150             uint pos = 1;
0151             foreach ( Parser::TokenType type, list ) {
0152                 if ( skipWhitespace && m_stream.at( m_pos - pos).kind == Parser::Token_WHITESPACE ) {
0153                     ++pos;
0154                 }
0155                 if ( m_stream.at( m_pos - pos).kind == type ) {
0156                     ++pos;
0157                     continue;
0158                 } else {
0159                     return -1;
0160                 }
0161             }
0162             return pos;
0163         }
0164     }
0165 
0166     /// Get the token relative to the current one.
0167     /// NOTE: Make sure you honor the boundaries.
0168     Parser::Token at(const qint64 &relPos) const {
0169         const qint64 pos = m_pos + relPos;
0170         Q_ASSERT(pos >= 0);
0171         Q_ASSERT(pos < m_stream.size());
0172         return m_stream.at(pos);
0173     }
0174 
0175 private:
0176     const QString m_code;
0177     TokenStream m_stream;
0178     qint64 m_pos;
0179 };
0180 
0181 /**
0182  * Pops all tokens from the @p lastToken and stops at the LPAREN.
0183  */
0184 void removeOtherArguments(TokenAccess &lastToken)
0185 {
0186     Q_ASSERT(lastToken.type() == Parser::Token_COMMA);
0187 
0188     // remove all other arguments
0189     int openLParen = 0;
0190     do {
0191         lastToken.pop();
0192         if ( lastToken.type() == Parser::Token_RPAREN ) {
0193             ++openLParen;
0194         } else if ( lastToken.type() == Parser::Token_LPAREN ) {
0195             if ( openLParen == 0 ) {
0196                 return;
0197             } else {
0198                 --openLParen;
0199             }
0200         }
0201     } while ( lastToken.type() != Parser::Token_INVALID );
0202 }
0203 
0204 /**
0205  * if token at @p pos is whitespace, decrease pos by one.
0206  */
0207 inline void skipWhiteSpace(const TokenAccess &lastToken, qint64 &pos)
0208 {
0209     if ( lastToken.typeAt(pos) == Parser::Token_WHITESPACE ) {
0210         --pos;
0211     }
0212 }
0213 
0214 /// add keyword to list of completion items
0215 #define ADD_KEYWORD(x) items << CompletionTreeItemPointer( new KeywordItem( QStringLiteral(x), Php::CodeCompletionContext::Ptr(this) ) )
0216 #define ADD_KEYWORD2(x, y) items << CompletionTreeItemPointer( new KeywordItem( QStringLiteral(x), Php::CodeCompletionContext::Ptr(this), QStringLiteral(y) ) )
0217 
0218 int completionRecursionDepth = 0;
0219 
0220 CodeCompletionContext::CodeCompletionContext(KDevelop::DUContextPointer context, const QString& text, const QString& followingText, const KDevelop::CursorInRevision& position, int depth)
0221         : KDevelop::CodeCompletionContext(context, text, position, depth)
0222         , m_memberAccessOperation(NoMemberAccess), m_parentAccess(false), m_isFileCompletionAfterDirname(false)
0223 {
0224     // use other ctor for parents
0225     Q_ASSERT(depth == 0);
0226 
0227     ifDebug(qCDebug(COMPLETION) << "non-processed text: " + text;)
0228 
0229     if ( context->type() == DUContext::Class || context->type() == DUContext::Function || context->type() == DUContext::Other
0230         || context->type() == DUContext::Namespace )
0231     {
0232         if ( !m_parentContext && !m_text.startsWith(QLatin1String("<?php ")) ) {
0233             ifDebug(qCDebug(COMPLETION) << "added start tag: " + m_text;)
0234             m_text.prepend("<?php ");
0235         }
0236     }
0237 
0238     m_valid = !m_text.isEmpty();
0239 
0240     if (!m_valid) {
0241         qCDebug(COMPLETION) << "empty completion text";
0242         return;
0243     }
0244 
0245     TokenAccess lastToken(m_text);
0246 //     ifDebug(qCDebug(COMPLETION) << "clearing completion text");)
0247 //     m_text.clear();
0248 
0249     /// even when we skip to some more meaning ful token, this will
0250     /// always be the end position of the last token
0251     const qint64 lastTokenEnd = lastToken.at(0).end + 1;
0252 
0253     bool lastWasWhitespace = lastToken == Parser::Token_WHITESPACE;
0254     if ( lastWasWhitespace ) {
0255         ifDebug(qCDebug(COMPLETION) << "skipping whitespace token";)
0256         lastToken.pop();
0257     }
0258 
0259     // when the text after the current token starts with /* we are inside
0260     // a multi line comment => don't offer completion
0261     if ( m_text.mid( lastTokenEnd, 2 ) == QLatin1String("/*") ) {
0262         ifDebug(qCDebug(COMPLETION) << "no completion in comments");
0263         m_valid = false;
0264         return;
0265     }
0266 
0267     ifDebug(qCDebug(COMPLETION) << tokenText(lastToken.type());)
0268 
0269     ///TODO: REFACTOR: push some stuff into its own methods
0270     ///                and call them from inside the big switch.
0271     ///                Then we can forget about having additional checks
0272     ///                beforehand and can handle it all in one place.
0273 
0274     // The following tokens require a whitespace after them for code-completion:
0275     if ( !lastWasWhitespace ) {
0276         switch ( lastToken.type() ) {
0277             case Parser::Token_EXTENDS:
0278             case Parser::Token_IMPLEMENTS:
0279             case Parser::Token_NEW:
0280             case Parser::Token_THROW:
0281                 ifDebug(qCDebug(COMPLETION) << "need whitespace after token for completion";)
0282                 m_valid = false;
0283                 return;
0284             default:
0285                 break;
0286         }
0287     }
0288 
0289     ifDebug(qCDebug(COMPLETION) << tokenText(lastToken.type());)
0290 
0291     switch ( lastToken.type() ) {
0292         case Parser::Token_COMMENT:
0293             // don't offer code completion in comments, i.e. single line comments that don't end on \n
0294             // multi-line comments are handled above
0295             if ( !lastWasWhitespace && !lastToken.stringAt(0).endsWith('\n')
0296                     && !lastToken.stringAt(0).startsWith(QLatin1String("/*")) ) {
0297                 ifDebug(qCDebug(COMPLETION) << "no completion in comments";)
0298                 m_valid = false;
0299             }
0300             break;
0301         case Parser::Token_EXTENDS:
0302             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
0303                                                    << Parser::Token_WHITESPACE << Parser::Token_CLASS) != -1 ) {
0304                 m_memberAccessOperation = ClassExtendsChoose;
0305                 forbidIdentifier(lastToken.stringAt(-2));
0306             } else if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
0307                                                    << Parser::Token_WHITESPACE << Parser::Token_INTERFACE) != -1 ) {
0308                 m_memberAccessOperation = InterfaceChoose;
0309                 forbidIdentifier(lastToken.stringAt(-2));
0310             } else {
0311                 ifDebug(qCDebug(COMPLETION) << "token prepended by bad tokens, don't do completion";)
0312                 m_valid = false;
0313             }
0314             break;
0315         case Parser::Token_IMPLEMENTS:
0316             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_STRING
0317                                                    << Parser::Token_WHITESPACE << Parser::Token_CLASS) != -1 ) {
0318                 m_memberAccessOperation = InterfaceChoose;
0319                 forbidIdentifier(lastToken.stringAt(-2));
0320             } else {
0321                 ifDebug(qCDebug(COMPLETION) << "token prepended by bad tokens, don't do completion";)
0322                 m_valid = false;
0323             }
0324             break;
0325         case Parser::Token_COMMA:
0326             {
0327             // check if we are in the list after Token_IMPLEMENTS:
0328             qint64 relPos = -1;
0329             QList<qint64> identifierPositions;
0330             while ( true ) {
0331                 skipWhiteSpace(lastToken, relPos);
0332                 if ( lastToken.typeAt(relPos) == Parser::Token_STRING ) {
0333                     identifierPositions << relPos;
0334                     --relPos;
0335                     skipWhiteSpace(lastToken, relPos);
0336                             // interfaces may extend more than one interface
0337                     if ( ( lastToken.typeAt(relPos) == Parser::Token_EXTENDS &&
0338                             lastToken.typeAt(relPos - 1) == Parser::Token_WHITESPACE &&
0339                             lastToken.typeAt(relPos - 2) == Parser::Token_STRING &&
0340                             lastToken.typeAt(relPos - 3) == Parser::Token_WHITESPACE &&
0341                             lastToken.typeAt(relPos - 4) == Parser::Token_INTERFACE )
0342                         || // classes may implement more than one interface
0343                          ( lastToken.typeAt(relPos) == Parser::Token_IMPLEMENTS &&
0344                             lastToken.typeAt(relPos - 1) == Parser::Token_WHITESPACE &&
0345                             lastToken.typeAt(relPos - 2) == Parser::Token_STRING &&
0346                             lastToken.typeAt(relPos - 3) == Parser::Token_WHITESPACE &&
0347                             lastToken.typeAt(relPos - 4) == Parser::Token_CLASS ) )
0348                     {
0349                         identifierPositions << (relPos - 2);
0350                         m_memberAccessOperation = InterfaceChoose;
0351                         break;
0352                     } else if ( lastToken.typeAt(relPos) == Parser::Token_COMMA ) {
0353                         // skip to next entry
0354                         --relPos;
0355                         continue;
0356                     }
0357                 } else {
0358                     break;
0359                 }
0360             }
0361             if ( m_memberAccessOperation == InterfaceChoose ) {
0362                 ifDebug(qCDebug(COMPLETION) << "in implementation list";)
0363                 m_memberAccessOperation = InterfaceChoose;
0364                 foreach ( qint64 pos, identifierPositions ) {
0365                     forbidIdentifier(lastToken.stringAt(pos));
0366                 }
0367             } else {
0368                 // else do function call completion
0369                 m_memberAccessOperation = FunctionCallAccess;
0370 
0371                 ///TODO: global, static etc. enumerations.
0372                 removeOtherArguments(lastToken);
0373 
0374                 if ( lastToken.type() == Parser::Token_INVALID ) {
0375                     m_valid = false;
0376                 }
0377             }
0378             }
0379             break;
0380         case Parser::Token_OPEN_TAG:
0381             // don't do completion if no whitespace is given and there is some text following,
0382             // esp. for stuff like <?php <?ph <?p
0383             if ( !lastWasWhitespace && !followingText.isEmpty() ) {
0384                 ifDebug(qCDebug(COMPLETION) << "no completion because <? is followed by" + followingText;)
0385                 m_valid = false;
0386             } else {
0387                 // else just do normal completion
0388                 m_memberAccessOperation = NoMemberAccess;
0389             }
0390             break;
0391         case Parser::Token_OBJECT_OPERATOR:
0392             m_memberAccessOperation = MemberAccess;
0393             lastToken.pop();
0394             break;
0395         case Parser::Token_PAAMAYIM_NEKUDOTAYIM:
0396             m_memberAccessOperation = StaticMemberAccess;
0397             lastToken.pop();
0398             break;
0399         case Parser::Token_LPAREN:
0400             {
0401             qint64 pos = -1;
0402             skipWhiteSpace(lastToken, pos);
0403             if ( lastToken.typeAt(pos) == Parser::Token_CATCH ) {
0404                 m_memberAccessOperation = ExceptionChoose;
0405             } else if ( lastToken.typeAt(pos) == Parser::Token_ARRAY ) {
0406                 m_memberAccessOperation = NoMemberAccess;
0407                 ifDebug(qCDebug(COMPLETION) << "NoMemberAccess";)
0408                 ifDebug(qCDebug(COMPLETION) << "returning early";)
0409                 return;
0410             } else {
0411                 m_memberAccessOperation = FunctionCallAccess;
0412             }
0413             }
0414             break;
0415         case Parser::Token_NEW:
0416             if ( lastToken.prependedBy(TokenList() << Parser::Token_WHITESPACE << Parser::Token_THROW) != -1 ) {
0417                 m_memberAccessOperation = ExceptionChoose;
0418             } else {
0419                 m_memberAccessOperation = NewClassChoose;
0420             }
0421             break;
0422         case Parser::Token_THROW:
0423             m_memberAccessOperation = ExceptionInstanceChoose;
0424             break;
0425         case Parser::Token_CONSTANT_ENCAPSED_STRING:
0426             {
0427                 // support something like `include dirname(__FILE__) . "/...`
0428                 bool isAfterDirname = false;
0429                 //NOTE: prependedBy will return -1 on failure, this is what we need in these cases
0430                 //      on success it will return a positive number, we'll need to switch it's sign in that case
0431                 qint64 relPos = lastToken.prependedBy(TokenList() << Parser::Token_CONCAT << Parser::Token_RPAREN << Parser::Token_FILE
0432                                                    << Parser::Token_LPAREN << Parser::Token_STRING, true);
0433                 if ( relPos != -1 ) {
0434                     // switch sign
0435                     relPos = -relPos;
0436                     if ( lastToken.stringAt(relPos + 1).compare(QLatin1String("dirname"), Qt::CaseInsensitive) == 0 ) {
0437                         isAfterDirname = true;
0438                     }
0439                 } else {
0440                     relPos = lastToken.prependedBy(TokenList() << Parser::Token_CONCAT << Parser::Token_DIR, true);
0441 
0442                     if ( relPos != -1 ) {
0443                         // switch sign
0444                         relPos = -relPos;
0445                         isAfterDirname = true;
0446                     }
0447                 }
0448 
0449                 skipWhiteSpace(lastToken, relPos);
0450                 if ( lastToken.typeAt(relPos) == Parser::Token_LPAREN ) {
0451                     --relPos;
0452                 }
0453                 skipWhiteSpace(lastToken, relPos);
0454                 switch ( lastToken.typeAt(relPos) ) {
0455                     case Parser::Token_REQUIRE:
0456                     case Parser::Token_REQUIRE_ONCE:
0457                     case Parser::Token_INCLUDE:
0458                     case Parser::Token_INCLUDE_ONCE:
0459                         m_memberAccessOperation = FileChoose;
0460                         m_expression = m_text.mid( lastToken.at(0).begin + 1 ).append(followingText).trimmed();
0461                         m_isFileCompletionAfterDirname = isAfterDirname;
0462                         break;
0463                     default:
0464                         if ( m_text.at( lastToken.at(0).begin ).unicode() == '"' ) {
0465                             ///TODO: only offer variable completion
0466                             m_valid = false;
0467                         } else {
0468                             // in or after constant strings ('...') don't offer completion at all
0469                             m_valid = false;
0470                         }
0471                         break;
0472                 }
0473                 break;
0474             }
0475             break;
0476         case Parser::Token_INSTANCEOF:
0477             m_memberAccessOperation = InstanceOfChoose;
0478             break;
0479         case Parser::Token_AND_ASSIGN:
0480         case Parser::Token_ARRAY_CAST:
0481         case Parser::Token_ASSIGN:
0482         case Parser::Token_AT:
0483         case Parser::Token_BANG:
0484         case Parser::Token_BIT_AND:
0485         case Parser::Token_BIT_OR:
0486         case Parser::Token_BIT_XOR:
0487         case Parser::Token_BOOLEAN_AND:
0488         case Parser::Token_BOOLEAN_OR:
0489         case Parser::Token_BOOL_CAST:
0490         case Parser::Token_COLON:
0491         case Parser::Token_CONCAT:
0492         case Parser::Token_CONCAT_ASSIGN:
0493         case Parser::Token_CURLY_OPEN:
0494         case Parser::Token_DEC:
0495         case Parser::Token_DIV:
0496         case Parser::Token_DIV_ASSIGN:
0497         case Parser::Token_DOC_COMMENT:
0498         case Parser::Token_DOLLAR_OPEN_CURLY_BRACES:
0499         case Parser::Token_DOUBLE_ARROW:
0500         case Parser::Token_DOUBLE_CAST:
0501         case Parser::Token_DOUBLE_QUOTE:
0502         case Parser::Token_ECHO:
0503         case Parser::Token_ELLIPSIS:
0504         case Parser::Token_ENCAPSED_AND_WHITESPACE:
0505         case Parser::Token_EXIT:
0506         case Parser::Token_INC:
0507         case Parser::Token_INT_CAST:
0508         case Parser::Token_IS_EQUAL:
0509         case Parser::Token_IS_GREATER:
0510         case Parser::Token_IS_GREATER_OR_EQUAL:
0511         case Parser::Token_IS_IDENTICAL:
0512         case Parser::Token_IS_NOT_EQUAL:
0513         case Parser::Token_IS_NOT_IDENTICAL:
0514         case Parser::Token_IS_SMALLER:
0515         case Parser::Token_IS_SMALLER_OR_EQUAL:
0516         case Parser::Token_LBRACE:
0517         case Parser::Token_LBRACKET:
0518         case Parser::Token_LOGICAL_AND:
0519         case Parser::Token_LOGICAL_OR:
0520         case Parser::Token_LOGICAL_XOR:
0521         case Parser::Token_MINUS:
0522         case Parser::Token_MINUS_ASSIGN:
0523         case Parser::Token_MOD:
0524         case Parser::Token_MOD_ASSIGN:
0525         case Parser::Token_MUL:
0526         case Parser::Token_MUL_ASSIGN:
0527         case Parser::Token_NULL_COALESCE:
0528         case Parser::Token_OBJECT_CAST:
0529         case Parser::Token_OPEN_TAG_WITH_ECHO:
0530         case Parser::Token_OR_ASSIGN:
0531         case Parser::Token_PLUS:
0532         case Parser::Token_PLUS_ASSIGN:
0533         case Parser::Token_PRINT:
0534         case Parser::Token_QUESTION:
0535         case Parser::Token_RBRACE:
0536         case Parser::Token_RETURN:
0537         case Parser::Token_SEMICOLON:
0538         case Parser::Token_SL:
0539         case Parser::Token_SL_ASSIGN:
0540         case Parser::Token_SPACESHIP:
0541         case Parser::Token_SR:
0542         case Parser::Token_SR_ASSIGN:
0543         case Parser::Token_START_HEREDOC:
0544         case Parser::Token_START_NOWDOC:
0545         case Parser::Token_STRING:
0546         case Parser::Token_STRING_CAST:
0547         case Parser::Token_TILDE:
0548         case Parser::Token_UNSET_CAST:
0549         case Parser::Token_XOR_ASSIGN:
0550         case Parser::Token_EXP:
0551         case Parser::Token_EXP_ASSIGN:
0552             // normal completion is valid
0553             if ( duContext() && duContext()->type() == DUContext::Class ) {
0554                 // when we are inside a class context, give overloadable members as completion
0555                 m_memberAccessOperation = ClassMemberChoose;
0556             } else {
0557                 m_memberAccessOperation = NoMemberAccess;
0558             }
0559             break;
0560         case Parser::Token_ABSTRACT:
0561         case Parser::Token_CONST:
0562         case Parser::Token_FINAL:
0563         case Parser::Token_PUBLIC:
0564         case Parser::Token_PRIVATE:
0565         case Parser::Token_PROTECTED:
0566         case Parser::Token_STATIC:
0567         case Parser::Token_VAR:
0568             if ( duContext() && duContext()->type() == DUContext::Class ) {
0569                 // when we are inside a class context, give overloadable members as completion
0570                 m_memberAccessOperation = ClassMemberChoose;
0571             } else {
0572                 m_valid = false;
0573             }
0574             break;
0575         case Parser::Token_NAMESPACE:
0576         case Parser::Token_BACKSLASH:
0577         {
0578             QString identifier;
0579             qint64 relPos = 0;
0580             while (lastToken.typeAt(relPos) == Parser::Token_STRING || lastToken.typeAt(relPos) == Parser::Token_BACKSLASH) {
0581                 if (lastToken.typeAt(relPos) == Parser::Token_BACKSLASH) {
0582                     identifier.prepend("::");
0583                 } else {
0584                     identifier.prepend(lastToken.stringAt(relPos));
0585                 }
0586                 --relPos;
0587             }
0588             if ( lastToken.typeAt(relPos) == Parser::Token_NAMESPACE ) {
0589                 m_memberAccessOperation = NamespaceChoose;
0590             } else {
0591                 m_memberAccessOperation = BackslashAccess;
0592             }
0593             m_namespace = QualifiedIdentifier(identifier);
0594             break;
0595         }
0596         case Parser::Token_ARRAY:
0597         case Parser::Token_AS:
0598         case Parser::Token_BACKTICK:
0599         case Parser::Token_BREAK:
0600         case Parser::Token_CALLABLE:
0601         case Parser::Token_CASE:
0602         case Parser::Token_CATCH:
0603         case Parser::Token_CLASS:
0604         case Parser::Token_CLASS_C:
0605         case Parser::Token_CLONE:
0606         case Parser::Token_CLOSE_TAG:
0607         case Parser::Token_CONTINUE:
0608         case Parser::Token_DECLARE:
0609         case Parser::Token_DEFAULT:
0610         case Parser::Token_DIR:
0611         case Parser::Token_DNUMBER:
0612         case Parser::Token_DO:
0613         case Parser::Token_DOLLAR:
0614         case Parser::Token_ELSE:
0615         case Parser::Token_ELSEIF:
0616         case Parser::Token_EMPTY:
0617         case Parser::Token_ENDDECLARE:
0618         case Parser::Token_ENDFOR:
0619         case Parser::Token_ENDFOREACH:
0620         case Parser::Token_ENDIF:
0621         case Parser::Token_ENDSWITCH:
0622         case Parser::Token_ENDWHILE:
0623         case Parser::Token_END_HEREDOC:
0624         case Parser::Token_END_NOWDOC:
0625         case Parser::Token_EOF:
0626         case Parser::Token_EVAL:
0627         case Parser::Token_FILE:
0628         case Parser::Token_FINALLY:
0629         case Parser::Token_FOR:
0630         case Parser::Token_FOREACH:
0631         case Parser::Token_FUNCTION:
0632         case Parser::Token_FUNC_C:
0633         case Parser::Token_GLOBAL:
0634         case Parser::Token_HALT_COMPILER:
0635         case Parser::Token_IF:
0636         case Parser::Token_INCLUDE:
0637         case Parser::Token_INCLUDE_ONCE:
0638         case Parser::Token_INLINE_HTML:
0639         case Parser::Token_INSTEADOF:
0640         case Parser::Token_INTERFACE:
0641         case Parser::Token_INVALID:
0642         case Parser::Token_ISSET:
0643         case Parser::Token_LINE:
0644         case Parser::Token_LIST:
0645         case Parser::Token_LNUMBER:
0646         case Parser::Token_METHOD_C:
0647         case Parser::Token_NAMESPACE_C:
0648         case Parser::Token_NUM_STRING:
0649         case Parser::Token_REQUIRE:
0650         case Parser::Token_REQUIRE_ONCE:
0651         case Parser::Token_RBRACKET:
0652         case Parser::Token_RPAREN:
0653         case Parser::Token_STRING_VARNAME:
0654         case Parser::Token_SWITCH:
0655         case Parser::Token_TRAIT:
0656         case Parser::Token_TRAIT_C:
0657         case Parser::Token_TRY:
0658         case Parser::Token_UNSET:
0659         case Parser::Token_USE:
0660         case Parser::Token_VARIABLE:
0661         case Parser::Token_VOID:
0662         case Parser::Token_WHILE:
0663         case Parser::Token_WHITESPACE:
0664         case Parser::Token_YIELD:
0665         case Parser::Token_YIELD_FROM:
0666         /// TODO: code completion after goto
0667         case Parser::Token_GOTO:
0668         case Parser::TokenTypeSize:
0669             ifDebug(qCDebug(COMPLETION) << "no completion after this token";)
0670             m_valid = false;
0671             break;
0672     }
0673 
0674     ifDebug(
0675         switch ( m_memberAccessOperation ) {
0676             case FileChoose:
0677                 qCDebug(COMPLETION) << "FileChoose";
0678                 break;
0679             case ExceptionInstanceChoose:
0680                 qCDebug(COMPLETION) << "ExceptionInstanceChoose";
0681                 break;
0682             case ExceptionChoose:
0683                 qCDebug(COMPLETION) << "ExceptionChoose";
0684                 break;
0685             case ClassMemberChoose:
0686                 qCDebug(COMPLETION) << "ClassMemberChoose";
0687                 break;
0688             case NoMemberAccess:
0689                 qCDebug(COMPLETION) << "NoMemberAccess";
0690                 break;
0691             case NewClassChoose:
0692                 qCDebug(COMPLETION) << "NewClassChoose";
0693                 break;
0694             case FunctionCallAccess:
0695                 qCDebug(COMPLETION) << "FunctionCallAccess";
0696                 break;
0697             case InterfaceChoose:
0698                 qCDebug(COMPLETION) << "InterfaceChoose";
0699                 break;
0700             case ClassExtendsChoose:
0701                 qCDebug(COMPLETION) << "ClassExtendsChoose";
0702                 break;
0703             case MemberAccess:
0704                 qCDebug(COMPLETION) << "MemberAccess";
0705                 break;
0706             case StaticMemberAccess:
0707                 qCDebug(COMPLETION) << "StaticMemberAccess";
0708                 break;
0709             case InstanceOfChoose:
0710                 qCDebug(COMPLETION) << "InstanceOfChoose";
0711                 break;
0712             case NamespaceChoose:
0713                 qCDebug(COMPLETION) << "NamespaceChoose";
0714                 break;
0715             case BackslashAccess:
0716                 qCDebug(COMPLETION) << "BackslashAccess";
0717                 break;
0718         }
0719     )
0720 
0721     ifDebug(qCDebug(COMPLETION) << tokenText(lastToken.type());)
0722 
0723     // if it's not valid, we should return early
0724     if ( !m_valid ) {
0725         ifDebug(qCDebug(COMPLETION) << "invalid completion";)
0726         return;
0727     }
0728 
0729     // trim the text to the end position of the current token
0730     m_text = m_text.left(lastToken.at(0).end + 1).trimmed();
0731     ifDebug(qCDebug(COMPLETION) << "trimmed text: " << m_text;)
0732 
0733     // check whether we need the expression or have everything we need and can return early
0734     switch ( m_memberAccessOperation ) {
0735         // these access operations don't need the previous expression evaluated
0736         case FileChoose:
0737         case ClassMemberChoose:
0738         case InterfaceChoose:
0739         case NewClassChoose:
0740         case ExceptionChoose:
0741         case ExceptionInstanceChoose:
0742         case ClassExtendsChoose:
0743         case NoMemberAccess:
0744         case InstanceOfChoose:
0745         case NamespaceChoose:
0746         case BackslashAccess:
0747             ifDebug(qCDebug(COMPLETION) << "returning early";)
0748             return;
0749         case FunctionCallAccess:
0750             m_memberAccessOperation = NoMemberAccess;
0751             Q_ASSERT(lastToken.type() == Parser::Token_LPAREN);
0752             if ( lastToken.prependedBy(TokenList() << Parser::Token_STRING, true) == -1 &&
0753                  lastToken.prependedBy(TokenList() << Parser::Token_VARIABLE, true) == -1 )
0754             {
0755                 // handle for, foreach, while, etc.
0756                 ifDebug(qCDebug(COMPLETION) << "NoMemberAccess (no function call)";)
0757             } else {
0758                 //The first context should never be a function-call context,
0759                 //so make this a NoMemberAccess context and the parent a function-call context.
0760                 ifDebug(qCDebug(COMPLETION) << "NoMemberAccess (creating parentContext for function call)";)
0761                 m_parentContext = new CodeCompletionContext(m_duContext, m_position, lastToken, depth + 1);
0762             }
0763             return;
0764         case MemberAccess:
0765         case StaticMemberAccess:
0766             // these types need the expression evaluated
0767             break;
0768     }
0769 
0770     evaluateExpression(lastToken);
0771 }
0772 
0773 CodeCompletionContext::CodeCompletionContext(KDevelop::DUContextPointer context, const KDevelop::CursorInRevision& position,
0774                                              TokenAccess& lastToken,  int depth)
0775         : KDevelop::CodeCompletionContext(context, QString(), position, depth)
0776         , m_memberAccessOperation(NoMemberAccess), m_parentAccess(false), m_isFileCompletionAfterDirname(false)
0777 {
0778     switch ( lastToken.type() ) {
0779         case Parser::Token_LPAREN:
0780             m_memberAccessOperation = FunctionCallAccess;
0781             break;
0782         default:
0783             qCDebug(COMPLETION) << "unhandled token type for parent context" << tokenText(lastToken.typeAt(0));
0784             Q_ASSERT(false);
0785             m_valid = false;
0786             return;
0787     }
0788 
0789     evaluateExpression(lastToken);
0790 }
0791 
0792 void CodeCompletionContext::evaluateExpression(TokenAccess& lastToken)
0793 {
0794     /// token pos
0795     qint64 startPos = 0;
0796     int openLParen = 0;
0797 
0798     if ( m_memberAccessOperation == FunctionCallAccess ) {
0799         Q_ASSERT(lastToken.type() == Parser::Token_LPAREN);
0800         // check ctor call
0801         qint64 pos = lastToken.prependedBy(TokenList() << Parser::Token_STRING << Parser::Token_NEW, true);
0802         if ( pos != -1 ) {
0803             startPos = -pos;
0804             ifDebug(qCDebug(COMPLETION) << "ctor call";)
0805         } else {
0806             // simple function call, get it's expression
0807             startPos = -1;
0808             ifDebug(qCDebug(COMPLETION) << "simple function call";)
0809         }
0810     }
0811 
0812     static const QList<int> defaultStopTokens = QList<int>()
0813             << Parser::Token_SEMICOLON << Parser::Token_INVALID << Parser::Token_OPEN_TAG
0814             << Parser::Token_OPEN_TAG_WITH_ECHO << Parser::Token_LBRACE << Parser::Token_RBRACE
0815             << Parser::Token_IF << Parser::Token_WHILE << Parser::Token_FOR << Parser::Token_FOREACH
0816             << Parser::Token_SWITCH << Parser::Token_ELSEIF;
0817 
0818 
0819     // find expression start
0820     while ( !defaultStopTokens.contains(lastToken.typeAt(startPos)) &&
0821             (m_memberAccessOperation == FunctionCallAccess || lastToken.typeAt(startPos) != Parser::Token_COMMA) )
0822     {
0823         if ( lastToken.typeAt(startPos) == Parser::Token_LPAREN ) {
0824             ++openLParen;
0825             if ( openLParen > 0 ) {
0826                 break;
0827             }
0828         } else if ( lastToken.typeAt(startPos) == Parser::Token_RPAREN ) {
0829             --openLParen;
0830         }
0831         --startPos;
0832     }
0833 
0834     if ( openLParen < 0 ) {
0835         ifDebug(qCDebug(COMPLETION) << "too many closed parenthesis";)
0836         m_valid = false;
0837         return;
0838     }
0839 
0840     // we actually incorporate the not-wanted token, hence move forward
0841     ++startPos;
0842 
0843     if ( lastToken.typeAt(startPos) == Parser::Token_WHITESPACE ) {
0844         ++startPos;
0845     }
0846 
0847     if ( lastToken.typeAt(startPos) == Parser::Token_RETURN ) {
0848         ///TODO: match against function return type
0849         ++startPos;
0850 
0851         if ( lastToken.typeAt(startPos) == Parser::Token_WHITESPACE ) {
0852             ++startPos;
0853         }
0854     }
0855 
0856     if ( m_memberAccessOperation == StaticMemberAccess ) {
0857         if ( lastToken.typeAt(startPos) != Parser::Token_STRING ) {
0858             ifDebug(qCDebug(COMPLETION) << "unsupported token for start member access:" << tokenText(lastToken.typeAt(startPos));)
0859             m_valid = false;
0860             return;
0861         }
0862 
0863         const QString identifier(lastToken.stringAt(startPos).toLower());
0864 
0865         if ( identifier == QLatin1String("self") || identifier == QLatin1String("parent") || identifier == QLatin1String("static") ) {
0866             // self and parent are only accessible from within a member function of a class
0867             if (DUContext* parent = m_duContext->parentContext()) {
0868                 LOCKDUCHAIN;
0869                 ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(parent->owner());
0870                 if (classDec) {
0871                     if (identifier == QLatin1String("parent")) {
0872                         FOREACH_FUNCTION(const BaseClassInstance& base, classDec->baseClasses) {
0873                             if (StructureType::Ptr classType = base.baseClass.type<StructureType>()) {
0874                                 if (ClassDeclaration* baseClass = dynamic_cast<ClassDeclaration*>(classType->declaration(m_duContext->topContext()))) {
0875                                     if (baseClass->classType() == ClassDeclarationData::Class
0876                                             && baseClass->classModifier() != ClassDeclarationData::Abstract) {
0877                                         ifDebug(qCDebug(COMPLETION) << "correction: parent can do MemberAccess");
0878                                         m_parentAccess = true;
0879                                         m_memberAccessOperation = MemberAccess;
0880                                         m_expressionResult.setDeclaration(baseClass);
0881                                         break;
0882                                     }
0883                                 }
0884                             }
0885                         }
0886                         if (!m_parentAccess) {
0887                             ifDebug(qCDebug(COMPLETION) << "class has no accessible parent class");
0888                             m_valid = false;
0889                             return;
0890                         }
0891                     } else {
0892                         m_expressionResult.setDeclaration(parent->owner());
0893                     }
0894                 }
0895             }
0896         } else {
0897             QualifiedIdentifier id(identifier);
0898 
0899             m_expressionResult.setDeclaration(findDeclarationImportHelper(duContext(), id, ClassDeclarationType));
0900         }
0901     } else {
0902         // Now get the string of the expression and evaluate it
0903         Q_ASSERT(m_expression.isEmpty());
0904 
0905         for (qint64 i = startPos; i <= 0; ++i ) {
0906             m_expression += lastToken.stringAt(i);
0907         }
0908 
0909         m_expression = m_expression.trimmed();
0910 
0911         // make sure the expression is valid
0912         if (m_memberAccessOperation == FunctionCallAccess) {
0913             m_expression.append(')');
0914         }
0915         for ( int i = openLParen; i > 0; --i ) {
0916             m_expression.append(')');
0917         }
0918 
0919         ifDebug(qCDebug(COMPLETION) << "expression: " << m_expression;)
0920 
0921         if ( !m_expression.isEmpty() ) {
0922             ExpressionParser expressionParser;
0923             m_expressionResult = expressionParser.evaluateType(m_expression.toUtf8(), m_duContext, m_position);
0924         }
0925 
0926         if (m_expressionResult.type()) {
0927             LOCKDUCHAIN;
0928             ifDebug(qCDebug(COMPLETION) << "expression type: " << m_expressionResult.type()->toString();)
0929         } else {
0930             ifDebug(qCDebug(COMPLETION) << QString("expression could not be evaluated"));
0931             if ( m_memberAccessOperation == FunctionCallAccess ) {
0932                 ifDebug(qCDebug(COMPLETION) << "function not found";)
0933                 return;
0934             }
0935             m_valid = false;
0936             return;
0937         }
0938     }
0939 
0940     lastToken.moveTo(startPos);
0941 
0942     // Handle recursive contexts (Example: "ret = function1(param1, function2(" )
0943     if ( lastToken.typeAt(-1) == Parser::Token_LPAREN ||
0944          lastToken.typeAt(-1) == Parser::Token_COMMA ) {
0945         //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.
0946 
0947         lastToken.moveTo(-1);
0948         if ( lastToken.type() == Parser::Token_COMMA ) {
0949             removeOtherArguments(lastToken);
0950             if ( lastToken.type() == Parser::Token_INVALID ) {
0951                 ifDebug(qCDebug(COMPLETION) << QString("Could not find start position for parent function-call. Aborting.");)
0952                 m_valid = false;
0953                 return;
0954             }
0955         }
0956 
0957         if ( lastToken.prependedBy(TokenList() << Parser::Token_STRING, true) == -1 ) {
0958             // handle for, foreach, while, etc.
0959             ifDebug(qCDebug(COMPLETION) << "No real function call";)
0960             return;
0961         }
0962 
0963         ifDebug(qCDebug(COMPLETION) << QString("Recursive function-call: creating parent context"));
0964         m_parentContext = new CodeCompletionContext(m_duContext, m_position, lastToken, m_depth + 1);
0965 
0966         if (!m_parentContext->isValid()) {
0967             m_parentContext = nullptr;
0968             m_valid = false;
0969             return;
0970         }
0971     }
0972 }
0973 
0974 void CodeCompletionContext::forbidIdentifier(const QString& identifier)
0975 {
0976     QualifiedIdentifier id(identifier.toLower());
0977 
0978     ClassDeclaration *dec = dynamic_cast<ClassDeclaration*>(
0979                                 findDeclarationImportHelper(m_duContext.data(), id,
0980                                                             ClassDeclarationType).data()
0981                             );
0982     if (dec) {
0983         forbidIdentifier(dec);
0984     } else {
0985         // might be a class we are currently writing, i.e. without a proper declaration
0986         m_forbiddenIdentifiers << id.index();
0987     }
0988 }
0989 
0990 void CodeCompletionContext::forbidIdentifier(ClassDeclaration* klass)
0991 {
0992     uint id;
0993     {
0994         LOCKDUCHAIN;
0995         // TODO: qualifiedIdentifier is marked as expensive - any other way
0996         //       we can do what we are doing here?
0997         // TODO: maybe we should clar the m_fobiddenIdentifiers after we got
0998         //       our list of items...
0999         id = klass->qualifiedIdentifier().index();
1000     }
1001     if (m_forbiddenIdentifiers.contains(id)) {
1002         // nothing to do
1003         return;
1004     }
1005 
1006     m_forbiddenIdentifiers << id;
1007 
1008     // add parents so those are excluded from the completion items as well
1009     if (klass->baseClassesSize() > 0) {
1010         FOREACH_FUNCTION(const BaseClassInstance& base, klass->baseClasses) {
1011             StructureType::Ptr type = base.baseClass.type<StructureType>();
1012             if (type.data()) {
1013                 ClassDeclaration* parent;
1014                 {
1015                     LOCKDUCHAIN;
1016                     parent = dynamic_cast<ClassDeclaration*>(
1017                                                type->declaration(m_duContext->topContext())
1018                                            );
1019                 }
1020                 if (parent) {
1021                     forbidIdentifier(parent);
1022                 }
1023             }
1024         }
1025     }
1026 }
1027 
1028 CodeCompletionContext::~CodeCompletionContext()
1029 {
1030 }
1031 
1032 CodeCompletionContext::MemberAccessOperation CodeCompletionContext::memberAccessOperation() const
1033 {
1034     return m_memberAccessOperation;
1035 }
1036 
1037 ExpressionEvaluationResult CodeCompletionContext::memberAccessContainer() const
1038 {
1039     return m_expressionResult;
1040 }
1041 
1042 CodeCompletionContext* CodeCompletionContext::parentContext()
1043 {
1044     return static_cast<CodeCompletionContext*>(KDevelop::CodeCompletionContext::parentContext());
1045 }
1046 
1047 QList<DUContext*> CodeCompletionContext::memberAccessContainers() const
1048 {
1049     QList<DUContext*> ret;
1050     QList<AbstractType::Ptr> types;
1051     AbstractType::Ptr expressionTarget = m_expressionResult.type();
1052     if (auto unsureType = m_expressionResult.type().dynamicCast<UnsureType>()) {
1053         FOREACH_FUNCTION(const IndexedType& t, unsureType->types) {
1054             types << t.abstractType();
1055         }
1056     } else if (auto referencedType = m_expressionResult.type().dynamicCast<ReferenceType>() ) {
1057         types << referencedType->baseType();
1058     } else {
1059         types << expressionTarget;
1060     }
1061     foreach (const AbstractType::Ptr &type, types) {
1062         const IdentifiedType* idType = dynamic_cast<const IdentifiedType*>(type.data());
1063         Declaration* declaration = nullptr;
1064         if (idType) {
1065             declaration = idType->declaration(m_duContext->topContext());
1066         }
1067         DUContext* ctx = nullptr;
1068         if (declaration) {
1069             ctx = declaration->logicalInternalContext(m_duContext->topContext());
1070         }
1071         if (ctx) {
1072             ret << ctx;
1073         } else if (declaration) {
1074             //Print some debug-output
1075             qCDebug(COMPLETION) << "Could not get internal context from" << declaration->toString();
1076         } else {
1077             //Print some debug-output
1078             qCDebug(COMPLETION) << "Could not get declaration";
1079         }
1080     }
1081     return ret;
1082 }
1083 
1084 QList<CompletionTreeItemPointer> CodeCompletionContext::completionItems(bool& abort, bool fullCompletion)
1085 {
1086     //TODO: how should this be handled?
1087     Q_UNUSED(fullCompletion)
1088 
1089     /// Indexed string for 'Php', identifies environment files from this language plugin
1090     static const IndexedString phpLangString("Php");
1091 
1092     LOCKDUCHAIN;
1093 
1094     QList<CompletionTreeItemPointer> items;
1095 
1096     if (!m_duContext)
1097         return items;
1098 
1099     typedef QPair<Declaration*, int> DeclarationDepthPair;
1100 
1101     if ( memberAccessOperation() == FileChoose ) {
1102         if ( !ICore::self() ) {
1103             // in unit tests we can't do anything
1104             qCDebug(COMPLETION) << "no core found";
1105             return items;
1106         }
1107         // file completion
1108         const Path currentDocument(m_duContext->url().str());
1109         Path path;
1110         Path base;
1111         if ( !m_isFileCompletionAfterDirname ) {
1112             path = Path(currentDocument.parent(), m_expression);
1113             base = path;
1114             if ( !m_expression.isEmpty() && !m_expression.endsWith('/') ) {
1115                 base = base.parent();
1116             }
1117         } else {
1118             if ( m_expression.startsWith('/') ) {
1119                 path = Path(currentDocument.parent(), m_expression.mid(1));
1120             } else {
1121                 path = currentDocument.parent();
1122             }
1123             base = path;
1124             if ( !m_expression.isEmpty() && !m_expression.endsWith('/') && m_expression != QLatin1String("/") ) {
1125                 base = base.parent();
1126             }
1127         }
1128         QList<Path> addedPaths;
1129         bool addedParentDir = false;
1130         const QUrl baseUrl = base.toUrl();
1131         foreach ( ProjectBaseItem* item, ICore::self()->projectController()->projectModel()->itemsForPath(IndexedString(base.toUrl())) ) {
1132             if ( abort || !item->folder() ) {
1133                 break;
1134             }
1135             auto folder = item->folder();
1136             foreach ( ProjectFileItem* subFile, folder->fileList() ) {
1137                 if ( abort ) {
1138                     break;
1139                 }
1140                 if ( addedPaths.contains(subFile->path()) ) {
1141                     continue;
1142                 } else {
1143                     addedPaths << subFile->path();
1144                 }
1145                 IncludeItem item;
1146                 item.isDirectory = false;
1147                 item.basePath = baseUrl;
1148                 item.name = subFile->fileName();
1149                 if ( m_isFileCompletionAfterDirname && !m_expression.startsWith('/') ) {
1150                     item.name.prepend('/');
1151                 }
1152                 items << CompletionTreeItemPointer(new IncludeFileItem(item));
1153             }
1154             foreach ( ProjectFolderItem* subFolder, folder->folderList() ) {
1155                 if ( abort ) {
1156                     break;
1157                 }
1158                 if ( addedPaths.contains(subFolder->path()) ) {
1159                     continue;
1160                 } else {
1161                     addedPaths << subFolder->path();
1162                 }
1163                 IncludeItem item;
1164                 item.isDirectory = true;
1165                 item.basePath = baseUrl;
1166                 item.name = subFolder->folderName();
1167                 if ( m_isFileCompletionAfterDirname && !m_expression.startsWith('/') ) {
1168                     item.name.prepend('/');
1169                 }
1170                 items << CompletionTreeItemPointer(new IncludeFileItem(item));
1171             }
1172             if ( !folder->parent() && !addedParentDir && m_expression.isEmpty() ) {
1173                 // expect a parent dir
1174                 IncludeItem item;
1175                 item.isDirectory = true;
1176                 item.basePath = baseUrl;
1177                 item.name = QStringLiteral("..");
1178                 items << CompletionTreeItemPointer(new IncludeFileItem(item));
1179             }
1180         }
1181 
1182         return items;
1183     } else if (memberAccessOperation() == ClassMemberChoose) {
1184         // get current class
1185         if (ClassDeclaration * currentClass = dynamic_cast<ClassDeclaration*>(m_duContext->owner())) {
1186             // whether we want to show a list of overloadable functions
1187             // i.e. not after we have typed one of the keywords var,const or abstract
1188             bool showOverloadable = true;
1189             // whether we want to remove static functions from the overloadable list
1190             // i.e. after we have typed "public function"
1191             bool filterStatic = false;
1192             // whether we want to remove non-static functions from the overloadable list
1193             // i.e. after we have typed "public static function"
1194             bool filterNonStatic = false;
1195             // private functions are always removed from the overloadable list
1196             // but when we type "protected function" only protected functions may be shown
1197             bool filterPublic = false;
1198 
1199             {
1200                 // add typical keywords for class member definitions
1201                 QStringList modifiers = getMethodTokens(m_text);
1202 
1203                 // don't add keywords when "function" was already typed
1204                 bool addKeywords = !modifiers.contains(QStringLiteral("function"));
1205 
1206                 if (currentClass->classModifier() == ClassDeclarationData::Abstract) {
1207                     // abstract is only allowed in abstract classes
1208                     if (modifiers.contains(QStringLiteral("abstract"))) {
1209                         // don't show overloadable functions when we are defining an abstract function
1210                         showOverloadable = false;
1211                     } else if (addKeywords) {
1212                         ADD_KEYWORD("abstract");
1213                     }
1214                 } else {
1215                     // final is only allowed in non-abstract classes
1216                     if (addKeywords && !modifiers.contains(QStringLiteral("final"))) {
1217                         ADD_KEYWORD("final");
1218                     }
1219                 }
1220 
1221                 if (modifiers.contains(QStringLiteral("private"))) {
1222                     // overloadable functions must not be declared private
1223                     showOverloadable = false;
1224                 } else if (modifiers.contains(QStringLiteral("protected"))) {
1225                     // only show protected overloadable methods
1226                     filterPublic = true;
1227                 } else if (addKeywords && !modifiers.contains(QStringLiteral("public"))) {
1228                     ADD_KEYWORD("public");
1229                     ADD_KEYWORD("protected");
1230                     ADD_KEYWORD("private");
1231                 }
1232 
1233                 if (modifiers.contains(QStringLiteral("static"))) {
1234                     filterNonStatic = true;
1235                 } else {
1236                     if (addKeywords) {
1237                         ADD_KEYWORD("static");
1238                     } else {
1239                         filterStatic = true;
1240                     }
1241                 }
1242 
1243                 if (addKeywords) {
1244                     ADD_KEYWORD("function");
1245                 }
1246 
1247                 if (modifiers.isEmpty()) {
1248                     // var and const may not have any modifiers
1249                     ADD_KEYWORD("var");
1250                     ADD_KEYWORD("const");
1251                 }
1252             }
1253             ifDebug( qCDebug(COMPLETION) << "showOverloadable" << showOverloadable; )
1254             // complete overloadable methods from parents
1255             if (showOverloadable) {
1256                 // TODO: use m_duContext instead of ctx
1257                 // overloadable choose is only possible inside classes which extend/implement others
1258                 if (currentClass->baseClassesSize()) {
1259                     DUContext* ctx = currentClass->internalContext();
1260                     if (!ctx) {
1261                         qCDebug(COMPLETION) << "invalid class context";
1262                         return items;
1263                     }
1264                     QList<uint> alreadyImplemented;
1265                     //TODO: use the token stream here as well
1266                     //TODO: always add __construct, __destruct and maby other magic functions
1267                     // get all visible declarations and add inherited to the completion items
1268                     foreach(DeclarationDepthPair decl, ctx->allDeclarations(ctx->range().end, m_duContext->topContext(), false)) {
1269                         ClassMemberDeclaration *member = dynamic_cast<ClassMemberDeclaration*>(decl.first);
1270                         ClassFunctionDeclaration *classFunc = dynamic_cast<ClassFunctionDeclaration*>(decl.first);
1271                         if (member) {
1272                             if (decl.second == 0) {
1273                                 // this function is already implemented
1274                                 alreadyImplemented << decl.first->indexedIdentifier().index();
1275                                 continue;
1276                             }
1277                             // skip already implemented functions
1278                             if (alreadyImplemented.contains(decl.first->indexedIdentifier().index())) {
1279                                 continue;
1280                             }
1281                             // skip non-static functions when requested
1282                             if (filterNonStatic && !member->isStatic()) {
1283                                 continue;
1284                             }
1285                             // skip static functions when requested
1286                             if (filterStatic && member->isStatic()) {
1287                                 continue;
1288                             }
1289                             // always skip private functions
1290                             if (member->accessPolicy() == Declaration::Private) {
1291                                 continue;
1292                             }
1293                             // skip public functions when requested
1294                             if (filterPublic && member->accessPolicy() == Declaration::Public) {
1295                                 // make sure no non-public base members are added
1296                                 alreadyImplemented << decl.first->indexedIdentifier().index();
1297                                 continue;
1298                             }
1299                             // skip final members
1300                             if (classFunc && classFunc->isFinal()) {
1301                                 // make sure no non-final base members are added
1302                                 alreadyImplemented << decl.first->indexedIdentifier().index();
1303                                 continue;
1304                             }
1305                             // make sure we inherit or implement the base class of this member
1306                             if (!member->context() || !member->context()->owner()) {
1307                                 qCDebug(COMPLETION) << "invalid parent context/owner:" << member->toString();
1308                                 continue;
1309                             }
1310                             if (!currentClass->isPublicBaseClass(dynamic_cast<ClassDeclaration*>(member->context()->owner()),
1311                                                                     m_duContext->topContext())) {
1312                                 continue;
1313                             }
1314 
1315                             ImplementationItem::HelperType itype;
1316                             if (!member->isFunctionDeclaration()) {
1317                                 itype = ImplementationItem::OverrideVar;
1318                             } else if (classFunc && classFunc->isAbstract()) {
1319                                 itype = ImplementationItem::Implement;
1320                             } else {
1321                                 itype = ImplementationItem::Override;
1322                             }
1323                             ifDebug( qCDebug(COMPLETION) << "ImplementationItem" << itype; )
1324 
1325                             items << CompletionTreeItemPointer(new ImplementationItem(itype, DeclarationPointer(decl.first),
1326                                                                 Php::CodeCompletionContext::Ptr(this), decl.second));
1327                             // don't add identical items twice to the completion choices
1328                             alreadyImplemented << decl.first->indexedIdentifier().index();
1329                         }
1330                     }
1331                 }
1332             } else {
1333                 qCDebug(COMPLETION) << "invalid owner declaration for overloadable completion";
1334             }
1335         }
1336     } else if (m_memberAccessOperation == BackslashAccess || m_memberAccessOperation == NamespaceChoose) {
1337         DUContext* ctx = nullptr;
1338         if (m_namespace.isEmpty()) {
1339             ctx = m_duContext->topContext();
1340         } else {
1341             foreach(Declaration* dec, m_duContext->topContext()->findDeclarations(m_namespace)) {
1342                 if (dec->kind() == Declaration::Namespace) {
1343                     ctx = dec->internalContext();
1344                     break;
1345                 }
1346             }
1347         }
1348         if (!ctx) {
1349             qCDebug(COMPLETION) << "could not find namespace:" << m_namespace.toString();
1350             return items;
1351         }
1352         foreach(Declaration* dec, ctx->localDeclarations()) {
1353             if (!isValidCompletionItem(dec)) {
1354                 continue;
1355             } else {
1356                 items << CompletionTreeItemPointer(
1357                             new NormalDeclarationCompletionItem(
1358                                     DeclarationPointer(dec),
1359                                     Php::CodeCompletionContext::Ptr(this), depth()
1360                                 )
1361                          );
1362             }
1363         }
1364     } else if (m_expressionResult.type()) {
1365         QList<DUContext*> containers = memberAccessContainers();
1366         qCDebug(COMPLETION) << "containers: " << containers.count();
1367         if (!containers.isEmpty()) {
1368             // get the parent class when we are inside a method declaration
1369             ClassDeclaration* currentClass = nullptr;
1370             if (m_duContext->owner() && m_duContext->owner()->isFunctionDeclaration() &&
1371                     m_duContext->parentContext() && m_duContext->parentContext()->owner()) {
1372                 currentClass = dynamic_cast<ClassDeclaration*>(m_duContext->parentContext()->owner());
1373             }
1374 
1375             foreach(DUContext* ctx, containers) {
1376                 ClassDeclaration* accessedClass = dynamic_cast<ClassDeclaration*>(ctx->owner());
1377                 if (abort)
1378                     return items;
1379 
1380                 foreach(DeclarationDepthPair decl, ctx->allDeclarations(
1381                                                             ctx->range().end, m_duContext->topContext(), false))
1382                 {
1383                     //If we have StaticMemberAccess, which means A::Bla, show only static members,
1384                     //except if we're within a class that derives from the container
1385                     ClassMemberDeclaration* classMember = dynamic_cast<ClassMemberDeclaration*>(decl.first);
1386                     if (memberAccessOperation() != StaticMemberAccess) {
1387                         if (classMember && classMember->isStatic())
1388                             continue; //Skip static class members when not doing static access
1389                     } else {
1390                         if (!classMember || !classMember->isStatic())
1391                             continue; //Skip static class members when not doing static access
1392                     }
1393 
1394                     // check access policy
1395                     if (classMember && accessedClass) {
1396                         // by default only show public declarations
1397                         Declaration::AccessPolicy ap = Declaration::Public;
1398                         if (currentClass) {
1399                             // if we are inside a class, we might want to show protected or private members
1400                             ClassDeclaration* memberClass = dynamic_cast<ClassDeclaration*>(classMember->context()->owner());
1401                             if (memberClass) {
1402                                 if (currentClass == accessedClass) {
1403                                     if (currentClass == memberClass) {
1404                                         // we can show all members of the current class
1405                                         ap = Declaration::Private;
1406                                     } else if (currentClass->isPublicBaseClass(memberClass, m_duContext->topContext())) {
1407                                         // we can show all but private members of ancestors of the current class
1408                                         ap = Declaration::Protected;
1409                                     }
1410                                 } else if (currentClass->isPublicBaseClass(accessedClass, m_duContext->topContext())
1411                                             && (accessedClass == memberClass ||
1412                                                 accessedClass->isPublicBaseClass(memberClass, m_duContext->topContext()))) {
1413                                     // we can show all but private members of ancestors of the current class
1414                                     ap = Declaration::Protected;
1415                                 }
1416                             }
1417                         }
1418                         if (ap < classMember->accessPolicy()) {
1419                             continue;
1420                         }
1421                     }
1422 
1423                     if (!decl.first->identifier().isEmpty())
1424                         items << CompletionTreeItemPointer(
1425                                     new NormalDeclarationCompletionItem(
1426                                             DeclarationPointer(
1427                                                 decl.first),
1428                                                 Php::CodeCompletionContext::Ptr(this),
1429                                                 decl.second
1430                                             )
1431                                     );
1432                 }
1433             }
1434         } else {
1435             qCDebug(COMPLETION) << "setContext: no container-type";
1436         }
1437 
1438     } else {
1439         //Show all visible declarations
1440         QSet<uint> existingIdentifiers;
1441         const auto decls = m_duContext->allDeclarations(
1442             CursorInRevision::invalid(),
1443             m_duContext->topContext()
1444         );
1445 
1446         qCDebug(COMPLETION) << "setContext: using all declarations visible:" << decls.size();
1447 
1448         QVectorIterator<DeclarationDepthPair> i(decls);
1449         i.toBack();
1450         while (i.hasPrevious()) {
1451             DeclarationDepthPair decl = i.previous();
1452             Declaration* dec = decl.first;
1453             if (dec->kind() == Declaration::Instance) {
1454                 // filter non-superglobal vars of other contexts
1455                 if (dec->context() != m_duContext.data() && !m_duContext->imports(dec->context())) {
1456                     VariableDeclaration* vDec = dynamic_cast<VariableDeclaration*>(dec);
1457                     if ( vDec && !vDec->isSuperglobal() ) {
1458                         continue;
1459                     }
1460                 }
1461 
1462                 if (existingIdentifiers.contains(dec->indexedIdentifier().index())) continue;
1463                 existingIdentifiers.insert(dec->indexedIdentifier().index());
1464             }
1465             if (abort)
1466                 return items;
1467             if (!isValidCompletionItem(dec)) continue;
1468             items << CompletionTreeItemPointer(
1469                         new NormalDeclarationCompletionItem(
1470                                 DeclarationPointer(dec),
1471                                 Php::CodeCompletionContext::Ptr(this),
1472                                 decl.second
1473                         )
1474                     );
1475         }
1476 
1477         foreach(QSet<IndexedString> urlSets, completionFiles()) {
1478             foreach(const IndexedString &url, urlSets) {
1479                 if (url == m_duContext->url()) {
1480                     continue;
1481                 }
1482                 uint count = 0;
1483                 const CodeModelItem* foundItems = nullptr;
1484                 CodeModel::self().items(url, count, foundItems);
1485                 for (uint i = 0; i < count; ++i) {
1486                     CodeModelItem::Kind k = foundItems[i].kind;
1487                     if (((k & CodeModelItem::Function) || (k & CodeModelItem::Variable)) && !(k & CodeModelItem::ClassMember)) {
1488                         foreach(const ParsingEnvironmentFilePointer &env, DUChain::self()->allEnvironmentFiles(url)) {
1489                             if (env->language() != phpLangString) continue;
1490                             TopDUContext* top = env->topContext();
1491                             if(!top) continue;
1492                             if (m_duContext->imports(top)) continue;
1493                             QList<Declaration*> decls = top->findDeclarations(foundItems[i].id);
1494                             foreach(Declaration* decl, decls) {
1495                                 if (abort) return items;
1496                                 // we don't want to have class methods/properties, just normal functions
1497                                 // and other global stuff
1498                                 if ( decl->context() && decl->context()->type() == DUContext::Class ) {
1499                                     continue;
1500                                 }
1501                                 if (!isValidCompletionItem(decl)) continue;
1502                                 if ( VariableDeclaration* vDec = dynamic_cast<VariableDeclaration*>(decl) ) {
1503                                     if ( !vDec->isSuperglobal() ) {
1504                                         continue;
1505                                     }
1506                                 }
1507                                 items << CompletionTreeItemPointer(
1508                                             new NormalDeclarationCompletionItem(
1509                                                     DeclarationPointer(decl),
1510                                                     Php::CodeCompletionContext::Ptr(this)
1511                                             )
1512                                         );
1513                             }
1514                         }
1515                     }
1516                 }
1517             }
1518         }
1519 
1520         foreach(QSet<IndexedString> urlSets, completionFiles()) {
1521             foreach(const IndexedString &url, urlSets) {
1522                 uint count = 0;
1523                 const CompletionCodeModelItem* foundItems = nullptr;
1524                 CompletionCodeModel::self().items(url, count, foundItems);
1525                 for (uint i = 0; i < count; ++i) {
1526                     if (abort) return items;
1527                     if (m_memberAccessOperation == ExceptionChoose) {
1528                         if (!(foundItems[i].kind & CompletionCodeModelItem::Exception)) continue;
1529                     }
1530                     auto files = DUChain::self()->allEnvironmentFiles(url);
1531                     items.reserve(files.size());
1532                     foreach(const ParsingEnvironmentFilePointer &env, files) {
1533                         Q_ASSERT(env->language() == phpLangString);
1534                         items << CompletionTreeItemPointer ( new CodeModelCompletionItem(env, foundItems[i]));
1535                     }
1536                 }
1537             }
1538         }
1539     }
1540 
1541     ///Find all recursive function-calls that should be shown as call-tips
1542     CodeCompletionContext::Ptr parentContext(this);
1543     do {
1544         if (abort)
1545             return items;
1546 
1547         parentContext = parentContext->parentContext();
1548         if (parentContext) {
1549             if (parentContext->memberAccessOperation() == CodeCompletionContext::FunctionCallAccess) {
1550                 if (!parentContext->memberAccessContainer().allDeclarationIds().isEmpty()) {
1551                     Declaration* decl = parentContext->memberAccessContainer().allDeclarationIds().first()
1552                                             .declaration(m_duContext->topContext());
1553 
1554                     if (!isValidCompletionItem(decl)) {
1555                         continue;
1556                     }
1557                     if ( !decl->isFunctionDeclaration() ) {
1558                         if ( ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(decl) ) {
1559                             // search for ctor
1560                             decl = nullptr;
1561                             foreach ( Declaration* dec, classDec->internalContext()->findDeclarations(Identifier(u"__construct")) ) {
1562                                 if ( dec->isFunctionDeclaration() ) {
1563                                     decl = dec;
1564                                     break;
1565                                 }
1566                             }
1567                             if ( !decl ) {
1568                                 foreach ( Declaration* dec, classDec->internalContext()->findDeclarations(classDec->identifier()) ) {
1569                                     if ( dec->isFunctionDeclaration() ) {
1570                                         decl = dec;
1571                                         break;
1572                                     }
1573                                 }
1574                             }
1575                             if ( !decl ) {
1576                                 continue;
1577                             }
1578                         } else if ( !decl->type<FunctionType>() ) {
1579                             qCDebug(COMPLETION) << "parent decl is neither function nor class nor closure, skipping" << decl->toString();
1580                             continue;
1581                         }
1582                     }
1583                     items << CompletionTreeItemPointer(
1584                                 new NormalDeclarationCompletionItem(
1585                                         DeclarationPointer(decl),
1586                                         Php::CodeCompletionContext::Ptr(parentContext.data())
1587                                 )
1588                             );
1589                 }
1590             } else {
1591                 qCDebug(COMPLETION) << "parent-context has non function-call access type";
1592             }
1593         }
1594     } while (parentContext);
1595 
1596     if ( m_memberAccessOperation == NoMemberAccess ) {
1597         ///TODO: function-like statements should just be handled as a function with declaration etc.
1598         ///      e.g.: empty, eval, die, exit, isset, unset
1599         ///      but _not_ echo, print, catch, include*, require*
1600         ///TODO: use user's style for indentation etc.
1601         ADD_KEYWORD2("abstract class", "abstract class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1602         ADD_KEYWORD2("final class", "final class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1603         ADD_KEYWORD2("class", "class %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1604         ADD_KEYWORD2("interface", "interface %SELECT%NAME%ENDSELECT% {\n%INDENT%\n}\n");
1605         ADD_KEYWORD2("array", "array(\n%INDENT%%CURSOR%\n)");
1606         ADD_KEYWORD2("break", "break;\n");
1607         ADD_KEYWORD2("case", "case %SELECT%CASE%ENDSELECT%:\n%INDENT%\n%INDENT%break;\n");
1608         ADD_KEYWORD2("throw", "throw %CURSOR%;\n");
1609         ADD_KEYWORD2("try", "try {\n%INDENT%%CURSOR%\n} catch() {\n$%INDENT%\n}\n");
1610         ADD_KEYWORD2("catch", "catch(%CURSOR%) {\n%INDENT%\n}\n");
1611         ADD_KEYWORD2("clone", "clone %CURSOR%;\n");
1612         ADD_KEYWORD2("continue", "continue;\n");
1613         ADD_KEYWORD2("declare", "declare(%CURSOR%);\n");
1614         ADD_KEYWORD2("default", "default:\n%INDENT%%CURSOR%\n%INDENT%break;\n");
1615         ADD_KEYWORD2("do", "do {\n%INDENT%%CURSOR%\n} while();\n");
1616         ADD_KEYWORD2("echo", "echo %CURSOR%;\n");
1617         ADD_KEYWORD2("else", "else {\n%INDENT%%CURSOR%\n}\n");
1618         ADD_KEYWORD2("elseif", "elseif (%CURSOR%) {\n%INDENT%\n}\n");
1619         ADD_KEYWORD2("endif", "endif;");
1620         ADD_KEYWORD2("endforeach", "endforeach;");
1621         ADD_KEYWORD2("endswitch", "endswitch;");
1622         ADD_KEYWORD2("endwhile", "endwhile;");
1623         ADD_KEYWORD2("endfor", "endfor;");
1624         ADD_KEYWORD2("enddeclare", "enddeclare;");
1625         ADD_KEYWORD2("empty", "empty(%CURSOR%)");
1626         ADD_KEYWORD2("eval", "eval(%CURSOR%)");
1627         ADD_KEYWORD2("die", "die(%CURSOR%);\n");
1628         ADD_KEYWORD2("exit", "exit(%CURSOR%);\n");
1629         ///TODO: only activate when after "class NAME "
1630         ADD_KEYWORD("extends");
1631         ADD_KEYWORD("implements");
1632         ADD_KEYWORD2("for", "for(%CURSOR%;;) {\n%INDENT%\n}\n");
1633         ADD_KEYWORD2("foreach", "foreach(%CURSOR%) {\n%INDENT%\n}\n");
1634         ADD_KEYWORD2("function", "function %SELECT%NAME%ENDSELECT%() {\n%INDENT%\n}\n");
1635         ADD_KEYWORD2("global", "global $%CURSOR%;");
1636         ADD_KEYWORD2("if", "if (%CURSOR%) {\n%INDENT%\n}\n");
1637         ADD_KEYWORD2("include", "include '%CURSOR%';\n");
1638         ADD_KEYWORD2("include_once", "include_once '%CURSOR%';\n");
1639         ADD_KEYWORD2("require", "require '%CURSOR%';\n");
1640         ADD_KEYWORD2("require_once", "require_once '%CURSOR%';\n");
1641         ADD_KEYWORD2("isset", "isset(%CURSOR%)");
1642         ADD_KEYWORD2("list", "list(%CURSOR%)");
1643         ADD_KEYWORD2("print", "print %CURSOR%;\n");
1644         ADD_KEYWORD2("return", "return %CURSOR%;\n");
1645         ADD_KEYWORD2("static", "static $%CURSOR%%;\n");
1646         ADD_KEYWORD2("unset", "unset(%CURSOR%);\n");
1647         ADD_KEYWORD2("while", "while (%CURSOR%) {\n%INDENT%\n}\n");
1648         ADD_KEYWORD2("switch", "switch (%CURSOR%) {\n%INDENT%\n}\n");
1649     }
1650 
1651     return items;
1652 }
1653 
1654 inline bool CodeCompletionContext::isValidCompletionItem(Declaration* dec)
1655 {
1656     if ( !dec || dec->range().isEmpty() ) {
1657         // hack for included files
1658         return false;
1659     }
1660     if ( dec->kind() == Declaration::Type && dec->qualifiedIdentifier().isEmpty() ) {
1661         // filter closures
1662         return false;
1663     }
1664 
1665     static DUChainPointer<ClassDeclaration> exceptionDecl;
1666     if (!exceptionDecl) {
1667         /// Qualified identifier for 'exception'
1668         static const KDevelop::QualifiedIdentifier exceptionQId(QStringLiteral("exception"));
1669         QList<Declaration*> decs = dec->context()->findDeclarations(exceptionQId);
1670         Q_ASSERT(decs.count());
1671         if (!decs.isEmpty()) { // additional safe-guard, see e.g. https://bugs.kde.org/show_bug.cgi?id=294218
1672             exceptionDecl = dynamic_cast<ClassDeclaration*>(decs.first());
1673             Q_ASSERT(exceptionDecl);
1674         }
1675     }
1676     if (!exceptionDecl) {
1677         // safe-guard, see: https://bugs.kde.org/show_bug.cgi?id=294218
1678         qWarning() << "could not find PHP-Exception declaration, related code completion will be broken.";
1679     }
1680 
1681     if (m_memberAccessOperation == ExceptionChoose
1682             || m_memberAccessOperation == NewClassChoose
1683             || m_memberAccessOperation == InterfaceChoose
1684             || m_memberAccessOperation == ClassExtendsChoose
1685             || m_memberAccessOperation == InstanceOfChoose) {
1686         // filter current class
1687         if (!m_forbiddenIdentifiers.isEmpty() && m_forbiddenIdentifiers.contains(dec->qualifiedIdentifier().index())) {
1688             return false;
1689         }
1690         ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
1691 
1692         // filter non-classes
1693         if (!classDec) {
1694             return false;
1695         }
1696         // show non-interface and non-abstract
1697         else if (m_memberAccessOperation == NewClassChoose) {
1698             return !(classDec->classModifier() & ClassDeclarationData::Abstract)
1699                    && classDec->classType() == ClassDeclarationData::Class;
1700         }
1701         // filter non-exception classes
1702         else if (m_memberAccessOperation == ExceptionChoose) {
1703             if (!exceptionDecl) {
1704                 // safe-guard, see: https://bugs.kde.org/show_bug.cgi?id=294218
1705                 return false;
1706             }
1707             return classDec->equalQualifiedIdentifier(exceptionDecl.data())
1708                    || classDec->isPublicBaseClass(exceptionDecl.data(), m_duContext->topContext());
1709         }
1710         // show interfaces
1711         else if (m_memberAccessOperation == InterfaceChoose) {
1712             return classDec->classType() == ClassDeclarationData::Interface;
1713         }
1714         // show anything but final classes and interfaces
1715         else if (m_memberAccessOperation == ClassExtendsChoose) {
1716             return !(classDec->classModifier() & ClassDeclarationData::Final)
1717                    && classDec->classType() == ClassDeclarationData::Class;
1718         }
1719         else if (m_memberAccessOperation == InstanceOfChoose) {
1720             return true;
1721         }
1722     }
1723 
1724     if (m_memberAccessOperation == ExceptionInstanceChoose) {
1725         if (!exceptionDecl) {
1726             // safe-guard, see: https://bugs.kde.org/show_bug.cgi?id=294218
1727             return false;
1728         }
1729         if (dec->kind() != Declaration::Instance) {
1730             return false;
1731         }
1732         StructureType::Ptr structType = dec->type<StructureType>();
1733         if (!structType) {
1734             return false;
1735         }
1736         ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(structType->declaration(dec->topContext()));
1737         if (!classDec) {
1738             return false;
1739         }
1740         return classDec->isPublicBaseClass(exceptionDecl.data(), m_duContext->topContext());
1741     }
1742 
1743     if (m_memberAccessOperation == NoMemberAccess) {
1744         // filter private methods and class members when doing a global completion
1745         // when we are inside a class function, don't filter the private stuff
1746         // of the current class
1747         // NOTE: ClassFunctionDeclaration inherits ClassMemberDeclaration
1748         // NOTE: both have to have a parent context with type class
1749         if ( dec->context() && dec->context()->type() == DUContext::Class
1750                 && m_duContext->parentContext() != dec->context() )
1751         {
1752             if ( ClassMemberDeclaration* memberDec = dynamic_cast<ClassMemberDeclaration*>(dec) ) {
1753                 if ( memberDec->accessPolicy() == Declaration::Private ) {
1754                     return false;
1755                 }
1756             }
1757         }
1758         if ( !dec->isFunctionDeclaration() && m_duContext.data() == dec->context() && m_position < dec->range().start ) {
1759             return false;
1760         }
1761     }
1762 
1763     if (m_memberAccessOperation == NamespaceChoose) {
1764         return dec->kind() == Declaration::Namespace;
1765     }
1766 
1767     return true;
1768 }
1769 
1770 QList<QSet<IndexedString> > CodeCompletionContext::completionFiles()
1771 {
1772     QList<QSet<IndexedString> > ret;
1773     if (ICore::self()) {
1774         auto projects = ICore::self()->projectController()->projects();
1775         ret.reserve(projects.size());
1776         foreach(IProject* project, projects) {
1777             ret << project->fileSet();
1778         }
1779     }
1780     return ret;
1781 }
1782 
1783 }