While this is quite a hack (fair warning), you can use the following form to track your module values. Also a fair warning: I haven't tested this extensively, but it should work for simple examples at the least.
HackModules[code_] := Block[{Module},
With[
{results = Unique[]},
SetAttributes[results, Temporary];
results = {};
Module[args_List, body_] := With[
{syms = First@Replace[
Hold[args],
{HoldPattern[x_Symbol = y_] :> {HoldPattern[x], Hold[y]},
HoldPattern[x_Symbol] :> HoldPattern[x]},
{2}]},
Do[
SetAttributes[Evaluate@If[ListQ[s], s[[1, 1]], s[[1]]], Temporary],
{s, syms}];
With[
{vars = Replace[
Hold @@ List@Table[
If[ListQ[s], Join[Hold[Evaluate@Unique[]], Last[s]], Unique[]],
{s, syms}],
Hold[a_, b_] :> Set[a, b],
{2}]},
With[
{bodyResult = CompoundExpression @@ Join[
First[Hold @@@ vars],
ReplaceAll[
Hold[body],
MapThread[
If[ListQ[#1], First[#1], #1] -> #2 &,
{syms, vars[[1, All, 1]]}]]]},
With[
{finalVals = MapThread[
If[ListQ[#1], First[#1], #1] -> #2 &,
{syms, vars[[1, All, 1]]}]},
AppendTo[results, Hold[body] :> finalVals];
bodyResult]]]];
SetAttributes[Module, HoldAll];
With[
{codeResult = code},
{results, codeResult}]]];
SetAttributes[HackModules, HoldAll];
This basically watches every module call you make and yields a list of {moduleVariableValues, allCodeReturnValue}:
HackModules[
Module[
{y = Module[{x = 12},
x -= 2;
x + 4]},
y *= 10;
y - 40]]
{{Hold[x -= 2; (x + 4) y] :> {HoldPattern[x] -> 10, HoldPattern[y] -> 2},
Hold[z *= 10; z - 80] :> {HoldPattern[z] -> 280}},
200}
The first element of the returned list is a list of delayed-rules where the LHS is the held code (the bodies of the modules) and the RHS is a list of the module variables with their values at the end of the module evaluation.
Modulevariables have an attributeTemporaryand are cleared after execution if not needed anymore. – Kuba Apr 27 '17 at 19:44Echo[b]? – rhermans Apr 27 '17 at 20:19