C#: Add extension callable and accessor classes.

This commit is contained in:
Michael Nebel
2026-02-04 16:17:54 +01:00
parent 9a4a6cfcb8
commit b9f36f37b6
2 changed files with 97 additions and 9 deletions

View File

@@ -221,6 +221,23 @@ class Callable extends Parameterizable, ExprOrStmtParent, @callable {
/** Gets a `Call` that has this callable as a target. */
Call getACall() { this = result.getTarget() }
/** Holds if this callable is declared in an extension type. */
predicate isInExtension() { this.getDeclaringType() instanceof ExtensionType }
}
/**
* A callable that is declared as an extension.
*
* Either an extension method (`ExtensionMethod`), an extension operator
* (`ExtensionOperator`) or an extension accessor (`ExtensionAccessor`).
*/
abstract class ExtensionCallable extends Callable {
/** Gets the type being extended by this method. */
pragma[noinline]
Type getExtendedType() { result = this.getDeclaringType().(ExtensionType).getExtendedType() }
override string getAPrimaryQlClass() { result = "ExtensionCallable" }
}
/**
@@ -267,8 +284,11 @@ class Method extends Callable, Virtualizable, Attributable, @method {
override Location getALocation() { method_location(this.getUnboundDeclaration(), result) }
/** Holds if this method is a classic extension method. */
predicate isClassicExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() }
/** Holds if this method is an extension method. */
predicate isExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() }
predicate isExtensionMethod() { this.isClassicExtensionMethod() or this.isInExtension() }
/** Gets the type of the `params` parameter of this method, if any. */
Type getParamsType() {
@@ -296,7 +316,17 @@ class Method extends Callable, Virtualizable, Attributable, @method {
}
/**
* An extension method, for example
* An extension method.
*
* Either a classic extension method (`ClassicExtensionMethod`) or an extension
* type extension method (`ExtensionTypeExtensionMethod`).
*/
abstract class ExtensionMethod extends ExtensionCallable, Method {
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
}
/**
* An extension method, for example
*
* ```csharp
* static bool IsDefined(this Widget w) {
@@ -304,16 +334,28 @@ class Method extends Callable, Virtualizable, Attributable, @method {
* }
* ```
*/
class ExtensionMethod extends Method {
ExtensionMethod() { this.isExtensionMethod() }
class ClassicExtensionMethod extends ExtensionMethod {
ClassicExtensionMethod() { this.isClassicExtensionMethod() }
pragma[noinline]
override Type getExtendedType() { result = this.getParameter(0).getType() }
override predicate isStatic() { any() }
}
/** Gets the type being extended by this method. */
pragma[noinline]
Type getExtendedType() { result = this.getParameter(0).getType() }
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
/**
* An extension method declared in an extension type, for example `IsNullOrEmpty` in
*
* ```csharp
* static class MyExtensions {
* extension(string s) {
* public bool IsNullOrEmpty() { ... }
* }
* }
* ```
*/
class ExtensionTypeExtensionMethod extends ExtensionMethod {
ExtensionTypeExtensionMethod() { this.isInExtension() }
}
/**
@@ -536,6 +578,21 @@ class RecordCloneMethod extends Method {
}
}
/**
* An extension operator, for example `*` in
*
* ```csharp
* static class MyExtensions {
* extension(string s) {
* public static string operator *(int s1, string s2) { ... }
* }
* }
* ```
*/
class ExtensionOperator extends ExtensionCallable, Operator {
ExtensionOperator() { this.isInExtension() }
}
/**
* A user-defined unary operator - an operator taking one operand.
*

View File

@@ -260,6 +260,21 @@ class Property extends DeclarationWithGetSetAccessors, @property {
override string getAPrimaryQlClass() { result = "Property" }
}
/**
* An extension property, for example `FirstChar` in
*
* ```csharp
* static class MyExtensions {
* extension(string s) {
* public char FirstChar { get { ... } }
* }
* }
* ```
*/
class ExtensionProperty extends Property {
ExtensionProperty() { this.getDeclaringType() instanceof ExtensionType }
}
/**
* An indexer, for example `string this[int i]` on line 2 in
*
@@ -413,6 +428,22 @@ class Accessor extends Callable, Modifiable, Attributable, Overridable, @callabl
override string toString() { result = this.getName() }
}
/**
* An extension accessor. Either a getter (`Getter`) or a setter (`Setter`) of an
* extension property, for example `get` in
*
* ```csharp
* static class MyExtensions {
* extension(string s) {
* public char FirstChar { get { ... } }
* }
* }
* ```
*/
class ExtensionAccessor extends ExtensionCallable, Accessor {
ExtensionAccessor() { this.getDeclaringType() instanceof ExtensionType }
}
/**
* A `get` accessor, for example `get { return p; }` in
*