@@ -96,27 +96,59 @@ class Modifiable extends Declaration, @modifiable {
96
96
/** Holds if this declaration is `async`. */
97
97
predicate isAsync() { this.hasModifier("async") }
98
98
99
+ private predicate isReallyPrivate() {
100
+ this.isPrivate() and
101
+ not this.isProtected() and
102
+ // Rare case when a member is defined with the same name in multiple assemblies with different visibility
103
+ not this.isPublic()
104
+ }
105
+
99
106
/**
100
- * Holds if this declaration is effectively `private` (either directly or
101
- * because one of the enclosing types is `private`).
107
+ * Holds if this declaration is effectively `private`. A declaration is considered
108
+ * effectively `private` if it can only be referenced from
109
+ * - the declaring and its nested types, similarly to `private` declarations, and
110
+ * - the enclosing types.
111
+ *
112
+ * Note that explicit interface implementations are also considered effectively
113
+ * `private` if the implemented interface is itself effectively `private`. Finally,
114
+ * `private protected` members are not considered effectively `private`, because
115
+ * they can be overriden within the declaring assembly.
102
116
*/
103
117
predicate isEffectivelyPrivate() {
104
- this.isPrivate() or
105
- this.getDeclaringType+().isPrivate()
118
+ this.isReallyPrivate() or
119
+ this.getDeclaringType+().(Modifiable).isReallyPrivate() or
120
+ this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyPrivate()
121
+ }
122
+
123
+ private predicate isReallyInternal() {
124
+ (
125
+ this.isInternal() and not this.isProtected()
126
+ or
127
+ this.isPrivate() and this.isProtected()
128
+ ) and
129
+ // Rare case when a member is defined with the same name in multiple assemblies with different visibility
130
+ not this.isPublic()
106
131
}
107
132
108
133
/**
109
- * Holds if this declaration is effectively `internal` (either directly or
110
- * because one of the enclosing types is `internal`).
134
+ * Holds if this declaration is effectively `internal`. A declaration is considered
135
+ * effectively `internal` if it can only be referenced from the declaring assembly.
136
+ *
137
+ * Note that friend assemblies declared in `InternalsVisibleToAttribute` are not
138
+ * considered. Explicit interface implementations are also considered effectively
139
+ * `internal` if the implemented interface is itself effectively `internal`. Finally,
140
+ * `internal protected` members are not considered effectively `internal`, because
141
+ * they can be overriden outside the declaring assembly.
111
142
*/
112
143
predicate isEffectivelyInternal() {
113
- this.isInternal() or
114
- this.getDeclaringType+().isInternal()
144
+ this.isReallyInternal() or
145
+ this.getDeclaringType+().(Modifiable).isReallyInternal() or
146
+ this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyInternal()
115
147
}
116
148
117
149
/**
118
- * Holds if this declaration is effectively `public`, because it
119
- * and all enclosing types are `public` .
150
+ * Holds if this declaration is effectively `public`, meaning that it can be
151
+ * referenced outside the declaring assembly .
120
152
*/
121
153
predicate isEffectivelyPublic() { not isEffectivelyPrivate() and not isEffectivelyInternal() }
122
154
}
@@ -159,6 +191,11 @@ class Virtualizable extends Member, @virtualizable {
159
191
implementsExplicitInterface()
160
192
}
161
193
194
+ override predicate isPrivate() {
195
+ super.isPrivate() and
196
+ not implementsExplicitInterface()
197
+ }
198
+
162
199
/**
163
200
* Gets any interface this member explicitly implements; this only applies
164
201
* to members that can be declared on an interface, i.e. methods, properties,
0 commit comments