@@ -52,22 +52,22 @@ private DataFlow::Node fileInstance() {
5252 )
5353}
5454
55- private string ioFileReaderClassMethodName ( ) {
55+ private string ioReaderClassMethodName ( ) {
5656 result = [ "binread" , "foreach" , "read" , "readlines" , "try_convert" ]
5757}
5858
59- private string ioFileReaderInstanceMethodName ( ) {
59+ private string ioReaderInstanceMethodName ( ) {
6060 result =
6161 [
6262 "getbyte" , "getc" , "gets" , "pread" , "read" , "read_nonblock" , "readbyte" , "readchar" ,
6363 "readline" , "readlines" , "readpartial" , "sysread"
6464 ]
6565}
6666
67- private string ioFileReaderMethodName ( boolean classMethodCall ) {
68- classMethodCall = true and result = ioFileReaderClassMethodName ( )
67+ private string ioReaderMethodName ( string receiverKind ) {
68+ receiverKind = "class" and result = ioReaderClassMethodName ( )
6969 or
70- classMethodCall = false and result = ioFileReaderInstanceMethodName ( )
70+ receiverKind = "instance" and result = ioReaderInstanceMethodName ( )
7171}
7272
7373/**
@@ -110,72 +110,81 @@ module IO {
110110 *
111111 * This class includes reads both from shell commands and reads from the
112112 * filesystem. For working with filesystem accesses specifically, see
113- * `IOFileReader ` or the `FileSystemReadAccess` concept.
113+ * `FileReader ` or the `FileSystemReadAccess` concept.
114114 */
115115 class IOReader extends DataFlow:: CallNode {
116- private boolean classMethodCall ;
117- private string api ;
116+ private string receiverKind ;
118117
119118 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 ) )
124122 or
125- // IO instance methods
126- classMethodCall = false and
127- api = "IO" and
123+ // `IO` instance method calls
124+ receiverKind = "instance" and
128125 exists ( IOInstanceStrict ii |
129126 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 )
139128 )
140129 // TODO: enumeration style methods such as `each`, `foreach`, etc.
141130 }
142131
143132 /**
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".
145134 */
146- string getAPI ( ) { result = api }
147-
148- predicate isClassMethodCall ( ) { classMethodCall = true }
135+ string getReceiverKind ( ) { result = receiverKind }
149136 }
150137
151138 /**
152139 * 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:
154141 *
155142 * ```rb
156143 * IO.read("foo.txt")
157144 * ```
158145 *
159146 * reads the file `foo.txt` and returns its contents as a string.
160147 */
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
168156 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.
169172 }
170173
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?
172176 // e.g. by tracing back to the instantiation of that instance
173177 override DataFlow:: Node getAPathArgument ( ) {
174- result = this .getArgument ( 0 ) and this . isClassMethodCall ( )
178+ result = this .getArgument ( 0 ) and receiverKind = "class"
175179 }
176180
177181 // This class represents calls that return data
178182 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 }
179188 }
180189}
181190
@@ -210,7 +219,7 @@ module File {
210219 * puts f.read()
211220 * ```
212221 */
213- class FileModuleReader extends IO:: IOFileReader {
222+ class FileModuleReader extends IO:: FileReader {
214223 FileModuleReader ( ) { this .getAPI ( ) = "File" }
215224 }
216225
0 commit comments