Skip to content

Commit eaa69df

Browse files
authored
Merge pull request github#6084 from tamasvajk/feature/effective-publicness
C#: Fix isEffectively* visibility predicates
2 parents 75d5fe6 + 28ef0e8 commit eaa69df

File tree

5 files changed

+76
-10
lines changed

5 files changed

+76
-10
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The `isEffectivelyPrivate`, `isEffectivelyInternal` and `isEffectivelyPublic` predicates on `Modifiable` have been reworked to handle `private protected` and `internal protected` visibilities and explicitly implemented interfaces.

csharp/ql/src/semmle/code/csharp/Member.qll

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,27 +96,59 @@ class Modifiable extends Declaration, @modifiable {
9696
/** Holds if this declaration is `async`. */
9797
predicate isAsync() { this.hasModifier("async") }
9898

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+
99106
/**
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.
102116
*/
103117
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()
106131
}
107132

108133
/**
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.
111142
*/
112143
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()
115147
}
116148

117149
/**
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.
120152
*/
121153
predicate isEffectivelyPublic() { not isEffectivelyPrivate() and not isEffectivelyInternal() }
122154
}
@@ -159,6 +191,11 @@ class Virtualizable extends Member, @virtualizable {
159191
implementsExplicitInterface()
160192
}
161193

194+
override predicate isPrivate() {
195+
super.isPrivate() and
196+
not implementsExplicitInterface()
197+
}
198+
162199
/**
163200
* Gets any interface this member explicitly implements; this only applies
164201
* to members that can be declared on an interface, i.e. methods, properties,

csharp/ql/test/library-tests/modifiers/Effectively.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@
2424
| Modifiers.cs:60:22:60:23 | I1 | public |
2525
| Modifiers.cs:62:14:62:15 | M1 | public |
2626
| Modifiers.cs:63:14:63:15 | M2 | public |
27+
| Modifiers.cs:68:14:68:15 | M1 | internal |
28+
| Modifiers.cs:71:18:71:19 | C2 | public |
29+
| Modifiers.cs:73:17:73:18 | M1 | internal |
30+
| Modifiers.cs:75:32:75:33 | M2 | internal |
31+
| Modifiers.cs:76:33:76:34 | M3 | public |

csharp/ql/test/library-tests/modifiers/Modifiers.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,17 @@ public interface I1
6262
void M1();
6363
void M2() => throw null;
6464
}
65+
66+
internal interface I2
67+
{
68+
void M1() => throw null;
69+
}
70+
71+
public class C2 : I2
72+
{
73+
void I2.M1() => throw null;
74+
75+
protected private void M2() { }
76+
protected internal void M3() { }
77+
}
6578
}

csharp/ql/test/library-tests/modifiers/Modifiers.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,12 @@
4949
| Modifiers.cs:62:14:62:15 | M1 | file://:0:0:0:0 | public |
5050
| Modifiers.cs:63:14:63:15 | M2 | file://:0:0:0:0 | public |
5151
| Modifiers.cs:63:14:63:15 | M2 | file://:0:0:0:0 | virtual |
52+
| Modifiers.cs:66:24:66:25 | I2 | file://:0:0:0:0 | internal |
53+
| Modifiers.cs:68:14:68:15 | M1 | file://:0:0:0:0 | public |
54+
| Modifiers.cs:68:14:68:15 | M1 | file://:0:0:0:0 | virtual |
55+
| Modifiers.cs:71:18:71:19 | C2 | file://:0:0:0:0 | public |
56+
| Modifiers.cs:73:17:73:18 | M1 | file://:0:0:0:0 | private |
57+
| Modifiers.cs:75:32:75:33 | M2 | file://:0:0:0:0 | private |
58+
| Modifiers.cs:75:32:75:33 | M2 | file://:0:0:0:0 | protected |
59+
| Modifiers.cs:76:33:76:34 | M3 | file://:0:0:0:0 | internal |
60+
| Modifiers.cs:76:33:76:34 | M3 | file://:0:0:0:0 | protected |

0 commit comments

Comments
 (0)