using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Semmle.Util;
namespace Semmle.Extraction.CommentProcessing
{
// The lexical type of the comment.
public enum CommentType
{
Singleline, // Comment starting // ...
XmlDoc, // Comment starting /// ...
Multiline, // Comment starting /* ..., even if the comment only spans one line.
MultilineContinuation // The second and subsequent lines of comment in a multiline comment.
};
// Relationship between a comment and a program element.
public enum Binding
{
Parent, // The parent element of a comment
Best, // The most likely element associated with a comment
Before, // The element before the comment
After // The element after the comment
};
///
/// A single line of text in a comment.
///
public interface ICommentLine
{
Location Location { get; }
CommentType Type { get; }
// Trimmed text of the comment.
string Text { get; }
// Complete text of the comment including leading/trailing whitespace and comment markers.
string RawText { get; }
}
///
/// A block of comment lines combined into one unit.
///
public interface ICommentBlock
{
Location Location { get; }
IList CommentLines { get; }
}
///
/// Output for generated comment associations.
///
/// The label of the element
/// The duplication guard key of the element, if any
/// The comment block associated with the element
/// The relationship between the commentblock and the element
public delegate void CommentBinding(Label elementLabel, Key duplicationGuardKey, ICommentBlock commentBlock, Binding binding);
///
/// Used by the populator to generate binding information between comments and program elements.
///
public interface ICommentGenerator
{
///
/// Registers the location of a program element to associate comments with.
/// Can be called in any order.
///
/// Label of the element.
/// The duplication guard key of the element, if any.
/// Location of the element.
void RegisterElementLocation(Label elementLabel, Key duplicationGuardKey, Location location);
void AddComment(ICommentLine comment);
///
/// Generate all binding information.
///
/// Receiver of the binding information.
void GenerateBindings(CommentBinding cb);
}
static class LocationExtension
{
public static int StartLine(this Location loc) => loc.GetLineSpan().Span.Start.Line;
public static int StartColumn(this Location loc) => loc.GetLineSpan().Span.Start.Character;
public static int EndLine(this Location loc) => loc.GetLineSpan().Span.End.Line;
///
/// Whether one Location outer completely contains another Location inner.
///
/// The outer location.
/// The inner location
/// Whether inner is completely container in outer.
public static bool Contains(this Location outer, Location inner)
{
bool sameFile = outer.SourceTree == inner.SourceTree;
bool startsBefore = outer.SourceSpan.Start <= inner.SourceSpan.Start;
bool endsAfter = outer.SourceSpan.End >= inner.SourceSpan.End;
return sameFile && startsBefore && endsAfter;
}
///
/// Whether one Location ends before another starts.
///
/// The Location coming before
/// The Location coming after
/// Whether 'before' comes before 'after'.
public static bool Before(this Location before, Location after)
{
bool sameFile = before.SourceTree == after.SourceTree;
bool endsBefore = before.SourceSpan.End <= after.SourceSpan.Start;
return sameFile && endsBefore;
}
}
///
/// Implements the comment processor.
/// Registers locations of comments and program elements,
/// then generates binding information.
///
class CommentProcessor : ICommentGenerator
{
public void AddComment(ICommentLine comment)
{
comments[comment.Location] = comment;
}
// Comments sorted by location.
readonly SortedDictionary comments = new SortedDictionary(new LocationComparer());
// Program elements sorted by location.
readonly SortedDictionary elements = new SortedDictionary(new LocationComparer());
readonly Dictionary