diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll index 3899fbe23fae..ba5656a8de2b 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll @@ -12,7 +12,6 @@ private import Completion private module CfgInput implements CfgShared::InputSig { private import ControlFlowGraphImpl as Impl private import Completion as Comp - private import Splitting as Splitting private import semmle.code.powershell.Cfg as Cfg class Completion = Comp::Completion; @@ -35,10 +34,6 @@ private module CfgInput implements CfgShared::InputSig { scope.(Impl::CfgScope).exit(last, c) } - class SplitKindBase = Splitting::TSplitKind; - - class Split = Splitting::Split; - class SuccessorType = Cfg::SuccessorType; SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } @@ -58,7 +53,22 @@ private module CfgInput implements CfgShared::InputSig { } private import CfgInput -import CfgShared::Make + +private module CfgSplittingInput implements CfgShared::SplittingInputSig { + private import Splitting as S + + class SplitKindBase = S::TSplitKind; + + class Split = S::Split; +} + +private module ConditionalCompletionSplittingInput implements + CfgShared::ConditionalCompletionSplittingInputSig +{ + import Splitting::ConditionalCompletionSplitting::ConditionalCompletionSplittingInput +} + +import CfgShared::MakeWithSplitting class CfgScope extends Scope { predicate entry(Ast first) { first(this, first) } diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Splitting.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Splitting.qll index 21ca95b70772..012f284eb445 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Splitting.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Splitting.qll @@ -3,7 +3,8 @@ */ private import powershell -private import Completion +private import Completion as Comp +private import Comp private import ControlFlowGraphImpl private import Cfg::SuccessorTypes private import semmle.code.powershell.controlflow.ControlFlowGraph as Cfg @@ -11,12 +12,10 @@ private import semmle.code.powershell.controlflow.ControlFlowGraph as Cfg cached private module Cached { cached - newtype TSplitKind = - TConditionalCompletionSplitKind() + newtype TSplitKind = TConditionalCompletionSplitKind() cached - newtype TSplit = - TConditionalCompletionSplit(ConditionalCompletion c) + newtype TSplit = TConditionalCompletionSplit(ConditionalCompletion c) } import Cached @@ -27,59 +26,57 @@ class Split extends TSplit { string toString() { none() } } -private module ConditionalCompletionSplitting { - /** - * A split for conditional completions. - */ +module ConditionalCompletionSplitting { class ConditionalCompletionSplit extends Split, TConditionalCompletionSplit { ConditionalCompletion completion; ConditionalCompletionSplit() { this = TConditionalCompletionSplit(completion) } + ConditionalCompletion getCompletion() { result = completion } + override string toString() { result = completion.toString() } } - // private class ConditionalCompletionSplitKind extends SplitKind, TConditionalCompletionSplitKind { - // override int getListOrder() { result = 0 } - - // override predicate isEnabled(Ast n) { this.appliesTo(n) } - - // override string toString() { result = "ConditionalCompletion" } - // } - - int getNextListOrder() { result = 1 } - -// private class ConditionalCompletionSplitImpl extends SplitImpl instanceof ConditionalCompletionSplit -// { -// ConditionalCompletion completion; - -// ConditionalCompletionSplitImpl() { this = TConditionalCompletionSplit(completion) } + private class ConditionalCompletionSplitKind_ extends SplitKind, TConditionalCompletionSplitKind { + override int getListOrder() { result = 0 } -// override ConditionalCompletionSplitKind getKind() { any() } + override predicate isEnabled(Ast n) { this.appliesTo(n) } -// override predicate hasEntry(Ast pred, Ast succ, Completion c) { -// succ(pred, succ, c) and -// last(succ, _, completion) and -// none() // TODO -// } - -// override predicate hasEntryScope(Cfg::CfgScope scope, Ast succ) { none() } - -// override predicate hasExit(Ast pred, Ast succ, Completion c) { -// this.appliesTo(pred) and -// succ(pred, succ, c) and -// if c instanceof ConditionalCompletion then completion = c else any() -// } - -// override predicate hasExitScope(Cfg::CfgScope scope, Ast last, Completion c) { -// this.appliesTo(last) and -// succExit(scope, last, c) and -// if c instanceof ConditionalCompletion then completion = c else any() -// } + override string toString() { result = "ConditionalCompletion" } + } -// override predicate hasSuccessor(Ast pred, Ast succ, Completion c) { none() } -// } + module ConditionalCompletionSplittingInput { + private import Completion as Comp + + class ConditionalCompletion = Comp::ConditionalCompletion; + + class ConditionalCompletionSplitKind extends ConditionalCompletionSplitKind_, TSplitKind { } + + class ConditionalCompletionSplit = ConditionalCompletionSplitting::ConditionalCompletionSplit; + + bindingset[parent, parentCompletion] + predicate condPropagateExpr( + Ast parent, ConditionalCompletion parentCompletion, Ast child, + ConditionalCompletion childCompletion + ) { + child = parent.(NotExpr).getOperand() and + childCompletion.(BooleanCompletion).getDual() = parentCompletion + or + childCompletion = parentCompletion and + ( + child = parent.(LogicalAndExpr).getAnOperand() + or + child = parent.(LogicalOrExpr).getAnOperand() + or + child = parent.(ConditionalExpr).getBranch(_) + ) + } + + int getNextListOrder() { result = 1 } + + private class ConditionalCompletionSplitImpl extends SplitImplementations::ConditionalCompletionSplitting::ConditionalCompletionSplitImpl + { } + } } class ConditionalCompletionSplit = ConditionalCompletionSplitting::ConditionalCompletionSplit; - diff --git a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected index 29113b4a9c4c..77a4997ae5ab 100644 --- a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -45,9 +45,11 @@ | conditionals.ps1:25:5:31:14 | {...} | conditionals.ps1:27:5:30:6 | if (...) {...} | | | conditionals.ps1:27:5:30:6 | if (...) {...} | conditionals.ps1:27:8:27:16 | myBool1 | | | conditionals.ps1:27:8:27:16 | myBool1 | conditionals.ps1:27:22:27:30 | myBool2 | false, true | -| conditionals.ps1:27:8:27:30 | ... -and ... | conditionals.ps1:27:8:27:30 | ... -and ... | false, true | | conditionals.ps1:27:8:27:30 | ... -and ... | conditionals.ps1:28:5:30:6 | {...} | true | -| conditionals.ps1:27:22:27:30 | myBool2 | conditionals.ps1:27:8:27:30 | ... -and ... | false, true | +| conditionals.ps1:27:8:27:30 | [false] ... -and ... | conditionals.ps1:27:8:27:30 | ... -and ... | false | +| conditionals.ps1:27:8:27:30 | [true] ... -and ... | conditionals.ps1:27:8:27:30 | ... -and ... | true | +| conditionals.ps1:27:22:27:30 | myBool2 | conditionals.ps1:27:8:27:30 | [false] ... -and ... | false | +| conditionals.ps1:27:22:27:30 | myBool2 | conditionals.ps1:27:8:27:30 | [true] ... -and ... | true | | conditionals.ps1:28:5:30:6 | {...} | conditionals.ps1:29:9:29:18 | return ... | | | conditionals.ps1:29:9:29:18 | return ... | conditionals.ps1:29:16:29:18 | 10 | | | conditionals.ps1:29:16:29:18 | 10 | conditionals.ps1:29:16:29:18 | 10 | | @@ -63,10 +65,12 @@ | conditionals.ps1:35:5:44:6 | {...} | conditionals.ps1:37:5:44:6 | if (...) {...} else {...} | | | conditionals.ps1:37:5:44:6 | if (...) {...} else {...} | conditionals.ps1:37:8:37:16 | myBool1 | | | conditionals.ps1:37:8:37:16 | myBool1 | conditionals.ps1:37:22:37:30 | myBool2 | false, true | -| conditionals.ps1:37:8:37:30 | ... -and ... | conditionals.ps1:37:8:37:30 | ... -and ... | false, true | | conditionals.ps1:37:8:37:30 | ... -and ... | conditionals.ps1:38:5:40:6 | {...} | true | | conditionals.ps1:37:8:37:30 | ... -and ... | conditionals.ps1:42:5:44:6 | {...} | false | -| conditionals.ps1:37:22:37:30 | myBool2 | conditionals.ps1:37:8:37:30 | ... -and ... | false, true | +| conditionals.ps1:37:8:37:30 | [false] ... -and ... | conditionals.ps1:37:8:37:30 | ... -and ... | false | +| conditionals.ps1:37:8:37:30 | [true] ... -and ... | conditionals.ps1:37:8:37:30 | ... -and ... | true | +| conditionals.ps1:37:22:37:30 | myBool2 | conditionals.ps1:37:8:37:30 | [false] ... -and ... | false | +| conditionals.ps1:37:22:37:30 | myBool2 | conditionals.ps1:37:8:37:30 | [true] ... -and ... | true | | conditionals.ps1:38:5:40:6 | {...} | conditionals.ps1:39:9:39:18 | return ... | | | conditionals.ps1:39:9:39:18 | return ... | conditionals.ps1:39:16:39:18 | 10 | | | conditionals.ps1:39:16:39:18 | 10 | conditionals.ps1:34:28:45:2 | exit {...} (normal) | | @@ -83,9 +87,11 @@ | conditionals.ps1:48:5:54:14 | {...} | conditionals.ps1:50:5:53:6 | if (...) {...} | | | conditionals.ps1:50:5:53:6 | if (...) {...} | conditionals.ps1:50:8:50:16 | myBool1 | | | conditionals.ps1:50:8:50:16 | myBool1 | conditionals.ps1:50:21:50:29 | myBool2 | false, true | -| conditionals.ps1:50:8:50:29 | ... -or ... | conditionals.ps1:50:8:50:29 | ... -or ... | false, true | | conditionals.ps1:50:8:50:29 | ... -or ... | conditionals.ps1:51:5:53:6 | {...} | true | -| conditionals.ps1:50:21:50:29 | myBool2 | conditionals.ps1:50:8:50:29 | ... -or ... | false, true | +| conditionals.ps1:50:8:50:29 | [false] ... -or ... | conditionals.ps1:50:8:50:29 | ... -or ... | false | +| conditionals.ps1:50:8:50:29 | [true] ... -or ... | conditionals.ps1:50:8:50:29 | ... -or ... | true | +| conditionals.ps1:50:21:50:29 | myBool2 | conditionals.ps1:50:8:50:29 | [false] ... -or ... | false | +| conditionals.ps1:50:21:50:29 | myBool2 | conditionals.ps1:50:8:50:29 | [true] ... -or ... | true | | conditionals.ps1:51:5:53:6 | {...} | conditionals.ps1:52:9:52:18 | return ... | | | conditionals.ps1:52:9:52:18 | return ... | conditionals.ps1:52:16:52:18 | 10 | | | conditionals.ps1:52:16:52:18 | 10 | conditionals.ps1:52:16:52:18 | 10 | | @@ -101,10 +107,12 @@ | conditionals.ps1:58:5:67:6 | {...} | conditionals.ps1:60:5:67:6 | if (...) {...} else {...} | | | conditionals.ps1:60:5:67:6 | if (...) {...} else {...} | conditionals.ps1:60:8:60:16 | myBool1 | | | conditionals.ps1:60:8:60:16 | myBool1 | conditionals.ps1:60:21:60:29 | myBool2 | false, true | -| conditionals.ps1:60:8:60:29 | ... -or ... | conditionals.ps1:60:8:60:29 | ... -or ... | false, true | | conditionals.ps1:60:8:60:29 | ... -or ... | conditionals.ps1:61:5:63:6 | {...} | true | | conditionals.ps1:60:8:60:29 | ... -or ... | conditionals.ps1:65:5:67:6 | {...} | false | -| conditionals.ps1:60:21:60:29 | myBool2 | conditionals.ps1:60:8:60:29 | ... -or ... | false, true | +| conditionals.ps1:60:8:60:29 | [false] ... -or ... | conditionals.ps1:60:8:60:29 | ... -or ... | false | +| conditionals.ps1:60:8:60:29 | [true] ... -or ... | conditionals.ps1:60:8:60:29 | ... -or ... | true | +| conditionals.ps1:60:21:60:29 | myBool2 | conditionals.ps1:60:8:60:29 | [false] ... -or ... | false | +| conditionals.ps1:60:21:60:29 | myBool2 | conditionals.ps1:60:8:60:29 | [true] ... -or ... | true | | conditionals.ps1:61:5:63:6 | {...} | conditionals.ps1:62:9:62:18 | return ... | | | conditionals.ps1:62:9:62:18 | return ... | conditionals.ps1:62:16:62:18 | 10 | | | conditionals.ps1:62:16:62:18 | 10 | conditionals.ps1:57:28:68:2 | exit {...} (normal) | | @@ -122,10 +130,17 @@ | conditionals.ps1:73:5:80:6 | if (...) {...} | conditionals.ps1:73:8:73:16 | myBool1 | | | conditionals.ps1:73:8:73:16 | myBool1 | conditionals.ps1:73:8:73:16 | myBool1 | false, true | | conditionals.ps1:73:8:73:16 | myBool1 | conditionals.ps1:74:5:76:6 | {...} | true | +| conditionals.ps1:73:8:73:16 | myBool1 | conditionals.ps1:77:12:77:19 | myBoo2 | false | | conditionals.ps1:74:5:76:6 | {...} | conditionals.ps1:75:9:75:18 | return ... | | | conditionals.ps1:75:9:75:18 | return ... | conditionals.ps1:75:16:75:18 | 10 | | | conditionals.ps1:75:16:75:18 | 10 | conditionals.ps1:75:16:75:18 | 10 | | | conditionals.ps1:75:16:75:18 | 10 | conditionals.ps1:81:5:81:14 | return ... | | +| conditionals.ps1:77:12:77:19 | myBoo2 | conditionals.ps1:77:12:77:19 | myBoo2 | false, true | +| conditionals.ps1:77:12:77:19 | myBoo2 | conditionals.ps1:78:5:80:6 | {...} | true | +| conditionals.ps1:78:5:80:6 | {...} | conditionals.ps1:79:9:79:18 | return ... | | +| conditionals.ps1:79:9:79:18 | return ... | conditionals.ps1:79:16:79:18 | 11 | | +| conditionals.ps1:79:16:79:18 | 11 | conditionals.ps1:79:16:79:18 | 11 | | +| conditionals.ps1:79:16:79:18 | 11 | conditionals.ps1:81:5:81:14 | return ... | | | conditionals.ps1:81:5:81:14 | return ... | conditionals.ps1:81:12:81:14 | 12 | | | conditionals.ps1:81:12:81:14 | 12 | conditionals.ps1:70:23:82:2 | exit {...} (normal) | | | conditionals.ps1:81:12:81:14 | 12 | conditionals.ps1:81:12:81:14 | 12 | | @@ -138,11 +153,18 @@ | conditionals.ps1:87:5:98:6 | if (...) {...} else {...} | conditionals.ps1:87:8:87:16 | myBool1 | | | conditionals.ps1:87:8:87:16 | myBool1 | conditionals.ps1:87:8:87:16 | myBool1 | false, true | | conditionals.ps1:87:8:87:16 | myBool1 | conditionals.ps1:88:5:90:6 | {...} | true | -| conditionals.ps1:87:8:87:16 | myBool1 | conditionals.ps1:96:5:98:6 | {...} | false | +| conditionals.ps1:87:8:87:16 | myBool1 | conditionals.ps1:91:12:91:19 | myBoo2 | false | | conditionals.ps1:88:5:90:6 | {...} | conditionals.ps1:89:9:89:18 | return ... | | | conditionals.ps1:89:9:89:18 | return ... | conditionals.ps1:89:16:89:18 | 10 | | | conditionals.ps1:89:16:89:18 | 10 | conditionals.ps1:84:28:99:2 | exit {...} (normal) | | | conditionals.ps1:89:16:89:18 | 10 | conditionals.ps1:89:16:89:18 | 10 | | +| conditionals.ps1:91:12:91:19 | myBoo2 | conditionals.ps1:91:12:91:19 | myBoo2 | false, true | +| conditionals.ps1:91:12:91:19 | myBoo2 | conditionals.ps1:92:5:94:6 | {...} | true | +| conditionals.ps1:91:12:91:19 | myBoo2 | conditionals.ps1:96:5:98:6 | {...} | false | +| conditionals.ps1:92:5:94:6 | {...} | conditionals.ps1:93:9:93:18 | return ... | | +| conditionals.ps1:93:9:93:18 | return ... | conditionals.ps1:93:16:93:18 | 11 | | +| conditionals.ps1:93:16:93:18 | 11 | conditionals.ps1:84:28:99:2 | exit {...} (normal) | | +| conditionals.ps1:93:16:93:18 | 11 | conditionals.ps1:93:16:93:18 | 11 | | | conditionals.ps1:96:5:98:6 | {...} | conditionals.ps1:97:9:97:18 | return ... | | | conditionals.ps1:97:9:97:18 | return ... | conditionals.ps1:97:16:97:18 | 12 | | | conditionals.ps1:97:16:97:18 | 12 | conditionals.ps1:84:28:99:2 | exit {...} (normal) | |