Skip to content

Commit 0d73498

Browse files
authored
Merge branch 'main' into rb/rm-library-models-cust-docs
2 parents 5b46256 + ed3dba8 commit 0d73498

File tree

18 files changed

+291
-92
lines changed

18 files changed

+291
-92
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Security.Cryptography;
77
using System.Text;
8+
using System.Text.RegularExpressions;
89
using System.Threading.Tasks;
910
using Semmle.Util;
1011
using Semmle.Util.Logging;
@@ -14,7 +15,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
1415
/// <summary>
1516
/// Main implementation of the build analysis.
1617
/// </summary>
17-
public sealed class DependencyManager : IDisposable
18+
public sealed partial class DependencyManager : IDisposable
1819
{
1920
private readonly AssemblyCache assemblyCache;
2021
private readonly ILogger logger;
@@ -783,13 +784,53 @@ private void RestoreProjects(IEnumerable<string> projects, out IEnumerable<strin
783784
CompilationInfos.Add(("Successfully restored project files", successCount.ToString()));
784785
}
785786

786-
private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<string> dllPaths)
787+
[GeneratedRegex(@"^(.+)\.(\d+\.\d+\.\d+(-(.+))?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
788+
private static partial Regex LegacyNugetPackage();
789+
790+
791+
private static IEnumerable<string> GetRestoredPackageDirectoryNames(DirectoryInfo root)
787792
{
788-
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName)
793+
return Directory.GetDirectories(root.FullName)
789794
.Select(d => Path.GetFileName(d).ToLowerInvariant());
790-
var notYetDownloadedPackages = fileContent.AllPackages
791-
.Except(alreadyDownloadedPackages)
792-
.ToList();
795+
}
796+
797+
private IEnumerable<string> GetRestoredLegacyPackageNames()
798+
{
799+
var oldPackageDirectories = GetRestoredPackageDirectoryNames(legacyPackageDirectory.DirInfo);
800+
foreach (var oldPackageDirectory in oldPackageDirectories)
801+
{
802+
// nuget install restores packages to 'packagename.version' folders (dotnet restore to 'packagename/version' folders)
803+
// typical folder names look like:
804+
// newtonsoft.json.13.0.3
805+
// there are more complex ones too, such as:
806+
// runtime.tizen.4.0.0-armel.Microsoft.NETCore.DotNetHostResolver.2.0.0-preview2-25407-01
807+
808+
var match = LegacyNugetPackage().Match(oldPackageDirectory);
809+
if (!match.Success)
810+
{
811+
logger.LogWarning($"Package directory '{oldPackageDirectory}' doesn't match the expected pattern.");
812+
continue;
813+
}
814+
815+
yield return match.Groups[1].Value.ToLowerInvariant();
816+
}
817+
}
818+
819+
private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<string> dllPaths)
820+
{
821+
var alreadyDownloadedPackages = GetRestoredPackageDirectoryNames(packageDirectory.DirInfo);
822+
var alreadyDownloadedLegacyPackages = GetRestoredLegacyPackageNames();
823+
824+
var notYetDownloadedPackages = new HashSet<string>(fileContent.AllPackages);
825+
foreach (var alreadyDownloadedPackage in alreadyDownloadedPackages)
826+
{
827+
notYetDownloadedPackages.Remove(alreadyDownloadedPackage);
828+
}
829+
foreach (var alreadyDownloadedLegacyPackage in alreadyDownloadedLegacyPackages)
830+
{
831+
notYetDownloadedPackages.Remove(alreadyDownloadedLegacyPackage);
832+
}
833+
793834
if (notYetDownloadedPackages.Count == 0)
794835
{
795836
return;

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,23 +157,35 @@ private static bool IsGroupMatch(ReadOnlySpan<char> line, Regex regex, string gr
157157
return false;
158158
}
159159

160+
private void AddPackageReference(ReadOnlySpan<char> line, string groupName, Func<Regex> regex)
161+
{
162+
foreach (var valueMatch in regex().EnumerateMatches(line))
163+
{
164+
// We can't get the group from the ValueMatch, so doing it manually:
165+
var packageName = GetGroup(line, valueMatch, groupName).ToLowerInvariant();
166+
if (!string.IsNullOrEmpty(packageName))
167+
{
168+
allPackages.Add(packageName);
169+
}
170+
}
171+
}
172+
160173
private void DoInitialize()
161174
{
162175
foreach (var file in files)
163176
{
164177
try
165178
{
179+
var isPackagesConfig = file.EndsWith("packages.config", StringComparison.OrdinalIgnoreCase);
180+
166181
foreach (ReadOnlySpan<char> line in unsafeFileReader.ReadLines(file))
167182
{
168183
// Find all the packages.
169-
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
184+
AddPackageReference(line, "Include", PackageReference);
185+
186+
if (isPackagesConfig)
170187
{
171-
// We can't get the group from the ValueMatch, so doing it manually:
172-
var packageName = GetGroup(line, valueMatch, "Include").ToLowerInvariant();
173-
if (!string.IsNullOrEmpty(packageName))
174-
{
175-
allPackages.Add(packageName);
176-
}
188+
AddPackageReference(line, "id", LegacyPackageReference);
177189
}
178190

179191
// Determine if ASP.NET is used.
@@ -223,6 +235,9 @@ private void DoInitialize()
223235
[GeneratedRegex("(?<!<!--.*)<PackageReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
224236
private static partial Regex PackageReference();
225237

238+
[GeneratedRegex("(?<!<!--.*)<package.*\\sid=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
239+
private static partial Regex LegacyPackageReference();
240+
226241
[GeneratedRegex("(?<!<!--.*)<FrameworkReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
227242
private static partial Regex FrameworkReference();
228243

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

Lines changed: 101 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Generic;
13
using System.Diagnostics.CodeAnalysis;
24
using System.IO;
35
using System.Linq;
@@ -10,8 +12,16 @@ namespace Semmle.Extraction.CSharp.Entities
1012
{
1113
internal class Constructor : Method
1214
{
15+
private readonly List<SyntaxNode> declaringReferenceSyntax;
16+
1317
private Constructor(Context cx, IMethodSymbol init)
14-
: base(cx, init) { }
18+
: base(cx, init)
19+
{
20+
declaringReferenceSyntax =
21+
Symbol.DeclaringSyntaxReferences
22+
.Select(r => r.GetSyntax())
23+
.ToList();
24+
}
1525

1626
public override void Populate(TextWriter trapFile)
1727
{
@@ -22,6 +32,12 @@ public override void Populate(TextWriter trapFile)
2232
trapFile.constructors(this, Symbol.ContainingType.Name, ContainingType, (Constructor)OriginalDefinition);
2333
trapFile.constructor_location(this, Location);
2434

35+
if (IsPrimary)
36+
{
37+
// Create a synthetic empty body for primary constructors.
38+
Statements.SyntheticEmptyBlock.Create(Context, this, 0, Location);
39+
}
40+
2541
if (Symbol.IsImplicitlyDeclared)
2642
{
2743
var lineCounts = new LineCounts() { Total = 2, Code = 1, Comment = 0 };
@@ -33,68 +49,79 @@ public override void Populate(TextWriter trapFile)
3349
protected override void ExtractInitializers(TextWriter trapFile)
3450
{
3551
// Do not extract initializers for constructed types.
36-
if (!IsSourceDeclaration)
52+
// Only extract initializers for constructors with a body and primary constructors.
53+
if (Block is null && ExpressionBody is null && !IsPrimary ||
54+
!IsSourceDeclaration)
55+
{
3756
return;
57+
}
3858

39-
var syntax = Syntax;
40-
var initializer = syntax?.Initializer;
41-
42-
if (initializer is null)
59+
if (OrdinaryConstructorSyntax?.Initializer is ConstructorInitializerSyntax initializer)
4360
{
44-
if (Symbol.MethodKind is MethodKind.Constructor)
61+
ITypeSymbol initializerType;
62+
var initializerInfo = Context.GetSymbolInfo(initializer);
63+
64+
switch (initializer.Kind())
4565
{
46-
var baseType = Symbol.ContainingType.BaseType;
47-
if (baseType is null)
48-
{
49-
if (Symbol.ContainingType.SpecialType != SpecialType.System_Object)
50-
{
51-
Context.ModelError(Symbol, "Unable to resolve base type in implicit constructor initializer");
52-
}
66+
case SyntaxKind.BaseConstructorInitializer:
67+
initializerType = Symbol.ContainingType.BaseType!;
68+
break;
69+
case SyntaxKind.ThisConstructorInitializer:
70+
initializerType = Symbol.ContainingType;
71+
break;
72+
default:
73+
Context.ModelError(initializer, "Unknown initializer");
5374
return;
54-
}
75+
}
5576

56-
var baseConstructor = baseType.InstanceConstructors.FirstOrDefault(c => c.Arity is 0);
77+
ExtractSourceInitializer(trapFile, initializerType, (IMethodSymbol?)initializerInfo.Symbol, initializer.ArgumentList, initializer.ThisOrBaseKeyword.GetLocation());
78+
}
79+
else if (PrimaryBase is PrimaryConstructorBaseTypeSyntax primaryInitializer)
80+
{
81+
var primaryInfo = Context.GetSymbolInfo(primaryInitializer);
82+
var primarySymbol = primaryInfo.Symbol;
5783

58-
if (baseConstructor is null)
84+
ExtractSourceInitializer(trapFile, primarySymbol?.ContainingType, (IMethodSymbol?)primarySymbol, primaryInitializer.ArgumentList, primaryInitializer.GetLocation());
85+
}
86+
else if (Symbol.MethodKind is MethodKind.Constructor)
87+
{
88+
var baseType = Symbol.ContainingType.BaseType;
89+
if (baseType is null)
90+
{
91+
if (Symbol.ContainingType.SpecialType != SpecialType.System_Object)
5992
{
60-
Context.ModelError(Symbol, "Unable to resolve implicit constructor initializer call");
61-
return;
93+
Context.ModelError(Symbol, "Unable to resolve base type in implicit constructor initializer");
6294
}
63-
64-
var baseConstructorTarget = Create(Context, baseConstructor);
65-
var info = new ExpressionInfo(Context,
66-
AnnotatedTypeSymbol.CreateNotAnnotated(baseType),
67-
Location,
68-
Kinds.ExprKind.CONSTRUCTOR_INIT,
69-
this,
70-
-1,
71-
isCompilerGenerated: true,
72-
null);
73-
74-
trapFile.expr_call(new Expression(info), baseConstructorTarget);
95+
return;
7596
}
76-
return;
77-
}
7897

79-
ITypeSymbol initializerType;
80-
var symbolInfo = Context.GetSymbolInfo(initializer);
98+
var baseConstructor = baseType.InstanceConstructors.FirstOrDefault(c => c.Arity is 0);
8199

82-
switch (initializer.Kind())
83-
{
84-
case SyntaxKind.BaseConstructorInitializer:
85-
initializerType = Symbol.ContainingType.BaseType!;
86-
break;
87-
case SyntaxKind.ThisConstructorInitializer:
88-
initializerType = Symbol.ContainingType;
89-
break;
90-
default:
91-
Context.ModelError(initializer, "Unknown initializer");
100+
if (baseConstructor is null)
101+
{
102+
Context.ModelError(Symbol, "Unable to resolve implicit constructor initializer call");
92103
return;
104+
}
105+
106+
var baseConstructorTarget = Create(Context, baseConstructor);
107+
var info = new ExpressionInfo(Context,
108+
AnnotatedTypeSymbol.CreateNotAnnotated(baseType),
109+
Location,
110+
Kinds.ExprKind.CONSTRUCTOR_INIT,
111+
this,
112+
-1,
113+
isCompilerGenerated: true,
114+
null);
115+
116+
trapFile.expr_call(new Expression(info), baseConstructorTarget);
93117
}
118+
}
94119

120+
private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IMethodSymbol? symbol, ArgumentListSyntax arguments, Location location)
121+
{
95122
var initInfo = new ExpressionInfo(Context,
96-
AnnotatedTypeSymbol.CreateNotAnnotated(initializerType),
97-
Context.CreateLocation(initializer.ThisOrBaseKeyword.GetLocation()),
123+
AnnotatedTypeSymbol.CreateNotAnnotated(type),
124+
Context.CreateLocation(location),
98125
Kinds.ExprKind.CONSTRUCTOR_INIT,
99126
this,
100127
-1,
@@ -103,7 +130,7 @@ protected override void ExtractInitializers(TextWriter trapFile)
103130

104131
var init = new Expression(initInfo);
105132

106-
var target = Constructor.Create(Context, (IMethodSymbol?)symbolInfo.Symbol);
133+
var target = Constructor.Create(Context, symbol);
107134
if (target is null)
108135
{
109136
Context.ModelError(Symbol, "Unable to resolve call");
@@ -112,19 +139,27 @@ protected override void ExtractInitializers(TextWriter trapFile)
112139

113140
trapFile.expr_call(init, target);
114141

115-
init.PopulateArguments(trapFile, initializer.ArgumentList, 0);
142+
init.PopulateArguments(trapFile, arguments, 0);
116143
}
117144

118-
private ConstructorDeclarationSyntax? Syntax
119-
{
120-
get
121-
{
122-
return Symbol.DeclaringSyntaxReferences
123-
.Select(r => r.GetSyntax())
124-
.OfType<ConstructorDeclarationSyntax>()
125-
.FirstOrDefault();
126-
}
127-
}
145+
private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax =>
146+
declaringReferenceSyntax
147+
.OfType<ConstructorDeclarationSyntax>()
148+
.FirstOrDefault();
149+
150+
private TypeDeclarationSyntax? PrimaryConstructorSyntax =>
151+
declaringReferenceSyntax
152+
.OfType<TypeDeclarationSyntax>()
153+
.FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax);
154+
155+
private PrimaryConstructorBaseTypeSyntax? PrimaryBase =>
156+
PrimaryConstructorSyntax?
157+
.BaseList?
158+
.Types
159+
.OfType<PrimaryConstructorBaseTypeSyntax>()
160+
.FirstOrDefault();
161+
162+
private bool IsPrimary => PrimaryConstructorSyntax is not null;
128163

129164
[return: NotNullIfNotNull(nameof(constructor))]
130165
public static new Constructor? Create(Context cx, IMethodSymbol? constructor)
@@ -160,19 +195,20 @@ public override void WriteId(EscapingTextWriter trapFile)
160195
trapFile.Write(";constructor");
161196
}
162197

163-
private ConstructorDeclarationSyntax? GetSyntax() =>
164-
Symbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<ConstructorDeclarationSyntax>().FirstOrDefault();
165-
166198
public override Microsoft.CodeAnalysis.Location? FullLocation => ReportingLocation;
167199

168200
public override Microsoft.CodeAnalysis.Location? ReportingLocation
169201
{
170202
get
171203
{
172-
var syn = GetSyntax();
173-
if (syn is not null)
204+
if (OrdinaryConstructorSyntax is not null)
205+
{
206+
return OrdinaryConstructorSyntax.Identifier.GetLocation();
207+
}
208+
209+
if (PrimaryConstructorSyntax is not null)
174210
{
175-
return syn.Identifier.GetLocation();
211+
return PrimaryConstructorSyntax.Identifier.GetLocation();
176212
}
177213

178214
if (Symbol.IsImplicitlyDeclared)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ protected virtual void PopulateMethodBody(TextWriter trapFile)
5454
var block = Block;
5555
var expr = ExpressionBody;
5656

57+
Context.PopulateLater(() => ExtractInitializers(trapFile));
58+
5759
if (block is not null || expr is not null)
5860
{
5961
Context.PopulateLater(
6062
() =>
6163
{
62-
ExtractInitializers(trapFile);
6364
if (block is not null)
6465
Statements.Block.Create(Context, block, this, 0);
6566
else

0 commit comments

Comments
 (0)