Use Abstract Base Classes to ensure Problem is not instantiated

- Problem() is a parent abstract class that should only be used for
  subclassing.
- With the help of ABC, implement abstract methods that force
  subclasses to implement quiet and verbose outputs.
- The repeated logic of "if self.quiet" is consolidated in Problem.

Signed-off-by: Yuto Takano <yuto.takano@arm.com>
diff --git a/tests/scripts/check_names.py b/tests/scripts/check_names.py
index 0eba967..a9aa118 100755
--- a/tests/scripts/check_names.py
+++ b/tests/scripts/check_names.py
@@ -42,6 +42,7 @@
 error. It must be run from Mbed TLS root.
 """
 
+import abc
 import argparse
 import glob
 import textwrap
@@ -92,9 +93,11 @@
             " {0} | {1}\n".format(" " * len(gutter), underline)
         )
 
-class Problem(): # pylint: disable=too-few-public-methods
+class Problem(abc.ABC): # pylint: disable=too-few-public-methods
     """
-    A parent class representing a form of static analysis error.
+    An abstract parent class representing a form of static analysis error.
+    It extends an Abstract Base Class, which means it is not instantiable, and
+    it also mandates certain abstract methods to be implemented in subclasses.
     """
     # Class variable to control the quietness of all problems
     quiet = False
@@ -104,6 +107,28 @@
         self.textwrapper.initial_indent = "    > "
         self.textwrapper.subsequent_indent = "      "
 
+    def __str__(self):
+        """
+        Unified string representation method for all Problems.
+        """
+        if self.__class__.quiet:
+            return self.quiet_output()
+        return self.verbose_output()
+
+    @abc.abstractmethod
+    def quiet_output(self):
+        """
+        The output when --quiet is enabled.
+        """
+        pass
+
+    @abc.abstractmethod
+    def verbose_output(self):
+        """
+        The default output with explanation and code snippet if appropriate.
+        """
+        pass
+
 class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
     """
     A problem that occurs when an exported/available symbol in the object file
@@ -117,10 +142,10 @@
         self.symbol_name = symbol_name
         Problem.__init__(self)
 
-    def __str__(self):
-        if self.quiet:
-            return "{0}".format(self.symbol_name)
+    def quiet_output(self):
+        return "{0}".format(self.symbol_name)
 
+    def verbose_output(self):
         return self.textwrapper.fill(
             "'{0}' was found as an available symbol in the output of nm, "
             "however it was not declared in any header files."
@@ -140,13 +165,14 @@
         self.match = match
         Problem.__init__(self)
 
-    def __str__(self):
-        if self.quiet:
-            return (
-                "{0}:{1}:{2}"
-                .format(self.match.filename, self.match.line_no, self.match.name)
-            )
 
+    def quiet_output(self):
+        return (
+            "{0}:{1}:{2}"
+            .format(self.match.filename, self.match.line_no, self.match.name)
+        )
+
+    def verbose_output(self):
         return self.textwrapper.fill(
             "{0}:{1}: '{2}' does not match the required pattern '{3}'."
             .format(
@@ -169,13 +195,13 @@
         self.match = match
         Problem.__init__(self)
 
-    def __str__(self):
-        if self.quiet:
-            return (
-                "{0}:{1}:{2}"
-                .format(self.match.filename, self.match.line_no, self.match.name)
-            )
+    def quiet_output(self):
+        return (
+            "{0}:{1}:{2}"
+            .format(self.match.filename, self.match.line_no, self.match.name)
+        )
 
+    def verbose_output(self):
         return self.textwrapper.fill(
             "{0}:{1}: '{2}' looks like a typo. It was not found in any "
             "macros or any enums. If this is not a typo, put "