diff --git a/codecompletion/items/functionitem.cpp b/codecompletion/items/functionitem.cpp index 65530b5..dd27777 100644 --- a/codecompletion/items/functionitem.cpp +++ b/codecompletion/items/functionitem.cpp @@ -1,209 +1,213 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "functionitem.h" #include #include #include #include +#include #include "types/gofunctiontype.h" #include "declarations/functiondeclaration.h" namespace go { FunctionCompletionItem::FunctionCompletionItem(DeclarationPointer decl, int depth, int atArgument): CompletionItem(decl, QExplicitlySharedDataPointer(), 0), m_depth(depth), m_atArgument(atArgument) { auto function = decl.dynamicCast(); GoFunctionType::Ptr type(fastCast(decl->abstractType().constData())); if(!type) return; bool variadicArgs = false; if(type->modifiers() == go::GoFunctionType::VariadicArgument) variadicArgs = true; - DUContext* argsContext = 0; + DUContext* argsContext = nullptr; if(function) - argsContext = function->internalContext(); + { + DUChainReadLocker lock; + argsContext = DUChainUtils::getArgumentContext(function.data()); + } m_arguments = "("; if(argsContext) { DUChainReadLocker lock; auto args = argsContext->allDeclarations(CursorInRevision::invalid(), decl->topContext(), false); //highlight last argument if it is variadic if(variadicArgs && m_atArgument >= args.size()) m_atArgument = args.size() - 1; int count = 0; for(auto arg : args) { if(m_atArgument == count) m_currentArgStart = m_arguments.length(); m_arguments += (arg.first->toString()); if(m_atArgument == count) m_currentArgEnd = m_arguments.length(); count++; if(count < args.size()) m_arguments += ", "; } }else if(type->arguments().size() != 0) { DUChainReadLocker lock; auto args = type->arguments(); int count = 0; if(variadicArgs && m_atArgument >= args.size()) m_atArgument = args.size() - 1; for(auto arg : args) { if(m_atArgument == count) m_currentArgStart = m_arguments.length(); m_arguments += (arg->toString()); if(m_atArgument == count) m_currentArgEnd = m_arguments.length(); count++; if(count < args.size()) m_arguments += ", "; } } if(variadicArgs && m_arguments.lastIndexOf('[') != -1) { if(m_currentArgEnd >= m_arguments.lastIndexOf('[')) m_currentArgEnd++; //fix highlighting m_arguments = m_arguments.replace(m_arguments.lastIndexOf('['), 2, "..."); } m_arguments += ")"; DUContext* returnContext = 0; if(function) returnContext = function->returnArgsContext(); m_prefix = ""; if(returnContext) { DUChainReadLocker lock; auto args = returnContext->allDeclarations(CursorInRevision::invalid(), decl->topContext(), false); int count = 0; for(auto arg : args) { m_prefix += (arg.first->toString()); count++; if(count < args.size()) m_prefix += ", "; } } //if there was no return context or it didn't contain anything try type if((!returnContext || m_prefix == "") && type->returnArguments().size() != 0) { int count = 0; DUChainReadLocker lock; auto args = type->returnArguments(); for(auto arg : args) { m_prefix += arg->toString(); count++; if(count < args.size()) m_prefix += ", "; } } } void FunctionCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word) { KTextEditor::Document* document = view->document(); QString suffix = "()"; KTextEditor::Range checkSuffix(word.end().line(), word.end().column(), word.end().line(), document->lineLength(word.end().line())); if(document->text(checkSuffix).startsWith('(')) { suffix.clear(); } document->replaceText(word, declaration()->identifier().toString() + suffix); AbstractType::Ptr type = declaration()->abstractType(); if(fastCast(type.constData())) { GoFunctionType* ftype = fastCast(type.constData()); //put cursor inside parentheses if function takes arguments if(ftype->arguments().size() > 0) view->setCursorPosition(KTextEditor::Cursor(word.end().line(), word.end().column() + 1)); } } QVariant FunctionCompletionItem::data(const QModelIndex& index, int role, const CodeCompletionModel* model) const { switch(role) { case Qt::DisplayRole: { switch (index.column()) { case CodeCompletionModel::Prefix: return m_prefix; case CodeCompletionModel::Arguments: return m_arguments; } break; } case CodeCompletionModel::CompletionRole: return (int)completionProperties(); case CodeCompletionModel::HighlightingMethod: if (index.column() == CodeCompletionModel::Arguments) { return (int)CodeCompletionModel::CustomHighlighting; } break; case KDevelop::CodeCompletionModel::CustomHighlight: { if (index.column() == CodeCompletionModel::Arguments && m_atArgument != -1) { QTextFormat format; format.setBackground(QBrush(QColor::fromRgb(142, 186, 255))); // Same color as kdev-python format.setProperty(QTextFormat::FontWeight, 99); return QVariantList() << m_currentArgStart << m_currentArgEnd - m_currentArgStart << format; } } } return CompletionItem::data(index, role, model); } CodeCompletionModel::CompletionProperties FunctionCompletionItem::completionProperties() const { return CodeCompletionModel::Function; } int FunctionCompletionItem::argumentHintDepth() const { return m_depth; } int FunctionCompletionItem::inheritanceDepth() const { return 0; } } diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp index 0fbe588..ca3a9b7 100644 --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -1,626 +1,607 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "declarationbuilder.h" #include #include #include #include #include #include #include #include #include #include #include "expressionvisitor.h" #include "helper.h" #include "duchaindebug.h" using namespace KDevelop; DeclarationBuilder::DeclarationBuilder(ParseSession* session, bool forExport) : m_export(forExport), m_preBuilding(false), m_lastTypeComment(), m_lastConstComment() { setParseSession(session); } KDevelop::ReferencedTopDUContext DeclarationBuilder::build(const KDevelop::IndexedString& url, go::AstNode* node, KDevelop::ReferencedTopDUContext updateContext) { qCDebug(DUCHAIN) << "DeclarationBuilder start"; if(!m_preBuilding) { qCDebug(DUCHAIN) << "Running prebuilder"; DeclarationBuilder preBuilder(m_session, m_export); preBuilder.m_preBuilding = true; updateContext = preBuilder.build(url, node, updateContext); } return DeclarationBuilderBase::build(url, node, updateContext); } void DeclarationBuilder::startVisiting(go::AstNode* node) { { DUChainWriteLocker lock; topContext()->clearImportedParentContexts(); topContext()->updateImportsCache(); } return DeclarationBuilderBase::startVisiting(node); } void DeclarationBuilder::visitVarSpec(go::VarSpecAst* node) { if(node->type) {//if type is supplied we don't visit expressions declareVariablesWithType(node->id, node->idList, node->type, false); }else if(node->expression) { declareVariables(node->id, node->idList, node->expression, node->expressionList, false); } } void DeclarationBuilder::visitShortVarDecl(go::ShortVarDeclAst* node) { declareVariables(node->id, node->idList, node->expression, node->expressionList, false); } void DeclarationBuilder::declareVariablesWithType(go::IdentifierAst* id, go::IdListAst* idList, go::TypeAst* type, bool declareConstant) { m_contextIdentifier = identifierForNode(id); visitType(type); if(!lastType()) injectType(AbstractType::Ptr(new IntegralType(IntegralType::TypeNone))); lastType()->setModifiers(declareConstant ? AbstractType::ConstModifier : AbstractType::NoModifiers); if(identifierForNode(id).toString() != "_") { declareVariable(id, lastType()); } if(declareConstant) m_constAutoTypes.append(lastType()); if(idList) { auto iter = idList->idSequence->front(), end = iter; do { if(identifierForNode(iter->element).toString() != "_") { declareVariable(iter->element, lastType()); } if(declareConstant) m_constAutoTypes.append(lastType()); iter = iter->next; } while (iter != end); } } void DeclarationBuilder::declareVariables(go::IdentifierAst* id, go::IdListAst* idList, go::ExpressionAst* expression, go::ExpressionListAst* expressionList, bool declareConstant) { m_contextIdentifier = identifierForNode(id); QList types; if(!expression) return; go::ExpressionVisitor exprVisitor(m_session, currentContext(), this); exprVisitor.visitExpression(expression); Q_ASSERT(exprVisitor.lastTypes().size() != 0); if(!expressionList) types = exprVisitor.lastTypes(); else { types.append(exprVisitor.lastTypes().first()); auto iter = expressionList->expressionsSequence->front(), end = iter; do { exprVisitor.clearAll(); exprVisitor.visitExpression(iter->element); Q_ASSERT(exprVisitor.lastTypes().size() != 0); types.append(exprVisitor.lastTypes().first()); iter = iter->next; } while (iter != end); } if(types.size() == 0) return; for(AbstractType::Ptr& type : types) type->setModifiers(declareConstant ? AbstractType::ConstModifier : AbstractType::NoModifiers); if(declareConstant) m_constAutoTypes = types; if(identifierForNode(id).toString() != "_") { declareVariable(id, types.first()); } if(idList) { int typeIndex = 1; auto iter = idList->idSequence->front(), end = iter; do { if(typeIndex >= types.size()) //not enough types to declare all variables return; if(identifierForNode(iter->element).toString() != "_") { declareVariable(iter->element, types.at(typeIndex)); } iter = iter->next; typeIndex++; } while (iter != end); } } void DeclarationBuilder::declareVariable(go::IdentifierAst* id, const AbstractType::Ptr& type) { if(type->modifiers() & AbstractType::ConstModifier) setComment(m_lastConstComment); DUChainWriteLocker lock; Declaration* dec = openDeclaration(identifierForNode(id), editorFindRange(id, 0)); dec->setType(type); dec->setKind(Declaration::Instance); closeDeclaration(); } void DeclarationBuilder::visitConstDecl(go::ConstDeclAst* node) { m_constAutoTypes.clear(); m_lastConstComment = m_session->commentBeforeToken(node->startToken); //adding const declaration code, just like in GoDoc m_lastConstComment.append(m_session->textForNode(node).toUtf8()); go::DefaultVisitor::visitConstDecl(node); m_lastConstComment = QByteArray(); } void DeclarationBuilder::visitConstSpec(go::ConstSpecAst* node) { if(node->type) { declareVariablesWithType(node->id, node->idList, node->type, true); }else if(node->expression) { declareVariables(node->id, node->idList, node->expression, node->expressionList, true); }else {//this can only happen after a previous constSpec with some expressionList //in this case identifiers assign same types as previous constSpec(http://golang.org/ref/spec#Constant_declarations) if(m_constAutoTypes.size() == 0) return; { declareVariable(node->id, m_constAutoTypes.first()); } if(node->idList) { int typeIndex = 1; auto iter = node->idList->idSequence->front(), end = iter; do { if(typeIndex >= m_constAutoTypes.size()) //not enough types to declare all constants return; declareVariable(iter->element, m_constAutoTypes.at(typeIndex)); iter = iter->next; typeIndex++; } while (iter != end); } } } void DeclarationBuilder::visitFuncDeclaration(go::FuncDeclarationAst* node) { - go::GoFunctionDeclaration* decl = parseSignature(node->signature, true, node->funcName, m_session->commentBeforeToken(node->startToken-1)); - if(!node->body) - return; - //a context will be opened when visiting block, but we still open another one here - //so we can import arguments into it.(same goes for methodDeclaration) - DUContext* bodyContext = openContext(node->body, DUContext::ContextType::Function, node->funcName); - {//import parameters into body context - DUChainWriteLocker lock; - if(decl->internalContext()) - currentContext()->addImportedParentContext(decl->internalContext()); - if(decl->returnArgsContext()) - currentContext()->addImportedParentContext(decl->returnArgsContext()); - } - - visitBlock(node->body); - { - DUChainWriteLocker lock; - lastContext()->setType(DUContext::Function); - decl->setInternalFunctionContext(lastContext()); //inner block context - decl->setKind(Declaration::Instance); - } - closeContext(); //body wrapper context + buildFunction(node->signature, node->body, node->funcName, m_session->commentBeforeToken(node->startToken-1)); } void DeclarationBuilder::visitPrimaryExpr(go::PrimaryExprAst *node) { if(node->signature && !node->convArg) // func type literal and not conversion. { buildFunction(node->signature, node->body); } else { go::DefaultVisitor::visitPrimaryExpr(node); } } void DeclarationBuilder::visitMethodDeclaration(go::MethodDeclarationAst* node) { Declaration* declaration=0; if(node->methodRecv) { - go::IdentifierAst* actualtype=0; - if(node->methodRecv->ptype) - actualtype = node->methodRecv->ptype; - else if(node->methodRecv->type) - actualtype = node->methodRecv->type; - else - actualtype = node->methodRecv->nameOrType; - DUChainWriteLocker lock; - declaration = openDeclaration(identifierForNode(actualtype), editorFindRange(actualtype, 0)); - declaration->setKind(Declaration::Namespace); - openContext(node, editorFindRange(node, 0), DUContext::Namespace, identifierForNode(actualtype)); - declaration->setInternalContext(currentContext()); + go::IdentifierAst* actualtype=0; + if(node->methodRecv->ptype) + actualtype = node->methodRecv->ptype; + else if(node->methodRecv->type) + actualtype = node->methodRecv->type; + else + actualtype = node->methodRecv->nameOrType; + DUChainWriteLocker lock; + declaration = openDeclaration(identifierForNode(actualtype), editorFindRange(actualtype, 0)); + declaration->setKind(Declaration::Namespace); + openContext(node, editorFindRange(node, 0), DUContext::Namespace, identifierForNode(actualtype)); + declaration->setInternalContext(currentContext()); } - go::GoFunctionDeclaration* decl = parseSignature(node->signature, true, node->methodName, m_session->commentBeforeToken(node->startToken-1)); - - if(!node->body) - return; - DUContext* bodyContext = openContext(node->body, DUContext::ContextType::Function, node->methodName); + auto functionDeclaration = buildFunction(node->signature, node->body, node->methodName, m_session->commentBeforeToken(node->startToken-1)); - {//import parameters into body context - DUChainWriteLocker lock; - if(decl->internalContext()) - currentContext()->addImportedParentContext(decl->internalContext()); - if(decl->returnArgsContext()) - currentContext()->addImportedParentContext(decl->returnArgsContext()); - } - - if(node->methodRecv->type) + if(node->methodRecv->type && functionDeclaration->internalContext()) {//declare method receiver variable('this' or 'self' analog in Go) + openContext(functionDeclaration->internalContext()); buildTypeName(node->methodRecv->type); - if(node->methodRecv->star!= -1) - { - PointerType* ptype = new PointerType(); - ptype->setBaseType(lastType()); - injectType(PointerType::Ptr(ptype)); - } - DUChainWriteLocker n; - Declaration* thisVariable = openDeclaration(identifierForNode(node->methodRecv->nameOrType), editorFindRange(node->methodRecv->nameOrType, 0)); - thisVariable->setAbstractType(lastType()); - closeDeclaration(); - } - - visitBlock(node->body); - { - DUChainWriteLocker lock; - lastContext()->setType(DUContext::Function); - decl->setInternalFunctionContext(lastContext()); //inner block context - decl->setKind(Declaration::Instance); + if(node->methodRecv->star!= -1) + { + PointerType* ptype = new PointerType(); + ptype->setBaseType(lastType()); + injectType(PointerType::Ptr(ptype)); + } + DUChainWriteLocker n; + Declaration* thisVariable = openDeclaration(identifierForNode(node->methodRecv->nameOrType), editorFindRange(node->methodRecv->nameOrType, 0)); + thisVariable->setAbstractType(lastType()); + closeDeclaration(); + closeContext(); } - - closeContext(); //body wrapper context + closeContext(); //namespace closeDeclaration(); //namespace declaration } void DeclarationBuilder::visitTypeSpec(go::TypeSpecAst* node) { //first try setting comment before type name //if it doesn't exists, set comment before type declaration QByteArray comment = m_session->commentBeforeToken(node->startToken); if(comment.size() == 0) comment = m_lastTypeComment; setComment(comment); Declaration* decl; { DUChainWriteLocker lock; decl = openDeclaration(identifierForNode(node->name), editorFindRange(node->name, 0)); //decl->setKind(Declaration::Namespace); decl->setKind(Declaration::Type); //force direct here because otherwise DeclarationId will mess up actual type declaration and method declarations //TODO perhaps we can do this with specialization or additional identity? decl->setAlwaysForceDirect(true); } m_contextIdentifier = identifierForNode(node->name); visitType(node->type); DUChainWriteLocker lock; //qCDebug(DUCHAIN) << lastType()->toString(); decl->setType(lastType()); decl->setIsTypeAlias(true); closeDeclaration(); //qCDebug(DUCHAIN) << "Type" << identifierForNode(node->name) << " exit"; } void DeclarationBuilder::visitImportSpec(go::ImportSpecAst* node) { //prevent recursive imports //without preventing recursive imports. importing standart go library(2000+ files) takes minutes and sometimes never stops //thankfully go import mechanism doesn't need recursive imports(I think) //if(m_export) //return; QString import(identifierForIndex(node->importpath->import).toString()); QList contexts = m_session->contextForImport(import); if(contexts.empty()) return; //usually package name matches directory, so try searching for that first QualifiedIdentifier packageName(import.mid(1, import.length()-2)); bool firstContext = true; for(const ReferencedTopDUContext& context : contexts) { //don't import itself if(context.data() == topContext()) continue; DeclarationPointer decl = go::checkPackageDeclaration(packageName.last(), context); if(!decl && firstContext) { decl = go::getFirstDeclaration(context); //package name differs from directory, so get the real name if(!decl) continue; DUChainReadLocker lock; packageName = decl->qualifiedIdentifier(); } if(!decl) //contexts belongs to a different package continue; DUChainWriteLocker lock; if(firstContext) //only open declarations once per import(others are redundant) { setComment(decl->comment()); if(node->packageName) {//create alias for package QualifiedIdentifier id = identifierForNode(node->packageName); NamespaceAliasDeclaration* decl = openDeclaration(id, editorFindRange(node->importpath, 0)); decl->setKind(Declaration::NamespaceAlias); decl->setImportIdentifier(packageName); //this needs to be actual package name closeDeclaration(); }else if(node->dot != -1) {//anonymous import NamespaceAliasDeclaration* decl = openDeclaration(QualifiedIdentifier(globalImportIdentifier()), editorFindRange(node->importpath, 0)); decl->setKind(Declaration::NamespaceAlias); decl->setImportIdentifier(packageName); //this needs to be actual package name closeDeclaration(); }else { Declaration* decl = openDeclaration(packageName, editorFindRange(node->importpath, 0)); decl->setKind(Declaration::Import); closeDeclaration(); } } topContext()->addImportedParentContext(context.data()); firstContext = false; } DUChainWriteLocker lock; } void DeclarationBuilder::visitSourceFile(go::SourceFileAst* node) { setComment(m_session->commentBeforeToken(node->startToken)); DUChainWriteLocker lock; Declaration* packageDeclaration = openDeclaration(identifierForNode(node->packageClause->packageName), editorFindRange(node->packageClause->packageName, 0)); packageDeclaration->setKind(Declaration::Namespace); openContext(node, editorFindRange(node, 0), DUContext::Namespace, identifierForNode(node->packageClause->packageName)); packageDeclaration->setInternalContext(currentContext()); lock.unlock(); m_thisPackage = identifierForNode(node->packageClause->packageName); //import package this context belongs to importThisPackage(); importBuiltins(); go::DefaultVisitor::visitSourceFile(node); closeContext(); closeDeclaration(); } void DeclarationBuilder::importThisPackage() { QList contexts = m_session->contextForThisPackage(document()); if(contexts.empty()) return; for(const ReferencedTopDUContext& context : contexts) { if(context.data() == topContext()) continue; //import only contexts with the same package name DeclarationPointer decl = go::checkPackageDeclaration(m_thisPackage.last(), context); if(!decl) continue; //if our package doesn't have comment, but some file in out package does, copy it if(currentDeclaration()->comment().size() == 0 && decl->comment().size() != 0) currentDeclaration()->setComment(decl->comment()); DUChainWriteLocker lock; //TODO Since package names are identical duchain should find declarations without namespace alias, right? //NamespaceAliasDeclaration* import = openDeclaration(QualifiedIdentifier(globalImportIdentifier()), RangeInRevision()); //import->setKind(Declaration::NamespaceAlias); //import->setImportIdentifier(packageName); //this needs to be actual package name //closeDeclaration(); topContext()->addImportedParentContext(context.data()); } DUChainWriteLocker lock; } void DeclarationBuilder::importBuiltins() { QString builtinFile = go::Helper::getBuiltinFile(); if(builtinFile.isEmpty() || IndexedString(builtinFile) == document()) return; ReferencedTopDUContext builtins = go::Helper::getBuiltinContext(); if(!builtins) {//schedule this file for reparsing, if builtins were not parsed yet m_session->rescheduleThisFile(); }else { DUChainWriteLocker lock; //import builtins NamespaceAliasDeclaration* decl = openDeclaration(QualifiedIdentifier(globalImportIdentifier()), RangeInRevision()); decl->setKind(Declaration::NamespaceAlias); decl->setImportIdentifier(QualifiedIdentifier("_builtins")); closeDeclaration(); topContext()->addImportedParentContext(builtins); } } void DeclarationBuilder::visitForStmt(go::ForStmtAst* node) { openContext(node, editorFindRange(node, 0), DUContext::Other); //wrapper context if(node->range != -1 && node->autoassign != -1) {//manually infer types go::ExpressionVisitor exprVisitor(m_session, currentContext(), this); exprVisitor.visitRangeClause(node->rangeExpression); auto types = exprVisitor.lastTypes(); if(!types.empty()) { declareVariable(identifierAstFromExpressionAst(node->expression), types.first()); if(types.size() > 1 && node->expressionList) { int typeIndex = 1; auto iter = node->expressionList->expressionsSequence->front(), end = iter; do { if(typeIndex >= types.size()) //not enough types to declare all variables break; declareVariable(identifierAstFromExpressionAst(iter->element), types.at(typeIndex)); iter = iter->next; typeIndex++; } while (iter != end); } } } DeclarationBuilderBase::visitForStmt(node); closeContext(); } void DeclarationBuilder::visitSwitchStmt(go::SwitchStmtAst* node) { openContext(node, editorFindRange(node, 0), DUContext::Other); //wrapper context if(node->typeSwitchStatement && node->typeSwitchStatement->typeSwitchGuard) { go::TypeSwitchGuardAst* typeswitch = node->typeSwitchStatement->typeSwitchGuard; go::ExpressionVisitor expVisitor(m_session, currentContext(), this); expVisitor.visitPrimaryExpr(typeswitch->primaryExpr); if(!expVisitor.lastTypes().empty()) { declareVariable(typeswitch->ident, expVisitor.lastTypes().first()); m_switchTypeVariable = identifierForNode(typeswitch->ident); } } DeclarationBuilderBase::visitSwitchStmt(node); closeContext(); //wrapper context m_switchTypeVariable.clear(); } void DeclarationBuilder::visitTypeCaseClause(go::TypeCaseClauseAst* node) { openContext(node, editorFindRange(node, 0), DUContext::Other); const KDevPG::ListNode* typeIter = 0; if(node->typelistSequence) typeIter = node->typelistSequence->front(); if(node->defaultToken == -1 && typeIter && typeIter->next == typeIter) {//if default is not specified and only one type is listed //we open another declaration of listed type visitType(typeIter->element); lastType()->setModifiers(AbstractType::NoModifiers); DUChainWriteLocker lock; if(lastType()->toString() != "nil" && !m_switchTypeVariable.isEmpty()) {//in that case we also don't open declaration Declaration* decl = openDeclaration(m_switchTypeVariable, editorFindRange(typeIter->element, 0)); decl->setAbstractType(lastType()); closeDeclaration(); } } go::DefaultVisitor::visitTypeCaseClause(node); closeContext(); } void DeclarationBuilder::visitExprCaseClause(go::ExprCaseClauseAst* node) { openContext(node, editorFindRange(node, 0), DUContext::Other); go::DefaultVisitor::visitExprCaseClause(node); closeContext(); } void DeclarationBuilder::visitCommCase(go::CommCaseAst* node) { if(node->autoassign != -1) { go::ExpressionVisitor expVisitor(m_session, currentContext(), this); expVisitor.visitExpression(node->recvExp); auto types = expVisitor.lastTypes(); if(types.size() == 2) //expression must be a receive operator, returning two values { declareVariable(identifierAstFromExpressionAst(node->sendOrRecv), types.first()); if(node->expressionList) { auto secondVariable = node->expressionList->expressionsSequence->front(); declareVariable(identifierAstFromExpressionAst(secondVariable->element), types.at(1)); } } } DeclarationBuilderBase::visitCommCase(node); } void DeclarationBuilder::visitCommClause(go::CommClauseAst* node) { openContext(node, editorFindRange(node, 0), DUContext::Other); //wrapper context DeclarationBuilderBase::visitCommClause(node); closeContext(); } void DeclarationBuilder::visitTypeDecl(go::TypeDeclAst* node) { m_lastTypeComment = m_session->commentBeforeToken(node->startToken); go::DefaultVisitor::visitTypeDecl(node); m_lastTypeComment = QByteArray(); } go::GoFunctionDeclaration* DeclarationBuilder::declareFunction(go::IdentifierAst* id, const go::GoFunctionType::Ptr& type, - DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment) + DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment, DUContext* bodyContext) { setComment(comment); DUChainWriteLocker lock; go::GoFunctionDeclaration* dec = openDefinition(identifierForNode(id), editorFindRange(id, 0)); dec->setType(type); - //dec->setKind(Declaration::Type); dec->setKind(Declaration::Instance); - dec->setInternalContext(paramContext); - if(retparamContext) - dec->setReturnArgsContext(retparamContext); - //dec->setInternalFunctionContext(bodyContext); + dec->setInternalContext(bodyContext); + + if(bodyContext) + { + if(paramContext) + { + bodyContext->addImportedParentContext(paramContext); + dec->setInternalFunctionContext(paramContext); + } + if(retparamContext) + { + bodyContext->addImportedParentContext(retparamContext); + dec->setReturnArgsContext(retparamContext); + } + } + closeDeclaration(); return dec; } +go::GoFunctionDeclaration* DeclarationBuilder::buildFunction(go::SignatureAst* node, go::BlockAst* block, go::IdentifierAst* name, const QByteArray& comment) +{ + DUContext* bodyContext = nullptr; + if(block) + { + visitBlock(block); + bodyContext = lastContext(); + } + return parseSignature(node, true, bodyContext, name, comment); +} \ No newline at end of file diff --git a/duchain/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h index 3d44a47..1a9a5e3 100644 --- a/duchain/builders/declarationbuilder.h +++ b/duchain/builders/declarationbuilder.h @@ -1,116 +1,120 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KDEVGOLANGDECLBUILDER_H #define KDEVGOLANGDECLBUILDER_H #include #include #include "duchain/kdevgoduchain_export.h" #include "contextbuilder.h" #include "typebuilder.h" #include "parser/parsesession.h" #include "parser/goast.h" typedef KDevelop::AbstractDeclarationBuilder DeclarationBuilderBase; class KDEVGODUCHAIN_EXPORT DeclarationBuilder : public DeclarationBuilderBase { public: DeclarationBuilder(ParseSession* session, bool forExport); virtual KDevelop::ReferencedTopDUContext build(const KDevelop::IndexedString& url, go::AstNode* node, KDevelop::ReferencedTopDUContext updateContext = KDevelop::ReferencedTopDUContext()); virtual void startVisiting(go::AstNode* node); virtual void visitVarSpec(go::VarSpecAst* node); virtual void visitShortVarDecl(go::ShortVarDeclAst* node); virtual void visitConstSpec(go::ConstSpecAst* node); virtual void visitConstDecl(go::ConstDeclAst* node); virtual void visitFuncDeclaration(go::FuncDeclarationAst* node); virtual void visitMethodDeclaration(go::MethodDeclarationAst* node); virtual void visitTypeSpec(go::TypeSpecAst* node); virtual void visitImportSpec(go::ImportSpecAst* node); virtual void visitSourceFile(go::SourceFileAst* node); virtual void visitForStmt(go::ForStmtAst* node); virtual void visitSwitchStmt(go::SwitchStmtAst* node); virtual void visitTypeCaseClause(go::TypeCaseClauseAst* node); virtual void visitExprCaseClause(go::ExprCaseClauseAst* node); virtual void visitPrimaryExpr(go::PrimaryExprAst *node); /** * this handles variable declaration in select statements, e.g. * select { case i := <-mychan: bla bla... } * NOTE: right hand side expression must be a receive operator, returning two values */ virtual void visitCommCase(go::CommCaseAst* node); virtual void visitCommClause(go::CommClauseAst* node); virtual void visitTypeDecl(go::TypeDeclAst* node); - + + /** + * A shortcut for ExpressionVisitor to build function type + **/ + go::GoFunctionDeclaration* buildFunction(go::SignatureAst* node, go::BlockAst* block = nullptr, go::IdentifierAst* name = nullptr, const QByteArray& comment = {}); /*struct GoImport{ GoImport(bool anon, KDevelop::TopDUContext* ctx) : anonymous(anon), context(ctx) {} bool anonymous; KDevelop::TopDUContext* context; };*/ private: /** * Deduces types of expression with ExpressionVisitor and declares variables * from idList with respective types. If there is a single expression, returning multiple types * idList will get assigned those types. Otherwise we get only first type no matter how many of them * expression returns.(I believe this is how it works in Go, correct it if I'm wrong) * @param declareConstant whether to declare usual variables or constants */ void declareVariables(go::IdentifierAst* id, go::IdListAst* idList, go::ExpressionAst* expression, go::ExpressionListAst* expressionList, bool declareConstant); /** * declares variables or constants with names from id and idList of type type. */ void declareVariablesWithType(go::IdentifierAst* id, go::IdListAst* idList, go::TypeAst* type, bool declareConstant); /** * Declares variable with identifier @param id of type @param type **/ virtual void declareVariable(go::IdentifierAst* id, const AbstractType::Ptr& type) override; /** * Declares GoFunction and assigns contexts to it. * Called from typebuilder when building functions and methods **/ virtual go::GoFunctionDeclaration* declareFunction(go::IdentifierAst* id, const go::GoFunctionType::Ptr& type, - DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment=QByteArray()) override; + DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment = {}, DUContext* bodyContext = nullptr) override; void importThisPackage(); void importBuiltins(); bool m_export; //QHash m_anonymous_imports; bool m_preBuilding; QList m_constAutoTypes; QualifiedIdentifier m_thisPackage; QualifiedIdentifier m_switchTypeVariable; QByteArray m_lastTypeComment, m_lastConstComment; }; #endif \ No newline at end of file diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp index 461e898..6a2a7ce 100644 --- a/duchain/builders/typebuilder.cpp +++ b/duchain/builders/typebuilder.cpp @@ -1,453 +1,431 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "typebuilder.h" #include #include #include #include "types/gointegraltype.h" #include "types/gostructuretype.h" #include "types/gomaptype.h" #include "types/gochantype.h" #include "helper.h" using namespace KDevelop; namespace go { void TypeBuilder::visitTypeName(go::TypeNameAst* node) { buildTypeName(node->name, node->type_resolve->fullName); } void TypeBuilder::buildTypeName(IdentifierAst* typeName, IdentifierAst* fullName) { uint type = IntegralType::TypeNone; QualifiedIdentifier id = identifierForNode(typeName); QString name = id.toString(); //Builtin types if(name == "uint8") type = go::GoIntegralType::TypeUint8; else if(name == "uint16") type = go::GoIntegralType::TypeUint16; else if(name == "uint32") type = go::GoIntegralType::TypeUint32; else if(name == "uint64") type = go::GoIntegralType::TypeUint64; else if(name == "int8") type = go::GoIntegralType::TypeUint8; else if(name == "int16") type = go::GoIntegralType::TypeInt16; else if(name == "int32") type = go::GoIntegralType::TypeInt32; else if(name == "int64") type = go::GoIntegralType::TypeInt64; else if(name == "float32") type = go::GoIntegralType::TypeFloat32; else if(name == "float64") type = go::GoIntegralType::TypeFloat64; else if(name == "complex64") type = go::GoIntegralType::TypeComplex64; else if(name == "complex128") type = go::GoIntegralType::TypeComplex128; else if(name == "rune") type = go::GoIntegralType::TypeRune; else if(name == "int") type = go::GoIntegralType::TypeInt; else if(name == "uint") type = go::GoIntegralType::TypeUint; else if(name == "uintptr") type = go::GoIntegralType::TypeUintptr; else if(name == "string") type = go::GoIntegralType::TypeString; else if(name == "bool") type = go::GoIntegralType::TypeBool; else if(name == "byte") type = go::GoIntegralType::TypeByte; if(type == IntegralType::TypeNone) { //in Go one can create variable of package type, like 'fmt fmt' //TODO support such declarations QualifiedIdentifier id(identifierForNode(typeName)); if(fullName) id.push(identifierForNode(fullName)); DeclarationPointer decl = go::getTypeDeclaration(id, currentContext()); if(decl) { DUChainReadLocker lock; StructureType* type = new StructureType(); type->setDeclaration(decl.data()); injectType(AbstractType::Ptr(type)); //kDebug() << decl->range(); return; } DelayedType* unknown = new DelayedType(); unknown->setIdentifier(IndexedTypeIdentifier(id)); injectType(AbstractType::Ptr(unknown)); return; } if(type != IntegralType::TypeNone) { injectType(AbstractType::Ptr(new go::GoIntegralType(type))); } } void TypeBuilder::visitArrayOrSliceType(go::ArrayOrSliceTypeAst* node) { if(node->arrayOrSliceResolve->array) visitType(node->arrayOrSliceResolve->array); else if(node->arrayOrSliceResolve->slice) visitType(node->arrayOrSliceResolve->slice); else //error injectType(AbstractType::Ptr()); //TODO create custom classes GoArrayType and GoSliceType //to properly distinguish between go slices and arrays ArrayType* array = new ArrayType(); //kDebug() << lastType()->toString(); array->setElementType(lastType()); injectType(ArrayType::Ptr(array)); } void TypeBuilder::visitPointerType(go::PointerTypeAst* node) { PointerType* type = new PointerType(); visitType(node->type); type->setBaseType(lastType()); injectType(PointerType::Ptr(type)); } void TypeBuilder::visitStructType(go::StructTypeAst* node) { openType(go::GoStructureType::Ptr(new go::GoStructureType)); { DUChainWriteLocker lock; openContext(node, editorFindRange(node, 0), DUContext::ContextType::Class, m_contextIdentifier); } TypeBuilderBase::visitStructType(node); { DUChainWriteLocker lock; currentType()->setContext(currentContext()); closeContext(); } currentType()->setPrettyName(m_session->textForNode(node)); currentType()->setStructureType(); closeType(); } void TypeBuilder::visitFieldDecl(go::FieldDeclAst* node) { StructureType::Ptr structure = currentType(); QList names; if(node->anonFieldStar) { PointerType* type = new PointerType(); visitTypeName(node->anonFieldStar->typeName); type->setBaseType(lastType()); go::IdentifierAst* id = node->anonFieldStar->typeName->type_resolve->fullName ? node->anonFieldStar->typeName->type_resolve->fullName : node->anonFieldStar->typeName->name; injectType(PointerType::Ptr(type)); names.append(id); }else if(node->type) { visitType(node->type); names.append(node->varid); if(node->idList) { auto elem = node->idList->idSequence->front(); while(true) { names.append(elem->element); if(elem->hasNext()) elem = elem->next; else break; } } }else { buildTypeName(node->varid, node->fullname); go::IdentifierAst* id = node->fullname ? node->fullname : node->varid; names.append(id); } for(auto name : names) { declareVariable(name, lastType()); } } void TypeBuilder::visitInterfaceType(go::InterfaceTypeAst* node) { openType(go::GoStructureType::Ptr(new go::GoStructureType)); //ClassDeclaration* decl; { DUChainWriteLocker lock; //decl = openDeclaration(QualifiedIdentifier(), RangeInRevision()); openContext(node, editorFindRange(node, 0), DUContext::ContextType::Class, m_contextIdentifier); } TypeBuilderBase::visitInterfaceType(node); { DUChainWriteLocker lock; //decl->setInternalContext(currentContext()); //decl->setClassType(ClassDeclarationData::Interface); currentType()->setContext(currentContext()); closeContext(); //closeDeclaration(); //currentType()->setDeclaration(decl); //decl->setIdentifier(Identifier(QString("interface type"))); } currentType()->setPrettyName(m_session->textForNode(node)); currentType()->setInterfaceType(); closeType(); } void TypeBuilder::visitMethodSpec(go::MethodSpecAst* node) { if(node->signature) { - parseSignature(node->signature, true, node->methodName); + parseSignature(node->signature, true, nullptr, node->methodName); }else{ buildTypeName(node->methodName, node->fullName); go::IdentifierAst* id = node->fullName ? node->fullName : node->methodName; { declareVariable(id, lastType()); } } } void TypeBuilder::visitMapType(go::MapTypeAst* node) { go::GoMapType* type = new go::GoMapType(); visitType(node->keyType); type->setKeyType(lastType()); visitType(node->elemType); type->setValueType(lastType()); injectType(AbstractType::Ptr(type)); } void TypeBuilder::visitChanType(go::ChanTypeAst* node) { visitType(node->rtype ? node->rtype : node->stype); go::GoChanType::Ptr type(new go::GoChanType()); if(node->stype) type->setKind(go::GoChanType::Receive); else if(node->send != -1) type->setKind(go::GoChanType::Send); else type->setKind(go::GoChanType::SendAndReceive); DUChainReadLocker lock; type->setValueType(lastType()); injectType(type); } void TypeBuilder::visitFunctionType(go::FunctionTypeAst* node) { parseSignature(node->signature, false); } void TypeBuilder::visitParameter(go::ParameterAst* node) { //parameter grammar rule is written in such a way that full types won't be parsed automatically //so we do it manually(see go.g:parameter) //also if type is just identifier it is impossible to say right now whether it is really a type //or a identifier. This will be decided in parseParameres method if(node->idOrType && node->fulltype) buildTypeName(node->idOrType, node->fulltype); TypeBuilderBase::visitParameter(node); } -go::GoFunctionDeclaration* TypeBuilder::parseSignature(go::SignatureAst* node, bool declareParameters, go::IdentifierAst* name, const QByteArray& comment) +go::GoFunctionDeclaration* TypeBuilder::parseSignature(go::SignatureAst* node, bool declareParameters, DUContext* bodyContext, go::IdentifierAst* name, const QByteArray& comment) { go::GoFunctionType::Ptr type(new go::GoFunctionType()); openType(type); - DUContext* parametersContext; + DUContext* parametersContext = nullptr; if(declareParameters) parametersContext = openContext(node->parameters, editorFindRange(node->parameters, 0), DUContext::ContextType::Function, name); parseParameters(node->parameters, true, declareParameters); if(declareParameters) closeContext(); - DUContext* returnArgsContext=0; + DUContext* returnArgsContext = nullptr; if(node->result) { visitResult(node->result); if(node->result->parameters) { if(declareParameters) returnArgsContext = openContext(node->result, editorFindRange(node->result, 0), DUContext::ContextType::Function, name); parseParameters(node->result->parameters, false, declareParameters); if(declareParameters) closeContext(); } if(!node->result->parameters && lastType()) type->addReturnArgument(lastType()); } closeType(); if(declareParameters) { - return declareFunction(name, type, parametersContext, returnArgsContext, comment); + return declareFunction(name, type, parametersContext, returnArgsContext, comment, bodyContext); } return 0; } void TypeBuilder::parseParameters(go::ParametersAst* node, bool parseArguments, bool declareParameters) { //code below is a bit ugly because of problems with parsing go parameter list(see details at parser/go.g:331) go::GoFunctionType::Ptr function; function = currentType(); if(node->parameter) { QList paramNames; go::ParameterAst* param=node->parameter; visitParameter(param); //variadic arguments if(param->unnamedvartype || param->vartype) { function->setModifiers(go::GoFunctionType::VariadicArgument); ArrayType* atype = new ArrayType(); atype->setElementType(lastType()); injectType(AbstractType::Ptr(atype)); } if(!param->complexType && !param->parenType && !param->unnamedvartype && !param->type && !param->vartype && !param->fulltype) paramNames.append(param->idOrType); //we only have an identifier else { addArgumentHelper(function, lastType(), parseArguments); //if we have a parameter name(but it's not part of fullname) open declaration if(param->idOrType && !param->fulltype && declareParameters) declareVariable(param->idOrType, lastType()); } if(node->parameterListSequence) { auto elem = node->parameterListSequence->front(); while(true) { go::ParameterAst* param=elem->element; visitParameter(param); //variadic arguments if(param->unnamedvartype || param->vartype) { function->setModifiers(go::GoFunctionType::VariadicArgument); ArrayType* atype = new ArrayType(); atype->setElementType(lastType()); injectType(AbstractType::Ptr(atype)); } if(param->complexType || param->parenType || param->unnamedvartype || param->fulltype) {//we have a unnamed parameter list of types AbstractType::Ptr lType = lastType(); for(auto id : paramNames) { buildTypeName(id); addArgumentHelper(function, lastType(), parseArguments); } addArgumentHelper(function, lType, parseArguments); paramNames.clear(); }else if(!param->complexType && !param->parenType && !param->unnamedvartype && !param->type && !param->vartype && !param->fulltype) {//just another identifier paramNames.append(param->idOrType); }else {//identifier with type, all previous identifiers are of the same type for(auto id : paramNames) { addArgumentHelper(function, lastType(), parseArguments); if(declareParameters) declareVariable(id, lastType()); } addArgumentHelper(function, lastType(), parseArguments); if(declareParameters) declareVariable(param->idOrType, lastType()); paramNames.clear(); } if(elem->hasNext()) elem = elem->next; else break; } if(!paramNames.empty()) {//we have only identifiers which means they are all type names //foreach(auto id, paramNames) for(auto id : paramNames) { buildTypeName(id); addArgumentHelper(function, lastType(), parseArguments); } paramNames.clear(); } }else if(!paramNames.empty()) { //one identifier that we have is a type buildTypeName(param->idOrType); addArgumentHelper(function, lastType(), parseArguments); } } } void TypeBuilder::addArgumentHelper(go::GoFunctionType::Ptr function, AbstractType::Ptr argument, bool parseArguments) { DUChainWriteLocker lock; if(argument) { if(parseArguments) function->addArgument(argument); else function->addReturnArgument(argument); } } -//TODO call this from DeclarationBuilder::visitFunctionDecl -void TypeBuilder::buildFunction(SignatureAst* node, BlockAst* block) -{ - go::GoFunctionDeclaration* decl = parseSignature(node, true); - AbstractType::Ptr type = lastType(); - if(block) - { - DUContext* bodyContext = openContext(block, DUContext::ContextType::Function); - {//import parameters into body context - DUChainWriteLocker lock; - if(decl->internalContext()) - currentContext()->addImportedParentContext(decl->internalContext()); - if(decl->returnArgsContext()) - currentContext()->addImportedParentContext(decl->returnArgsContext()); - } - visitBlock(block); - closeContext(); //wrapper context - injectType(type); - } -} - - } \ No newline at end of file diff --git a/duchain/builders/typebuilder.h b/duchain/builders/typebuilder.h index bdd1713..8fabe1f 100644 --- a/duchain/builders/typebuilder.h +++ b/duchain/builders/typebuilder.h @@ -1,106 +1,99 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef TYPEBUILDER_H #define TYPEBUILDER_H #include #include "contextbuilder.h" #include "duchain/kdevgoduchain_export.h" #include "duchain/declarations/functiondeclaration.h" #include "duchain/types/gofunctiontype.h" namespace go { typedef KDevelop::AbstractTypeBuilder TypeBuilderBase; class KDEVGODUCHAIN_EXPORT TypeBuilder : public TypeBuilderBase { public: virtual void visitTypeName(go::TypeNameAst* node); virtual void visitArrayOrSliceType(go::ArrayOrSliceTypeAst* node); virtual void visitPointerType(go::PointerTypeAst* node); virtual void visitStructType(go::StructTypeAst* node); virtual void visitFieldDecl(go::FieldDeclAst* node); virtual void visitInterfaceType(go::InterfaceTypeAst* node); virtual void visitMethodSpec(go::MethodSpecAst* node); virtual void visitMapType(go::MapTypeAst* node); virtual void visitChanType(go::ChanTypeAst* node); virtual void visitFunctionType(go::FunctionTypeAst* node); virtual void visitParameter(go::ParameterAst* node); /** * When building named types we often have IdentifierAst instead of TypeNameAst, * so it makes sense to have this convenience function **/ void buildTypeName(go::IdentifierAst* typeName, go::IdentifierAst* fullName = 0); - /** - * A shortcut for ExpressionVisitor to build function type - **/ - void buildFunction(go::SignatureAst* node, go::BlockAst* block=0); - /** * Used by external classes like ExpressionVisitor after building a type. */ AbstractType::Ptr getLastType() { return lastType(); } protected: //when building some types we need to open declarations //so next methods are placeholders for that, which will be implemented in DeclarationBuilder //that way we can keep type building logic in TypeBuilder /** * declared here as pure virtual so we can use that when building functions, structs and interfaces. **/ virtual void declareVariable(go::IdentifierAst* id, const KDevelop::AbstractType::Ptr& type) = 0; /** * declared here as pure virtual so we can use that when building functions **/ virtual GoFunctionDeclaration* declareFunction(go::IdentifierAst* id, const GoFunctionType::Ptr& type, - DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment=QByteArray()) = 0; - - + DUContext* paramContext, DUContext* retparamContext, const QByteArray& comment=QByteArray(), DUContext* bodyContext = nullptr) = 0; /** * opens GoFunctionType, parses it's parameters and return declaration if @param declareParameters is true. **/ - go::GoFunctionDeclaration* parseSignature(go::SignatureAst* node, bool declareParameters, go::IdentifierAst* name=0, const QByteArray& comment=QByteArray()); + go::GoFunctionDeclaration* parseSignature(go::SignatureAst* node, bool declareParameters, DUContext* bodyContext = nullptr, go::IdentifierAst* name=0, const QByteArray& comment=QByteArray()); /** * Convenience function that parses function parameters. * @param parseParemeters if true - add parameter to function arguments, otherwise add it to return params * @param declareParameters open parameter declarations if true **/ void parseParameters(go::ParametersAst* node, bool parseParameters=true, bool declareParameters=false); /** * Convenience function that adds argument to function params or output params **/ void addArgumentHelper(go::GoFunctionType::Ptr function, KDevelop::AbstractType::Ptr argument, bool parseArguments); KDevelop::QualifiedIdentifier m_contextIdentifier; }; } #endif \ No newline at end of file diff --git a/duchain/tests/testduchain.cpp b/duchain/tests/testduchain.cpp index 6d0f9e9..0511c02 100644 --- a/duchain/tests/testduchain.cpp +++ b/duchain/tests/testduchain.cpp @@ -1,732 +1,732 @@ /************************************************************************************* * Copyright (C) 2014 by Pavel Petrushkov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "testduchain.h" #include "parser/parsesession.h" #include "builders/declarationbuilder.h" #include "builders/usebuilder.h" #include "types/gointegraltype.h" #include "helper.h" #include #include //#include #include #include #include QTEST_MAIN(TestDuchain); using namespace KDevelop; DUContext* getPackageContext(const ReferencedTopDUContext& topContext); DUContext* getPackageContext(const QString& code); DUContext* getMainContext(DUContext* packageContext); DUContext* getMainContext(const QString& code); void TestDuchain::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); //build builtins file, because it won't be scheduled automatically QString builtinsFile = go::Helper::getBuiltinFile(); QFile f(builtinsFile); f.open(QIODevice::ReadOnly); QByteArray contents = f.readAll(); f.close(); ParseSession session(QString(contents).toUtf8(), 0); session.setCurrentDocument(IndexedString(builtinsFile)); if(session.startParsing()) { DeclarationBuilder builder(&session, false); builder.build(session.currentDocument(), session.ast()); } } void TestDuchain::cleanupTestCase() { TestCore::shutdown(); } void TestDuchain::sanityCheck() { QString code("package main; func main() {}"); ParseSession session(code.toUtf8(), 0); session.setCurrentDocument(IndexedString("file:///temp/1")); QVERIFY(session.startParsing()); DeclarationBuilder builder(&session, false); ReferencedTopDUContext context = builder.build(session.currentDocument(), session.ast()); QVERIFY(context.data()); DUChainReadLocker lock; auto decls = context->localDeclarations(); QCOMPARE(decls.size(), 1); Declaration* packageDeclaration = decls.first(); QVERIFY(packageDeclaration); DUContext* packageContext = packageDeclaration->internalContext(); QVERIFY(packageContext); decls = packageContext->localDeclarations(); QCOMPARE(decls.size(), 2); Declaration* funcDeclaration = decls.at(1); QVERIFY(funcDeclaration); QCOMPARE(funcDeclaration->identifier().toString(), QString("main")); } void TestDuchain::builtinFunctions_data() { QTest::addColumn("expression"); QTest::addColumn("type"); QTest::newRow("make slice") << "make([]int)" << "int[]"; QTest::newRow("make slice 2") << "make([]mytype)" << "main::mytype[]"; QTest::newRow("make chan") << "make(chan float64)" << "chan float64"; QTest::newRow("make chan 2") << "make(chan unknown)" << "chan unknown"; QTest::newRow("make map") << "make(map[uint]rune)" << "map[uint]rune"; QTest::newRow("make map 2") << "make(map[string]mytype)" << "map[string]main::mytype"; QTest::newRow("new") << "new(map[byte]mytype)" << "map[byte]main::mytype*"; QTest::newRow("new 2") << "new(mytype)" << "main::mytype*"; QTest::newRow("new 2") << "new([]unknown)" << "unknown[]*"; QTest::newRow("append") << "append([]int{0}, 1, 2)" << "int[]"; QTest::newRow("append 2") << "append(myslice, \"a\")" << "string[]"; QTest::newRow("cap") << "cap(myvar)" << "int"; QTest::newRow("copy") << "copy(a, b)" << "int"; QTest::newRow("len") << "len(array)" << "int"; QTest::newRow("real") << "real(5i)" << "float64"; QTest::newRow("real 2") << "real(myvar)" << "float64"; QTest::newRow("imag") << "imag(2i)" << "float64"; QTest::newRow("imag 2") << "imag(myvar)" << "float64"; QTest::newRow("imag 3") << "imag(2+3i)" << "float64"; QTest::newRow("complex") << "complex(-1, 0)" << "complex128"; QTest::newRow("recover") << "recover()" << "interface {}"; QTest::newRow("real + complex") << "real(complex(1, 1))" << "float64"; } void TestDuchain::builtinFunctions() { QFETCH(QString, expression); QFETCH(QString, type); QString code(QString("package main; type mytype int; var myvar complex64; var myslice []string; func main() { \ testvar := %1; }").arg(expression)); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; auto decls = context->findDeclarations(QualifiedIdentifier("testvar")); QCOMPARE(decls.size(), 1); Declaration* decl = decls.first(); AbstractType::Ptr result = decl->abstractType(); QCOMPARE(result->toString(), type); } void TestDuchain::test_declareVariables() { QString code("package main; func multitest() (int, bool) { return 1, true; } \n \ func singletest() rune { return 'a'; } \n func main() { test1, test2 := multitest(); \ test3, test4 := singletest(), 3., 100; var test5, test6, test7 = multitest(), singletest(); var _, test8, _ = 0., 5, true;}"); DUContext* context = getMainContext(code); DUChainReadLocker lock; Declaration* decl = context->findDeclarations(QualifiedIdentifier("test1")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); QVERIFY((decl->abstractType()->modifiers() & AbstractType::NoModifiers) == AbstractType::NoModifiers); decl = context->findDeclarations(QualifiedIdentifier("test2")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeBool)); decl = context->findDeclarations(QualifiedIdentifier("test3")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeRune)); decl = context->findDeclarations(QualifiedIdentifier("test4")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat64)); decl = context->findDeclarations(QualifiedIdentifier("test5")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); decl = context->findDeclarations(QualifiedIdentifier("test6")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeRune)); auto declarations = context->findDeclarations(QualifiedIdentifier("test7")); QCOMPARE(declarations.size(), 0); decl = context->findDeclarations(QualifiedIdentifier("test8")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); declarations = context->findDeclarations(QualifiedIdentifier("_")); QCOMPARE(declarations.size(), 0); } void TestDuchain::test_declareVariablesInParametersOfNestedFunction() { QString code("package main; func main() {\n" "a := func(test1 int) { return test1 }\n" "func(test2 int) { return test2 } (5)\n" "pass(func(test3 int) {return test3 })\n}\n"); auto mainContext = getMainContext(code); DUChainReadLocker lock; auto firstFunctionContext = mainContext->childContexts().at(0); auto declarations = firstFunctionContext->findDeclarations(QualifiedIdentifier("test1")); QCOMPARE(declarations.size(), 1); auto declaration = declarations.first(); QCOMPARE(fastCast(declaration->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); auto secondFunctionContext = mainContext->childContexts().at(2); declarations = secondFunctionContext->findDeclarations(QualifiedIdentifier("test2")); QCOMPARE(declarations.size(), 1); declaration = declarations.first(); QCOMPARE(fastCast(declaration->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); auto thirdFunctionContext = mainContext->childContexts().at(4); declarations = thirdFunctionContext->findDeclarations(QualifiedIdentifier("test3")); QCOMPARE(declarations.size(), 1); declaration = declarations.first(); QCOMPARE(fastCast(declaration->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); } void TestDuchain::test_constants() { QString code("package main; const const1, const2 float32 = 1, 2; const ( const3, const4, const5 = 'a', 3, \"abc\"; ); "); DUContext* context = getPackageContext(code); DUChainReadLocker lock; Declaration* decl = context->findDeclarations(QualifiedIdentifier("const1")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat32)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const2")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat32)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const3")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeRune)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const4")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const5")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeString)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); } void TestDuchain::test_constants_omittedType() { QString code("package main; const ( const1, const2 uint = 1, 2; const3, const4; ); const ( const5, const6 = 3, 4.; const7, const8; ); const const9;"); DUContext* context = getPackageContext(code); DUChainReadLocker lock; Declaration* decl = context->findDeclarations(QualifiedIdentifier("const1")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeUint)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const2")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeUint)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const3")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeUint)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const4")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeUint)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const5")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const6")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat64)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const7")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeInt)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); decl = context->findDeclarations(QualifiedIdentifier("const8")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat64)); QVERIFY(decl->abstractType()->modifiers() & AbstractType::ConstModifier); QCOMPARE(context->findDeclarations(QualifiedIdentifier("const9")).size(), 0); } void TestDuchain::test_indexexpressions_data() { QTest::addColumn("vardecl"); QTest::addColumn("indexexpr"); QTest::addColumn("type"); QTest::addColumn("mapaccess"); QTest::newRow("array index") << "var array [5]int" << "array[1]" << "int" << false; QTest::newRow("slice index") << "var slice []string" << "slice[4]" << "string" << false; QTest::newRow("array pointer index") << "var array *[5]int" << "array[1]" << "int" << false; QTest::newRow("string index") << "var str string" << "str[4]" << "byte" << false; QTest::newRow("map index") << "var mymap map[int][]string" << "mymap[2*2]" << "string[]" << true; QTest::newRow("map index 2") << "var mymap map[rune]*mytype" << "mymap[\'o\']" << "main::mytype*" << true; QTest::newRow("slice expression") << "var slice []int" << "slice[1:4]" << "int[]" << false; QTest::newRow("slice expression 2") << "var slice []int" << "slice[:4]" << "int[]" << false; QTest::newRow("slice expression 3") << "var slice []int" << "slice[1:]" << "int[]" << false; QTest::newRow("slice expression 4") << "var slice []int" << "slice[:]" << "int[]" << false; QTest::newRow("array expression") << "var array [5-3]bool" << "array[0:2]" << "bool[]" << false; QTest::newRow("string expression") << "var str string" << "str[0:]" << "string" << false; QTest::newRow("array pointer expression") << "var array *[]bool" << "array[0:2]" << "bool[]" << false; QTest::newRow("full slice expression") << "var slice []int" << "slice[1:3:5]" << "int[]" << false; QTest::newRow("full slice expression 2") << "var slice []mytype" << "slice[:3:5]" << "main::mytype[]" << false; } void TestDuchain::test_indexexpressions() { QFETCH(QString, vardecl); QFETCH(QString, indexexpr); QFETCH(QString, type); QString code(QString("package main; type mytype int; func main() { %1; testvar, ok := %2; }").arg(vardecl).arg(indexexpr)); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; auto decls = context->findDeclarations(QualifiedIdentifier("testvar")); QCOMPARE(decls.size(), 1); Declaration* decl = decls.first(); AbstractType::Ptr result = decl->abstractType(); QCOMPARE(result->toString(), type); QFETCH(bool, mapaccess); decls = context->findDeclarations(QualifiedIdentifier("ok")); if(mapaccess) { QCOMPARE(decls.size(), 1); decl = decls.first(); result = decl->abstractType(); QCOMPARE(result->toString(), QString("bool")); }else QCOMPARE(decls.size(), 0); } void TestDuchain::test_ifcontexts() { QString code("package main; func main() { var test1 int; if test2:=0; true { var test3 int; } else if false { } else { var test4 int; } }"); DUContext* context = getMainContext(code); DUChainReadLocker lock; QCOMPARE(context->findDeclarations(QualifiedIdentifier("test1")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test2")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test3")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test4")).size(), 0); DUContext* childContext = context->findContextAt(CursorInRevision(0, 65)); //if block context QVERIFY(context); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test1")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test2")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test3")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test4")).size(), 0); childContext = context->findContextAt(CursorInRevision(0, 97)); //else-if block context QVERIFY(context); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test1")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test2")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test3")).size(), 0); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test4")).size(), 0); childContext = context->findContextAt(CursorInRevision(0, 116)); //else-else block context QVERIFY(context); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test1")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test2")).size(), 1); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test3")).size(), 0); QCOMPARE(childContext->findDeclarations(QualifiedIdentifier("test4")).size(), 1); } void TestDuchain::test_funccontexts() { QString code("package main; type mytype int; func (i mytype) main(a, b string, c float64) (d, e int) { var f mytype; }"); DUContext* context = getPackageContext(code); QVERIFY(context); DUChainReadLocker lock; auto decls = context->findDeclarations(QualifiedIdentifier("mytype::main")); QCOMPARE(decls.size(), 1); - AbstractFunctionDeclaration* function = dynamic_cast(decls.first()); + auto function = dynamic_cast(decls.first()); QVERIFY(function); - context = function->internalFunctionContext(); - QCOMPARE(context->localDeclarations().size(), 1); + context = function->internalContext(); + QCOMPARE(context->localDeclarations().size(), 2); QCOMPARE(context->findDeclarations(QualifiedIdentifier("i")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("a")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("b")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("c")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("d")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("e")).size(), 1); QCOMPARE(context->findDeclarations(QualifiedIdentifier("f")).size(), 1); } void TestDuchain::test_rangeclause_data() { QTest::addColumn("vardecl"); QTest::addColumn("rangeexpr"); QTest::addColumn("type1"); QTest::addColumn("type2"); QTest::newRow("array range") << "var array [5]int" << "array" << "int" << "int"; QTest::newRow("slice range") << "var slice []string" << "slice" << "int" << "string"; QTest::newRow("array pointer range") << "var longestshittyname *[]string" << "longestshittyname" << "int" << "string"; QTest::newRow("string range") << "var str string" << "str" << "int" << "rune"; QTest::newRow("map range") << "var mymap map[int][]string" << "mymap" << "int" << "string[]"; QTest::newRow("map range 2") << "var mymap map[rune]*mytype" << "mymap" << "rune" << "main::mytype*"; QTest::newRow("chan range") << "var mychan chan *mytype" << "mychan" << "main::mytype*" << "nil"; QTest::newRow("chan range 2") << "var mychan <- chan []int" << "mychan" << "int[]" << "nil"; QTest::newRow("chan range 3") << "var mychan chan <- struct{ b int}" << "mychan" << "struct{ b int}" << "nil"; QTest::newRow("named type") << "var myvar mytype" << "myvar" << "int" << "float64"; QTest::newRow("named type pointer") << "var myvar *mytype" << "myvar" << "int" << "float64"; } void TestDuchain::test_rangeclause() { QFETCH(QString, vardecl); QFETCH(QString, rangeexpr); QFETCH(QString, type1); QFETCH(QString, type2); QString code(QString("package main; type mytype []float64; func main() { %1; for test, test2 := range %2 { } }").arg(vardecl).arg(rangeexpr)); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; context = context->findContextAt(CursorInRevision(0, 90)); QVERIFY(context); auto decls = context->findDeclarations(QualifiedIdentifier("test")); QCOMPARE(decls.size(), 1); Declaration* decl = decls.first(); AbstractType::Ptr result = decl->abstractType(); QCOMPARE(result->toString(), type1); if(type2 != "nil") { decls = context->findDeclarations(QualifiedIdentifier("test2")); QCOMPARE(decls.size(), 1); Declaration* decl = decls.first(); result = decl->abstractType(); QCOMPARE(result->toString(), type2); }else { QCOMPARE(context->findDeclarations(QualifiedIdentifier("test2")).size(), 0); } } void TestDuchain::test_typeswitch() { QString code("package main; type mytype int; func main() { var test1 int \n \ switch test2:=2; test3:=test1.(type) { case rune: test4:=4. \n \ case func(int) string: test5:=\'a\' \n \ case nil: \n \ case byte, float32: \n \ default: \n } }"); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; //DUContext* ctx = context->findContextAt(CursorInRevision(1, 0)); // QVERIFY(context); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test2")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test3")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test4")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("test5")).size(), 0); DUContext* ctx = context->findContextAt(CursorInRevision(1, 61)); //first case QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test2")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test3")).first()->abstractType()->toString(), QString("rune")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test4")).first()->abstractType()->toString(), QString("float64")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test5")).size(), 0); ctx = context->findContextAt(CursorInRevision(2, 30)); //second case QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test2")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test3")).first()->abstractType()->toString(), QString("function (int) string")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test4")).size(), 0); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test5")).first()->abstractType()->toString(), QString("rune")); ctx = context->findContextAt(CursorInRevision(3, 30)); //third case QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test2")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test3")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test4")).size(), 0); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test5")).size(), 0); ctx = context->findContextAt(CursorInRevision(4, 30)); //fourth case QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test2")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test3")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test4")).size(), 0); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test5")).size(), 0); ctx = context->findContextAt(CursorInRevision(5, 30)); //fifth case QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test1")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test2")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test3")).first()->abstractType()->toString(), QString("int")); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test4")).size(), 0); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("test5")).size(), 0); } void TestDuchain::test_funcparams_data() { QTest::addColumn("params"); QTest::addColumn("result"); QTest::newRow("zero params") << "()" << "function () "; QTest::newRow("simple param") << "(a int)" << "function (int) "; QTest::newRow("unnamed param") << "(int)" << "function (int) "; QTest::newRow("unknown param") << "(unknown)" << "function (unknown) "; QTest::newRow("complex param") << "([]int)" << "function (int[]) "; QTest::newRow("complex params") << "(*int, [10]rune)" << "function (int*, rune[]) "; QTest::newRow("two separate params") << "(a int, b float32)" << "function (int, float32) "; QTest::newRow("two combined params") << "(a, b rune)" << "function (rune, rune) "; QTest::newRow("two complex params") << "(a, b chan <- rune)" << "function (chan <- rune, chan <- rune) "; QTest::newRow("three params") << "(_, c, d float32)" << "function (float32, float32, float32) "; QTest::newRow("three separate params") << "(a []uint8, b *rune, c interface{})" << "function (uint8[], rune*, interface{}) "; QTest::newRow("return param") << "(test []mytype) *byte" << "function (main::mytype[]) byte*"; QTest::newRow("multiple return params") << "(chan mytype) (b, e string)" << "function (chan main::mytype) (string, string)"; QTest::newRow("multiple return params") << "(<- chan func (string)) (mytype, mytype)" << "function (<- chan function (string) ) (main::mytype, main::mytype)"; QTest::newRow("three types") << "(a, b, int)" << "function (a, b, int) "; QTest::newRow("three types 2") << "(a, b, []int)" << "function (a, b, int[]) "; QTest::newRow("three types 3") << "(a, _ int, z float32) bool" << "function (int, int, float32) bool"; QTest::newRow("return func type") << "(n int) func(p *T)" << "function (int) function (T*) "; QTest::newRow("variadic param") << "(a ...int) mytype" << "function (int[]) main::mytype"; QTest::newRow("variadic param 2") << "(a, b int, z float64, opt ...interface{}) (success bool)" << "function (int, int, float64, interface{}[]) bool"; } void TestDuchain::test_funcparams() { QFETCH(QString, params); QFETCH(QString, result); QString code(QString("package main; type mytype int; func main%1 { }").arg(params)); DUContext* context = getPackageContext(code); QVERIFY(context); DUChainReadLocker lock; Declaration* decl = context->findDeclarations(QualifiedIdentifier("main::main")).first(); QVERIFY(decl); go::GoFunctionType::Ptr func = decl->abstractType().cast(); QVERIFY(func); QCOMPARE(func->toString(), result); } void TestDuchain::test_literals_data() { QTest::addColumn("expr"); QTest::addColumn("type"); QTest::newRow("basic literal") << "1" << "int"; QTest::newRow("basic literal 2") << ".2" << "float64"; QTest::newRow("basic literal 3") << "1E6i" << "complex128"; QTest::newRow("basic literal 4") << "\'y\'" << "rune"; QTest::newRow("basic literal 5") << "\"str\"" << "string"; QTest::newRow("named type literal") << "mytype{2}" << "main::mytype"; QTest::newRow("named type conversion") << "mytype(2)" << "main::mytype"; //not call QTest::newRow("named type conversion 2") << "(mytype)(2)" << "main::mytype";//not paren expression with resolve QTest::newRow("named type literal 2") << "unknown{2}" << "unknown"; QTest::newRow("struct type literal") << "struct { a int }{2}" << "struct { a int }"; QTest::newRow("struct type literal 2") << "struct { f func(); b rune }{f:main, b:3}" << "struct { f func(); b rune }"; QTest::newRow("struct type conversion") << "struct { mytype }(100)" << "struct { mytype }"; QTest::newRow("struct type access") << "struct { c rune }{}.c" << "rune"; QTest::newRow("array type literal") << "[10]int{1, 2, 3}" << "int[]"; QTest::newRow("slice type conversion") << "[]mytype(anotherslice)" << "main::mytype[]"; QTest::newRow("slice special form") << "[...]float32{-1., 0., 1.}" << "float32[]"; QTest::newRow("map type literal") << "map[int]rune{}" << "map[int]rune"; QTest::newRow("map type conversion") << "(map[int]rune)(f)" << "map[int]rune"; QTest::newRow("map type conversion access") << "(map[int]rune)(f)[0]" << "rune"; QTest::newRow("func type literal") << "func() int { return 0; }" << "function () int"; QTest::newRow("func type literal 2") << "func(a, b mytype) (b bool) { c := 2; return true; }" << "function (main::mytype, main::mytype) bool"; QTest::newRow("func type literal 3") << "func() int { var b int; }" << "function () int"; QTest::newRow("func type conversion") << "(func())(main) " << "function () "; QTest::newRow("func type conversion 2") << "func() (i int)(main) " << "function () int"; QTest::newRow("func type call") << "func(f []int) float64 {} ( []int{1, 2} )" << "float64"; QTest::newRow("pointer type conversion") << "(*mytype)(v) " << "main::mytype*"; QTest::newRow("pointer type conversion 2") << "(*unnamed)(v) " << "unnamed*"; QTest::newRow("interface type conversion") << "interface{}(main) " << "interface{}"; QTest::newRow("interface type conversion 2") << "interface{ Read(a int) string }(Reader) " << "interface{ Read(a int) string }"; QTest::newRow("chan") << "<-chan int(c) " << "int"; QTest::newRow("chan conversion") << "(<-chan int)(c) " << "<- chan int"; QTest::newRow("chan conversion 2") << "chan<- mytype(tt) " << "chan <- main::mytype"; } void TestDuchain::test_literals() { QFETCH(QString, expr); QFETCH(QString, type); QString code(QString("package main; type mytype int; func main() { test := %1 }").arg(expr)); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; auto decls = context->findDeclarations(QualifiedIdentifier("test")); QCOMPARE(decls.size(), 1); QCOMPARE(decls.first()->abstractType()->toString(), type); } void TestDuchain::test_unaryOps_data() { QTest::addColumn("pointer"); QTest::addColumn("expr"); QTest::addColumn("result"); QTest::newRow("pointer") << "new(int)" << "*pointer" << "int"; QTest::newRow("pointer 2") << "new(mytype)" << "*pointer" << "main::mytype"; QTest::newRow("address") << "int(2)" << "&pointer" << "int*"; QTest::newRow("address 2") << "mytype{2}" << "&pointer" << "main::mytype*"; QTest::newRow("chan") << "make(chan rune)" << "<- pointer" << "rune"; QTest::newRow("chan 2") << "make(<- chan []mytype)" << "<-pointer" << "main::mytype[]"; QTest::newRow("not") << "1 != 2" << "! pointer" << "bool"; QTest::newRow("func test") << "func () float32 { return 1; }" << "(pointer)()" << "float32"; QTest::newRow("func test 2") << "func (a float64) float64 { return a; }" << "(pointer)(2.0)" << "float64"; QTest::newRow("func test 3") << "func (a, b float64) float64 { return a; }" << "(pointer)(2.0, 3)" << "float64"; } void TestDuchain::test_unaryOps() { QFETCH(QString, pointer); QFETCH(QString, expr); QFETCH(QString, result); QString code(QString("package main; type mytype int; func main() { pointer := %1; test := %2 }").arg(pointer).arg(expr)); DUContext* context = getMainContext(code); QVERIFY(context); DUChainReadLocker lock; auto decls = context->findDeclarations(QualifiedIdentifier("test")); QCOMPARE(decls.size(), 1); QCOMPARE(decls.first()->abstractType()->toString(), result); } void TestDuchain::test_typeAssertions() { QString code("package main; type mytype int; func main() { var a int; test := a.(float64); test2 := a.(*uint); \ test3 := a.(map[int]string); test4 := a.([]rune); test5 := a.(mytype)}"); DUContext* context = getMainContext(code); DUChainReadLocker lock; Declaration* decl = context->findDeclarations(QualifiedIdentifier("test")).first(); QCOMPARE(fastCast(decl->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeFloat64)); decl = context->findDeclarations(QualifiedIdentifier("test2")).first(); QCOMPARE(decl->abstractType()->toString(), QString("uint*")); decl = context->findDeclarations(QualifiedIdentifier("test3")).first(); QCOMPARE(decl->abstractType()->toString(), QString("map[int]string")); decl = context->findDeclarations(QualifiedIdentifier("test4")).first(); QCOMPARE(decl->abstractType()->toString(), QString("rune[]")); decl = context->findDeclarations(QualifiedIdentifier("test5")).first(); QCOMPARE(decl->abstractType()->toString(), QString("main::mytype")); } void TestDuchain::test_selectCases() { QString code("package main; type mytype int; func main() { mychan := make(chan mytype, 1); select { case test, ok := <- mychan: mychan <- mytype{1}; } }"); DUContext* context = getMainContext(code); DUChainReadLocker lock; QCOMPARE(context->findDeclarations(QualifiedIdentifier("test")).size(), 0); QCOMPARE(context->findDeclarations(QualifiedIdentifier("ok")).size(), 0); context = context->findContextAt(CursorInRevision(0, 120)); Declaration* decl = context->findDeclarations(QualifiedIdentifier("test")).first(); Declaration* ok = context->findDeclarations(QualifiedIdentifier("ok")).first(); QCOMPARE(decl->abstractType()->toString(), QString("main::mytype")); QCOMPARE(fastCast(ok->abstractType().constData())->dataType(), uint(go::GoIntegralType::TypeBool)); } void TestDuchain::test_usesAreAddedInCorrectContext() { QString code("package main\n" "var d int = 6\n" "func main() {\n" " var x int = d\n" "}"); ParseSession session(code.toUtf8(), 0); session.setCurrentDocument(IndexedString("file:///temp/1")); QVERIFY(session.startParsing()); DeclarationBuilder builder(&session, false); ReferencedTopDUContext topContext = builder.build(session.currentDocument(), session.ast()); QVERIFY(topContext.data()); go::UseBuilder builder2(&session); builder2.buildUses(session.ast()); auto packageContext = getPackageContext(topContext); auto context = getMainContext(packageContext); DUChainReadLocker lock; QCOMPARE(context->usesCount(), 1); - QCOMPARE(context->uses()->usedDeclaration(context->topContext()), context->parentContext()->parentContext()->localDeclarations().at(1)); + QCOMPARE(context->uses()->usedDeclaration(context->topContext()), context->parentContext()->localDeclarations().at(1)); } void TestDuchain::test_functionContextIsCreatedWhenDeclaringAsMemberOfStruct_data() { QTest::addColumn("declaration"); QTest::newRow("short variable declaration") << "t := "; QTest::newRow("variable declaration") << "var t = "; QTest::newRow("assignment") << "t = "; } void TestDuchain::test_functionContextIsCreatedWhenDeclaringAsMemberOfStruct() { QFETCH(QString, declaration); QString code = QString("package main\n" "type Test struct {\n" "TestFunc func() ()\n" "}\n" "func main() {\n" "%1 Test{\n" "TestFunc: func() () {\n" "fmt.Println(\"Works!\")\n" "}\n" "}\n" "}").arg(declaration); ParseSession session(code.toUtf8(), 0); session.setCurrentDocument(IndexedString("file:///temp/1")); QVERIFY(session.startParsing()); DeclarationBuilder builder(&session, false); ReferencedTopDUContext topContext = builder.build(session.currentDocument(), session.ast()); QVERIFY(topContext.data()); auto mainContext = getMainContext(getPackageContext(topContext)); DUChainReadLocker lock; QCOMPARE(mainContext->childContexts().size(), 3); } DUContext* getPackageContext(const ReferencedTopDUContext &topContext) { if(!topContext) { return 0; } DUChainReadLocker lock; auto decls = topContext->localDeclarations(); if(decls.size() != 1) return 0; Declaration* packageDeclaration = decls.first(); DUContext* packageContext = packageDeclaration->internalContext(); return packageContext; } DUContext* getPackageContext(const QString& code) { ParseSession session(code.toUtf8(), 0); static int testNumber = 0; session.setCurrentDocument(IndexedString(QString("file:///temp/%1").arg(testNumber++))); if(!session.startParsing()) { return 0; } DeclarationBuilder builder(&session, false); ReferencedTopDUContext context = builder.build(session.currentDocument(), session.ast()); return getPackageContext(context); } DUContext* getMainContext(DUContext *packageContext) { if(!packageContext) { return 0; } DUChainReadLocker lock; auto decls = packageContext->findDeclarations(QualifiedIdentifier("main")); if(decls.size() == 0) return 0; - AbstractFunctionDeclaration* function = dynamic_cast(decls.first()); + auto function = dynamic_cast(decls.first()); if(!function) return 0; - return function->internalFunctionContext(); + return function->internalContext(); } DUContext* getMainContext(const QString& code) { DUContext* package = getPackageContext(code); return getMainContext(package); }