Skip to content

Commit 59d9be4

Browse files
authored
Merge pull request #4438 from tamasvajk/feature/ast-fixes
C#: Fixes for AST printing
2 parents c8bb050 + 1830eea commit 59d9be4

File tree

62 files changed

+4538
-1991
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+4538
-1991
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
tooling
2+
* AST printing in Visual Studio Code has been improved. Notable changes are:
3+
* Duplications of namespace declarations have been removed.
4+
* `TypeMention` nodes have been added to the tree.
5+
* Child nodes of assignments and casts have been rearranged to represent syntax order
6+
instead of execution order.
7+
* Various fixes have been applied for `TypeMention` extraction.

csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger
140140
var extractor = new Extractor(false, assemblyPath, logger, pathTransformer);
141141
var transformedAssemblyPath = pathTransformer.Transform(assemblyPath);
142142
var project = layout.LookupProjectOrDefault(transformedAssemblyPath);
143-
using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), true, trapCompression);
143+
using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), trapCompression, discardDuplicates: true);
144144
trapFile = trapWriter.TrapFile;
145145
if (nocache || !System.IO.File.Exists(trapFile))
146146
{

csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ private void DoAnalyseCompilation(string cwd, string[] args)
230230
var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
231231
var assembly = compilation.Assembly;
232232
var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
233-
var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression);
233+
var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false);
234234
compilationTrapFile = trapWriter; // Dispose later
235235
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true), AddAssemblyTrapPrefix);
236236

@@ -260,7 +260,7 @@ private void DoAnalyseAssembly(PortableExecutableReference r)
260260
var assemblyPath = r.FilePath;
261261
var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
262262
var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
263-
using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression);
263+
using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: true);
264264

265265
var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile);
266266

@@ -365,7 +365,7 @@ private void DoExtractTree(SyntaxTree tree)
365365
if (!excluded)
366366
{
367367
// compilation.Clone() is used to allow symbols to be garbage collected.
368-
using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, false, options.TrapCompression);
368+
using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false);
369369

370370
upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);
371371

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ protected ExplicitArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(Exp
2424

2525
protected override void PopulateExpression(TextWriter trapFile)
2626
{
27-
2827
var explicitlySized = false;
2928

3029
if (TypeSyntax is null)
@@ -54,6 +53,8 @@ protected override void PopulateExpression(TextWriter trapFile)
5453

5554
if (explicitlySized)
5655
trapFile.explicitly_sized_array_creation(this);
56+
57+
TypeMention.Create(cx, TypeSyntax, this, Type);
5758
}
5859

5960
private void SetArraySizes(InitializerExpressionSyntax initializer, int rank)

csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,32 @@
88

99
namespace Semmle.Extraction.CSharp.Entities
1010
{
11-
internal class NamespaceDeclaration : FreshEntity
11+
internal class NamespaceDeclaration : CachedEntity<NamespaceDeclarationSyntax>
1212
{
1313
private readonly NamespaceDeclaration parent;
1414
private readonly NamespaceDeclarationSyntax node;
1515

1616
public NamespaceDeclaration(Context cx, NamespaceDeclarationSyntax node, NamespaceDeclaration parent)
17-
: base(cx)
17+
: base(cx, node)
1818
{
1919
this.node = node;
2020
this.parent = parent;
21-
TryPopulate();
2221
}
2322

24-
protected override void Populate(TextWriter trapFile)
23+
public override void WriteId(TextWriter trapFile)
2524
{
26-
var @namespace = (INamespaceSymbol)cx.GetModel(node).GetSymbolInfo(node.Name).Symbol;
27-
var ns = Namespace.Create(cx, @namespace);
25+
trapFile.WriteSubId(Context.Create(ReportingLocation));
26+
trapFile.Write(";namespacedeclaration");
27+
}
28+
29+
public override void Populate(TextWriter trapFile)
30+
{
31+
var @namespace = (INamespaceSymbol)Context.GetModel(node).GetSymbolInfo(node.Name).Symbol;
32+
var ns = Namespace.Create(Context, @namespace);
2833
trapFile.namespace_declarations(this, ns);
29-
trapFile.namespace_declaration_location(this, cx.Create(node.Name.GetLocation()));
34+
trapFile.namespace_declaration_location(this, Context.Create(node.Name.GetLocation()));
3035

31-
var visitor = new Populators.TypeOrNamespaceVisitor(cx, trapFile, this);
36+
var visitor = new Populators.TypeOrNamespaceVisitor(Context, trapFile, this);
3237

3338
foreach (var member in node.Members.Cast<CSharpSyntaxNode>().Concat(node.Usings))
3439
{
@@ -41,8 +46,24 @@ protected override void Populate(TextWriter trapFile)
4146
}
4247
}
4348

44-
public static NamespaceDeclaration Create(Context cx, NamespaceDeclarationSyntax decl, NamespaceDeclaration parent) => new NamespaceDeclaration(cx, decl, parent);
49+
public static NamespaceDeclaration Create(Context cx, NamespaceDeclarationSyntax decl, NamespaceDeclaration parent)
50+
{
51+
var init = (decl, parent);
52+
return NamespaceDeclarationFactory.Instance.CreateEntity(cx, decl, init);
53+
}
54+
55+
private class NamespaceDeclarationFactory : ICachedEntityFactory<(NamespaceDeclarationSyntax decl, NamespaceDeclaration parent), NamespaceDeclaration>
56+
{
57+
public static readonly NamespaceDeclarationFactory Instance = new NamespaceDeclarationFactory();
58+
59+
public NamespaceDeclaration Create(Context cx, (NamespaceDeclarationSyntax decl, NamespaceDeclaration parent) init) =>
60+
new NamespaceDeclaration(cx, init.decl, init.parent);
61+
}
4562

4663
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
64+
65+
public override Microsoft.CodeAnalysis.Location ReportingLocation => node.Name.GetLocation();
66+
67+
public override bool NeedsPopulation => true;
4768
}
4869
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,36 @@ private TypeMention(Context cx, TypeSyntax syntax, IEntity parent, Type type, Mi
2323
this.loc = loc;
2424
}
2525

26-
private static TypeSyntax GetElementType(TypeSyntax type)
26+
private TypeSyntax GetArrayElementType(TypeSyntax type)
2727
{
2828
switch (type)
2929
{
3030
case ArrayTypeSyntax ats:
31-
return GetElementType(ats.ElementType);
31+
return GetArrayElementType(ats.ElementType);
3232
case NullableTypeSyntax nts:
33-
return GetElementType(nts.ElementType);
33+
// int[]? -> int[] -> int
34+
// int? -> int?
35+
return cx.GetTypeInfo(nts.ElementType).Type.IsReferenceType
36+
? GetArrayElementType(nts.ElementType)
37+
: nts;
38+
case PointerTypeSyntax pts:
39+
return GetArrayElementType(pts.ElementType);
3440
default:
3541
return type;
3642
}
3743
}
3844

39-
private static Type GetElementType(Type type)
45+
private static Type GetArrayElementType(Type type)
4046
{
4147
switch (type)
4248
{
4349
case ArrayType at:
44-
return GetElementType(at.ElementType.Type);
45-
case NamedType nt when nt.symbol.IsBoundNullable():
50+
return GetArrayElementType(at.ElementType.Type);
51+
case NamedType nt when nt.symbol.IsBoundSpan() ||
52+
nt.symbol.IsBoundReadOnlySpan():
4653
return nt.TypeArguments.Single();
54+
case PointerType pt:
55+
return GetArrayElementType(pt.PointedAtType);
4756
default:
4857
return type;
4958
}
@@ -54,19 +63,27 @@ protected override void Populate(TextWriter trapFile)
5463
switch (syntax.Kind())
5564
{
5665
case SyntaxKind.ArrayType:
66+
case SyntaxKind.PointerType:
5767
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
58-
Create(cx, GetElementType(syntax), this, GetElementType(type));
68+
Create(cx, GetArrayElementType(syntax), this, GetArrayElementType(type));
5969
return;
6070
case SyntaxKind.NullableType:
6171
var nts = (NullableTypeSyntax)syntax;
6272
if (type is NamedType nt)
6373
{
64-
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
65-
Create(cx, nts.ElementType, this, nt.symbol.IsReferenceType ? nt : nt.TypeArguments[0]);
74+
if (!nt.symbol.IsReferenceType)
75+
{
76+
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
77+
Create(cx, nts.ElementType, this, nt.TypeArguments[0]);
78+
}
79+
else
80+
{
81+
Create(cx, nts.ElementType, parent, type);
82+
}
6683
}
67-
else if (type is ArrayType array)
84+
else if (type is ArrayType)
6885
{
69-
Create(cx, nts.ElementType, parent, array);
86+
Create(cx, nts.ElementType, parent, type);
7087
}
7188
return;
7289
case SyntaxKind.TupleType:
@@ -75,28 +92,20 @@ protected override void Populate(TextWriter trapFile)
7592
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
7693
tts.Elements.Zip(tt.TupleElements, (s, t) => Create(cx, s.Type, this, t.Type)).Enumerate();
7794
return;
78-
case SyntaxKind.PointerType:
79-
var pts = (PointerTypeSyntax)syntax;
80-
var pt = (PointerType)type;
81-
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
82-
Create(cx, pts.ElementType, this, pt.PointedAtType);
83-
return;
8495
case SyntaxKind.GenericName:
85-
var gns = (GenericNameSyntax)syntax;
86-
Emit(trapFile, loc ?? gns.Identifier.GetLocation(), parent, type);
87-
cx.PopulateLater(() => gns.TypeArgumentList.Arguments.Zip(type.TypeMentions, (s, t) => Create(cx, s, this, t)).Enumerate());
96+
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
97+
cx.PopulateLater(() =>
98+
((GenericNameSyntax)syntax)
99+
.TypeArgumentList
100+
.Arguments
101+
.Zip(type.TypeMentions, (s, t) => Create(cx, s, this, t)).Enumerate());
88102
return;
89103
case SyntaxKind.QualifiedName:
90-
if (type.ContainingType == null)
91-
{
92-
// namespace qualifier
93-
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
94-
}
95-
else
104+
var qns = (QualifiedNameSyntax)syntax;
105+
var right = Create(cx, qns.Right, parent, type);
106+
if (type.ContainingType is object)
96107
{
97108
// Type qualifier
98-
var qns = (QualifiedNameSyntax)syntax;
99-
var right = Create(cx, qns.Right, parent, type);
100109
Create(cx, qns.Left, right, type.ContainingType);
101110
}
102111
return;

csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,30 @@ public static bool IsBoundNullable(this ITypeSymbol type) =>
450450
public static bool IsUnboundNullable(this ITypeSymbol type) =>
451451
type.SpecialType == SpecialType.System_Nullable_T;
452452

453+
/// <summary>
454+
/// Holds if this type is <code>System.Span<T></code>.
455+
/// </summary>
456+
public static bool IsUnboundSpan(this ITypeSymbol type) =>
457+
type.ToString() == "System.Span<T>";
458+
459+
/// <summary>
460+
/// Holds if this type is of the form <code>System.Span<byte></code>.
461+
/// </summary>
462+
public static bool IsBoundSpan(this ITypeSymbol type) =>
463+
type.SpecialType == SpecialType.None && type.OriginalDefinition.IsUnboundSpan();
464+
465+
/// <summary>
466+
/// Holds if this type is <code>System.ReadOnlySpan<T></code>.
467+
/// </summary>
468+
public static bool IsUnboundReadOnlySpan(this ITypeSymbol type) =>
469+
type.ToString() == "System.ReadOnlySpan<T>";
470+
471+
/// <summary>
472+
/// Holds if this type is of the form <code>System.ReadOnlySpan<byte></code>.
473+
/// </summary>
474+
public static bool IsBoundReadOnlySpan(this ITypeSymbol type) =>
475+
type.SpecialType == SpecialType.None && type.OriginalDefinition.IsUnboundReadOnlySpan();
476+
453477
/// <summary>
454478
/// Gets the parameters of a method or property.
455479
/// </summary>

csharp/extractor/Semmle.Extraction.Tests/Layout.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void TestDefaultLayout()
6060

6161
// Test trap file generation
6262
var trapwriterFilename = project.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
63-
using (var trapwriter = project.CreateTrapWriter(logger, new TransformedPathStub("foo.cs"), false, TrapWriter.CompressionMode.Gzip))
63+
using (var trapwriter = project.CreateTrapWriter(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip, discardDuplicates: false))
6464
{
6565
trapwriter.Emit("1=*");
6666
Assert.False(File.Exists(trapwriterFilename));
@@ -104,7 +104,7 @@ public void TestLayoutFile()
104104
trapwriterFilename);
105105

106106
// Test the source archive
107-
var trapWriter = project.CreateTrapWriter(logger, new TransformedPathStub("bar.cs"), false, TrapWriter.CompressionMode.Gzip);
107+
var trapWriter = project.CreateTrapWriter(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip, discardDuplicates: false);
108108
trapWriter.Archive("layout.txt", new TransformedPathStub("layout.txt"), System.Text.Encoding.ASCII);
109109
var writtenFile = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\archive"), "layout.txt");
110110
Assert.True(File.Exists(writtenFile));

csharp/extractor/Semmle.Extraction/Layout.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public string GetTrapPath(ILogger logger, PathTransformer.ITransformedPath srcFi
6262
/// </summary>
6363
/// <param name="srcFile">The source file.</param>
6464
/// <returns>A newly created TrapWriter.</returns>
65-
public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) =>
66-
new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression);
65+
public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression, bool discardDuplicates) =>
66+
new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, trapCompression, discardDuplicates);
6767
}
6868

6969
private readonly SubProject defaultProject;

csharp/extractor/Semmle.Extraction/TrapWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public enum CompressionMode
3939

4040
private readonly CompressionMode trapCompression;
4141

42-
public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression)
42+
public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, string? trap, string? archive, CompressionMode trapCompression, bool discardDuplicates)
4343
{
4444
this.logger = logger;
4545
this.trapCompression = trapCompression;

0 commit comments

Comments
 (0)