mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 19:02:53 +00:00
328 lines
12 KiB
C++
328 lines
12 KiB
C++
/* This file is part of KDevelop
|
|
Copyright 2010 Aleix Pol Gonzalez <aleixpol@kde.org>
|
|
|
|
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 "controlflowgraphbuilder.h"
|
|
#include <language/checks/controlflownode.h>
|
|
#include <language/checks/controlflowgraph.h>
|
|
#include <language/duchain/topducontext.h>
|
|
#include <language/duchain/duchainlock.h>
|
|
#include <parsesession.h>
|
|
#include <lexer.h>
|
|
#include <tokens.h>
|
|
#include <util/pushvalue.h>
|
|
|
|
using namespace KDevelop;
|
|
QString nodeToString(const ParseSession* s, AST* node);
|
|
|
|
ControlFlowGraphBuilder::ControlFlowGraphBuilder(const KDevelop::ReferencedTopDUContext& top, const ParseSession* session, ControlFlowGraph* graph)
|
|
: m_session(session)
|
|
, m_graph(graph)
|
|
, m_currentNode(0)
|
|
, m_returnNode(0)
|
|
, m_breakNode(0)
|
|
, m_continueNode(0)
|
|
, m_defaultNode(0)
|
|
, m_top(top)
|
|
{}
|
|
|
|
ControlFlowGraphBuilder::~ControlFlowGraphBuilder()
|
|
{}
|
|
|
|
void ControlFlowGraphBuilder::run(AST* node)
|
|
{
|
|
Q_ASSERT(!m_currentNode);
|
|
visit(node);
|
|
}
|
|
|
|
CursorInRevision ControlFlowGraphBuilder::cursorForToken(uint token)
|
|
{
|
|
return m_session->positionAt(m_session->token_stream->position(token));
|
|
}
|
|
|
|
RangeInRevision ControlFlowGraphBuilder::nodeRange(AST* node)
|
|
{
|
|
RangeInRevision ret = node ? RangeInRevision(cursorForToken(node->start_token), cursorForToken(node->end_token)) : RangeInRevision::invalid();
|
|
if(ret.start>=ret.end) { //I don't understand why I need this
|
|
CursorInRevision start=ret.end;
|
|
ret.start=ret.end;
|
|
ret.end=start;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// RangeInRevision rangeBetween(uint start_token, uint end_token);
|
|
|
|
ControlFlowNode* ControlFlowGraphBuilder::createCompoundStatement(AST* node, ControlFlowNode* next)
|
|
{
|
|
ControlFlowNode* startNode = new ControlFlowNode;
|
|
if(node)
|
|
createCompoundStatementFrom(startNode, node, next);
|
|
else {
|
|
startNode->setNext(next);
|
|
m_currentNode = startNode;
|
|
}
|
|
return startNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::createCompoundStatementFrom(ControlFlowNode* startNode, AST* node, ControlFlowNode* next)
|
|
{
|
|
Q_ASSERT(node && startNode);
|
|
CursorInRevision startcursor = cursorForToken(node->start_token);
|
|
startNode->setStartCursor(startcursor);
|
|
m_currentNode = startNode;
|
|
visit(node);
|
|
|
|
if(!m_currentNode->next()) {
|
|
m_currentNode->setNext(next);
|
|
m_currentNode->setEndCursor(cursorForToken(node->end_token));
|
|
}
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitFunctionDefinition(FunctionDefinitionAST* node)
|
|
{
|
|
if(!node->function_body || !node->function_body->ducontext) //means its ducontext hasn't been built, we probably don't need the flow diagram
|
|
return;
|
|
|
|
PushValue<ControlFlowNode*> currentNode(m_currentNode);
|
|
m_returnNode = new ControlFlowNode;
|
|
|
|
Declaration* d;
|
|
{
|
|
KDevelop::DUChainReadLocker lock;
|
|
d=node->function_body->ducontext->owner();
|
|
// qDebug() << "lalala" << nodeToString(m_session, node) << node->function_body->ducontext->owner() << node->function_body;
|
|
}
|
|
|
|
if(d)
|
|
m_graph->addEntry(d, createCompoundStatement(node->function_body, m_returnNode));
|
|
else
|
|
m_graph->addEntry(createCompoundStatement(node->function_body, m_returnNode));
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitEnumerator(EnumeratorAST* node)
|
|
{
|
|
bool create=!m_currentNode;
|
|
if(create && node->expression)
|
|
m_graph->addEntry(createCompoundStatement(node->expression, 0));
|
|
else
|
|
DefaultVisitor::visitEnumerator(node);
|
|
}
|
|
|
|
|
|
void ControlFlowGraphBuilder::visitIfStatement(IfStatementAST* node)
|
|
{
|
|
ControlFlowNode* previous = m_currentNode;
|
|
m_currentNode->setEndCursor(cursorForToken(node->condition->end_token));
|
|
visit(node->condition);
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
|
|
previous->setConditionRange(nodeRange(node->condition));
|
|
previous->setNext(createCompoundStatement(node->statement, nextNode));
|
|
previous->setAlternative(node->else_statement ? createCompoundStatement(node->else_statement, nextNode) : nextNode);
|
|
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitWhileStatement(WhileStatementAST* node)
|
|
{
|
|
//TODO: m_currentNode->m_conditionRange = rangeBetween(node->condition->start_token, node->condition->end_token);
|
|
m_currentNode->setEndCursor(cursorForToken(node->start_token));
|
|
ControlFlowNode* previous = m_currentNode;
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
ControlFlowNode* conditionNode = createCompoundStatement(node->condition, 0);
|
|
|
|
PushValue<ControlFlowNode*> pushBreak(m_breakNode, nextNode);
|
|
PushValue<ControlFlowNode*> pushContinue(m_continueNode, conditionNode);
|
|
ControlFlowNode* bodyNode = createCompoundStatement(node->statement, conditionNode);
|
|
|
|
previous->setNext(conditionNode);
|
|
conditionNode->setConditionRange(nodeRange(node->condition));
|
|
conditionNode->setNext(bodyNode);
|
|
conditionNode->setAlternative(nextNode);
|
|
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitForStatement(ForStatementAST* node)
|
|
{
|
|
AST* flownode = node->condition ? (AST*) node->condition : (AST*) node->range_declaration;
|
|
visit(node->init_statement);
|
|
m_currentNode->setEndCursor(cursorForToken(flownode ? flownode->start_token : node->init_statement->end_token));
|
|
ControlFlowNode* previous = m_currentNode;
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
ControlFlowNode* conditionNode = createCompoundStatement(flownode, nextNode);
|
|
ControlFlowNode* endCondition = m_currentNode;
|
|
ControlFlowNode* incNode = createCompoundStatement(node->expression, conditionNode);
|
|
|
|
PushValue<ControlFlowNode*> pushBreak(m_breakNode, nextNode);
|
|
PushValue<ControlFlowNode*> pushContinue(m_continueNode, incNode);
|
|
ControlFlowNode* bodyNode = createCompoundStatement(node->statement, incNode);
|
|
|
|
endCondition->setAlternative(bodyNode);
|
|
endCondition->setConditionRange(nodeRange(flownode));
|
|
|
|
previous->setNext(conditionNode);
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitConditionalExpression(ConditionalExpressionAST* node)
|
|
{
|
|
visit(node->condition);
|
|
m_currentNode->setEndCursor(cursorForToken(node->condition->end_token));
|
|
ControlFlowNode* previous = m_currentNode;
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
ControlFlowNode* trueNode = createCompoundStatement(node->left_expression, nextNode);
|
|
ControlFlowNode* elseNode = createCompoundStatement(node->right_expression, nextNode);
|
|
|
|
previous->setNext(trueNode);
|
|
previous->setAlternative(elseNode);
|
|
previous->setConditionRange(nodeRange(node->condition));
|
|
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitDoStatement(DoStatementAST* node)
|
|
{
|
|
m_currentNode->setEndCursor(cursorForToken(node->start_token));
|
|
ControlFlowNode* previous = m_currentNode;
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
ControlFlowNode* condNode = createCompoundStatement(node->expression, nextNode);
|
|
|
|
PushValue<ControlFlowNode*> pushBreak(m_breakNode, nextNode);
|
|
PushValue<ControlFlowNode*> pushContinue(m_continueNode, condNode);
|
|
ControlFlowNode* bodyNode = createCompoundStatement(node->statement, condNode);
|
|
|
|
previous->setNext(bodyNode);
|
|
condNode->setAlternative(bodyNode);
|
|
condNode->setConditionRange(nodeRange(node->expression));
|
|
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitReturnStatement(ReturnStatementAST* node)
|
|
{
|
|
DefaultVisitor::visitReturnStatement(node);
|
|
m_currentNode->setEndCursor(cursorForToken(node->end_token));
|
|
m_currentNode->setNext(m_returnNode);
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitJumpStatement(JumpStatementAST* node)
|
|
{
|
|
m_currentNode->setEndCursor(cursorForToken(node->end_token));
|
|
switch(m_session->token_stream->token(node->start_token).kind) {
|
|
case Token_continue:
|
|
// if(!m_continueNode) addproblem(!!!);
|
|
m_currentNode->setNext(m_continueNode);
|
|
break;
|
|
case Token_break:
|
|
// if(!m_breakNode) addproblem(!!!);
|
|
m_currentNode->setNext(m_breakNode);
|
|
break;
|
|
case Token_goto: {
|
|
// qDebug() << "goto!";
|
|
IndexedString tag = m_session->token_stream->symbol(node->identifier);
|
|
QMap< IndexedString, ControlFlowNode* >::const_iterator tagIt = m_taggedNodes.constFind(tag);
|
|
if(tagIt!=m_taggedNodes.constEnd())
|
|
m_currentNode->setNext(*tagIt);
|
|
else {
|
|
m_pendingGotoNodes[tag] += m_currentNode;
|
|
m_currentNode->setNext(0); //we set null waiting to find a proper node to jump to
|
|
}
|
|
} break;
|
|
}
|
|
|
|
//here we create a node with the dead code
|
|
ControlFlowNode* deadNode = new ControlFlowNode;
|
|
deadNode->setStartCursor(m_currentNode->nodeRange().end);
|
|
// deadNode->setEndCursor(cursorForToken(node->end_token));
|
|
m_currentNode=deadNode;
|
|
m_graph->addDeadNode(deadNode);
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitSwitchStatement(SwitchStatementAST* node)
|
|
{
|
|
visit(node->condition);
|
|
m_currentNode->setEndCursor(cursorForToken(node->condition->end_token));
|
|
// ControlFlowNode* previous = m_currentNode;
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
PushValue<ControlFlowNode*> pushBreak(m_breakNode, nextNode);
|
|
PushValue<ControlFlowNode*> pushDefault(m_defaultNode, nextNode);
|
|
|
|
ControlFlowNode* switchNode = m_currentNode;
|
|
switchNode->setNext(nextNode);
|
|
PushValue<QList< QPair<ControlFlowNode*, ControlFlowNode*> > > pushCases(m_caseNodes, QList<QPair<ControlFlowNode*,ControlFlowNode*> > ());
|
|
visit(node->statement);
|
|
switchNode->setNext(m_defaultNode);
|
|
switchNode->setAlternative(m_caseNodes.isEmpty() ? 0 : m_caseNodes.first().first);
|
|
switchNode->setConditionRange(nodeRange(node->condition));
|
|
nextNode->setStartCursor(cursorForToken(node->end_token));
|
|
m_currentNode = nextNode;
|
|
}
|
|
|
|
void ControlFlowGraphBuilder::visitLabeledStatement(LabeledStatementAST* node)
|
|
{
|
|
visit(node->expression);
|
|
|
|
int token = m_session->token_stream->token(node->start_token).kind;
|
|
|
|
if(token==Token_default || token==Token_case) {
|
|
ControlFlowNode* condNode = new ControlFlowNode;
|
|
condNode->setStartCursor(cursorForToken(node->start_token));
|
|
condNode->setEndCursor(cursorForToken(node->end_token));
|
|
|
|
condNode->setNext(createCompoundStatement(node->statement, 0));
|
|
|
|
if(!m_caseNodes.isEmpty()) {
|
|
m_caseNodes.last().first->setAlternative(condNode);
|
|
if(!m_caseNodes.last().second->next())
|
|
m_caseNodes.last().second->setNext(condNode->next());
|
|
}
|
|
|
|
m_caseNodes+=qMakePair(condNode, m_currentNode);
|
|
|
|
if(token==Token_default)
|
|
m_defaultNode = condNode;
|
|
|
|
} else { //it is a goto tag
|
|
m_currentNode->setEndCursor(cursorForToken(node->start_token));
|
|
|
|
ControlFlowNode* nextNode = new ControlFlowNode;
|
|
nextNode->setStartCursor(cursorForToken(node->start_token));
|
|
if(!m_currentNode->next()) m_currentNode->setNext(nextNode);
|
|
|
|
IndexedString tag = m_session->token_stream->symbol(node->label);
|
|
m_taggedNodes.insert(tag, nextNode);
|
|
QList< ControlFlowNode* > pendingNodes = m_pendingGotoNodes.take(tag);
|
|
foreach(ControlFlowNode* pending, pendingNodes)
|
|
pending->setNext(nextNode);
|
|
|
|
m_currentNode = nextNode;
|
|
visit(node->statement);
|
|
}
|
|
}
|