mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
151 lines
7.0 KiB
C#
151 lines
7.0 KiB
C#
using Semmle.Extraction.CommentProcessing;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Semmle.Extraction.Entities;
|
|
using System.IO;
|
|
|
|
namespace Semmle.Extraction.CSharp.Entities
|
|
{
|
|
internal class CommentLine : CachedEntity<(Microsoft.CodeAnalysis.Location, string)>, ICommentLine
|
|
{
|
|
private CommentLine(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw)
|
|
: base(cx, (loc, text))
|
|
{
|
|
Type = type;
|
|
RawText = raw;
|
|
}
|
|
|
|
public Microsoft.CodeAnalysis.Location Location => symbol.Item1;
|
|
public CommentLineType Type { get; private set; }
|
|
|
|
public string Text { get { return symbol.Item2; } }
|
|
public string RawText { get; private set; }
|
|
|
|
public static void Extract(Context cx, SyntaxTrivia trivia)
|
|
{
|
|
switch (trivia.Kind())
|
|
{
|
|
case SyntaxKind.SingleLineDocumentationCommentTrivia:
|
|
/*
|
|
This is actually a multi-line comment consisting of /// lines.
|
|
So split it up.
|
|
*/
|
|
|
|
var text = trivia.ToFullString();
|
|
|
|
var split = text.Split('\n');
|
|
var currentLocation = trivia.GetLocation().SourceSpan.Start - 3;
|
|
|
|
for (var line = 0; line < split.Length - 1; ++line)
|
|
{
|
|
var fullLine = split[line];
|
|
var nextLineLocation = currentLocation + fullLine.Length + 1;
|
|
fullLine = fullLine.TrimEnd('\r');
|
|
var trimmedLine = fullLine;
|
|
|
|
var leadingSpaces = trimmedLine.IndexOf('/');
|
|
if (leadingSpaces != -1)
|
|
{
|
|
fullLine = fullLine.Substring(leadingSpaces);
|
|
currentLocation += leadingSpaces;
|
|
trimmedLine = trimmedLine.Substring(leadingSpaces + 3); // Remove leading spaces and the "///"
|
|
trimmedLine = trimmedLine.Trim();
|
|
|
|
var span = Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(currentLocation, currentLocation + fullLine.Length);
|
|
var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree, span);
|
|
var commentType = CommentLineType.XmlDoc;
|
|
cx.CommentGenerator.AddComment(Create(cx, location, commentType, trimmedLine, fullLine));
|
|
}
|
|
else
|
|
{
|
|
cx.ModelError("Unexpected comment format");
|
|
}
|
|
currentLocation = nextLineLocation;
|
|
}
|
|
break;
|
|
|
|
case SyntaxKind.SingleLineCommentTrivia:
|
|
{
|
|
var contents = trivia.ToString().Substring(2);
|
|
var commentType = CommentLineType.Singleline;
|
|
if (contents.Length > 0 && contents[0] == '/')
|
|
{
|
|
commentType = CommentLineType.XmlDoc;
|
|
contents = contents.Substring(1); // An XML comment.
|
|
}
|
|
cx.CommentGenerator.AddComment(Create(cx, trivia.GetLocation(), commentType, contents.Trim(), trivia.ToFullString()));
|
|
}
|
|
break;
|
|
case SyntaxKind.MultiLineDocumentationCommentTrivia:
|
|
case SyntaxKind.MultiLineCommentTrivia:
|
|
/* We receive a single SyntaxTrivia for a multiline block spanning several lines.
|
|
So we split it into separate lines
|
|
*/
|
|
text = trivia.ToFullString();
|
|
|
|
split = text.Split('\n');
|
|
currentLocation = trivia.GetLocation().SourceSpan.Start;
|
|
|
|
for (var line = 0; line < split.Length; ++line)
|
|
{
|
|
var fullLine = split[line];
|
|
var nextLineLocation = currentLocation + fullLine.Length + 1;
|
|
fullLine = fullLine.TrimEnd('\r');
|
|
var trimmedLine = fullLine;
|
|
if (line == 0)
|
|
trimmedLine = trimmedLine.Substring(2);
|
|
if (line == split.Length - 1)
|
|
trimmedLine = trimmedLine.Substring(0, trimmedLine.Length - 2);
|
|
trimmedLine = trimmedLine.Trim();
|
|
|
|
var span = Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(currentLocation, currentLocation + fullLine.Length);
|
|
var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree, span);
|
|
var commentType = line == 0 ? CommentLineType.Multiline : CommentLineType.MultilineContinuation;
|
|
cx.CommentGenerator.AddComment(Create(cx, location, commentType, trimmedLine, fullLine));
|
|
currentLocation = nextLineLocation;
|
|
}
|
|
break;
|
|
// Strangely, these are reported as SingleLineCommentTrivia.
|
|
case SyntaxKind.DocumentationCommentExteriorTrivia:
|
|
cx.ModelError($"Unhandled comment type {trivia.Kind()} for {trivia}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private Extraction.Entities.Location location;
|
|
|
|
public override void Populate(TextWriter trapFile)
|
|
{
|
|
location = Context.Create(Location);
|
|
trapFile.commentline(this, Type == CommentLineType.MultilineContinuation ? CommentLineType.Multiline : Type, Text, RawText);
|
|
trapFile.commentline_location(this, location);
|
|
}
|
|
|
|
public override Microsoft.CodeAnalysis.Location ReportingLocation => location.symbol;
|
|
|
|
public override bool NeedsPopulation => true;
|
|
|
|
public override void WriteId(TextWriter trapFile)
|
|
{
|
|
trapFile.WriteSubId(Context.Create(Location));
|
|
trapFile.Write(";commentline");
|
|
}
|
|
|
|
private static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw)
|
|
{
|
|
var init = (loc, type, text, raw);
|
|
return CommentLineFactory.Instance.CreateEntity(cx, init, init);
|
|
}
|
|
|
|
private class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine>
|
|
{
|
|
public static CommentLineFactory Instance { get; } = new CommentLineFactory();
|
|
|
|
public CommentLine Create(Context cx, (Microsoft.CodeAnalysis.Location, CommentLineType, string, string) init) =>
|
|
new CommentLine(cx, init.Item1, init.Item2, init.Item3, init.Item4);
|
|
}
|
|
|
|
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
|
|
}
|
|
}
|