Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6e5c2ef
C#: Remove assembly qualifier from some trap-ids.
calumgrant Feb 11, 2020
6649d72
C#: Qualify type parameters with the entity that declares them
calumgrant Feb 12, 2020
4657ddc
C#: Avoid qualifying explicit interface implementations.
calumgrant Feb 12, 2020
d1cde2a
C#: Address review comment.
calumgrant Apr 7, 2020
cd51a67
C#: Take nullability into account when creating symbol entities. Othe…
calumgrant Apr 20, 2020
9051758
C#: Address review comment: Make dictionary type more specific.
calumgrant Apr 20, 2020
f61fdc6
C#: Only resolve a single, canonical type for each typeref.
calumgrant Apr 24, 2020
f4b1594
C#: Unqualify method names and nested types.
calumgrant May 5, 2020
4740b47
C#: Minor edits
calumgrant May 6, 2020
9a51192
C#: Move TypeRefs into a separate file and import it privately. Reord…
calumgrant May 13, 2020
0cfe424
C#: Address review comments.
calumgrant May 18, 2020
aa99269
C#: Fix merge conflicts. Unfortunately, the type of `symbolEntityCach…
calumgrant May 18, 2020
7628caa
C#: Avoid typerefs for constructed types.
calumgrant Jun 8, 2020
d17f88b
C#: Remove assembly prefix from all extractor IDs
hvitved Aug 27, 2020
afbbafe
C#: Simplify `TypeRef.qll`
hvitved Aug 5, 2020
8a03557
C#: Make `Callable::get[Expression|Statement]Body()` return all possi…
hvitved Aug 27, 2020
c7f7769
C#: Add CFG tests for callables with multiple implementations
hvitved Aug 27, 2020
92bf830
C#: Avoid bad magic in `UselessUpcast.ql`
hvitved Aug 17, 2020
51dc151
C#: Address review comments
hvitved Aug 31, 2020
1b769eb
C#: Address more review comments
hvitved Sep 2, 2020
701e189
C#: Add change note
hvitved Sep 2, 2020
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
4 changes: 4 additions & 0 deletions change-notes/1.26/analysis-csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ The following changes in version 1.26 affect C# analysis in all applications.
* Partial method bodies are extracted. Previously, partial method bodies were skipped completely.
* Inferring the lengths of implicitely sized arrays is fixed. Previously, multidimensional arrays were always extracted with the same length for
each dimension. With the fix, the array sizes `2` and `1` are extracted for `new int[,]{{1},{2}}`. Previously `2` and `2` were extracted.
* The extractor is now assembly-insensitive by default. This means that two entities with the same
fully-qualified name are now mapped to the same entity in the resulting database, regardless of
whether they belong to different assemblies. Assembly sensitivity can be reenabled by passing
`--assemblysensitivetrap` to the extractor.

## Changes to libraries

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger
trapFile = trapWriter.TrapFile;
if (nocache || !System.IO.File.Exists(trapFile))
{
var cx = extractor.CreateContext(null, trapWriter, null);
var cx = extractor.CreateContext(null, trapWriter, null, false);
ExtractCIL(cx, assemblyPath, extractPdbs);
extracted = true;
}
Expand Down
11 changes: 7 additions & 4 deletions csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ public class Analyser : IDisposable

public readonly ILogger Logger;

public Analyser(IProgressMonitor pm, ILogger logger)
public readonly bool AddAssemblyTrapPrefix;

public Analyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix)
{
Logger = logger;
AddAssemblyTrapPrefix = addAssemblyTrapPrefix;
Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now);
stopWatch.Start();
progressMonitor = pm;
Expand Down Expand Up @@ -231,7 +234,7 @@ void DoAnalyseCompilation(string cwd, string[] args)
var projectLayout = layout.LookupProjectOrDefault(assemblyPath);
var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression);
compilationTrapFile = trapWriter; // Dispose later
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true));
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true), AddAssemblyTrapPrefix);

compilationEntity = new Entities.Compilation(cx, cwd, args);
}
Expand Down Expand Up @@ -286,7 +289,7 @@ void DoAnalyseAssembly(PortableExecutableReference r)

if (assembly != null)
{
var cx = extractor.CreateContext(c, trapWriter, new AssemblyScope(assembly, assemblyPath, false));
var cx = extractor.CreateContext(c, trapWriter, new AssemblyScope(assembly, assemblyPath, false), AddAssemblyTrapPrefix);

foreach (var module in assembly.Modules)
{
Expand Down Expand Up @@ -372,7 +375,7 @@ void DoExtractTree(SyntaxTree tree)

if (!upToDate)
{
Context cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree));
Context cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix);
Populators.CompilationUnit.Extract(cx, tree.GetRoot());
cx.PopulateAll();
cx.ExtractComments(cx.CommentGenerator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public override void Populate(TextWriter trapFile)
}

public new static Accessor Create(Context cx, IMethodSymbol symbol) =>
AccessorFactory.Instance.CreateEntity(cx, symbol);
AccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol);

class AccessorFactory : ICachedEntityFactory<IMethodSymbol, Accessor>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void BindTo(Label entity, CommentBinding binding)
Context.TrapWriter.Writer.commentblock_binding(this, entity, binding);
}

public static CommentBlock Create(Context cx, ICommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block);
public static CommentBlock Create(Context cx, ICommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block, block);

class CommentBlockFactory : ICachedEntityFactory<ICommentBlock, CommentBlock>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ public override void WriteId(TextWriter trapFile)
trapFile.Write(";commentline");
}

static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) => CommentLineFactory.Instance.CreateEntity(cx, loc, type, text, raw);
static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw)
{
var init = (loc, type, text, raw);
return CommentLineFactory.Instance.CreateEntity(cx, init, init);
}

class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ ConstructorDeclarationSyntax Syntax
{
case MethodKind.StaticConstructor:
case MethodKind.Constructor:
return ConstructorFactory.Instance.CreateEntity(cx, constructor);
return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor);
default:
throw new InternalError(constructor, "Attempt to create a Constructor from a symbol that isn't a constructor");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Conversion : UserOperator
: base(cx, init) { }

public new static Conversion Create(Context cx, IMethodSymbol symbol) =>
ConversionFactory.Instance.CreateEntity(cx, symbol);
ConversionFactory.Instance.CreateEntityFromSymbol(cx, symbol);

public override Microsoft.CodeAnalysis.Location ReportingLocation
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public override void Populate(TextWriter trapFile)
}

public new static Destructor Create(Context cx, IMethodSymbol symbol) =>
DestructorFactory.Instance.CreateEntity(cx, symbol);
DestructorFactory.Instance.CreateEntityFromSymbol(cx, symbol);

class DestructorFactory : ICachedEntityFactory<IMethodSymbol, Destructor>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public override void Populate(TextWriter trapFile)
TypeMention.Create(Context, syntaxType, this, type);
}

public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntity(cx, symbol);
public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol);

class EventFactory : ICachedEntityFactory<IEventSymbol, Event>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public override void Populate(TextWriter trapFile)
}

public new static EventAccessor Create(Context cx, IMethodSymbol symbol) =>
EventAccessorFactory.Instance.CreateEntity(cx, symbol);
EventAccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol);

class EventAccessorFactory : ICachedEntityFactory<IMethodSymbol, EventAccessor>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Field : CachedSymbol<IFieldSymbol>, IExpressionParentEntity
type = new Lazy<AnnotatedType>(() => Entities.Type.Create(cx, symbol.GetAnnotatedType()));
}

public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntity(cx, field);
public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntityFromSymbol(cx, field);

// Do not populate backing fields.
// Populate Tuple fields.
Expand Down Expand Up @@ -101,6 +101,8 @@ public override void Populate(TextWriter trapFile)

public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(Type.Type);
trapFile.Write(" ");
trapFile.WriteSubId(ContainingType);
trapFile.Write('.');
trapFile.Write(symbol.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override void Populate(TextWriter trapFile)
TypeMention.Create(Context, syntax.Type, this, type);
}

public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntity(cx, prop);
public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop);

public override void WriteId(TextWriter trapFile)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public override void WriteQuotedId(TextWriter trapFile)
trapFile.Write('*');
}

public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntity(cx, field);
public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntityFromSymbol(cx, field);

class LocalFunctionFactory : ICachedEntityFactory<IMethodSymbol, LocalFunction>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ symbol is ILocalSymbol l ?

public static LocalVariable Create(Context cx, ISymbol local)
{
return LocalVariableFactory.Instance.CreateEntity(cx, local);
return LocalVariableFactory.Instance.CreateEntityFromSymbol(cx, local);
}

void DefineConstantValue(TextWriter trapFile)
Expand Down
65 changes: 11 additions & 54 deletions csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ public void Overrides(TextWriter trapFile)
/// </summary>
protected static void BuildMethodId(Method m, TextWriter trapFile)
{
m.symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.symbol);
trapFile.Write(" ");

trapFile.WriteSubId(m.ContainingType);

AddExplicitInterfaceQualifierToId(m.Context, trapFile, m.symbol.ExplicitInterfaceImplementations);
Expand All @@ -129,7 +132,7 @@ protected static void BuildMethodId(Method m, TextWriter trapFile)
// Type arguments with different nullability can result in
// a constructed method with different nullability of its parameters and return type,
// so we need to create a distinct database entity for it.
trapFile.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { AddSignatureTypeToId(m.Context, tb0, m.symbol, ta.Symbol); trapFile.Write((int)ta.Nullability); });
trapFile.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { ta.Symbol.BuildOrWriteId(m.Context, tb0, m.symbol); trapFile.Write((int)ta.Nullability); });
trapFile.Write('>');
}
}
Expand Down Expand Up @@ -163,65 +166,21 @@ public override void WriteId(TextWriter trapFile)
BuildMethodId(this, trapFile);
}

/// <summary>
/// Adds an appropriate label ID to the trap builder <paramref name="trapFile"/>
/// for the type <paramref name="type"/> belonging to the signature of method
/// <paramref name="method"/>.
///
/// For methods without type parameters this will always add the key of the
/// corresponding type.
///
/// For methods with type parameters, this will add the key of the
/// corresponding type if the type does *not* contain one of the method
/// type parameters, otherwise it will add a textual representation of
/// the type. This distinction is required because type parameter IDs
/// refer to their declaring methods.
///
/// Example:
///
/// <code>
/// int Count&lt;T&gt;(IEnumerable<T> items)
/// </code>
///
/// The label definitions for <code>Count</code> (<code>#4</code>) and <code>T</code>
/// (<code>#5</code>) will look like:
///
/// <code>
/// #1=&lt;label for System.Int32&gt;
/// #2=&lt;label for type containing Count&gt;
/// #3=&lt;label for IEnumerable`1&gt;
/// #4=@"{#1} {#2}.Count`2(#3<T>);method"
/// #5=@"{#4}T;typeparameter"
/// </code>
///
/// Note how <code>int</code> is referenced in the label definition <code>#3</code> for
/// <code>Count</code>, while <code>T[]</code> is represented textually in order
/// to make the reference to <code>#3</code> in the label definition <code>#4</code> for
/// <code>T</code> valid.
/// </summary>
protected static void AddSignatureTypeToId(Context cx, TextWriter trapFile, IMethodSymbol method, ITypeSymbol type)
{
if (type.ContainsTypeParameters(cx, method))
type.BuildTypeId(cx, trapFile, (cx0, tb0, type0) => AddSignatureTypeToId(cx, tb0, method, type0));
else
trapFile.WriteSubId(Type.Create(cx, type));
}

protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethodSymbol method)
{
trapFile.Write('(');
int index = 0;

if (method.MethodKind == MethodKind.ReducedExtension)
{
trapFile.WriteSeparator(",", ref index);
AddSignatureTypeToId(cx, trapFile, method, method.ReceiverType);
}
var @params = method.MethodKind == MethodKind.ReducedExtension
? method.ReducedFrom.Parameters
: method.Parameters;

foreach (var param in method.Parameters)
foreach (var param in @params)
{
trapFile.WriteSeparator(",", ref index);
AddSignatureTypeToId(cx, trapFile, method, param.Type);
param.Type.BuildOrWriteId(cx, trapFile, method);
trapFile.Write(" ");
trapFile.Write(param.Name);
switch (param.RefKind)
{
case RefKind.Out:
Expand All @@ -245,9 +204,7 @@ protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethod
public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter trapFile, IEnumerable<ISymbol> explicitInterfaceImplementations)
{
if (explicitInterfaceImplementations.Any())
{
trapFile.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType)));
}
}

public virtual string Name => symbol.Name;
Expand Down
46 changes: 15 additions & 31 deletions csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,24 @@

namespace Semmle.Extraction.CSharp.Entities
{
/// <summary>
/// Provide a "Key" object to allow modifiers to exist as entities in the extractor
/// hash map. (Raw strings would work as keys but might clash with other types).
/// </summary>
class ModifierKey : Object
class Modifier : Extraction.CachedEntity<string>
{
public readonly string name;

public ModifierKey(string m)
{
name = m;
}

public override bool Equals(Object obj)
{
return obj.GetType() == GetType() && name == ((ModifierKey)obj).name;
}

public override int GetHashCode() => 13 * name.GetHashCode();
}

class Modifier : Extraction.CachedEntity<ModifierKey>
{
Modifier(Context cx, ModifierKey init)
Modifier(Context cx, string init)
: base(cx, init) { }

public override Microsoft.CodeAnalysis.Location ReportingLocation => null;

public override void WriteId(TextWriter trapFile)
{
trapFile.Write(symbol.name);
trapFile.Write(symbol);
trapFile.Write(";modifier");
}

public override bool NeedsPopulation => true;

public override void Populate(TextWriter trapFile)
{
trapFile.modifiers(Label, symbol.name);
trapFile.modifiers(Label, symbol);
}

public static string AccessbilityModifier(Accessibility access)
Expand Down Expand Up @@ -152,17 +131,22 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key
}
}

public static Modifier Create(Context cx, string modifier) =>
ModifierFactory.Instance.CreateEntity(cx, new ModifierKey(modifier));
public static Modifier Create(Context cx, string modifier)
{
return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier);
}

public static Modifier Create(Context cx, Accessibility access) =>
ModifierFactory.Instance.CreateEntity(cx, new ModifierKey(AccessbilityModifier(access)));
public static Modifier Create(Context cx, Accessibility access)
{
var modifier = AccessbilityModifier(access);
return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier);
}

class ModifierFactory : ICachedEntityFactory<ModifierKey, Modifier>
class ModifierFactory : ICachedEntityFactory<string, Modifier>
{
public static readonly ModifierFactory Instance = new ModifierFactory();

public Modifier Create(Context cx, ModifierKey init) => new Modifier(cx, init);
public Modifier Create(Context cx, string init) => new Modifier(cx, init);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public override void WriteId(TextWriter trapFile)
trapFile.Write(";namespace");
}

public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntity2(cx, ns);
public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntityFromSymbol(cx, ns);

class NamespaceFactory : ICachedEntityFactory<INamespaceSymbol, Namespace>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public override void Populate(TextWriter trapFile)
ExtractCompilerGenerated(trapFile);
}

public new static OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntity(cx, method);
public new static OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method);

class OrdinaryMethodFactory : ICachedEntityFactory<IMethodSymbol, OrdinaryMethod>
{
Expand Down
Loading