{"record":{"id":"294f8c9026247b0d","repo":"devlooped/moq","slug":"unsupported-expression-0","errorCode":null,"errorMessage":"Unsupported expression: {0}","messagePattern":"Unsupported expression: (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/ExpressionExtensions.cs","lineNumber":164,"sourceCode":"        {\n            Debug.Assert(expression != null);\n\n            var parts = new Stack<MethodExpectation>();\n\n            Expression remainder = expression.Body;\n            while (CanSplit(remainder))\n            {\n                Split(remainder, out remainder, out var part, allowNonOverridableLastProperty: allowNonOverridableLastProperty && parts.Count == 0);\n                parts.Push(part);\n            }\n\n            if (parts.Count > 0 && remainder is ParameterExpression)\n            {\n                return parts;\n            }\n            else\n            {\n                throw new ArgumentException(\n                    string.Format(\n                        CultureInfo.CurrentCulture,\n                        Resources.UnsupportedExpression,\n                        remainder.ToStringFixed()));\n            }\n\n            void Split(Expression e, out Expression r /* remainder */, out MethodExpectation p /* part */, bool assignment = false, bool allowNonOverridableLastProperty = false)\n            {\n                const string ParameterName = \"...\";\n\n                switch (e.NodeType)\n                {\n                    case ExpressionType.Assign:          // assignment to a property or indexer\n                    case ExpressionType.AddAssign:       // subscription of event handler to event\n                    case ExpressionType.SubtractAssign:  // unsubscription of event handler from event\n                        {\n                            var assignmentExpression = (BinaryExpression)e;\n                            Split(assignmentExpression.Left, out r, out var lhs, assignment: true);","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/ExpressionExtensions.cs#L146-L182","documentation":"ExpressionExtensions.Split decomposes a lambda expression into its member-access chain pieces (e.g. `x => x.A.B.C` into A, B, C). If, after stripping member accesses, the remainder is not a simple ParameterExpression (the lambda's root parameter), the expression is not a plain chain and Moq throws ArgumentException 'Unsupported expression: {0}'.","triggerScenarios":"Calling Split (via ToArray/Setup helpers) with a lambda whose body is not rooted at the parameter: indexers, method calls at the root, `x.A()`, casts, conversions, or expressions like `x => x.A.B ?? something`, `x => new Foo().Bar`, or a captured variable instead of the parameter.","commonSituations":"Passing compound expressions where a plain property chain is required; using `?.` (null-conditional) operators which compile to complex trees; boxing/cast wrappers `(IFoo)x.A`; setups written against local variables instead of the mock parameter.","solutions":["Rewrite the lambda as a pure member chain rooted at the parameter: `x => x.A.B`, no method calls, casts, operators, or null-conditionals.","For method-call roots, use the appropriate Setup overload (e.g. `Setup(x => x.A())`) rather than Split-based APIs.","Hoist conditional/default logic out of the expression: compute values outside and use `.Returns(...)` for them.","Print the expression with ToStringFixed() from the message to identify which node breaks the chain."],"exampleFix":"// before\nmock.Setup(x => x.Bar?.Baz); // null-conditional -> unsupported\n// after\nmock.Setup(x => x.Bar.Baz);","handlingStrategy":"type-guard","validationCode":"// Accept only plain parameter-rooted chains\nstatic bool IsSplitSafe(LambdaExpression e) =>\n    StripMembers(e.Body) is ParameterExpression;","typeGuard":"static bool IsMemberChain<T, R>(Expression<Func<T, R>> e) =>\n    e.Body is MemberExpression me && StripToParameter(me) is ParameterExpression;","tryCatchPattern":"try\n{\n    var parts = expression.Split();\n}\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"Unsupported expression\"))\n{\n    // rewrite lambda as a plain chain or use a different Setup API\n}","preventionTips":["No null-conditional (?.), ??, casts, or method calls in chain lambdas","Root every lambda at the mock parameter, not a local/captured variable","Split complex logic: navigate chains in the expression, compute values via Returns","Test setups after upgrades that might change expression shapes"],"tags":["moq","linq-expressions","argument-exception","expression-chain"],"backgroundTag":"unsupported-operation","analyzedSha":"89a5be629c752960fe403e95ff583e4ae6f00542","analyzedAt":"2026-09-16T05:31:39.496Z","contentChangedAt":"2026-09-16T05:31:39.496Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}