Skip to content

Commit f5f4b8e

Browse files
authored
C#: Enable nullability of Semmle.Extraction.CSharp.Standalone (#4115)
1 parent 021aa64 commit f5f4b8e

File tree

10 files changed

+241
-257
lines changed

10 files changed

+241
-257
lines changed

csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs

Lines changed: 74 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,12 @@ public AssemblyCache(IEnumerable<string> dirs, IProgressMonitor progress)
3333
/// (Indexing is performed at a later stage by IndexReferences()).
3434
/// </summary>
3535
/// <param name="dir">The directory to index.</param>
36-
/// <returns>The number of DLLs within this directory.</returns>
37-
int AddReferenceDirectory(string dir)
36+
void AddReferenceDirectory(string dir)
3837
{
39-
int count = 0;
4038
foreach (var dll in new DirectoryInfo(dir).EnumerateFiles("*.dll", SearchOption.AllDirectories))
4139
{
42-
dlls.Add(dll.FullName);
43-
++count;
40+
pendingDllsToIndex.Enqueue(dll.FullName);
4441
}
45-
return count;
4642
}
4743

4844
/// <summary>
@@ -52,38 +48,42 @@ int AddReferenceDirectory(string dir)
5248
void IndexReferences()
5349
{
5450
// Read all of the files
55-
foreach (var filename in dlls)
51+
foreach (var filename in pendingDllsToIndex)
5652
{
57-
var info = AssemblyInfo.ReadFromFile(filename);
58-
59-
if (info.Valid)
60-
{
61-
assemblyInfo[filename] = info;
62-
}
63-
else
64-
{
65-
failedDlls.Add(filename);
66-
}
53+
IndexReference(filename);
6754
}
6855

6956
// Index "assemblyInfo" by version string
7057
// The OrderBy is used to ensure that we by default select the highest version number.
71-
foreach (var info in assemblyInfo.Values.OrderBy(info => info.Id))
58+
foreach (var info in assemblyInfoByFileName.Values.OrderBy(info => info.Id))
7259
{
7360
foreach (var index in info.IndexStrings)
74-
references[index] = info;
61+
assemblyInfoById[index] = info;
62+
}
63+
}
64+
65+
private void IndexReference(string filename)
66+
{
67+
try
68+
{
69+
var info = AssemblyInfo.ReadFromFile(filename);
70+
assemblyInfoByFileName[filename] = info;
71+
}
72+
catch (AssemblyLoadException)
73+
{
74+
failedAssemblyInfoFileNames.Add(filename);
7575
}
7676
}
7777

7878
/// <summary>
7979
/// The number of DLLs which are assemblies.
8080
/// </summary>
81-
public int AssemblyCount => assemblyInfo.Count;
81+
public int AssemblyCount => assemblyInfoByFileName.Count;
8282

8383
/// <summary>
8484
/// The number of DLLs which weren't assemblies. (E.g. C++).
8585
/// </summary>
86-
public int NonAssemblyCount => failedDlls.Count;
86+
public int NonAssemblyCount => failedAssemblyInfoFileNames.Count;
8787

8888
/// <summary>
8989
/// Given an assembly id, determine its full info.
@@ -93,70 +93,67 @@ void IndexReferences()
9393
public AssemblyInfo ResolveReference(string id)
9494
{
9595
// Fast path if we've already seen this before.
96-
if (failedReferences.Contains(id))
97-
return AssemblyInfo.Invalid;
96+
if (failedAssemblyInfoIds.Contains(id))
97+
throw new AssemblyLoadException();
9898

99-
var query = AssemblyInfo.MakeFromId(id);
100-
id = query.Id; // Sanitise the id.
99+
string assemblyName;
100+
(id, assemblyName) = AssemblyInfo.ComputeSanitizedAssemblyInfo(id);
101101

102102
// Look up the id in our references map.
103-
AssemblyInfo result;
104-
if (references.TryGetValue(id, out result))
103+
if (assemblyInfoById.TryGetValue(id, out AssemblyInfo? result))
105104
{
106105
// The string is in the references map.
107106
return result;
108107
}
109-
else
110-
{
111-
// Attempt to load the reference from the GAC.
112-
try
113-
{
114-
var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id);
115-
116-
if (loadedAssembly != null)
117-
{
118-
// The assembly was somewhere we haven't indexed before.
119-
// Add this assembly to our index so that subsequent lookups are faster.
120-
121-
result = AssemblyInfo.MakeFromAssembly(loadedAssembly);
122-
references[id] = result;
123-
assemblyInfo[loadedAssembly.Location] = result;
124-
return result;
125-
}
126-
}
127-
catch (FileNotFoundException)
128-
{
129-
// A suitable assembly could not be found
130-
}
131-
catch (FileLoadException)
132-
{
133-
// The assembly cannot be loaded for some reason
134-
// e.g. The name is malformed.
135-
}
136-
catch (PlatformNotSupportedException)
137-
{
138-
// .NET Core does not have a GAC.
139-
}
140108

141-
// Fallback position - locate the assembly by its lower-case name only.
142-
var asmName = query.Name.ToLowerInvariant();
109+
// Attempt to load the reference from the GAC.
110+
try
111+
{
112+
var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id);
143113

144-
if (references.TryGetValue(asmName, out result))
114+
if (loadedAssembly != null)
145115
{
146-
references[asmName] = result; // Speed up the next time the same string is resolved
116+
// The assembly was somewhere we haven't indexed before.
117+
// Add this assembly to our index so that subsequent lookups are faster.
118+
119+
result = AssemblyInfo.MakeFromAssembly(loadedAssembly);
120+
assemblyInfoById[id] = result;
121+
assemblyInfoByFileName[loadedAssembly.Location] = result;
147122
return result;
148123
}
124+
}
125+
catch (FileNotFoundException)
126+
{
127+
// A suitable assembly could not be found
128+
}
129+
catch (FileLoadException)
130+
{
131+
// The assembly cannot be loaded for some reason
132+
// e.g. The name is malformed.
133+
}
134+
catch (PlatformNotSupportedException)
135+
{
136+
// .NET Core does not have a GAC.
137+
}
149138

150-
failedReferences.Add(id); // Fail early next time
139+
// Fallback position - locate the assembly by its lower-case name only.
140+
var asmName = assemblyName.ToLowerInvariant();
151141

152-
return AssemblyInfo.Invalid;
142+
if (assemblyInfoById.TryGetValue(asmName, out result))
143+
{
144+
assemblyInfoById[asmName] = result; // Speed up the next time the same string is resolved
145+
return result;
153146
}
147+
148+
failedAssemblyInfoIds.Add(id); // Fail early next time
149+
150+
throw new AssemblyLoadException();
154151
}
155152

156153
/// <summary>
157154
/// All the assemblies we have indexed.
158155
/// </summary>
159-
public IEnumerable<AssemblyInfo> AllAssemblies => assemblyInfo.Select(a => a.Value);
156+
public IEnumerable<AssemblyInfo> AllAssemblies => assemblyInfoByFileName.Select(a => a.Value);
160157

161158
/// <summary>
162159
/// Retrieve the assembly info of a pre-cached assembly.
@@ -165,32 +162,32 @@ public AssemblyInfo ResolveReference(string id)
165162
/// <returns>The assembly info.</returns>
166163
public AssemblyInfo GetAssemblyInfo(string filepath)
167164
{
168-
if(assemblyInfo.TryGetValue(filepath, out var info))
165+
if (assemblyInfoByFileName.TryGetValue(filepath, out var info))
169166
{
170167
return info;
171168
}
172-
else
169+
170+
IndexReference(filepath);
171+
172+
if (assemblyInfoByFileName.TryGetValue(filepath, out info))
173173
{
174-
info = AssemblyInfo.ReadFromFile(filepath);
175-
assemblyInfo.Add(filepath, info);
176174
return info;
177175
}
176+
177+
throw new AssemblyLoadException();
178178
}
179179

180-
// List of pending DLLs to index.
181-
readonly List<string> dlls = new List<string>();
180+
readonly Queue<string> pendingDllsToIndex = new Queue<string>();
182181

183-
// Map from filename to assembly info.
184-
readonly Dictionary<string, AssemblyInfo> assemblyInfo = new Dictionary<string, AssemblyInfo>();
182+
readonly Dictionary<string, AssemblyInfo> assemblyInfoByFileName = new Dictionary<string, AssemblyInfo>();
185183

186184
// List of DLLs which are not assemblies.
187185
// We probably don't need to keep this
188-
readonly List<string> failedDlls = new List<string>();
186+
readonly List<string> failedAssemblyInfoFileNames = new List<string>();
189187

190188
// Map from assembly id (in various formats) to the full info.
191-
readonly Dictionary<string, AssemblyInfo> references = new Dictionary<string, AssemblyInfo>();
189+
readonly Dictionary<string, AssemblyInfo> assemblyInfoById = new Dictionary<string, AssemblyInfo>();
192190

193-
// Set of failed assembly ids.
194-
readonly HashSet<string> failedReferences = new HashSet<string>();
191+
readonly HashSet<string> failedAssemblyInfoIds = new HashSet<string>();
195192
}
196193
}

0 commit comments

Comments
 (0)