Skip to content

Commit a684f45

Browse files
Change flag to dump context info
1 parent d33af55 commit a684f45

5 files changed

Lines changed: 116 additions & 3 deletions

File tree

liquidjava-verifier/src/main/java/liquidjava/api/CommandLineArgs.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public class CommandLineArgs {
2222
@Option(names = { "-d", "--debug" }, description = "Enable debug mode for more detailed output")
2323
public boolean debugMode;
2424

25+
@Option(names = { "-a", "--all-context" }, description = "Print the full context after every checked element")
26+
public boolean printContext;
27+
2528
@Option(names = { "-lsp", "--language-server" }, description = "Enable language server mode for editor support")
2629
public boolean lspMode;
2730

liquidjava-verifier/src/main/java/liquidjava/diagnostics/DebugLog.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import java.util.List;
55
import liquidjava.api.CommandLineLauncher;
66
import liquidjava.processor.VCImplication;
7+
import liquidjava.processor.context.Context;
8+
import liquidjava.processor.context.ContextHistory;
79
import liquidjava.rj_language.Predicate;
810
import liquidjava.rj_language.ast.BinaryExpression;
911
import liquidjava.rj_language.ast.Expression;
1012
import liquidjava.rj_language.ast.GroupExpression;
1113
import liquidjava.utils.Utils;
1214
import spoon.reflect.cu.SourcePosition;
15+
import spoon.reflect.declaration.CtElement;
1316
import spoon.reflect.reference.CtTypeReference;
1417

1518
/**
@@ -29,6 +32,7 @@ public final class DebugLog {
2932
private static final String SMT_TAG = Colors.BLUE + "[SMT]" + Colors.RESET;
3033
private static final String SMT_CHECK = Colors.SALMON + "[SMT CHECK]" + Colors.RESET;
3134
private static final String SMP_TAG = Colors.YELLOW + "[SMP]" + Colors.RESET;
35+
private static final String CTX_TAG = Colors.BRIGHT_MAGENTA + "[CTX]" + Colors.RESET;
3236

3337
private DebugLog() {
3438
}
@@ -37,6 +41,11 @@ public static boolean enabled() {
3741
return CommandLineLauncher.cmdArgs.debugMode;
3842
}
3943

44+
/** Gate for the full-context dump, driven by the {@code -a} / {@code --all-context} flag (independent of debug). */
45+
public static boolean contextEnabled() {
46+
return CommandLineLauncher.cmdArgs.printContext;
47+
}
48+
4049
/**
4150
* One-line header for a verification check: emits the absolute file path + line so terminals (iTerm2, VS Code,
4251
* WezTerm, …) make it ⌘/Ctrl-clickable. Replaces the older two-line {@code info()} prints.
@@ -445,4 +454,31 @@ public static void smtError(String message) {
445454
System.out.println(SMT_TAG + " Result: " + Colors.RED + "ERROR" + Colors.RESET + " — "
446455
+ (message == null ? "(no message)" : message));
447456
}
457+
458+
public static void contextAtElement(CtElement element, String contextInfo) {
459+
if (!contextEnabled()) {
460+
return;
461+
}
462+
SourcePosition position = element.getPosition();
463+
String header;
464+
if (position != null && position.getFile() != null) {
465+
// file:line so terminals make it ⌘/Ctrl-clickable (same form as smtVerifying), plus the source line itself.
466+
String where = position.getFile().getAbsolutePath() + ":" + position.getLine();
467+
String code = Utils.getExpressionFromPosition(position);
468+
header = CTX_TAG + " Context at " + Colors.CYAN + where + Colors.RESET
469+
+ (code != null ? " \n" + Colors.BOLD_YELLOW + code + Colors.RESET : ":");
470+
} else {
471+
header = CTX_TAG + " Context at " + position + ":";
472+
}
473+
System.out.println(header);
474+
System.out.println(contextInfo);
475+
}
476+
477+
public static void log(String string) {
478+
if (!enabled()) {
479+
return;
480+
}
481+
System.out.println(string);
482+
483+
}
448484
}

liquidjava-verifier/src/main/java/liquidjava/processor/context/ContextHistory.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.Set;
77

88
import liquidjava.api.CommandLineLauncher;
9+
import liquidjava.diagnostics.DebugLog;
910
import liquidjava.utils.Utils;
1011
import spoon.reflect.cu.SourcePosition;
1112
import spoon.reflect.declaration.CtElement;
@@ -45,7 +46,8 @@ public void clearHistory() {
4546
}
4647

4748
public void saveContext(CtElement element, Context context) {
48-
if (!CommandLineLauncher.cmdArgs.lspMode)
49+
if (!CommandLineLauncher.cmdArgs.lspMode && !CommandLineLauncher.cmdArgs.debugMode
50+
&& !CommandLineLauncher.cmdArgs.printContext)
4951
return;
5052

5153
String file = Utils.getFile(element);
@@ -64,6 +66,9 @@ public void saveContext(CtElement element, Context context) {
6466
ghosts.addAll(context.getGhostStates());
6567
aliases.addAll(context.getAliases());
6668
methods.addAll(context.getCtxFunctions());
69+
70+
// Gate lives inside DebugLog.contextAtElement (-a / --all-context); no-op unless that flag is set.
71+
DebugLog.contextAtElement(element, prettyPrint());
6772
}
6873

6974
private String getScopePosition(CtElement element) {
@@ -96,4 +101,26 @@ public Set<RefinedFunction> getMethods() {
96101
public Map<String, Set<String>> getFileScopes() {
97102
return fileScopes;
98103
}
104+
105+
public String prettyPrint() {
106+
StringBuilder sb = new StringBuilder("ContextHistory:\n");
107+
108+
// FileScopes are intentionally omitted here — they drive LSP hover ranges (see getFileScopes), not debug
109+
// output.
110+
appendSection(sb, "LocalVars", localVars);
111+
appendSection(sb, "GlobalVars", globalVars);
112+
appendSection(sb, "Ghosts", ghosts);
113+
appendSection(sb, "Aliases", aliases);
114+
appendSection(sb, "Methods", methods);
115+
116+
return sb.toString();
117+
}
118+
119+
private static void appendSection(StringBuilder sb, String label, Iterable<?> items) {
120+
sb.append(label).append(":\n");
121+
for (Object item : items) {
122+
sb.append(" - ").append(item).append("\n");
123+
}
124+
}
125+
99126
}

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/RefinementTypeChecker.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66
import java.util.Optional;
77

8+
import liquidjava.diagnostics.DebugLog;
89
import liquidjava.diagnostics.Diagnostics;
910
import liquidjava.diagnostics.errors.LJError;
1011
import liquidjava.processor.context.*;
@@ -48,6 +49,7 @@
4849
import spoon.reflect.code.CtVariableRead;
4950
import spoon.reflect.declaration.*;
5051
import spoon.reflect.factory.Factory;
52+
import spoon.reflect.path.CtRole;
5153
import spoon.reflect.reference.CtFieldReference;
5254
import spoon.reflect.reference.CtTypeReference;
5355
import spoon.reflect.reference.CtVariableReference;
@@ -72,6 +74,21 @@ public RefinementTypeChecker(Context context, Factory factory) {
7274

7375
// --------------------- Visitors -----------------------------------
7476

77+
/**
78+
* Under {@code -a} / {@code --all-context}, dump the context after every top-level statement. {@code super.scan}
79+
* runs the full {@code visitCtXxx} override first, so the statement's refinement effect is already applied to the
80+
* context by the time we save it. The {@code STATEMENT} role filter keeps this to actual statements (direct block /
81+
* case children), skipping sub-expressions like the {@code g(x)} in {@code f(g(x))}.
82+
*/
83+
@Override
84+
public void scan(CtElement element) {
85+
super.scan(element);
86+
if (DebugLog.contextEnabled() && element instanceof CtStatement
87+
&& element.getRoleInParent() == CtRole.STATEMENT) {
88+
contextHistory.saveContext(element, context);
89+
}
90+
}
91+
7592
@Override
7693
public <T> void visitCtClass(CtClass<T> ctClass) {
7794
// System.out.println("CTCLASS:"+ctClass.getSimpleName());

liquidjava-verifier/src/main/java/liquidjava/utils/Utils.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,51 @@ public static SourcePosition getRealPosition(CtElement element) {
110110
return element.getPosition();
111111
}
112112

113+
/**
114+
* The source text of the element at {@code position}. The first line is taken from the element's column (leading
115+
* indentation / preceding tokens dropped); a statement that wraps across lines has its continuation lines appended
116+
* (joined with a space) until a line ends in a statement terminator ({@code ;}, {@code {} or {@code }}) or the
117+
* element's end line is reached. The terminator stop keeps multi-line statements whole while halting block-bodied
118+
* elements (methods, ifs, loops) at their opening brace instead of swallowing the whole body. Single-line elements
119+
* read exactly one line, unchanged.
120+
*/
113121
public static String getExpressionFromPosition(SourcePosition position) {
114122
if (position == null || position.getFile() == null)
115123
return null;
124+
int startLine = position.getLine();
125+
int endLine = position.getEndLine();
116126
try (Scanner scanner = new Scanner(position.getFile())) {
127+
StringBuilder sb = new StringBuilder();
117128
int currentLine = 1;
118129
while (scanner.hasNextLine()) {
119130
String line = scanner.nextLine();
120-
if (currentLine == position.getLine()) {
121-
return line.substring(position.getColumn() - 2).trim();
131+
if (currentLine >= startLine && currentLine <= endLine) {
132+
// First line starts at the element's column; continuation lines are taken whole.
133+
String piece = ((currentLine == startLine) ? line.substring(position.getColumn() - 2) : line)
134+
.trim();
135+
if (sb.length() > 0)
136+
sb.append(' ');
137+
sb.append(piece);
138+
if (currentLine >= endLine || endsStatement(piece))
139+
break;
122140
}
123141
currentLine++;
124142
}
143+
return sb.length() == 0 ? null : sb.toString();
125144
} catch (Exception e) {
126145
// ignore
127146
}
128147
return null;
129148
}
149+
150+
/**
151+
* A trimmed source line ends a statement / opens a block when its last char is {@code ;}, <code>{</code> or
152+
* <code>}</code> — used to bound multi-line snippet extraction.
153+
*/
154+
private static boolean endsStatement(String trimmedLine) {
155+
if (trimmedLine.isEmpty())
156+
return false;
157+
char last = trimmedLine.charAt(trimmedLine.length() - 1);
158+
return last == ';' || last == '{' || last == '}';
159+
}
130160
}

0 commit comments

Comments
 (0)