File indexing completed on 2024-05-05 04:36:50

0001 /* This file is part of KDevelop
0002  *
0003  * Copyright 2010 Niko Sams <niko.sams@gmail.com>
0004  * Copyright 2010 Alexander Dymo <adymo@kdevelop.org>
0005  * Copyright (C) 2011-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
0006  *
0007  * This program is free software; you can redistribute it and/or modify
0008  * it under the terms of the GNU Library General Public License as
0009  * published by the Free Software Foundation; either version 2 of the
0010  * License, or (at your option) any later version.
0011  *
0012  * This program is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  * GNU General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU General Public
0018  * License along with this program; if not, write to the
0019  * Free Software Foundation, Inc.,
0020  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0021  */
0022 
0023 #include <duchain/builders/contextbuilder.h>
0024 
0025 #include <interfaces/icore.h>
0026 #include <interfaces/ilanguagecontroller.h>
0027 #include <language/backgroundparser/backgroundparser.h>
0028 #include <language/editor/documentrange.h>
0029 
0030 #include <duchaindebug.h>
0031 #include <duchain/editorintegrator.h>
0032 #include <duchain/helpers.h>
0033 #include <duchain/loader.h>
0034 #include <duchain/rubyducontext.h>
0035 
0036 using namespace KDevelop;
0037 using namespace ruby;
0038 
0039 ContextBuilder::ContextBuilder()
0040 {
0041     m_mapAst = false;
0042 }
0043 
0044 ContextBuilder::~ContextBuilder()
0045 {
0046 }
0047 
0048 ReferencedTopDUContext ContextBuilder::build(const IndexedString &url,
0049                                              Ast *node,
0050                                              const ReferencedTopDUContext& updateContext_)
0051 {
0052     ReferencedTopDUContext updateContext(updateContext_);
0053     if (!updateContext) {
0054         DUChainReadLocker lock;
0055         updateContext = DUChain::self()->chainForDocument(url);
0056     }
0057     if (updateContext) {
0058         qCDebug(DUCHAIN) << "Re-compiling" << url.str();
0059         DUChainWriteLocker lock;
0060         updateContext->clearImportedParentContexts();
0061         updateContext->parsingEnvironmentFile()->clearModificationRevisions();
0062         updateContext->clearProblems();
0063         updateContext->updateImportsCache();
0064     } else {
0065         qCDebug(DUCHAIN) << "Compiling";
0066     }
0067 
0068     auto top = ContextBuilderBase::build(url, node, updateContext);
0069     {
0070         DUChainWriteLocker lock(DUChain::lock());
0071         top->updateImportsCache();
0072     }
0073     return top;
0074 }
0075 
0076 void ContextBuilder::setEditor(EditorIntegrator *editor)
0077 {
0078     m_editor = editor;
0079 }
0080 
0081 EditorIntegrator * ContextBuilder::editor() const
0082 {
0083     return m_editor;
0084 }
0085 
0086 void ContextBuilder::setContextOnNode(Ast *node, KDevelop::DUContext *ctx)
0087 {
0088     if (node->tree) {
0089         node->tree->context = ctx;
0090     }
0091     node->context = ctx;
0092 }
0093 
0094 KDevelop::DUContext * ContextBuilder::contextFromNode(Ast *node)
0095 {
0096     if (node->tree) {
0097         DUContext *ctx = static_cast<DUContext *>(node->tree->context);
0098         return ctx;
0099     }
0100     return node->context;
0101 }
0102 
0103 DUContext * ContextBuilder::newContext(const RangeInRevision &range)
0104 {
0105     return new RubyNormalDUContext(range, currentContext());
0106 }
0107 
0108 KDevelop::TopDUContext * ContextBuilder::newTopContext(const RangeInRevision &range,
0109                                                        ParsingEnvironmentFile *file)
0110 {
0111     KDevelop::IndexedString doc(m_editor->url());
0112     if (!file) {
0113         file = new KDevelop::ParsingEnvironmentFile(doc);
0114         file->setLanguage(KDevelop::IndexedString("Ruby"));
0115     }
0116     TopDUContext *top = new RubyDUContext<TopDUContext>(doc, range, file);
0117     top->setType(DUContext::Global);
0118     return top;
0119 }
0120 
0121 const KDevelop::CursorInRevision ContextBuilder::startPos(const Ast *node) const
0122 {
0123     return m_editor->findPosition(node->tree, Edge::FrontEdge);
0124 }
0125 
0126 KDevelop::RangeInRevision ContextBuilder::editorFindRange(Ast *fromRange,
0127                                                           Ast *toRange)
0128 {
0129     return m_editor->findRange(fromRange->tree, toRange->tree);
0130 }
0131 
0132 const DocumentRange ContextBuilder::getDocumentRange(const Node *node) const
0133 {
0134     KTextEditor::Range range(node->pos.start_line - 1, node->pos.start_col,
0135                              node->pos.end_line - 1, node->pos.end_col);
0136     return DocumentRange(m_editor->url(), range);
0137 }
0138 
0139 const DocumentRange ContextBuilder::getDocumentRange(const RangeInRevision &range) const
0140 {
0141     return DocumentRange(m_editor->url(), range.castToSimpleRange());
0142 }
0143 
0144 KDevelop::QualifiedIdentifier ContextBuilder::identifierForNode(NameAst *name)
0145 {
0146     if (!name) {
0147         return QualifiedIdentifier();
0148     }
0149     return QualifiedIdentifier(name->value);
0150 }
0151 
0152 void ContextBuilder::startVisiting(Ast *node)
0153 {
0154     const auto &builtins = builtinsFile();
0155 
0156     if (compilingContexts()) {
0157         TopDUContext *top = dynamic_cast<TopDUContext *>(currentContext());
0158         {
0159             // Mark that we will use a cached import-structure.
0160             DUChainWriteLocker lock;
0161             top->updateImportsCache();
0162         }
0163 
0164         Q_ASSERT(top);
0165         bool hasImports;
0166         {
0167             DUChainReadLocker rlock;
0168             hasImports = !top->importedParentContexts().isEmpty();
0169         }
0170         if (!hasImports && top->url() != builtins) {
0171             DUChainWriteLocker wlock;
0172             TopDUContext *import = DUChain::self()->chainForDocument(builtins);
0173             if (!import) {
0174                 qCDebug(DUCHAIN) << "importing the builtins file failed";
0175                 Q_ASSERT(false);
0176             } else {
0177                 top->addImportedParentContext(import);
0178                 top->updateImportsCache();
0179             }
0180         }
0181     }
0182     AstVisitor::visitCode(node);
0183 }
0184 
0185 void ContextBuilder::visitModuleStatement(Ast *node)
0186 {
0187     if (!node->foundProblems) {
0188         AstVisitor::visitModuleStatement(node);
0189     }
0190 }
0191 
0192 void ContextBuilder::visitClassStatement(Ast *node)
0193 {
0194     if (!node->foundProblems) {
0195         AstVisitor::visitClassStatement(node);
0196     }
0197 }
0198 
0199 void ContextBuilder::visitMethodStatement(Ast *node)
0200 {
0201     Node *aux = node->tree;
0202     NameAst name(node);
0203     DUContext *params = nullptr;
0204 
0205     node->tree = aux->r;
0206     if (node->tree) {
0207       RangeInRevision range = rangeForMethodArguments(node);
0208       params = openContext(node, range, DUContext::Function, &name);
0209       visitMethodArguments(node);
0210       closeContext();
0211     }
0212 
0213     node->tree = aux->l;
0214     DUContext *body = openContext(node, DUContext::Other, &name);
0215     if (compilingContexts()) {
0216         DUChainWriteLocker wlock;
0217         if (params) {
0218             body->addImportedParentContext(params);
0219         }
0220         body->setInSymbolTable(false);
0221     }
0222     visitBody(node);
0223     closeContext();
0224     node->tree = aux;
0225 }
0226 
0227 void ContextBuilder::visitBlock(Ast *node)
0228 {
0229     if (!node->tree) {
0230         return;
0231     }
0232 
0233     DUContext *block = openContext(node, DUContext::Other);
0234     if (compilingContexts()) {
0235         DUChainWriteLocker wlock;
0236         block->setInSymbolTable(false);
0237     }
0238     AstVisitor::visitBlock(node);
0239     closeContext();
0240 }
0241 
0242 void ContextBuilder::visitRequire(Ast *node, bool relative)
0243 {
0244     AstVisitor::visitRequire(node);
0245     Node *aux = node->tree->r;
0246 
0247     /* If this is not a string, don't even care about it. */
0248     if (aux->kind != token_string) {
0249         return;
0250     }
0251 
0252     Path path = Loader::getRequiredFile(aux, m_editor, relative);
0253     if (!path.isValid()) {
0254         appendProblem(
0255             aux,
0256             i18n("LoadError: cannot load such file: %1", path.path()),
0257             IProblem::Warning
0258         );
0259         return;
0260     }
0261     require(path);
0262 }
0263 
0264 void ContextBuilder::require(const KDevelop::Path &path)
0265 {
0266     DUChainWriteLocker lock;
0267     const IndexedString idx(path.path());
0268     ReferencedTopDUContext ctx = DUChain::self()->chainForDocument(idx);
0269 
0270     if (!ctx) {
0271         /*
0272          * Schedule the required file for parsing, and schedule the current one
0273          * for reparsing after that is done.
0274          */
0275         m_unresolvedImports.append(idx);
0276         auto parser = ICore::self()->languageController()->backgroundParser();
0277         if (parser->isQueued(idx)) {
0278             parser->removeDocument(idx);
0279         }
0280         parser->addDocument(idx, TopDUContext::ForceUpdate, m_priority - 1,
0281                             nullptr, ParseJob::FullSequentialProcessing);
0282     } else {
0283         currentContext()->addImportedParentContext(ctx);
0284     }
0285 }
0286 
0287 void ContextBuilder::appendProblem(const Node *node, const QString &msg,
0288                                    IProblem::Severity sev)
0289 {
0290     ProblemPointer p(new Problem());
0291     p->setFinalLocation(getDocumentRange(node));
0292     p->setSource(IProblem::SemanticAnalysis);
0293     p->setDescription(msg);
0294     p->setSeverity(sev);
0295 
0296     {
0297         DUChainWriteLocker lock;
0298         topContext()->addProblem(p);
0299     }
0300 }
0301 
0302 void ContextBuilder::appendProblem(const RangeInRevision &range, const QString &msg,
0303                                    IProblem::Severity sev)
0304 {
0305     ProblemPointer p(new Problem());
0306     p->setFinalLocation(getDocumentRange(range));
0307     p->setSource(IProblem::SemanticAnalysis);
0308     p->setDescription(msg);
0309     p->setSeverity(sev);
0310 
0311     {
0312         DUChainWriteLocker lock;
0313         topContext()->addProblem(p);
0314     }
0315 }
0316 
0317 const RangeInRevision ContextBuilder::rangeForMethodArguments(Ast *node)
0318 {
0319     if (!node->tree) {
0320         return RangeInRevision();
0321     }
0322 
0323     Ast last(get_last_expr(node->tree), node->context);
0324     return editorFindRange(node, &last);
0325 }
0326