From 7711067df12920b3e2ab93e281124987e483728c Mon Sep 17 00:00:00 2001 From: Leshana Date: Sat, 15 May 2021 14:37:17 -0400 Subject: [PATCH] Basic handling of relative type-paths to procs --- src/dreamchecker/tests/static_type_tests.rs | 41 +++++++++++++++++++++ src/dreammaker/objtree.rs | 34 +++++++++++------ src/langserver/completion.rs | 4 +- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/dreamchecker/tests/static_type_tests.rs b/src/dreamchecker/tests/static_type_tests.rs index f52886dfb..f7815fefd 100644 --- a/src/dreamchecker/tests/static_type_tests.rs +++ b/src/dreamchecker/tests/static_type_tests.rs @@ -50,3 +50,44 @@ fn return_type() { "##.trim(); check_errors_match(code, RETURN_TYPE_ERRORS); } + +pub const RELATIVE_PROC_PATHS_ERRORS: &[(u32, u16, &str)] = &[ + (13, 17, "failed to resolve path .fake"), +]; + +// In particular we'll test the examples documented in SS13's callback.dm. None should error. +#[test] +fn relative_proc_paths() { + let code = r##" +/proc/global_one() + return /proc/global_two // absolute paths should always work + +/proc/global_two() + return .global_one // global proc while in another global proc + +/mob/proc/test() + return + +/mob/proc/other() + var/proc_path + proc_path = .test // proc defined on current(src) object (when in a /proc/ and not an override) + proc_path = .fake // Doesn't exist, should error + return proc_path + +/mob/subtype/test() + . = .proc/foo // Proc defined on current object (when in an override not a /proc/) + return ..() + +/mob/subtype/proc/foo() + var/proc_path + proc_path = /proc/global_one // global proc while in a datum proc + proc_path = .test // Proc overridden at src + proc_path = .proc/other // Proc defined in parent + proc_path = .proc/test // Proc defined in parent and also overridden in current. + return proc_path + +/mob/subtype/deepertype/proc/bar() + return .test // Proc overridden in parent type (but not current type) +"##.trim(); + check_errors_match(code, RELATIVE_PROC_PATHS_ERRORS); +} diff --git a/src/dreammaker/objtree.rs b/src/dreammaker/objtree.rs index 363c9086e..a2f58228e 100644 --- a/src/dreammaker/objtree.rs +++ b/src/dreammaker/objtree.rs @@ -296,16 +296,27 @@ impl<'a> TypeRef<'a> { } } + // Get child node by name whether its a type, proc, or verb. + pub fn get_child_type_or_proc(self, name: &str) -> Option> { + if let Some(child) = self.child(name) { + return Some(NavigatePathResult::Type(child)); + } + if let Some(proc_ref) = self.get_proc(name) { + return Some(NavigatePathResult::ProcPath(proc_ref, ProcDeclKind::Proc)); + } + return None + } + /// Navigate the tree according to the given path operator. - pub fn navigate(self, op: PathOp, name: &str) -> Option> { + pub fn navigate(self, op: PathOp, name: &str) -> Option> { match op { // '/' always looks for a direct child - PathOp::Slash => self.child(name), + PathOp::Slash => self.get_child_type_or_proc(name), // '.' looks for a child of us or of any of our parents PathOp::Dot => { let mut next = Some(self); while let Some(current) = next { - if let Some(child) = current.child(name) { + if let Some(child) = current.get_child_type_or_proc(name) { return Some(child); } next = current.parent_path(); @@ -314,7 +325,7 @@ impl<'a> TypeRef<'a> { }, // ':' looks for a child of us or of any of our children PathOp::Colon => { - if let Some(child) = self.child(name) { + if let Some(child) = self.get_child_type_or_proc(name) { return Some(child); } for &idx in self.children.values() { @@ -341,11 +352,7 @@ impl<'a> TypeRef<'a> { let name = s.as_ref(); if let Some(current) = next { // Check if it's `proc` or `verb` in the path. - // Note that this doesn't catch this confusing corner case: - // /proc/foo() - // /proc/bar() - // return .foo - // It also doesn't yet handle the difference between `:proc`, + // Note that this doesn't yet handle the difference between `:proc`, // `.proc`, and `/proc`, treating everything as `.proc`. if let Some(kind) = ProcDeclKind::from_name(s.as_ref()) { if let Some((_, name)) = iter.next() { @@ -360,12 +367,17 @@ impl<'a> TypeRef<'a> { } // Otherwise keep navigating as normal. - next = current.navigate(op, name.as_ref()); + let next_step = current.navigate(op, name.as_ref()); + match next_step { + Some(ret @ NavigatePathResult::Type(_)) => next = Some(ret.ty()), + Some(_) => return next_step, + None => next = None, + } } else { return None; } } - next.map(NavigatePathResult::Type) + return next.map(NavigatePathResult::Type) } /// Checks whether this type is a subtype of the given type. diff --git a/src/langserver/completion.rs b/src/langserver/completion.rs index 94d290e0c..a4d4e1440 100644 --- a/src/langserver/completion.rs +++ b/src/langserver/completion.rs @@ -6,7 +6,7 @@ use lsp_types::*; use dm::ast::PathOp; use dm::annotation::Annotation; -use dm::objtree::{TypeRef, TypeVar, TypeProc, ProcValue}; +use dm::objtree::{TypeRef, NavigatePathResult, TypeVar, TypeProc, ProcValue}; use crate::{Engine, Span, is_constructor_name}; use crate::symbol_search::contains; @@ -206,7 +206,7 @@ impl<'a> Engine<'a> { decl = Some("verb"); break; } - if let Some(next) = ty.navigate(op, name) { + if let Some(next) = ty.navigate(op, name).map(NavigatePathResult::ty) { ty = next; } else { break;