@@ -52,22 +52,22 @@ private DataFlow::Node fileInstance() {
52
52
)
53
53
}
54
54
55
- private string ioFileReaderClassMethodName ( ) {
55
+ private string ioReaderClassMethodName ( ) {
56
56
result = [ "binread" , "foreach" , "read" , "readlines" , "try_convert" ]
57
57
}
58
58
59
- private string ioFileReaderInstanceMethodName ( ) {
59
+ private string ioReaderInstanceMethodName ( ) {
60
60
result =
61
61
[
62
62
"getbyte" , "getc" , "gets" , "pread" , "read" , "read_nonblock" , "readbyte" , "readchar" ,
63
63
"readline" , "readlines" , "readpartial" , "sysread"
64
64
]
65
65
}
66
66
67
- private string ioFileReaderMethodName ( boolean classMethodCall ) {
68
- classMethodCall = true and result = ioFileReaderClassMethodName ( )
67
+ private string ioReaderMethodName ( string receiverKind ) {
68
+ receiverKind = "class" and result = ioReaderClassMethodName ( )
69
69
or
70
- classMethodCall = false and result = ioFileReaderInstanceMethodName ( )
70
+ receiverKind = "instance" and result = ioReaderInstanceMethodName ( )
71
71
}
72
72
73
73
/**
@@ -110,72 +110,81 @@ module IO {
110
110
*
111
111
* This class includes reads both from shell commands and reads from the
112
112
* filesystem. For working with filesystem accesses specifically, see
113
- * `IOFileReader ` or the `FileSystemReadAccess` concept.
113
+ * `FileReader ` or the `FileSystemReadAccess` concept.
114
114
*/
115
115
class IOReader extends DataFlow:: CallNode {
116
- private boolean classMethodCall ;
117
- private string api ;
116
+ private string receiverKind ;
118
117
119
118
IOReader ( ) {
120
- // Class methods
121
- api = [ "File" , "IO" ] and
122
- classMethodCall = true and
123
- this = API:: getTopLevelMember ( api ) .getAMethodCall ( ioFileReaderMethodName ( classMethodCall ) )
119
+ // `IO` class method calls
120
+ receiverKind = "class" and
121
+ this = API:: getTopLevelMember ( "IO" ) .getAMethodCall ( ioReaderMethodName ( receiverKind ) )
124
122
or
125
- // IO instance methods
126
- classMethodCall = false and
127
- api = "IO" and
123
+ // `IO` instance method calls
124
+ receiverKind = "instance" and
128
125
exists ( IOInstanceStrict ii |
129
126
this .getReceiver ( ) = ii and
130
- this .getMethodName ( ) = ioFileReaderMethodName ( classMethodCall )
131
- )
132
- or
133
- // File instance methods
134
- classMethodCall = false and
135
- api = "File" and
136
- exists ( File:: FileInstance fi |
137
- this .getReceiver ( ) = fi and
138
- this .getMethodName ( ) = ioFileReaderMethodName ( classMethodCall )
127
+ this .getMethodName ( ) = ioReaderMethodName ( receiverKind )
139
128
)
140
129
// TODO: enumeration style methods such as `each`, `foreach`, etc.
141
130
}
142
131
143
132
/**
144
- * Returns the most specific core class used for this read, `IO` or `File`
133
+ * Gets a string representation of the receiver kind, either "class" or "instance".
145
134
*/
146
- string getAPI ( ) { result = api }
147
-
148
- predicate isClassMethodCall ( ) { classMethodCall = true }
135
+ string getReceiverKind ( ) { result = receiverKind }
149
136
}
150
137
151
138
/**
152
139
* A `DataFlow::CallNode` that reads data from the filesystem using the `IO`
153
- * class . For example, the `IO.read call in:
140
+ * or `File` classes . For example, the `IO.read call in:
154
141
*
155
142
* ```rb
156
143
* IO.read("foo.txt")
157
144
* ```
158
145
*
159
146
* reads the file `foo.txt` and returns its contents as a string.
160
147
*/
161
- class IOFileReader extends IOReader , FileSystemReadAccess:: Range {
162
- IOFileReader ( ) {
163
- this .getAPI ( ) = "File"
164
- or
165
- this .isClassMethodCall ( ) and
166
- // Assume that calls that don't invoke shell commands will instead
167
- // read from a file.
148
+ class FileReader extends DataFlow:: CallNode , FileSystemReadAccess:: Range {
149
+ private string receiverKind ;
150
+ private string api ;
151
+
152
+ FileReader ( ) {
153
+ // A viable `IOReader` that could feasibly read from the filesystem
154
+ api = "IO" and
155
+ receiverKind = this .( IOReader ) .getReceiverKind ( ) and
168
156
not pathArgSpawnsSubprocess ( this .getArgument ( 0 ) .asExpr ( ) .getExpr ( ) )
157
+ or
158
+ api = "File" and
159
+ (
160
+ // `File` class method calls
161
+ receiverKind = "class" and
162
+ this = API:: getTopLevelMember ( api ) .getAMethodCall ( ioReaderMethodName ( receiverKind ) )
163
+ or
164
+ // `File` instance method calls
165
+ receiverKind = "instance" and
166
+ exists ( File:: FileInstance fi |
167
+ this .getReceiver ( ) = fi and
168
+ this .getMethodName ( ) = ioReaderMethodName ( receiverKind )
169
+ )
170
+ )
171
+ // TODO: enumeration style methods such as `each`, `foreach`, etc.
169
172
}
170
173
171
- // TODO: can we infer a path argument for instance method calls?
174
+ // TODO: Currently this only handles class method calls.
175
+ // Can we infer a path argument for instance method calls?
172
176
// e.g. by tracing back to the instantiation of that instance
173
177
override DataFlow:: Node getAPathArgument ( ) {
174
- result = this .getArgument ( 0 ) and this . isClassMethodCall ( )
178
+ result = this .getArgument ( 0 ) and receiverKind = "class"
175
179
}
176
180
177
181
// This class represents calls that return data
178
182
override DataFlow:: Node getADataNode ( ) { result = this }
183
+
184
+ /**
185
+ * Returns the most specific core class used for this read, `IO` or `File`
186
+ */
187
+ string getAPI ( ) { result = api }
179
188
}
180
189
}
181
190
@@ -210,7 +219,7 @@ module File {
210
219
* puts f.read()
211
220
* ```
212
221
*/
213
- class FileModuleReader extends IO:: IOFileReader {
222
+ class FileModuleReader extends IO:: FileReader {
214
223
FileModuleReader ( ) { this .getAPI ( ) = "File" }
215
224
}
216
225
0 commit comments