mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-25 11:22:55 +00:00
1755 lines
65 KiB
C++
1755 lines
65 KiB
C++
/* This file is part of KDevelop
|
|
Copyright 2006-2007 Hamish Rodda <rodda@kde.org>
|
|
Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "declarationbuilder.h"
|
|
|
|
#include "debugbuilders.h"
|
|
|
|
#include <QByteArray>
|
|
#include <typeinfo>
|
|
#include <iterator>
|
|
|
|
#include "templatedeclaration.h"
|
|
|
|
#include "parser/type_compiler.h"
|
|
#include "parser/commentformatter.h"
|
|
#include "parser/parser.h"
|
|
#include "parser/control.h"
|
|
|
|
#include <language/duchain/forwarddeclaration.h>
|
|
#include <language/duchain/duchain.h>
|
|
#include <language/duchain/duchainlock.h>
|
|
#include <language/duchain/repositories/itemrepository.h>
|
|
#include <language/duchain/types/identifiedtype.h>
|
|
#include <language/duchain/namespacealiasdeclaration.h>
|
|
#include <language/duchain/aliasdeclaration.h>
|
|
#include <util/pushvalue.h>
|
|
|
|
#include "qtfunctiondeclaration.h"
|
|
#include "cppeditorintegrator.h"
|
|
#include "environmentmanager.h"
|
|
#include <language/duchain/classfunctiondeclaration.h>
|
|
#include <language/duchain/functiondeclaration.h>
|
|
#include <language/duchain/functiondefinition.h>
|
|
#include "templateparameterdeclaration.h"
|
|
#include "tokens.h"
|
|
#include "parsesession.h"
|
|
#include "cpptypes.h"
|
|
#include "cppduchain.h"
|
|
#include "cpptypes.h"
|
|
#include <language/duchain/classdeclaration.h>
|
|
|
|
#include "cppdebughelper.h"
|
|
#include "name_visitor.h"
|
|
#include "usebuilder.h"
|
|
|
|
#include "overloadresolutionhelper.h"
|
|
#include "expressionparser.h"
|
|
|
|
using namespace KTextEditor;
|
|
using namespace KDevelop;
|
|
using namespace Cpp;
|
|
|
|
ClassDeclarationData::ClassType classTypeFromTokenKind(int kind)
|
|
{
|
|
switch(kind)
|
|
{
|
|
case Token_struct:
|
|
return ClassDeclarationData::Struct;
|
|
case Token_union:
|
|
return ClassDeclarationData::Union;
|
|
default:
|
|
return ClassDeclarationData::Class;
|
|
}
|
|
}
|
|
|
|
bool DeclarationBuilder::changeWasSignificant() const
|
|
{
|
|
///@todo Also set m_changeWasSignificant if publically visible declarations were removed(needs interaction with abstractcontextbuilder)
|
|
return m_changeWasSignificant;
|
|
}
|
|
|
|
DeclarationBuilder::DeclarationBuilder (ParseSession* session)
|
|
: DeclarationBuilderBase(session), m_changeWasSignificant(false), m_ignoreDeclarators(false), m_functionFlag(NoFunctionFlag), m_collectQtFunctionSignature(false), m_lastDeclaration(0)
|
|
{
|
|
}
|
|
|
|
ReferencedTopDUContext DeclarationBuilder::buildDeclarations(Cpp::EnvironmentFilePointer file, AST *node, IncludeFileList* includes, const ReferencedTopDUContext& updateContext, bool removeOldImports)
|
|
{
|
|
ReferencedTopDUContext top = buildContexts(file, node, includes, updateContext, removeOldImports);
|
|
|
|
Q_ASSERT(m_accessPolicyStack.isEmpty());
|
|
Q_ASSERT(m_functionDefinedStack.isEmpty());
|
|
|
|
return top;
|
|
}
|
|
|
|
void DeclarationBuilder::visitTemplateParameter(TemplateParameterAST * ast) {
|
|
|
|
//Backup and zero the parameter declaration, because we will handle it here directly, and don't want a normal one to be created
|
|
|
|
m_ignoreDeclarators = true;
|
|
DeclarationBuilderBase::visitTemplateParameter(ast);
|
|
m_ignoreDeclarators = false;
|
|
|
|
if( ast->type_parameter || ast->parameter_declaration ) {
|
|
///@todo deal with all the other stuff the AST may contain
|
|
TemplateParameterDeclaration* decl;
|
|
if(ast->type_parameter)
|
|
decl = openDeclaration<TemplateParameterDeclaration>(ast->type_parameter->name, ast, Identifier(), false, !ast->type_parameter->name);
|
|
else
|
|
decl = openDeclaration<TemplateParameterDeclaration>(ast->parameter_declaration->declarator ? ast->parameter_declaration->declarator->id : 0, ast, Identifier(), false, !ast->parameter_declaration->declarator);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
AbstractType::Ptr type = lastType();
|
|
if( type.cast<CppTemplateParameterType>() ) {
|
|
type.cast<CppTemplateParameterType>()->setDeclaration(decl);
|
|
} else {
|
|
kDebug(9007) << "bad last type";
|
|
}
|
|
decl->setAbstractType(type);
|
|
|
|
if( ast->type_parameter && ast->type_parameter->type_id ) {
|
|
//Extract default type-parameter
|
|
QualifiedIdentifier defaultParam;
|
|
|
|
QString str;
|
|
///Only record the strings, because these expressions may depend on template-parameters and thus must be evaluated later
|
|
str += stringFromSessionTokens( editor()->parseSession(), ast->type_parameter->type_id->start_token, ast->type_parameter->type_id->end_token );
|
|
|
|
defaultParam = QualifiedIdentifier(str);
|
|
|
|
decl->setDefaultParameter(defaultParam);
|
|
}
|
|
|
|
if( ast->parameter_declaration ) {
|
|
if( ast->parameter_declaration->expression )
|
|
decl->setDefaultParameter( QualifiedIdentifier( stringFromSessionTokens( editor()->parseSession(), ast->parameter_declaration->expression->start_token, ast->parameter_declaration->expression->end_token ) ) );
|
|
}
|
|
closeDeclaration(ast->parameter_declaration);
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::parseComments(const ListNode<uint> *comments)
|
|
{
|
|
setComment(editor()->parseSession()->m_commentFormatter.formatComment(comments, editor()->parseSession()));
|
|
}
|
|
|
|
void DeclarationBuilder::visitFunctionDeclaration(FunctionDefinitionAST* node)
|
|
{
|
|
FunctionFlag flag = NoFunctionFlag;
|
|
switch(node->defaultDeleted) {
|
|
case FunctionDefinitionAST::NotDefaultOrDeleted:
|
|
flag = NoFunctionFlag;
|
|
break;
|
|
case FunctionDefinitionAST::Default:
|
|
flag = DefaultFunction;
|
|
break;
|
|
case FunctionDefinitionAST::Deleted:
|
|
flag = DeleteFunction;
|
|
break;
|
|
}
|
|
PushValue<FunctionFlag> setDefaultDeleted(m_functionFlag, flag);
|
|
|
|
parseComments(node->comments);
|
|
parseStorageSpecifiers(node->storage_specifiers);
|
|
parseFunctionSpecifiers(node->function_specifiers);
|
|
|
|
//Used to map to the top level function node once the Declaration is built
|
|
if(m_mapAst)
|
|
m_mappedNodes.push(node);
|
|
|
|
m_functionDefinedStack.push(node->start_token);
|
|
|
|
DeclarationBuilderBase::visitFunctionDeclaration(node);
|
|
|
|
m_functionDefinedStack.pop();
|
|
|
|
if(m_mapAst)
|
|
m_mappedNodes.pop();
|
|
|
|
popSpecifiers();
|
|
}
|
|
|
|
//Visitor that clears the ducontext from all AST nodes
|
|
struct ClearDUContextVisitor : public DefaultVisitor {
|
|
|
|
virtual void visit(AST* node) {
|
|
if(node)
|
|
node->ducontext = 0;
|
|
DefaultVisitor::visit(node);
|
|
}
|
|
};
|
|
|
|
void DeclarationBuilder::visitInitDeclarator(InitDeclaratorAST *node)
|
|
{
|
|
///FIXME: properly add support for member-declaration/member-declarator
|
|
PushValue<FunctionFlag> setHasInitialize(m_functionFlag,
|
|
(node->initializer && node->initializer->initializer_clause && node->initializer->initializer_clause->expression)
|
|
? AbstractFunction : NoFunctionFlag);
|
|
|
|
bool parameter_is_initializer = false;
|
|
|
|
if(currentContext()->type() == DUContext::Other) {
|
|
//Cannot declare a a function within a code-context
|
|
parameter_is_initializer = true;
|
|
}else if(!m_inFunctionDefinition && node->declarator && node->declarator->parameter_declaration_clause && node->declarator->id) {
|
|
//Decide whether the parameter-declaration clause is valid
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge);
|
|
|
|
QualifiedIdentifier id;
|
|
identifierForNode(node->declarator->id, id);
|
|
DUContext* previous = currentContext();
|
|
|
|
DUContext* previousLast = lastContext();
|
|
QVector<KDevelop::DUContext::Import> importedParentContexts = m_importedParentContexts;
|
|
|
|
openPrefixContext(node, id, pos); //We create a temporary prefix-context to search from within the right scope
|
|
|
|
DUContext* tempContext = currentContext();
|
|
if (currentContext()->type() != DUContext::Class)
|
|
parameter_is_initializer = !checkParameterDeclarationClause(node->declarator->parameter_declaration_clause);
|
|
closePrefixContext(id);
|
|
|
|
if(tempContext != previous) {
|
|
|
|
//We remove all of its traces from the AST using ClearDUContextVisitor.
|
|
ClearDUContextVisitor clear;
|
|
clear.visit(node);
|
|
|
|
///@todo We don't delete the tempContext, as that may cause crashes. Problem: This leaves garbage in the duchain
|
|
///@todo Solve the redundancy issue once and for all, properly, using a SimpleDeclarationOrFunctionDeclarationAST or similar.
|
|
|
|
//Since we don't delete the temporary context, at least collapse its range.
|
|
tempContext->setRange(RangeInRevision(tempContext->range().start, tempContext->range().end));
|
|
|
|
setLastContext(previousLast);
|
|
m_importedParentContexts = importedParentContexts;
|
|
}
|
|
Q_ASSERT(currentContext() == previous);
|
|
}
|
|
|
|
if (parameter_is_initializer && node->declarator->parameter_declaration_clause && !node->initializer) {
|
|
Control control;
|
|
Parser parser(&control);
|
|
parser.fixupInitializerFromParameter(node, m_editor.parseSession());
|
|
}
|
|
|
|
DeclarationBuilderBase::visitInitDeclarator(node);
|
|
}
|
|
|
|
void DeclarationBuilder::handleRangeBasedFor(ExpressionAST* container, ForRangeDeclarationAst* iterator)
|
|
{
|
|
ContextBuilder::handleRangeBasedFor(container, iterator);
|
|
|
|
if (!container || !iterator) {
|
|
return;
|
|
}
|
|
|
|
if (lastTypeWasAuto() && m_lastDeclaration) {
|
|
// auto support for range-based for
|
|
AbstractType::Ptr listType;
|
|
|
|
{
|
|
DUChainReadLocker lock;
|
|
container->ducontext = currentContext();
|
|
Cpp::ExpressionParser parser;
|
|
Cpp::ExpressionEvaluationResult res = parser.evaluateType( container, editor()->parseSession() );
|
|
listType = res.type.abstractType();
|
|
}
|
|
|
|
if (!listType) {
|
|
// invalid type
|
|
DUChainWriteLocker lock;
|
|
m_lastDeclaration->setAbstractType(AbstractType::Ptr(0));
|
|
return;
|
|
}
|
|
|
|
AbstractType::Ptr realListType = TypeUtils::realType(listType);
|
|
// step 1: find type of elements in list
|
|
AbstractType::Ptr elementType;
|
|
if (ArrayType::Ptr array = realListType.cast<ArrayType>()) {
|
|
// case a: c-array, i.e. foo bar[5]; -> type is foo
|
|
elementType = array->elementType();
|
|
} else {
|
|
// case b: look for begin(listType) function using ADL
|
|
DUChainReadLocker lock;
|
|
OverloadResolutionHelper helper = OverloadResolutionHelper( DUContextPointer(currentContext()), TopDUContextPointer(topContext()) );
|
|
helper.setKnownParameters(OverloadResolver::ParameterList(listType, false));
|
|
// first try begin in current context
|
|
static const QualifiedIdentifier begin("begin");
|
|
helper.setFunctionNameForADL(begin);
|
|
helper.setFunctions( currentContext()->findDeclarations(begin, CursorInRevision::invalid(),
|
|
AbstractType::Ptr(), 0,
|
|
DUContext::OnlyFunctions) );
|
|
ViableFunction func = helper.resolve();
|
|
if (!func.isValid()) {
|
|
// not valid, fall-back to std, it's an associated namespace,
|
|
// see also: spec, 6.5.4
|
|
static const QualifiedIdentifier stdBegin("::std::begin");
|
|
helper.setFunctionNameForADL(stdBegin);
|
|
helper.setFunctions( currentContext()->findDeclarations(stdBegin, CursorInRevision::invalid(),
|
|
AbstractType::Ptr(), 0,
|
|
DUContext::OnlyFunctions) );
|
|
func = helper.resolve();
|
|
}
|
|
if (func.isValid()) {
|
|
AbstractType::Ptr type = func.declaration()->type<FunctionType>()->returnType();
|
|
// see spec: for-range-declaration = *__begin;
|
|
elementType = TypeUtils::decreasePointerDepth(type, topContext(), true);
|
|
}
|
|
}
|
|
|
|
// step 2: set last type, but keep const&
|
|
DUChainWriteLocker lock;
|
|
if (elementType) {
|
|
AbstractType::Ptr type = m_lastDeclaration->abstractType();
|
|
elementType->setModifiers(type->modifiers());
|
|
if (ReferenceType::Ptr ref = type.cast<ReferenceType>()) {
|
|
ref->setBaseType(elementType);
|
|
} else {
|
|
type = elementType;
|
|
}
|
|
m_lastDeclaration->setAbstractType(type);
|
|
} else {
|
|
// invalid type
|
|
m_lastDeclaration->setAbstractType(AbstractType::Ptr(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitSimpleDeclaration(SimpleDeclarationAST* node)
|
|
{
|
|
parseComments(node->comments);
|
|
parseStorageSpecifiers(node->storage_specifiers);
|
|
parseFunctionSpecifiers(node->function_specifiers);
|
|
|
|
if(m_mapAst)
|
|
m_mappedNodes.push(node);
|
|
|
|
m_functionDefinedStack.push(0);
|
|
|
|
DeclarationBuilderBase::visitSimpleDeclaration(node);
|
|
|
|
m_functionDefinedStack.pop();
|
|
|
|
if(m_mapAst)
|
|
m_mappedNodes.pop();
|
|
|
|
popSpecifiers();
|
|
}
|
|
|
|
void DeclarationBuilder::findDeclarationForDefinition(const QualifiedIdentifier &definitionSearchId)
|
|
{
|
|
//TODO: FunctionDeclarations (as distinct from ClassFunctionDeclarations) should probably do what template forward declarations do.
|
|
//That is, the function definition should have no idea they exist and any default arguments should just be copied over
|
|
FunctionDefinition *funDef = dynamic_cast<FunctionDefinition*>(currentDeclaration());
|
|
if (!funDef || (currentContext()->type() != DUContext::Namespace && currentContext()->type() != DUContext::Global))
|
|
return;
|
|
QList<Declaration*> declarations = currentContext()->findDeclarations(definitionSearchId, currentDeclaration()->range().start,
|
|
AbstractType::Ptr(), 0, DUContext::OnlyFunctions);
|
|
if (!declarations.size())
|
|
return;
|
|
//First look for an exact match for the function declaration
|
|
foreach (Declaration* dec, declarations) {
|
|
if (dec->isForwardDeclaration() || dec->isDefinition())
|
|
continue;
|
|
if (dec->abstractType()->indexed() == lastType()->indexed()) {
|
|
//If this declaration is already assigned to a partial match, unassign it
|
|
if (FunctionDefinition* oldDef = FunctionDefinition::definition(dec)) {
|
|
if (oldDef->abstractType()->indexed() != dec->abstractType()->indexed())
|
|
oldDef->setDeclaration(0);
|
|
}
|
|
funDef->setDeclaration(dec);
|
|
return;
|
|
}
|
|
}
|
|
//Allow claiming of unclaimed declarations with the same arg count. This allows the signature assistant to function.
|
|
int functionArgumentCount = 0;
|
|
if(FunctionType::Ptr funDefType = funDef->abstractType().cast<FunctionType>())
|
|
functionArgumentCount = funDefType->arguments().count();
|
|
Declaration *anyUnclaimedFunctionDeclaration = 0;
|
|
foreach (Declaration* dec, declarations) {
|
|
if (!dec->isFunctionDeclaration() || dec->isDefinition())
|
|
continue;
|
|
if(FunctionDefinition::definition(dec) && wasEncountered(FunctionDefinition::definition(dec)))
|
|
continue;
|
|
if (FunctionType::Ptr foundType = dec->abstractType().cast<FunctionType>()) {
|
|
if (foundType->arguments().count() == functionArgumentCount) {
|
|
funDef->setDeclaration(dec);
|
|
return;
|
|
}
|
|
}
|
|
anyUnclaimedFunctionDeclaration = dec;
|
|
}
|
|
//Allow any unclaimed function-definition with a matching name. This allows the signature assistant to function.
|
|
if (anyUnclaimedFunctionDeclaration)
|
|
funDef->setDeclaration(anyUnclaimedFunctionDeclaration);
|
|
}
|
|
|
|
void DeclarationBuilder::visitDeclarator (DeclaratorAST* node)
|
|
{
|
|
if(m_ignoreDeclarators) {
|
|
DeclarationBuilderBase::visitDeclarator(node);
|
|
return;
|
|
}
|
|
|
|
m_collectQtFunctionSignature = !m_accessPolicyStack.isEmpty() && ((m_accessPolicyStack.top() & FunctionIsSlot) || (m_accessPolicyStack.top() & FunctionIsSignal));
|
|
m_qtFunctionSignature = QByteArray();
|
|
|
|
// pretty ugly but seems to work for now...
|
|
bool isFuncPtr = node->parameter_declaration_clause && !node->id && node->sub_declarator && node->sub_declarator->ptr_ops;
|
|
if (node->parameter_declaration_clause && !isFuncPtr) {
|
|
|
|
if(m_collectQtFunctionSignature) //We need to do this just to collect the signature
|
|
checkParameterDeclarationClause(node->parameter_declaration_clause);
|
|
|
|
Declaration* decl = openFunctionDeclaration(node->id, node);
|
|
///Create mappings iff the AST feature is specified
|
|
if(m_mapAst && !m_mappedNodes.empty())
|
|
editor()->parseSession()->mapAstDuChain(m_mappedNodes.top(), KDevelop::DeclarationPointer(decl));
|
|
|
|
if (m_functionFlag == DeleteFunction) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
decl->setExplicitlyDeleted(true);
|
|
}
|
|
|
|
if( !m_functionDefinedStack.isEmpty() ) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
// don't overwrite isDefinition if that was already set (see openFunctionDeclaration)
|
|
decl->setDeclarationIsDefinition( (bool)m_functionDefinedStack.top() );
|
|
}
|
|
|
|
applyFunctionSpecifiers();
|
|
} else if (isFuncPtr) {
|
|
openDeclaration<Declaration>(node->sub_declarator->id, node);
|
|
} else {
|
|
openDefinition(node->id, node, node->id == 0);
|
|
}
|
|
|
|
m_collectQtFunctionSignature = false;
|
|
|
|
applyStorageSpecifiers();
|
|
|
|
// don't visit nested declarators for function pointers
|
|
DeclaratorAST* sub = node->sub_declarator;
|
|
if (isFuncPtr) {
|
|
node->sub_declarator = 0;
|
|
}
|
|
DeclarationBuilderBase::visitDeclarator(node);
|
|
if (isFuncPtr) {
|
|
node->sub_declarator = sub;
|
|
}
|
|
|
|
if (node->parameter_declaration_clause && !isFuncPtr) {
|
|
if (!m_functionDefinedStack.isEmpty() && m_functionDefinedStack.top() && node->id) {
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
//We have to search for the fully qualified identifier, so we always get the correct class
|
|
QualifiedIdentifier id = currentContext()->scopeIdentifier(false);
|
|
QualifiedIdentifier id2;
|
|
identifierForNode(node->id, id2);
|
|
id += id2;
|
|
id.setExplicitlyGlobal(true);
|
|
findDeclarationForDefinition(id);
|
|
}
|
|
}
|
|
|
|
closeDeclaration();
|
|
}
|
|
|
|
ForwardDeclaration * DeclarationBuilder::openForwardDeclaration(NameAST * name, AST * range)
|
|
{
|
|
return openDeclaration<ForwardDeclaration>(name, range);
|
|
}
|
|
|
|
template<class Type>
|
|
Type hasTemplateContext( const QList<Type>& contexts ) {
|
|
foreach( const Type& context, contexts )
|
|
if( context && context->type() == KDevelop::DUContext::Template )
|
|
return context;
|
|
return Type(0);
|
|
}
|
|
|
|
DUContext::Import hasTemplateContext( const QVector<DUContext::Import>& contexts, TopDUContext* top ) {
|
|
foreach( const DUContext::Import& context, contexts )
|
|
if( context.context(top) && context.context(top)->type() == KDevelop::DUContext::Template )
|
|
return context;
|
|
|
|
return DUContext::Import();
|
|
}
|
|
|
|
//Check whether the given context is a template-context by checking whether it imports a template-parameter context
|
|
KDevelop::DUContext* isTemplateContext( KDevelop::DUContext* context ) {
|
|
return hasTemplateContext( context->importedParentContexts(), context->topContext() ).context(context->topContext());
|
|
}
|
|
|
|
bool isSpecialization(TemplateDeclaration *templDecl)
|
|
{
|
|
//A class specialization or partial specialization will have template identifiers in its identifier
|
|
if (ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(templDecl))
|
|
{
|
|
if (classDecl->identifier().templateIdentifiersCount())
|
|
return true;
|
|
}
|
|
//A function specialization may or may not have template identifiers, but at least has "template<>"
|
|
if (dynamic_cast<FunctionDeclaration*>(templDecl))
|
|
{
|
|
DUContext *specFromCtxt = templDecl->templateParameterContext();
|
|
if (specFromCtxt && !specFromCtxt->localDeclarations().size())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DUContext* functionClassContext(Declaration* functionDecl, DUContext *functionCtxt)
|
|
{
|
|
//FIXME: needed as long as functions have their QID merged into their id
|
|
QualifiedIdentifier currentScope = functionCtxt->scopeIdentifier(true);
|
|
QualifiedIdentifier className = currentScope + QualifiedIdentifier(functionDecl->identifier().toString());
|
|
className.pop(); //Pop off the function name at the end, leaving the class QID
|
|
className.setExplicitlyGlobal(true);
|
|
QList<Declaration*> classDeclarations = functionCtxt->findDeclarations(className);
|
|
if (classDeclarations.size())
|
|
return classDeclarations.first()->internalContext();
|
|
|
|
return 0;
|
|
}
|
|
|
|
TemplateDeclaration* DeclarationBuilder::findSpecializedFrom(Declaration* specializedDeclaration)
|
|
{
|
|
Identifier searchForIdentifier;
|
|
if (dynamic_cast<FunctionDeclaration*>(specializedDeclaration))
|
|
searchForIdentifier = QualifiedIdentifier(specializedDeclaration->identifier().toString()).last();
|
|
else
|
|
searchForIdentifier = specializedDeclaration->identifier();
|
|
searchForIdentifier.clearTemplateIdentifiers();
|
|
|
|
DUContext* searchInContext = 0;
|
|
if (dynamic_cast<AbstractFunctionDeclaration*>(specializedDeclaration))
|
|
searchInContext = functionClassContext(specializedDeclaration, currentContext());
|
|
if (!searchInContext)
|
|
searchInContext = currentContext();
|
|
|
|
QList<Declaration*> specFromDecls = searchInContext->findDeclarations(searchForIdentifier);
|
|
foreach(Declaration * possibleSpec, specFromDecls)
|
|
{
|
|
TemplateDeclaration *asTemplateDecl = dynamic_cast<TemplateDeclaration*>(possibleSpec);
|
|
if (!isSpecialization(asTemplateDecl))
|
|
return asTemplateDecl;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template<class T>
|
|
T* DeclarationBuilder::openDeclaration(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRangeAtStart, bool collapseRangeAtEnd)
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
KDevelop::DUContext* templateCtx = hasTemplateContext(m_importedParentContexts + currentContext()->importedParentContexts(), topContext()).context(topContext());
|
|
|
|
///We always need to create a template declaration when we're within a template, so the declaration can be accessed
|
|
///by specialize(..) and its indirect DeclarationId
|
|
if( templateCtx || m_templateDeclarationDepth ) {
|
|
Cpp::SpecialTemplateDeclaration<T>* ret = openDeclarationReal<Cpp::SpecialTemplateDeclaration<T> >( name, rangeNode, customName, collapseRangeAtStart, collapseRangeAtEnd );
|
|
ret->setTemplateParameterContext(templateCtx);
|
|
//FIXME: A FunctionDeclaration w/o a definition should actually be a kind of forward declaration (ie, there can be more than one)
|
|
if( templateCtx && !m_onlyComputeSimplified && isSpecialization(ret) &&
|
|
( dynamic_cast<FunctionDefinition*>(ret) || !dynamic_cast<FunctionDeclaration*>(ret) ) )
|
|
{
|
|
if( TemplateDeclaration *specializedFrom = findSpecializedFrom(ret) )
|
|
{
|
|
TemplateDeclaration *templateDecl = dynamic_cast<TemplateDeclaration*>(ret);
|
|
IndexedInstantiationInformation specializedWith = createSpecializationInformation(name, templateCtx);
|
|
templateDecl->setSpecializedFrom(specializedFrom);
|
|
templateDecl->setSpecializedWith(specializedWith);
|
|
}
|
|
//TODO: else problem
|
|
}
|
|
return ret;
|
|
} else{
|
|
return openDeclarationReal<T>( name, rangeNode, customName, collapseRangeAtStart, collapseRangeAtEnd );
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
T* DeclarationBuilder::openDeclarationReal(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRangeAtStart, bool collapseRangeAtEnd, const RangeInRevision* customRange)
|
|
{
|
|
RangeInRevision newRange;
|
|
if(name) {
|
|
uint start = name->unqualified_name->start_token;
|
|
uint end = name->unqualified_name->end_token;
|
|
|
|
//We must exclude the tilde. Else we may get totally messed up ranges when the name of a destructor is renamed in a macro
|
|
if(name->unqualified_name->tilde) {
|
|
Q_ASSERT(name->unqualified_name->id);
|
|
start = name->unqualified_name->id;
|
|
}
|
|
|
|
newRange = editor()->findRange(start, end);
|
|
}else if(rangeNode) {
|
|
newRange = editor()->findRange(rangeNode);
|
|
}else if(customRange) {
|
|
newRange = *customRange;
|
|
}
|
|
|
|
if(collapseRangeAtStart)
|
|
newRange.end = newRange.start;
|
|
else if(collapseRangeAtEnd)
|
|
newRange.start = newRange.end;
|
|
|
|
Identifier localId = customName;
|
|
|
|
if (name) {
|
|
//If this is an operator thing, build the type first. Since it's part of the name, the type-builder doesn't catch it normally
|
|
if(name->unqualified_name && name->unqualified_name->operator_id)
|
|
visit(name->unqualified_name->operator_id);
|
|
|
|
QualifiedIdentifier id;
|
|
identifierForNode(name, id);
|
|
|
|
if(localId.isEmpty())
|
|
localId = id.last();
|
|
}
|
|
|
|
T* declaration = 0;
|
|
|
|
if (recompiling()) {
|
|
// Seek a matching declaration
|
|
|
|
///@todo maybe order the declarations within ducontext and change here back to walking the indices, because that's easier to debug and faster
|
|
QList<Declaration*> decls = currentContext()->findLocalDeclarations(localId, CursorInRevision::invalid(), 0, AbstractType::Ptr(), DUContext::NoFiltering);
|
|
foreach( Declaration* dec, decls ) {
|
|
|
|
if( wasEncountered(dec) )
|
|
continue;
|
|
|
|
if (dec->range() == newRange &&
|
|
(localId == dec->identifier() || (localId.isUnique() && dec->identifier().isUnique())) &&
|
|
typeid(T) == typeid(*dec)
|
|
)
|
|
{
|
|
// Match
|
|
TemplateDeclaration* templateDecl = dynamic_cast<TemplateDeclaration*>(dec);
|
|
if(templateDecl)
|
|
templateDecl->deleteAllInstantiations(); //Delete all instantiations so we have a fresh start
|
|
|
|
declaration = dynamic_cast<T*>(dec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!declaration) {
|
|
///Second run of the above, this time ignoring the ranges.
|
|
foreach( Declaration* dec, decls ) {
|
|
if( wasEncountered(dec) )
|
|
continue;
|
|
|
|
if ((localId == dec->identifier() || (localId.isUnique() && dec->identifier().isUnique())) &&
|
|
typeid(*dec) == typeid(T)
|
|
)
|
|
{
|
|
// Match
|
|
declaration = dynamic_cast<T*>(dec);
|
|
declaration->setRange(newRange);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG_UPDATE_MATCHING
|
|
if(declaration)
|
|
kDebug() << "found match for" << localId.toString();
|
|
else
|
|
kDebug() << "nothing found for" << localId.toString();
|
|
#endif
|
|
|
|
if (!declaration) {
|
|
if(currentContext()->inSymbolTable())
|
|
m_changeWasSignificant = true; //We are adding a declaration that comes into the symbol table, so mark the change significant
|
|
|
|
declaration = new T(newRange, currentContext());
|
|
declaration->setIdentifier(localId);
|
|
}
|
|
|
|
//Clear some settings
|
|
AbstractFunctionDeclaration* funDecl = dynamic_cast<AbstractFunctionDeclaration*>(declaration);
|
|
if(funDecl)
|
|
funDecl->clearDefaultParameters();
|
|
|
|
declaration->setDeclarationIsDefinition(false); //May be set later
|
|
declaration->setIsTypeAlias(m_inTypedef);
|
|
declaration->setComment(comment());
|
|
clearComment();
|
|
|
|
setEncountered(declaration);
|
|
|
|
m_declarationStack.push(declaration);
|
|
|
|
return declaration;
|
|
}
|
|
|
|
ClassDeclaration* DeclarationBuilder::openClassDefinition(NameAST* name, AST* range, bool collapseRange, ClassDeclarationData::ClassType classType) {
|
|
Identifier id;
|
|
|
|
if(!name) {
|
|
//Unnamed class/struct, use a unique id
|
|
static QAtomicInt& uniqueClassNumber( KDevelop::globalItemRepositoryRegistry().getCustomCounter("Unnamed Class Ids", 1) );
|
|
id = Identifier::unique( uniqueClassNumber.fetchAndAddRelaxed(1) );
|
|
}
|
|
|
|
ClassDeclaration* ret = openDeclaration<ClassDeclaration>(name, range, id, collapseRange);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
ret->setDeclarationIsDefinition(true);
|
|
ret->clearBaseClasses();
|
|
|
|
if(m_accessPolicyStack.isEmpty())
|
|
ret->setAccessPolicy(KDevelop::Declaration::Public);
|
|
else
|
|
ret->setAccessPolicy(currentAccessPolicy());
|
|
|
|
ret->setClassType(classType);
|
|
return ret;
|
|
}
|
|
|
|
Declaration* DeclarationBuilder::openDefinition(NameAST* name, AST* rangeNode, bool collapseRange)
|
|
{
|
|
Declaration* ret = openNormalDeclaration(name, rangeNode, KDevelop::Identifier(), collapseRange);
|
|
|
|
///Create mappings iff the AST feature is specified
|
|
if(m_mapAst && !m_mappedNodes.empty())
|
|
editor()->parseSession()->mapAstDuChain(m_mappedNodes.top(), KDevelop::DeclarationPointer(ret));
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
ret->setDeclarationIsDefinition(true);
|
|
return ret;
|
|
}
|
|
|
|
Declaration* DeclarationBuilder::openNormalDeclaration(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRange) {
|
|
if(currentContext()->type() == DUContext::Class) {
|
|
ClassMemberDeclaration* mem = openDeclaration<ClassMemberDeclaration>(name, rangeNode, customName, collapseRange);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
mem->setAccessPolicy(currentAccessPolicy());
|
|
return mem;
|
|
} else if(currentContext()->type() == DUContext::Template) {
|
|
return openDeclaration<TemplateParameterDeclaration>(name, rangeNode, customName, collapseRange);
|
|
} else {
|
|
return openDeclaration<Declaration>(name, rangeNode, customName, collapseRange);
|
|
}
|
|
}
|
|
|
|
Declaration* DeclarationBuilder::openFunctionDeclaration(NameAST* name, AST* rangeNode) {
|
|
|
|
QualifiedIdentifier id;
|
|
identifierForNode(name, id);
|
|
Identifier localId = id.last(); //This also copies the template arguments
|
|
if(id.count() > 1) {
|
|
//Merge the scope of the declaration, else the declarations could be confused with global functions.
|
|
//This is done before the actual search, so there are no name-clashes while searching the class for a constructor.
|
|
//FIXME: Can we do without this?
|
|
localId.setIdentifier(id.left(-1).toString() + "::" + localId.identifier().str());
|
|
}
|
|
|
|
if(currentContext()->type() == DUContext::Class) {
|
|
DUChainWriteLocker lock;
|
|
ClassFunctionDeclaration* fun = 0;
|
|
if(!m_collectQtFunctionSignature) {
|
|
fun = openDeclaration<ClassFunctionDeclaration>(name, rangeNode, localId);
|
|
}else{
|
|
QtFunctionDeclaration* qtFun = openDeclaration<QtFunctionDeclaration>(name, rangeNode, localId);
|
|
fun = qtFun;
|
|
qtFun->setIsSlot(m_accessPolicyStack.top() & FunctionIsSlot);
|
|
qtFun->setIsSignal(m_accessPolicyStack.top() & FunctionIsSignal);
|
|
QByteArray temp(QMetaObject::normalizedSignature("(" + m_qtFunctionSignature + ")"));
|
|
IndexedString signature(temp.mid(1, temp.length()-2));
|
|
// kDebug() << "normalized signature:" << signature.str() << "from:" << QString::fromUtf8(m_qtFunctionSignature);
|
|
qtFun->setNormalizedSignature(signature);
|
|
}
|
|
Q_ASSERT(fun);
|
|
fun->setAccessPolicy(currentAccessPolicy());
|
|
fun->setIsAbstract(m_functionFlag == AbstractFunction);
|
|
return fun;
|
|
} else if(m_inFunctionDefinition && (currentContext()->type() == DUContext::Namespace || currentContext()->type() == DUContext::Global)) {
|
|
//May be a definition
|
|
FunctionDefinition* ret = openDeclaration<FunctionDefinition>(name, rangeNode, localId);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
ret->setDeclaration(0);
|
|
return ret;
|
|
}else{
|
|
return openDeclaration<FunctionDeclaration>(name, rangeNode, localId);
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::classTypeOpened(AbstractType::Ptr type) {
|
|
//We override this so we can get the class-declaration into a usable state(with filled type) earlier
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
IdentifiedType* idType = dynamic_cast<IdentifiedType*>(type.unsafeData());
|
|
|
|
if( idType && !idType->declarationId().isValid() ) //When the given type has no declaration yet, assume we are declaring it now
|
|
idType->setDeclaration( currentDeclaration() );
|
|
|
|
currentDeclaration()->setType(type);
|
|
}
|
|
|
|
void DeclarationBuilder::closeDeclaration(bool forceInstance)
|
|
{
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
if (lastType()) {
|
|
|
|
AbstractType::Ptr type = typeForCurrentDeclaration();
|
|
IdentifiedType* idType = dynamic_cast<IdentifiedType*>(type.unsafeData());
|
|
DelayedType::Ptr delayed = type.cast<DelayedType>();
|
|
|
|
//When the given type has no declaration yet, assume we are declaring it now.
|
|
//If the type is a delayed type, it is a searched type, and not a declared one, so don't set the declaration then.
|
|
if( !forceInstance && idType && !idType->declarationId().isValid() && !delayed ) {
|
|
idType->setDeclaration( currentDeclaration() );
|
|
//Q_ASSERT(idType->declaration(topContext()) == currentDeclaration());
|
|
}
|
|
|
|
if(currentDeclaration()->kind() != Declaration::NamespaceAlias && currentDeclaration()->kind() != Declaration::Alias) {
|
|
//If the type is not identified, it is an instance-declaration too, because those types have no type-declarations.
|
|
if( (((!idType) || (idType && idType->declarationId() != currentDeclaration()->id())) && !currentDeclaration()->isTypeAlias() && !currentDeclaration()->isForwardDeclaration() ) || dynamic_cast<AbstractFunctionDeclaration*>(currentDeclaration()) || forceInstance )
|
|
currentDeclaration()->setKind(Declaration::Instance);
|
|
else
|
|
currentDeclaration()->setKind(Declaration::Type);
|
|
}
|
|
|
|
currentDeclaration()->setType(type);
|
|
}else{
|
|
currentDeclaration()->setAbstractType(AbstractType::Ptr());
|
|
if(dynamic_cast<ClassDeclaration*>(currentDeclaration()))
|
|
currentDeclaration()->setKind(Declaration::Type);
|
|
}
|
|
if(TemplateDeclaration* templateDecl = dynamic_cast<TemplateDeclaration*>(currentDeclaration())) {
|
|
//The context etc. may have been filled with new items, and the declaration may have been searched unsuccessfully, or wrong instantiations created.
|
|
TemplateDeclaration* deleteInstantiationsOf = 0;
|
|
if(templateDecl->instantiatedFrom())
|
|
deleteInstantiationsOf = templateDecl->instantiatedFrom();
|
|
else if(templateDecl->specializedFrom().data())
|
|
deleteInstantiationsOf = dynamic_cast<TemplateDeclaration*>(templateDecl->specializedFrom().data());
|
|
else
|
|
deleteInstantiationsOf = templateDecl;
|
|
|
|
if(deleteInstantiationsOf) {
|
|
CppDUContext<DUContext>* ctx = dynamic_cast<CppDUContext<DUContext>*>(dynamic_cast<Declaration*>(deleteInstantiationsOf)->internalContext());
|
|
deleteInstantiationsOf->deleteAllInstantiations();
|
|
if(ctx)
|
|
ctx->deleteAllInstantiations();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lastContext())
|
|
{
|
|
if (!m_onlyComputeSimplified && currentDeclaration()->isFunctionDeclaration())
|
|
currentDeclaration<AbstractFunctionDeclaration>()->setInternalFunctionContext(lastContext());
|
|
|
|
if(lastContext()->type() != DUContext::Other || currentDeclaration()->isFunctionDeclaration())
|
|
eventuallyAssignInternalContext();
|
|
}
|
|
|
|
ifDebugCurrentFile( DUChainReadLocker lock(DUChain::lock()); kDebug() << "closing declaration" << currentDeclaration()->toString() << "type" << (currentDeclaration()->abstractType() ? currentDeclaration()->abstractType()->toString() : QString("notype")) << "last:" << (lastType() ? lastType()->toString() : QString("(notype)")); )
|
|
|
|
m_lastDeclaration = m_declarationStack.pop();
|
|
}
|
|
|
|
void DeclarationBuilder::visitTypedef(TypedefAST *def)
|
|
{
|
|
parseComments(def->comments);
|
|
|
|
DeclarationBuilderBase::visitTypedef(def);
|
|
}
|
|
|
|
void DeclarationBuilder::visitEnumSpecifier(EnumSpecifierAST* node)
|
|
{
|
|
Declaration * declaration = 0;
|
|
if (!node->isOpaque) {
|
|
declaration = openDefinition(node->name, node, node->name == 0);
|
|
} else {
|
|
// opaque-enum-declaration
|
|
declaration = openForwardDeclaration(node->name, node);
|
|
}
|
|
|
|
///Create mappings iff the AST feature is specified
|
|
if(m_mapAst)
|
|
editor()->parseSession()->mapAstDuChain(node, KDevelop::DeclarationPointer(declaration));
|
|
|
|
DeclarationBuilderBase::visitEnumSpecifier(node);
|
|
|
|
closeDeclaration();
|
|
}
|
|
|
|
///Replaces a CppTemplateParameterType with a DelayedType
|
|
struct TemplateTypeExchanger : public KDevelop::TypeExchanger {
|
|
|
|
TemplateTypeExchanger(TopDUContext* top) : m_top(top) {
|
|
}
|
|
|
|
virtual AbstractType::Ptr exchange( const AbstractType::Ptr& type )
|
|
{
|
|
if(CppTemplateParameterType::Ptr templateParamType = type.cast<CppTemplateParameterType>()) {
|
|
Declaration* decl = templateParamType->declaration(m_top);
|
|
if(decl) {
|
|
DelayedType::Ptr newType(new DelayedType());
|
|
|
|
IndexedTypeIdentifier id(QualifiedIdentifier(decl->identifier()));
|
|
|
|
if(type->modifiers() & AbstractType::ConstModifier)
|
|
id.setIsConstant(true);
|
|
if(type->modifiers() & AbstractType::VolatileModifier)
|
|
id.setIsVolatile(true);
|
|
|
|
newType->setIdentifier(id);
|
|
newType->setKind(KDevelop::DelayedType::Delayed);
|
|
|
|
return newType.cast<AbstractType>();
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
private:
|
|
TopDUContext* m_top;
|
|
};
|
|
|
|
Cpp::InstantiationInformation DeclarationBuilder::createSpecializationInformation(const Cpp::InstantiationInformation& base, UnqualifiedNameAST* name, KDevelop::DUContext* templateContext) {
|
|
if(name->template_arguments || base.isValid())
|
|
{
|
|
//Append a scope part
|
|
InstantiationInformation newCurrent;
|
|
newCurrent.previousInstantiationInformation = base.indexed();
|
|
if(!name->template_arguments)
|
|
return newCurrent;
|
|
//Process the template arguments if they exist
|
|
const ListNode<TemplateArgumentAST*> * start = name->template_arguments->toFront();
|
|
const ListNode<TemplateArgumentAST*> * current = start;
|
|
do {
|
|
NameASTVisitor visitor(editor()->parseSession(), 0, templateContext, currentContext()->topContext(), templateContext, templateContext->range().end/*, DUContext::NoUndefinedTemplateParams*/);
|
|
ExpressionEvaluationResult res = visitor.processTemplateArgument(current->element);
|
|
AbstractType::Ptr type = res.type.abstractType();
|
|
|
|
TemplateTypeExchanger exchanger(currentContext()->topContext());
|
|
|
|
if(type) {
|
|
type = exchanger.exchange(type);
|
|
type->exchangeTypes(&exchanger);
|
|
}
|
|
|
|
newCurrent.addTemplateParameter(type);
|
|
|
|
current = current->next;
|
|
}while(current != start);
|
|
return newCurrent;
|
|
}else{
|
|
return base;
|
|
}
|
|
}
|
|
|
|
Cpp::IndexedInstantiationInformation DeclarationBuilder::createSpecializationInformation(NameAST* name, DUContext* templateContext)
|
|
{
|
|
InstantiationInformation currentInfo;
|
|
if(name->qualified_names) {
|
|
const ListNode<UnqualifiedNameAST*> * start = name->qualified_names->toFront();
|
|
const ListNode<UnqualifiedNameAST*> * current = start;
|
|
do {
|
|
currentInfo = createSpecializationInformation(currentInfo, current->element, templateContext);
|
|
current = current->next;
|
|
}while(current != start);
|
|
}
|
|
if(name->unqualified_name)
|
|
currentInfo = createSpecializationInformation(currentInfo, name->unqualified_name, templateContext);
|
|
return currentInfo.indexed();
|
|
}
|
|
|
|
void DeclarationBuilder::visitEnumerator(EnumeratorAST* node)
|
|
{
|
|
//Ugly hack: Since we want the identifier only to have the range of the id(not
|
|
//the assigned expression), we change the range of the node temporarily
|
|
uint oldEndToken = node->end_token;
|
|
node->end_token = node->id + 1;
|
|
|
|
Identifier id(editor()->parseSession()->token_stream->symbol(node->id));
|
|
Declaration* decl = openNormalDeclaration(0, node, id);
|
|
|
|
node->end_token = oldEndToken;
|
|
|
|
DeclarationBuilderBase::visitEnumerator(node);
|
|
|
|
EnumeratorType::Ptr enumeratorType = lastType().cast<EnumeratorType>();
|
|
|
|
if(ClassMemberDeclaration* classMember = dynamic_cast<ClassMemberDeclaration*>(currentDeclaration())) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
classMember->setStatic(true);
|
|
}
|
|
|
|
closeDeclaration(true);
|
|
|
|
if(enumeratorType) { ///@todo Move this into closeDeclaration in a logical way
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
enumeratorType->setDeclaration(decl);
|
|
decl->setAbstractType(enumeratorType.cast<AbstractType>());
|
|
}else if(!lastType().cast<DelayedType>()){ //If it's in a template, it may be DelayedType
|
|
AbstractType::Ptr type = lastType();
|
|
kWarning() << "not assigned enumerator type:" << typeid(*type.unsafeData()).name() << type->toString();
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::classContextOpened(ClassSpecifierAST* /*node*/, DUContext* context) {
|
|
|
|
//We need to set this early, so we can do correct search while building
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
currentDeclaration()->setInternalContext(context);
|
|
}
|
|
|
|
void DeclarationBuilder::visitNamespace(NamespaceAST* ast) {
|
|
|
|
RangeInRevision range;
|
|
{
|
|
Identifier id;
|
|
|
|
if(ast->namespace_name)
|
|
{
|
|
id = Identifier(editor()->tokensToStrings(ast->namespace_name, ast->namespace_name+1));
|
|
range = editor()->findRange(ast->namespace_name, ast->namespace_name+1);
|
|
}else
|
|
{
|
|
id = unnamedNamespaceIdentifier().identifier();
|
|
range.start = editor()->findPosition(ast->linkage_body ? ast->linkage_body->start_token : ast->start_token, CppEditorIntegrator::FrontEdge);
|
|
range.end = range.start;
|
|
}
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
Declaration * declaration = openDeclarationReal<Declaration>(0, 0, id, false, false, &range);
|
|
|
|
///Create mappings iff the AST feature is specified
|
|
if(m_mapAst)
|
|
editor()->parseSession()->mapAstDuChain(ast, KDevelop::DeclarationPointer(declaration));
|
|
}
|
|
|
|
DeclarationBuilderBase::visitNamespace(ast);
|
|
|
|
QualifiedIdentifier qid;
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
currentDeclaration()->setKind(KDevelop::Declaration::Namespace);
|
|
qid = currentDeclaration()->qualifiedIdentifier();
|
|
clearLastType();
|
|
closeDeclaration();
|
|
}
|
|
|
|
// support for C++11 inlined namespaces by implicitly "using" the namespace in the parent context
|
|
// i.e. compare to visitUsingDirective()
|
|
if( ast->inlined && compilingContexts() ) {
|
|
RangeInRevision aliasRange(range.end + CursorInRevision(0, 1), 0);
|
|
DUChainWriteLocker lock;
|
|
NamespaceAliasDeclaration* decl = openDeclarationReal<NamespaceAliasDeclaration>(0, 0, globalImportIdentifier(), false, false,
|
|
&aliasRange);
|
|
decl->setImportIdentifier( qid );
|
|
closeDeclaration();
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::copyTemplateDefaultsFromForward(Identifier searchId, const CursorInRevision& pos)
|
|
{
|
|
KDevelop::DUContext* currentTemplateContext = getTemplateContext(currentDeclaration());
|
|
if (!currentTemplateContext)
|
|
return;
|
|
|
|
///We need to clear the template identifiers, or else it may try to instantiate
|
|
///Note that template specializations cannot have default parameters
|
|
searchId.clearTemplateIdentifiers();
|
|
|
|
QList<Declaration*> declarations = Cpp::findDeclarationsSameLevel(currentContext(), searchId, pos);
|
|
foreach( Declaration* decl, declarations ) {
|
|
ForwardDeclaration* forward = dynamic_cast<ForwardDeclaration*>(decl);
|
|
if (!forward || !decl->abstractType())
|
|
continue;
|
|
KDevelop::DUContext* forwardTemplateContext = forward->internalContext();
|
|
if (!forwardTemplateContext || forwardTemplateContext->type() != DUContext::Template)
|
|
continue;
|
|
|
|
const QVector<Declaration*>& forwardList = forwardTemplateContext->localDeclarations();
|
|
const QVector<Declaration*>& realList = currentTemplateContext->localDeclarations();
|
|
|
|
if (forwardList.size() != realList.size())
|
|
continue;
|
|
|
|
QVector<Declaration*>::const_iterator forwardIt = forwardList.begin();
|
|
QVector<Declaration*>::const_iterator realIt = realList.begin();
|
|
|
|
for( ; forwardIt != forwardList.end(); ++forwardIt, ++realIt ) {
|
|
TemplateParameterDeclaration* forwardParamDecl = dynamic_cast<TemplateParameterDeclaration*>(*forwardIt);
|
|
TemplateParameterDeclaration* realParamDecl = dynamic_cast<TemplateParameterDeclaration*>(*realIt);
|
|
if( forwardParamDecl && realParamDecl && !forwardParamDecl->defaultParameter().isEmpty())
|
|
realParamDecl->setDefaultParameter(forwardParamDecl->defaultParameter());
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitClassSpecifier(ClassSpecifierAST *node)
|
|
{
|
|
PushValue<bool> setNotInTypedef(m_inTypedef, false);
|
|
|
|
/**Open helper contexts around the class, so the qualified identifier matches.
|
|
* Example: "class MyClass::RealClass{}"
|
|
* Will create one helper-context named "MyClass" around RealClass
|
|
* */
|
|
|
|
CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge);
|
|
|
|
IndexedInstantiationInformation specializedWith;
|
|
|
|
QualifiedIdentifier id;
|
|
if( node->name ) {
|
|
identifierForNode(node->name, id);
|
|
openPrefixContext(node, id, pos);
|
|
}
|
|
|
|
int kind = editor()->parseSession()->token_stream->kind(node->class_key);
|
|
|
|
ClassDeclaration * declaration = openClassDefinition(node->name, node, node->name == 0, classTypeFromTokenKind(kind));
|
|
|
|
if (kind == Token_struct || kind == Token_union)
|
|
m_accessPolicyStack.push(Declaration::Public);
|
|
else
|
|
m_accessPolicyStack.push(Declaration::Private);
|
|
|
|
DeclarationBuilderBase::visitClassSpecifier(node);
|
|
|
|
eventuallyAssignInternalContext();
|
|
|
|
if( node->name ) {
|
|
///Copy template default-parameters from the forward-declaration to the real declaration if possible
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
copyTemplateDefaultsFromForward(id.last(), pos);
|
|
}
|
|
|
|
closeDeclaration();
|
|
|
|
///Create mappings iff the AST feature is specified
|
|
if(m_mapAst)
|
|
editor()->parseSession()->mapAstDuChain(node, KDevelop::DeclarationPointer(declaration));
|
|
|
|
if(node->name)
|
|
closePrefixContext(id);
|
|
|
|
m_accessPolicyStack.pop();
|
|
}
|
|
|
|
void DeclarationBuilder::visitBaseSpecifier(BaseSpecifierAST *node) {
|
|
DeclarationBuilderBase::visitBaseSpecifier(node);
|
|
|
|
BaseClassInstance instance;
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
ClassDeclaration* currentClass = dynamic_cast<ClassDeclaration*>(currentDeclaration());
|
|
if(currentClass) {
|
|
|
|
instance.virtualInheritance = (bool)node->virt;
|
|
|
|
//TypeUtils::unAliasedType(
|
|
instance.baseClass = TypeUtils::unAliasedType(lastType())->indexed();
|
|
if(currentClass->classType() == ClassDeclarationData::Struct)
|
|
instance.access = KDevelop::Declaration::Public;
|
|
else
|
|
instance.access = KDevelop::Declaration::Private;
|
|
|
|
if( node->access_specifier ) {
|
|
quint16 tk = editor()->parseSession()->token_stream->token(node->access_specifier).kind;
|
|
|
|
switch( tk ) {
|
|
case Token_private:
|
|
instance.access = KDevelop::Declaration::Private;
|
|
break;
|
|
case Token_public:
|
|
instance.access = KDevelop::Declaration::Public;
|
|
break;
|
|
case Token_protected:
|
|
instance.access = KDevelop::Declaration::Protected;
|
|
break;
|
|
}
|
|
}
|
|
|
|
currentClass->addBaseClass(instance);
|
|
}else{
|
|
kWarning() << "base-specifier without class declaration";
|
|
}
|
|
}
|
|
addBaseType(instance, node);
|
|
}
|
|
|
|
QualifiedIdentifier DeclarationBuilder::resolveNamespaceIdentifier(const QualifiedIdentifier& identifier, const CursorInRevision& position)
|
|
{
|
|
QList< Declaration* > decls = currentContext()->findDeclarations(identifier, position);
|
|
|
|
QList<DUContext*> contexts;
|
|
|
|
// qlist does not provide convenient stable iterators
|
|
std::list<Declaration*> worklist(decls.begin(), decls.end());
|
|
for (std::list<Declaration*>::iterator it = worklist.begin(); it != worklist.end(); ++it) {
|
|
Declaration * decl = *it;
|
|
if(decl->kind() == Declaration::Namespace && decl->internalContext()) {
|
|
contexts << decl->internalContext();
|
|
} else if (decl->kind() == Declaration::NamespaceAlias) {
|
|
NamespaceAliasDeclaration *aliasDecl = dynamic_cast<NamespaceAliasDeclaration*>(decl);
|
|
if (aliasDecl) {
|
|
QList<Declaration*> importedDecls = currentContext()->findDeclarations(aliasDecl->importIdentifier(), position);
|
|
std::copy(importedDecls.begin(), importedDecls.end(),
|
|
std::back_inserter(worklist));
|
|
}
|
|
}
|
|
}
|
|
|
|
if( contexts.isEmpty() ) {
|
|
//Failed to resolve namespace
|
|
kDebug(9007) << "Failed to resolve namespace \"" << identifier << "\"";
|
|
QualifiedIdentifier ret = identifier;
|
|
ret.setExplicitlyGlobal(false);
|
|
Q_ASSERT(ret.count());
|
|
return ret;
|
|
} else {
|
|
QualifiedIdentifier ret = contexts.first()->scopeIdentifier(true);
|
|
ret.setExplicitlyGlobal(false);
|
|
if(ret.isEmpty())
|
|
return ret;
|
|
Q_ASSERT(ret.count());
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitUsing(UsingAST * node)
|
|
{
|
|
DeclarationBuilderBase::visitUsing(node);
|
|
|
|
QualifiedIdentifier id;
|
|
identifierForNode(node->name, id);
|
|
|
|
///@todo only use the last name component as range
|
|
AliasDeclaration* decl = openDeclaration<AliasDeclaration>(0, node->name ? (AST*)node->name : (AST*)node, id.last());
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge);
|
|
QList<Declaration*> declarations = currentContext()->findDeclarations(id, pos);
|
|
if(!declarations.isEmpty()) {
|
|
decl->setAliasedDeclaration(declarations[0]);
|
|
}else{
|
|
kDebug(9007) << "Aliased declaration not found:" << id.toString();
|
|
}
|
|
|
|
if(m_accessPolicyStack.isEmpty())
|
|
decl->setAccessPolicy(KDevelop::Declaration::Public);
|
|
else
|
|
decl->setAccessPolicy(currentAccessPolicy());
|
|
}
|
|
|
|
closeDeclaration();
|
|
}
|
|
|
|
void DeclarationBuilder::visitUsingDirective(UsingDirectiveAST * node)
|
|
{
|
|
DeclarationBuilderBase::visitUsingDirective(node);
|
|
|
|
if( compilingContexts() ) {
|
|
RangeInRevision range = editor()->findRange(node->start_token);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
NamespaceAliasDeclaration* decl = openDeclarationReal<NamespaceAliasDeclaration>(0, 0, globalImportIdentifier(), false, false, &range);
|
|
{
|
|
QualifiedIdentifier id;
|
|
identifierForNode(node->name, id);
|
|
decl->setImportIdentifier( resolveNamespaceIdentifier(id, currentDeclaration()->range().start) );
|
|
}
|
|
closeDeclaration();
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitAliasDeclaration(AliasDeclarationAST* node)
|
|
{
|
|
DeclarationBuilderBase::visitAliasDeclaration(node);
|
|
|
|
if( compilingContexts() ) {
|
|
PushValue<bool> setTypeDef(m_inTypedef, true);
|
|
openDeclaration<Declaration>(node->name, node->name);
|
|
closeDeclaration();
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitTypeId(TypeIdAST * typeId)
|
|
{
|
|
//TypeIdAST contains a declarator, but that one does not declare anything
|
|
PushValue<bool> disableDeclarators(m_ignoreDeclarators, true);
|
|
|
|
DeclarationBuilderBase::visitTypeId(typeId);
|
|
}
|
|
|
|
void DeclarationBuilder::visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST* node)
|
|
{
|
|
DeclarationBuilderBase::visitNamespaceAliasDefinition(node);
|
|
|
|
{
|
|
DUChainReadLocker lock(DUChain::lock());
|
|
if( currentContext()->type() != DUContext::Namespace && currentContext()->type() != DUContext::Global ) {
|
|
///@todo report problem
|
|
kDebug(9007) << "Namespace-alias used in non-global scope";
|
|
}
|
|
}
|
|
|
|
if( compilingContexts() ) {
|
|
RangeInRevision range = editor()->findRange(node->namespace_name);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
NamespaceAliasDeclaration* decl = openDeclarationReal<NamespaceAliasDeclaration>(0, 0, Identifier(editor()->parseSession()->token_stream->symbol(node->namespace_name)), false, false, &range);
|
|
{
|
|
QualifiedIdentifier id;
|
|
identifierForNode(node->alias_name, id);
|
|
decl->setImportIdentifier( resolveNamespaceIdentifier(id, currentDeclaration()->range().start) );
|
|
}
|
|
closeDeclaration();
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST* node)
|
|
{
|
|
PushValue<bool> setNotInTypedef(m_inTypedef, false);
|
|
|
|
int kind = editor()->parseSession()->token_stream->kind(node->type);
|
|
|
|
if( kind == Token_typename ) {
|
|
//typename is completely handled by the type-builder
|
|
DeclarationBuilderBase::visitElaboratedTypeSpecifier(node);
|
|
return;
|
|
}
|
|
|
|
bool isFriendDeclaration = !m_storageSpecifiers.isEmpty() && (m_storageSpecifiers.top() & ClassMemberDeclaration::FriendSpecifier);
|
|
|
|
bool openedDeclaration = false;
|
|
|
|
if (node->name) {
|
|
QualifiedIdentifier id;
|
|
identifierForNode(node->name, id);
|
|
|
|
bool forwardDeclarationGlobal = false;
|
|
|
|
if(m_typeSpecifierWithoutInitDeclarators != node->start_token || isFriendDeclaration) {
|
|
/**This is an elaborated type-specifier
|
|
*
|
|
* See iso c++ draft 3.3.4 for details.
|
|
* Said shortly it means:
|
|
* - Search for an existing declaration of the type. If it is found,
|
|
* it will be used, and we don't need to create a declaration.
|
|
* - If it is not found, create a forward-declaration in the global/namespace scope.
|
|
* - @todo While searching for the existing declarations, non-fitting overloaded names should be ignored.
|
|
* */
|
|
|
|
///@todo think how this interacts with re-using duchains. In some cases a forward-declaration should still be created.
|
|
QList<Declaration*> declarations;
|
|
CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge);
|
|
|
|
{
|
|
DUChainReadLocker lock(DUChain::lock());
|
|
|
|
declarations = currentContext()->findDeclarations( id, pos);
|
|
|
|
forwardDeclarationGlobal = true;
|
|
|
|
//If a good declaration has been found, use its type. Else, create a new forward-declaration.
|
|
foreach(Declaration* decl, declarations)
|
|
{
|
|
if((decl->topContext() != currentContext()->topContext() || wasEncountered(decl)) && decl->abstractType())
|
|
{
|
|
setLastType(declarations.first()->abstractType());
|
|
|
|
if( isFriendDeclaration ) {
|
|
lock.unlock();
|
|
createFriendDeclaration(node);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
node->isDeclaration = true;
|
|
|
|
// Create forward declaration
|
|
switch (kind) {
|
|
case Token_class:
|
|
case Token_struct:
|
|
case Token_union:
|
|
case Token_enum:
|
|
|
|
if(forwardDeclarationGlobal) {
|
|
//Open the global context, so it is currentContext() and we can insert the forward-declaration there
|
|
DUContext* globalCtx;
|
|
{
|
|
DUChainReadLocker lock(DUChain::lock());
|
|
globalCtx = currentContext();
|
|
while(globalCtx && globalCtx->type() != DUContext::Global && globalCtx->type() != DUContext::Namespace)
|
|
globalCtx = globalCtx->parentContext();
|
|
Q_ASSERT(globalCtx);
|
|
}
|
|
|
|
//Just temporarily insert the new context
|
|
injectContext( globalCtx );
|
|
}
|
|
|
|
openForwardDeclaration(node->name, node);
|
|
|
|
if(forwardDeclarationGlobal) {
|
|
closeInjectedContext();
|
|
}
|
|
|
|
openedDeclaration = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DeclarationBuilderBase::visitElaboratedTypeSpecifier(node);
|
|
|
|
if (openedDeclaration) {
|
|
/* DUChainWriteLocker lock(DUChain::lock());
|
|
//Resolve forward-declarations that are declared after the real type was already declared
|
|
Q_ASSERT(dynamic_cast<ForwardDeclaration*>(currentDeclaration()));
|
|
IdentifiedType* idType = dynamic_cast<IdentifiedType*>(lastType().data());
|
|
if(idType && idType->declaration())
|
|
static_cast<ForwardDeclaration*>(currentDeclaration())->setResolved(idType->declaration());*/
|
|
closeDeclaration();
|
|
}
|
|
|
|
if( isFriendDeclaration )
|
|
createFriendDeclaration(node);
|
|
}
|
|
|
|
void DeclarationBuilder::createFriendDeclaration(AST* range) {
|
|
static IndexedIdentifier friendIdentifier(Identifier("friend"));
|
|
openDeclaration<Declaration>(0, range, friendIdentifier.identifier(), true);
|
|
closeDeclaration();
|
|
}
|
|
|
|
void DeclarationBuilder::visitAccessSpecifier(AccessSpecifierAST* node)
|
|
{
|
|
bool isSlot = false;
|
|
bool isSignal = false;
|
|
if (node->specs) {
|
|
const ListNode<uint> *it = node->specs->toFront();
|
|
const ListNode<uint> *end = it;
|
|
do {
|
|
int kind = editor()->parseSession()->token_stream->kind(it->element);
|
|
switch (kind) {
|
|
case Token___qt_slots__:
|
|
case Token_k_dcop:
|
|
isSlot = true;
|
|
break;
|
|
case Token_public:
|
|
setAccessPolicy(Declaration::Public);
|
|
break;
|
|
case Token_k_dcop_signals:
|
|
case Token___qt_signals__:
|
|
isSignal = true;
|
|
case Token_protected:
|
|
setAccessPolicy(Declaration::Protected);
|
|
break;
|
|
case Token_private:
|
|
setAccessPolicy(Declaration::Private);
|
|
break;
|
|
}
|
|
|
|
it = it->next;
|
|
} while (it != end);
|
|
}
|
|
|
|
if(isSignal)
|
|
setAccessPolicy((KDevelop::Declaration::AccessPolicy)(currentAccessPolicy() | FunctionIsSignal));
|
|
|
|
if(isSlot)
|
|
setAccessPolicy((KDevelop::Declaration::AccessPolicy)(currentAccessPolicy() | FunctionIsSlot));
|
|
|
|
|
|
DeclarationBuilderBase::visitAccessSpecifier(node);
|
|
}
|
|
|
|
void DeclarationBuilder::parseStorageSpecifiers(const ListNode<uint>* storage_specifiers)
|
|
{
|
|
ClassMemberDeclaration::StorageSpecifiers specs = 0;
|
|
|
|
if (storage_specifiers) {
|
|
const ListNode<uint> *it = storage_specifiers->toFront();
|
|
const ListNode<uint> *end = it;
|
|
do {
|
|
int kind = editor()->parseSession()->token_stream->kind(it->element);
|
|
switch (kind) {
|
|
case Token_friend:
|
|
specs |= ClassMemberDeclaration::FriendSpecifier;
|
|
break;
|
|
case Token_auto:
|
|
specs |= ClassMemberDeclaration::AutoSpecifier;
|
|
break;
|
|
case Token_register:
|
|
specs |= ClassMemberDeclaration::RegisterSpecifier;
|
|
break;
|
|
case Token_static:
|
|
specs |= ClassMemberDeclaration::StaticSpecifier;
|
|
break;
|
|
case Token_extern:
|
|
specs |= ClassMemberDeclaration::ExternSpecifier;
|
|
break;
|
|
case Token_mutable:
|
|
specs |= ClassMemberDeclaration::MutableSpecifier;
|
|
break;
|
|
}
|
|
|
|
it = it->next;
|
|
} while (it != end);
|
|
}
|
|
|
|
m_storageSpecifiers.push(specs);
|
|
}
|
|
|
|
void DeclarationBuilder::parseFunctionSpecifiers(const ListNode<uint>* function_specifiers)
|
|
{
|
|
AbstractFunctionDeclaration::FunctionSpecifiers specs = 0;
|
|
|
|
if (function_specifiers) {
|
|
const ListNode<uint> *it = function_specifiers->toFront();
|
|
const ListNode<uint> *end = it;
|
|
do {
|
|
int kind = editor()->parseSession()->token_stream->kind(it->element);
|
|
switch (kind) {
|
|
case Token_inline:
|
|
specs |= AbstractFunctionDeclaration::InlineSpecifier;
|
|
break;
|
|
case Token_virtual:
|
|
specs |= AbstractFunctionDeclaration::VirtualSpecifier;
|
|
break;
|
|
case Token_explicit:
|
|
specs |= AbstractFunctionDeclaration::ExplicitSpecifier;
|
|
break;
|
|
}
|
|
|
|
it = it->next;
|
|
} while (it != end);
|
|
}
|
|
|
|
m_functionSpecifiers.push(specs);
|
|
}
|
|
|
|
void DeclarationBuilder::visitParameterDeclaration(ParameterDeclarationAST* node)
|
|
{
|
|
if(m_mapAst)
|
|
m_mappedNodes.push(node);
|
|
|
|
// arguments of a function pointer typedef are not typedefs themselves
|
|
PushValue<bool> setNotInTypedef(m_inTypedef, false);
|
|
DeclarationBuilderBase::visitParameterDeclaration(node);
|
|
|
|
AbstractFunctionDeclaration* function = currentDeclaration<AbstractFunctionDeclaration>();
|
|
|
|
if( function ) {
|
|
|
|
if( node->expression ) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
//Fill default-parameters
|
|
QString defaultParam = stringFromSessionTokens( editor()->parseSession(), node->expression->start_token, node->expression->end_token ).trimmed();
|
|
|
|
function->addDefaultParameter(IndexedString(defaultParam));
|
|
}
|
|
if( !node->declarator ) {
|
|
//If there is no declarator, still create a declaration
|
|
openDefinition(0, node, true);
|
|
closeDeclaration();
|
|
}
|
|
}
|
|
|
|
if(m_mapAst)
|
|
m_mappedNodes.pop();
|
|
}
|
|
|
|
void DeclarationBuilder::popSpecifiers()
|
|
{
|
|
m_functionSpecifiers.pop();
|
|
m_storageSpecifiers.pop();
|
|
}
|
|
|
|
void DeclarationBuilder::applyStorageSpecifiers()
|
|
{
|
|
if (!m_storageSpecifiers.isEmpty() && m_storageSpecifiers.top() != 0)
|
|
if (ClassMemberDeclaration* member = dynamic_cast<ClassMemberDeclaration*>(currentDeclaration())) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
member->setStorageSpecifiers(m_storageSpecifiers.top());
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::inheritVirtualSpecifierFromOverridden(ClassFunctionDeclaration* classFun)
|
|
{
|
|
//To be truly correct, this function should:
|
|
// 1. differentiate between various overloads
|
|
// 2. differentiate between cast operators, which all have the same identifier
|
|
// 3. perform a correct search for the destructor (which has a different identifier in each base class)
|
|
//This correctness is currently ignored as a matter of cost(in speed) vs benefit (TODO: #3 at least)
|
|
if(!classFun || classFun->isVirtual() || classFun->isConstructor() || classFun->isDestructor())
|
|
return;
|
|
|
|
QList<Declaration*> overridden;
|
|
Identifier searchId = classFun->identifier();
|
|
//In correct code this should actually only happen in the case of a specialization destructor
|
|
//(Which isn't handled). In any case though, we don't need or want to search in instantiations.
|
|
searchId.clearTemplateIdentifiers();
|
|
|
|
foreach(const DUContext::Import &import, currentContext()->importedParentContexts()) {
|
|
DUContext* iContext = import.context(topContext());
|
|
if(iContext && iContext->type() == DUContext::Class) {
|
|
overridden += iContext->findDeclarations(QualifiedIdentifier(searchId), CursorInRevision::invalid(),
|
|
classFun->abstractType(), classFun->topContext(), DUContext::DontSearchInParent);
|
|
}
|
|
}
|
|
foreach(Declaration* decl, overridden) {
|
|
if(AbstractFunctionDeclaration* fun = dynamic_cast<AbstractFunctionDeclaration*>(decl))
|
|
if(fun->isVirtual())
|
|
classFun->setVirtual(true);
|
|
}
|
|
}
|
|
|
|
void DeclarationBuilder::applyFunctionSpecifiers()
|
|
{
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
AbstractFunctionDeclaration* function = dynamic_cast<AbstractFunctionDeclaration*>(currentDeclaration());
|
|
if(!function)
|
|
return;
|
|
|
|
if (!m_functionSpecifiers.isEmpty() && m_functionSpecifiers.top() != 0) {
|
|
|
|
function->setFunctionSpecifiers(m_functionSpecifiers.top());
|
|
}else{
|
|
function->setFunctionSpecifiers((AbstractFunctionDeclaration::FunctionSpecifiers)0);
|
|
}
|
|
|
|
inheritVirtualSpecifierFromOverridden(dynamic_cast<ClassFunctionDeclaration*>(function));
|
|
}
|
|
|
|
bool DeclarationBuilder::checkParameterDeclarationClause(ParameterDeclarationClauseAST* clause)
|
|
{
|
|
{
|
|
DUChainReadLocker lock(DUChain::lock());
|
|
if(currentContext()->type() == DUContext::Other) //Cannot declare a function in a code-context
|
|
return false; ///@todo create warning/error
|
|
}
|
|
if(!clause || !clause->parameter_declarations)
|
|
return true;
|
|
AbstractType::Ptr oldLastType = lastType();
|
|
bool oldLastTypeWasAuto = lastTypeWasAuto();
|
|
bool oldLastTypeWasInstance = lastTypeWasInstance();
|
|
|
|
// type builder must do all its work here
|
|
bool oldComputeSimplified = m_onlyComputeSimplified;
|
|
setComputeSimplified(false);
|
|
|
|
const ListNode<ParameterDeclarationAST*> *start = clause->parameter_declarations->toFront();
|
|
|
|
const ListNode<ParameterDeclarationAST*> *it = start;
|
|
|
|
bool ret = false;
|
|
|
|
do {
|
|
ParameterDeclarationAST* ast = it->element;
|
|
if(ast) {
|
|
if(m_collectQtFunctionSignature) {
|
|
uint endToken = ast->end_token;
|
|
|
|
if(ast->type_specifier)
|
|
endToken = ast->type_specifier->end_token;
|
|
if(ast->declarator) {
|
|
if(ast->declarator->id)
|
|
endToken = ast->declarator->id->start_token;
|
|
else
|
|
endToken = ast->declarator->end_token;
|
|
}
|
|
|
|
if(!m_qtFunctionSignature.isEmpty())
|
|
m_qtFunctionSignature += ", ";
|
|
|
|
m_qtFunctionSignature += editor()->tokensToByteArray(ast->start_token, endToken);
|
|
ret = true;
|
|
}else{
|
|
if(ast->expression || ast->declarator) {
|
|
ret = true; //If one parameter has a default argument or a parameter name, it is surely a parameter
|
|
break;
|
|
}
|
|
|
|
visit(ast->type_specifier);
|
|
if( lastType() ) {
|
|
//Break on the first valid thing found
|
|
if( lastTypeWasInstance() ) {
|
|
ret = false;
|
|
break;
|
|
}else if(lastType().cast<DelayedType>() && lastType().cast<DelayedType>()->kind() == DelayedType::Unresolved) {
|
|
//When the searched item was not found, expect it to be a non-type
|
|
//except for varargs
|
|
ret = TypeUtils::isVarArgs(lastType());
|
|
break;
|
|
}else{
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
it = it->next;
|
|
} while (it != start);
|
|
|
|
setLastType(oldLastType);
|
|
setLastTypeWasAuto(oldLastTypeWasAuto);
|
|
setLastTypeWasInstance(oldLastTypeWasInstance);
|
|
setComputeSimplified(oldComputeSimplified);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/// Set the internal context of a declaration; for example, a class declaration's internal context
|
|
/// is the context inside the brackets: class ClassName { ... }
|
|
void DeclarationBuilder::eventuallyAssignInternalContext()
|
|
{
|
|
if (TypeBuilder::lastContext()) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
if( dynamic_cast<ClassFunctionDeclaration*>(currentDeclaration()) )
|
|
Q_ASSERT( !static_cast<ClassFunctionDeclaration*>(currentDeclaration())->isConstructor() || currentDeclaration()->context()->type() == DUContext::Class );
|
|
|
|
if(TypeBuilder::lastContext() &&
|
|
(TypeBuilder::lastContext()->type() == DUContext::Class ||
|
|
TypeBuilder::lastContext()->type() == DUContext::Other ||
|
|
TypeBuilder::lastContext()->type() == DUContext::Function ||
|
|
TypeBuilder::lastContext()->type() == DUContext::Template ||
|
|
TypeBuilder::lastContext()->type() == DUContext::Enum ||
|
|
(TypeBuilder::lastContext()->type() == DUContext::Namespace && currentDeclaration()->kind() == Declaration::Namespace)
|
|
) )
|
|
{
|
|
if( !TypeBuilder::lastContext()->owner() || !TypeBuilder::wasEncountered(TypeBuilder::lastContext()->owner()) ) { //if the context is already internalContext of another declaration, leave it alone
|
|
currentDeclaration()->setInternalContext(TypeBuilder::lastContext());
|
|
|
|
TypeBuilder::clearLastContext();
|
|
}
|
|
}
|
|
}
|
|
}
|