diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp index 8c0a078..4363c24 100644 --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -1,716 +1,718 @@ /************************************************************************************* * 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 "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); 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) { if(type != nullptr) { type->setModifiers(declareConstant ? AbstractType::ConstModifier : AbstractType::NoModifiers); } } if(declareConstant) m_constAutoTypes = types; - if(identifierForNode(id).toString() != "_") - { - declareVariable(id, types.first()); - } + 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)); - } + 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(); + auto identifier = identifierForNode(id); + auto declaration = go::getDeclaration(identifier, currentContext(), false); + auto wasDeclaredInCurrentContext = declaration && declaration.data()->range() != editorFindRange(id, 0); + if(identifier.toString() != "_" && !wasDeclaredInCurrentContext) + { + 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) { 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 { DeclarationBuilderBase::visitPrimaryExpr(node); } } void DeclarationBuilder::visitMethodDeclaration(go::MethodDeclarationAst* node) { go::GoFunctionDeclaration* functionDeclaration = nullptr; QualifiedIdentifier typeIdentifier; auto containerType = go::getMethodRecvTypeIdentifier(node->methodRecv); { DUChainWriteLocker lock; typeIdentifier = identifierForNode(containerType); auto typeDeclaration = go::getTypeDeclaration(typeIdentifier, currentContext()); auto toBeInjectedContext = typeDeclaration ? typeDeclaration->internalContext() : nullptr; if(!typeDeclaration || !typeDeclaration->internalContext()) { openContext(node->methodName, node->methodName, DUContext::ContextType::Class, typeIdentifier); if(typeDeclaration) { typeDeclaration->setInternalContext(currentContext()); } toBeInjectedContext = currentContext(); closeContext(); } injectContext(toBeInjectedContext); auto range = RangeInRevision(currentContext()->range().start, currentContext()->range().start); functionDeclaration = openDeclaration(identifierForNode(node->methodName).last(), range); functionDeclaration->setAutoDeclaration(true); closeDeclaration(); closeInjectedContext(); } QualifiedIdentifier identifier; { DUChainReadLocker lock; identifier = functionDeclaration->qualifiedIdentifier(); } openContext(node, editorFindRange(node, 0), DUContext::ContextType::Class, typeIdentifier); DUChainWriteLocker lock; auto functionDefinition = buildMethod(node->signature, node->body, node->methodName, functionDeclaration, m_session->commentBeforeToken(node->startToken-1), identifier); functionDeclaration->setType(functionDefinition->type()); functionDeclaration->setKind(Declaration::Instance); lock.unlock(); if(node->methodRecv->type && functionDefinition->internalContext()) { //declare method receiver variable('this' or 'self' analog in Go) openContext(functionDefinition->internalContext()); buildTypeName(node->methodRecv->type); if(node->methodRecv->star!= -1) { PointerType* ptype = new PointerType(); ptype->setBaseType(lastType()); injectType(PointerType::Ptr(ptype)); } DUChainWriteLocker n; functionDefinition->setDeclaration(functionDeclaration); auto methodReceiverName = node->methodRecv->nameOrType; Declaration* thisVariable = openDeclaration(identifierForNode(methodReceiverName).last(), editorFindRange(methodReceiverName, 0)); thisVariable->setAbstractType(lastType()); closeDeclaration(); closeContext(); } closeContext(); } 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); ClassDeclaration* 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()); auto structType = fastCast(lastType().data()); if(structType) { structType->setDeclaration(decl); } if(!lastContext()) { openContext(node->name, DUContext::ContextType::Class, identifierForNode(node->name)); closeContext(); } decl->setInternalContext(lastContext()); if(node->type && node->type->complexType && node->type->complexType->structType && node->type->complexType->structType->fieldDeclSequence) { auto sequence = node->type->complexType->structType->fieldDeclSequence; for(int i = 0; icount(); ++i) { auto anonymousField = sequence->at(i)->element->anonFieldStar; if(anonymousField) { visitTypeName(anonymousField->typeName); StructureType::Ptr baseClassType = lastType().cast(); if(baseClassType) { auto baseClassDeclaration = baseClassType->declaration(topContext()); if(baseClassDeclaration && baseClassDeclaration->internalContext()) { decl->internalContext()->addImportedParentContext(baseClassType->declaration(topContext())->internalContext()); } } } } } 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* bodyContext) { setComment(comment); DUChainWriteLocker lock; auto dec = openDefinition(identifierForNode(id), editorFindRange(id, 0)); dec->setType(type); dec->setKind(Declaration::Instance); 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::GoFunctionDefinition* DeclarationBuilder::declareMethod(go::IdentifierAst *id, const go::GoFunctionType::Ptr &type, DUContext *paramContext, DUContext *retparamContext, const QByteArray &comment, DUContext *bodyContext, go::GoFunctionDeclaration *declaration, const QualifiedIdentifier &identifier) { setComment(comment); DUChainWriteLocker lock; auto dec = openDefinition(identifier, editorFindRange(id, 0)); dec->setType(type); dec->setKind(Declaration::Instance); 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(); } DUContext* returnArgsContext = nullptr; DUContext* parametersContext = nullptr; auto type = parseSignature(node, true, ¶metersContext, &returnArgsContext, identifierForNode(name), comment); return declareFunction(name, type, parametersContext, returnArgsContext, comment, bodyContext); } go::GoFunctionDefinition* DeclarationBuilder::buildMethod(go::SignatureAst *node, go::BlockAst *block, go::IdentifierAst *name, go::GoFunctionDeclaration *declaration, const QByteArray &comment, const QualifiedIdentifier &identifier) { DUContext* bodyContext = nullptr; if(block) { visitBlock(block); bodyContext = lastContext(); } DUContext* parametersContext = nullptr; DUContext* returnArgsContext = nullptr; auto type = parseSignature(node, true, ¶metersContext, &returnArgsContext, identifier, comment); return declareMethod(name, type, parametersContext, returnArgsContext, comment, bodyContext, declaration, identifier); } diff --git a/duchain/builders/usebuilder.cpp b/duchain/builders/usebuilder.cpp index e1feb90..fd4a1b5 100644 --- a/duchain/builders/usebuilder.cpp +++ b/duchain/builders/usebuilder.cpp @@ -1,115 +1,142 @@ /************************************************************************************* * 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 "usebuilder.h" #include "expressionvisitor.h" #include "helper.h" #include "duchaindebug.h" using namespace KDevelop; namespace go { UseBuilder::UseBuilder(ParseSession* session) { setParseSession(session); } /*ReferencedTopDUContext UseBuilder::build(const IndexedString& url, AstNode* node, ReferencedTopDUContext updateContext) { qCDebug(DUCHAIN) << "Uses builder run"; return UseBuilderBase::build(url, node, updateContext); }*/ void UseBuilder::visitTypeName(TypeNameAst* node) { QualifiedIdentifier id(identifierForNode(node->name)); if(node->type_resolve->fullName) id.push(identifierForNode(node->type_resolve->fullName)); DUContext* context; { DUChainReadLocker lock; context = currentContext()->findContextIncluding(editorFindRange(node, 0)); } DeclarationPointer decl = getTypeDeclaration(id, context); if(decl) { newUse(node, decl); } } void UseBuilder::visitPrimaryExpr(PrimaryExprAst* node) { DUContext* context; { DUChainReadLocker lock; //context = currentContext()->findContextAt(editorFindRange(node, 0).start); context = currentContext()->findContextIncluding(editorFindRange(node, 0)); } if(!context) return; ExpressionVisitor visitor(m_session, context); visitor.visitPrimaryExpr(node); auto ids = visitor.allIds(); auto decls = visitor.allDeclarations(); if(ids.size() != decls.size()) return; for(int i=0; imethodRecv->type) { typeIdentifierNode = node->methodRecv->type; } else if(node->methodRecv->nameOrType) { typeIdentifierNode = node->methodRecv->nameOrType; } else { typeIdentifierNode = node->methodRecv->ptype; } QualifiedIdentifier id(identifierForNode(typeIdentifierNode)); DUContext* context; { DUChainReadLocker lock; context = currentContext()->findContextIncluding(editorFindRange(typeIdentifierNode, 0)); } DeclarationPointer declaration = getTypeDeclaration(id, context); if(declaration) { newUse(typeIdentifierNode, declaration); } ContextBuilder::visitMethodDeclaration(node); } +void UseBuilder::visitShortVarDecl(go::ShortVarDeclAst *node) +{ + createUseInDeclaration(node->id); + if(node->idList) + { + auto iter = node->idList->idSequence->front(), end = iter; + do + { + createUseInDeclaration(iter->element); + iter = iter->next; + } + while (iter != end); + } + ContextBuilder::visitShortVarDecl(node); +} + +void UseBuilder::createUseInDeclaration(IdentifierAst *idNode) +{ + auto identifier = identifierForNode(idNode); + auto declaration = getDeclaration(identifier, currentContext(), false); + auto wasDeclaredInCurrentContext = declaration && declaration.data()->range() != editorFindRange(idNode, 0); + if(identifier.toString() != "_" && wasDeclaredInCurrentContext) + { + newUse(idNode, declaration); + } +} + } diff --git a/duchain/builders/usebuilder.h b/duchain/builders/usebuilder.h index b7a1aff..7820935 100644 --- a/duchain/builders/usebuilder.h +++ b/duchain/builders/usebuilder.h @@ -1,55 +1,57 @@ /************************************************************************************* * 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 GOLANGUSEBUILDER_H #define GOLANGUSEBUILDER_H #include #include "parser/goast.h" #include "contextbuilder.h" #include namespace go { typedef KDevelop::AbstractUseBuilder UseBuilderBase; class KDEVGODUCHAIN_EXPORT UseBuilder : public UseBuilderBase { public: UseBuilder(ParseSession* session); //virtual KDevelop::ReferencedTopDUContext build(const KDevelop::IndexedString& url, AstNode* node, //KDevelop::ReferencedTopDUContext updateContext = KDevelop::ReferencedTopDUContext()); void visitPrimaryExpr(go::PrimaryExprAst* node) override; void visitTypeName(go::TypeNameAst* node) override; void visitBlock(go::BlockAst* node) override; void visitMethodDeclaration(go::MethodDeclarationAst* node) override; - + void visitShortVarDecl(go::ShortVarDeclAst* node) override; + private: + void createUseInDeclaration(IdentifierAst *idNode); + QStack m_types; - }; } #endif diff --git a/duchain/helper.cpp b/duchain/helper.cpp index 10981f9..e6037bd 100644 --- a/duchain/helper.cpp +++ b/duchain/helper.cpp @@ -1,245 +1,245 @@ /************************************************************************************* * 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 "helper.h" #include #include #include #include #include #include #include #include #include #include #include namespace go { QList Helper::m_CachedSearchPaths; QString Helper::builtinFile; DUChainPointer Helper::builtinContext; QList< QString > Helper::getSearchPaths(QUrl document) { QList paths; if(document != QUrl()) {//try to find path automatically for opened documents QDir currentDir(document.adjusted(QUrl::RemoveFilename).path()); //qCDebug(Go) << currentDir.dirName(); while(currentDir.exists() && currentDir.dirName() != "src") if(!currentDir.cdUp()) break; if(currentDir.exists() && currentDir.dirName() == "src") paths.append(currentDir.absolutePath()); } if(Helper::m_CachedSearchPaths.empty()) { //check $GOPATH env var //since Go 1.8 $GOPATH may be not set because it defaults to $HOME/go //so use go tool which can correctly handle default case. QByteArray result = getGoEnv("GOPATH"); if(!result.isEmpty()) { QDir path(result); if(path.exists() && path.cd("src") && path.exists()) m_CachedSearchPaths.append(path.absolutePath()); } //then check $GOROOT //these days most people don't set GOROOT manually //instead go tool can find correct value for GOROOT on its own //in order for this to work go exec must be in $PATH result = getGoEnv("GOROOT"); if(!result.isEmpty()) { //since Go 1.4 stdlib packages are stored in $GOROOT/src/ //but we also support old layout $GOROOT/src/pkg/ QDir path = QDir(result); if(path.exists() && path.cd("src") && path.exists()) { m_CachedSearchPaths.append(path.absolutePath()); if(path.cd("pkg") && path.exists()) m_CachedSearchPaths.append(path.absolutePath()); } } } paths.append(m_CachedSearchPaths); return paths; } QByteArray Helper::getGoEnv(QString name) { QProcess p; p.start("go env " + name); p.waitForFinished(); QByteArray result = p.readAllStandardOutput(); if(result.endsWith("\n")) result.remove(result.length()-1, 1); return result; } QString Helper::getBuiltinFile() { if(Helper::builtinFile.isNull()) Helper::builtinFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdev-go/builtins.go"); return Helper::builtinFile; } ReferencedTopDUContext Helper::getBuiltinContext() { if(Helper::builtinContext) { return ReferencedTopDUContext(Helper::builtinContext.data()); }else { DUChainReadLocker lock; IndexedString file = IndexedString(Helper::getBuiltinFile()); if(file.isEmpty()) return ReferencedTopDUContext(0); Helper::builtinContext = DUChain::self()->chainForDocument(file); if(!Helper::builtinContext) { //if builtins were not parsed, schedule for parsing with high priority KDevelop::ICore::self()->languageController()->backgroundParser()->addDocument(file, KDevelop::TopDUContext::ForceUpdate, BackgroundParser::BestPriority, 0, ParseJob::FullSequentialProcessing); return ReferencedTopDUContext(0); } return ReferencedTopDUContext(Helper::builtinContext.data()); } } DeclarationPointer getDeclaration(QualifiedIdentifier id, DUContext* context, bool searchInParent) { DUChainReadLocker lock; if(context) { - auto declarations = context->findDeclarations(id, CursorInRevision(INT_MAX, INT_MAX)); + auto declarations = context->findDeclarations(id, CursorInRevision(INT_MAX, INT_MAX), AbstractType::Ptr(), nullptr, searchInParent ? DUContext::NoSearchFlags : DUContext::DontSearchInParent); for(Declaration* decl: declarations) { //import declarations are just decorations and need not be returned if(decl->kind() == Declaration::Import) continue; return DeclarationPointer(decl); } if(searchInParent && context->parentContext()) { return getDeclaration(id, context->parentContext(), searchInParent); } } return DeclarationPointer(); } DeclarationPointer getTypeOrVarDeclaration(QualifiedIdentifier id, DUContext* context, bool searchInParent) { DUChainReadLocker lock; if(context) { auto declarations = context->findDeclarations(id, CursorInRevision(INT_MAX, INT_MAX)); for(Declaration* decl : declarations) { if((decl->kind() == Declaration::Import) || (decl->kind() == Declaration::Namespace) || (decl->kind() == Declaration::NamespaceAlias)) continue; return DeclarationPointer(decl); } if(searchInParent && context->parentContext()) { return getTypeOrVarDeclaration(id, context->parentContext(), searchInParent); } } return DeclarationPointer(); } DeclarationPointer getTypeDeclaration(QualifiedIdentifier id, DUContext* context, bool searchInParent) { DUChainReadLocker lock; if(context) { auto declarations = context->findDeclarations(id, CursorInRevision(INT_MAX, INT_MAX)); for(Declaration* decl : declarations) { //TODO change this to just decl->kind() != Declaration::Type if((decl->kind() == Declaration::Import) || (decl->kind() == Declaration::Namespace) || (decl->kind() == Declaration::NamespaceAlias) || (decl->kind() == Declaration::Instance)) continue; return DeclarationPointer(decl); } if(searchInParent && context->parentContext()) { return getTypeDeclaration(id, context->parentContext(), searchInParent); } } return DeclarationPointer(); } QList< Declaration* > getDeclarations(QualifiedIdentifier id, DUContext* context, bool searchInParent) { DUChainReadLocker lock; if(context) { QList decls; auto declarations = context->findDeclarations(id, CursorInRevision(INT_MAX, INT_MAX)); for(Declaration* decl: declarations) { if(decl->kind() == Declaration::Import) continue; decls << decl; } return decls; } return QList(); } IdentifierAst* getMethodRecvTypeIdentifier(go::MethodRecvAst* methodRecv) { go::IdentifierAst* actualtype = nullptr; if(methodRecv->ptype) actualtype = methodRecv->ptype; else if(methodRecv->type) actualtype = methodRecv->type; else actualtype = methodRecv->nameOrType; return actualtype; } DeclarationPointer getFirstDeclaration(DUContext* context, bool searchInParent) { DUChainReadLocker lock; auto declarations = context->allDeclarations(CursorInRevision::invalid(), context->topContext(), searchInParent); if(declarations.size()>0) return DeclarationPointer(declarations.first().first); return DeclarationPointer(); } DeclarationPointer checkPackageDeclaration(Identifier id, TopDUContext* context) { DUChainReadLocker lock; auto declarations = context->findLocalDeclarations(id); if(declarations.size() > 0) return DeclarationPointer(declarations.first()); return DeclarationPointer(); } } \ No newline at end of file diff --git a/duchain/tests/testduchain.cpp b/duchain/tests/testduchain.cpp index d7ac614..1a8b256 100644 --- a/duchain/tests/testduchain.cpp +++ b/duchain/tests/testduchain.cpp @@ -1,735 +1,795 @@ /************************************************************************************* * 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_redeclareVariables_data() +{ + QTest::addColumn("globalDeclarations"); + QTest::addColumn("localDeclarations"); + QTest::addColumn("assignmentStatement"); + QTest::addColumn("redeclaredIdentifier"); + QTest::addColumn("usedIdentifier"); + + QTest::newRow("no redeclaration") << "" << "" << "a := 3" << "" << ""; + QTest::newRow("redeclaration of top context var") << "var a int" << "" << "a := 3" << "a" << ""; + QTest::newRow("use of current context var") << "" << "a, b := 3, 4" << "a, c := 3, 5" << "" << "a"; + QTest::newRow("use of current context var 2") << "" << "a, b := 3, 4" << "c, b := 3, 5" << "" << "b"; +} + +void TestDuchain::test_redeclareVariables() +{ + QFETCH(QString, globalDeclarations); + QFETCH(QString, localDeclarations); + QFETCH(QString, assignmentStatement); + QFETCH(QString, redeclaredIdentifier); + QFETCH(QString, usedIdentifier); + QString code(QString("package main;\n" + "%1\n" + "func main () {\n" + " %2\n" + " %3\n" + "};") + .arg(globalDeclarations, localDeclarations, assignmentStatement)); + + + 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 mainContext = getMainContext(packageContext); + QVERIFY(packageContext); + DUChainReadLocker lock; + + if(usedIdentifier.isEmpty()) + { + QCOMPARE(mainContext->usesCount(), 0); + } + else + { + QCOMPARE(mainContext->usesCount(), 1); + QCOMPARE(mainContext->uses()->usedDeclaration(mainContext->topContext())->identifier().toString(), usedIdentifier); + } + + if(!redeclaredIdentifier.isEmpty()) + { + auto declarations = go::getDeclarations(QualifiedIdentifier(redeclaredIdentifier), mainContext, false); + QCOMPARE(declarations.size(), 1); + } +} + 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(3); 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(5); 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(), 2); auto firstDeclaration = dynamic_cast(decls.first()); auto secondDeclaration = dynamic_cast(decls.at(1)); auto function = firstDeclaration == nullptr ? secondDeclaration : firstDeclaration; QVERIFY(function); context = function->internalContext(); QVERIFY(context); 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()->localDeclarations().at(1)); } void TestDuchain::test_functionContextIsCreatedWhenDeclaringAsMemberOfStruct_data() { QTest::addColumn("declaration"); QTest::newRow("short variable declaration") << "t := Test "; QTest::newRow("variable declaration") << "var t = Test "; QTest::newRow("assignment") << "t = Test "; QTest::newRow("short variable declaration with complex name") << "t := main.Test "; QTest::newRow("variable declaration with complex name") << "var t = main.Test "; QTest::newRow("assignment with complex name") << "t = main.Test "; } 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{ TestFunc: func() () { fmt.Println(\"Works!\"); } }\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(), 1); auto context = mainContext->childContexts().first(); QCOMPARE(context->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; auto function = dynamic_cast(decls.first()); if(!function) return 0; return function->internalContext(); } DUContext* getMainContext(const QString& code) { DUContext* package = getPackageContext(code); return getMainContext(package); } diff --git a/duchain/tests/testduchain.h b/duchain/tests/testduchain.h index ada64cf..bc20f1c 100644 --- a/duchain/tests/testduchain.h +++ b/duchain/tests/testduchain.h @@ -1,58 +1,60 @@ /************************************************************************************* * 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 GOLANGTESTDUCHAIN_H #define GOLANGTESTDUCHAIN_H #include class TestDuchain : public QObject { Q_OBJECT private slots: void initTestCase(); void sanityCheck(); void cleanupTestCase(); void builtinFunctions_data(); void builtinFunctions(); void test_declareVariables(); + void test_redeclareVariables_data(); + void test_redeclareVariables(); void test_constants(); void test_constants_omittedType(); void test_indexexpressions_data(); void test_indexexpressions(); void test_ifcontexts(); void test_funccontexts(); void test_rangeclause_data(); void test_rangeclause(); void test_typeswitch(); void test_funcparams_data(); void test_funcparams(); void test_literals_data(); void test_literals(); void test_unaryOps_data(); void test_unaryOps(); void test_typeAssertions(); void test_selectCases(); void test_declareVariablesInParametersOfNestedFunction(); void test_usesAreAddedInCorrectContext(); void test_functionContextIsCreatedWhenDeclaringAsMemberOfStruct_data(); void test_functionContextIsCreatedWhenDeclaringAsMemberOfStruct(); }; #endif \ No newline at end of file