Repository: stfc/PSyclone

Issue:
Call create method should be a classmethod not a staticmethod
In the future we may want to subclass a `Call` node and use the `create` method in the `Call` node to create an instance of the subclass. An example of this could be in the generation of a PSyIR version of the algorithm layer for LFRic where invoke calls will contain `builtin` or `coded` calls which are subclasses of `Call` and could be simply created using the `create` method.

Discussion:

Patch:
diff --git a/changelog b/changelog
index 89d16decf..3bf34bc3b 100644
--- a/changelog
+++ b/changelog
@@ -358,6 +358,8 @@
 	115) PR #1102 towards #1031. Improves PSyIR fparser2 frontend support for
 	structures types.
 
+	116) PR #1111 for #1110. The Call create method is a classmethod.
+
 release 1.9.0 20th May 2020
 
 	1) #602 for #597. Modify Node.ancestor() to optionally include
diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py
index f25f9cf6d..380d97ec9 100644
--- a/src/psyclone/psyir/nodes/call.py
+++ b/src/psyclone/psyir/nodes/call.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2020, Science and Technology Facilities Council.
+# Copyright (c) 2020-21, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -66,19 +66,19 @@ class Call(Statement):
 
         self._routine = routine
 
-    @staticmethod
-    def create(routine, arguments):
-        '''Create a Call instance given valid instances of a routine symbol,
-        and a list of child nodes for its arguments.
+    @classmethod
+    def create(cls, routine, arguments):
+        '''Create an instance of class cls given valid instances of a routine
+        symbol, and a list of child nodes for its arguments.
 
-        :param routine: the routine that this call calls.
+        :param routine: the routine that class cls calls.
         :type routine: py:class:`psyclone.psyir.symbols.RoutineSymbol`
         :param arguments: the arguments to this routine. These are \
             added as child nodes.
         :type arguments: list of :py:class:`psyclone.psyir.nodes.DataNode`
 
-        :returns: a Call instance.
-        :rtype: :py:class:`psyclone.psyir.nodes.Call`
+        :returns: an instance of cls.
+        :rtype: :py:class:`psyclone.psyir.nodes.Call` or a subclass thereof.
 
         '''
         if not isinstance(routine, RoutineSymbol):
@@ -90,7 +90,7 @@ class Call(Statement):
                 "Call create arguments argument should be a list but found "
                 "'{0}'.".format(type(arguments).__name__))
 
-        call = Call(routine)
+        call = cls(routine)
         call.children = arguments
         for child in call.children:
             child.parent = call


Test patch:
diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py
index 6d8986b05..c73c09334 100644
--- a/src/psyclone/tests/psyir/nodes/call_test.py
+++ b/src/psyclone/tests/psyir/nodes/call_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2020, Science and Technology Facilities Council.
+# Copyright (c) 2020-21, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -45,6 +45,10 @@ from psyclone.psyir.symbols import ArrayType, INTEGER_TYPE, DataSymbol, \
 from psyclone.errors import GenerationError
 
 
+class SpecialCall(Call):
+    '''Test Class specialising the Call class'''
+
+
 def test_call_init():
     '''Test that a Call can be created as expected. Also test the routine
     property.
@@ -77,14 +81,17 @@ def test_call_init_error():
             "'NoneType'." in str(info.value))
 
 
-def test_call_create():
+@pytest.mark.parametrize("cls", [Call, SpecialCall])
+def test_call_create(cls):
     '''Test that the create method creates a valid call with arguments'''
 
     routine = RoutineSymbol("ellie")
     array_type = ArrayType(INTEGER_TYPE, shape=[10, 20])
     arguments = [Reference(DataSymbol("arg1", INTEGER_TYPE)),
                  ArrayReference(DataSymbol("arg2", array_type))]
-    call = Call.create(routine, arguments)
+    call = cls.create(routine, arguments)
+    # pylint: disable=unidiomatic-typecheck
+    assert type(call) is cls
     assert call.routine is routine
     for idx, child, in enumerate(call.children):
         assert child is arguments[idx]