Skip to content

Commit 25d5c81

Browse files
committed
C#: Enable nullability for Semmle.Extraction project. Some refactoring required.
1 parent 6b8a560 commit 25d5c81

21 files changed

+168
-116
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public override bool Equals(object obj)
202202
return obj != null && obj.GetType() == typeof(VarargsType);
203203
}
204204

205-
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, null);
205+
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateNullableEntity(cx, null);
206206

207207
class VarargsTypeFactory : ICachedEntityFactory<string, VarargsType>
208208
{

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public override bool Equals(object obj)
2727
return obj != null && obj.GetType() == typeof(NullType);
2828
}
2929

30-
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, null), NullableAnnotation.None);
30+
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateNullableEntity(cx, null), NullableAnnotation.None);
3131

3232
class NullTypeFactory : ICachedEntityFactory<ITypeSymbol, NullType>
3333
{

csharp/extractor/Semmle.Extraction/CommentProcessing.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ public void AddComment(ICommentLine comment)
2626

2727
private readonly Dictionary<Label, Key> duplicationGuardKeys = new Dictionary<Label, Key>();
2828

29-
private Key GetDuplicationGuardKey(Label label)
29+
private Key? GetDuplicationGuardKey(Label label)
3030
{
31-
Key duplicationGuardKey;
32-
if (duplicationGuardKeys.TryGetValue(label, out duplicationGuardKey))
31+
if (duplicationGuardKeys.TryGetValue(label, out var duplicationGuardKey))
3332
return duplicationGuardKey;
3433
return null;
3534
}
@@ -60,7 +59,7 @@ static int Compare(Location l1, Location l2)
6059
/// <param name="elementLabel">The label of the element in the trap file.</param>
6160
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any.</param>
6261
/// <param name="loc">The location of the element.</param>
63-
public void AddElement(Label elementLabel, Key duplicationGuardKey, Location loc)
62+
public void AddElement(Label elementLabel, Key? duplicationGuardKey, Location loc)
6463
{
6564
if (loc != null && loc.IsInSource)
6665
elements[loc] = elementLabel;
@@ -257,19 +256,24 @@ bool GenerateBindings(
257256
CommentBindingCallback cb
258257
)
259258
{
260-
CommentBlock block = new CommentBlock();
259+
CommentBlock? block = null;
261260

262261
// Iterate comments until the commentEnumerator has gone past nextElement
263262
while (nextElement == null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0)
264263
{
264+
if(block is null)
265+
block = new CommentBlock(commentEnumerator.Current.Value);
266+
265267
if (!block.CombinesWith(commentEnumerator.Current.Value))
266268
{
267269
// Start of a new block, so generate the bindings for the old block first.
268270
GenerateBindings(block, elementStack, nextElement, cb);
269-
block = new CommentBlock();
271+
block = new CommentBlock(commentEnumerator.Current.Value);
272+
}
273+
else
274+
{
275+
block.AddCommentLine(commentEnumerator.Current.Value);
270276
}
271-
272-
block.AddCommentLine(commentEnumerator.Current.Value);
273277

274278
// Get the next comment.
275279
if (!commentEnumerator.MoveNext())
@@ -280,7 +284,9 @@ CommentBindingCallback cb
280284
}
281285
}
282286

283-
GenerateBindings(block, elementStack, nextElement, cb);
287+
if(!(block is null))
288+
GenerateBindings(block, elementStack, nextElement, cb);
289+
284290
return true;
285291
}
286292

@@ -332,12 +338,18 @@ public void GenerateBindings(CommentBindingCallback cb)
332338

333339
class CommentBlock : ICommentBlock
334340
{
335-
private readonly List<ICommentLine> lines = new List<ICommentLine>();
341+
private readonly List<ICommentLine> lines;
336342

337343
public IEnumerable<ICommentLine> CommentLines => lines;
338344

339345
public Location Location { get; private set; }
340346

347+
public CommentBlock(ICommentLine firstLine)
348+
{
349+
lines = new List<ICommentLine> { firstLine };
350+
Location = firstLine.Location;
351+
}
352+
341353
/// <summary>
342354
/// Determine whether commentlines should be merged.
343355
/// </summary>

csharp/extractor/Semmle.Extraction/Comments.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public interface ICommentBlock
7474
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any</param>
7575
/// <param name="commentBlock">The comment block associated with the element</param>
7676
/// <param name="binding">The relationship between the commentblock and the element</param>
77-
public delegate void CommentBindingCallback(Label elementLabel, Key duplicationGuardKey, ICommentBlock commentBlock, CommentBinding binding);
77+
public delegate void CommentBindingCallback(Label elementLabel, Key? duplicationGuardKey, ICommentBlock commentBlock, CommentBinding binding);
7878

7979
/// <summary>
8080
/// Computes the binding information between comments and program elements.
@@ -88,7 +88,7 @@ public interface ICommentGenerator
8888
/// <param name="elementLabel">Label of the element.</param>
8989
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any.</param>
9090
/// <param name="location">Location of the element.</param>
91-
void AddElement(Label elementLabel, Key duplicationGuardKey, Location location);
91+
void AddElement(Label elementLabel, Key? duplicationGuardKey, Location location);
9292

9393
/// <summary>
9494
/// Registers a line of comment.

csharp/extractor/Semmle.Extraction/Context.cs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public SemanticModel GetModel(SyntaxNode node)
3434
return cachedModel;
3535
}
3636

37-
private SemanticModel cachedModel;
37+
private SemanticModel? cachedModel;
3838

3939
/// <summary>
4040
/// Access to the trap file.
@@ -49,7 +49,31 @@ public SemanticModel GetModel(SyntaxNode node)
4949
/// <param name="factory">The entity factory.</param>
5050
/// <param name="init">The initializer for the entity.</param>
5151
/// <returns>The new/existing entity.</returns>
52-
public Entity CreateEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
52+
public Entity CreateEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity where Type:struct
53+
{
54+
return CreateNonNullEntity(factory, init);
55+
}
56+
57+
/// <summary>
58+
/// Creates a new entity using the factory.
59+
/// </summary>
60+
/// <param name="factory">The entity factory.</param>
61+
/// <param name="init">The initializer for the entity.</param>
62+
/// <returns>The new/existing entity.</returns>
63+
public Entity CreateNullableEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
64+
{
65+
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
66+
}
67+
68+
/// <summary>
69+
/// Creates a new entity using the factory.
70+
/// </summary>
71+
/// <param name="factory">The entity factory.</param>
72+
/// <param name="init">The initializer for the entity.</param>
73+
/// <returns>The new/existing entity.</returns>
74+
public Entity CreateEntityFromSymbol<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init)
75+
where Entity : ICachedEntity
76+
where Type: ISymbol
5377
{
5478
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
5579
}
@@ -135,10 +159,13 @@ private void CheckEntityHasUniqueLabel(string id, ICachedEntity entity)
135159

136160
public Label GetNewLabel() => new Label(GetNewId());
137161

138-
private Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
162+
public Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init)
163+
where Entity : ICachedEntity
139164
{
165+
if (init is null) throw new ArgumentException("Unexpected null value", nameof(init));
166+
140167
if (objectEntityCache.TryGetValue(init, out var cached))
141-
return (Entity)cached;
168+
return (Entity)cached!;
142169

143170
using (StackGuard)
144171
{
@@ -360,7 +387,7 @@ public void EmitTrap(TextWriter trapFile)
360387
/// <param name="optionalSymbol">Symbol for reporting errors.</param>
361388
/// <param name="entity">The entity to populate.</param>
362389
/// <exception cref="InternalError">Thrown on invalid trap stack behaviour.</exception>
363-
public void Populate(ISymbol optionalSymbol, ICachedEntity entity)
390+
public void Populate(ISymbol? optionalSymbol, ICachedEntity entity)
364391
{
365392
if (WritingLabel)
366393
{
@@ -453,9 +480,9 @@ public void BindComments(IEntity entity, Microsoft.CodeAnalysis.Location l)
453480
/// <param name="message">The error message.</param>
454481
/// <param name="entityText">A textual representation of the failed entity.</param>
455482
/// <param name="location">The location of the error.</param>
456-
/// <param name="stackTrace">An optional stack trace of the error, or an empty string.</param>
483+
/// <param name="stackTrace">An optional stack trace of the error, or null.</param>
457484
/// <param name="severity">The severity of the error.</param>
458-
public void ExtractionError(string message, string entityText, Entities.Location location, string stackTrace = "", Severity severity = Severity.Error)
485+
public void ExtractionError(string message, string entityText, Entities.Location location, string? stackTrace = null, Severity severity = Severity.Error)
459486
{
460487
var msg = new Message(message, entityText, location, stackTrace, severity);
461488
ExtractionError(msg);
@@ -467,7 +494,7 @@ public void ExtractionError(string message, string entityText, Entities.Location
467494
/// <param name="message">The text of the message.</param>
468495
/// <param name="optionalSymbol">The symbol of the error, or null.</param>
469496
/// <param name="optionalEntity">The entity of the error, or null.</param>
470-
public void ExtractionError(string message, ISymbol optionalSymbol, IEntity optionalEntity)
497+
public void ExtractionError(string message, ISymbol? optionalSymbol, IEntity optionalEntity)
471498
{
472499
if (!(optionalSymbol is null))
473500
{
@@ -539,7 +566,7 @@ static public void ModelError(this Context cx, string msg)
539566
/// <param name="node">Optional syntax node for error reporting.</param>
540567
/// <param name="symbol">Optional symbol for error reporting.</param>
541568
/// <param name="a">The action to perform.</param>
542-
static public void Try(this Context context, SyntaxNode node, ISymbol symbol, Action a)
569+
static public void Try(this Context context, SyntaxNode? node, ISymbol? symbol, Action a)
543570
{
544571
try
545572
{

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class Assembly : Location
88
readonly string assemblyPath;
99
readonly IAssemblySymbol assembly;
1010

11-
Assembly(Context cx, Microsoft.CodeAnalysis.Location init)
11+
Assembly(Context cx, Microsoft.CodeAnalysis.Location? init)
1212
: base(cx, init)
1313
{
1414
if (init == null)
@@ -19,7 +19,7 @@ public class Assembly : Location
1919
}
2020
else
2121
{
22-
assembly = symbol.MetadataModule.ContainingAssembly;
22+
assembly = init.MetadataModule.ContainingAssembly;
2323
var identity = assembly.Identity;
2424
var idString = identity.Name + " " + identity.Version;
2525
assemblyPath = cx.Extractor.GetAssemblyFile(idString);
@@ -30,7 +30,7 @@ public override void Populate(TextWriter trapFile)
3030
{
3131
if (assemblyPath != null)
3232
{
33-
trapFile.assemblies(this, File.Create(Context, assemblyPath), assembly.ToString(),
33+
trapFile.assemblies(this, File.Create(Context, assemblyPath), assembly.ToString() ?? "",
3434
assembly.Identity.Name, assembly.Identity.Version.ToString());
3535
}
3636
}
@@ -41,7 +41,7 @@ public override void Populate(TextWriter trapFile)
4141
public override int GetHashCode() =>
4242
symbol == null ? 91187354 : symbol.GetHashCode();
4343

44-
public override bool Equals(object obj)
44+
public override bool Equals(object? obj)
4545
{
4646
var other = obj as Assembly;
4747
if (other == null || other.GetType() != typeof(Assembly))
@@ -50,20 +50,20 @@ public override bool Equals(object obj)
5050
return Equals(symbol, other.symbol);
5151
}
5252

53-
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) => AssemblyConstructorFactory.Instance.CreateEntity(cx, loc);
53+
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location? loc) => AssemblyConstructorFactory.Instance.CreateNullableEntity(cx, loc);
5454

55-
class AssemblyConstructorFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location, Assembly>
55+
class AssemblyConstructorFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location?, Assembly>
5656
{
5757
public static readonly AssemblyConstructorFactory Instance = new AssemblyConstructorFactory();
5858

59-
public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location init) => new Assembly(cx, init);
59+
public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new Assembly(cx, init);
6060
}
6161

6262
public static Location CreateOutputAssembly(Context cx)
6363
{
6464
if (cx.Extractor.OutputPath == null)
6565
throw new InternalError("Attempting to create the output assembly in standalone extraction mode");
66-
return AssemblyConstructorFactory.Instance.CreateEntity(cx, null);
66+
return AssemblyConstructorFactory.Instance.CreateNullableEntity(cx, null);
6767
}
6868

6969
public override void WriteId(System.IO.TextWriter trapFile)

csharp/extractor/Semmle.Extraction/Entities/ExtractionError.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public ExtractionMessage(Context cx, Message msg) : base(cx)
1414

1515
protected override void Populate(TextWriter trapFile)
1616
{
17-
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location, msg.StackTrace);
17+
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? GeneratedLocation.Create(cx), msg.StackTrace);
1818
}
1919

2020
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;

csharp/extractor/Semmle.Extraction/Entities/File.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ public override void Populate(TextWriter trapFile)
5050
Where(t => t.FilePath == Path).
5151
Select(tree => tree.GetText()))
5252
{
53-
var rawText = text.ToString();
53+
var rawText = text.ToString() ?? "";
5454
var lineCounts = LineCounter.ComputeLineCounts(rawText);
5555
if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++;
5656

5757
trapFile.numlines(this, lineCounts);
58-
Context.TrapWriter.Archive(fi.FullName, text.Encoding);
58+
Context.TrapWriter.Archive(fi.FullName, text.Encoding ?? System.Text.Encoding.Default);
5959
}
6060
}
6161

@@ -111,17 +111,17 @@ public override void WriteId(TextWriter trapFile)
111111
}
112112

113113
public static GeneratedFile Create(Context cx) =>
114-
GeneratedFileFactory.Instance.CreateEntity(cx, null);
114+
GeneratedFileFactory.Instance.CreateNullableEntity(cx, null);
115115

116-
class GeneratedFileFactory : ICachedEntityFactory<string, GeneratedFile>
116+
class GeneratedFileFactory : ICachedEntityFactory<string?, GeneratedFile>
117117
{
118118
public static readonly GeneratedFileFactory Instance = new GeneratedFileFactory();
119119

120-
public GeneratedFile Create(Context cx, string init) => new GeneratedFile(cx);
120+
public GeneratedFile Create(Context cx, string? init) => new GeneratedFile(cx);
121121
}
122122
}
123123

124-
public override Microsoft.CodeAnalysis.Location ReportingLocation => null;
124+
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
125125

126126
class FileFactory : ICachedEntityFactory<string, File>
127127
{

csharp/extractor/Semmle.Extraction/Entities/Folder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public override void WriteId(System.IO.TextWriter trapFile)
4545
public static Folder Create(Context cx, DirectoryInfo folder) =>
4646
FolderFactory.Instance.CreateEntity2(cx, folder);
4747

48-
public override Microsoft.CodeAnalysis.Location ReportingLocation => null;
48+
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
4949

5050
class FolderFactory : ICachedEntityFactory<DirectoryInfo, Folder>
5151
{
@@ -58,7 +58,7 @@ class FolderFactory : ICachedEntityFactory<DirectoryInfo, Folder>
5858

5959
public override int GetHashCode() => Path.GetHashCode();
6060

61-
public override bool Equals(object obj)
61+
public override bool Equals(object? obj)
6262
{
6363
return obj is Folder folder && folder.Path == Path;
6464
}

csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ public override void WriteId(TextWriter trapFile)
2626

2727
public override int GetHashCode() => 98732567;
2828

29-
public override bool Equals(object obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
29+
public override bool Equals(object? obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
3030

31-
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, null);
31+
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateNullableEntity(cx, null);
3232

33-
class GeneratedLocationFactory : ICachedEntityFactory<string, GeneratedLocation>
33+
class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
3434
{
3535
public static GeneratedLocationFactory Instance = new GeneratedLocationFactory();
3636

37-
public GeneratedLocation Create(Context cx, string init) => new GeneratedLocation(cx);
37+
public GeneratedLocation Create(Context cx, string? init) => new GeneratedLocation(cx);
3838
}
3939
}
4040
}

0 commit comments

Comments
 (0)