File: lint_util.py

package info (click to toggle)
cmake-format 0.6.13-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,436 kB
  • sloc: python: 16,990; makefile: 14
file content (174 lines) | stat: -rw-r--r-- 5,366 bytes parent folder | download | duplicates (4)
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
from __future__ import unicode_literals
import collections
import logging

from cmakelang.lint import lintdb

logger = logging.getLogger(__name__)


class LintRecord(object):
  """Records an instance of lint at a particular location
  """

  def __init__(self, spec, location, msg):
    self.spec = spec
    self.location = location
    self.msg = msg

  def __repr__(self):
    if self.location is None:
      return " [{:s}] {:s}".format(self.spec.idstr, self.msg)

    if isinstance(self.location, tuple):
      return "{:s}: [{:s}] {:s}".format(
          ",".join("{:02d}".format(val) for val in self.location[:2]),
          self.spec.idstr, self.msg)

    raise ValueError(
        "Unexpected type {} for location".format(type(self.location)))


SuppressionEvent = collections.namedtuple(
    "SuppresionEvent", ["lineno", "mode", "suppressions"])


class FileContext(object):
  def __init__(self, global_ctx, infile_path):
    self.global_ctx = global_ctx
    self.infile_path = infile_path
    self.config = None
    self._lint = []

    # Suppressions active at the current depth
    self._suppressions = set()
    self._supressed_count = collections.defaultdict(int)

    # Map of line number to list of active suppressions starting at this line
    # and ending at the line number of the next entry in the list
    self._suppression_events = []

  def is_idstr(self, idstr):
    return idstr in self.global_ctx.lintdb

  def suppress(self, lineno, idlist):
    """
    Given a list of lint ids, enable a suppression for each one which is not
    already supressed. Return the list of new suppressions
    """
    new_suppressions = []
    for idstr in idlist:
      if idstr in self._suppressions:
        continue
      self._suppressions.add(idstr)
      new_suppressions.append(idstr)

    self._suppression_events.append(
        SuppressionEvent(lineno, "add", list(new_suppressions)))
    return new_suppressions

  def unsuppress(self, lineno, idlist):
    for idstr in idlist:
      if idstr not in self._suppressions:
        logger.warning(
            "Unsupressing %s which is not currently surpressed", idstr)
      self._suppressions.discard(idstr)
    self._suppression_events.append(
        SuppressionEvent(lineno, "remove", list(idlist)))

  def record_lint(self, idstr, *args, **kwargs):
    if idstr in self.config.lint.disabled_codes:
      self._supressed_count[idstr] += 1
      return

    if idstr in self._suppressions:
      self._supressed_count[idstr] += 1
      return

    spec = self.global_ctx.lintdb[idstr]
    location = kwargs.pop("location", ())
    msg = spec.msgfmt.format(*args, **kwargs)
    record = LintRecord(spec, location, msg)
    self._lint.append(record)

  def get_lint(self):
    """Return lint records in sorted order"""
    records = [
        record for _, _, _, record in sorted(
            (record.location, record.spec.idstr, idx, record)
            for idx, record in enumerate(self._lint))]

    # Remove any lint records that were suppressed at the line number where
    # they were recorded
    out = []
    events = list(self._suppression_events)
    active_suppressions = set()

    for record in records:
      if record.location:
        while events and record.location[0] >= events[0].lineno:
          event = events.pop(0)
          if event.mode == "add":
            for idstr in event.suppressions:
              active_suppressions.add(idstr)
          elif event.mode == "remove":
            for idstr in event.suppressions:
              active_suppressions.discard(idstr)
          else:
            raise ValueError("Illegal suppression event {}".format(event.mode))
      if record.spec.idstr in active_suppressions:
        continue
      out.append(record)

    return out

  def writeout(self, outfile):
    for record in self.get_lint():
      outfile.write("{:s}:{}\n".format(self.infile_path, record))

  def has_lint(self):
    return bool(self.get_lint())


class GlobalContext(object):
  category_names = {
      "C": "Convention",
      "E": "Error",
      "R": "Refactor",
      "W": "Warning",
  }

  def __init__(self, outfile):
    self.outfile = outfile
    self.lintdb = lintdb.get_database()
    self.file_ctxs = {}

  def get_file_ctx(self, infile_path, config):
    if infile_path not in self.file_ctxs:
      self.file_ctxs[infile_path] = FileContext(self, infile_path)
    ctx = self.file_ctxs[infile_path]
    ctx.config = config
    return ctx

  def get_category_counts(self):
    lint_counts = {}
    for _, file_ctx in sorted(self.file_ctxs.items()):
      for record in file_ctx.get_lint():
        category_char = record.spec.idstr[0]
        if category_char not in lint_counts:
          lint_counts[category_char] = 0
        lint_counts[category_char] += 1
    return lint_counts

  def write_summary(self, outfile):
    outfile.write("Summary\n=======\n")
    outfile.write("files scanned: {:d}\n".format(len(self.file_ctxs)))
    outfile.write("found lint:\n")

    lint_counts = self.get_category_counts()
    fieldwidth = max(len(name) for _, name in self.category_names.items())
    fmtstr = "  {:>" + str(fieldwidth) + "s}: {:d}\n"
    for (category_char, count) in sorted(lint_counts.items()):
      category_name = self.category_names[category_char]
      outfile.write(fmtstr.format(category_name, count))
    outfile.write("\n")