This file is indexed.

/usr/lib/python3/dist-packages/libcpychecker/diagnostics.py is in gcc-python3-plugin 0.15-4.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
#   Copyright 2011, 2012 David Malcolm <dmalcolm@redhat.com>
#   Copyright 2011, 2012 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/>.

"""
Error reporting interface, supporting regular GCC messages plus higher-level
HTML visualizations, with de-duplication.

GCC diagnostic messages are buffered up within Report instances and eventually
flushed, allowing us to de-duplicate error reports.
"""

import gcc
from gccutils import get_src_for_loc, check_isinstance
from libcpychecker.visualizations import HtmlRenderer
from libcpychecker.utils import log

class Annotator:
    """
    A collection of hooks for use when describing a trace (either as text,
    or as an HTML report)

    This allows us to add the annotations to the correct place in the textual
    stream when reporting on flow through a function
    """
    def get_notes(self, transition):
        """
        Return a list of Note instances giving extra information about the
        transition
        """
        raise NotImplementedError

class TestAnnotator(Annotator):
    """
    A sample annotator that adds information to the trace on movement between
    gimple statements
    """
    def get_notes(self, transition):
        result = []
        srcloc = transition.src.get_gcc_loc_or_none()
        if srcloc:
            if transition.src.loc != transition.dest.loc:
                result.append(Note(srcloc,
                                   ('transition from "%s" to "%s"'
                                    % (transition.src.loc.get_stmt(),
                                       transition.dest.loc.get_stmt()))))
        return result

def describe_trace(trace, report, annotator):
    """
    Buffer up more details about the path through the function that
    leads to the error, using report.add_inform()
    """
    awaiting_target = None
    for t in trace.transitions:
        log('transition: %s', t)
        srcloc = t.src.get_gcc_loc_or_none()
        if t.desc:
            if srcloc:
                report.add_inform(t.src.get_gcc_loc(report.fun),
                                  ('%s at: %s'
                                   % (t.desc, get_src_for_loc(srcloc))))
            else:
                report.add_inform(t.src.get_gcc_loc(report.fun),
                                  '%s' % t.desc)

            if t.src.stmtnode.bb != t.dest.stmtnode.bb:
                # Tell the user where conditionals reach:
                destloc = t.dest.get_gcc_loc_or_none()
                if destloc:
                    report.add_inform(destloc,
                                      'reaching: %s' % get_src_for_loc(destloc))

        if annotator:
            notes = annotator.get_notes(t)
            for note in notes:
                if note.loc and note.loc == srcloc:
                    report.add_inform(note.loc, note.msg)

class Reporter:
    """
    Error-reporting interface.  Gathers information, sending it to GCC's
    regular diagnostic interface, but also storing it for e.g. HTML dumps

    Error reports can be de-duplicated by finding sufficiently similar Report
    instances, and only fully flushing one of them within each equivalence
    class
    """
    def __init__(self):
        self.reports = []
        self._got_warnings = False

    def make_warning(self, fun, loc, msg):
        assert isinstance(fun, gcc.Function)
        assert isinstance(loc, gcc.Location)

        self._got_warnings = True

        w = Report(fun, loc, msg)
        self.reports.append(w)

        w.add_warning(loc, msg)

        return w

    def make_debug_dump(self, fun, loc, msg):
        assert isinstance(fun, gcc.Function)
        assert isinstance(loc, gcc.Location)
        r = Report(fun, loc, msg)
        self.reports.append(r)
        return r

    def got_warnings(self):
        return self._got_warnings

    def to_json(self, fun):
        result = dict(filename=fun.start.file,
                      function=dict(name=fun.decl.name,
                                    # line number range:
                                    lines=(fun.decl.location.line - 1,
                                           fun.end.line + 1)),
                      reports=[])
        for report in self.reports:
            result['reports'].append(report.to_json(fun))
        return result

    def dump_json(self, fun, filename):
        js = self.to_json(fun)
        from json import dump, dumps
        with open(filename, 'w') as f:
            dump(js, f, sort_keys=True, indent=4)
        if 0:
            print(dumps(js, sort_keys=True, indent=4))

    def to_html(self, fun):
        # (FIXME: eliminate self.fun from HtmlRenderer and the above arg)
        r = HtmlRenderer(fun)
        html = r.make_header()
        for report in self.reports:
            html += r.make_report(report)
        html += r.make_footer()
        return html

    def dump_html(self, fun, filename):
        html = self.to_html(fun)
        with open(filename, 'w') as f:
            f.write(html)

    def remove_duplicates(self):
        """
        Try to organize Report instances into equivalence classes, and only
        keep the first Report within each class
        """
        for report in self.reports[:]:
            # The report might have been removed during the iteration:
            if report.is_duplicate:
                continue
            for candidate in self.reports[:]:
                if report != candidate and not report.is_duplicate:
                    if candidate.is_duplicate_of(report):
                        report.add_duplicate(candidate)
                        self.reports.remove(candidate)

        # Add a note to each report that survived about any duplicates:
        for report in self.reports:
            if report.duplicates:
                report.add_note(report.loc,
                                ('found %i similar trace(s) to this'
                                 % len(report.duplicates)))

    def flush(self):
        for r in self.reports:
            r.flush()

class SavedDiagnostic:
    """
    A saved GCC diagnostic, which we can choose to emit or suppress at a later
    date
    """
    def __init__(self, loc, msg):
        assert isinstance(loc, gcc.Location)
        assert isinstance(msg, str)
        self.loc = loc
        self.msg = msg

class SavedWarning(SavedDiagnostic):
    def flush(self):
        gcc.warning(self.loc, self.msg)

class SavedInform(SavedDiagnostic):
    def flush(self):
        gcc.inform(self.loc, self.msg)

class Report:
    """
    Data about a particular bug found by the checker
    """
    def __init__(self, fun, loc, msg):
        self.fun = fun
        self.loc = loc
        self.msg = msg
        self.trace = None
        self._annotators = {}
        self.notes = []
        self._saved_diagnostics = [] # list of SavedDiagnostic

        # De-duplication handling:
        self.is_duplicate = False
        self.duplicates = [] # list of Report

    def add_warning(self, loc, msg):
        # Add a gcc.warning() to the buffer of GCC diagnostics
        self._saved_diagnostics.append(SavedWarning(loc, msg))

    def add_inform(self, loc, msg):
        # Add a gcc.inform() to the buffer of GCC diagnostics
        self._saved_diagnostics.append(SavedInform(loc, msg))

    def flush(self):
        # Flush the buffer of GCC diagnostics
        for d in self._saved_diagnostics:
            d.flush()

    def add_trace(self, trace, annotator=None):
        self.trace = trace
        self._annotators[trace] = annotator
        describe_trace(trace, self, annotator)

    def add_note(self, loc, msg):
        """
        Add a note at the given location.  This is added both to
        the buffer of GCC diagnostics, and also to a saved list that's
        available to the HTML renderer.
        """
        self.add_inform(loc, msg)
        note = Note(loc, msg)
        self.notes.append(note)
        return note

    def get_annotator_for_trace(self, trace):
        return self._annotators.get(trace)

    def is_duplicate_of(self, other):
        check_isinstance(other, Report)

        # Simplistic equivalence classes for now:
        # the same function, source location, and message; everything
        # else can be different
        if self.fun != other.fun:
            return False
        if self.loc != other.loc:
            return False
        if self.msg != other.msg:
            return False

        return True

    def add_duplicate(self, other):
        assert not self.is_duplicate
        self.duplicates.append(other)
        other.is_duplicate = True

    def to_json(self, fun):
        assert self.trace
        result = dict(message=self.msg,
                      severity='warning', # FIXME
                      states=[])
        # Generate a list of (state, desc) pairs, putting the desc from the
        # transition into source state; the final state will have an empty
        # string
        pairs = []
        for t_iter in self.trace.transitions:
            pairs.append( (t_iter.src, t_iter.desc) )
        pairs.append( (self.trace.transitions[-1].dest, None) )
        for i, (s_iter, desc) in enumerate(pairs):
            result['states'].append(s_iter.as_json(desc))
        result['notes'] = [dict(location=location_as_json(note.loc),
                                message=note.msg)
                           for note in self.notes]
        return result


class Note:
    """
    A note within a self
    """
    def __init__(self, loc, msg):
        self.loc = loc
        self.msg = msg


def location_as_json(loc):
    if loc:
        return (dict(line=loc.line,
                     column=loc.column),
                dict(line=loc.line,
                     column=loc.column))
    else:
        return None

def type_as_json(t):
    if t:
        return str(t)
    else:
        return None