Repository: Qiskit/qiskit

Issue:
Appending an `Instruction` class instead of an instance gives an unhelpful error message
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

If a user forgets the `()` for their gate, they'll see the following error message:
```
>>> import qiskit as qk
>>> from qiskit.extensions.standard import *
>>> qr = qk.QuantumRegister(2)
>>> qc = qk.QuantumCircuit(qr)
>>> qc.append(CnotGate, qr[:], [])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/circuit/quantumcircuit.py", line 462, in append
    for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
TypeError: broadcast_arguments() missing 1 required positional argument: 'cargs'
```

because `instruction` will be an `Instruction` subclass, and not an instance. (`broadcast_arguments` will be called as a static method on the class, instead of an instance method so `self` will be missing. Thus the `TypeError` for missing `cargs`)

In `QuantumCircuit.append`, we already check `if not isinstance(instruction, Instruction)` before checking `hasattr(instruction, 'to_instruction')`.

This should be restructured so that if `instruction` is not an `Instruction` instance, and does not have a `to_instruction`, we raise. (Optionally, we can check if `issubclass(instruction, Instruction)` and if so, suggest adding `()`).


Discussion:
@1ucian0 could I try and implement this one? I am new to Qiskit, and this looks like a good first issue!

Go for it! Let me know if you need any assistance! 

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -557,9 +557,19 @@ def append(self, instruction, qargs=None, cargs=None):
         Returns:
             qiskit.circuit.Instruction: a handle to the instruction that was just added
 
+        Raises:
+            CircuitError: if object passed is a subclass of Instruction
+            CircuitError: if object passed is neither subclass nor an instance of Instruction
         """
-        # Convert input to Instruction
-        if not isinstance(instruction, Instruction) and hasattr(instruction, 'to_instruction'):
+        # Convert input to instruction
+        if not isinstance(instruction, Instruction) and not hasattr(instruction, 'to_instruction'):
+            if issubclass(instruction, Instruction):
+                raise CircuitError('Object is a subclass of Instruction, please add () to '
+                                   'pass an instance of this object.')
+
+            raise CircuitError('Object to append must be an Instruction or '
+                               'have a to_instruction() method.')
+        if not isinstance(instruction, Instruction) and hasattr(instruction, "to_instruction"):
             instruction = instruction.to_instruction()
 
         expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]


Test patch:
diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py
--- a/test/python/circuit/test_instructions.py
+++ b/test/python/circuit/test_instructions.py
@@ -332,6 +332,15 @@ def test_modifying_copied_params_leaves_orig(self):
 
         self.assertEqual(inst.params, [0, 1, 2])
 
+    def test_instance_of_instruction(self):
+        """Test correct error message is raised when invalid instruction
+        is passed to append"""
+
+        qr = QuantumRegister(2)
+        qc = QuantumCircuit(qr)
+        with self.assertRaises(CircuitError):
+            qc.append(HGate, qr[:], [])
+
 
 if __name__ == '__main__':
     unittest.main()