diff --git a/CMakeLists.txt b/CMakeLists.txt index d5434f6..4303835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,90 +1,91 @@ cmake_minimum_required(VERSION 2.8.12) project(kdevrust) set(KDEVRUST_VERSION_MAJOR 0) set(KDEVRUST_VERSION_MINOR 1) set(KDEVRUST_VERSION_PATCH 0) # KDevplatform dependency version set(KDEVPLATFORM_VERSION "${KDEVRUST_VERSION_MAJOR}.${KDEVRUST_VERSION_MINOR}.${KDEVRUST_VERSION_PATCH}") find_package(ECM 5.14.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(KDECompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMAddTests) include(ECMQtDeclareLoggingCategory) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) find_package(Qt5 REQUIRED Core Widgets Test) find_package(KF5 REQUIRED COMPONENTS ItemModels ThreadWeaver TextEditor I18n) find_package(KDevPlatform ${KDEVPLATFORM_VERSION} REQUIRED) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdocumentation") endif() add_definitions( -DTRANSLATION_DOMAIN=\"kdevrust\" ) enable_testing() #add_subdirectory(parser) #add_subdirectory(duchain) #add_subdirectory(codecompletion) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} # ${CMAKE_CURRENT_SOURCE_DIR}/duchain # ${CMAKE_CURRENT_SOURCE_DIR}/parser # ${CMAKE_CURRENT_BINARY_DIR}/parser ) link_directories(${AST_REDUX_LIBDIR}) set(kdevrustlanguagesupport_PART_SRCS rustlanguagesupport.cpp rustparsejob.cpp rusthighlighting.cpp duchain/contextbuilder.cpp duchain/declarationbuilder.cpp + duchain/usebuilder.cpp duchain/rustnode.cpp duchain/rustducontext.cpp duchain/parsesession.cpp duchain/astredux.h duchain/nodetraits.h # rustdebug.cpp ) ecm_qt_declare_logging_category(kdevrustlanguagesupport_PART_SRCS HEADER rustdebug.h IDENTIFIER KDEV_RUST CATEGORY_NAME "kdevelop.languages.rust" ) kdevplatform_add_plugin(kdevrustlanguagesupport JSON kdevrustsupport.json SOURCES ${kdevrustlanguagesupport_PART_SRCS} ) target_link_libraries(kdevrustlanguagesupport KDev::Interfaces KDev::Language KF5::ThreadWeaver KF5::TextEditor # kdevrustparser # kdevrustduchain # kdevrustcompletion ast_redux ) install(FILES kdevrustsupport.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/duchain/astredux.h b/duchain/astredux.h index 290fcb1..13bb6e4 100644 --- a/duchain/astredux.h +++ b/duchain/astredux.h @@ -1,65 +1,66 @@ #ifndef ASTREDUX_H #define ASTREDUX_H extern "C" { enum RSNodeKind { Crate, StructDecl, EnumDecl, TraitDecl, ImplDecl, TypeAliasDecl, FieldDecl, EnumVariantDecl, FunctionDecl, ParmDecl, VarDecl, + PathUse, Unexposed }; enum RSVisitResult { Break, Continue, Recurse }; struct RSLocation { int line; int column; }; struct RSRange { RSLocation start; RSLocation end; }; struct RSCrate; struct RSNode; typedef RSVisitResult (*CallbackFn)(RSNode *node, RSNode *parent, void *data); RSCrate *parse_crate(const char *name, const char *source); void destroy_crate(RSCrate *crate); RSNode *node_from_crate(RSCrate *crate); void destroy_node(RSNode *node); RSCrate *node_get_crate(RSNode *node); RSNodeKind node_get_kind(RSNode *node); const char *node_get_spelling_name(RSNode *node); RSRange node_get_spelling_range(RSNode *node); RSRange node_get_extent(RSNode *node); void destroy_string(const char *str); void visit_children(RSNode *node, CallbackFn callback, void *data); } #endif // ASTREDUX_H diff --git a/duchain/usebuilder.cpp b/duchain/usebuilder.cpp new file mode 100644 index 0000000..a51d285 --- /dev/null +++ b/duchain/usebuilder.cpp @@ -0,0 +1,51 @@ +#include "usebuilder.h" + +#include +#include +#include +#include + +#include "rustdebug.h" + +namespace Rust +{ + +RSVisitResult UseBuilder::visitNode(RustNode *node, RustNode *parent) +{ + using namespace KDevelop; + RSNodeKind kind = node_get_kind(node->data()); + + if (kind == PathUse) { + RustPath path(node); + + + RustPath name(node); + RangeInRevision useRange = editorFindSpellingRange(node, name.value); + + qCDebug(KDEV_RUST) << "USE:" << name.value << "; spelling range: (" + << useRange.start.line + 1 << ":" << useRange.start.column << "-" + << useRange.end.line + 1 << ":" << useRange.end.column << ")"; + + DUContext *context = topContext()->findContextAt(useRange.start); + QList declarations = context->findDeclarations(identifierForNode(&path)); + + if (declarations.isEmpty()) { + Problem *p = new Problem(); + p->setFinalLocation(DocumentRange(document(), useRange.castToSimpleRange())); + p->setSource(IProblem::SemanticAnalysis); + p->setSeverity(IProblem::Hint); + p->setDescription(i18n("Undefined %1", path.value)); + { + DUChainWriteLocker wlock(DUChain::lock()); + ProblemPointer ptr(p); + topContext()->addProblem(ptr); + } + } else if (declarations.first() && declarations.first()->range() != useRange) { + UseBuilderBase::newUse(node, useRange, DeclarationPointer(declarations.first())); + } + } + + return Recurse; +} + +} diff --git a/duchain/usebuilder.h b/duchain/usebuilder.h new file mode 100644 index 0000000..796c68a --- /dev/null +++ b/duchain/usebuilder.h @@ -0,0 +1,25 @@ +#ifndef USEBUILDER_H +#define USEBUILDER_H + +#include + +#include "contextbuilder.h" +#include "rustnode.h" + +namespace Rust +{ + +using UseBuilderBase = KDevelop::AbstractUseBuilder; + +class UseBuilder : public UseBuilderBase +{ +public: + UseBuilder() = default; + virtual ~UseBuilder() = default; + + virtual RSVisitResult visitNode(RustNode *node, RustNode *parent); +}; + +} + +#endif // USEBUILDER_H diff --git a/rustparsejob.cpp b/rustparsejob.cpp index ce35203..8bee8f8 100644 --- a/rustparsejob.cpp +++ b/rustparsejob.cpp @@ -1,111 +1,115 @@ #include "rustparsejob.h" #include #include #include #include #include #include #include #include "duchain/parsesession.h" #include "duchain/astredux.h" #include "duchain/declarationbuilder.h" +#include "duchain/usebuilder.h" #include "rustlanguagesupport.h" #include "rustdebug.h" using namespace KDevelop; namespace Rust { ParseJob::ParseJob(const IndexedString &url, ILanguageSupport *languageSupport) : KDevelop::ParseJob(url, languageSupport) { } LanguageSupport *ParseJob::rust() const { return static_cast(languageSupport()); } ParseSessionData::Ptr ParseJob::findParseSessionData(const IndexedString &url) { DUChainReadLocker lock; auto context = KDevelop::DUChainUtils::standardContextForUrl(url.toUrl()); if (context) { return ParseSessionData::Ptr(dynamic_cast(context->ast().data())); } return {}; } void ParseJob::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { if (abortRequested() || ICore::self()->shuttingDown()) { return abortJob(); } qCDebug(KDEV_RUST) << "Parse job starting for: " << document().toUrl(); QReadLocker parseLock(languageSupport()->parseLock()); { UrlParseLock urlLock(document()); readContents(); } if (abortRequested()) { return; } ParseSession session(findParseSessionData(document())); if (!session.data()) { session.setData(ParseSessionData::Ptr(new ParseSessionData(document(), contents().contents))); } if (abortRequested()) { return; } ReferencedTopDUContext toUpdate = nullptr; { DUChainReadLocker lock; toUpdate = DUChainUtils::standardContextForUrl(document().toUrl()); } if (toUpdate) { translateDUChainToRevision(toUpdate); toUpdate->setRange(RangeInRevision(0, 0, INT_MAX, INT_MAX)); } session.parse(); RustOwnedNode crateNode = RustOwnedNode(node_from_crate(session.crate())); RustNode node(crateNode); DeclarationBuilder builder; auto context = builder.build(document(), &node, toUpdate); setDuChain(context); + UseBuilder uses; + uses.buildUses(&node); + if (abortRequested()) { return; } if (context) { if (minimumFeatures() & TopDUContext::AST) { DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } highlightDUChain(); } qCDebug(KDEV_RUST) << "Parse job finished for: " << document().toUrl(); } }