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 }