~ubuntu-branches/ubuntu/utopic/kdevelop-php/utopic

« back to all changes in this revision

Viewing changes to duchain/helper.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Fathi Boudra
  • Date: 2010-01-17 17:10:22 UTC
  • Revision ID: james.westby@ubuntu.com-20100117171022-q2xlgd9ekewo2ijx
Tags: upstream-1.0.0~beta2
ImportĀ upstreamĀ versionĀ 1.0.0~beta2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   This file is part of KDevelop                                         *
 
3
 *   Copyright 2008 Niko Sams <niko.sams@gmail.com>                        *
 
4
 *                                                                         *
 
5
 *   This program is free software; you can redistribute it and/or modify  *
 
6
 *   it under the terms of the GNU Library General Public License as       *
 
7
 *   published by the Free Software Foundation; either version 2 of the    *
 
8
 *   License, or (at your option) any later version.                       *
 
9
 *                                                                         *
 
10
 *   This program is distributed in the hope that it will be useful,       *
 
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
13
 *   GNU General Public License for more details.                          *
 
14
 *                                                                         *
 
15
 *   You should have received a copy of the GNU Library General Public     *
 
16
 *   License along with this program; if not, write to the                 *
 
17
 *   Free Software Foundation, Inc.,                                       *
 
18
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 
19
 ***************************************************************************/
 
20
#include "helper.h"
 
21
 
 
22
#include <KParts/MainWindow>
 
23
#include <KLocalizedString>
 
24
 
 
25
#include <language/duchain/ducontext.h>
 
26
#include <language/duchain/duchainlock.h>
 
27
#include <language/duchain/persistentsymboltable.h>
 
28
#include <language/duchain/duchain.h>
 
29
#include <language/duchain/stringhelpers.h>
 
30
#include <language/duchain/parsingenvironment.h>
 
31
#include <interfaces/icore.h>
 
32
#include <interfaces/iprojectcontroller.h>
 
33
#include <interfaces/iuicontroller.h>
 
34
#include <interfaces/iproject.h>
 
35
#include <project/projectmodel.h>
 
36
 
 
37
#include "editorintegrator.h"
 
38
#include "../parser/parsesession.h"
 
39
#include "phpast.h"
 
40
#include "phpdefaultvisitor.h"
 
41
#include "classdeclaration.h"
 
42
#include "classmethoddeclaration.h"
 
43
#include "functiondeclaration.h"
 
44
 
 
45
 
 
46
#define ifDebug(x)
 
47
 
 
48
using namespace KDevelop;
 
49
 
 
50
namespace Php
 
51
{
 
52
 
 
53
bool isMatch(Declaration* declaration, DeclarationType declarationType)
 
54
{
 
55
    if (declarationType == ClassDeclarationType
 
56
            && dynamic_cast<ClassDeclaration*>(declaration)
 
57
       ) {
 
58
        return true;
 
59
    } else if (declarationType == FunctionDeclarationType
 
60
               && dynamic_cast<FunctionDeclaration*>(declaration)
 
61
              ) {
 
62
        return true;
 
63
    } else if (declarationType == ConstantDeclarationType
 
64
               && declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier
 
65
               && (!declaration->context() || declaration->context()->type() != DUContext::Class)
 
66
              ) {
 
67
        return true;
 
68
    } else if (declarationType == GlobalVariableDeclarationType
 
69
               && declaration->kind() == Declaration::Instance
 
70
               && !(declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier)
 
71
              ) {
 
72
        return true;
 
73
    }
 
74
    return false;
 
75
}
 
76
 
 
77
Declaration* findDeclarationImportHelper(DUContext* currentContext, QualifiedIdentifier id,
 
78
        DeclarationType declarationType, AstNode* node, EditorIntegrator* editor)
 
79
{
 
80
    ifDebug(kDebug() << id.toString() << declarationType;)
 
81
    if (declarationType == ClassDeclarationType && id == QualifiedIdentifier("self")) {
 
82
        DUChainReadLocker lock(DUChain::lock());
 
83
        if (currentContext->parentContext()) {
 
84
            Declaration* declaration = currentContext->parentContext()->owner();
 
85
            return declaration;
 
86
        }
 
87
    } else if (declarationType == ClassDeclarationType && id == QualifiedIdentifier("parent")) {
 
88
        //there can be just one Class-Context imported
 
89
        DUChainReadLocker lock(DUChain::lock());
 
90
        if (currentContext->parentContext()) {
 
91
            foreach(const DUContext::Import &i, currentContext->parentContext()->importedParentContexts()) {
 
92
                if (i.context(currentContext->topContext())->type() == DUContext::Class) {
 
93
                    return i.context(currentContext->topContext())->owner();
 
94
                }
 
95
            }
 
96
        }
 
97
    } else {
 
98
        QList<Declaration*> foundDeclarations;
 
99
        DUChainReadLocker lock(DUChain::lock());
 
100
        foundDeclarations = currentContext->topContext()->findDeclarations(id);
 
101
        foreach(Declaration *declaration, foundDeclarations) {
 
102
            if (isMatch(declaration, declarationType)) {
 
103
                return declaration;
 
104
            }
 
105
        }
 
106
        if ( currentContext->url() == IndexedString("InternalFunctions.php") ) {
 
107
            // when compiling php internal functions, we don't need to ask the persistent symbol table for anything
 
108
            return 0;
 
109
        }
 
110
        if (declarationType != GlobalVariableDeclarationType) {
 
111
            ifDebug(kDebug() << "No declarations found with findDeclarations, trying through PersistentSymbolTable" << id.toString();)
 
112
            uint nr;
 
113
            const IndexedDeclaration* declarations = 0;
 
114
            PersistentSymbolTable::self().declarations(id, nr, declarations);
 
115
            ifDebug(kDebug() << "found declarations:" << nr;)
 
116
            lock.unlock();
 
117
 
 
118
            DUChainWriteLocker wlock(DUChain::lock());
 
119
            for (uint i = 0; i < nr; ++i) {
 
120
                ParsingEnvironmentFilePointer env = DUChain::self()->environmentFileForDocument(declarations[i].indexedTopContext());
 
121
                if(!env) {
 
122
                    ifDebug(kDebug() << "skipping declaration, missing meta-data";)
 
123
                    continue;
 
124
                }
 
125
                if(env->language() != IndexedString("Php")) {
 
126
                    ifDebug(kDebug() << "skipping declaration, invalid language" << env->language().str();)
 
127
                    continue;
 
128
                }
 
129
 
 
130
                if (!declarations[i].declaration()) {
 
131
                    ifDebug(kDebug() << "skipping declaration, doesn't have declaration";)
 
132
                    continue;
 
133
                } else if (!isMatch(declarations[i].declaration(), declarationType)) {
 
134
                    ifDebug(kDebug() << "skipping declaration, doesn't match with declarationType";)
 
135
                    continue;
 
136
                }
 
137
                TopDUContext* top = declarations[i].declaration()->context()->topContext();
 
138
 
 
139
                if (top->url() != IndexedString("InternalFunctions.php") && ICore::self()) {
 
140
                    bool loadedProjectContainsUrl = false;
 
141
                    foreach(IProject *project, ICore::self()->projectController()->projects()) {
 
142
                        if (project->fileSet().contains(top->url())) {
 
143
                            loadedProjectContainsUrl = true;
 
144
                            break;
 
145
                        }
 
146
                    }
 
147
                    if (!loadedProjectContainsUrl) {
 
148
                        ifDebug(kDebug() << "skipping declaration, not in loaded project";)
 
149
                        continue;
 
150
                    }
 
151
                }
 
152
                currentContext->topContext()->addImportedParentContext(top);
 
153
                currentContext->topContext()->parsingEnvironmentFile()
 
154
                ->addModificationRevisions(top->parsingEnvironmentFile()->allModificationRevisions());
 
155
                ifDebug(kDebug() << "using" << declarations[i].declaration()->toString() << top->url().str();)
 
156
                return declarations[i].declaration();
 
157
            }
 
158
        }
 
159
    }
 
160
 
 
161
    return 0;
 
162
}
 
163
 
 
164
QByteArray formatComment(AstNode* node, EditorIntegrator* editor)
 
165
{
 
166
    return KDevelop::formatComment(editor->parseSession()->docComment(node->startToken).toUtf8());
 
167
}
 
168
 
 
169
//Helper visitor to extract a commonScalar node
 
170
//used to get the value of an function call argument
 
171
class ScalarExpressionVisitor : public DefaultVisitor
 
172
{
 
173
public:
 
174
    ScalarExpressionVisitor() : m_node(0) {}
 
175
    CommonScalarAst* node() const {
 
176
        return m_node;
 
177
    }
 
178
private:
 
179
    virtual void visitCommonScalar(CommonScalarAst* node) {
 
180
        m_node = node;
 
181
    }
 
182
    CommonScalarAst* m_node;
 
183
};
 
184
 
 
185
CommonScalarAst* findCommonScalar(AstNode* node)
 
186
{
 
187
    ScalarExpressionVisitor visitor;
 
188
    visitor.visitNode(node);
 
189
    return visitor.node();
 
190
}
 
191
 
 
192
bool includeExists(const KUrl &url)
 
193
{
 
194
    {
 
195
        DUChainReadLocker lock(DUChain::lock());
 
196
        ///TODO: this may load the chain into memory, do we really want that here?
 
197
        if (DUChain::self()->chainForDocument(url)) {
 
198
            return true;
 
199
        }
 
200
    }
 
201
    if ( url.isLocalFile() ) {
 
202
        return QFile::exists(url.toLocalFile());
 
203
    } else {
 
204
        return false;
 
205
    }
 
206
}
 
207
 
 
208
KUrl getUrlForBase(const QString &includeFile, const KUrl &baseUrl) {
 
209
    if ( includeFile.isEmpty() ) {
 
210
        return baseUrl;
 
211
    }
 
212
    KUrl url = baseUrl;
 
213
    if ( includeFile[0] == '/' ) {
 
214
        url.setPath(includeFile);
 
215
    } else {
 
216
        url.addPath(includeFile);
 
217
    }
 
218
    url.cleanPath();
 
219
    return url;
 
220
}
 
221
 
 
222
IndexedString findIncludeFileUrl(const QString &includeFile, const KUrl &currentUrl)
 
223
{
 
224
    if ( includeFile.isEmpty() ) {
 
225
        return IndexedString();
 
226
    }
 
227
 
 
228
    // check remote files
 
229
    if ( includeFile.startsWith(QLatin1String("http://"), Qt::CaseInsensitive)
 
230
            || includeFile.startsWith(QLatin1String("ftp://"), Qt::CaseInsensitive) ) {
 
231
        // always expect remote includes to exist
 
232
        return IndexedString(includeFile);
 
233
    }
 
234
 
 
235
    KUrl url;
 
236
 
 
237
    // look for file relative to current url
 
238
    url = getUrlForBase(includeFile, currentUrl.upUrl());
 
239
    if ( ICore::self()->projectController()->findProjectForUrl(url) || includeExists(url) ) {
 
240
        return IndexedString(url);
 
241
    }
 
242
 
 
243
    // look for file relative to current project base
 
244
    IProject* ownProject = ICore::self()->projectController()->findProjectForUrl(currentUrl);
 
245
    if ( ownProject ) {
 
246
        url = getUrlForBase(includeFile, ownProject->folder());
 
247
        if ( ownProject->inProject(url) || includeExists(url) ) {
 
248
            return IndexedString(url);
 
249
        }
 
250
    }
 
251
 
 
252
    // now look in all other projects
 
253
    foreach(IProject* project, ICore::self()->projectController()->projects()) {
 
254
        if ( project == ownProject ) {
 
255
            continue;
 
256
        }
 
257
        url = getUrlForBase(includeFile, project->folder());
 
258
        if ( project->inProject(url) || includeExists(url) ) {
 
259
            return IndexedString(url);
 
260
        }
 
261
    }
 
262
 
 
263
    //TODO configurable include paths
 
264
 
 
265
    return IndexedString();
 
266
}
 
267
 
 
268
IndexedString getIncludeFileForNode(UnaryExpressionAst* node, EditorIntegrator* editor) {
 
269
    if ( node->includeExpression ) {
 
270
        //find name of the constant (first argument of the function call)
 
271
        CommonScalarAst* scalar = findCommonScalar(node->includeExpression);
 
272
        if (scalar && scalar->string != -1) {
 
273
            QString str = editor->parseSession()->symbol(scalar->string);
 
274
            str = str.mid(1, str.length() - 2);
 
275
            if ( str == "." || str == ".." || str.endsWith('/') ) {
 
276
                return IndexedString();
 
277
            }
 
278
            return findIncludeFileUrl(str, editor->currentUrl().toUrl());
 
279
        }
 
280
    }
 
281
 
 
282
    return IndexedString();
 
283
}
 
284
 
 
285
QString prettyName(Declaration* dec) {
 
286
    if ( ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec) ) {
 
287
        return classDec->prettyName();
 
288
    } else if ( ClassMethodDeclaration* classMember = dynamic_cast<ClassMethodDeclaration*>(dec) ) {
 
289
        return classMember->prettyName();
 
290
    } else if ( FunctionDeclaration* func = dynamic_cast<FunctionDeclaration*>(dec) ) {
 
291
        return func->prettyName();
 
292
    } else {
 
293
        return dec->identifier().toString();
 
294
    }
 
295
}
 
296
 
 
297
}