Skip to content

Commit 72e3aed

Browse files
committed
C#: Add extension call classes.
1 parent e275e1c commit 72e3aed

File tree

1 file changed

+87
-1
lines changed
  • csharp/ql/lib/semmle/code/csharp/exprs

1 file changed

+87
-1
lines changed

csharp/ql/lib/semmle/code/csharp/exprs/Call.qll

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,33 @@ class Call extends Expr, @call {
267267
class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invocation_expr {
268268
override Method getTarget() { expr_call(this, result) }
269269

270+
/**
271+
* Gets the accessor that was used to generate this method, if any. For example, the
272+
* method call `MyExtensions.get_FirstChar(s)` on line 9 is generated from the property
273+
* accessor `get_FirstChar` on line 3 in
274+
*
275+
* ```csharp
276+
* static class MyExtensions {
277+
* extension(string s) {
278+
* public char FirstChar { get { ... } }
279+
* }
280+
* }
281+
*
282+
* class A {
283+
* char M(string s) {
284+
* return MyExtensions.get_FirstChar(s);
285+
* }
286+
* }
287+
*/
288+
Accessor getTargetAccessor() { expr_call(this, result) }
289+
270290
override Method getQualifiedDeclaration() { result = this.getTarget() }
271291

272-
override string toString() { result = "call to method " + concat(this.getTarget().getName()) }
292+
override string toString() {
293+
if exists(this.getTargetAccessor())
294+
then result = "call to extension accessor " + concat(this.getTargetAccessor().getName())
295+
else result = "call to method " + concat(this.getTarget().getName())
296+
}
273297

274298
override string getAPrimaryQlClass() { result = "MethodCall" }
275299

@@ -479,6 +503,30 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr {
479503
override string getAPrimaryQlClass() { result = "OperatorCall" }
480504
}
481505

506+
/**
507+
* A call to an extension operator, for example `3 * s` on
508+
* line 9 in
509+
*
510+
* ```csharp
511+
* static class MyExtensions {
512+
* extension(string s) {
513+
* public static string operator *(int i, string s) { ... }
514+
* }
515+
* }
516+
*
517+
* class A {
518+
* string M(string s) {
519+
* return 3 * s;
520+
* }
521+
* }
522+
* ```
523+
*/
524+
class ExtensionOperatorCall extends OperatorCall {
525+
ExtensionOperatorCall() { this.getTarget() instanceof ExtensionOperator }
526+
527+
override string getAPrimaryQlClass() { result = "ExtensionOperatorCall" }
528+
}
529+
482530
/**
483531
* A call to a user-defined mutator operator, for example `a++` on
484532
* line 7 in
@@ -658,6 +706,44 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr {
658706
override string getAPrimaryQlClass() { result = "IndexerCall" }
659707
}
660708

709+
/**
710+
* A call to an extension property accessor (via the property), for example
711+
* `s.FirstChar` on line 9 in
712+
*
713+
* ```csharp
714+
* static class MyExtensions {
715+
* extension(string s) {
716+
* public char FirstChar { get { ... } }
717+
* }
718+
* }
719+
*
720+
* class A {
721+
* char M(string s) {
722+
* return s.FirstChar;
723+
* }
724+
* }
725+
* ```
726+
*/
727+
class ExtensionPropertyCall extends PropertyCall {
728+
private ExtensionProperty prop;
729+
730+
ExtensionPropertyCall() { this.getProperty() = prop }
731+
732+
override Expr getArgument(int i) {
733+
if prop.isStatic()
734+
then result = super.getArgument(i)
735+
else (
736+
// Shift arguments as the qualifier is an explicit argument in the getter/setter.
737+
i = 0 and
738+
result = this.getQualifier()
739+
or
740+
result = super.getArgument(i - 1)
741+
)
742+
}
743+
744+
override string getAPrimaryQlClass() { result = "ExtensionPropertyCall" }
745+
}
746+
661747
/**
662748
* A call to an event accessor, for example the call to `add_Click`
663749
* (defined on line 5) on line 12 in

0 commit comments

Comments
 (0)