mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
HandleConditionalOperator collapses `if (c) a = x; else a = y;` into a conditional operator, innermost first, and keeps going for as long as the chain does. A source else-if ladder therefore comes back as one expression, however long it was: the sample in #2027 decompiles to a single 2095-character statement, and one NLog method to 2230 characters nested 29 brackets deep. ExpandNestedConditionals undoes that past one level, so a statement keeps at most a single conditional operator. It runs at the end of the pipeline rather than inside ExpressionTransforms, because every transform that needs its input to be a single expression has to see the collapsed form first: object and collection initializers, `with`, switch expressions, interpolated string handlers, and the query lambdas the C# stage later rewrites into clauses. Cutting the chain earlier leaves an if-else between the statements they pattern-match on and they silently stop matching - an object initializer assigning an init-only member then does not even compile. The same reasoning ReduceNestingTransform gives for walking back ConditionDetection's aggressive else-inlining once the structure is settled. A chain already stored to a variable is expanded into that variable, so nothing has to be decided: the variable carries its own type. A chain in any other position - an argument, a return value, a field store - has nothing to expand into, and ILExtraction can give it one. The temporary ILExtraction creates is typed from the stack type though, where `I4` is `int`, `bool`, `char` and every enum at once, so extracting on that basis turned a bool into `int num` with `if (num == 0)` and an enum into `dbType = (IsFixedLength ? 22 : 0)`. InferExpectedType is the counterpart to InferType that answers this: where InferType asks what a value is, it asks what the position the value flows into says it should be - a parameter, a return type, a field, all of which carry their type in metadata. Extraction is done only where that question has an answer, and the temporary is typed from it. The receiver of a call then reads `XPathNavigator xPathNavigator`, not `object obj` with a cast back, and a field store keeps its enum's member names. The Pretty fixture covers what must NOT change: an array initializer, a query lambda, a ref local, a switch expression, an object initializer with an init-only member, a `with` expression, a catch-when filter and both constructor-initializer forms. The positive case is an ILPretty test, because a Pretty fixture is its own input and expected output, and a chain that round-trips through collapse and expansion has no fixed point there. The PdbGen test records the cost in breakpoints: the compiler's single sequence point for the collapsed statement becomes one per expanded statement, which is inherent to splitting a statement in two. #2027 Assisted-by: Claude:claude-opus-5:Claude Code