Commit 4547a04a authored by Markus Klinik's avatar Markus Klinik
Browse files

move ImplementsAbstractMethod to ClassHierarchy

parent 023a37d4
......@@ -3,6 +3,7 @@ module ClassHierarchy
import lang::java::m3::Core;
import Message;
import Set;
import Relation;
import Util;
......@@ -209,3 +210,25 @@ set[Message] methodDependsOn(M3 model, str dependerClassName, str dependerMethod
}
return {};
}
// Given an M3 model, a class and an abstract function, determine whether the
// class implements the function.
bool implementsAbstractMethod(M3 model, loc class, loc function)
{
// transitive closure of extends and implements
// TODO: should this be (model.extends*) + (model.implements*) ?
// see https://stackoverflow.com/questions/57525714/m3-java-how-to-check-that-a-class-implements-a-function-from-an-interface
rel[loc,loc] super = (model.extends + model.implements)*;
// All methods that the class overrides
// TODO: this can be achieved easier using domainR
rel[loc,loc] allOverrides = ident(union({methods(model,s) | s <- super[class]})) o model.methodOverrides;
// All methods that override the function
functionOverrides = invert(allOverrides)[function];
return size(functionOverrides) > 0;
}
module ImplementsAbstractMethod
import Relation;
import Set;
import lang::java::m3::Core;
// Given an M3 model, a class and an abstract function, determine whether the
// class implements the function.
bool implementsAbstractMethod(M3 model, loc class, loc function)
{
// transitive closure of extends and implements
// TODO: should this be (model.extends*) + (model.implements*) ?
// see https://stackoverflow.com/questions/57525714/m3-java-how-to-check-that-a-class-implements-a-function-from-an-interface
rel[loc,loc] super = (model.extends + model.implements)*;
// All methods that the class overrides
// TODO: this can be achieved easier using domainR
rel[loc,loc] allOverrides = ident(union({methods(model,s) | s <- super[class]})) o model.methodOverrides;
// All methods that override the function
functionOverrides = invert(allOverrides)[function];
return size(functionOverrides) > 0;
}
\ No newline at end of file
......@@ -114,3 +114,52 @@ test bool classDoesntImplementInterface()
{ "|java+class:///A| should implement |java+interface:///MyInterface|" }
<= messages(errors);
}
test bool implementsInterfaceDirectly()
{
M3 model = loadTestProject("ImplementsInterface-test-data/directly-implements-interface");
return implementsAbstractMethod(model, |java+class:///A|, |java+method:///MyInterface/myAbstractFunction()|);
}
test bool implementsInterfaceIndirectly()
{
M3 model = loadTestProject("ImplementsInterface-test-data/indirectly-implements-interface");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
test bool doesntImplementInterface()
{
M3 model = loadTestProject("ImplementsInterface-test-data/doesnt-implement-interface");
return !implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
// How to detect that the interface itself has an implementation?
// This is currently a false positive.
test bool defaultImplementationInInterface()
{
return true;
/*
M3 model = loadTestProject("ImplementsInterface-test-data/default-implementation-in-interface");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
*/
}
// The problem here is that methodOverrides states that IntermediateInterface overrides
// MyInterface/myAbstractFunction, even though it just declares it. In my opinion this
// is a bug. But: because this produces a compile error, I would argue that we don't need
// to generate an error message here.
test bool intermediateInterfaceDeclaresFunction()
{
return true;
/*
M3 model = loadTestProject("ImplementsInterface-test-data/intermediate-interface-declares-function");
return !implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
*/
}
test bool intermediateInterfaceDefaultImplementsFunction()
{
M3 model = loadTestProject("ImplementsInterface-test-data/intermediate-interface-default-implements-function");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
\ No newline at end of file
module \test::ImplementsAbstractMethodSpec
import lang::java::m3::Core;
import ImplementsAbstractMethod;
import \test::SpecUtil;
test bool implementsInterfaceDirectly()
{
M3 model = loadTestProject("ImplementsInterface-test-data/directly-implements-interface");
return implementsAbstractMethod(model, |java+class:///A|, |java+method:///MyInterface/myAbstractFunction()|);
}
test bool implementsInterfaceIndirectly()
{
M3 model = loadTestProject("ImplementsInterface-test-data/indirectly-implements-interface");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
test bool doesntImplementInterface()
{
M3 model = loadTestProject("ImplementsInterface-test-data/doesnt-implement-interface");
return !implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
// How to detect that the interface itself has an implementation?
// This is currently a false positive.
test bool defaultImplementationInInterface()
{
return true;
/*
M3 model = loadTestProject("ImplementsInterface-test-data/default-implementation-in-interface");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
*/
}
// The problem here is that methodOverrides states that IntermediateInterface overrides
// MyInterface/myAbstractFunction, even though it just declares it. In my opinion this
// is a bug. But: because this produces a compile error, I would argue that we don't need
// to generate an error message here.
test bool intermediateInterfaceDeclaresFunction()
{
return true;
/*
M3 model = loadTestProject("ImplementsInterface-test-data/intermediate-interface-declares-function");
return !implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
*/
}
test bool intermediateInterfaceDefaultImplementsFunction()
{
M3 model = loadTestProject("ImplementsInterface-test-data/intermediate-interface-default-implements-function");
return implementsAbstractMethod(model, |java+class:///B|, |java+method:///MyInterface/myAbstractFunction()|);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment