C#: Enable nullability for Semmle.Extraction project. Some refactoring required.

This commit is contained in:
Calum Grant
2020-03-31 11:47:22 +01:00
parent 6b8a5606d6
commit 25d5c81896
21 changed files with 169 additions and 117 deletions

View File

@@ -202,7 +202,7 @@ namespace Semmle.Extraction.CSharp.Entities
return obj != null && obj.GetType() == typeof(VarargsType);
}
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, null);
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateNullableEntity(cx, null);
class VarargsTypeFactory : ICachedEntityFactory<string, VarargsType>
{

View File

@@ -27,7 +27,7 @@ namespace Semmle.Extraction.CSharp.Entities
return obj != null && obj.GetType() == typeof(NullType);
}
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, null), NullableAnnotation.None);
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateNullableEntity(cx, null), NullableAnnotation.None);
class NullTypeFactory : ICachedEntityFactory<ITypeSymbol, NullType>
{

View File

@@ -26,10 +26,9 @@ namespace Semmle.Extraction.CommentProcessing
private readonly Dictionary<Label, Key> duplicationGuardKeys = new Dictionary<Label, Key>();
private Key GetDuplicationGuardKey(Label label)
private Key? GetDuplicationGuardKey(Label label)
{
Key duplicationGuardKey;
if (duplicationGuardKeys.TryGetValue(label, out duplicationGuardKey))
if (duplicationGuardKeys.TryGetValue(label, out var duplicationGuardKey))
return duplicationGuardKey;
return null;
}
@@ -60,7 +59,7 @@ namespace Semmle.Extraction.CommentProcessing
/// <param name="elementLabel">The label of the element in the trap file.</param>
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any.</param>
/// <param name="loc">The location of the element.</param>
public void AddElement(Label elementLabel, Key duplicationGuardKey, Location loc)
public void AddElement(Label elementLabel, Key? duplicationGuardKey, Location loc)
{
if (loc != null && loc.IsInSource)
elements[loc] = elementLabel;
@@ -257,19 +256,24 @@ namespace Semmle.Extraction.CommentProcessing
CommentBindingCallback cb
)
{
CommentBlock block = new CommentBlock();
CommentBlock? block = null;
// Iterate comments until the commentEnumerator has gone past nextElement
while (nextElement == null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0)
{
if(block is null)
block = new CommentBlock(commentEnumerator.Current.Value);
if (!block.CombinesWith(commentEnumerator.Current.Value))
{
// Start of a new block, so generate the bindings for the old block first.
GenerateBindings(block, elementStack, nextElement, cb);
block = new CommentBlock();
block = new CommentBlock(commentEnumerator.Current.Value);
}
else
{
block.AddCommentLine(commentEnumerator.Current.Value);
}
block.AddCommentLine(commentEnumerator.Current.Value);
// Get the next comment.
if (!commentEnumerator.MoveNext())
@@ -280,7 +284,9 @@ namespace Semmle.Extraction.CommentProcessing
}
}
GenerateBindings(block, elementStack, nextElement, cb);
if(!(block is null))
GenerateBindings(block, elementStack, nextElement, cb);
return true;
}
@@ -332,12 +338,18 @@ namespace Semmle.Extraction.CommentProcessing
class CommentBlock : ICommentBlock
{
private readonly List<ICommentLine> lines = new List<ICommentLine>();
private readonly List<ICommentLine> lines;
public IEnumerable<ICommentLine> CommentLines => lines;
public Location Location { get; private set; }
public CommentBlock(ICommentLine firstLine)
{
lines = new List<ICommentLine> { firstLine };
Location = firstLine.Location;
}
/// <summary>
/// Determine whether commentlines should be merged.
/// </summary>

View File

@@ -74,7 +74,7 @@ namespace Semmle.Extraction.CommentProcessing
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any</param>
/// <param name="commentBlock">The comment block associated with the element</param>
/// <param name="binding">The relationship between the commentblock and the element</param>
public delegate void CommentBindingCallback(Label elementLabel, Key duplicationGuardKey, ICommentBlock commentBlock, CommentBinding binding);
public delegate void CommentBindingCallback(Label elementLabel, Key? duplicationGuardKey, ICommentBlock commentBlock, CommentBinding binding);
/// <summary>
/// Computes the binding information between comments and program elements.
@@ -88,7 +88,7 @@ namespace Semmle.Extraction.CommentProcessing
/// <param name="elementLabel">Label of the element.</param>
/// <param name="duplicationGuardKey">The duplication guard key of the element, if any.</param>
/// <param name="location">Location of the element.</param>
void AddElement(Label elementLabel, Key duplicationGuardKey, Location location);
void AddElement(Label elementLabel, Key? duplicationGuardKey, Location location);
/// <summary>
/// Registers a line of comment.

View File

@@ -34,7 +34,7 @@ namespace Semmle.Extraction
return cachedModel;
}
private SemanticModel cachedModel;
private SemanticModel? cachedModel;
/// <summary>
/// Access to the trap file.
@@ -49,7 +49,31 @@ namespace Semmle.Extraction
/// <param name="factory">The entity factory.</param>
/// <param name="init">The initializer for the entity.</param>
/// <returns>The new/existing entity.</returns>
public Entity CreateEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
public Entity CreateEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity where Type:struct
{
return CreateNonNullEntity(factory, init);
}
/// <summary>
/// Creates a new entity using the factory.
/// </summary>
/// <param name="factory">The entity factory.</param>
/// <param name="init">The initializer for the entity.</param>
/// <returns>The new/existing entity.</returns>
public Entity CreateNullableEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
{
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
}
/// <summary>
/// Creates a new entity using the factory.
/// </summary>
/// <param name="factory">The entity factory.</param>
/// <param name="init">The initializer for the entity.</param>
/// <returns>The new/existing entity.</returns>
public Entity CreateEntityFromSymbol<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init)
where Entity : ICachedEntity
where Type: ISymbol
{
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
}
@@ -135,10 +159,13 @@ namespace Semmle.Extraction
public Label GetNewLabel() => new Label(GetNewId());
private Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
public Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init)
where Entity : ICachedEntity
{
if (init is null) throw new ArgumentException("Unexpected null value", nameof(init));
if (objectEntityCache.TryGetValue(init, out var cached))
return (Entity)cached;
return (Entity)cached!;
using (StackGuard)
{
@@ -360,7 +387,7 @@ namespace Semmle.Extraction
/// <param name="optionalSymbol">Symbol for reporting errors.</param>
/// <param name="entity">The entity to populate.</param>
/// <exception cref="InternalError">Thrown on invalid trap stack behaviour.</exception>
public void Populate(ISymbol optionalSymbol, ICachedEntity entity)
public void Populate(ISymbol? optionalSymbol, ICachedEntity entity)
{
if (WritingLabel)
{
@@ -453,9 +480,9 @@ namespace Semmle.Extraction
/// <param name="message">The error message.</param>
/// <param name="entityText">A textual representation of the failed entity.</param>
/// <param name="location">The location of the error.</param>
/// <param name="stackTrace">An optional stack trace of the error, or an empty string.</param>
/// <param name="stackTrace">An optional stack trace of the error, or null.</param>
/// <param name="severity">The severity of the error.</param>
public void ExtractionError(string message, string entityText, Entities.Location location, string stackTrace = "", Severity severity = Severity.Error)
public void ExtractionError(string message, string entityText, Entities.Location location, string? stackTrace = null, Severity severity = Severity.Error)
{
var msg = new Message(message, entityText, location, stackTrace, severity);
ExtractionError(msg);
@@ -467,7 +494,7 @@ namespace Semmle.Extraction
/// <param name="message">The text of the message.</param>
/// <param name="optionalSymbol">The symbol of the error, or null.</param>
/// <param name="optionalEntity">The entity of the error, or null.</param>
public void ExtractionError(string message, ISymbol optionalSymbol, IEntity optionalEntity)
public void ExtractionError(string message, ISymbol? optionalSymbol, IEntity optionalEntity)
{
if (!(optionalSymbol is null))
{
@@ -539,7 +566,7 @@ namespace Semmle.Extraction
/// <param name="node">Optional syntax node for error reporting.</param>
/// <param name="symbol">Optional symbol for error reporting.</param>
/// <param name="a">The action to perform.</param>
static public void Try(this Context context, SyntaxNode node, ISymbol symbol, Action a)
static public void Try(this Context context, SyntaxNode? node, ISymbol? symbol, Action a)
{
try
{

View File

@@ -8,7 +8,7 @@ namespace Semmle.Extraction.Entities
readonly string assemblyPath;
readonly IAssemblySymbol assembly;
Assembly(Context cx, Microsoft.CodeAnalysis.Location init)
Assembly(Context cx, Microsoft.CodeAnalysis.Location? init)
: base(cx, init)
{
if (init == null)
@@ -19,7 +19,7 @@ namespace Semmle.Extraction.Entities
}
else
{
assembly = symbol.MetadataModule.ContainingAssembly;
assembly = init.MetadataModule.ContainingAssembly;
var identity = assembly.Identity;
var idString = identity.Name + " " + identity.Version;
assemblyPath = cx.Extractor.GetAssemblyFile(idString);
@@ -30,7 +30,7 @@ namespace Semmle.Extraction.Entities
{
if (assemblyPath != null)
{
trapFile.assemblies(this, File.Create(Context, assemblyPath), assembly.ToString(),
trapFile.assemblies(this, File.Create(Context, assemblyPath), assembly.ToString() ?? "",
assembly.Identity.Name, assembly.Identity.Version.ToString());
}
}
@@ -41,7 +41,7 @@ namespace Semmle.Extraction.Entities
public override int GetHashCode() =>
symbol == null ? 91187354 : symbol.GetHashCode();
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as Assembly;
if (other == null || other.GetType() != typeof(Assembly))
@@ -50,20 +50,20 @@ namespace Semmle.Extraction.Entities
return Equals(symbol, other.symbol);
}
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) => AssemblyConstructorFactory.Instance.CreateEntity(cx, loc);
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location? loc) => AssemblyConstructorFactory.Instance.CreateNullableEntity(cx, loc);
class AssemblyConstructorFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location, Assembly>
class AssemblyConstructorFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location?, Assembly>
{
public static readonly AssemblyConstructorFactory Instance = new AssemblyConstructorFactory();
public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location init) => new Assembly(cx, init);
public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new Assembly(cx, init);
}
public static Location CreateOutputAssembly(Context cx)
{
if (cx.Extractor.OutputPath == null)
throw new InternalError("Attempting to create the output assembly in standalone extraction mode");
return AssemblyConstructorFactory.Instance.CreateEntity(cx, null);
return AssemblyConstructorFactory.Instance.CreateNullableEntity(cx, null);
}
public override void WriteId(System.IO.TextWriter trapFile)

View File

@@ -14,7 +14,7 @@ namespace Semmle.Extraction.Entities
protected override void Populate(TextWriter trapFile)
{
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location, msg.StackTrace);
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? GeneratedLocation.Create(cx), msg.StackTrace);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;

View File

@@ -50,12 +50,12 @@ namespace Semmle.Extraction.Entities
Where(t => t.FilePath == Path).
Select(tree => tree.GetText()))
{
var rawText = text.ToString();
var rawText = text.ToString() ?? "";
var lineCounts = LineCounter.ComputeLineCounts(rawText);
if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++;
trapFile.numlines(this, lineCounts);
Context.TrapWriter.Archive(fi.FullName, text.Encoding);
Context.TrapWriter.Archive(fi.FullName, text.Encoding ?? System.Text.Encoding.Default);
}
}
@@ -111,17 +111,17 @@ namespace Semmle.Extraction.Entities
}
public static GeneratedFile Create(Context cx) =>
GeneratedFileFactory.Instance.CreateEntity(cx, null);
GeneratedFileFactory.Instance.CreateNullableEntity(cx, null);
class GeneratedFileFactory : ICachedEntityFactory<string, GeneratedFile>
class GeneratedFileFactory : ICachedEntityFactory<string?, GeneratedFile>
{
public static readonly GeneratedFileFactory Instance = new GeneratedFileFactory();
public GeneratedFile Create(Context cx, string init) => new GeneratedFile(cx);
public GeneratedFile Create(Context cx, string? init) => new GeneratedFile(cx);
}
}
public override Microsoft.CodeAnalysis.Location ReportingLocation => null;
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
class FileFactory : ICachedEntityFactory<string, File>
{

View File

@@ -45,7 +45,7 @@ namespace Semmle.Extraction.Entities
public static Folder Create(Context cx, DirectoryInfo folder) =>
FolderFactory.Instance.CreateEntity2(cx, folder);
public override Microsoft.CodeAnalysis.Location ReportingLocation => null;
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
class FolderFactory : ICachedEntityFactory<DirectoryInfo, Folder>
{
@@ -58,7 +58,7 @@ namespace Semmle.Extraction.Entities
public override int GetHashCode() => Path.GetHashCode();
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is Folder folder && folder.Path == Path;
}

View File

@@ -26,15 +26,15 @@ namespace Semmle.Extraction.Entities
public override int GetHashCode() => 98732567;
public override bool Equals(object obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
public override bool Equals(object? obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, null);
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateNullableEntity(cx, null);
class GeneratedLocationFactory : ICachedEntityFactory<string, GeneratedLocation>
class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
{
public static GeneratedLocationFactory Instance = new GeneratedLocationFactory();
public GeneratedLocation Create(Context cx, string init) => new GeneratedLocation(cx);
public GeneratedLocation Create(Context cx, string? init) => new GeneratedLocation(cx);
}
}
}

View File

@@ -1,17 +1,17 @@
namespace Semmle.Extraction.Entities
{
public abstract class Location : CachedEntity<Microsoft.CodeAnalysis.Location>
public abstract class Location : CachedEntity<Microsoft.CodeAnalysis.Location?>
{
public Location(Context cx, Microsoft.CodeAnalysis.Location init)
public Location(Context cx, Microsoft.CodeAnalysis.Location? init)
: base(cx, init) { }
public static Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) =>
public static Location Create(Context cx, Microsoft.CodeAnalysis.Location? loc) =>
(loc == null || loc.Kind == Microsoft.CodeAnalysis.LocationKind.None) ? GeneratedLocation.Create(cx)
: loc.IsInSource ? SourceLocation.Create(cx, loc)
: loc.IsInSource ? NonGeneratedSourceLocation.Create(cx, loc)
: Assembly.Create(cx, loc);
public override Microsoft.CodeAnalysis.Location ReportingLocation => symbol;
public override Microsoft.CodeAnalysis.Location? ReportingLocation => symbol;
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
}
@@ -24,7 +24,7 @@ namespace Semmle.Extraction.Entities
/// <param name="cx">The extraction context.</param>
/// <param name="location">The CodeAnalysis location.</param>
/// <returns>The Location entity.</returns>
public static Location Create(this Context cx, Microsoft.CodeAnalysis.Location location) =>
public static Location Create(this Context cx, Microsoft.CodeAnalysis.Location? location) =>
Location.Create(cx, location);
}
}

View File

@@ -1,58 +1,64 @@
using System;
using System.IO;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction.Entities
{
public class SourceLocation : Location
{
protected SourceLocation(Context cx, Microsoft.CodeAnalysis.Location init)
: base(cx, init) { }
public abstract class SourceLocation : Location {
protected SourceLocation(Context cx, Microsoft.CodeAnalysis.Location? init) : base(cx, init)
{
}
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) => SourceLocationFactory.Instance.CreateEntity(cx, loc);
public override bool NeedsPopulation => true;
}
public class NonGeneratedSourceLocation : SourceLocation
{
protected NonGeneratedSourceLocation(Context cx, Microsoft.CodeAnalysis.Location? init)
: base(cx, init)
{
if (init is null) throw new ArgumentException("Location may not be null", nameof(init));
Position = init.GetLineSpan();
FileEntity = File.Create(Context, Position.Path);
}
public new static Location Create(Context cx, Microsoft.CodeAnalysis.Location? loc) => SourceLocationFactory.Instance.CreateNullableEntity(cx, loc);
public override void Populate(TextWriter trapFile)
{
Position = symbol.GetLineSpan();
FileEntity = File.Create(Context, Position.Path);
trapFile.locations_default(this, FileEntity, Position.Span.Start.Line + 1, Position.Span.Start.Character + 1,
Position.Span.End.Line + 1, Position.Span.End.Character);
}
public override bool NeedsPopulation => true;
public FileLinePositionSpan Position
{
get;
private set;
}
public File FileEntity
{
get;
private set;
}
public override void WriteId(System.IO.TextWriter trapFile)
{
FileLinePositionSpan l = symbol.GetLineSpan();
FileEntity = Entities.File.Create(Context, l.Path);
trapFile.Write("loc,");
trapFile.WriteSubId(FileEntity);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Line + 1);
trapFile.Write(Position.Span.Start.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Character + 1);
trapFile.Write(Position.Span.Start.Character + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Line + 1);
trapFile.Write(Position.Span.End.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Character);
trapFile.Write(Position.Span.End.Character);
}
class SourceLocationFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location, SourceLocation>
class SourceLocationFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location?, SourceLocation>
{
public static readonly SourceLocationFactory Instance = new SourceLocationFactory();
public SourceLocation Create(Context cx, Microsoft.CodeAnalysis.Location init) => new SourceLocation(cx, init);
public SourceLocation Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new NonGeneratedSourceLocation(cx, init);
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Semmle.Extraction
/// <summary>
/// The location for reporting purposes.
/// </summary>
Location ReportingLocation { get; }
Location? ReportingLocation { get; }
/// <summary>
/// How the entity handles .push and .pop.
@@ -92,7 +92,7 @@ namespace Semmle.Extraction
bool NeedsPopulation { get; }
object UnderlyingObject { get; }
object? UnderlyingObject { get; }
}
/// <summary>
@@ -127,8 +127,11 @@ namespace Semmle.Extraction
/// <param name="factory">The factory used to construct the entity.</param>
/// <param name="init">The initializer for the entity, which may not be null.</param>
/// <returns>The entity.</returns>
public static Entity CreateEntity<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity(factory, init);
public static Entity CreateEntity<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init) where Type : notnull
where Entity : ICachedEntity => cx.CreateNonNullEntity(factory, init);
public static Entity CreateNullableEntity<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateNullableEntity(factory, init);
/// <summary>
/// Creates and populates a new entity, but uses a different cache.
@@ -153,7 +156,7 @@ namespace Semmle.Extraction
catch(Exception ex) // lgtm[cs/catch-of-all-exceptions]
{
trapFile.WriteLine("\"");
extractor.Message(new Message("Unhandled exception generating id", entity.ToString(), null, ex.StackTrace.ToString()));
extractor.Message(new Message("Unhandled exception generating id", entity.ToString() ?? "", null, ex.StackTrace));
}
trapFile.WriteLine();
}

View File

@@ -56,7 +56,7 @@ namespace Semmle.Extraction
public override string ToString() => Label.ToString();
public virtual Microsoft.CodeAnalysis.Location ReportingLocation => null;
public virtual Microsoft.CodeAnalysis.Location? ReportingLocation => null;
public abstract TrapStackBehaviour TrapStackBehaviour { get; }
}

View File

@@ -34,7 +34,12 @@ namespace Semmle.Extraction
public override string ToString() => "*";
public override bool Equals(object obj) => obj.GetType() == GetType();
public override bool Equals(object? obj)
{
// Expand logic to allow for nullability control flow analysis
if (obj is null) return false;
return obj.GetType() == GetType();
}
public override int GetHashCode() => 0;
@@ -87,9 +92,9 @@ namespace Semmle.Extraction
return TrapBuilder.ToString();
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (obj.GetType() != GetType())
if (obj is null || obj.GetType() != GetType())
return false;
var id = (Key)obj;
return TrapBuilder.ToString() == id.TrapBuilder.ToString();
@@ -133,8 +138,9 @@ namespace Semmle.Extraction
public static bool operator !=(Label l1, Label l2) => l1.Value != l2.Value;
public override bool Equals(object other)
public override bool Equals(object? other)
{
if (other is null) return false;
return GetType() == other.GetType() && ((Label)other).Value == Value;
}

View File

@@ -12,7 +12,7 @@ namespace Semmle.Extraction
public InternalError(ISymbol symbol, string msg)
{
Text = msg;
EntityText = symbol.ToString();
EntityText = symbol.ToString() ?? "";
Location = symbol.Locations.FirstOrDefault();
}
@@ -30,7 +30,7 @@ namespace Semmle.Extraction
Location = null;
}
public Location Location { get; }
public Location? Location { get; }
public string Text { get; }
public string EntityText { get; }

View File

@@ -26,7 +26,7 @@ namespace Semmle.Extraction
/// <summary>
/// List of blocks in the layout file.
/// </summary>
List<LayoutBlock> blocks;
readonly List<LayoutBlock> blocks;
/// <summary>
/// A subproject in the layout file.
@@ -36,14 +36,14 @@ namespace Semmle.Extraction
/// <summary>
/// The trap folder, or null for current directory.
/// </summary>
public readonly string TRAP_FOLDER;
public readonly string? TRAP_FOLDER;
/// <summary>
/// The source archive, or null to skip.
/// </summary>
public readonly string SOURCE_ARCHIVE;
public readonly string? SOURCE_ARCHIVE;
public SubProject(string traps, string archive)
public SubProject(string? traps, string? archive)
{
TRAP_FOLDER = traps;
SOURCE_ARCHIVE = archive;
@@ -73,7 +73,7 @@ namespace Semmle.Extraction
/// </summary>
/// <param name="sourceFile">The file to look up.</param>
/// <returns>The relevant subproject, or null if not found.</returns>
public SubProject LookupProjectOrNull(string sourceFile)
public SubProject? LookupProjectOrNull(string sourceFile)
{
if (!useLayoutFile) return DefaultProject;
@@ -113,13 +113,14 @@ namespace Semmle.Extraction
/// <param name="archive">Directory for source archive, or null for layout/no archive.</param>
/// <param name="layout">Path of layout file, or null for no layout.</param>
/// <exception cref="InvalidLayoutException">Failed to read layout file.</exception>
public Layout(string traps, string archive, string layout)
public Layout(string? traps, string? archive, string? layout)
{
useLayoutFile = string.IsNullOrEmpty(traps) && !string.IsNullOrEmpty(layout);
blocks = new List<LayoutBlock>();
if (useLayoutFile)
{
ReadLayoutFile(layout);
ReadLayoutFile(layout!);
DefaultProject = blocks[0].Directories;
}
else
@@ -141,15 +142,12 @@ namespace Semmle.Extraction
{
var lines = File.ReadAllLines(layout);
blocks = new List<LayoutBlock>();
int i = 0;
while (!lines[i].StartsWith("#"))
i++;
while (i < lines.Length)
{
LayoutBlock block = new LayoutBlock();
i = block.Read(lines, i);
LayoutBlock block = new LayoutBlock(lines, ref i);
blocks.Add(block);
}
@@ -197,9 +195,9 @@ namespace Semmle.Extraction
private readonly List<Condition> conditions = new List<Condition>();
public Layout.SubProject Directories;
public readonly Layout.SubProject Directories;
string ReadVariable(string name, string line)
string? ReadVariable(string name, string line)
{
string prefix = name + "=";
if (!line.StartsWith(prefix))
@@ -207,23 +205,22 @@ namespace Semmle.Extraction
return line.Substring(prefix.Length).Trim();
}
public int Read(string[] lines, int start)
public LayoutBlock(string[] lines, ref int i)
{
// first line: #name
int i = start + 1;
var TRAP_FOLDER = ReadVariable("TRAP_FOLDER", lines[i++]);
i++;
string? TRAP_FOLDER = ReadVariable("TRAP_FOLDER", lines[i++]);
// Don't care about ODASA_DB.
ReadVariable("ODASA_DB", lines[i++]);
var SOURCE_ARCHIVE = ReadVariable("SOURCE_ARCHIVE", lines[i++]);
string? SOURCE_ARCHIVE = ReadVariable("SOURCE_ARCHIVE", lines[i++]);
Directories = new Extraction.Layout.SubProject(TRAP_FOLDER, SOURCE_ARCHIVE);
Directories = new Layout.SubProject(TRAP_FOLDER, SOURCE_ARCHIVE);
// Don't care about ODASA_BUILD_ERROR_DIR.
ReadVariable("ODASA_BUILD_ERROR_DIR", lines[i++]);
while (i < lines.Length && !lines[i].StartsWith("#"))
{
conditions.Add(new Condition(lines[i++]));
}
return i;
}
public bool Matches(string path)

View File

@@ -15,23 +15,23 @@ namespace Semmle.Extraction
public readonly string Text;
public readonly string StackTrace;
public readonly string EntityText;
public readonly Entities.Location Location;
public readonly Entities.Location? Location;
public Message(string text, string entityText, Entities.Location location, string stackTrace="", Severity severity = Severity.Error)
public Message(string text, string entityText, Entities.Location? location, string? stackTrace = null, Severity severity = Severity.Error)
{
Severity = severity;
Text = text;
StackTrace = stackTrace;
StackTrace = stackTrace ?? "";
EntityText = entityText;
Location = location;
}
public static Message Create(Context cx, string text, ISymbol symbol, string stackTrace= "", Severity severity = Severity.Error)
public static Message Create(Context cx, string text, ISymbol symbol, string? stackTrace = null, Severity severity = Severity.Error)
{
return new Message(text, symbol.ToString(), Entities.Location.Create(cx, symbol.Locations.FirstOrDefault()), stackTrace, severity);
return new Message(text, symbol.ToString() ?? "", Entities.Location.Create(cx, symbol.Locations.FirstOrDefault()), stackTrace, severity);
}
public static Message Create(Context cx, string text, SyntaxNode node, string stackTrace = "", Severity severity = Severity.Error)
public static Message Create(Context cx, string text, SyntaxNode node, string? stackTrace = null, Severity severity = Severity.Error)
{
return new Message(text, node.ToString(), Entities.Location.Create(cx, node.GetLocation()), stackTrace, severity);
}

View File

@@ -7,6 +7,7 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<CodeAnalysisRuleSet>Semmle.Extraction.ruleset</CodeAnalysisRuleSet>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@@ -16,7 +16,7 @@ namespace Semmle.Extraction
public Label Label { get; set; }
public abstract Microsoft.CodeAnalysis.Location ReportingLocation { get; }
public abstract Microsoft.CodeAnalysis.Location? ReportingLocation { get; }
public override string ToString() => Label.ToString();
@@ -39,15 +39,15 @@ namespace Semmle.Extraction
public Context Context
{
get; private set;
get;
}
public Initializer symbol
{
get; private set;
get;
}
object ICachedEntity.UnderlyingObject => symbol;
object? ICachedEntity.UnderlyingObject => symbol;
public Initializer UnderlyingObject => symbol;
@@ -75,9 +75,9 @@ namespace Semmle.Extraction
Context.WithDuplicationGuard(key, a);
}
public override int GetHashCode() => symbol.GetHashCode();
public override int GetHashCode() => symbol is null ? 0 : symbol.GetHashCode();
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as CachedEntity<Initializer>;
return other?.GetType() == GetType() && Equals(other.symbol, symbol);

View File

@@ -30,7 +30,7 @@ namespace Semmle.Extraction
/// <summary>
/// The location of the src_archive directory.
/// </summary>
private readonly string archive;
private readonly string? archive;
private static readonly Encoding UTF8 = new UTF8Encoding(false);
private readonly bool discardDuplicates;
@@ -45,7 +45,7 @@ namespace Semmle.Extraction
readonly CompressionMode TrapCompression;
public TrapWriter(ILogger logger, string outputfile, string trap, string archive, bool discardDuplicates, CompressionMode trapCompression)
public TrapWriter(ILogger logger, string outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression)
{
Logger = logger;
TrapCompression = trapCompression;
@@ -101,7 +101,7 @@ namespace Semmle.Extraction
/// The output filename of the trap.
/// </summary>
public readonly string TrapFile;
string tmpFile; // The temporary file which is moved to trapFile once written.
string tmpFile = ""; // The temporary file which is moved to trapFile once written.
/// <summary>
/// Adds the specified input file to the source archive. It may end up in either the normal or long path area
@@ -236,7 +236,7 @@ namespace Semmle.Extraction
}
}
public static string NestPaths(ILogger logger, string outerpath, string innerpath, InnerPathComputation innerPathComputation)
public static string NestPaths(ILogger logger, string? outerpath, string innerpath, InnerPathComputation innerPathComputation)
{
string nested = innerpath;
if (!string.IsNullOrEmpty(outerpath))
@@ -276,7 +276,7 @@ namespace Semmle.Extraction
}
}
public static string TrapPath(ILogger logger, string folder, string filename, TrapWriter.CompressionMode trapCompression)
public static string TrapPath(ILogger logger, string? folder, string filename, TrapWriter.CompressionMode trapCompression)
{
filename = $"{Path.GetFullPath(filename)}.trap{TrapExtension(trapCompression)}";
if (string.IsNullOrEmpty(folder))