GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Show states in hints in the headline's form, with the receiver · liquid-java/liquidjava@e2ce936

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • liquidjava-verifier/src
    • main/java/liquidjava
      • diagnostics/hints
        • StateHints.java
      • processor/refinement_checker/object_checkers
        • AuxStateHandler.java
    • test/java/liquidjava/api/tests
      • TestStateHints.java
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎liquidjava-verifier/src/main/java/liquidjava/diagnostics/hints/StateHints.java‎

Copy file name to clipboardExpand all lines: liquidjava-verifier/src/main/java/liquidjava/diagnostics/hints/StateHints.java
+44-16Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import java.util.Set;
88
import java.util.TreeSet;
99
import java.util.function.Supplier;
10-
import java.util.function.UnaryOperator;
10+
import java.util.function.Function;
1111
import java.util.stream.Collectors;
1212
import java.util.stream.Stream;
1313

@@ -42,12 +42,21 @@ public final class StateHints {
4242
private final List<Set<State>> goals;
4343
/** The state sets the failing call checks, as indexes into {@link State#names()} */
4444
private final Set<Integer> checkedSets;
45-
private final UnaryOperator<String> stateName;
45+
/** The name of the state set a state is shown as a value of, or empty when it keeps the shorthand */
46+
private final Function<String, Optional<String>> setNameOf;
47+
/**
48+
* Whether states are shown as the error's message shows them, e.g. {@code lockState(d) == locked}: when some state
49+
* set of the class is shown by its name
50+
*/
51+
private final boolean asValues;
4652

47-
private StateHints(StateMachine machine, String receiver, RefinedFunction call, UnaryOperator<String> stateName) {
53+
private StateHints(StateMachine machine, String receiver, RefinedFunction call,
54+
Function<String, Optional<String>> setNameOf) {
4855
this.machine = machine;
4956
this.receiver = receiver;
50-
this.stateName = stateName;
57+
this.setNameOf = setNameOf;
58+
this.asValues = machine.states().stream().flatMap(s -> s.names().stream())
59+
.anyMatch(name -> setNameOf.apply(name).isPresent());
5160
this.goals = machine.statesAllowingByRefinement(call);
5261
this.checkedSets = goals.stream().flatMap(g -> machine.stateSetsNamed(g).stream())
5362
.collect(Collectors.toCollection(TreeSet::new));
@@ -65,14 +74,15 @@ private StateHints(StateMachine machine, String receiver, RefinedFunction call,
6574
* @param checkedElsewhere
6675
* whether the receiver's state was checked elsewhere, though not decided, e.g. by an earlier call whose
6776
* result says which state it is in
68-
* @param stateName
69-
* a state's name as the error shows it, e.g. {@code lockState == locked} for {@code locked}
77+
* @param setNameOf
78+
* the name of the state set the error's message shows a state as a value of, or empty when it shows the
79+
* shorthand
7080
*/
7181
public static List<Hint> forCall(StateMachine machine, RefinedFunction call, Set<State> current, String receiver,
72-
ErrorCategory category, boolean checkedElsewhere, UnaryOperator<String> stateName) {
82+
ErrorCategory category, boolean checkedElsewhere, Function<String, Optional<String>> setNameOf) {
7383
if (!machine.models(call) || current.isEmpty() || machine.allows(current, call))
7484
return List.of(); // the machine cannot explain why the call failed
75-
StateHints hints = new StateHints(machine, receiver, call, stateName);
85+
StateHints hints = new StateHints(machine, receiver, call, setNameOf);
7686
if (hints.goals.isEmpty())
7787
return List.of();
7888

@@ -112,7 +122,7 @@ public static Hint decidingArgument(DecidingArgument deciding, String receiver)
112122
private List<Hint> anyState(Set<State> current, RefinedFunction call, boolean checkedElsewhere) {
113123
String known = checkedElsewhere ? "What is known here does not decide the state of " + receiver
114124
: "The state of " + receiver + " is unknown here";
115-
String message = known + ", and this call needs it to be " + needed();
125+
String message = known + ", and this call needs " + (asValues ? "" : "it to be ") + needed();
116126
message += newObjects(call).map(c -> ". Objects created with " + c + " start in such a state").orElse("");
117127
List<Hint> hints = new ArrayList<>();
118128
hints.add(new Hint(Kind.NEEDED_STATE, message));
@@ -166,8 +176,8 @@ private List<Hint> guard(Set<State> current, RefinedFunction call) {
166176
String check = (negated ? "!" : "") + guardCall(guard.method());
167177
String arguments = guard.method().getArguments().isEmpty() ? ""
168178
: ", filling in the arguments of " + declaration(guard.method());
169-
String message = String.format("Guard this call with if (%s)%s: when it returns %s, %s is %s", check, arguments,
170-
!negated, receiver, needed());
179+
String message = String.format("Guard this call with if (%s)%s: when it returns %s, %s", check, arguments,
180+
!negated, (asValues ? "" : receiver + " is ") + needed());
171181
return List.of(new Hint(Kind.GUARD, message));
172182
}
173183

@@ -211,6 +221,11 @@ private Hint callSequence(Set<State> current, List<CallVariant> sequence, boolea
211221
}
212222

213223
private Hint unreachable(Set<State> current, RefinedFunction call, boolean pathDependent) {
224+
if (pathDependent && asValues)
225+
return new Hint(Kind.PATH_DEPENDENT_STATE, String.format(
226+
"Depending on the path taken, %s here, and no sequence of calls moves %s from all of them to %s. "
227+
+ "Make %s reach this line in the same state on every path",
228+
namesHere(current), receiver, needed(), receiver));
214229
if (pathDependent)
215230
return new Hint(Kind.PATH_DEPENDENT_STATE, String.format(
216231
"%s can be in %s here, depending on the path taken, and no sequence of calls moves all of them to %s. "
@@ -227,13 +242,15 @@ private List<Hint> allowedCalls(Set<State> current) {
227242
if (!allowed.isEmpty()) {
228243
List<String> signatures = allowed.stream().map(StateHints::signature).toList();
229244
String others = machine.complete() ? "" : " (and possibly others)";
230-
return List.of(new Hint(Kind.AVAILABLE_CALLS, String.format("In state %s, %s can call: %s%s",
231-
namesHere(current), receiver, list(signatures), others)));
245+
return List.of(new Hint(Kind.AVAILABLE_CALLS, String.format("%s %s, %s can call: %s%s",
246+
asValues ? "When" : "In state", namesHere(current), receiver, list(signatures), others)));
232247
}
233248
boolean noCallsInAnyState = current.stream().allMatch(s -> machine.allowedIn(Set.of(s)).isEmpty());
234249
if (machine.complete() && noCallsInAnyState)
235-
return List.of(new Hint(Kind.FINAL_STATE,
236-
String.format("%s is in state %s, where none of its methods with state refinements can be called",
250+
return List.of(new Hint(Kind.FINAL_STATE, asValues
251+
? String.format("When %s, none of the methods of %s with state refinements can be called",
252+
namesHere(current), receiver)
253+
: String.format("%s is in state %s, where none of its methods with state refinements can be called",
237254
receiver, namesHere(current))));
238255
return List.of();
239256
}
@@ -322,11 +339,22 @@ private String needed() {
322339
/** The groups joined by {@code or}, each in parentheses if it has several states and there are other groups */
323340
private String join(List<List<String>> groups) {
324341
return groups.stream().map(group -> {
325-
String states = group.stream().map(stateName).collect(Collectors.joining(" and "));
342+
String states = group.stream().map(this::stateName).collect(Collectors.joining(" and "));
326343
return groups.size() > 1 && group.size() > 1 ? "(" + states + ")" : states;
327344
}).collect(Collectors.joining(" or "));
328345
}
329346

347+
/**
348+
* A state as the error's message shows it, e.g. {@code lockState(d) == locked}, or {@code closed(d)} for a state
349+
* that keeps the shorthand, or only its name if no state set of the class is shown by its name
350+
*/
351+
private String stateName(String state) {
352+
if (!asValues)
353+
return state;
354+
return setNameOf.apply(state).map(set -> set + "(" + receiver + ") == " + state)
355+
.orElse(state + "(" + receiver + ")");
356+
}
357+
330358
private static String list(List<String> items) {
331359
if (items.size() <= MAX_LISTED_CALLS)
332360
return String.join(", ", items);
Collapse file

‎liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/object_checkers/AuxStateHandler.java‎

Copy file name to clipboardExpand all lines: liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/object_checkers/AuxStateHandler.java
+1-6Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.lang.annotation.Annotation;
44
import java.util.*;
5-
import java.util.function.UnaryOperator;
65
import java.util.stream.Collectors;
76

87
import liquidjava.diagnostics.DebugLog;
@@ -890,14 +889,10 @@ private static List<Hint> stateHints(TypeChecker tc, ReceiverStates states, Refi
890889
Optional<String> receiver = receiverOf(invocation);
891890
if (receiver.isEmpty() || !StateMachineBuilder.canModel(function))
892891
return List.of();
893-
// states are named as the message names them: by their set, unless it shows the shorthand
894892
Context context = tc.getContext();
895-
UnaryOperator<String> stateName = state -> StateValues
896-
.setNameOf(state, context.getGhostStates(), context.getGhosts()).map(set -> set + " == " + state)
897-
.orElse(state);
898893
try {
899894
return StateHints.forCall(states.machine(), function, states.current(), receiver.get(), category, checked,
900-
stateName);
895+
state -> StateValues.setNameOf(state, context.getGhostStates(), context.getGhosts()));
901896
} catch (RuntimeException e) {
902897
DebugLog.warn("Could not compute the state hints: " + e.getMessage());
903898
return List.of();
Collapse file

‎liquidjava-verifier/src/test/java/liquidjava/api/tests/TestStateHints.java‎

Copy file name to clipboardExpand all lines: liquidjava-verifier/src/test/java/liquidjava/api/tests/TestStateHints.java
+5-7Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ void missingCallsAreSuggestedInOrder() {
3838

3939
@Test
4040
void callSequenceNamesOnlyTheStateSetsItChanges() {
41-
assertHints("ErrorStateHintMultipleSets.java",
42-
new Hint(Kind.CALL_SEQUENCE,
43-
"Call d.unlock() before this call to move d from lockState == locked to lockState == unlocked"),
41+
assertHints("ErrorStateHintMultipleSets.java", new Hint(Kind.CALL_SEQUENCE,
42+
"Call d.unlock() before this call to move d from lockState(d) == locked to lockState(d) == unlocked"),
4443
new Hint(Kind.AVAILABLE_CALLS,
45-
"In state doorState == closed and lockState == locked, d can call: unlock()"));
44+
"When doorState(d) == closed and lockState(d) == locked, d can call: unlock()"));
4645
}
4746

4847
@Test
@@ -364,10 +363,9 @@ void setNameOfTheClassAndOfItsSuperclassKeepsTheShorthand() {
364363
@Test
365364
void queryWrittenWithItsStateSetIsOfferedAsAGuard() {
366365
assertHints("ErrorStateValueQuery.java",
367-
new Hint(Kind.NEEDED_STATE,
368-
"The state of d is unknown here, and this call needs it to be lock == unlocked"),
366+
new Hint(Kind.NEEDED_STATE, "The state of d is unknown here, and this call needs lock(d) == unlocked"),
369367
new Hint(Kind.GUARD,
370-
"Guard this call with if (d.isUnlocked()): when it returns true, d is lock == unlocked"));
368+
"Guard this call with if (d.isUnlocked()): when it returns true, lock(d) == unlocked"));
371369
}
372370

373371
@Test

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.