Class implements Interface error check
General Pattern
We need a way to expres that a class implementing an interface should implement all the methods required by the interface.
interface I{ public void foo(); } class A implements I{ }
There should be an error on
A
: The typeA
must implement the inherited abstract methodI.foo()
This pattern surfaces with required attributes in entities.
model entity X{ i : Int j : String } data X x{ i = 3 }
This should give an error on
x
:x.j
should be set.Solution (Tasks)
For classes implementing interface the solution will be along the lines of.
- match on the
Class(name, _, implementsInterfaces, _, methods)
- resolve
interface
tointeface uri
by looking at the Use(Def(…)) =>/ ClassOrInterface:"I"#0 /
- get all
method uris
insideinterface uri
=>[ / ClassOrInterface:"I"#0 / Method:"foo"#0 / , ... ]
- get all
method uris
insideself
=>[ ... ]
- use stratego to find missing, and if there are give error on
name
However this only works if the thing matched on defines stuff in the index.
The entity instantiation does not define the attributes(fields) inside the entity. So in that case step 4 does not apply and one compares the
methods
in ‘Class(…)’ directly with themethod uris
.Solution (TS and NaBL)
…
Submitted by Daco Harkes on 13 March 2014 at 13:39
Issue Log
You can do some of this with existing tasks, which we typically use for content completion. These tasks will give you all definitions in a scope, for example all methods of an interface or all methods of the class. There is also a task which turns the results of another task into a list as a single result, but this might not even be needed. The comparison is the issue here. Maybe you can come up with a new task for this. This task should be a combinator and should take two parameters
s1
ands2
. It should result ins1 \ s2
. When this task has no results, everything is fine in your example.
One more problem is that properties of definitions also need to be taken into account. In Java we need to compare the signature of methods, not their names.
This is probably related to the solution for disambiguation/filtering then. But for Daco’s use case, a difference task should be sufficient. BTW, there is already a
Diff
task, what does exactly what I described.
A possible syntax:
t@ClassDec(m*, c, SuperDec(sc), _) :- where not m* contains Abstract() and foreach Method m with modifiers Abstract() in definition of sc where m has parameter-types p-ty* and surrounding Type contains Method m with parameter-types p-ty* without modifiers Abstract() else error "Non-abstract class must implement all inherited abstract methods" on t
The “contains all of something” has been generalized to a stratego implementation.
Interface:
ContainsAll( namespace of which all should be present, filter on properties of elements in the namespace, function that succeeds for element which should contain all items, function that gets the definition for this element, function that gets all uses in the element, function that gets the element name (for error reporting) )
Use:
rules // check if entities have all the required attributes and roles assigned contains-all-errors = ![ ContainsAll( NablNsAttribute(), [ (NablProp_derivation-type(), Normal()), (NablProp_multiplicity(), One()) ], "ei-a1", "ei-a2", "ei-a3", "ei-a4" ), ContainsAll( NablNsRole(), [], "ei-r1", "ei-r2", "ei-r3", "ei-r4" ) ] eval(|f) = where("ei-a1":=f); is-entityinstance eval(|f) = where("ei-a2":=f); entityinstance-get-type eval(|f) = where("ei-a3":=f); entityinstance-get-attrvalues;map(attributevalue-get-attr) eval(|f) = where("ei-a4":=f); entityinstance-get-name eval(|f) = where("ei-r1":=f); is-entityinstance eval(|f) = where("ei-r2":=f); entityinstance-get-type eval(|f) = where("ei-r3":=f); entityinstance-get-rolevalues;map(rolevalue-get-role) eval(|f) = where("ei-r4":=f); entityinstance-get-name
Implementation:
rules // api for checking something contains all of def // interface implemented by language contains-all-errors = fail eval(|f) = fail nabl-constraint(|ctx): a -> <fail> with contains-all-errors;map(try(nabl-constraint(|ctx, a))) nabl-constraint(|ctx, a): f -> <id> where ContainsAll( def-child-ns, filters, f-on, f-def-of, f-ast-children, f-msg-on ) := f where <eval(|f-on)>a with a-ty := <eval(|f-def-of)>a; a-ty-def := <_nabl-create-collectdefs(|ctx)> a-ty; [a-ty-def-children] := <_nabl-resolve-all-tasks(|ctx, def-child-ns, [])> [a-ty-def]; a-children := <eval(|f-ast-children)>a; a-children-defs := <map(_nabl-create-collectdefs(|ctx));_task-create-combine(|ctx)>a-children; diff := <_task-create-diff(|ctx)> (a-ty-def-children, a-children-defs); diff-filtered := <filter-propconstraints(|ctx,filters)>diff; a-msg-on := <eval(|f-msg-on)>a; msg := ["Provide all required ", <_pp-ns>def-child-ns, "s: ", diff-filtered]; <_task-create-error-on-success(|ctx, diff-filtered, msg)> a-msg-on filter-propconstraints(|ctx, filters) = where(!(filters, []);equal);id filter-propconstraints(|ctx, filters): def-list -> def-list-filtered-rec where !(filters, []);not(equal) with [filter|filters-tail] := filters; (nablprop, propvalue) := filter; def-list-filtered := <_nabl-create-propconstraint(|ctx, nablprop, def-list)> propvalue; def-list-filtered-rec := <filter-propconstraints(|ctx, filters-tail)>def-list-filtered
Log in to post comments