kde-extraapps/kdevelop/languages/cpp/cppduchain/contextbuilder.cpp
2015-07-26 14:23:17 +03:00

1176 lines
36 KiB
C++

/* This file is part of KDevelop
Copyright 2006 Roberto Raggi <roberto@kdevelop.org>
Copyright 2006-2008 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 "contextbuilder.h"
#include <ktexteditor/document.h>
#include <language/duchain/duchain.h>
#include <language/duchain/topducontext.h>
#include <language/duchain/declaration.h>
#include <language/duchain/use.h>
#include <util/pushvalue.h>
#include "parsesession.h"
#include "name_compiler.h"
#include <language/duchain/dumpchain.h>
#include "environmentmanager.h"
#include "expressionvisitor.h"
#include "cppdebughelper.h"
#include "debugbuilders.h"
#include "rpp/chartools.h"
#include "tokens.h"
using namespace KTextEditor;
using namespace KDevelop;
using namespace Cpp;
bool containsContext( const QList<LineContextPair>& lineContexts, TopDUContext* context ) {
foreach( const LineContextPair& ctx, lineContexts )
if( ctx.context.data() == context )
return true;
return false;
}
bool importsContext( const QList<LineContextPair>& lineContexts, TopDUContext* context ) {
foreach( const LineContextPair& ctx, lineContexts )
if( ctx.context && ctx.context->imports(context, KDevelop::CursorInRevision()) )
return true;
return false;
}
void removeContext( QList<LineContextPair>& lineContexts, TopDUContext* context ) {
for( QList<LineContextPair>::iterator it = lineContexts.begin(); it != lineContexts.end(); ++it )
if( (*it).context.data() == context ) {
lineContexts.erase(it);
return;
}
}
///Retrieves the first and last item from a list
template <class _Tp>
void getFirstLast(AST** first, AST** last, const ListNode<_Tp> *nodes)
{
*first = 0;
*last = 0;
if (!nodes)
return;
const ListNode<_Tp>
*it = nodes->toFront(),
*end = it;
do
{
if( !*first )
*first = it->element;
*last = it->element;
it = it->next;
}
while (it != end);
}
ContextBuilder::ContextBuilder (ParseSession* session)
: m_inFunctionDefinition(false)
, m_editor(session)
, m_nameCompiler(session)
, m_templateDeclarationDepth(0)
, m_typeSpecifierWithoutInitDeclarators((uint)-1)
, m_onlyComputeVisible(false)
, m_onlyComputeSimplified(false)
, m_computeEmpty(false)
, m_currentInitializer(0)
, m_currentCondition(0)
, m_mapAst(false)
{
}
void ContextBuilder::setOnlyComputeVisible(bool onlyVisible) {
m_onlyComputeVisible = onlyVisible;
}
void ContextBuilder::setComputeSimplified(bool simplified)
{
m_onlyComputeSimplified = simplified;
}
void ContextBuilder::setComputeEmpty(bool empty)
{
m_computeEmpty = empty;
}
void ContextBuilder::createUserProblem(AST* node, QString text) {
DUChainWriteLocker lock(DUChain::lock());
KDevelop::ProblemPointer problem(new KDevelop::Problem);
problem->setDescription(text);
problem->setSource(KDevelop::ProblemData::DUChainBuilder);
problem->setFinalLocation(DocumentRange(IndexedString(currentContext()->url().str()), editor()->findRange(node).castToSimpleRange()));
currentContext()->topContext()->addProblem(problem);
}
void ContextBuilder::addBaseType( KDevelop::BaseClassInstance base, BaseSpecifierAST *node ) {
DUChainWriteLocker lock(DUChain::lock());
addImportedContexts(); //Make sure the template-contexts are imported first, before any parent-class contexts.
Q_ASSERT(currentContext()->type() == DUContext::Class);
AbstractType::Ptr baseClass = base.baseClass.abstractType();
IdentifiedType* idType = dynamic_cast<IdentifiedType*>(baseClass.unsafeData());
Declaration* idDecl = 0;
if( idType && (idDecl = idType->declaration(currentContext()->topContext())) ) {
DUContext* ctx = idDecl->logicalInternalContext(currentContext()->topContext());
if(ctx) {
currentContext()->addImportedParentContext( ctx );
}else{
currentContext()->addIndirectImport( DUContext::Import(idType->declarationId()) );
QString text = i18n("Could not resolve base class, adding it indirectly: %1", (base.baseClass ? base.baseClass.abstractType()->toString() : QString()));
lock.unlock();
createUserProblem(node, text);
}
} else if( !baseClass.cast<DelayedType>() ) {
QString text = i18n("Invalid base class: %1", (base.baseClass ? base.baseClass.abstractType()->toString() : QString()));
lock.unlock();
createUserProblem(node, text);
}
}
void addImportedParentContextSafely(DUContext* context, DUContext* import) {
if(import->imports(context)) {
kDebug() << "prevented endless recursive import";
}else{
context->addImportedParentContext(import);
}
}
QualifiedIdentifier ContextBuilder::identifierForNode(NameAST* id) {
QualifiedIdentifier ret;
identifierForNode(id, ret);
return ret;
}
void ContextBuilder::setMapAst(bool mapAst)
{
m_mapAst = mapAst;
}
void ContextBuilder::identifierForNode(NameAST* id, QualifiedIdentifier& target)
{
return identifierForNode(id, 0, target);
}
void ContextBuilder::startVisiting( AST* node )
{
visit( node );
}
void ContextBuilder::setContextOnNode( AST* node, DUContext* ctx )
{
node->ducontext = ctx;
}
DUContext* ContextBuilder::contextFromNode( AST* node )
{
return node->ducontext;
}
RangeInRevision ContextBuilder::editorFindRange( AST* fromRange, AST* toRange )
{
return editor()->findRange(fromRange, toRange);
}
RangeInRevision ContextBuilder::editorFindRangeForContext( AST* fromRange, AST* toRange )
{
return editor()->findRangeForContext(fromRange->start_token, toRange->end_token);
}
ContextBuilder::~ContextBuilder ()
{
}
QPair<DUContext*, QualifiedIdentifier> ContextBuilder::findPrefixContext(const QualifiedIdentifier& id, KDevelop::CursorInRevision pos) {
if(id.count() < 2)
return qMakePair((DUContext*)0, QualifiedIdentifier());
QualifiedIdentifier prefixId(id);
prefixId.pop();
DUContext* import = 0;
{
DUChainReadLocker lock(DUChain::lock());
QualifiedIdentifier currentScopeId = currentContext()->scopeIdentifier(true);
QList<Declaration*> decls = currentContext()->findDeclarations(prefixId, pos);
if(!decls.isEmpty()) {
DUContext* classContext = decls.first()->logicalInternalContext(0);
if(classContext && classContext->type() == DUContext::Class) {
import = classContext;
//Change the prefix-id so it respects namespace-imports
prefixId = classContext->scopeIdentifier(true);
if(prefixId.count() >= currentScopeId.count() && prefixId.left(currentScopeId.count()) == currentScopeId)
prefixId = prefixId.mid(currentScopeId.count());
else
kDebug() << "resolved bad prefix context. Should start with" << currentScopeId.toString() << "but is" << prefixId.toString();
}
}
}
return qMakePair(import, prefixId);
}
void ContextBuilder::openPrefixContext(AST* ast, const QualifiedIdentifier& id, const CursorInRevision& pos) {
if(id.count() < 2)
return;
//When creating a prefix-context that is there to embed a class within another class, import the enclosing class into that context.
//That way items from the base class can be found.
QPair<DUContext*, QualifiedIdentifier> prefix = findPrefixContext(id, pos);
openContext(ast, DUContext::Helper, prefix.second);
DUContext* import = prefix.first;
if(import) {
DUChainWriteLocker lock(DUChain::lock());
addImportedParentContextSafely(currentContext(), import);
}
}
void ContextBuilder::closePrefixContext(const QualifiedIdentifier& id) {
if(id.count() < 2)
return;
closeContext();
}
void ContextBuilder::visitTemplateDeclaration(TemplateDeclarationAST * ast) {
++m_templateDeclarationDepth;
if(!m_onlyComputeSimplified)
{
AST* first, *last;
getFirstLast(&first, &last, ast->template_parameters);
DUContext* ctx = 0;
if( first && last )
ctx = openContext(first, last, DUContext::Template); //Open anonymous context for the template-parameters
else
ctx = openContextEmpty(ast, DUContext::Template); //Open an empty context, because there are no template-parameters
visitNodes(this,ast->template_parameters);
closeContext();
queueImportedContext(ctx); //Import the context into the following function-argument context(so the template-parameters can be found from there)
}
DefaultVisitor::visit(ast->declaration);
--m_templateDeclarationDepth;
}
KDevelop::TopDUContext* ContextBuilder::buildProxyContextFromContent(Cpp::EnvironmentFilePointer file, const TopDUContextPointer& content, const TopDUContextPointer& updateContext)
{
Cpp::EnvironmentFile* filePtr = const_cast<Cpp::EnvironmentFile*>(file.data() );
filePtr->setIsProxyContext(true);
TopDUContext* topLevelContext = 0;
{
DUChainWriteLocker lock(DUChain::lock());
topLevelContext = updateContext.data();
CppDUContext<TopDUContext>* cppContext = 0;
if (topLevelContext) {
kDebug(9007) << "ContextBuilder::buildProxyContextFromContent: recompiling";
Q_ASSERT(dynamic_cast<CppDUContext<TopDUContext>* >(topLevelContext));
cppContext = static_cast<CppDUContext<TopDUContext>* >(topLevelContext);
DUChain::self()->updateContextEnvironment( topLevelContext, filePtr );
} else {
kDebug(9007) << "ContextBuilder::buildProxyContextFromContent: compiling";
topLevelContext = new CppDUContext<TopDUContext>(file->url(), RangeInRevision(), filePtr);
topLevelContext->setType(DUContext::Global);
Q_ASSERT(dynamic_cast<CppDUContext<TopDUContext>* >(topLevelContext));
cppContext = static_cast<CppDUContext<TopDUContext>* >(topLevelContext);
DUChain::self()->addDocumentChain(topLevelContext);
topLevelContext->updateImportsCache(); //Mark that we will use a cached import-structure
}
Q_ASSERT(content);
cppContext->clearImportedParentContexts();
cppContext->addImportedParentContext(content.data());
cppContext->updateImportsCache(); //Mark that we will use a cached import-structure
Q_ASSERT(topLevelContext->importedParentContexts().count());
}
return topLevelContext;
}
ReferencedTopDUContext ContextBuilder::buildContexts(Cpp::EnvironmentFilePointer file, AST *node, IncludeFileList* includes, const ReferencedTopDUContext& updateContext, bool removeOldImports)
{
Q_ASSERT(file);
setCompilingContexts(true);
{
DUChainWriteLocker lock(DUChain::lock());
if(updateContext && (updateContext->parsingEnvironmentFile() && updateContext->parsingEnvironmentFile()->isProxyContext())) {
kDebug(9007) << "updating a context " << file->url().str() << " from a proxy-context to a content-context";
updateContext->parsingEnvironmentFile()->setIsProxyContext(false);
}
}
ReferencedTopDUContext topLevelContext;
{
DUChainWriteLocker lock(DUChain::lock());
topLevelContext = updateContext;
RangeInRevision topRange = RangeInRevision(CursorInRevision(0,0), CursorInRevision(INT_MAX, INT_MAX));
if (topLevelContext) {
kDebug(9007) << "ContextBuilder::buildContexts: recompiling";
setRecompiling(true);
DUChain::self()->updateContextEnvironment( topLevelContext, const_cast<Cpp::EnvironmentFile*>(file.data() ) );
topLevelContext->setRange(topRange);
} else {
kDebug(9007) << "ContextBuilder::buildContexts: compiling";
setRecompiling(false);
Q_ASSERT(compilingContexts());
topLevelContext = new CppDUContext<TopDUContext>(file->url(), topRange, const_cast<Cpp::EnvironmentFile*>(file.data()));
topLevelContext->setType(DUContext::Global);
topLevelContext->setFlags((TopDUContext::Flags)(TopDUContext::UpdatingContext | topLevelContext->flags()));
DUChain::self()->addDocumentChain(topLevelContext);
topLevelContext->updateImportsCache(); //Mark that we will use a cached import-structure
}
setEncountered(topLevelContext);
if (includes) {
if(removeOldImports) {
foreach (const DUContext::Import &parent, topLevelContext->importedParentContexts())
if (!containsContext(*includes, dynamic_cast<TopDUContext*>(parent.context(0))))
topLevelContext->removeImportedParentContext(parent.context(0));
}
QList< QPair<TopDUContext*, CursorInRevision> > realIncluded;
QList< QPair<TopDUContext*, CursorInRevision> > realTemporaryIncluded;
foreach (const LineContextPair &included, *includes)
if(!included.temporary)
realIncluded << qMakePair(included.context.data(), CursorInRevision(included.sourceLine, 0));
else
realTemporaryIncluded << qMakePair(included.context.data(), CursorInRevision(included.sourceLine, 0));
topLevelContext->addImportedParentContexts(realIncluded);
topLevelContext->addImportedParentContexts(realTemporaryIncluded, true);
topLevelContext->updateImportsCache();
}
}
{
DUChainReadLocker lock(DUChain::lock());
//If we're debugging the current file, dump its preprocessed contents and the AST
ifDebugFile( IndexedString(file->identity().url().str()), { kDebug() << stringFromContents(editor()->parseSession()->contentsVector()); Cpp::DumpChain dump; dump.dump(node, editor()->parseSession()); } );
}
if(m_computeEmpty)
{
//Empty the top-context, in case we're updating
DUChainWriteLocker lock(DUChain::lock());
topLevelContext->cleanIfNotEncountered(QSet<DUChainBase*>());
}else{
Q_ASSERT(node);
node->ducontext = topLevelContext;
supportBuild(node);
}
{
DUChainReadLocker lock(DUChain::lock());
kDebug(9007) << "built top-level context with" << topLevelContext->localDeclarations().size() << "declarations and" << topLevelContext->importedParentContexts().size() << "included files";
//If we're debugging the current file, dump the du-chain and the smart ranges
ifDebugFile( IndexedString(file->identity().url().str()), { KDevelop::dumpDUContext(topLevelContext); } );
/* if( m_recompiling ) {
dumpDUContext(topLevelContext);
kDebug(9007) << dump.dotGraph(topLevelContext);
}*/
}
setCompilingContexts(false);
if (!m_importedParentContexts.isEmpty()) {
DUChainReadLocker lock(DUChain::lock());
kWarning() << file->url().str() << "Previous parameter declaration context didn't get used??" ;
// KDevelop::DumpChain dump;
// dump.dump(topLevelContext);
m_importedParentContexts.clear();
}
DUChainWriteLocker lock(DUChain::lock());
topLevelContext->squeeze();
return topLevelContext;
}
void ContextBuilder::visitNamespace (NamespaceAST *node)
{
QualifiedIdentifier identifier;
if (compilingContexts()) {
DUChainReadLocker lock(DUChain::lock());
if (node->namespace_name)
identifier.push(QualifiedIdentifier(editor()->tokenToString(node->namespace_name)));
}
size_t realStart = node->start_token;
if(node->namespace_name) //Move the start behind the name, the simple + hacky way
node->start_token = node->namespace_name+1;
openContext(node, DUContext::Namespace, identifier);
node->start_token = realStart;
DefaultVisitor::visitNamespace (node);
closeContext();
}
void ContextBuilder::visitEnumSpecifier(EnumSpecifierAST* node)
{
if(m_onlyComputeSimplified)
return;
//The created context must be unnamed if not an "enum class", so the enumerators can be found globally with the correct scope.
//In case of an "enum class" this enum creates its own context.
openContext(node, DUContext::Enum, node->isClass ? node->name : 0 );
if (!node->isClass) {
DUChainWriteLocker lock(DUChain::lock());
currentContext()->setPropagateDeclarations(true);
}
DefaultVisitor::visitEnumSpecifier(node);
closeContext();
}
void ContextBuilder::classContextOpened(ClassSpecifierAST *node, DUContext* context)
{
Q_UNUSED(node);
Q_UNUSED(context);
}
void ContextBuilder::visitClassSpecifier (ClassSpecifierAST *node)
{
//We only use the local identifier here, because we build a prefix-context around
///@todo think about this.
QualifiedIdentifier id;
if(node->name) {
NameCompiler nc(editor()->parseSession());
nc.run(node->name);
id = nc.identifier();
}
openContext(node, editor()->findRangeForContext(node->name ? node->name->end_token : node->start_token, node->end_token), DUContext::Class, id.isEmpty() ? QualifiedIdentifier() : QualifiedIdentifier(id.last()) );
addImportedContexts(); //eventually add template-context
if(!node->name) {
int kind = editor()->parseSession()->token_stream->kind(node->class_key);
if ((kind == Token_union || id.isEmpty())) {
//It's an unnamed union context, or an unnamed struct, propagate the declarations to the parent
DUChainWriteLocker lock(DUChain::lock());
if(kind == Token_enum || kind == Token_union || m_typeSpecifierWithoutInitDeclarators == node->start_token) {
///@todo Mark unions in the duchain in some way, instead of just representing them as a class
currentContext()->setInSymbolTable(currentContext()->parentContext()->inSymbolTable());
currentContext()->setPropagateDeclarations(true);
}
}
}
classContextOpened(node, currentContext());
DefaultVisitor::visitClassSpecifier (node);
closeContext();
}
void ContextBuilder::visitTypedef (TypedefAST *node)
{
DefaultVisitor::visitTypedef (node);
// Didn't get claimed if it was still set
m_importedParentContexts.clear();
}
void ContextBuilder::visitFunctionDefinition (FunctionDefinitionAST *node)
{
PushValue<bool> push(m_inFunctionDefinition, (bool)node->function_body || node->defaultDeleted != FunctionDefinitionAST::NotDefaultOrDeleted);
QualifiedIdentifier functionName;
if (compilingContexts() && node->declarator && node->declarator->id) {
identifierForNode(node->declarator->id, functionName);
if (functionName.count() >= 2) {
// This is a class function definition
DUChainReadLocker lock(DUChain::lock());
QualifiedIdentifier currentScope = currentContext()->scopeIdentifier(true);
QualifiedIdentifier className = currentScope + functionName;
className.pop();
className.setExplicitlyGlobal(true);
QList<Declaration*> classDeclarations = currentContext()->findDeclarations(className);
if (classDeclarations.count() != 0 && classDeclarations.first()->internalContext()) {
queueImportedContext(classDeclarations.first()->internalContext());
QualifiedIdentifier newFunctionName(className);
newFunctionName.push(functionName.last());
if(newFunctionName.count() > currentScope.count())
functionName = newFunctionName.mid(currentScope.count());
}
}
}
visitFunctionDeclaration(node);
if(!m_onlyComputeVisible) { //If we only compute the publically visible, we don't need to go into function bodies
m_openingFunctionBody = functionName;
if (node->constructor_initializers && node->function_body) {
//Since we put the context around the context for the compound statement, it also gets the local scope identifier.
openContext(node->constructor_initializers, node->function_body, DUContext::Other, m_openingFunctionBody); //The constructor initializer context
addImportedContexts();
m_openingFunctionBody = QualifiedIdentifier();
}
// Otherwise, the context is created in the function body visit
visit(node->constructor_initializers);
visit(node->function_body);
m_openingFunctionBody = QualifiedIdentifier();
if (node->constructor_initializers) {
closeContext();
}
}
visit(node->win_decl_specifiers);
// If still defined, not needed
m_importedParentContexts.clear();
}
void ContextBuilder::visitFunctionDeclaration (FunctionDefinitionAST* node)
{
visit(node->type_specifier);
visit(node->declarator);
}
DUContext* ContextBuilder::openContextEmpty(AST* rangeNode, DUContext::ContextType type)
{
if (compilingContexts()) {
#ifdef DEBUG_UPDATE_MATCHING
kDebug() << "opening context with text" << editor()->tokensToStrings( rangeNode->start_token, rangeNode->end_token );
#endif
KDevelop::RangeInRevision range = editor()->findRangeForContext(rangeNode->start_token, rangeNode->end_token);
range.end = range.start;
DUContext* ret = openContextInternal(range, type, QualifiedIdentifier());
rangeNode->ducontext = ret;
return ret;
} else {
openContext(rangeNode->ducontext);
return currentContext();
}
}
DUContext* ContextBuilder::openContextInternal(const KDevelop::RangeInRevision& range, DUContext::ContextType type, const QualifiedIdentifier& identifier)
{
DUContext* ret = ContextBuilderBase::openContextInternal(range, type, identifier);
{
DUChainWriteLocker lock(DUChain::lock());
static_cast<CppDUContext<DUContext>*>(ret)->deleteAllInstantiations();
}
/**
* @todo either remove this here and add it to the correct other places, or remove it in the over places.
* The problem: For template-parameter contexts this needs to be imported into function-parameter contexts
* and into class-contexts, directly when they are opened. Maybe it is easier leaving it here.
* */
addImportedContexts();
return ret;
}
DUContext* ContextBuilder::newContext(const RangeInRevision& range)
{
return new CppDUContext<DUContext>(range, currentContext());
}
#ifdef DEBUG_CONTEXT_RANGES
void ContextBuilder::checkRanges()
{
for(QHash<KDevelop::DUContext*, KDevelop::RangeInRevision>::iterator it = m_contextRanges.begin(); it != m_contextRanges.end(); ) {
if(it.key()->range() != *it) {
kDebug(9007) << "Range of" << it.key()->scopeIdentifier(true).toString() << "changed from" << (*it).textRange() << "to" << it.key()->range().textRange() << "at\n" << kBacktrace();
it = m_contextRanges.erase(it); //Remove it so we see each change only once
}else{
++it;
}
}
}
#endif
void ContextBuilder::visitCompoundStatement(CompoundStatementAST * node)
{
openContext(node, DUContext::Other, m_openingFunctionBody);
m_openingFunctionBody.clear();
addImportedContexts();
DefaultVisitor::visitCompoundStatement(node);
closeContext();
}
void ContextBuilder::preVisitSimpleDeclaration(SimpleDeclarationAST * node) {
if(!node->init_declarators && node->type_specifier)
m_typeSpecifierWithoutInitDeclarators = node->type_specifier->start_token;
}
void ContextBuilder::visitSimpleDeclaration(SimpleDeclarationAST *node)
{
preVisitSimpleDeclaration(node);
DefaultVisitor::visitSimpleDeclaration(node);
// Didn't get claimed if it was still set
m_importedParentContexts.clear();
}
void ContextBuilder::visitPostSimpleDeclaration(SimpleDeclarationAST*)
{
// Didn't get claimed if it was still set
m_importedParentContexts.clear();
}
void ContextBuilder::visitName (NameAST *)
{
// Note: we don't want to visit the name node, the name compiler does that for us (only when we need it)
}
void ContextBuilder::visitUsing(UsingAST* node)
{
// TODO store the using
DefaultVisitor::visitUsing(node);
}
/**
* This class is used to decide whether something is an expression or a declaration
* Maybe using it is overkill
* @todo Check how to do the test fast and correctly
* */
class VerifyExpressionVisitor : public Cpp::ExpressionVisitor {
public:
VerifyExpressionVisitor(ParseSession* session) : Cpp::ExpressionVisitor(session), result(true) {
}
virtual void problem(AST* /*node*/, const QString& /*str*/) {
result = false;
}
bool result;
};
class IdentifierVerifier : public DefaultVisitor
{
public:
IdentifierVerifier(ContextBuilder* _builder, const CursorInRevision& _cursor)
: builder(_builder)
, result(true)
, cursor(_cursor)
{
}
ContextBuilder* builder;
bool result; //Will be true when this should be an expression, else false.
CursorInRevision cursor;
void visitPostfixExpression(PostfixExpressionAST* node)
{
if( node->expression && node->expression->kind == AST::Kind_PrimaryExpression &&
node->sub_expressions ) {
const ListNode<ExpressionAST*> *it = node->sub_expressions->toFront(), *end = it;
if( it->element && it->element->kind == AST::Kind_FunctionCall && it->next == end ) {
///Special-case: We have a primary expression with a function-call, always treat that as an expression.
return;
}
}
//First evaluate the primary expression, and then pass the result from sub-expression to sub-expression through m_lastType
visit(node->expression);
if( !node->sub_expressions )
return;
visitNodes( this, node->sub_expressions );
}
virtual void visitName (NameAST * node)
{
if (result) {
QualifiedIdentifier id;
builder->identifierForNode(node, id);
if (!builder->currentContext()->findDeclarations(id, cursor).isEmpty()) {
result = false;
}else{
}
}
}
};
void ContextBuilder::visitExpressionOrDeclarationStatement(ExpressionOrDeclarationStatementAST* node)
{
if(m_onlyComputeSimplified) {
visit(node->declaration);
return;
}
DUContext::ContextType type;
{
DUChainReadLocker lock(DUChain::lock());
type = currentContext()->type();
}
switch (type) {
case DUContext::Global:
case DUContext::Namespace:
case DUContext::Class:
case DUContext::Helper:
case DUContext::Enum:
visit(node->declaration);
break;
case DUContext::Function:
case DUContext::Other:
if (compilingContexts()) {
DUChainReadLocker lock(DUChain::lock());
/* VerifyExpressionVisitor iv(editor()->parseSession());
node->expression->ducontext = currentContext();
iv.parse(node->expression);*/
IdentifierVerifier iv(this, CursorInRevision(editor()->findPosition(node->start_token)));
iv.visit(node->expression);
//kDebug(9007) << editor()->findPosition(node->start_token) << "IdentifierVerifier returned" << iv.result;
node->expressionChosen = iv.result;
}
if (node->expressionChosen)
visit(node->expression);
else
visit(node->declaration);
break;
case DUContext::Template:
break;
}
}
void ContextBuilder::visitForStatement(ForStatementAST *node)
{
// Not setting the member var because it gets nuked in visitSimpleDeclaration
AST* first = node->init_statement;
if (!first)
first = node->range_declaration;
if (!first)
first = node->condition;
if (!first)
first = node->expression;
if (!first)
return;
AST* second = node->expression;
if (!second)
second = node->condition;
if (!second)
second = node->range_declaration;
if (!second)
second = node->init_statement;
DUContext* secondParentContext = openContext(first, second, DUContext::Other);
if (node->range_declaration) {
// in range-based for we first visit the expression
// since it might define the type for cases like
// for(auto i : foo)
handleRangeBasedFor(node->expression, node->range_declaration);
Q_ASSERT(!node->init_statement);
Q_ASSERT(!node->condition);
} else {
visit(node->init_statement);
visit(node->condition);
visit(node->expression);
}
closeContext();
if (node->statement) {
const bool contextNeeded = createContextIfNeeded(node->statement, secondParentContext);
visit(node->statement);
if (contextNeeded)
closeContext();
}
// Didn't get claimed if it was still set
m_importedParentContexts.clear();
}
void ContextBuilder::handleRangeBasedFor(ExpressionAST* container, ForRangeDeclarationAst* iterator)
{
visit(container);
visit(iterator);
}
void ContextBuilder::createTypeForDeclarator(DeclaratorAST* /*node*/)
{ }
void ContextBuilder::closeTypeForDeclarator(DeclaratorAST* /*node*/)
{ }
void ContextBuilder::createTypeForInitializer(InitializerAST* /*node*/)
{ }
void ContextBuilder::createTypeForCondition(ConditionAST* /*node*/)
{ }
void ContextBuilder::visitParameterDeclarationClause(ParameterDeclarationClauseAST* node)
{
//Don't assign the initializer to parameter-declarations
InitializerAST* oldCurrentInitializer = m_currentInitializer;
m_currentInitializer = 0;
DefaultVisitor::visitParameterDeclarationClause(node);
m_currentInitializer = oldCurrentInitializer;
}
void ContextBuilder::visitInitDeclarator(InitDeclaratorAST *node)
{
QualifiedIdentifier id;
if(node->declarator && node->declarator->id && node->declarator->id->qualified_names && !node->declarator->parameter_declaration_clause) {
//Build a prefix-context for external variable-definitions
CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge);
identifierForNode(node->declarator->id, id);
openPrefixContext(node, id, pos);
}
m_currentInitializer = node->initializer;
if(node->declarator)
visitDeclarator(node->declarator);
if(node->initializer)
visitInitializer(node->initializer);
m_currentInitializer = 0;
closePrefixContext(id);
}
void ContextBuilder::visitDeclarator(DeclaratorAST *node) {
//BEGIN Copied from default visitor
visit(node->sub_declarator);
visitNodes(this, node->ptr_ops);
visit(node->id);
visit(node->bit_expression);
//END Finished with first part of default visitor
if(m_onlyComputeSimplified)
return;
createTypeForDeclarator(node);
// These need to be visited now, so the type-builder can use them
// to build a constant integral types
if(m_currentInitializer)
createTypeForInitializer(m_currentInitializer);
else if(m_currentCondition)
createTypeForCondition(m_currentCondition);
if (node->parameter_declaration_clause && (compilingContexts() || node->parameter_declaration_clause->ducontext)) {
DUContext* ctx = openContext(node->parameter_declaration_clause, DUContext::Function, node->id);
addImportedContexts();
if(compilingContexts())
queueImportedContext(ctx);
}
//BEGIN Copied from default visitor
visitNodes(this, node->array_dimensions);
visit(node->parameter_declaration_clause);
visit(node->exception_spec);
visit(node->trailing_return_type);
//END Finished with default visitor
closeTypeForDeclarator(node);
if (node->parameter_declaration_clause && (compilingContexts() || node->parameter_declaration_clause->ducontext))
closeContext();
}
void ContextBuilder::addImportedContexts()
{
if (compilingContexts() && !m_importedParentContexts.isEmpty()) {
DUChainWriteLocker lock(DUChain::lock());
foreach (const DUContext::Import& imported, m_importedParentContexts)
if(DUContext* imp = imported.context(topContext()))
addImportedParentContextSafely(currentContext(), imp);
//Move on the internal-context of Declarations/Definitions
foreach( const DUContext::Import& importedContext, m_importedParentContexts ) {
if( DUContext* imp = importedContext.context(topContext()) )
if( imp->type() == DUContext::Template || imp->type() == DUContext::Function )
if( imp->owner() && imp->owner()->internalContext() == imp )
imp->owner()->setInternalContext(currentContext());
}
m_importedParentContexts.clear();
}
clearLastContext();
}
void ContextBuilder::setInSymbolTable(KDevelop::DUContext* context) {
if(context->type() == DUContext::Class) {
//Do not put unnamed/unique structs and all their members into the symbol-table
QualifiedIdentifier scopeId = context->localScopeIdentifier();
if(scopeId.isEmpty() || (scopeId.count() == 1 && scopeId.first().isUnique())) {
context->setInSymbolTable(false);
return;
}
}
ContextBuilderBase::setInSymbolTable(context);
}
void ContextBuilder::visitIfStatement(IfStatementAST* node)
{
// Not setting the member var because it gets nuked in visitSimpleDeclaration
DUContext* secondParentContext = openContext(node->condition, DUContext::Other);
visit(node->condition);
closeContext();
if (node->statement) {
const bool contextNeeded = createContextIfNeeded(node->statement, secondParentContext);
visit(node->statement);
if (contextNeeded)
closeContext();
}
if (node->else_statement) {
const bool contextNeeded = createContextIfNeeded(node->else_statement, secondParentContext);
visit(node->else_statement);
if (contextNeeded)
closeContext();
}
}
void ContextBuilder::visitSwitchStatement(SwitchStatementAST* node)
{
DUContext* secondParentContext = openContext(node->condition, DUContext::Other);
visit(node->condition);
closeContext();
if (node->statement) {
const bool contextNeeded = createContextIfNeeded(node->statement, secondParentContext);
visit(node->statement);
if (contextNeeded)
closeContext();
}
}
void ContextBuilder::visitDoStatement(DoStatementAST *node)
{
if(!node->statement) {
kWarning() << "error, no statement"; //Warning instead of crashing here
return;
}
//We don't need to create a context for compound-statements, since those create a context by themselves
if(node->statement->kind != AST::Kind_CompoundStatement) {
openContext(node->statement, DUContext::Other);
visit(node->statement);
closeContext();
}else{
visit(node->statement);
}
if (node->expression) {
const bool contextNeeded = createContextIfNeeded(node->expression, lastContext());
visit(node->expression);
if (contextNeeded)
closeContext();
}
}
void ContextBuilder::visitTryBlockStatement(TryBlockStatementAST *node)
{
QVector<DUContext::Import> parentContextsToImport = m_importedParentContexts;
if(node->try_block->kind != AST::Kind_CompoundStatement) {
openContext(node->try_block, DUContext::Other, m_openingFunctionBody);
m_openingFunctionBody.clear();
addImportedContexts();
visit(node->try_block);
closeContext();
}else{
//Do not double-open a context on the same node, because that will lead to problems in the mapping
//and failures in use-building
visit(node->try_block);
}
m_tryParentContexts.push(parentContextsToImport);
visitNodes(this, node->catch_blocks);
m_tryParentContexts.pop();
}
void ContextBuilder::visitCatchStatement(CatchStatementAST *node)
{
QVector<DUContext::Import> contextsToImport;
if (node->condition) {
DUContext* secondParentContext = openContext(node->condition, DUContext::Other);
{
DUChainReadLocker lock(DUChain::lock());
contextsToImport.append(DUContext::Import(secondParentContext, 0));
}
visit(node->condition);
closeContext();
}
contextsToImport += m_tryParentContexts.top();
if (node->statement) {
const bool contextNeeded = createContextIfNeeded(node->statement, contextsToImport);
visit(node->statement);
if (contextNeeded)
closeContext();
}
}
void ContextBuilder::visitLambdaDeclarator(LambdaDeclaratorAST* node)
{
if (node->parameter_declaration_clause) {
DUContext* ctx = openContext(node->parameter_declaration_clause, DUContext::Function);
addImportedContexts();
if(compilingContexts())
queueImportedContext(ctx);
}
DefaultVisitor::visitLambdaDeclarator(node);
if (node->parameter_declaration_clause) {
closeContext();
}
}
void ContextBuilder::visitCondition(ConditionAST* node)
{
m_currentCondition = node;
DefaultVisitor::visitCondition(node);
m_currentCondition = 0;
}
bool ContextBuilder::createContextIfNeeded(AST* node, DUContext* importedParentContext)
{
QVector<DUContext::Import> imports;
{
DUChainReadLocker lock(DUChain::lock());
imports << DUContext::Import(importedParentContext, 0);
}
return createContextIfNeeded(node, imports);
}
bool ContextBuilder::createContextIfNeeded(AST* node, const QVector<DUContext::Import>& importedParentContexts)
{
m_importedParentContexts = importedParentContexts;
const bool contextNeeded = !ast_cast<CompoundStatementAST*>(node);
if (contextNeeded) {
openContext(node, DUContext::Other);
addImportedContexts();
}
return contextNeeded;
}
void ContextBuilder::identifierForNode(NameAST* id, TypeSpecifierAST** typeSpecifier, QualifiedIdentifier& target)
{
if( !id ) {
target = QualifiedIdentifier();
}
m_nameCompiler.run(id, &target);
if( typeSpecifier )
*typeSpecifier = m_nameCompiler.lastTypeSpecifier();
}