mirror of
https://github.com/github/codeql.git
synced 2026-02-11 20:51:06 +01:00
C#: Address review comments.
This commit is contained in:
@@ -647,15 +647,13 @@ namespace Semmle.Extraction.CSharp
|
||||
/// Return true if this method is a compiler-generated extension method.
|
||||
/// </summary>
|
||||
public static bool IsCompilerGeneratedExtensionMethod(this IMethodSymbol method) =>
|
||||
method.TryGetExtensionMethod(out _);
|
||||
method.TryGetExtensionMethod() is not null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this method is a compiler-generated extension method,
|
||||
/// and outputs the original extension method declaration.
|
||||
/// Returns the extension method corresponding to this compiler-generated extension method, if it exists.
|
||||
/// </summary>
|
||||
public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodSymbol? declaration)
|
||||
public static IMethodSymbol? TryGetExtensionMethod(this IMethodSymbol method)
|
||||
{
|
||||
declaration = null;
|
||||
if (method.IsImplicitlyDeclared && method.ContainingSymbol is INamedTypeSymbol containingType)
|
||||
{
|
||||
// Extension types are declared within the same type as the generated
|
||||
@@ -688,23 +686,22 @@ namespace Semmle.Extraction.CSharp
|
||||
.First(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration));
|
||||
|
||||
// If the extension declaration is unbound apply the remaning type arguments and construct it.
|
||||
declaration = extensionDeclaration.IsUnboundGenericMethod()
|
||||
return extensionDeclaration.IsUnboundGenericMethod()
|
||||
? extensionDeclaration.Construct(extensionMethodArguments.ToArray())
|
||||
: extensionDeclaration;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If anything goes wrong, fall back to the unbound declaration.
|
||||
declaration = unboundDeclaration;
|
||||
return unboundDeclaration;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
declaration = unboundDeclaration;
|
||||
return unboundDeclaration;
|
||||
}
|
||||
|
||||
}
|
||||
return declaration is not null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -820,5 +817,35 @@ namespace Semmle.Extraction.CSharp
|
||||
/// </summary>
|
||||
public static IEnumerable<T> ExtractionCandidates<T>(this IEnumerable<T> symbols) where T : ISymbol =>
|
||||
symbols.Where(symbol => symbol.ShouldExtractSymbol());
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parameter kind for this parameter symbol, e.g. `ref`, `out`, `params`, etc.
|
||||
/// </summary>
|
||||
public static Parameter.Kind GetParameterKind(this IParameterSymbol parameter)
|
||||
{
|
||||
switch (parameter.RefKind)
|
||||
{
|
||||
case RefKind.Out:
|
||||
return Parameter.Kind.Out;
|
||||
case RefKind.Ref:
|
||||
return Parameter.Kind.Ref;
|
||||
case RefKind.In:
|
||||
return Parameter.Kind.In;
|
||||
case RefKind.RefReadOnlyParameter:
|
||||
return Parameter.Kind.RefReadOnly;
|
||||
default:
|
||||
if (parameter.IsParams)
|
||||
return Parameter.Kind.Params;
|
||||
|
||||
if (parameter.Ordinal == 0)
|
||||
{
|
||||
if (parameter.ContainingSymbol is IMethodSymbol method && method.IsExtensionMethod)
|
||||
{
|
||||
return Parameter.Kind.This;
|
||||
}
|
||||
}
|
||||
return Parameter.Kind.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +126,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
|
||||
private static bool IsOperatorLikeCall(ExpressionNodeInfo info)
|
||||
{
|
||||
return info.SymbolInfo.Symbol is IMethodSymbol method &&
|
||||
method.TryGetExtensionMethod(out var original) &&
|
||||
original!.MethodKind == MethodKind.UserDefinedOperator;
|
||||
method.TryGetExtensionMethod()?.MethodKind == MethodKind.UserDefinedOperator;
|
||||
}
|
||||
|
||||
public IMethodSymbol? TargetSymbol
|
||||
@@ -140,13 +139,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
|
||||
{
|
||||
var method = symbol as IMethodSymbol;
|
||||
// Case for compiler-generated extension methods.
|
||||
if (method is not null &&
|
||||
method.TryGetExtensionMethod(out var original))
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
return method;
|
||||
return method?.TryGetExtensionMethod() ?? method;
|
||||
}
|
||||
|
||||
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure)
|
||||
|
||||
@@ -41,36 +41,6 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
protected virtual int Ordinal => Symbol.Ordinal + PositionOffset;
|
||||
|
||||
private Kind ParamKind
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Symbol.RefKind)
|
||||
{
|
||||
case RefKind.Out:
|
||||
return Kind.Out;
|
||||
case RefKind.Ref:
|
||||
return Kind.Ref;
|
||||
case RefKind.In:
|
||||
return Kind.In;
|
||||
case RefKind.RefReadOnlyParameter:
|
||||
return Kind.RefReadOnly;
|
||||
default:
|
||||
if (Symbol.IsParams)
|
||||
return Kind.Params;
|
||||
|
||||
if (Ordinal == 0)
|
||||
{
|
||||
if (Symbol.ContainingSymbol is IMethodSymbol method && method.IsExtensionMethod)
|
||||
{
|
||||
return Kind.This;
|
||||
}
|
||||
}
|
||||
return Kind.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null, int positionOffset = 0)
|
||||
{
|
||||
var cachedSymbol = cx.GetPossiblyCachedParameterSymbol(param);
|
||||
@@ -125,7 +95,8 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
Context.ModelError(Symbol, "Inconsistent parameter declaration");
|
||||
|
||||
var type = Type.Create(Context, Symbol.Type);
|
||||
trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, Original);
|
||||
var kind = Symbol.GetParameterKind();
|
||||
trapFile.@params(this, Name, type.TypeRef, Ordinal, kind, Parent!, Original);
|
||||
|
||||
if (Context.OnlyScaffold)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Synthetic parameter for extension methods declared using the extension syntax.
|
||||
/// That is, we add a synthetic parameter s to IsValid in the following example:
|
||||
/// That is, we add a synthetic parameter `s` to `IsValid` in the following example:
|
||||
/// extension(string s) {
|
||||
/// public bool IsValid() { ... }
|
||||
/// }
|
||||
@@ -30,24 +30,6 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
private static int Ordinal => 0;
|
||||
|
||||
private Parameter.Kind ParamKind
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ExtensionParameter.RefKind)
|
||||
{
|
||||
case RefKind.Ref:
|
||||
return Parameter.Kind.Ref;
|
||||
case RefKind.In:
|
||||
return Parameter.Kind.In;
|
||||
case RefKind.RefReadOnlyParameter:
|
||||
return Parameter.Kind.RefReadOnly;
|
||||
default:
|
||||
return Parameter.Kind.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string Name => ExtensionParameter.Name;
|
||||
|
||||
private bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration();
|
||||
@@ -58,7 +40,8 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
PopulateRefKind(trapFile, ExtensionParameter.RefKind);
|
||||
|
||||
var type = Type.Create(Context, ExtensionParameter.Type);
|
||||
trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, ExtensionMethod, Original);
|
||||
var kind = ExtensionParameter.GetParameterKind();
|
||||
trapFile.@params(this, Name, type.TypeRef, Ordinal, kind, ExtensionMethod, Original);
|
||||
|
||||
if (Context.OnlyScaffold)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ import exprs.Call
|
||||
private import commons.QualifiedName
|
||||
private import commons.Collections
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.internal.Callable
|
||||
private import semmle.code.csharp.metrics.Complexity
|
||||
private import TypeRef
|
||||
|
||||
@@ -221,24 +222,9 @@ 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" }
|
||||
}
|
||||
final class ExtensionCallable = ExtensionCallableImpl;
|
||||
|
||||
/**
|
||||
* A method, for example
|
||||
@@ -315,15 +301,7 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
override string getAPrimaryQlClass() { result = "Method" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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" }
|
||||
}
|
||||
final class ExtensionMethod = ExtensionMethodImpl;
|
||||
|
||||
/**
|
||||
* An extension method, for example
|
||||
@@ -334,7 +312,7 @@ abstract class ExtensionMethod extends ExtensionCallable, Method {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ClassicExtensionMethod extends ExtensionMethod {
|
||||
class ClassicExtensionMethod extends ExtensionMethodImpl {
|
||||
ClassicExtensionMethod() { this.isClassicExtensionMethod() }
|
||||
|
||||
pragma[noinline]
|
||||
@@ -354,7 +332,7 @@ class ClassicExtensionMethod extends ExtensionMethod {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ExtensionTypeExtensionMethod extends ExtensionMethod {
|
||||
class ExtensionTypeExtensionMethod extends ExtensionMethodImpl {
|
||||
ExtensionTypeExtensionMethod() { this.isInExtension() }
|
||||
}
|
||||
|
||||
@@ -589,7 +567,7 @@ class RecordCloneMethod extends Method {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ExtensionOperator extends ExtensionCallable, Operator {
|
||||
class ExtensionOperator extends ExtensionCallableImpl, Operator {
|
||||
ExtensionOperator() { this.isInExtension() }
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,9 @@ class Declaration extends NamedElement, @declaration {
|
||||
* implicit constructors or accessors.
|
||||
*/
|
||||
predicate isCompilerGenerated() { compiler_generated(this) }
|
||||
|
||||
/** Holds if this declaration is in an extension type. */
|
||||
predicate isInExtension() { this.getDeclaringType() instanceof ExtensionType }
|
||||
}
|
||||
|
||||
/** A declaration that can have a modifier. */
|
||||
|
||||
@@ -6,6 +6,7 @@ import Member
|
||||
import Stmt
|
||||
import Type
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.internal.Callable
|
||||
private import TypeRef
|
||||
|
||||
/**
|
||||
@@ -272,7 +273,7 @@ class Property extends DeclarationWithGetSetAccessors, @property {
|
||||
* ```
|
||||
*/
|
||||
class ExtensionProperty extends Property {
|
||||
ExtensionProperty() { this.getDeclaringType() instanceof ExtensionType }
|
||||
ExtensionProperty() { this.isInExtension() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,8 +441,8 @@ class Accessor extends Callable, Modifiable, Attributable, Overridable, @callabl
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ExtensionAccessor extends ExtensionCallable, Accessor {
|
||||
ExtensionAccessor() { this.getDeclaringType() instanceof ExtensionType }
|
||||
class ExtensionAccessor extends ExtensionCallableImpl, Accessor {
|
||||
ExtensionAccessor() { this.isInExtension() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -236,10 +236,8 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr {
|
||||
* ```
|
||||
*/
|
||||
class SyntheticExtensionParameterAccess extends ParameterAccess {
|
||||
private Parameter p;
|
||||
|
||||
SyntheticExtensionParameterAccess() {
|
||||
exists(ExtensionType et |
|
||||
exists(ExtensionType et, Parameter p |
|
||||
p = et.getReceiverParameter() and
|
||||
expr_access(this, p)
|
||||
)
|
||||
|
||||
33
csharp/ql/lib/semmle/code/csharp/internal/Callable.qll
Normal file
33
csharp/ql/lib/semmle/code/csharp/internal/Callable.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides `Callable` classes, which are things that can be called
|
||||
* such as methods and operators.
|
||||
*/
|
||||
|
||||
private import semmle.code.csharp.Callable
|
||||
private import semmle.code.csharp.Property
|
||||
|
||||
/**
|
||||
* A callable that is declared as an extension.
|
||||
*
|
||||
* Either an extension method (`ExtensionMethod`), an extension operator
|
||||
* (`ExtensionOperator`) or an extension accessor (`ExtensionAccessor`).
|
||||
*/
|
||||
abstract class ExtensionCallableImpl extends Callable {
|
||||
/** Gets the type being extended by this method. */
|
||||
pragma[noinline]
|
||||
Type getExtendedType() { result = this.getDeclaringType().(ExtensionType).getExtendedType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExtensionCallable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension method.
|
||||
*
|
||||
* Either a classic extension method (`ClassicExtensionMethod`) or an extension
|
||||
* type extension method (`ExtensionTypeExtensionMethod`).
|
||||
*/
|
||||
abstract class ExtensionMethodImpl extends ExtensionCallableImpl, Method {
|
||||
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
|
||||
}
|
||||
@@ -222,7 +222,7 @@ overlayChangedFiles(
|
||||
| @using_directive | @type_parameter_constraints | @externalDataElement
|
||||
| @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
|
||||
|
||||
@declaration = @callable | @generic | @assignable | @namespace | @extension_type;
|
||||
@declaration = @callable | @generic | @assignable | @namespace;
|
||||
|
||||
@named_element = @namespace | @declaration;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user