From 629f3c7d07a981073bb0573e7867ed087c67cf04 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 25 May 2017 14:23:14 -0400
Subject: [PATCH 17/28] FIXME: add example of info handling

---
 checkers/Makefile |  1 +
 checkers/ianal.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/checkers.c    |  7 ++---
 3 files changed, 90 insertions(+), 5 deletions(-)
 create mode 100755 checkers/ianal.py

diff --git a/checkers/Makefile b/checkers/Makefile
index 2a8d399..a5da135 100644
--- a/checkers/Makefile
+++ b/checkers/Makefile
@@ -6,3 +6,4 @@ check-all:
 	python cppcheck.py unittest
 	python flawfinder.py unittest
 	python always_fails.py unittest
+	python ianal.py unittest
diff --git a/checkers/ianal.py b/checkers/ianal.py
new file mode 100755
index 0000000..73081b0
--- /dev/null
+++ b/checkers/ianal.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#   Copyright 2017 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2017 Red Hat, Inc.
+#
+#   This is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see
+#   <http://www.gnu.org/licenses/>.
+
+import re
+import sys
+import tempfile
+import unittest
+
+from gccinvocation import GccInvocation
+
+from checker import Checker, Context, CheckerTests, make_file, \
+    tool_main
+
+from firehose.model import Analysis, Generator, Metadata, Info, \
+    Location, File, Message, Range, Point
+
+class NotALawyer(Checker):
+    """
+    Checker subclass that looks for "Copyright" lines, as a demo
+    of handling "info" results.
+    """
+    def __init__(self, ctxt):
+        Checker.__init__(self, 'not-a-lawyer', ctxt)
+
+    def raw_invoke(self, gccinv, sourcefile):
+        results = []
+        file_ = make_file(sourcefile)
+        with open(sourcefile) as f:
+            for lineidx, line in enumerate(f):
+                m = re.match('.*(Copyright).*', line)
+                if m:
+                    start, end = m.span(1)
+                    # FIXME: use proper range
+                    linenum = lineidx + 1
+                    range_ = Range(start=Point(linenum, start + 1),
+                                   end=Point(linenum, end))
+                    location = Location(file_, None, range_=range_)
+                    info = Info(infoid='copyright-line',
+                                location=location,
+                                message=Message('I am not a lawyer'),
+                                customfields=None)
+                    results.append(info)
+        metadata = Metadata(generator=Generator(self.name), sut=None,
+                            file_=file_, stats=None)
+        analysis = Analysis(metadata, results)
+        return analysis
+
+class NotALawyerTests(CheckerTests):
+    def make_tool(self):
+        ctxt = self.make_ctxt()
+        return NotALawyer(ctxt)
+
+    def verify_basic_metadata(self, analysis, sourcefile):
+        # Verify basic metadata:
+        self.assert_metadata(analysis, 'not-a-lawyer', sourcefile)
+
+    def test_basic(self):
+        analysis = self.invoke('test-sources/cpychecker-demo.c')
+        self.assertEqual(len(analysis.results), 2)
+        r0 = analysis.results[0]
+        self.assertIsInstance(r0, Info)
+        self.assertEqual(r0.infoid, 'copyright-line')
+        self.assertEqual(r0.location.file.givenpath,
+                         'test-sources/cpychecker-demo.c')
+        self.assertEqual(r0.message.text, 'I am not a lawyer')
+        self.assertEqual(r0.location.range_.start.line, 2)
+        self.assertEqual(r0.location.range_.start.column, 4)
+        self.assertEqual(r0.location.range_.end.line, 2)
+        self.assertEqual(r0.location.range_.end.column, 12)
+
+if __name__ == '__main__':
+    sys.exit(tool_main(sys.argv, NotALawyer))
diff --git a/gcc/checkers.c b/gcc/checkers.c
index 7c68dc1..9179778 100644
--- a/gcc/checkers.c
+++ b/gcc/checkers.c
@@ -115,10 +115,8 @@ static void
 handle_info (const firehose::analysis &analysis,
 	     const firehose::info &info)
 {
-  // FIXME: generate "In function", perhaps based on blt_node ?
-  // FIXME:  ^^^ better to get it from the JSON
   diagnostic_info diagnostic;
-  diagnostic.kind = DK_ERROR;
+  diagnostic.kind = DK_NOTE;
   diagnostic.external_tool = analysis.m_metadata.m_generator.m_name;
   diagnostic.external_test_id = info.m_infoid;
   diagnostic_at (info.m_location, &diagnostic, "%s",
@@ -131,8 +129,6 @@ static void
 handle_failure (const firehose::analysis &analysis,
 		const firehose::failure &failure)
 {
-  // FIXME: generate "In function", perhaps based on blt_node ?
-  // FIXME:  ^^^ better to get it from the JSON
   diagnostic_info diagnostic;
   diagnostic.kind = DK_ERROR;
   diagnostic.external_tool = analysis.m_metadata.m_generator.m_name;
@@ -291,4 +287,5 @@ run_analyzers (const char *path)
   run_one_analyzer ("../../src/checkers/cppcheck.py");
   run_one_analyzer ("../../src/checkers/flawfinder.py");
   run_one_analyzer ("../../src/checkers/always_fails.py");
+  run_one_analyzer ("../../src/checkers/ianal.py");
 }
-- 
1.8.5.3

