@@ -36,56 +36,119 @@ class MethodBase extends Callable, BodyStmt, Scope, TMethodBase {
3636 result = BodyStmt .super .getAChild ( pred )
3737 }
3838
39+ /**
40+ * Holds if this method is public.
41+ * Methods are public by default.
42+ */
43+ predicate isPublic ( ) { this .getVisibility ( ) = "public" }
44+
3945 /** 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+ }
4160}
4261
4362/**
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+ * ```
4675 */
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+ }
5084
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+ )
60118}
61119
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+ }
66129
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 ) }
68132
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
83140 }
84- }
85141
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+ }
89152}
90153
91154/** A normal method. */
@@ -139,29 +202,49 @@ class Method extends MethodBase, TMethod {
139202 * end
140203 * ```
141204 */
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 ( ) }
151206
152207 final override Parameter getParameter ( int n ) {
153208 toGenerated ( result ) = g .getParameters ( ) .getChild ( n )
154209 }
155210
156211 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 ( )
157230}
158231
159232/**
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 `.
161234 */
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 ( )
165248}
166249
167250/** A singleton method. */
@@ -216,12 +299,7 @@ class SingletonMethod extends MethodBase, TSingletonMethod {
216299 * end
217300 * ```
218301 */
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 ( ) }
225303}
226304
227305/**
0 commit comments