|
1 | | -package org.thingml.xtext.validation.checks |
2 | | - |
3 | | -import org.eclipse.emf.common.util.EList |
4 | | -import org.eclipse.xtext.validation.Check |
5 | | -import org.thingml.xtext.constraints.ThingMLHelpers |
6 | | -import org.thingml.xtext.constraints.Types |
7 | | -import org.thingml.xtext.helpers.ActionHelper |
8 | | -import org.thingml.xtext.helpers.StateHelper |
9 | | -import org.thingml.xtext.helpers.TyperHelper |
10 | | -import org.thingml.xtext.thingML.Configuration |
11 | | -import org.thingml.xtext.thingML.ExternalConnector |
12 | | -import org.thingml.xtext.thingML.Message |
13 | | -import org.thingml.xtext.thingML.SendAction |
14 | | -import org.thingml.xtext.thingML.Thing |
15 | | -import org.thingml.xtext.thingML.ThingMLPackage |
16 | | -import org.thingml.xtext.validation.ThingMLValidatorCheck |
17 | | -import org.thingml.xtext.validation.TypeChecker |
18 | | - |
19 | | -class MessageUsage extends ThingMLValidatorCheck { |
20 | | - |
21 | | -def boolean isSerializable(Message m) { |
22 | | -return m.parameters.forall[ p | |
23 | | -p.typeRef !== null && p.typeRef.type !== null && TyperHelper.isSerializable(p.typeRef.type ) |
24 | | -] |
25 | | -} |
26 | | - |
27 | | -@Check(FAST) |
28 | | -def checkSerialization(ExternalConnector c) { |
29 | | -val nonSerializable = c.port.receives.filter[m | !isSerializable(m)].toSet |
30 | | -nonSerializable.addAll(c.port.sends.filter[m | !isSerializable(m)]) |
31 | | -if (nonSerializable.size > 0) { |
32 | | -val msg = nonSerializable.join("Message(s) ", ", ", " is/are not serializable and cannot be used on an external connector ", [name]) |
33 | | -error(msg, c.eContainer, ThingMLPackage.eINSTANCE.configuration_Connectors, (c.eContainer as Configuration).connectors.indexOf(c), "serialization") |
34 | | -} |
35 | | -} |
36 | | - |
37 | | -@Check(FAST) |
38 | | -def checkMessageNotSent(Thing thing) { |
39 | | -if (thing.fragment) return |
40 | | -val allSendActions = ActionHelper.getAllActions(thing, SendAction) |
41 | | -ThingMLHelpers.allPorts(thing).forEach[p | |
42 | | -p.sends.forEach[m, i| |
43 | | -val isSent = allSendActions.exists[sa | sa.port === p && sa.message === m] |
44 | | -if (!isSent) { |
45 | | -val msg = "Message " + p.name + "." + m.name + " is never sent" |
46 | | -val t = ThingMLHelpers.findContainingThing(p) |
47 | | -if (t == thing) |
48 | | -warning(msg, p, ThingMLPackage.eINSTANCE.port_Sends, i, "message-never-sent") |
49 | | -else //FIXME: this assumes the port/message comes from a Thing directly included (which is OK for 99% of the case), but will highlight the wrong include if it comes from several levels of includes... The old implementation was also doing the same "mistale" |
50 | | -warning(msg, thing, ThingMLPackage.eINSTANCE.thing_Includes, thing.includes.indexOf(t), "included-messages-never-sent") |
51 | | -} |
52 | | -] |
53 | | -] |
54 | | -} |
55 | | - |
56 | | -@Check(FAST) |
57 | | -def checkMessageNotReceived(Thing thing) { |
58 | | -if (thing.fragment) return; |
59 | | -val handlers = StateHelper.allMessageHandlers(thing) |
60 | | -// Check own ports |
61 | | -ThingMLHelpers.allPorts(thing).forEach[p | |
62 | | -p.receives.forEach[m, i| |
63 | | -if (handlers.get(p) === null || handlers.get(p).get(m) === null) { |
64 | | -val msg = "Message " + p.name + "." + m.name + " is never received" |
65 | | -val t = ThingMLHelpers.findContainingThing(p) |
66 | | -if (t == thing) |
67 | | -warning(msg, p, ThingMLPackage.eINSTANCE.port_Receives, i, "message-never-used") |
68 | | -else //FIXME: this assumes the port/message comes from a Thing directly included (which is OK for 99% of the case), but will highlight the wrong include if it comes from several levels of includes... The old implementation was also doing the same "mistale" |
69 | | -warning(msg, thing, ThingMLPackage.eINSTANCE.thing_Includes, thing.includes.indexOf(t), "included-messages-never-used") |
70 | | - |
71 | | -} |
72 | | -] |
73 | | -] |
74 | | -} |
75 | | - |
76 | | -@Check(FAST) |
77 | | -def checkSendAction(SendAction send) { |
78 | | -val msg = send.message |
79 | | -val params = send.parameters |
80 | | -val parent = send.eContainer.eGet(send.eContainingFeature) |
81 | | -// Check that the message is sent with the right number of parameters |
82 | | -if (msg.parameters.size !== params.size) { |
83 | | -val m = "Message "+msg.name+" is sent with a wrong number of parameters. Expected "+msg.parameters.size+", sent with "+params.size |
84 | | -if (parent instanceof EList) |
85 | | -error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "message-send-wrong-number-parameters") |
86 | | -else |
87 | | -error(m, send.eContainer, send.eContainingFeature, "message-send-wrong-number-parameters") |
88 | | -return; |
89 | | -} |
90 | | -// Check that the parameters are properly typed |
91 | | -msg.parameters.forEach [ p, i | |
92 | | -val e = params.get(i); |
93 | | -val expected = TyperHelper.getBroadType(p.getTypeRef()); |
94 | | -val actual = TypeChecker.computeTypeOf(e); |
95 | | -if (actual !== null) { |
96 | | -if (actual.equals(Types.ERROR_TYPEREF)) { |
97 | | -val m = "Message "+msg.name+" is sent with an erroneous parameter. Expected "+Types.toString(expected)+", sent with "+Types.toString(TyperHelper.getBroadType(actual)) |
98 | | -if (parent instanceof EList) |
99 | | -error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type") |
100 | | -else |
101 | | -error(m, send.eContainer, send.eContainingFeature, "type") |
102 | | -} else if (actual.equals(Types.ANY_TYPEREF)) { |
103 | | -val m = "Message "+msg.name+" is sent with a parameter which cannot be typed. Consider using a cast (<exp> as <type>)." |
104 | | -if (parent instanceof EList) |
105 | | -warning(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type-cast", p.getTypeRef().getType().name) |
106 | | -else |
107 | | -warning(m, send.eContainer, send.eContainingFeature, "type-cast", p.getTypeRef().getType().name) |
108 | | -} else if (!TyperHelper.isA(actual, expected)) { |
109 | | -val m = "Message "+msg.name+" is sent with an erroneous parameter. Expected "+Types.toString(expected)+", sent with "+Types.toString(TyperHelper.getBroadType(actual)) |
110 | | -if (parent instanceof EList) |
111 | | -error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type") |
112 | | -else |
113 | | -error(m, send.eContainer, send.eContainingFeature, "type") |
114 | | -} |
115 | | -} |
116 | | -] |
117 | | -} |
118 | | -} |
| 1 | +package org.thingml.xtext.validation.checks |
| 2 | + |
| 3 | +import org.eclipse.emf.common.util.EList |
| 4 | +import org.eclipse.xtext.validation.Check |
| 5 | +import org.thingml.xtext.constraints.ThingMLHelpers |
| 6 | +import org.thingml.xtext.constraints.Types |
| 7 | +import org.thingml.xtext.helpers.ActionHelper |
| 8 | +import org.thingml.xtext.helpers.AnnotatedElementHelper |
| 9 | +import org.thingml.xtext.helpers.StateHelper |
| 10 | +import org.thingml.xtext.helpers.TyperHelper |
| 11 | +import org.thingml.xtext.thingML.Configuration |
| 12 | +import org.thingml.xtext.thingML.ExternalConnector |
| 13 | +import org.thingml.xtext.thingML.Message |
| 14 | +import org.thingml.xtext.thingML.SendAction |
| 15 | +import org.thingml.xtext.thingML.Thing |
| 16 | +import org.thingml.xtext.thingML.ThingMLPackage |
| 17 | +import org.thingml.xtext.validation.ThingMLValidatorCheck |
| 18 | +import org.thingml.xtext.validation.TypeChecker |
| 19 | + |
| 20 | +class MessageUsage extends ThingMLValidatorCheck { |
| 21 | + |
| 22 | +def boolean isSerializable(Message m) { |
| 23 | +return m.parameters.forall[ p | |
| 24 | +p.typeRef !== null && p.typeRef.type !== null && TyperHelper.isSerializable(p.typeRef.type ) |
| 25 | +] |
| 26 | +} |
| 27 | + |
| 28 | +@Check(FAST) |
| 29 | +def checkSerialization(ExternalConnector c) { |
| 30 | +val nonSerializable = c.port.receives.filter[m | !isSerializable(m)].toSet |
| 31 | +nonSerializable.addAll(c.port.sends.filter[m | !isSerializable(m)]) |
| 32 | +if (nonSerializable.size > 0) { |
| 33 | +val msg = nonSerializable.join("Message(s) ", ", ", " is/are not serializable and cannot be used on an external connector ", [name]) |
| 34 | +error(msg, c.eContainer, ThingMLPackage.eINSTANCE.configuration_Connectors, (c.eContainer as Configuration).connectors.indexOf(c), "serialization") |
| 35 | +} |
| 36 | +} |
| 37 | + |
| 38 | +@Check(FAST) |
| 39 | +def checkMessageNotSent(Thing thing) { |
| 40 | +if (thing.fragment) return |
| 41 | +val allSendActions = ActionHelper.getAllActions(thing, SendAction) |
| 42 | +ThingMLHelpers.allPorts(thing).forEach[p | |
| 43 | +p.sends.forEach[m, i| |
| 44 | +if (!AnnotatedElementHelper.isDefined(m, "ignore", "not-used")) { |
| 45 | +val isSent = allSendActions.exists[sa | sa.port === p && sa.message === m] |
| 46 | +if (!isSent) { |
| 47 | +val msg = "Message " + p.name + "." + m.name + " is never sent" |
| 48 | +val t = ThingMLHelpers.findContainingThing(p) |
| 49 | +if (t == thing) |
| 50 | +warning(msg, p, ThingMLPackage.eINSTANCE.port_Sends, i, "message-never-sent") |
| 51 | +else //FIXME: this assumes the port/message comes from a Thing directly included (which is OK for 99% of the case), but will highlight the wrong include if it comes from several levels of includes... The old implementation was also doing the same "mistale" |
| 52 | +warning(msg, thing, ThingMLPackage.eINSTANCE.thing_Includes, thing.includes.indexOf(t), "included-messages-never-sent") |
| 53 | +} |
| 54 | +} |
| 55 | +] |
| 56 | +] |
| 57 | +} |
| 58 | + |
| 59 | +@Check(FAST) |
| 60 | +def checkMessageNotReceived(Thing thing) { |
| 61 | +if (thing.fragment) return; |
| 62 | +val handlers = StateHelper.allMessageHandlers(thing) |
| 63 | +// Check own ports |
| 64 | +ThingMLHelpers.allPorts(thing).forEach[p | |
| 65 | +p.receives.forEach[m, i| |
| 66 | +if (AnnotatedElementHelper.isDefined(m, "ignore", "not-used")) { |
| 67 | +if (handlers.get(p) === null || handlers.get(p).get(m) === null) { |
| 68 | +val msg = "Message " + p.name + "." + m.name + " is never received" |
| 69 | +val t = ThingMLHelpers.findContainingThing(p) |
| 70 | +if (t == thing) |
| 71 | +warning(msg, p, ThingMLPackage.eINSTANCE.port_Receives, i, "message-never-used") |
| 72 | +else //FIXME: this assumes the port/message comes from a Thing directly included (which is OK for 99% of the case), but will highlight the wrong include if it comes from several levels of includes... The old implementation was also doing the same "mistale" |
| 73 | +warning(msg, thing, ThingMLPackage.eINSTANCE.thing_Includes, thing.includes.indexOf(t), "included-messages-never-used") |
| 74 | + |
| 75 | +} |
| 76 | +} |
| 77 | +] |
| 78 | +] |
| 79 | +} |
| 80 | + |
| 81 | +@Check(FAST) |
| 82 | +def checkSendAction(SendAction send) { |
| 83 | +val msg = send.message |
| 84 | +val params = send.parameters |
| 85 | +val parent = send.eContainer.eGet(send.eContainingFeature) |
| 86 | +// Check that the message is sent with the right number of parameters |
| 87 | +if (msg.parameters.size !== params.size) { |
| 88 | +val m = "Message "+msg.name+" is sent with a wrong number of parameters. Expected "+msg.parameters.size+", sent with "+params.size |
| 89 | +if (parent instanceof EList) |
| 90 | +error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "message-send-wrong-number-parameters") |
| 91 | +else |
| 92 | +error(m, send.eContainer, send.eContainingFeature, "message-send-wrong-number-parameters") |
| 93 | +return; |
| 94 | +} |
| 95 | +// Check that the parameters are properly typed |
| 96 | +msg.parameters.forEach [ p, i | |
| 97 | +val e = params.get(i); |
| 98 | +val expected = TyperHelper.getBroadType(p.getTypeRef()); |
| 99 | +val actual = TypeChecker.computeTypeOf(e); |
| 100 | +if (actual !== null) { |
| 101 | +if (actual.equals(Types.ERROR_TYPEREF)) { |
| 102 | +val m = "Message "+msg.name+" is sent with an erroneous parameter. Expected "+Types.toString(expected)+", sent with "+Types.toString(TyperHelper.getBroadType(actual)) |
| 103 | +if (parent instanceof EList) |
| 104 | +error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type") |
| 105 | +else |
| 106 | +error(m, send.eContainer, send.eContainingFeature, "type") |
| 107 | +} else if (actual.equals(Types.ANY_TYPEREF)) { |
| 108 | +val m = "Message "+msg.name+" is sent with a parameter which cannot be typed. Consider using a cast (<exp> as <type>)." |
| 109 | +if (parent instanceof EList) |
| 110 | +warning(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type-cast", p.getTypeRef().getType().name) |
| 111 | +else |
| 112 | +warning(m, send.eContainer, send.eContainingFeature, "type-cast", p.getTypeRef().getType().name) |
| 113 | +} else if (!TyperHelper.isA(actual, expected)) { |
| 114 | +val m = "Message "+msg.name+" is sent with an erroneous parameter. Expected "+Types.toString(expected)+", sent with "+Types.toString(TyperHelper.getBroadType(actual)) |
| 115 | +if (parent instanceof EList) |
| 116 | +error(m, send.eContainer, send.eContainingFeature, (parent as EList).indexOf(send), "type") |
| 117 | +else |
| 118 | +error(m, send.eContainer, send.eContainingFeature, "type") |
| 119 | +} |
| 120 | +} |
| 121 | +] |
| 122 | +} |
| 123 | +} |
0 commit comments