diff --git a/core/GlobalState.cc b/core/GlobalState.cc index 50c2f17a0..6637f2e64 100644 --- a/core/GlobalState.cc +++ b/core/GlobalState.cc @@ -1962,6 +1962,7 @@ unique_ptr GlobalState::deepCopy(bool keepId) const { result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->lspExperimentalFastPathEnabled = this->lspExperimentalFastPathEnabled; + result->isSCIPRuby = this->isSCIPRuby; if (keepId) { result->globalStateId = this->globalStateId; @@ -2055,6 +2056,7 @@ unique_ptr GlobalState::copyForIndex() const { result->runningUnderAutogen = this->runningUnderAutogen; result->censorForSnapshotTests = this->censorForSnapshotTests; result->lspExperimentalFastPathEnabled = this->lspExperimentalFastPathEnabled; + result->isSCIPRuby = this->isSCIPRuby; result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->kvstoreUuid = this->kvstoreUuid; diff --git a/core/GlobalState.h b/core/GlobalState.h index 14b12277e..f7a7d726e 100644 --- a/core/GlobalState.h +++ b/core/GlobalState.h @@ -291,6 +291,13 @@ class GlobalState final { // If 'true', enable the experimental, symbol-deletion-based fast path mode bool lspExperimentalFastPathEnabled = false; + // If 'true', we're running in scip-ruby mode. + bool isSCIPRuby = true; + + // --- begin scip-ruby specific state + UnorderedMap> unresolvedFields; + // --- end scip-ruby specific state + // When present, this indicates that single-package rbi generation is being performed, and contains metadata about // the packages that are imported by the one whose interface is being generated. std::optional singlePackageImports; diff --git a/resolver/resolver.cc b/resolver/resolver.cc index 4d0e4916d..78ece5115 100644 --- a/resolver/resolver.cc +++ b/resolver/resolver.cc @@ -3924,6 +3924,93 @@ class ResolveSignaturesWalk { } }; +/// Helper type to traverse ASTs and aggregate information about unresolved fields. +/// +/// For perf, it would make sense to fuse this along with some other map-reduce +/// operation over files, but I've kept it separate for now for ease of maintenance. +class CollectUnresolvedFieldsWalk final { + UnorderedMap> unresolvedFields; + +public: + void postTransformUnresolvedIdent(core::Context ctx, ast::ExpressionPtr &tree) { + auto &unresolvedIdent = ast::cast_tree_nonnull(tree); + using Kind = ast::UnresolvedIdent::Kind; + core::ClassOrModuleRef klass; + switch (unresolvedIdent.kind) { + case Kind::Global: + case Kind::Local: + return; + case Kind::Class: { + auto enclosingClass = ctx.owner.enclosingClass(ctx); + klass = enclosingClass.data(ctx)->isSingletonClass(ctx) + ? enclosingClass // in + : enclosingClass.data(ctx)->lookupSingletonClass(ctx); // in static methods + break; + } + case Kind::Instance: { + klass = ctx.owner.enclosingClass(ctx); + } + } + this->unresolvedFields[klass].insert(unresolvedIdent.name); + } + + struct CollectWalkResult { + UnorderedMap> unresolvedFields; + vector trees; + }; + + static vector collect(core::GlobalState &gs, vector trees, WorkerPool &workers) { + Timer timeit(gs.tracer(), "resolver.collect_unresolved_fields"); + const core::GlobalState &igs = gs; + auto resultq = make_shared>(trees.size()); + auto fileq = make_shared>(trees.size()); + for (auto &tree : trees) { + fileq->push(move(tree), 1); + } + trees.clear(); + + workers.multiplexJob("collectUnresolvedFieldsWalk", [&igs, fileq, resultq]() { + Timer timeit(igs.tracer(), "CollectUnresolvedFieldsWorker"); + CollectUnresolvedFieldsWalk collect; + CollectWalkResult walkResult; + vector collectedTrees; + ast::ParsedFile job; + for (auto result = fileq->try_pop(job); !result.done(); result = fileq->try_pop(job)) { + if (!result.gotItem()) { + continue; + } + core::Context ictx(igs, core::Symbols::root(), job.file); + ast::TreeWalk::apply(ictx, collect, job.tree); + collectedTrees.emplace_back(move(job)); + } + if (!collectedTrees.empty()) { + walkResult.trees = move(collectedTrees); + walkResult.unresolvedFields = std::move(collect.unresolvedFields); + resultq->push(move(walkResult), walkResult.trees.size()); + } + }); + + { + CollectWalkResult threadResult; + for (auto result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); + !result.done(); + result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer())) { + if (!result.gotItem()) { + continue; + } + trees.insert(trees.end(), make_move_iterator(threadResult.trees.begin()), + make_move_iterator(threadResult.trees.end())); + gs.unresolvedFields.reserve(gs.unresolvedFields.size() + threadResult.unresolvedFields.size()); + gs.unresolvedFields.insert(make_move_iterator(threadResult.unresolvedFields.begin()), + make_move_iterator(threadResult.unresolvedFields.end())); + } + } + + fast_sort(trees, [](const auto &lhs, const auto &rhs) -> bool { return lhs.file < rhs.file; }); + return trees; + } +}; + class ResolveSanityCheckWalk { public: void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { @@ -4102,6 +4189,9 @@ ast::ParsedFilesOrCancelled Resolver::run(core::GlobalState &gs, vectorbegin(), line->length()); + if (line == lines.begin()) { + e.setHeader("[scip-ruby] {}", text); + } else { + e.addErrorNote("{}", text); + } + } + } +} + +#ifndef NDEBUG +#define LOG_DEBUG(__gs, __loc, __s) _log_debug(__gs, __loc, __s) +#else +#define LOG_DEBUG(__gs, __s) \ + {} +#endif + // TODO(varun): This is an inline workaround for https://github.com/sorbet/sorbet/issues/5925 // I've not changed the main definition because I didn't bother to rerun the tests with the change. static bool isTemporary(const core::GlobalState &gs, const core::LocalVariable &var) { @@ -778,6 +802,63 @@ string format_ancestry(const core::GlobalState &gs, core::SymbolRef sym) { return out.str(); } +static absl::variant +findUnresolvedFieldTransitive(const core::GlobalState &gs, core::Loc loc, core::ClassOrModuleRef start, + core::NameRef field) { + auto fieldText = field.shortName(gs); + auto isInstanceVar = fieldText.size() >= 2 && fieldText[0] == '@' && fieldText[1] != '@'; + auto isClassInstanceVar = isInstanceVar && start.data(gs)->isSingletonClass(gs); + // Class instance variables are not inherited, unlike ordinary instance + // variables or class variables. + if (isClassInstanceVar) { + return start; + } + auto isClassVar = fieldText.size() >= 2 && fieldText[0] == '@' && fieldText[1] == '@'; + if (isClassVar && !start.data(gs)->isSingletonClass(gs)) { + // Triggered when undeclared class variables are accessed from instance methods. + start = start.data(gs)->lookupSingletonClass(gs); + } + + // TODO(varun): Should we add a cache here? It seems wasteful to redo + // work for every occurrence. + if (gs.unresolvedFields.find(start) == gs.unresolvedFields.end() || + !gs.unresolvedFields.find(start)->second.contains(field)) { + // Triggered by code patterns like: + // # top-level + // def MyClass.method + // # blah + // end + // which is not supported by Sorbet. + LOG_DEBUG(gs, loc, + fmt::format("couldn't find field {} in class {};\n" + "are you using a code pattern like def MyClass.method which is unsupported by Sorbet?", + field.exists() ? field.toString(gs) : "", + start.exists() ? start.showFullName(gs) : "")); + // As a best-effort guess, assume that the definition is + // in this class but we somehow missed it. + return start; + } + + auto best = start; + auto cur = start; + while (cur.exists()) { + auto klass = cur.data(gs); + auto sym = klass->findMember(gs, field); + if (sym.exists()) { + return sym; + } + auto it = gs.unresolvedFields.find(cur); + if (it != gs.unresolvedFields.end() && it->second.contains(field)) { + best = cur; + } + if (cur == klass->superClass()) { // FIXME(varun): Handle mix-ins + break; + } + cur = klass->superClass(); + } + return best; +} + // Loosely inspired by AliasesAndKeywords in IREmitterContext.cc class AliasMap final { public: @@ -816,9 +897,27 @@ class AliasMap final { } if (sym == core::Symbols::Magic_undeclaredFieldStub()) { ENFORCE(!bind.loc.empty()); - this->map.insert( // no trim(...) because undeclared fields shouldn't have :: - {bind.bind.variable, - {NamedSymbolRef::undeclaredField(klass, instr->name, bind.bind.type), bind.loc, false}}); + ENFORCE(klass.isClassOrModule()); + auto result = findUnresolvedFieldTransitive(ctx, ctx.locAt(bind.loc), klass.asClassOrModuleRef(), + instr->name); + if (absl::holds_alternative(result)) { + auto klass = absl::get(result); + if (klass.exists()) { + this->map.insert( // no trim(...) because undeclared fields shouldn't have :: + {bind.bind.variable, + {NamedSymbolRef::undeclaredField(klass, instr->name, bind.bind.type), bind.loc, + false}}); + } + } else if (absl::holds_alternative(result)) { + auto fieldSym = absl::get(result); + if (fieldSym.exists()) { + this->map.insert( + {bind.bind.variable, + {NamedSymbolRef::declaredField(fieldSym, bind.bind.type), trim(bind.loc), false}}); + } + } else { + ENFORCE(false, "Should've handled all cases of variant earlier"); + } continue; } if (sym.isFieldOrStaticField()) { @@ -1019,9 +1118,18 @@ class CFGTraversal final { } else { uint32_t localId = this->functionLocals[localRef]; auto it = this->localDefinitionType.find(localId); - ENFORCE(it != this->localDefinitionType.end(), "file:{}, code:\n{}\naliasMap: {}\n", file.data(gs).path(), - core::Loc(file, loc).toString(gs), this->aliasMap.showRaw(gs, file, cfg)); - auto overrideType = computeOverrideType(it->second, type); + optional overrideType; + if (it != this->localDefinitionType.end()) { + overrideType = computeOverrideType(it->second, type); + } else { + LOG_DEBUG( + gs, core::Loc(file, loc), + fmt::format( + "failed to find type info; are you using a code pattern unsupported by Sorbet?\ndebugging " + "information: aliasMap: {}", + this->aliasMap.showRaw(gs, file, cfg))); + overrideType = type; + } if (isDefinition) { status = this->scipState.saveDefinition(gs, file, OwnedLocal{this->ctx.owner, localId, loc}, type); } else { diff --git a/test/scip/testdata/field_inheritance.rb b/test/scip/testdata/field_inheritance.rb new file mode 100644 index 000000000..c218bd4a1 --- /dev/null +++ b/test/scip/testdata/field_inheritance.rb @@ -0,0 +1,136 @@ +# typed: true + +# First, check that instance variables are propagated through +# the inheritance chain. + +class C1 + attr_accessor :h + attr_accessor :i + + def set_ivar + @f = 1 + return + end +end + +class C2 < C1 + def get_inherited_ivar + return @f + @h + end + + def set_inherited_ivar + @f = 10 + return + end + + def set_new_ivar + @g = 1 + return + end + + def get_new_ivar + return @g + end +end + +class C3 < C2 + def refs + @f = @g + @i + return + end +end + +def c_check + C1.new.instance_variable_get(:@f) + C1.new.instance_variable_get(:@h) + C1.new.instance_variable_get(:@i) + + C2.new.instance_variable_get(:@f) + C2.new.instance_variable_get(:@g) + C2.new.instance_variable_get(:@h) + C2.new.instance_variable_get(:@i) + + C3.new.instance_variable_get(:@f) + C3.new.instance_variable_get(:@g) + C3.new.instance_variable_get(:@h) + C3.new.instance_variable_get(:@i) + return +end + +# Now, check that class variables work as expected. + +class D1 + @@d1_v = 0 + + def self.set_x + @@d1_x = @@d1_v + return + end + + class << self + def set_y + @@d1_y = @@d1_v + end + end +end + +class D2 < D1 + def self.get + @@d2_x = @@d1_v + @@d1_x + @@d1_y + @@d1_z + return + end +end + +class D3 < D2 + def self.get_2 + @@d1_v + @@d1_x + @@d1_y + @@d1_z + @@d2_x + return + end +end + +def f + D2.class_variable_get(:@@d1_v) + D2.class_variable_get(:@@d1_x) + D2.class_variable_get(:@@d2_x) + D2.class_variable_get(:@@d1_y) + D2.class_variable_get(:@@d1_z) + + D3.class_variable_get(:@@d1_v) + D3.class_variable_get(:@@d1_x) + D3.class_variable_get(:@@d2_x) + D3.class_variable_get(:@@d1_y) + D3.class_variable_get(:@@d1_z) + return +end + +# Class instance variables are not inherited. + +class E1 + @x = 0 + + def self.set_x + @x = @y + return + end + + def self.set_y + @y = 10 + return + end +end + +class E2 < E1 + @x = 0 + + def self.set_x_2 + @x = @y + return + end + + def self.set_y_2 + @y = 10 + end +end diff --git a/test/scip/testdata/field_inheritance.snapshot.rb b/test/scip/testdata/field_inheritance.snapshot.rb new file mode 100644 index 000000000..e28d8a717 --- /dev/null +++ b/test/scip/testdata/field_inheritance.snapshot.rb @@ -0,0 +1,256 @@ + # typed: true + + # First, check that instance variables are propagated through + # the inheritance chain. + + class C1 +# ^^ definition [..] C1# + attr_accessor :h +# ^ definition [..] C1#h(). +# ^ definition [..] C1#`h=`(). + attr_accessor :i +# ^ definition [..] C1#i(). +# ^ definition [..] C1#`i=`(). + + def set_ivar +# ^^^^^^^^ definition [..] C1#set_ivar(). + @f = 1 +# ^^ definition [..] C1#`@f`. + return + end + end + + class C2 < C1 +# ^^ definition [..] C2# +# ^^ definition [..] C1# + def get_inherited_ivar +# ^^^^^^^^^^^^^^^^^^ definition [..] C2#get_inherited_ivar(). + return @f + @h +# ^^ reference [..] C1#`@f`. +# ^^ reference [..] C1#`@h`. + end + + def set_inherited_ivar +# ^^^^^^^^^^^^^^^^^^ definition [..] C2#set_inherited_ivar(). + @f = 10 +# ^^ definition [..] C1#`@f`. + return + end + + def set_new_ivar +# ^^^^^^^^^^^^ definition [..] C2#set_new_ivar(). + @g = 1 +# ^^ definition [..] C2#`@g`. + return + end + + def get_new_ivar +# ^^^^^^^^^^^^ definition [..] C2#get_new_ivar(). + return @g +# ^^^^^^^^^ reference [..] C2#`@g`. + end + end + + class C3 < C2 +# ^^ definition [..] C3# +# ^^ definition [..] C2# + def refs +# ^^^^ definition [..] C3#refs(). + @f = @g + @i +# ^^ definition [..] C1#`@f`. +# ^^ reference [..] C2#`@g`. +# ^^ reference [..] C1#`@i`. + return + end + end + + def c_check +# ^^^^^^^ definition [..] Object#c_check(). + C1.new.instance_variable_get(:@f) +# ^^ reference [..] C1# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C1.new.instance_variable_get(:@h) +# ^^ reference [..] C1# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C1.new.instance_variable_get(:@i) +# ^^ reference [..] C1# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + + C2.new.instance_variable_get(:@f) +# ^^ reference [..] C2# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C2.new.instance_variable_get(:@g) +# ^^ reference [..] C2# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C2.new.instance_variable_get(:@h) +# ^^ reference [..] C2# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C2.new.instance_variable_get(:@i) +# ^^ reference [..] C2# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + + C3.new.instance_variable_get(:@f) +# ^^ reference [..] C3# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C3.new.instance_variable_get(:@g) +# ^^ reference [..] C3# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C3.new.instance_variable_get(:@h) +# ^^ reference [..] C3# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + C3.new.instance_variable_get(:@i) +# ^^ reference [..] C3# +# ^^^ reference [..] Class#new(). +# ^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#instance_variable_get(). + return + end + + # Now, check that class variables work as expected. + + class D1 +# ^^ definition [..] D1# + @@d1_v = 0 +# ^^^^^^ definition [..] ``#`@@d1_v`. + + def self.set_x +# ^^^^^ definition [..] ``#set_x(). + @@d1_x = @@d1_v +# ^^^^^^ definition [..] ``#`@@d1_x`. +# ^^^^^^ reference [..] ``#`@@d1_v`. + return + end + + class << self + def set_y +# ^^^^^ definition [..] ``#set_y(). + @@d1_y = @@d1_v +# ^^^^^^ definition [..] ``#`@@d1_y`. +# ^^^^^^^^^^^^^^^ reference [..] ``#`@@d1_y`. +# ^^^^^^ reference [..] ``#`@@d1_v`. + end + end + end + + class D2 < D1 +# ^^ definition [..] D2# +# ^^ definition [..] D1# + def self.get +# ^^^ definition [..] ``#get(). + @@d2_x = @@d1_v + @@d1_x +# ^^^^^^ definition [..] ``#`@@d2_x`. +# ^^^^^^ reference [..] ``#`@@d1_v`. +# ^^^^^^ reference [..] ``#`@@d1_x`. + @@d1_y + @@d1_z +# ^^^^^^ reference [..] ``#`@@d1_y`. +# ^^^^^^ reference [..] ``#`@@d1_z`. + return + end + end + + class D3 < D2 +# ^^ definition [..] D3# +# ^^ definition [..] D2# + def self.get_2 +# ^^^^^ definition [..] ``#get_2(). + @@d1_v + @@d1_x +# ^^^^^^ reference [..] ``#`@@d1_v`. +# ^^^^^^ reference [..] ``#`@@d1_x`. + @@d1_y + @@d1_z +# ^^^^^^ reference [..] ``#`@@d1_y`. +# ^^^^^^ reference [..] ``#`@@d1_z`. + @@d2_x +# ^^^^^^ reference [..] ``#`@@d2_x`. + return + end + end + + def f +# ^ definition [..] Object#f(). + D2.class_variable_get(:@@d1_v) +# ^^ reference [..] D2# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D2.class_variable_get(:@@d1_x) +# ^^ reference [..] D2# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D2.class_variable_get(:@@d2_x) +# ^^ reference [..] D2# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D2.class_variable_get(:@@d1_y) +# ^^ reference [..] D2# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D2.class_variable_get(:@@d1_z) +# ^^ reference [..] D2# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + + D3.class_variable_get(:@@d1_v) +# ^^ reference [..] D3# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D3.class_variable_get(:@@d1_x) +# ^^ reference [..] D3# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D3.class_variable_get(:@@d2_x) +# ^^ reference [..] D3# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D3.class_variable_get(:@@d1_y) +# ^^ reference [..] D3# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + D3.class_variable_get(:@@d1_z) +# ^^ reference [..] D3# +# ^^^^^^^^^^^^^^^^^^ reference [..] Module#class_variable_get(). + return + end + + # Class instance variables are not inherited. + + class E1 +# ^^ definition [..] E1# + @x = 0 +# ^^ definition [..] ``#`@x`. + + def self.set_x +# ^^^^^ definition [..] ``#set_x(). + @x = @y +# ^^ definition [..] ``#`@x`. +# ^^ reference [..] ``#`@y`. + return + end + + def self.set_y +# ^^^^^ definition [..] ``#set_y(). + @y = 10 +# ^^ definition [..] ``#`@y`. + return + end + end + + class E2 < E1 +# ^^ definition [..] E2# +# ^^ definition [..] E1# + @x = 0 +# ^^ definition [..] ``#`@x`. + + def self.set_x_2 +# ^^^^^^^ definition [..] ``#set_x_2(). + @x = @y +# ^^ definition [..] ``#`@x`. +# ^^ reference [..] ``#`@y`. + return + end + + def self.set_y_2 +# ^^^^^^^ definition [..] ``#set_y_2(). + @y = 10 +# ^^ definition [..] ``#`@y`. +# ^^^^^^^ reference [..] ``#`@y`. + end + end diff --git a/test/scip/testdata/fields_and_attrs.snapshot.rb b/test/scip/testdata/fields_and_attrs.snapshot.rb index dfb2162be..ac6284252 100644 --- a/test/scip/testdata/fields_and_attrs.snapshot.rb +++ b/test/scip/testdata/fields_and_attrs.snapshot.rb @@ -78,8 +78,8 @@ def self.m1 def m2 # ^^ definition [..] N#m2(). @@b = @@a -# ^^^ definition [..] N#`@@b`. -# ^^^ reference [..] N#`@@a`. +# ^^^ definition [..] ``#`@@b`. +# ^^^ reference [..] ``#`@@a`. return end diff --git a/test/scip/testdata/hoverdocs.snapshot.rb b/test/scip/testdata/hoverdocs.snapshot.rb index 8436c37f5..f4c40d348 100644 --- a/test/scip/testdata/hoverdocs.snapshot.rb +++ b/test/scip/testdata/hoverdocs.snapshot.rb @@ -296,12 +296,12 @@ def p1 # | @x (T.untyped) # | ``` @@y = 10 -# ^^^ definition [..] K1#`@@y`. +# ^^^ definition [..] ``#`@@y`. # documentation # | ```ruby # | @@y (T.untyped) # | ``` -# ^^^^^^^^ reference [..] K1#`@@y`. +# ^^^^^^^^ reference [..] ``#`@@y`. # override_documentation # | ```ruby # | @@y (Integer(10)) @@ -369,13 +369,13 @@ def p1 # documentation # | overrides K1's p1 @x = 20 -# ^^ definition [..] K2#`@x`. +# ^^ definition [..] K1#`@x`. # documentation # | ```ruby # | @x (T.untyped) # | ``` @@y = 20 -# ^^^ definition [..] K2#`@@y`. +# ^^^ definition [..] ``#`@@y`. # documentation # | ```ruby # | @@y (T.untyped) @@ -384,7 +384,7 @@ def p1 # ^^ reference [..] K2#`@z`. # ^^ reference (write) [..] K2#`@z`. # ^^^^^^^^ reference [..] K2#`@z`. -# ^^ reference [..] K2#`@x`. +# ^^ reference [..] K1#`@x`. # override_documentation # | ```ruby # | @x (Integer(20)) diff --git a/test/scip/testdata/inheritance.snapshot.rb b/test/scip/testdata/inheritance.snapshot.rb index 3560b8ce1..62c35269d 100644 --- a/test/scip/testdata/inheritance.snapshot.rb +++ b/test/scip/testdata/inheritance.snapshot.rb @@ -61,7 +61,7 @@ class Z3 < Z1 def read_f_plus_1? # ^^^^^^^^^^^^^^ definition [..] Z3#`read_f_plus_1?`(). @f + 1 -# ^^ reference [..] Z3#`@f`. +# ^^ reference [..] Z1#`@f`. end end @@ -80,8 +80,8 @@ def write_f_plus_1(a) # ^^^^^^^ reference [..] Z1#write_f(). # ^ reference local 1~#3337417690 @f = read_f_plus_1? -# ^^ definition [..] Z4#`@f`. -# ^^^^^^^^^^^^^^^^^^^ reference [..] Z4#`@f`. +# ^^ definition [..] Z1#`@f`. +# ^^^^^^^^^^^^^^^^^^^ reference [..] Z1#`@f`. # ^^^^^^^^^^^^^^ reference [..] Z3#`read_f_plus_1?`(). end end diff --git a/test/scip/testdata/type_change.snapshot.rb b/test/scip/testdata/type_change.snapshot.rb index 18a07b16c..3db241301 100644 --- a/test/scip/testdata/type_change.snapshot.rb +++ b/test/scip/testdata/type_change.snapshot.rb @@ -156,7 +156,7 @@ def change_type(b) # | @f (T.untyped) # | ``` @@g = nil -# ^^^ definition [..] C#`@@g`. +# ^^^ definition [..] ``#`@@g`. # documentation # | ```ruby # | @@g (T.untyped) @@ -175,7 +175,7 @@ def change_type(b) # | @f (Integer(1)) # | ``` @@g = 1 -# ^^^ reference (write) [..] C#`@@g`. +# ^^^ reference (write) [..] ``#`@@g`. # override_documentation # | ```ruby # | @@g (Integer(1)) @@ -199,7 +199,7 @@ def change_type(b) # | @f (String("f")) # | ``` @@g = 'g' -# ^^^ reference (write) [..] C#`@@g`. +# ^^^ reference (write) [..] ``#`@@g`. # override_documentation # | ```ruby # | @@g (String("g")) @@ -246,48 +246,48 @@ def change_type(b) # ^ reference [..] BasicObject#`!`(). # ^ reference local 1~#2066187318 @f = 1 -# ^^ definition [..] D#`@f`. +# ^^ definition [..] C#`@f`. # documentation # | ```ruby # | @f (T.untyped) # | ``` @@g = 1 -# ^^^ definition [..] D#`@@g`. +# ^^^ definition [..] ``#`@@g`. # documentation # | ```ruby # | @@g (T.untyped) # | ``` @k = 1 -# ^^ definition [..] D#`@k`. +# ^^ definition [..] C#`@k`. # documentation # | ```ruby # | @k (T.untyped) # | ``` -# ^^^^^^ reference [..] D#`@k`. +# ^^^^^^ reference [..] C#`@k`. # override_documentation # | ```ruby # | @k (Integer(1)) # | ``` else @f = 'f' -# ^^ definition [..] D#`@f`. +# ^^ definition [..] C#`@f`. # documentation # | ```ruby # | @f (T.untyped) # | ``` @@g = 'g' -# ^^^ definition [..] D#`@@g`. +# ^^^ definition [..] ``#`@@g`. # documentation # | ```ruby # | @@g (T.untyped) # | ``` @k = 'k' -# ^^ definition [..] D#`@k`. +# ^^ definition [..] C#`@k`. # documentation # | ```ruby # | @k (T.untyped) # | ``` -# ^^^^^^^^ reference [..] D#`@k`. +# ^^^^^^^^ reference [..] C#`@k`. # override_documentation # | ```ruby # | @k (String("k"))