mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
C#: Migrate extractor to this repository.
This commit is contained in:
69
csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs
Normal file
69
csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
class Ast : CSharpSyntaxVisitor
|
||||
{
|
||||
readonly Context cx;
|
||||
readonly IExpressionParentEntity parent;
|
||||
readonly int child;
|
||||
|
||||
public Ast(Context cx, IExpressionParentEntity parent, int child)
|
||||
{
|
||||
this.cx = cx;
|
||||
this.parent = parent;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
public override void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
cx.ModelError(node, "Unhandled syntax node {0}", node.Kind());
|
||||
}
|
||||
|
||||
public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
|
||||
{
|
||||
((Property)parent).VisitDeclaration(cx, node);
|
||||
}
|
||||
|
||||
|
||||
public override void VisitArgumentList(ArgumentListSyntax node)
|
||||
{
|
||||
int c = 0;
|
||||
foreach (var m in node.Arguments)
|
||||
{
|
||||
cx.Extract(m, parent, c++);
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitArgument(ArgumentSyntax node)
|
||||
{
|
||||
Expression.Create(cx, node.Expression, parent, child);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AstExtensions
|
||||
{
|
||||
public static void Extract(this Context cx, CSharpSyntaxNode node, IExpressionParentEntity parent, int child)
|
||||
{
|
||||
using (cx.StackGuard)
|
||||
{
|
||||
try
|
||||
{
|
||||
node.Accept(new Ast(cx, parent, child));
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
cx.ModelError(node, "Exception processing syntax node of type {0}: {1}", node.Kind(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Extract(this Context cx, SyntaxNode node, IEntity parent, int child)
|
||||
{
|
||||
cx.Extract(((CSharpSyntaxNode)node), parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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,125 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
using Semmle.Extraction.Entities;
|
||||
using Semmle.Util.Logging;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
public class TypeContainerVisitor : CSharpSyntaxVisitor
|
||||
{
|
||||
protected readonly Context cx;
|
||||
protected readonly IEntity parent;
|
||||
|
||||
public TypeContainerVisitor(Context cx, IEntity parent)
|
||||
{
|
||||
this.cx = cx;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
throw new InternalError(node, "Unhandled top-level syntax node");
|
||||
}
|
||||
|
||||
public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node)
|
||||
{
|
||||
Entities.NamedType.Create(cx, cx.Model(node).GetDeclaredSymbol(node)).ExtractRecursive(parent);
|
||||
}
|
||||
|
||||
public override void VisitClassDeclaration(ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
Entities.Type.Create(cx, cx.Model(classDecl).GetDeclaredSymbol(classDecl)).ExtractRecursive(parent);
|
||||
}
|
||||
|
||||
public override void VisitStructDeclaration(StructDeclarationSyntax node)
|
||||
{
|
||||
Entities.Type.Create(cx, cx.Model(node).GetDeclaredSymbol(node)).ExtractRecursive(parent);
|
||||
}
|
||||
|
||||
public override void VisitEnumDeclaration(EnumDeclarationSyntax node)
|
||||
{
|
||||
Entities.Type.Create(cx, cx.Model(node).GetDeclaredSymbol(node)).ExtractRecursive(parent);
|
||||
}
|
||||
|
||||
public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
|
||||
{
|
||||
Entities.Type.Create(cx, cx.Model(node).GetDeclaredSymbol(node)).ExtractRecursive(parent);
|
||||
}
|
||||
|
||||
public override void VisitAttributeList(AttributeListSyntax node)
|
||||
{
|
||||
if (cx.Extractor.Standalone) return;
|
||||
|
||||
var outputAssembly = Assembly.CreateOutputAssembly(cx);
|
||||
foreach (var attribute in node.Attributes)
|
||||
{
|
||||
var ae = new Attribute(cx, attribute, outputAssembly);
|
||||
cx.BindComments(ae, attribute.GetLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypeOrNamespaceVisitor : TypeContainerVisitor
|
||||
{
|
||||
public TypeOrNamespaceVisitor(Context cx, IEntity parent)
|
||||
: base(cx, 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);
|
||||
}
|
||||
}
|
||||
|
||||
class CompilationUnitVisitor : TypeOrNamespaceVisitor
|
||||
{
|
||||
public CompilationUnitVisitor(Context cx)
|
||||
: base(cx, null) { }
|
||||
|
||||
public override void VisitExternAliasDirective(ExternAliasDirectiveSyntax node)
|
||||
{
|
||||
// This information is not yet extracted.
|
||||
cx.Extractor.Message(new Message { severity = Severity.Info, message = "Ignoring extern alias directive" });
|
||||
}
|
||||
|
||||
public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit)
|
||||
{
|
||||
foreach (var m in compilationUnit.ChildNodes())
|
||||
{
|
||||
cx.Try(m, null, () => ((CSharpSyntaxNode)m).Accept(this));
|
||||
}
|
||||
|
||||
// Gather comments:
|
||||
foreach (SyntaxTrivia 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)
|
||||
{
|
||||
((CSharpSyntaxNode)unit).Accept(new CompilationUnitVisitor(cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
public static class LocationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Manually extend a location.
|
||||
/// </summary>
|
||||
/// <param name="l1">The location to extend.</param>
|
||||
/// <param name="n2">The node to extend the location to.</param>
|
||||
/// <returns>Extended location.</returns>
|
||||
public static Location ExtendLocation(this Location l1, SyntaxNode n2)
|
||||
{
|
||||
if (n2 == null)
|
||||
{
|
||||
return l1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var l2 = n2.FixedLocation();
|
||||
int start = System.Math.Min(l1.SourceSpan.Start, l2.SourceSpan.Start);
|
||||
int end = System.Math.Max(l1.SourceSpan.End, l2.SourceSpan.End);
|
||||
return Location.Create(n2.SyntaxTree, new Microsoft.CodeAnalysis.Text.TextSpan(start, end - start));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the location of some syntax nodes
|
||||
/// to make them more suitable for displaying results.
|
||||
/// Sometimes we do not wish to highlight the whole node,
|
||||
/// so select a sub-node such as the name.
|
||||
/// !! Refactor this into each entity.
|
||||
/// </summary>
|
||||
/// <param name="node">The syntax node.</param>
|
||||
/// <returns>The fixed location.</returns>
|
||||
public static Location FixedLocation(this SyntaxNode node)
|
||||
{
|
||||
Location result;
|
||||
switch (node.Kind())
|
||||
{
|
||||
case SyntaxKind.EqualsValueClause:
|
||||
result = ((EqualsValueClauseSyntax)node).Value.FixedLocation();
|
||||
break;
|
||||
case SyntaxKind.OperatorDeclaration:
|
||||
{
|
||||
var decl = (OperatorDeclarationSyntax)node;
|
||||
result = decl.OperatorKeyword.GetLocation().ExtendLocation(decl.ParameterList);
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ConversionOperatorDeclaration:
|
||||
{
|
||||
var decl = (ConversionOperatorDeclarationSyntax)node;
|
||||
result = decl.OperatorKeyword.GetLocation();
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.DelegateDeclaration:
|
||||
{
|
||||
var decl = (DelegateDeclarationSyntax)node;
|
||||
return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList);
|
||||
}
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.StructDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
{
|
||||
var decl = (TypeDeclarationSyntax)node;
|
||||
return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList);
|
||||
}
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return ((EnumDeclarationSyntax)node).Identifier.GetLocation();
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
{
|
||||
var decl = (MethodDeclarationSyntax)node;
|
||||
return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList);
|
||||
}
|
||||
case SyntaxKind.ConstructorDeclaration:
|
||||
{
|
||||
var decl = (ConstructorDeclarationSyntax)node;
|
||||
return decl.Identifier.GetLocation();
|
||||
}
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return ((ParenthesizedExpressionSyntax)node).Expression.FixedLocation();
|
||||
case SyntaxKind.CatchDeclaration:
|
||||
return ((CatchDeclarationSyntax)node).Identifier.GetLocation();
|
||||
case SyntaxKind.LabeledStatement:
|
||||
return ((LabeledStatementSyntax)node).Identifier.GetLocation();
|
||||
default:
|
||||
result = node.GetLocation();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Location GetSymbolLocation(this ISymbol symbol)
|
||||
{
|
||||
return symbol.DeclaringSyntaxReferences.Any() ?
|
||||
symbol.DeclaringSyntaxReferences.First().GetSyntax().FixedLocation() :
|
||||
symbol.Locations.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
public static class MethodExtensions
|
||||
{
|
||||
class AstLineCounter : CSharpSyntaxVisitor<LineCounts>
|
||||
{
|
||||
public override LineCounts DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
string text = node.SyntaxTree.GetText().GetSubText(node.GetLocation().SourceSpan).ToString();
|
||||
return Semmle.Util.LineCounter.ComputeLineCounts(text);
|
||||
}
|
||||
|
||||
public override LineCounts VisitMethodDeclaration(MethodDeclarationSyntax method)
|
||||
{
|
||||
return Visit(method.Identifier, method.Body ?? (SyntaxNode)method.ExpressionBody);
|
||||
}
|
||||
|
||||
public LineCounts Visit(SyntaxToken identifier, SyntaxNode body)
|
||||
{
|
||||
int start = identifier.GetLocation().SourceSpan.Start;
|
||||
int end = body.GetLocation().SourceSpan.End - 1;
|
||||
|
||||
var textSpan = new Microsoft.CodeAnalysis.Text.TextSpan(start, end - start);
|
||||
|
||||
string text = body.SyntaxTree.GetText().GetSubText(textSpan) + "\r\n";
|
||||
return Semmle.Util.LineCounter.ComputeLineCounts(text);
|
||||
}
|
||||
|
||||
public override LineCounts VisitConstructorDeclaration(ConstructorDeclarationSyntax method)
|
||||
{
|
||||
return Visit(method.Identifier, (SyntaxNode)method.Body ?? method.ExpressionBody);
|
||||
}
|
||||
|
||||
public override LineCounts VisitDestructorDeclaration(DestructorDeclarationSyntax method)
|
||||
{
|
||||
return Visit(method.Identifier, (SyntaxNode)method.Body ?? method.ExpressionBody);
|
||||
}
|
||||
|
||||
public override LineCounts VisitOperatorDeclaration(OperatorDeclarationSyntax node)
|
||||
{
|
||||
return Visit(node.OperatorToken, node.Body ?? (SyntaxNode)node.ExpressionBody);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NumberOfLines(this Context cx, ISymbol symbol, IEntity callable)
|
||||
{
|
||||
foreach (var decl in symbol.DeclaringSyntaxReferences)
|
||||
{
|
||||
cx.NumberOfLines((CSharpSyntaxNode)decl.GetSyntax(), callable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NumberOfLines(this Context cx, CSharpSyntaxNode node, IEntity callable)
|
||||
{
|
||||
var lineCounts = node.Accept(new AstLineCounter());
|
||||
cx.Emit(Tuples.numlines(callable, lineCounts));
|
||||
}
|
||||
}
|
||||
}
|
||||
136
csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs
Normal file
136
csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Semmle.Extraction.CSharp.Entities;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Populators
|
||||
{
|
||||
class Symbols : SymbolVisitor<IEntity>
|
||||
{
|
||||
readonly Context cx;
|
||||
|
||||
public Symbols(Context cx)
|
||||
{
|
||||
this.cx = cx;
|
||||
}
|
||||
|
||||
public override IEntity DefaultVisit(ISymbol symbol) => throw new InternalError(symbol, "Unhandled symbol '{0}' of kind '{1}'", symbol, symbol.Kind);
|
||||
|
||||
public override IEntity VisitArrayType(IArrayTypeSymbol array) => ArrayType.Create(cx, array);
|
||||
|
||||
public override IEntity VisitMethod(IMethodSymbol methodDecl)
|
||||
{
|
||||
return Method.Create(cx, methodDecl);
|
||||
}
|
||||
|
||||
public override IEntity VisitField(IFieldSymbol field) => Field.Create(cx, field);
|
||||
|
||||
public override IEntity VisitNamedType(INamedTypeSymbol type) =>
|
||||
type.IsTupleType ? TupleType.Create(cx, type) : (IEntity)NamedType.Create(cx, type);
|
||||
|
||||
public override IEntity VisitNamespace(INamespaceSymbol ns) => Namespace.Create(cx, ns);
|
||||
|
||||
public override IEntity VisitParameter(IParameterSymbol param) => Parameter.GetAlreadyCreated(cx, param);
|
||||
|
||||
public override IEntity VisitProperty(IPropertySymbol symbol) => Property.Create(cx, symbol);
|
||||
|
||||
public override IEntity VisitEvent(IEventSymbol symbol) => Event.Create(cx, symbol);
|
||||
|
||||
public override IEntity VisitTypeParameter(ITypeParameterSymbol param) => TypeParameter.Create(cx, param);
|
||||
|
||||
public override IEntity VisitPointerType(IPointerTypeSymbol symbol) => PointerType.Create(cx, symbol);
|
||||
|
||||
public override IEntity VisitDynamicType(IDynamicTypeSymbol symbol) => DynamicType.Create(cx, symbol);
|
||||
}
|
||||
|
||||
public static class SymbolsExtensions
|
||||
{
|
||||
public static IEntity CreateEntity(this Context cx, ISymbol symbol)
|
||||
{
|
||||
if (symbol == null) return null;
|
||||
|
||||
using (cx.StackGuard)
|
||||
{
|
||||
try
|
||||
{
|
||||
return symbol.Accept(new Symbols(cx));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
cx.ModelError(symbol, "Exception processing symbol '{2}' of type '{0}': {1}", symbol.Kind, e, symbol);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to recover from an ErrorType.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="cx">Extraction context.</param>
|
||||
/// <param name="type">The type to disambiguate.</param>
|
||||
/// <returns></returns>
|
||||
public static ITypeSymbol DisambiguateType(this Context cx, ITypeSymbol type)
|
||||
{
|
||||
/* A type could not be determined.
|
||||
* Sometimes this happens due to a missing reference,
|
||||
* or sometimes because the same type is defined in multiple places.
|
||||
*
|
||||
* In the case that a symbol is multiply-defined, Roslyn tells you which
|
||||
* symbols are candidates. It usually resolves to the same DB entity,
|
||||
* so it's reasonably safe to just pick a candidate.
|
||||
*
|
||||
* The conservative option would be to resolve all error types as null.
|
||||
*/
|
||||
|
||||
var errorType = type as IErrorTypeSymbol;
|
||||
|
||||
return errorType != null && errorType.CandidateSymbols.Any() ?
|
||||
errorType.CandidateSymbols.First() as ITypeSymbol :
|
||||
type;
|
||||
}
|
||||
|
||||
public static TypeInfo GetTypeInfo(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node) =>
|
||||
cx.Model(node).GetTypeInfo(node);
|
||||
|
||||
public static SymbolInfo GetSymbolInfo(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node) =>
|
||||
cx.Model(node).GetSymbolInfo(node);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the symbol for a particular syntax node.
|
||||
/// Throws an exception if the symbol is not found.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This gives a nicer message than a "null pointer exception",
|
||||
/// and should be used where we require a symbol to be resolved.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="cx">The extraction context.</param>
|
||||
/// <param name="node">The syntax node.</param>
|
||||
/// <returns>The resolved symbol.</returns>
|
||||
public static ISymbol GetSymbol(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node)
|
||||
{
|
||||
var info = GetSymbolInfo(cx, node);
|
||||
if (info.Symbol == null)
|
||||
{
|
||||
throw new InternalError(node, "Could not resolve symbol");
|
||||
}
|
||||
|
||||
return info.Symbol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the type of a node, or null
|
||||
/// if the type could not be determined.
|
||||
/// </summary>
|
||||
/// <param name="cx">Extractor context.</param>
|
||||
/// <param name="node">The node to determine.</param>
|
||||
/// <returns>The type symbol of the node, or null.</returns>
|
||||
public static ITypeSymbol GetType(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node)
|
||||
{
|
||||
var info = GetTypeInfo(cx, node);
|
||||
return cx.DisambiguateType(info.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user