@@ -42,7 +42,8 @@ newtype SynthKind =
4242 StmtSequenceKind ( ) or
4343 SelfKind ( SelfVariable v ) or
4444 SubExprKind ( ) or
45- ConstantReadAccessKind ( string value ) { any ( Synthesis s ) .constantReadAccess ( value ) }
45+ ConstantReadAccessKind ( string value ) { any ( Synthesis s ) .constantReadAccess ( value ) } or
46+ ConstantWriteAccessKind ( string value ) { any ( Synthesis s ) .constantWriteAccess ( value ) }
4647
4748/**
4849 * An AST child.
@@ -107,6 +108,11 @@ class Synthesis extends TSynthesis {
107108 */
108109 predicate constantReadAccess ( string name ) { none ( ) }
109110
111+ /**
112+ * Holds if a constant write access of `name` is needed.
113+ */
114+ predicate constantWriteAccess ( string name ) { none ( ) }
115+
110116 /**
111117 * Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
112118 */
@@ -493,6 +499,231 @@ private module AssignOperationDesugar {
493499 }
494500 }
495501
502+ /**
503+ * An assignment operation where the left-hand side is a constant
504+ * without scope expression, such as`FOO` or `::Foo`.
505+ */
506+ private class ConstantAssignOperation extends AssignOperation {
507+ string name ;
508+
509+ pragma [ nomagic]
510+ ConstantAssignOperation ( ) {
511+ name =
512+ any ( Ruby:: Constant constant | TTokenConstantAccess ( constant ) = this .getLeftOperand ( ) )
513+ .getValue ( )
514+ or
515+ name =
516+ "::" +
517+ any ( Ruby:: Constant constant |
518+ TScopeResolutionConstantAccess ( any ( Ruby:: ScopeResolution g | not exists ( g .getScope ( ) ) ) ,
519+ constant ) = this .getLeftOperand ( )
520+ ) .getValue ( )
521+ }
522+
523+ final string getName ( ) { result = name }
524+ }
525+
526+ pragma [ nomagic]
527+ private predicate constantAssignOperationSynthesis ( AstNode parent , int i , Child child ) {
528+ exists ( ConstantAssignOperation cao |
529+ parent = cao and
530+ i = - 1 and
531+ child = SynthChild ( AssignExprKind ( ) )
532+ or
533+ exists ( AstNode assign | assign = TAssignExprSynth ( cao , - 1 ) |
534+ parent = assign and
535+ i = 0 and
536+ child = childRef ( cao .getLeftOperand ( ) )
537+ or
538+ parent = assign and
539+ i = 1 and
540+ child = SynthChild ( getKind ( cao ) )
541+ or
542+ parent = getSynthChild ( assign , 1 ) and
543+ (
544+ i = 0 and
545+ child = SynthChild ( ConstantReadAccessKind ( cao .getName ( ) ) )
546+ or
547+ i = 1 and
548+ child = childRef ( cao .getRightOperand ( ) )
549+ )
550+ )
551+ )
552+ }
553+
554+ /**
555+ * ```rb
556+ * FOO += y
557+ * ```
558+ *
559+ * desugars to
560+ *
561+ * ```rb
562+ * FOO = FOO + y
563+ * ```
564+ */
565+ private class ConstantAssignOperationSynthesis extends Synthesis {
566+ final override predicate child ( AstNode parent , int i , Child child ) {
567+ constantAssignOperationSynthesis ( parent , i , child )
568+ }
569+
570+ final override predicate constantReadAccess ( string name ) {
571+ name = any ( ConstantAssignOperation o ) .getName ( )
572+ }
573+
574+ final override predicate location ( AstNode n , Location l ) {
575+ exists ( ConstantAssignOperation cao , BinaryOperation bo |
576+ bo = cao .getDesugared ( ) .( AssignExpr ) .getRightOperand ( )
577+ |
578+ n = bo and
579+ l = getAssignOperationLocation ( cao )
580+ or
581+ n = bo .getLeftOperand ( ) and
582+ hasLocation ( cao .getLeftOperand ( ) , l )
583+ )
584+ }
585+ }
586+
587+ /**
588+ * An assignment operation where the left-hand side is a constant
589+ * with scope expression, such as `expr::FOO`.
590+ */
591+ private class ScopeResolutionAssignOperation extends AssignOperation {
592+ string name ;
593+ Expr scope ;
594+
595+ pragma [ nomagic]
596+ ScopeResolutionAssignOperation ( ) {
597+ exists ( Ruby:: Constant constant , Ruby:: ScopeResolution g |
598+ TScopeResolutionConstantAccess ( g , constant ) = this .getLeftOperand ( ) and
599+ name = constant .getValue ( ) and
600+ toGenerated ( scope ) = g .getScope ( )
601+ )
602+ }
603+
604+ final string getName ( ) { result = name }
605+
606+ final Expr getScopeExpr ( ) { result = scope }
607+ }
608+
609+ pragma [ nomagic]
610+ private predicate scopeResolutionAssignOperationSynthesis ( AstNode parent , int i , Child child ) {
611+ exists ( ScopeResolutionAssignOperation cao |
612+ parent = cao and
613+ i = - 1 and
614+ child = SynthChild ( StmtSequenceKind ( ) )
615+ or
616+ exists ( AstNode stmts | stmts = TStmtSequenceSynth ( cao , - 1 ) |
617+ parent = stmts and
618+ i = 0 and
619+ child = SynthChild ( AssignExprKind ( ) )
620+ or
621+ exists ( AstNode assign | assign = TAssignExprSynth ( stmts , 0 ) |
622+ parent = assign and
623+ i = 0 and
624+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( cao , 0 ) ) )
625+ or
626+ parent = assign and
627+ i = 1 and
628+ child = childRef ( cao .getScopeExpr ( ) )
629+ )
630+ or
631+ parent = stmts and
632+ i = 1 and
633+ child = SynthChild ( AssignExprKind ( ) )
634+ or
635+ exists ( AstNode assign | assign = TAssignExprSynth ( stmts , 1 ) |
636+ parent = assign and
637+ i = 0 and
638+ child = SynthChild ( ConstantWriteAccessKind ( cao .getName ( ) ) )
639+ or
640+ exists ( AstNode cwa | cwa = TConstantWriteAccessSynth ( assign , 0 , cao .getName ( ) ) |
641+ parent = cwa and
642+ i = 0 and
643+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( cao , 0 ) ) )
644+ )
645+ or
646+ parent = assign and
647+ i = 1 and
648+ child = SynthChild ( getKind ( cao ) )
649+ or
650+ exists ( AstNode op | op = getSynthChild ( assign , 1 ) |
651+ parent = op and
652+ i = 0 and
653+ child = SynthChild ( ConstantReadAccessKind ( cao .getName ( ) ) )
654+ or
655+ exists ( AstNode cra | cra = TConstantReadAccessSynth ( op , 0 , cao .getName ( ) ) |
656+ parent = cra and
657+ i = 0 and
658+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( cao , 0 ) ) )
659+ )
660+ or
661+ parent = op and
662+ i = 1 and
663+ child = childRef ( cao .getRightOperand ( ) )
664+ )
665+ )
666+ )
667+ )
668+ }
669+
670+ /**
671+ * ```rb
672+ * expr::FOO += y
673+ * ```
674+ *
675+ * desugars to
676+ *
677+ * ```rb
678+ * __synth__0 = expr
679+ * __synth__0::FOO = _synth__0::FOO + y
680+ * ```
681+ */
682+ private class ScopeResolutionAssignOperationSynthesis extends Synthesis {
683+ final override predicate child ( AstNode parent , int i , Child child ) {
684+ scopeResolutionAssignOperationSynthesis ( parent , i , child )
685+ }
686+
687+ final override predicate constantReadAccess ( string name ) {
688+ name = any ( ScopeResolutionAssignOperation o ) .getName ( )
689+ }
690+
691+ final override predicate localVariable ( AstNode n , int i ) {
692+ n instanceof ScopeResolutionAssignOperation and
693+ i = 0
694+ }
695+
696+ final override predicate constantWriteAccess ( string name ) { this .constantReadAccess ( name ) }
697+
698+ final override predicate location ( AstNode n , Location l ) {
699+ exists ( ScopeResolutionAssignOperation cao , StmtSequence stmts | stmts = cao .getDesugared ( ) |
700+ n = stmts .getStmt ( 0 ) and
701+ hasLocation ( cao .getScopeExpr ( ) , l )
702+ or
703+ exists ( AssignExpr assign | assign = stmts .getStmt ( 1 ) |
704+ n = assign and hasLocation ( cao , l )
705+ or
706+ n = assign .getLeftOperand ( ) and
707+ hasLocation ( cao .getLeftOperand ( ) , l )
708+ or
709+ n = assign .getLeftOperand ( ) .( ConstantAccess ) .getScopeExpr ( ) and
710+ hasLocation ( cao .getScopeExpr ( ) , l )
711+ or
712+ exists ( BinaryOperation bo | bo = assign .getRightOperand ( ) |
713+ n = bo and
714+ l = getAssignOperationLocation ( cao )
715+ or
716+ n = bo .getLeftOperand ( ) and
717+ hasLocation ( cao .getLeftOperand ( ) , l )
718+ or
719+ n = bo .getLeftOperand ( ) .( ConstantAccess ) .getScopeExpr ( ) and
720+ hasLocation ( cao .getScopeExpr ( ) , l )
721+ )
722+ )
723+ )
724+ }
725+ }
726+
496727 /** An assignment operation where the left-hand side is a method call. */
497728 private class SetterAssignOperation extends AssignOperation {
498729 private MethodCall mc ;
0 commit comments