Include instance variables in jump-to-def query

By convention, instance variables are considered to be "defined" in the
`#initialize` method of their containing class. If an instance variable
is written to in `#initialize` and then read elsewhere in the program,
we will point from the read to the write. If it is not written to in
`#initialize` then we won't provide any jump-to-definition information
for it.
This commit is contained in:
Harry Maclean
2021-08-05 14:59:45 +01:00
parent a16cd8967b
commit e84ebe2b94
4 changed files with 32 additions and 7 deletions

View File

@@ -168,6 +168,12 @@ class InstanceVariableAccess extends VariableAccess, TInstanceVariableAccess {
final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" }
}
/** An access to an instance variable where the value is updated. */
class InstanceVariableWriteAccess extends InstanceVariableAccess, VariableWriteAccess { }
/** An access to an instance variable where the value is read. */
class InstanceVariableReadAccess extends InstanceVariableAccess, VariableReadAccess { }
/** An access to a class variable. */
class ClassVariableAccess extends VariableAccess, TClassVariableAccess {
final override string getAPrimaryQlClass() { result = "ClassVariableAccess" }

View File

@@ -7,7 +7,7 @@
/*
* TODO:
* - instance and class variables
* - class variables
* - should `Foo.new` point to `Foo#initialize`?
*/
@@ -22,6 +22,8 @@ where
LocalMethodLoc(src, target) = loc and kind = "method"
or
LocalVariableLoc(src, target) = loc and kind = "variable"
or
InstanceVariableLoc(src, target) = loc and kind = "instance variable"
select src, target, kind
/**
@@ -46,6 +48,18 @@ newtype DefLoc =
not read.getLocation() = write.getLocation() and
not read.isSynthesized()
)
} or
/** An instance variable */
InstanceVariableLoc(InstanceVariableReadAccess read, InstanceVariableWriteAccess write) {
/*
* We consider instance variables to be "defined" in the initialize method of their enclosing class.
* If that method doesn't exist, we won't provide any jump-to-def information for the instance variable.
*/
exists(Method m |
m.getAChild+() = write and
m.getName() = "initialize" and
write.getVariable() = read.getVariable()
)
}

View File

@@ -1,9 +1,10 @@
| Definitions.rb:4:7:4:9 | call to g | Definitions.rb:7:5:9:7 | g | method |
| Definitions.rb:8:7:8:7 | x | Definitions.rb:7:11:7:11 | x | variable |
| Definitions.rb:12:7:12:7 | call to f | Definitions.rb:3:5:5:7 | f | method |
| Definitions.rb:21:7:21:7 | y | Definitions.rb:20:10:20:10 | y | variable |
| Definitions.rb:25:7:25:7 | A | Definitions.rb:1:1:15:3 | A | constant |
| Definitions.rb:25:7:25:10 | B | Definitions.rb:2:3:14:5 | B | constant |
| Definitions.rb:25:18:25:18 | y | Definitions.rb:24:11:24:11 | y | variable |
| Definitions.rb:31:1:31:1 | C | Definitions.rb:17:1:29:3 | C | constant |
| Definitions.rb:31:1:31:4 | D | Definitions.rb:18:3:28:5 | D | constant |
| Definitions.rb:22:7:22:7 | y | Definitions.rb:21:10:21:10 | y | variable |
| Definitions.rb:26:7:26:7 | A | Definitions.rb:1:1:15:3 | A | constant |
| Definitions.rb:26:7:26:10 | B | Definitions.rb:2:3:14:5 | B | constant |
| Definitions.rb:26:18:26:18 | y | Definitions.rb:25:11:25:11 | y | variable |
| Definitions.rb:29:7:29:8 | @e | Definitions.rb:20:7:20:8 | @e | instance variable |
| Definitions.rb:35:1:35:1 | C | Definitions.rb:17:1:33:3 | C | constant |
| Definitions.rb:35:1:35:4 | D | Definitions.rb:18:3:32:5 | D | constant |

View File

@@ -17,6 +17,7 @@ end
module C
class D
def initialize
@e = 1
x, y = [1, 2]
y
end
@@ -24,6 +25,9 @@ module C
def h y
A::B.new.g y
UnknownClass.some_method
@f = 2
@e
@f
end
end
end