diff --git a/examples/statemachine/src/language-server/statemachine-code-actions.ts b/examples/statemachine/src/language-server/statemachine-code-actions.ts index 97c3f9ea9..3a7158165 100644 --- a/examples/statemachine/src/language-server/statemachine-code-actions.ts +++ b/examples/statemachine/src/language-server/statemachine-code-actions.ts @@ -38,7 +38,7 @@ export class StatemachineCodeActionProvider implements CodeActionProvider { case IssueCodes.StateNameUppercase: accept(this.makeUpperCase(diagnostic, document)); break; - case IssueCodes.UnusedSymbol: + case IssueCodes.UnreachedState: accept(this.removeUnusedSymbol(diagnostic, document)); break; } diff --git a/examples/statemachine/src/language-server/statemachine-validator.ts b/examples/statemachine/src/language-server/statemachine-validator.ts index 3d7ff4689..155709505 100644 --- a/examples/statemachine/src/language-server/statemachine-validator.ts +++ b/examples/statemachine/src/language-server/statemachine-validator.ts @@ -11,7 +11,9 @@ import { MultiMap, diagnosticData } from 'langium'; export namespace IssueCodes { export const StateNameUppercase = 'state-name-uppercase'; - export const UnusedSymbol = 'unused-symbol'; + export const UnreachedState = 'unreached-state'; + export const UnreachedCommand = 'unreached-command'; + export const UnreachedEvent = 'unreached-event'; } export function registerValidationChecks(services: StatemachineServices) { @@ -22,7 +24,8 @@ export function registerValidationChecks(services: StatemachineServices) { Statemachine: [ validator.checkUniqueSymbolName, validator.checkUnreachedStates, - validator.checkUnusedCommandsAndEvents, + validator.checkUnreachedCommands, + validator.checkUnreachedEvents, ] }; registry.register(checks, validator); @@ -69,7 +72,7 @@ export class StatemachineValidator { } /** - * Checks if there are unreached states. + * Checks for unreached states within the statemachine. * @param statemachine the statemachine to check * @param accept the acceptor to report errors */ @@ -88,57 +91,78 @@ export class StatemachineValidator { for (const [name, state] of states.entries()) { accept('hint', `Unreached state: ${name}`, { node: state, - data: diagnosticData(IssueCodes.UnusedSymbol), + data: diagnosticData(IssueCodes.UnreachedState), tags: [1], }); } } - private removeStates(states: Map, transitions: Transition[]): void { - for (const { state: { ref } } of transitions) { - if (ref && states.has(ref.name)) { - states.delete(ref.name); - this.removeStates(states, ref.transitions); + /** + * Checks for unreached commands within the statemachine. + * @param statemachine The statemachine to check. + * @param acceptor The acceptor to report errors. + */ + checkUnreachedCommands(statemachine: Statemachine, acceptor: ValidationAcceptor): void { + const commandsByName = new Map(); + + for (const command of statemachine.commands) { + commandsByName.set(command.name, command); + } + + for (const { actions } of statemachine.states) { + for (const { ref } of actions) { + if (ref && commandsByName.has(ref.name)) { + commandsByName.delete(ref.name); + } } } + + for (const [name, command] of commandsByName.entries()) { + acceptor('warning', `Unreached command: ${name}`, { + node: command, + property: 'name', + data: diagnosticData(IssueCodes.UnreachedCommand), + tags: [1], + }); + } } /** - * Checks if there are unused commands and events. + * Checks for unreached evens within the statemachine. * @param statemachine the statemachine to check * @param accept the acceptor to report errors */ - checkUnusedCommandsAndEvents(statemachine: Statemachine, accept: ValidationAcceptor): void { - const commands = new Map(); - for (const command of statemachine.commands) { - commands.set(command.name, command); - } + checkUnreachedEvents(statemachine: Statemachine, acceptor: ValidationAcceptor): void { + const eventsByName = new Map(); - const events = new Map(); for (const event of statemachine.events) { - events.set(event.name, event); + eventsByName.set(event.name, event); } - for (const { actions, transitions } of statemachine.states) { - for (const { ref } of actions) { - if (ref && commands.has(ref.name)) { - commands.delete(ref.name); - } - } + for (const { transitions } of statemachine.states) { for (const { event: { ref: refEvent } } of transitions) { - if (refEvent && events.has(refEvent.name)) { - events.delete(refEvent.name); + if (refEvent && eventsByName.has(refEvent.name)) { + eventsByName.delete(refEvent.name); } } } - for (const [name, symbol] of [...commands.entries(), ...events.entries()]) { - accept('warning', `Unused command or event name: ${name}`, { - node: symbol, + for (const [name, event] of eventsByName.entries()) { + acceptor('warning', `Unreached event: ${name}`, { + node: event, property: 'name', - data: diagnosticData(IssueCodes.UnusedSymbol), + data: diagnosticData(IssueCodes.UnreachedEvent), tags: [1], }); } } + + private removeStates(states: Map, transitions: Transition[]): void { + for (const { state: { ref } } of transitions) { + if (ref && states.has(ref.name)) { + states.delete(ref.name); + this.removeStates(states, ref.transitions); + } + } + } }