@@ -36,56 +36,119 @@ class MethodBase extends Callable, BodyStmt, Scope, TMethodBase {
36
36
result = BodyStmt .super .getAChild ( pred )
37
37
}
38
38
39
+ /**
40
+ * Holds if this method is public.
41
+ * Methods are public by default.
42
+ */
43
+ predicate isPublic ( ) { this .getVisibility ( ) = "public" }
44
+
39
45
/** Holds if this method is private. */
40
- predicate isPrivate ( ) { none ( ) }
46
+ predicate isPrivate ( ) { this .getVisibility ( ) = "private" }
47
+
48
+ /** Holds if this method is protected. */
49
+ predicate isProtected ( ) { this .getVisibility ( ) = "protected" }
50
+
51
+ /**
52
+ * Gets a string describing the visibility of this method.
53
+ * This is either 'public', 'private' or 'protected'.
54
+ */
55
+ string getVisibility ( ) {
56
+ result = getVisibilityModifier ( this ) .getVisibility ( )
57
+ or
58
+ not exists ( getVisibilityModifier ( this ) ) and result = "public"
59
+ }
41
60
}
42
61
43
62
/**
44
- * A method call which modifies another method in some way.
45
- * For example, `private :foo` makes the method `foo` private.
63
+ * Gets the visibility modifier that explicitly sets the visibility of method
64
+ * `m`.
65
+ *
66
+ * Examples:
67
+ * ```rb
68
+ * def f
69
+ * end
70
+ * private :f
71
+ *
72
+ * private def g
73
+ * end
74
+ * ```
46
75
*/
47
- private class MethodModifier extends MethodCall {
48
- /** Gets the name of the method that this call applies to. */
49
- Expr getMethodArgument ( ) { result = this .getArgument ( 0 ) }
76
+ private VisibilityModifier getExplicitVisibilityModifier ( Method m ) {
77
+ result .getMethodArgument ( ) = m
78
+ or
79
+ exists ( ModuleBase n , string name |
80
+ methodIsDeclaredIn ( m , n , name ) and
81
+ modifiesIn ( result , n , name )
82
+ )
83
+ }
50
84
51
- /** Holds if this call modifies a method with name `name` in namespace `n`. */
52
- pragma [ noinline]
53
- predicate modifiesMethod ( Namespace n , string name ) {
54
- this = n .getAStmt ( ) and
55
- [
56
- this .getMethodArgument ( ) .getConstantValue ( ) .getStringlikeValue ( ) ,
57
- this .getMethodArgument ( ) .( MethodBase ) .getName ( )
58
- ] = name
59
- }
85
+ /**
86
+ * Gets the visibility modifier that defines the visibility of method `m`, if
87
+ * any.
88
+ */
89
+ private VisibilityModifier getVisibilityModifier ( MethodBase mb ) {
90
+ mb =
91
+ any ( Method m |
92
+ result = getExplicitVisibilityModifier ( m )
93
+ or
94
+ not exists ( getExplicitVisibilityModifier ( m ) ) and
95
+ exists ( ModuleBase n , int methodPos | isDeclaredIn ( m , n , methodPos ) |
96
+ // The relevant visibility modifier is the closest call that occurs before
97
+ // the definition of `m` (typically this means higher up the file).
98
+ result =
99
+ max ( int modifierPos , VisibilityModifier modifier |
100
+ modifier .modifiesAmbientVisibility ( ) and
101
+ isDeclaredIn ( modifier , n , modifierPos ) and
102
+ modifierPos < methodPos
103
+ |
104
+ modifier order by modifierPos
105
+ )
106
+ )
107
+ )
108
+ or
109
+ mb =
110
+ any ( SingletonMethod m |
111
+ result .getMethodArgument ( ) = m
112
+ or
113
+ exists ( ModuleBase n , string name |
114
+ methodIsDeclaredIn ( m , n , name ) and
115
+ modifiesIn ( result , n , name )
116
+ )
117
+ )
60
118
}
61
119
62
- /** A call to `private` or `private_class_method`. */
63
- private class Private extends MethodModifier {
64
- private Namespace namespace ;
65
- private int position ;
120
+ /**
121
+ * A method call that sets the visibility of other methods.
122
+ * For example, `private :foo` makes the method `foo` private.
123
+ */
124
+ private class VisibilityModifier extends MethodCall {
125
+ VisibilityModifier ( ) {
126
+ this .getMethodName ( ) =
127
+ [ "public" , "private" , "protected" , "public_class_method" , "private_class_method" ]
128
+ }
66
129
67
- Private ( ) { this .getMethodName ( ) = "private" and namespace .getStmt ( position ) = this }
130
+ /** Gets the name of the method that this call applies to. */
131
+ Expr getMethodArgument ( ) { result = this .getArgument ( 0 ) }
68
132
69
- override predicate modifiesMethod ( Namespace n , string name ) {
70
- n = namespace and
71
- (
72
- // def foo
73
- // ...
74
- // private :foo
75
- super .modifiesMethod ( n , name )
76
- or
77
- // private
78
- // ...
79
- // def foo
80
- not exists ( this .getMethodArgument ( ) ) and
81
- exists ( MethodBase m , int i | n .getStmt ( i ) = m and m .getName ( ) = name and i > position )
82
- )
133
+ /**
134
+ * Holds if this modifier changes the "ambient" visibility - i.e. the default
135
+ * visibility of any subsequent method definitions.
136
+ */
137
+ predicate modifiesAmbientVisibility ( ) {
138
+ this .getMethodName ( ) = [ "public" , "private" , "protected" ] and
139
+ this .getNumberOfArguments ( ) = 0
83
140
}
84
- }
85
141
86
- /** A call to `private_class_method`. */
87
- private class PrivateClassMethod extends MethodModifier {
88
- PrivateClassMethod ( ) { this .getMethodName ( ) = "private_class_method" }
142
+ /** Gets the visibility set by this modifier. */
143
+ string getVisibility ( ) {
144
+ this .getMethodName ( ) = [ "public" , "public_class_method" ] and result = "public"
145
+ or
146
+ this .getMethodName ( ) = [ "private" , "private_class_method" ] and
147
+ result = "private"
148
+ or
149
+ this .getMethodName ( ) = "protected" and
150
+ result = "protected"
151
+ }
89
152
}
90
153
91
154
/** A normal method. */
@@ -139,29 +202,49 @@ class Method extends MethodBase, TMethod {
139
202
* end
140
203
* ```
141
204
*/
142
- override predicate isPrivate ( ) {
143
- exists ( Namespace n , string name |
144
- any ( Private p ) .modifiesMethod ( n , name ) and
145
- isDeclaredIn ( this , n , name )
146
- )
147
- or
148
- // Top-level methods are private members of the Object class
149
- this .getEnclosingModule ( ) instanceof Toplevel
150
- }
205
+ override predicate isPrivate ( ) { super .isPrivate ( ) }
151
206
152
207
final override Parameter getParameter ( int n ) {
153
208
toGenerated ( result ) = g .getParameters ( ) .getChild ( n )
154
209
}
155
210
156
211
final override string toString ( ) { result = this .getName ( ) }
212
+
213
+ override string getVisibility ( ) {
214
+ result = getVisibilityModifier ( this ) .getVisibility ( )
215
+ or
216
+ this .getEnclosingModule ( ) instanceof Toplevel and
217
+ not exists ( getVisibilityModifier ( this ) ) and
218
+ result = "private"
219
+ or
220
+ not this .getEnclosingModule ( ) instanceof Toplevel and
221
+ not exists ( getVisibilityModifier ( this ) ) and
222
+ result = "public"
223
+ }
224
+ }
225
+
226
+ pragma [ nomagic]
227
+ private predicate modifiesIn ( VisibilityModifier vm , ModuleBase n , string name ) {
228
+ n = vm .getEnclosingModule ( ) and
229
+ name = vm .getMethodArgument ( ) .getConstantValue ( ) .getStringlikeValue ( )
157
230
}
158
231
159
232
/**
160
- * Holds if the method `m` has name `name` and is declared in namespace `n`.
233
+ * Holds if statement `s` is declared in namespace `n` at position `pos `.
161
234
*/
162
- pragma [ noinline]
163
- private predicate isDeclaredIn ( MethodBase m , Namespace n , string name ) {
164
- n = m .getEnclosingModule ( ) and name = m .getName ( )
235
+ pragma [ nomagic]
236
+ private predicate isDeclaredIn ( Stmt s , ModuleBase n , int pos ) {
237
+ n = s .getEnclosingModule ( ) and
238
+ n .getStmt ( pos ) = s
239
+ }
240
+
241
+ /**
242
+ * Holds if method `m` with name `name` is declared in namespace `n`.
243
+ */
244
+ pragma [ nomagic]
245
+ private predicate methodIsDeclaredIn ( MethodBase m , ModuleBase n , string name ) {
246
+ isDeclaredIn ( m , n , _) and
247
+ name = m .getName ( )
165
248
}
166
249
167
250
/** A singleton method. */
@@ -216,12 +299,7 @@ class SingletonMethod extends MethodBase, TSingletonMethod {
216
299
* end
217
300
* ```
218
301
*/
219
- override predicate isPrivate ( ) {
220
- exists ( Namespace n , string name |
221
- any ( PrivateClassMethod p ) .modifiesMethod ( n , name ) and
222
- isDeclaredIn ( this , n , name )
223
- )
224
- }
302
+ override predicate isPrivate ( ) { super .isPrivate ( ) }
225
303
}
226
304
227
305
/**
0 commit comments