[hawkmoth] Re: [PATCH v2 2/3] hawkmoth: add support for propagating diagnostics

  • From: Jani Nikula <jani@xxxxxxxxxx>
  • To: Bruno Santos <brunomanuelsantos@xxxxxxxxxxxxxxxxxx>, hawkmoth mailing list <hawkmoth@xxxxxxxxxxxxx>
  • Date: Mon, 06 May 2019 23:02:55 +0300

On Wed, 01 May 2019, Bruno Santos <brunomanuelsantos@xxxxxxxxxxxxxxxxxx> wrote:

At the same time, the first diagnostics are implemented or rather
forwarded from libclang parse.

Original concept: Marius Vlad
---
 hawkmoth/__init__.py  | 22 ++++++++++++++++++++--
 hawkmoth/__main__.py  |  6 +++++-
 hawkmoth/parser.py    | 27 ++++++++++++++++++++++++++-
 test/test_hawkmoth.py |  2 +-
 4 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/hawkmoth/__init__.py b/hawkmoth/__init__.py
index 3355922..0896453 100644
--- a/hawkmoth/__init__.py
+++ b/hawkmoth/__init__.py
@@ -21,7 +21,7 @@
 from sphinx.util.docutils import switch_source_input
 from sphinx.util import logging
 
-from hawkmoth.parser import parse
+from hawkmoth.parser import parse, ErrorLevel
 
 with open(os.path.join(os.path.abspath(os.path.dirname(__file__)),
                        'VERSION')) as version_file:
@@ -42,13 +42,31 @@ class CAutoDocDirective(Directive):
     }
     has_content = False
 
+    # Map verbosity levels to logger levels.
+    _log_lvl = {ErrorLevel.ERROR: logging.LEVEL_NAMES['ERROR'],
+                ErrorLevel.WARNING: logging.LEVEL_NAMES['WARNING'],
+                ErrorLevel.INFO: logging.LEVEL_NAMES['INFO'],
+                ErrorLevel.DEBUG: logging.LEVEL_NAMES['DEBUG']}
+
+    def __display_parser_diagnostics(self, errors):
+        env = self.state.document.settings.env
+
+        for (severity, filename, lineno, msg) in errors:
+            toprint = '{}:{}: {}'.format(filename, lineno, msg)
+
+            if severity.value <= env.app.verbosity:
+                self.logger.log(self._log_lvl[severity], toprint,
+                                location=(env.docname, self.lineno))
+
     def __parse(self, viewlist, filename):
         env = self.state.document.settings.env
 
         compat = self.options.get('compat', env.config.cautodoc_compat)
         clang = self.options.get('clang', env.config.cautodoc_clang)
 
-        comments = parse(filename, compat=compat, clang=clang)
+        comments, errors = parse(filename, compat=compat, clang=clang)
+
+        self.__display_parser_diagnostics(errors)
 
         for (comment, meta) in comments:
             lineoffset = meta['line'] - 1
diff --git a/hawkmoth/__main__.py b/hawkmoth/__main__.py
index 1271772..1efb267 100644
--- a/hawkmoth/__main__.py
+++ b/hawkmoth/__main__.py
@@ -31,11 +31,15 @@ def main():
                         help='Verbose output.')
     args = parser.parse_args()
 
-    docs = parse(args.file, compat=args.compat, clang=args.clang)
+    docs, errors = parse(args.file, compat=args.compat, clang=args.clang)
 
     for (doc, meta) in docs:
         if args.verbose:
             print('# {}'.format(meta))
         print(doc)
 
+    for (severity, filename, lineno, msg) in errors:
+        print('{}: {}:{}: {}\n'.format(severity.name,
+                                       filename, lineno, msg), 
file=sys.stderr)

I removed the extra \n here while applying.

There's perhaps some room for bikeshedding in the way the messages are
highlighted in the Sphinx output... but it's much easier to tweak that
when we have the code in.

I also noticed using multiple -v for Sphinx floods the logs with so much
stuff that any extra diagnostics from Clang get drowned. Again, easier
to tweak as a follow-up if it bugs someone.

Thanks for doing this.

BR,
Jani.




+
 main()
diff --git a/hawkmoth/parser.py b/hawkmoth/parser.py
index ecb0d24..60002bc 100644
--- a/hawkmoth/parser.py
+++ b/hawkmoth/parser.py
@@ -33,6 +33,7 @@
 Otherwise, documentation comments are passed through verbatim.
 """
 
+import enum
 import itertools
 import sys
 
@@ -43,6 +44,16 @@
 
 from hawkmoth.util import docstr, doccompat
 
+class ErrorLevel(enum.Enum):
+    """
+    Supported error levels in inverse numerical order of severity. The values
+    are chosen so that they map directly to a 'verbosity level'.
+    """
+    ERROR = 0
+    WARNING = 1
+    INFO = 2
+    DEBUG = 3
+
 def comment_extract(tu):
 
     # FIXME: How to handle top level comments above a cursor that it does 
*not*
@@ -239,10 +250,22 @@ def _recursive_parse(comments, cursor, nest, compat):
 
     return [(doc, meta)]
 
+def clang_diagnostics(errors, diagnostics):
+    sev = {0: ErrorLevel.DEBUG,
+           1: ErrorLevel.DEBUG,
+           2: ErrorLevel.WARNING,
+           3: ErrorLevel.ERROR,
+           4: ErrorLevel.ERROR}
+
+    for diag in diagnostics:
+        errors.extend([(sev[diag.severity], diag.location.file.name,
+                        diag.location.line, diag.spelling)])
+
 # return a list of (comment, metadata) tuples
 # options - dictionary with directive options
 def parse(filename, **options):
 
+    errors = []
     args = options.get('clang')
     if args is not None:
         args = [s.strip() for s in args.split(',') if len(s.strip()) > 0]
@@ -255,6 +278,8 @@ def parse(filename, **options):
                      TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD |
                      TranslationUnit.PARSE_SKIP_FUNCTION_BODIES)
 
+    clang_diagnostics(errors, tu.diagnostics)
+
     top_level_comments, comments = comment_extract(tu)
 
     result = []
@@ -270,4 +295,4 @@ def parse(filename, **options):
     # Sort all elements by order of appearance.
     result.sort(key=lambda r: r[1]['line'])
 
-    return result
+    return result, errors
diff --git a/test/test_hawkmoth.py b/test/test_hawkmoth.py
index 07c2c14..aa68a9c 100755
--- a/test/test_hawkmoth.py
+++ b/test/test_hawkmoth.py
@@ -11,7 +11,7 @@
 def _get_output(input_filename, **options):
     docs_str = ''
 
-    docs = parse(input_filename, **options)
+    docs, errors = parse(input_filename, **options)
 
     for (doc, meta) in docs:
         docs_str += doc + '\n'
-- 
2.21.0

Other related posts: