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