using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities { class UserOperator : Method { protected UserOperator(Context cx, IMethodSymbol init) : base(cx, init) { } public override void Populate(TextWriter trapFile) { PopulateMethod(trapFile); PopulateModifiers(trapFile); var returnType = Type.Create(Context, symbol.ReturnType); trapFile.operators(this, symbol.Name, OperatorSymbol(Context, symbol.Name), ContainingType, returnType.TypeRef, (UserOperator)OriginalDefinition); foreach (var l in Locations) trapFile.operator_location(this, l); if (IsSourceDeclaration) { var declSyntaxReferences = symbol.DeclaringSyntaxReferences.Select(s => s.GetSyntax()).ToArray(); foreach (var declaration in declSyntaxReferences.OfType()) TypeMention.Create(Context, declaration.ReturnType, this, returnType); foreach (var declaration in declSyntaxReferences.OfType()) TypeMention.Create(Context, declaration.Type, this, returnType); } ContainingType.PopulateGenerics(); } public override bool NeedsPopulation => Context.Defines(symbol) || IsImplicitOperator(out _); public override Type ContainingType { get { IsImplicitOperator(out var containingType); return Type.Create(Context, containingType); } } public override void WriteId(TextWriter trapFile) { AddSignatureTypeToId(Context, trapFile, symbol, symbol.ReturnType); // Needed for op_explicit(), which differs only by return type. trapFile.Write(' '); BuildMethodId(this, trapFile); } /// /// For some reason, some operators are missing from the Roslyn database of mscorlib. /// This method returns true for such operators. /// /// The type containing this operator. /// bool IsImplicitOperator(out ITypeSymbol containingType) { containingType = symbol.ContainingType; if (containingType != null) { var containingNamedType = containingType as INamedTypeSymbol; return containingNamedType == null || !containingNamedType.MemberNames.Contains(symbol.Name); } var pointerType = symbol.Parameters.Select(p => p.Type).OfType().FirstOrDefault(); if (pointerType != null) { containingType = pointerType; return true; } Context.ModelError(symbol, "Unexpected implicit operator"); return true; } /// /// Convert an operator method name in to a symbolic name. /// A return value indicates whether the conversion succeeded. /// /// The method name. /// The converted operator name. public static bool OperatorSymbol(string methodName, out string operatorName) { var success = true; switch (methodName) { case "op_LogicalNot": operatorName = "!"; break; case "op_BitwiseAnd": operatorName = "&"; break; case "op_Equality": operatorName = "=="; break; case "op_Inequality": operatorName = "!="; break; case "op_UnaryPlus": case "op_Addition": operatorName = "+"; break; case "op_UnaryNegation": case "op_Subtraction": operatorName = "-"; break; case "op_Multiply": operatorName = "*"; break; case "op_Division": operatorName = "/"; break; case "op_Modulus": operatorName = "%"; break; case "op_GreaterThan": operatorName = ">"; break; case "op_GreaterThanOrEqual": operatorName = ">="; break; case "op_LessThan": operatorName = "<"; break; case "op_LessThanOrEqual": operatorName = "<="; break; case "op_Decrement": operatorName = "--"; break; case "op_Increment": operatorName = "++"; break; case "op_Implicit": operatorName = "implicit conversion"; break; case "op_Explicit": operatorName = "explicit conversion"; break; case "op_OnesComplement": operatorName = "~"; break; case "op_RightShift": operatorName = ">>"; break; case "op_LeftShift": operatorName = "<<"; break; case "op_BitwiseOr": operatorName = "|"; break; case "op_ExclusiveOr": operatorName = "^"; break; case "op_True": operatorName = "true"; break; case "op_False": operatorName = "false"; break; default: operatorName = methodName; success = false; break; } return success; } /// /// Converts a method name into a symbolic name. /// Logs an error if the name is not found. /// /// Extractor context. /// The method name. /// The converted name. public static string OperatorSymbol(Context cx, string methodName) { string result; if (!OperatorSymbol(methodName, out result)) cx.ModelError($"Unhandled operator name in OperatorSymbol(): '{methodName}'"); return result; } public new static UserOperator Create(Context cx, IMethodSymbol symbol) => UserOperatorFactory.Instance.CreateEntity(cx, symbol); class UserOperatorFactory : ICachedEntityFactory { public static readonly UserOperatorFactory Instance = new UserOperatorFactory(); public UserOperator Create(Context cx, IMethodSymbol init) => new UserOperator(cx, init); } } }