using System.IO; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { internal class Binary : Expression { private Binary(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info.Context, (BinaryExpressionSyntax)info.Node))) { } public static Expression Create(ExpressionNodeInfo info) => new Binary(info).TryPopulate(); private Expression CreateChild(Context cx, ExpressionSyntax node, int child) { // If this is a "+" expression we might need to wrap the child expressions // in ToString calls return Kind == ExprKind.ADD && Type.IsStringType() ? ImplicitToString.Create(cx, node, this, child) : Create(cx, node, this, child); } /// /// Creates an expression from a syntax node. /// Inserts type conversion as required. /// Population is deferred to avoid overflowing the stack. /// private void CreateDeferred(Context cx, ExpressionSyntax node, int child) { if (ContainsPattern(node)) // Expressions with patterns should be created right away, as they may introduce // local variables referenced in `LocalVariable::GetAlreadyCreated()` CreateChild(cx, node, child); else cx.PopulateLater(() => CreateChild(cx, node, child)); } protected override void PopulateExpression(TextWriter trapFile) { OperatorCall(trapFile, Syntax); CreateDeferred(Context, Syntax.Left, 0); CreateDeferred(Context, Syntax.Right, 1); } private static ExprKind GetKind(Context cx, BinaryExpressionSyntax node) { var k = GetBinaryTokenKind(cx, node); return GetCallType(cx, node).AdjustKind(k); } private static ExprKind GetBinaryTokenKind(Context cx, BinaryExpressionSyntax node) { var kind = node.OperatorToken.Kind(); switch (kind) { case SyntaxKind.LessThanToken: return ExprKind.LT; case SyntaxKind.PlusToken: return ExprKind.ADD; case SyntaxKind.LessThanEqualsToken: return ExprKind.LE; case SyntaxKind.GreaterThanToken: return ExprKind.GT; case SyntaxKind.AsteriskToken: return ExprKind.MUL; case SyntaxKind.AmpersandAmpersandToken: return ExprKind.LOG_AND; case SyntaxKind.EqualsEqualsToken: return ExprKind.EQ; case SyntaxKind.PercentToken: return ExprKind.REM; case SyntaxKind.MinusToken: return ExprKind.SUB; case SyntaxKind.AmpersandToken: return ExprKind.BIT_AND; case SyntaxKind.BarToken: return ExprKind.BIT_OR; case SyntaxKind.SlashToken: return ExprKind.DIV; case SyntaxKind.ExclamationEqualsToken: return ExprKind.NE; case SyntaxKind.AsKeyword: return ExprKind.AS; case SyntaxKind.IsKeyword: return ExprKind.IS; case SyntaxKind.BarBarToken: return ExprKind.LOG_OR; case SyntaxKind.GreaterThanEqualsToken: return ExprKind.GE; case SyntaxKind.GreaterThanGreaterThanToken: return ExprKind.RSHIFT; case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return ExprKind.URSHIFT; case SyntaxKind.LessThanLessThanToken: return ExprKind.LSHIFT; case SyntaxKind.CaretToken: return ExprKind.BIT_XOR; case SyntaxKind.QuestionQuestionToken: return ExprKind.NULL_COALESCING; // !! And the rest default: cx.ModelError(node, $"Unhandled operator type {kind}"); return ExprKind.UNKNOWN; } } } }