Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ protected override void ExtractInitializers(TextWriter trapFile)
{
case SyntaxKind.BaseConstructorInitializer:
initializerType = Symbol.ContainingType.BaseType!;
ExtractObjectInitCall(trapFile);
break;
case SyntaxKind.ThisConstructorInitializer:
initializerType = Symbol.ContainingType;
Expand All @@ -90,10 +91,12 @@ protected override void ExtractInitializers(TextWriter trapFile)
var primaryInfo = Context.GetSymbolInfo(primaryInitializer);
var primarySymbol = primaryInfo.Symbol;

ExtractObjectInitCall(trapFile);
ExtractSourceInitializer(trapFile, primarySymbol?.ContainingType, (IMethodSymbol?)primarySymbol, primaryInitializer.ArgumentList, primaryInitializer.GetLocation());
}
else if (Symbol.MethodKind is MethodKind.Constructor)
{
ExtractObjectInitCall(trapFile);
var baseType = Symbol.ContainingType.BaseType;
if (baseType is null)
{
Expand Down Expand Up @@ -127,6 +130,27 @@ protected override void ExtractInitializers(TextWriter trapFile)
}
}

private void ExtractObjectInitCall(TextWriter trapFile)
{
var target = ObjectInitMethod.Create(Context, ContainingType!);

var type = Context.Compilation.GetSpecialType(SpecialType.System_Void);

var info = new ExpressionInfo(Context,
AnnotatedTypeSymbol.CreateNotAnnotated(type),
Location,
Kinds.ExprKind.METHOD_INVOCATION,
this,
-2,
isCompilerGenerated: true,
null);
var obinitCall = new Expression(info);

trapFile.expr_call(obinitCall, target);

Expressions.This.CreateImplicit(Context, Symbol.ContainingType, Location, obinitCall, -1);
}

private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IMethodSymbol? symbol, ArgumentListSyntax arguments, Microsoft.CodeAnalysis.Location location)
{
var initInfo = new ExpressionInfo(Context,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Semmle.Extraction.CSharp.Entities
{
/// <summary>
/// Marker interface for method entities.
/// </summary>
public interface IMethodEntity : IEntity
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Semmle.Extraction.CSharp.Entities
{
internal abstract class Method : CachedSymbol<IMethodSymbol>, IExpressionParentEntity, IStatementParentEntity
internal abstract class Method : CachedSymbol<IMethodSymbol>, IExpressionParentEntity, IStatementParentEntity, IMethodEntity
{
protected Method(Context cx, IMethodSymbol init)
: base(cx, init) { }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.IO;
using Microsoft.CodeAnalysis;

namespace Semmle.Extraction.CSharp.Entities
{
internal sealed class ObjectInitMethod : CachedEntity, IMethodEntity
{
private Type ContainingType { get; }

private ObjectInitMethod(Context cx, Type containingType)
: base(cx)
{
this.ContainingType = containingType;
}

private static readonly string Name = "<object initializer>";

public static ObjectInitMethod Create(Context cx, Type containingType)
{
return ObjectInitMethodFactory.Instance.CreateEntity(cx, (typeof(ObjectInitMethod), containingType), containingType);
}

public override void Populate(TextWriter trapFile)
{
var returnType = Type.Create(Context, Context.Compilation.GetSpecialType(SpecialType.System_Void));

trapFile.methods(this, Name, ContainingType, returnType.TypeRef, this);

trapFile.compiler_generated(this);

trapFile.method_location(this, Context.CreateLocation(ReportingLocation));
}

public override void WriteId(EscapingTextWriter trapFile)
{
trapFile.WriteSubId(ContainingType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work as expected for generics (I suspect it does due to how WriteId looks like for constructors)? Just wondering whether you actively considered that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe extracted Callable bodies are 1-1 with their corresponding lines of code, ie. we extract only one Constructor body regardless of generics. And since ObjectInitMethod extraction is triggered by inserting a call in those constructor bodies we will also only extract one ObjectInitMethod. That is at least my understanding. And I think I've located the line that ensures this: In Method.cs the call to ExtractInitializers is guarded by IsSourceDeclaration.

trapFile.Write(".");
trapFile.Write(Name);
trapFile.Write(";method");
}

public override Microsoft.CodeAnalysis.Location? ReportingLocation => ContainingType.ReportingLocation;

public override bool NeedsPopulation => true;

public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;

private class ObjectInitMethodFactory : CachedEntityFactory<Type, ObjectInitMethod>
{
public static ObjectInitMethodFactory Instance { get; } = new ObjectInitMethodFactory();

public override ObjectInitMethod Create(Context cx, Type containingType) =>
new ObjectInitMethod(cx, containingType);
}
}
}
6 changes: 3 additions & 3 deletions csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ internal static void expr_argument(this TextWriter trapFile, Expression expr, in
internal static void expr_argument_name(this TextWriter trapFile, Expression expr, string name) =>
trapFile.WriteTuple("expr_argument_name", expr, name);

internal static void expr_call(this TextWriter trapFile, Expression expr, Method target) =>
internal static void expr_call(this TextWriter trapFile, Expression expr, IMethodEntity target) =>
trapFile.WriteTuple("expr_call", expr, target);

internal static void expr_flowstate(this TextWriter trapFile, Expression expr, int flowState) =>
Expand Down Expand Up @@ -247,10 +247,10 @@ internal static void localvar_location(this TextWriter trapFile, LocalVariable v
internal static void localvars(this TextWriter trapFile, LocalVariable key, VariableKind kind, string name, int @var, Type type, Expression expr) =>
trapFile.WriteTuple("localvars", key, (int)kind, name, @var, type, expr);

internal static void method_location(this TextWriter trapFile, Method method, Location location) =>
internal static void method_location(this TextWriter trapFile, IMethodEntity method, Location location) =>
trapFile.WriteTuple("method_location", method, location);

internal static void methods(this TextWriter trapFile, Method method, string name, Type declType, Type retType, Method originalDefinition) =>
internal static void methods(this TextWriter trapFile, IMethodEntity method, string name, Type declType, Type retType, IMethodEntity originalDefinition) =>
trapFile.WriteTuple("methods", method, name, declType, retType, originalDefinition);

internal static void modifiers(this TextWriter trapFile, Label entity, string modifier) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
diagnosticAttributes
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
#select
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
| Program | Program.<Main>$ |
| Program | Program.<object initializer> |
| Program | Program.Program |
| Resx.Test1.Test2.test | Resx.Test1.Test2.test.Culture |
| Resx.Test1.Test2.test | Resx.Test1.Test2.test.GetResourceString |
Expand Down
17 changes: 16 additions & 1 deletion csharp/ql/lib/semmle/code/csharp/Callable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ class Method extends Callable, Virtualizable, Attributable, @method {
/** Holds if this method has a `params` parameter. */
predicate hasParams() { exists(this.getParamsType()) }

// Remove when `Callable.isOverridden()` is removed
override predicate fromSource() {
Callable.super.fromSource() and
not this.isCompilerGenerated()
Expand Down Expand Up @@ -317,6 +316,19 @@ class ExtensionMethod extends Method {
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
}

/**
* An object initializer method.
*
* This is an extractor-synthesized method that executes the field
* initializers. Note that the AST nodes for the field initializers are nested
* directly under the class, and therefore this method has no body in the AST.
* On the other hand, this provides the unique enclosing callable for the field
* initializers and their control flow graph.
*/
class ObjectInitMethod extends Method {
ObjectInitMethod() { this.getName() = "<object initializer>" }
}

/**
* A constructor, for example `public C() { }` on line 2 in
*
Expand Down Expand Up @@ -350,6 +362,9 @@ class Constructor extends Callable, Member, Attributable, @constructor {
*/
ConstructorInitializer getInitializer() { result = this.getChildExpr(-1) }

/** Gets the object initializer call of this constructor, if any. */
MethodCall getObjectInitializerCall() { result = this.getChildExpr(-2) }

/** Holds if this constructor has an initializer. */
predicate hasInitializer() { exists(this.getInitializer()) }

Expand Down
5 changes: 4 additions & 1 deletion csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class TopLevelExprParent extends Element, @top_level_expr_parent {
/** INTERNAL: Do not use. */
Expr getExpressionBody(Callable c) {
result = c.getAChildExpr() and
not result = c.(Constructor).getInitializer()
not result = c.(Constructor).getInitializer() and
not result = c.(Constructor).getObjectInitializerCall()
}

/** INTERNAL: Do not use. */
Expand Down Expand Up @@ -211,6 +212,8 @@ private module Cached {
enclosingBody(cfe, getBody(c))
or
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
or
parent*(cfe, c.(Constructor).getObjectInitializerCall())
}

/** Holds if the enclosing statement of expression `e` is `s`. */
Expand Down
Loading
Loading