File indexing completed on 2024-04-21 04:35:54

0001 /* This file is part of KDevelop
0002  *
0003  * Copyright (C) 2011-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
0004  *
0005  * This program is free software: you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation, either version 3 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 
0020 // Qt + KDE
0021 #include <QtCore/QProcess>
0022 #include <QtCore/QStandardPaths>
0023 
0024 // KDevelop
0025 #include <language/duchain/identifier.h>
0026 #include <language/duchain/duchain.h>
0027 #include <language/duchain/duchainlock.h>
0028 #include <language/duchain/types/unsuretype.h>
0029 #include <language/duchain/types/integraltype.h>
0030 #include <language/duchain/parsingenvironment.h>
0031 #include <language/duchain/persistentsymboltable.h>
0032 
0033 // Ruby
0034 #include <duchain/helpers.h>
0035 #include <duchain/editorintegrator.h>
0036 #include <duchain/declarations/methoddeclaration.h>
0037 #include <duchain/declarations/moduledeclaration.h>
0038 #include <duchain/declarations/variabledeclaration.h>
0039 #include <duchain/types/classtype.h>
0040 
0041 using namespace KDevelop;
0042 namespace ruby {
0043 
0044 // The following anonymous namespace contains helper functions that should not
0045 // be exported outside.
0046 namespace {
0047 
0048 /**
0049  * @returns true if the given @p type is useful, and false otherwise.
0050  */
0051 bool isUsefulType(const AbstractType::Ptr &type)
0052 {
0053     if (!type) {
0054         return false;
0055     }
0056     if (type->whichType() != AbstractType::TypeIntegral) {
0057         auto ct = type.dynamicCast<ClassType>();
0058         if (ct) {
0059             return ct->isUseful();
0060         }
0061         return true;
0062     }
0063 
0064     const auto &data = type.staticCast<IntegralType>()->dataType();
0065     return data == IntegralType::TypeMixed ||
0066            data == IntegralType::TypeNone ||
0067            data == IntegralType::TypeNull;
0068 }
0069 
0070 /**
0071  * Find a declaration from the Persistent Symbol Table.
0072  *
0073  * @param id The qualified indentifier of the declaration.
0074  * @param context The current context.
0075  * @param kind The kind of the declaration.
0076  * @returns the first declaration that matches the given parameters from
0077  * the Persistent Symbol Table.
0078  * @note The given context has to be valid.
0079  * @note This method already acquires a write lock for the DUChain.
0080  */
0081 DeclarationPointer getDeclarationFromPST(const QualifiedIdentifier &id,
0082                                          const DUContextPointer &context,
0083                                          DeclarationKind kind)
0084 {
0085     DeclarationPointer ret;
0086 
0087     // As specified by the documentation, the context *has* to be valid.
0088     Q_ASSERT(context);
0089 
0090     DUChainWriteLocker lock;
0091     auto visitor = [&](const IndexedDeclaration &indexedDeclaration) {
0092         // Check that the file matches the environment.
0093         auto env = DUChain::self()->environmentFileForDocument(indexedDeclaration.indexedTopContext());
0094         if(!env || env->language() != languageString()) {
0095             return PersistentSymbolTable::VisitorState::Continue;
0096         }
0097 
0098         // It doesn't have a declaration, skipping.
0099         Declaration *d = indexedDeclaration.declaration();
0100         if (!d) {
0101             return PersistentSymbolTable::VisitorState::Continue;
0102         }
0103 
0104         /*
0105          * Only global variables should be available for other files, but
0106          * global variables are always fetched by the getDeclaration method.
0107          * Therefore, at this point, we discard variable declarations.
0108          */
0109         if (dynamic_cast<VariableDeclaration *>(d)) {
0110             return PersistentSymbolTable::VisitorState::Continue;
0111         }
0112 
0113         // If it's a method declaration, check that we've got the proper one.
0114         if (kind != DeclarationKind::Unknown) {
0115             MethodDeclaration *mDecl = dynamic_cast<MethodDeclaration *>(d);
0116             if (mDecl) {
0117                 // TODO: remove this.
0118                 if ((mDecl->isClassMethod() && kind != DeclarationKind::ClassMethod) ||
0119                     (!mDecl->isClassMethod() && kind != DeclarationKind::InstanceMethod)) {
0120                     return PersistentSymbolTable::VisitorState::Continue;
0121                 }
0122             }
0123         }
0124 
0125         // Get the declaration and add its top context to the current one.
0126         TopDUContext *top = d->context()->topContext();
0127         auto mods = top->parsingEnvironmentFile()->allModificationRevisions();
0128         context->topContext()->addImportedParentContext(top);
0129         context->topContext()->parsingEnvironmentFile()->addModificationRevisions(mods);
0130         context->topContext()->updateImportsCache();
0131         ret = d;
0132         return PersistentSymbolTable::VisitorState::Break;
0133     };
0134     PersistentSymbolTable::self().visitDeclarations(id, visitor);
0135 
0136     return ret;
0137 }
0138 
0139 }
0140 
0141 const IndexedString & languageString()
0142 {
0143     static const IndexedString lang("Ruby");
0144     return lang;
0145 }
0146 
0147 const IndexedString & builtinsFile()
0148 {
0149     const auto &rb = QStringLiteral("kdevrubysupport/documentation/builtins.rb");
0150     static IndexedString doc(QStandardPaths::locate(
0151                                 QStandardPaths::GenericDataLocation, rb));
0152     return doc;
0153 }
0154 
0155 const QString getName(Ast *ast)
0156 {
0157     return QString(rb_name_node(ast->tree)->name);
0158 }
0159 
0160 const QByteArray getComment(Ast *ast)
0161 {
0162     char *m_comment = ast->tree->comment;
0163     return (m_comment) ? QByteArray(m_comment) : QByteArray("");
0164 }
0165 
0166 DeclarationPointer getDeclaration(const QualifiedIdentifier &id,
0167                                   const RangeInRevision &range,
0168                                   const DUContextPointer &context,
0169                                   DeclarationKind kind)
0170 {
0171     QList<Declaration *> decls;
0172 
0173     // As specified by the documentation, the context *has* to be valid.
0174     Q_ASSERT(context);
0175 
0176     {
0177         DUChainReadLocker lock;
0178 
0179         // If this is a class method, look at the eigen class and get out.
0180         if (kind == DeclarationKind::ClassMethod ||
0181             kind == DeclarationKind::Unknown) {
0182 
0183             Declaration *d = context->owner();
0184             ModuleDeclaration *md = dynamic_cast<ModuleDeclaration *>(d);
0185             if (md) {
0186                 DUContext *ctx = md->eigenClass();
0187                 if (ctx) {
0188                     decls = ctx->findLocalDeclarations(id.last(), range.end);
0189                     if (!decls.isEmpty()) {
0190                         return DeclarationPointer(decls.last());
0191                     }
0192                 }
0193             }
0194             kind = DeclarationKind::Unknown;
0195         }
0196 
0197         /*
0198          * Search first for local declarations. If no local declarations have
0199          * been found, then take a look at imported contexts (e.g. method
0200          * arguments). Otherwise, we'll search for global declarations. If
0201          * we're still failing at getting a valid declaration, then we check
0202          * the PST. The PST will only go for classes, modules and methods.
0203          */
0204         decls = context->findLocalDeclarations(id.last(), range.end);
0205         if (decls.isEmpty()) {
0206             decls = context->findDeclarations(id.last(), range.end);
0207             if (decls.isEmpty() && kind != DeclarationKind::Local) {
0208                 if (context.data() == context->topContext()) {
0209                     decls = context->topContext()->findDeclarations(id, range.end);
0210                 } else {
0211                     decls = context->topContext()->findDeclarations(id);
0212                 }
0213 
0214                 // If it's empty, then we're going for some PST time!
0215                 if (decls.isEmpty() && kind != DeclarationKind::ClassMethod) {
0216                     lock.unlock();
0217                     return getDeclarationFromPST(id, context, kind);
0218                 }
0219             }
0220         }
0221     }
0222 
0223     if (decls.isEmpty()) {
0224         return DeclarationPointer();
0225     }
0226     return DeclarationPointer(decls.last());
0227 }
0228 
0229 TypePtr<AbstractType> getBuiltinsType(const QString &desc,
0230                                       const DUContext *ctx)
0231 {
0232     // As specified by the documentation, the context *has* to be valid.
0233     Q_ASSERT(ctx);
0234 
0235     DUChainReadLocker lock;
0236     auto decls = ctx->topContext()->findDeclarations(QualifiedIdentifier(desc));
0237     Declaration *dec = (decls.isEmpty()) ? nullptr : decls.first();
0238     AbstractType::Ptr type = dec ? dec->abstractType() : AbstractType::Ptr(nullptr);
0239     return type;
0240 }
0241 
0242 DUContext * getClassContext(const DUContext *ctx)
0243 {
0244     // As specified by the documentation, the context *has* to be valid.
0245     Q_ASSERT(ctx);
0246 
0247     DUChainReadLocker lock;
0248     const auto &type = getBuiltinsType(QStringLiteral("Class"), ctx);
0249     auto klass = type.dynamicCast<StructureType>();
0250     if (klass) {
0251         return klass->declaration(ctx->topContext())->internalContext();
0252     }
0253     return nullptr;
0254 }
0255 
0256 AbstractType::Ptr mergeTypes(AbstractType::Ptr type, AbstractType::Ptr newType)
0257 {
0258     DUChainReadLocker lock;
0259     auto unsure = type.dynamicCast<UnsureType>();
0260     auto newUnsure = newType.dynamicCast<UnsureType>();
0261     UnsureType::Ptr res;
0262 
0263     if (unsure && newUnsure) {
0264         int len = newUnsure->typesSize();
0265         for (int i = 0; i < len; i++) {
0266             unsure->addType(newUnsure->types()[i]);
0267         }
0268         res = unsure;
0269     } else if (unsure) {
0270         if (isUsefulType(newType)) {
0271             unsure->addType(newType->indexed());
0272         }
0273         res = unsure;
0274     } else if (newUnsure) {
0275         UnsureType::Ptr cloned = UnsureType::Ptr(static_cast<UnsureType *>(newUnsure->clone()));
0276         if (isUsefulType(type)) {
0277             cloned->addType(type->indexed());
0278         }
0279         res = cloned;
0280     } else {
0281         unsure = UnsureType::Ptr(new UnsureType());
0282         if (isUsefulType(type)) {
0283             unsure->addType(type->indexed());
0284         } if (isUsefulType(newType)) {
0285             unsure->addType(newType->indexed());
0286         }
0287         res = unsure;
0288     }
0289 
0290     if (res->typesSize() == 0) {
0291         return type;
0292     }
0293     if (res->typesSize() == 1) {
0294         return res->types()[0].abstractType();
0295     }
0296     return res;
0297 }
0298 
0299 int nodeListSize(Node *node)
0300 {
0301     int i = 0;
0302     for (Node *n = node; n; n = n->next, i++);
0303     return i;
0304 }
0305 
0306 const QualifiedIdentifier getIdentifier(const Ast *ast)
0307 {
0308     NameAst nameAst(ast);
0309     return KDevelop::QualifiedIdentifier(nameAst.value);
0310 }
0311 
0312 bool declaredIn(const QByteArray &name, DUContextPointer context)
0313 {
0314     DUChainReadLocker lock;
0315     KDevelop::QualifiedIdentifier id = QualifiedIdentifier(QString(name));
0316     QList<Declaration *> decls = context->findLocalDeclarations(id.last());
0317     return !decls.empty();
0318 }
0319 
0320 }