mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
C#: Separate visitors to dedicated files, rename and reorganize comment extraction related classes
This commit is contained in:
@@ -374,9 +374,9 @@ namespace Semmle.Extraction.CSharp
|
||||
if (!upToDate)
|
||||
{
|
||||
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix);
|
||||
Populators.CompilationUnit.Extract(cx, tree.GetRoot());
|
||||
CompilationUnitVisitor.Extract(cx, tree.GetRoot());
|
||||
cx.PopulateAll();
|
||||
cx.ExtractComments(cx.CommentGenerator);
|
||||
TriviaPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator);
|
||||
cx.PopulateAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,97 +21,6 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
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)
|
||||
@@ -131,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.Write(";commentline");
|
||||
}
|
||||
|
||||
private static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw)
|
||||
internal 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);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using Semmle.Extraction.CommentProcessing;
|
||||
using System;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
/// <summary>
|
||||
/// Populators for comments.
|
||||
/// </summary>
|
||||
public static class Comments
|
||||
{
|
||||
public static void ExtractComments(this Context cx, ICommentGenerator gen)
|
||||
{
|
||||
cx.Try(null, null, () =>
|
||||
{
|
||||
gen.GenerateBindings((entity, duplicationGuardKey, block, binding) =>
|
||||
{
|
||||
var commentBlock = Entities.CommentBlock.Create(cx, block);
|
||||
Action a = () =>
|
||||
{
|
||||
commentBlock.BindTo(entity, binding);
|
||||
};
|
||||
// When the duplication guard key exists, it means that the entity is guarded against
|
||||
// trap duplication (<see cref = "Context.BindComments(IEntity, Location)" />).
|
||||
// We must therefore also guard comment construction.
|
||||
if (duplicationGuardKey != null)
|
||||
cx.WithDuplicationGuard(duplicationGuardKey, a);
|
||||
else
|
||||
a();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Util.Logging;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
internal class CompilationUnitVisitor : TypeOrNamespaceVisitor
|
||||
{
|
||||
private CompilationUnitVisitor(Context cx)
|
||||
: base(cx, cx.TrapWriter.Writer, null) { }
|
||||
|
||||
public override void VisitExternAliasDirective(ExternAliasDirectiveSyntax node)
|
||||
{
|
||||
// This information is not yet extracted.
|
||||
cx.ExtractionError("Not implemented extern alias directive", node.ToFullString(), Extraction.Entities.Location.Create(cx, node.GetLocation()), "", Severity.Info);
|
||||
}
|
||||
|
||||
public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit)
|
||||
{
|
||||
foreach (var m in compilationUnit.ChildNodes())
|
||||
{
|
||||
cx.Try(m, null, () => ((CSharpSyntaxNode)m).Accept(this));
|
||||
}
|
||||
|
||||
// Gather comments:
|
||||
foreach (var trivia in compilationUnit.DescendantTrivia(compilationUnit.Span))
|
||||
{
|
||||
TriviaPopulator.ExtractTrivia(cx, trivia);
|
||||
}
|
||||
|
||||
foreach (var trivia in compilationUnit.GetLeadingTrivia())
|
||||
{
|
||||
TriviaPopulator.ExtractTrivia(cx, trivia);
|
||||
}
|
||||
|
||||
foreach (var trivia in compilationUnit.GetTrailingTrivia())
|
||||
{
|
||||
TriviaPopulator.ExtractTrivia(cx, trivia);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Extract(Context cx, SyntaxNode unit)
|
||||
{
|
||||
// Ensure that the file itself is populated in case the source file is totally empty
|
||||
Extraction.Entities.File.Create(cx, unit.SyntaxTree.FilePath);
|
||||
|
||||
((CSharpSyntaxNode)unit).Accept(new CompilationUnitVisitor(cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Semmle.Extraction.CommentProcessing;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
using System;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
/// <summary>
|
||||
/// Populators for trivias.
|
||||
/// </summary>
|
||||
public static class TriviaPopulator
|
||||
{
|
||||
public static void ExtractCommentBlocks(Context cx, ICommentGenerator gen)
|
||||
{
|
||||
cx.Try(null, null, () =>
|
||||
{
|
||||
gen.GenerateBindings((entity, duplicationGuardKey, block, binding) =>
|
||||
{
|
||||
var commentBlock = Entities.CommentBlock.Create(cx, block);
|
||||
Action a = () =>
|
||||
{
|
||||
commentBlock.BindTo(entity, binding);
|
||||
};
|
||||
// When the duplication guard key exists, it means that the entity is guarded against
|
||||
// trap duplication (<see cref = "Context.BindComments(IEntity, Location)" />).
|
||||
// We must therefore also guard comment construction.
|
||||
if (duplicationGuardKey != null)
|
||||
cx.WithDuplicationGuard(duplicationGuardKey, a);
|
||||
else
|
||||
a();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void ExtractTrivia(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(CommentLine.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(CommentLine.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(CommentLine.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
using Semmle.Extraction.Entities;
|
||||
using Semmle.Util;
|
||||
using Semmle.Util.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -82,69 +79,4 @@ namespace Semmle.Extraction.CSharp.Populators
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeOrNamespaceVisitor : TypeContainerVisitor
|
||||
{
|
||||
public TypeOrNamespaceVisitor(Context cx, TextWriter trapFile, IEntity parent)
|
||||
: base(cx, trapFile, parent) { }
|
||||
|
||||
public override void VisitUsingDirective(UsingDirectiveSyntax usingDirective)
|
||||
{
|
||||
// Only deal with "using namespace" not "using X = Y"
|
||||
if (usingDirective.Alias == null)
|
||||
new UsingDirective(cx, usingDirective, (NamespaceDeclaration)parent);
|
||||
}
|
||||
|
||||
public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
|
||||
{
|
||||
NamespaceDeclaration.Create(cx, node, (NamespaceDeclaration)parent);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CompilationUnitVisitor : TypeOrNamespaceVisitor
|
||||
{
|
||||
public CompilationUnitVisitor(Context cx)
|
||||
: base(cx, cx.TrapWriter.Writer, null) { }
|
||||
|
||||
public override void VisitExternAliasDirective(ExternAliasDirectiveSyntax node)
|
||||
{
|
||||
// This information is not yet extracted.
|
||||
cx.ExtractionError("Not implemented extern alias directive", node.ToFullString(), Extraction.Entities.Location.Create(cx, node.GetLocation()), "", Severity.Info);
|
||||
}
|
||||
|
||||
public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit)
|
||||
{
|
||||
foreach (var m in compilationUnit.ChildNodes())
|
||||
{
|
||||
cx.Try(m, null, () => ((CSharpSyntaxNode)m).Accept(this));
|
||||
}
|
||||
|
||||
// Gather comments:
|
||||
foreach (var trivia in compilationUnit.DescendantTrivia(compilationUnit.Span))
|
||||
{
|
||||
CommentLine.Extract(cx, trivia);
|
||||
}
|
||||
|
||||
foreach (var trivia in compilationUnit.GetLeadingTrivia())
|
||||
{
|
||||
CommentLine.Extract(cx, trivia);
|
||||
}
|
||||
|
||||
foreach (var trivia in compilationUnit.GetTrailingTrivia())
|
||||
{
|
||||
CommentLine.Extract(cx, trivia);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CompilationUnit
|
||||
{
|
||||
public static void Extract(Context cx, SyntaxNode unit)
|
||||
{
|
||||
// Ensure that the file itself is populated in case the source file is totally empty
|
||||
Semmle.Extraction.Entities.File.Create(cx, unit.SyntaxTree.FilePath);
|
||||
|
||||
((CSharpSyntaxNode)unit).Accept(new CompilationUnitVisitor(cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
internal class TypeOrNamespaceVisitor : TypeContainerVisitor
|
||||
{
|
||||
public TypeOrNamespaceVisitor(Context cx, TextWriter trapFile, IEntity parent)
|
||||
: base(cx, trapFile, parent) { }
|
||||
|
||||
public override void VisitUsingDirective(UsingDirectiveSyntax usingDirective)
|
||||
{
|
||||
// Only deal with "using namespace" not "using X = Y"
|
||||
if (usingDirective.Alias == null)
|
||||
new UsingDirective(cx, usingDirective, (NamespaceDeclaration)parent);
|
||||
}
|
||||
|
||||
public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
|
||||
{
|
||||
NamespaceDeclaration.Create(cx, node, (NamespaceDeclaration)parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user