using System; using System.Linq; using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; namespace Semmle.Extraction.CSharp.Populators { class Symbols : SymbolVisitor { 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; } } } /// /// Tries to recover from an ErrorType. /// /// /// Extraction context. /// The type to disambiguate. /// 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); /// /// Gets the symbol for a particular syntax node. /// Throws an exception if the symbol is not found. /// /// /// /// This gives a nicer message than a "null pointer exception", /// and should be used where we require a symbol to be resolved. /// /// /// The extraction context. /// The syntax node. /// The resolved symbol. 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; } /// /// Determines the type of a node, or null /// if the type could not be determined. /// /// Extractor context. /// The node to determine. /// The type symbol of the node, or null. public static ITypeSymbol GetType(this Context cx, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode node) { var info = GetTypeInfo(cx, node); return cx.DisambiguateType(info.Type); } } }