All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.rubysrc2cpg.parser.AntlrContextHelpers.scala Maven / Gradle / Ivy

package io.joern.rubysrc2cpg.parser

import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.TextSpan
import io.joern.rubysrc2cpg.parser.RubyParser.*
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.misc.Interval
import org.slf4j.LoggerFactory

import scala.jdk.CollectionConverters.*

object AntlrContextHelpers {

  private val logger = LoggerFactory.getLogger(getClass)

  sealed implicit class ParserRuleContextHelper(ctx: ParserRuleContext) {
    def toTextSpan: TextSpan = {
      // The stopIndex could precede startIndex for rules that do not consume anything, cf. `getStop`.
      // We need to make sure this doesn't happen when building the `text` field.
      val startIndex = ctx.getStart.getStartIndex
      val stopIndex  = math.max(startIndex, ctx.getStop.getStopIndex)
      TextSpan(
        line = Option(ctx.getStart.getLine),
        column = Option(ctx.getStart.getCharPositionInLine),
        lineEnd = Option(ctx.getStop.getLine),
        columnEnd = Option(ctx.getStop.getCharPositionInLine),
        text = ctx.getStart.getInputStream.getText(new Interval(startIndex, stopIndex))
      )
    }

    /** @return
      *   true if this token's text is the same as a keyword, false if otherwise.
      */
    def isKeyword: Boolean = {
      // See RubyParser for why the bounds are used
      val minBound = 19
      val maxBound = 56
      val typ      = ctx.start.getType
      typ >= minBound && typ <= maxBound
    }
  }

  sealed implicit class CompoundStatementContextHelper(ctx: CompoundStatementContext) {
    def getStatements: List[ParserRuleContext] =
      Option(ctx.statements()).map(_.statement().asScala.toList).getOrElse(List())
  }

  sealed implicit class NumericLiteralContextHelper(ctx: NumericLiteralContext) {
    def hasSign: Boolean = Option(ctx.sign).isDefined
  }

  sealed implicit class SingleOrDoubleQuotedStringContextHelper(ctx: SingleOrDoubleQuotedStringContext) {
    def isInterpolated: Boolean = Option(ctx.doubleQuotedString()).exists(_.isInterpolated)
  }

  sealed implicit class DoubleQuotedStringContextHelper(ctx: DoubleQuotedStringContext) {
    def interpolations: List[ParserRuleContext] = ctx
      .doubleQuotedStringContent()
      .asScala
      .filter(ctx => Option(ctx.compoundStatement()).isDefined)
      .map(ctx => ctx.compoundStatement())
      .toList
    def isInterpolated: Boolean = interpolations.nonEmpty
  }

  sealed implicit class QuotedExpandedStringLiteralContextHelper(ctx: QuotedExpandedStringLiteralContext) {
    def interpolations: List[ParserRuleContext] = ctx
      .quotedExpandedLiteralStringContent()
      .asScala
      .filter(ctx => Option(ctx.compoundStatement()).isDefined)
      .map(ctx => ctx.compoundStatement())
      .toList
    def isInterpolated: Boolean = interpolations.nonEmpty
  }

  sealed implicit class DoubleQuotedStringExpressionContextHelper(ctx: DoubleQuotedStringExpressionContext) {
    def interpolations: List[ParserRuleContext] = ctx.doubleQuotedString().interpolations ++ ctx
      .singleOrDoubleQuotedString()
      .asScala
      .filter(_.isInterpolated)
      .flatMap(_.doubleQuotedString().interpolations)
      .toList

    def concatenations: List[SingleOrDoubleQuotedStringContext] = ctx.singleOrDoubleQuotedString().asScala.toList
    def isInterpolated: Boolean = ctx.doubleQuotedString().isInterpolated || concatenations.exists(_.isInterpolated)
  }

  sealed implicit class DoubleQuotedSymbolExpressionContextHelper(ctx: DoubleQuotedSymbolLiteralContext) {
    def interpolations: List[ParserRuleContext] = ctx.doubleQuotedString().interpolations ++ (ctx
      .doubleQuotedString() :: Nil)
      .filter(_.isInterpolated)
      .flatMap(_.interpolations)

    def concatenations: List[DoubleQuotedStringContext] = ctx.doubleQuotedString() :: Nil
    def isInterpolated: Boolean = ctx.doubleQuotedString().isInterpolated || concatenations.exists(_.isInterpolated)
  }

  sealed implicit class SingleQuotedStringExpressionContextHelper(ctx: SingleQuotedStringExpressionContext) {
    def concatenations: List[SingleOrDoubleQuotedStringContext] = ctx.singleOrDoubleQuotedString().asScala.toList
    def isInterpolated: Boolean                                 = concatenations.exists(_.isInterpolated)
    def interpolations: List[ParserRuleContext] =
      concatenations.filter(_.isInterpolated).flatMap(_.doubleQuotedString().interpolations)
  }

  sealed implicit class RegularExpressionLiteralContextHelper(ctx: RegularExpressionLiteralContext) {
    def isStatic: Boolean  = !isDynamic
    def isDynamic: Boolean = interpolations.nonEmpty

    def interpolations: List[ParserRuleContext] = ctx
      .regexpLiteralContent()
      .asScala
      .filter(ctx => Option(ctx.compoundStatement()).isDefined)
      .map(ctx => ctx.compoundStatement())
      .toList
  }

  sealed implicit class QuotedExpandedRegularExpressionLiteralContextHelper(
    ctx: QuotedExpandedRegularExpressionLiteralContext
  ) {

    def isStatic: Boolean  = !isDynamic
    def isDynamic: Boolean = interpolations.nonEmpty

    def interpolations: List[ParserRuleContext] = ctx
      .quotedExpandedLiteralStringContent()
      .asScala
      .filter(ctx => Option(ctx.compoundStatement()).isDefined)
      .map(ctx => ctx.compoundStatement())
      .toList

  }

  sealed implicit class CurlyBracesBlockContextHelper(ctx: CurlyBracesBlockContext) {
    def parameters: List[ParserRuleContext] = Option(ctx.blockParameter()).map(_.parameters).getOrElse(List())
  }

  sealed implicit class BlockParameterContextHelper(ctx: BlockParameterContext) {
    def parameters: List[ParserRuleContext] = Option(ctx.parameterList()).map(_.parameters).getOrElse(List())
  }

  sealed implicit class CommandArgumentContextHelper(ctx: CommandArgumentContext) {
    def arguments: List[ParserRuleContext] = ctx match {
      case ctx: CommandCommandArgumentListContext         => ctx.command() :: Nil
      case ctx: CommandArgumentCommandArgumentListContext => ctx.commandArgumentList().elements
      case ctx                                            => Nil
    }
  }

  sealed implicit class CommandArgumentListContextHelper(ctx: CommandArgumentListContext) {
    def elements: List[ParserRuleContext] = {
      val primaryValues = Option(ctx.primaryValueList()).map(_.primaryValue().asScala.toList).getOrElse(List())
      val associations  = Option(ctx.associationList()).map(_.association().asScala.toList).getOrElse(List())
      primaryValues ++ associations
    }
  }

  sealed implicit class ModifierStatementContextHelpers(ctx: ModifierStatementContext) {
    def isUnless: Boolean = Option(ctx.statementModifier().UNLESS()).isDefined
    def isIf: Boolean     = Option(ctx.statementModifier().IF()).isDefined
  }

  sealed implicit class QuotedNonExpandedArrayElementListContextHelper(ctx: QuotedNonExpandedArrayElementListContext) {
    def elements: List[ParserRuleContext] = ctx.quotedNonExpandedArrayElementContent().asScala.toList
  }

  sealed implicit class AssociationListContextHelper(ctx: AssociationListContext) {
    def associations: List[ParserRuleContext] = ctx.association().asScala.toList
  }

  sealed implicit class MethodIdentifierContextHelper(ctx: MethodIdentifierContext) {
    def isAttrDeclaration: Boolean = Set("attr_reader", "attr_writer", "attr_accessor").contains(ctx.getText)
  }

  sealed implicit class MandatoryOrOptionalParameterListContextHelper(ctx: MandatoryOrOptionalParameterListContext) {
    def parameters: List[ParserRuleContext] = ctx.mandatoryOrOptionalParameter().asScala.toList
  }

  sealed implicit class MethodParameterPartContextHelper(ctx: MethodParameterPartContext) {
    def parameters: List[ParserRuleContext] = Option(ctx.parameterList()).map(_.parameters).getOrElse(List())
  }

  sealed implicit class ParameterListContextHelper(ctx: ParameterListContext) {
    def parameters: List[ParserRuleContext] = {
      val mandatoryOrOptionals = Option(ctx.mandatoryOrOptionalParameterList()).map(_.parameters).getOrElse(List())
      val arrayParameter       = Option(ctx.arrayParameter()).toList
      val hashParameter        = Option(ctx.hashParameter()).toList
      val procParameter        = Option(ctx.procParameter()).toList
      mandatoryOrOptionals ++ arrayParameter ++ hashParameter ++ procParameter
    }
  }

  sealed implicit class IndexingArgumentListContextHelper(ctx: IndexingArgumentListContext) {
    def arguments: List[ParserRuleContext] = ctx match
      case ctx: CommandIndexingArgumentListContext => List(ctx.command())
      case ctx: OperatorExpressionListIndexingArgumentListContext =>
        ctx.operatorExpressionList().operatorExpression().asScala.toList
      case ctx: AssociationListIndexingArgumentListContext   => ctx.associationList().associations
      case ctx: SplattingArgumentIndexingArgumentListContext => ctx.splattingArgument() :: Nil
      case ctx: OperatorExpressionListWithSplattingArgumentIndexingArgumentListContext => ctx.splattingArgument() :: Nil
      case ctx =>
        logger.warn(s"IndexingArgumentListContextHelper - Unsupported argument type ${ctx.getClass}")
        List()
  }

  sealed implicit class ArgumentWithParenthesesContextHelper(ctx: ArgumentWithParenthesesContext) {
    def arguments: List[ParserRuleContext] = ctx match
      case _: EmptyArgumentWithParenthesesContext          => List()
      case ctx: ArgumentListArgumentWithParenthesesContext => ctx.argumentList().elements
      case ctx =>
        logger.warn(s"ArgumentWithParenthesesContextHelper - Unsupported argument type ${ctx.getClass}")
        List()
  }

  sealed implicit class ArgumentListContextHelper(ctx: ArgumentListContext) {
    def elements: List[ParserRuleContext] = ctx match
      case ctx: OperatorsArgumentListContext =>
        val operatorExpressions = ctx.operatorExpressionList().operatorExpression().asScala.toList
        val associations        = Option(ctx.associationList()).fold(List())(_.association().asScala)
        val splatting           = Option(ctx.splattingArgument()).toList
        val block               = Option(ctx.blockArgument()).toList
        operatorExpressions ++ associations ++ splatting ++ block
      case ctx: AssociationsArgumentListContext =>
        Option(ctx.associationList()).map(_.associations).getOrElse(List.empty)
      case ctx: SplattingArgumentArgumentListContext =>
        Option(ctx.splattingArgument()).toList ++ Option(ctx.blockArgument()).toList ++ Option(
          ctx.operatorExpressionList()
        ).toList
      case ctx: BlockArgumentArgumentListContext =>
        Option(ctx.blockArgument()).toList
      case ctx =>
        logger.warn(s"ArgumentListContextHelper - Unsupported element type ${ctx.getClass.getSimpleName}")
        List()
  }

  sealed implicit class CommandWithDoBlockContextHelper(ctx: CommandWithDoBlockContext) {
    def arguments: List[ParserRuleContext] = Option(ctx.argumentList()).map(_.elements).getOrElse(Nil)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy