diff --git a/pythonparsejob.cpp b/pythonparsejob.cpp --- a/pythonparsejob.cpp +++ b/pythonparsejob.cpp @@ -29,6 +29,7 @@ #include "usebuilder.h" #include "kshell.h" #include "duchain/helpers.h" +#include "unreachablevisitor.h" #include "pep8kcm/kcm_pep8.h" #include @@ -178,6 +179,9 @@ UseBuilder usebuilder(editor.data(), builder.missingModules()); usebuilder.setCurrentlyParsedDocument(document()); usebuilder.buildUses(m_ast.data()); + + UnreachableVisitor unreachableVisitor(m_duContext); + unreachableVisitor.addWarnings(m_ast.data()); // check whether any unresolved imports were encountered bool needsReparse = ! builder.unresolvedImports().isEmpty(); @@ -267,7 +271,7 @@ m_currentSession->ast = m_ast; m_duContext->setAst(QExplicitlySharedDataPointer(m_currentSession.data())); } - + setDuChain(m_duContext); DUChain::self()->emitUpdateReady(document(), duChain()); } diff --git a/unreachablevisitor.h b/unreachablevisitor.h new file mode 100644 --- /dev/null +++ b/unreachablevisitor.h @@ -0,0 +1,156 @@ +/***************************************************************************** + * Copyright 2017 Francis Herne * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************** + */ + +#ifndef PYTHON_UNREACHABLEVISITOR_H +#define PYTHON_UNREACHABLEVISITOR_H + +#include +#include +#include + +#include "astdefaultvisitor.h" +#include "pythonduchainexport.h" + +#include + +using namespace KDevelop; + +namespace Python +{ + +/** + * @brief Scan for (simple cases of) unreachable code and insert warnings. + */ +class KDEVPYTHONDUCHAIN_EXPORT UnreachableVisitor : public AstDefaultVisitor +{ +public: + UnreachableVisitor(const ReferencedTopDUContext& ctx) : + AstDefaultVisitor(), m_topContext(ctx) {}; + + void addWarnings(Ast* node) { + visitNode(node); + m_currentlyUnreachable = false; + } + + void visitNode(Ast* node) override + { + if ( node && m_currentlyUnreachable ) { + DUChainWriteLocker lock; + auto p = new Problem(); + auto range = KTextEditor::Range(node->start(), 3); + p->setDescription(i18n("Unreachable code")); + p->setFinalLocation(DocumentRange(m_topContext->url(), range)); + p->setSource(IProblem::SemanticAnalysis); + p->setSeverity(IProblem::Warning); + ProblemPointer ptr(p); + m_topContext->addProblem(ptr); + m_currentlyUnreachable = false; // Only one warning per instance + } + AstDefaultVisitor::visitNode(node); + } + + void visitBreak(BreakAst* node) override + { + Q_UNUSED(node); + m_currentlyUnreachable = true; + } + + void visitContinue(ContinueAst* node) override + { + Q_UNUSED(node); + m_currentlyUnreachable = true; + } + + void visitReturn(ReturnAst* node) override + { + Q_UNUSED(node); // Child is never a statement, no point visiting + m_currentlyUnreachable = true; + } + + void visitRaise(Python::RaiseAst* node) override + { + Q_UNUSED(node); + m_currentlyUnreachable = true; + } + + void visitIf(Python::IfAst* node) override + { + // No need to visit the condition + visitNodeList(node->body); + bool unreachableAfterFirst = m_currentlyUnreachable; + // `if` block doesn't affect `else`. + m_currentlyUnreachable = false; + visitNodeList(node->orelse); + // Following code is unreachable only if both branches cause that. + m_currentlyUnreachable &= unreachableAfterFirst; + } + + void visitFor(ForAst* node) override + { + visitNodeList(node->body); + // Loop body may not always be executed: `for x in []` + m_currentlyUnreachable = false; + visitNodeList(node->orelse); + m_currentlyUnreachable = false; + } + + void visitWhile(WhileAst* node) override + { + visitNodeList(node->body); + m_currentlyUnreachable = false; + visitNodeList(node->orelse); + m_currentlyUnreachable = false; + } + + void visitTry(TryAst* node) override + { + visitNodeList(node->body); + m_currentlyUnreachable = false; + visitNodeList(node->handlers); + visitNodeList(node->orelse); + m_currentlyUnreachable = false; + visitNodeList(node->finally); + m_currentlyUnreachable = false; + } + + void visitExceptionHandler(ExceptionHandlerAst* node) override + { + visitNodeList(node->body); + m_currentlyUnreachable = false; + } + + void visitFunctionDefinition(FunctionDefinitionAst* node) override + { + visitNodeList(node->body); + m_currentlyUnreachable = false; + } + + void visitClassDefinition(ClassDefinitionAst* node) override + { + visitNodeList(node->body); + m_currentlyUnreachable = false; + } + +private: + const ReferencedTopDUContext m_topContext; + bool m_currentlyUnreachable = false; +}; + +} + +#endif // PYTHON_UNREACHABLEVISITOR_H