suppress.py 4.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. #
  2. # Metrix++, Copyright 2009-2013, Metrix++ Project
  3. # Link: http://metrixplusplus.sourceforge.net
  4. #
  5. # This file is a part of Metrix++ Tool.
  6. #
  7. # Metrix++ is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, version 3 of the License.
  10. #
  11. # Metrix++ is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Metrix++. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import core.api
  20. import core.export.cout
  21. import re
  22. class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
  23. def declare_configuration(self, parser):
  24. parser.add_option("--std.suppress.code", "--ssc", action="store_true", default=False,
  25. help="If set (True), suppression markers are collected from comments in code. "
  26. "Suppressions are used by post-processing tools, like limit, to remove false-positive warnings. "
  27. "Suppressions should be in the first comment block of a region (function/class/interface). "
  28. "Format of suppressions: 'metrix++: suppress metric-name'. "
  29. "For example: 'metrix++: suppress std.code.complexity:cyclomatic'. "
  30. " [default: %default]")
  31. def configure(self, options):
  32. self.is_active = options.__dict__['std.suppress.code']
  33. def initialize(self):
  34. if self.is_active == True:
  35. # trigger version property set
  36. core.api.Plugin.initialize(self)
  37. namespace = self.get_plugin_loader().get_database_loader().create_namespace(self.get_name(), support_regions = True)
  38. namespace.add_field('count', int, non_zero=True)
  39. namespace.add_field('list', str)
  40. core.api.subscribe_by_parents_interface(core.api.ICode, self, 'callback')
  41. # suppress pattern
  42. pattern = re.compile(r'''metrix[+][+][:][ \t]+suppress[ \t]+([^ \t\r\n\*]+)''')
  43. def callback(self, parent, data, is_updated):
  44. is_updated = is_updated or self.is_updated
  45. if is_updated == True:
  46. text = data.get_content()
  47. for region in data.iterate_regions():
  48. count = 0
  49. list_text = ""
  50. last_comment_end = None
  51. for marker in data.iterate_markers(
  52. filter_group = data.get_marker_types().COMMENT,
  53. region_id = region.get_id(),
  54. exclude_children = True):
  55. if last_comment_end != None and marker.get_offset_begin() > last_comment_end + 2:
  56. # check continues comment blocks
  57. # stop searching, if this comment block is far from the last
  58. break
  59. last_comment_end = marker.get_offset_end()
  60. matches = self.pattern.findall(text, marker.get_offset_begin(), marker.get_offset_end())
  61. for m in matches:
  62. namespace_name, field = m.split(':')
  63. namespace = self.get_plugin_loader().get_database_loader().get_namespace(namespace_name)
  64. if namespace == None or namespace.get_field_packager(field) == None:
  65. core.export.cout.cout(data.get_path(), region.get_cursor(),
  66. core.export.cout.SEVERITY_WARNING,
  67. "Suppressed metric '" + namespace_name + "/" + field +
  68. "' is not being collected",
  69. [("Metric name", namespace_name + "/" + field),
  70. ("Region name", region.get_name())])
  71. continue
  72. count += 1
  73. list_text += "[" + m +"]"
  74. if count > 0:
  75. region.set_data(self.get_name(), 'count', count)
  76. region.set_data(self.get_name(), 'list', list_text)