adlint-postfilter 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ # Message control annotations.
2
+ #
3
+ # Author:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
4
+ # Copyright:: Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
5
+ # License:: GPLv3+: GNU General Public License version 3 or later
6
+ #
7
+ # Owner:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
8
+
9
+ #--
10
+ # ___ ____ __ ___ _________
11
+ # / | / _ |/ / / / | / /__ __/ Source Code Static Analyzer
12
+ # / /| | / / / / / / / |/ / / / AdLint - Advanced Lint
13
+ # / __ |/ /_/ / /___/ / /| / / /
14
+ # /_/ |_|_____/_____/_/_/ |_/ /_/ Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
15
+ #
16
+ # This file is part of adlint-postfilter.
17
+ #
18
+ # adlint-postfilter is free software: you can redistribute it and/or modify it
19
+ # under the terms of the GNU General Public License as published by the Free
20
+ # Software Foundation, either version 3 of the License, or (at your option) any
21
+ # later version.
22
+ #
23
+ # adlint-postfilter is distributed in the hope that it will be useful, but
24
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
26
+ # more details.
27
+ #
28
+ # You should have received a copy of the GNU General Public License along with
29
+ # adlint-postfilter. If not, see <http://www.gnu.org/licenses/>.
30
+ #
31
+ #++
32
+
33
+ require "adlint/postfilter/suppress"
34
+
35
+ module AdLint #:nodoc:
36
+ module Postfilter #:nodoc:
37
+
38
+ class MessageControlAnnotation
39
+ def initialize(message_controller_str)
40
+ @message_controller = MessageController.new(message_controller_str)
41
+ end
42
+
43
+ attr_reader :message_controller
44
+ end
45
+
46
+ class TranslationUnitWideAnnotation < MessageControlAnnotation
47
+ def self.detect(str, composing_fpaths)
48
+ str =~ /ADLINT:TUNIT:\[.*\]/ ? new($&, composing_fpaths) : nil
49
+ end
50
+
51
+ def initialize(message_controller_str, composing_fpaths)
52
+ super(message_controller_str)
53
+ @composing_fpaths = composing_fpaths
54
+ end
55
+ private_class_method :new
56
+
57
+ def generate_suppressions
58
+ message_controller.message_control_list.map { |mesg_ctrl|
59
+ @composing_fpaths.map do |fpath|
60
+ FileWiseSuppression.new(mesg_ctrl.message_id, fpath,
61
+ mesg_ctrl.suppression_activation?)
62
+ end
63
+ }.flatten
64
+ end
65
+ end
66
+
67
+ class LineWiseAnnotation < MessageControlAnnotation
68
+ def self.detect(str, fpath, line_no)
69
+ str =~ /(?:ADLINT:LINE:|ADLINT::)\[.*\]/ ? new($&, fpath, line_no) : nil
70
+ end
71
+
72
+ def initialize(message_controller_str, fpath, line_no)
73
+ super(message_controller_str)
74
+ @fpath, @line_no = fpath, line_no
75
+ end
76
+ private_class_method :new
77
+
78
+ def generate_suppressions
79
+ message_controller.message_control_list.map do |mesg_ctrl|
80
+ LineWiseSuppression.new(mesg_ctrl.message_id, @fpath, @line_no,
81
+ mesg_ctrl.suppression_activation?)
82
+ end
83
+ end
84
+ end
85
+
86
+ class MessageController
87
+ def initialize(message_controller_str)
88
+ @message_control_list = parse_message_controller(message_controller_str)
89
+ end
90
+
91
+ attr_reader :message_control_list
92
+
93
+ private
94
+ def parse_message_controller(message_controller_str)
95
+ if message_controller_str =~ /\[(.*)\]/
96
+ parse_message_control_list($1)
97
+ else
98
+ []
99
+ end
100
+ end
101
+
102
+ def parse_message_control_list(message_control_list_str)
103
+ message_control_list_str.scan(/([+-])([WC]\d{4})/).map do |op, mesg_id|
104
+ case op
105
+ when "-"
106
+ MessageSuppressionActivation.new(mesg_id)
107
+ when "+"
108
+ MessageSuppressionDeactivation.new(mesg_id)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ class MessageControl
115
+ def initialize(message_id_str)
116
+ @message_id = message_id_str.to_sym
117
+ end
118
+
119
+ attr_reader :message_id
120
+
121
+ def suppression_activation?
122
+ subclass_responsibility
123
+ end
124
+
125
+ def suppression_deactivation?
126
+ !suppression_activation?
127
+ end
128
+ end
129
+
130
+ class MessageSuppressionActivation < MessageControl
131
+ def suppression_activation?
132
+ true
133
+ end
134
+ end
135
+
136
+ class MessageSuppressionDeactivation < MessageControl
137
+ def suppression_activation?
138
+ false
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,184 @@
1
+ # AdLint analysis commands.
2
+ #
3
+ # Author:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
4
+ # Copyright:: Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
5
+ # License:: GPLv3+: GNU General Public License version 3 or later
6
+ #
7
+ # Owner:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
8
+
9
+ #--
10
+ # ___ ____ __ ___ _________
11
+ # / | / _ |/ / / / | / /__ __/ Source Code Static Analyzer
12
+ # / /| | / / / / / / / |/ / / / AdLint - Advanced Lint
13
+ # / __ |/ /_/ / /___/ / /| / / /
14
+ # /_/ |_|_____/_____/_/_/ |_/ /_/ Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
15
+ #
16
+ # This file is part of adlint-postfilter.
17
+ #
18
+ # adlint-postfilter is free software: you can redistribute it and/or modify it
19
+ # under the terms of the GNU General Public License as published by the Free
20
+ # Software Foundation, either version 3 of the License, or (at your option) any
21
+ # later version.
22
+ #
23
+ # adlint-postfilter is distributed in the hope that it will be useful, but
24
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
26
+ # more details.
27
+ #
28
+ # You should have received a copy of the GNU General Public License along with
29
+ # adlint-postfilter. If not, see <http://www.gnu.org/licenses/>.
30
+ #
31
+ #++
32
+
33
+ require "fileutils"
34
+ require "getoptlong"
35
+ require "pathname"
36
+
37
+ require "adlint/postfilter/config"
38
+ require "adlint/postfilter/content"
39
+ require "adlint/postfilter/filter"
40
+
41
+ module AdLint #:nodoc:
42
+ module Postfilter #:nodoc:
43
+
44
+ class AnalysisCommand
45
+ def self.for(command_line, config_fpath, src_vpath = nil)
46
+ case name = command_line.split.first
47
+ when "adlint"
48
+ ADLINT.new(command_line, config_fpath, src_vpath)
49
+ when "adlint_sma"
50
+ ADLINT_SMA.new(command_line, config_fpath, src_vpath)
51
+ when "adlint_cma"
52
+ ADLINT_CMA.new(command_line, config_fpath, src_vpath)
53
+ when "adlint_chk"
54
+ ADLINT_CHK.new(command_line, config_fpath, src_vpath)
55
+ else
56
+ raise "unknown command `#{name}'"
57
+ end
58
+ end
59
+
60
+ def initialize(command_line, config_fpath, src_vpath = nil)
61
+ @command_line = command_line
62
+ @config_fpath = config_fpath
63
+ @src_vpath = src_vpath
64
+
65
+ @traits_fpath, @strip_num, @output_dpath, @src_fpaths =
66
+ parse_adlint_command_line(@command_line, @src_vpath)
67
+ end
68
+
69
+ def execute
70
+ status, stderr_content = exec_adlint_command(@command_line)
71
+ filter_results(create_filters, stderr_content)
72
+ status
73
+ end
74
+
75
+ private
76
+ def exec_adlint_command(command_line)
77
+ system("#{command_line} 2>#{stderr_fpath}")
78
+ return $?, StderrContent.new(File.read(stderr_fpath))
79
+ ensure
80
+ FileUtils.rm_f(stderr_fpath)
81
+ end
82
+
83
+ def filter_results(filters, stderr_content)
84
+ filters.each { |filter| filter.execute(stderr_content) }
85
+ stderr_content.overwrite!
86
+ end
87
+
88
+ def create_filters
89
+ subclass_responsibility
90
+ end
91
+
92
+ def parse_adlint_command_line(command_line, src_vpath = nil)
93
+ parser = GetoptLong.new(
94
+ ["--traits", "-t", GetoptLong::REQUIRED_ARGUMENT],
95
+ ["--list-file", "-l", GetoptLong::REQUIRED_ARGUMENT],
96
+ ["--output-dir", "-o", GetoptLong::REQUIRED_ARGUMENT],
97
+ ["--strip", "-p", GetoptLong::REQUIRED_ARGUMENT],
98
+ ["--verbose", "-v", GetoptLong::NO_ARGUMENT])
99
+ parser.quiet = true
100
+
101
+ begin
102
+ traits_fpath = nil
103
+ output_dpath = nil
104
+ strip_num = src_vpath ? src_vpath.components.size : 0
105
+
106
+ ARGV.replace(command_line.split).shift
107
+
108
+ parser.each_option do |optname, optarg|
109
+ case optname
110
+ when "--traits"
111
+ traits_fpath = Pathname.new(optarg)
112
+ when "--output-dir"
113
+ output_dpath = Pathname.new(optarg)
114
+ when "--strip"
115
+ strip_num = optarg.to_i
116
+ end
117
+ end
118
+ rescue
119
+ # NOTE: If the AdLint's command line is invalid, child analysis command
120
+ # will encounter the same problem and will notice it.
121
+ # So, nothing to be done.
122
+ end
123
+
124
+ src_fpaths = ARGV.map { |str|
125
+ if str.end_with?(".c.met.csv")
126
+ @src_vpath.join(Pathname.new(str.sub(/.met.csv\z/, "")))
127
+ else
128
+ Pathname.new(str)
129
+ end
130
+ }
131
+
132
+ return traits_fpath, strip_num, output_dpath, src_fpaths
133
+ end
134
+
135
+ def create_sma_filters
136
+ create_sma_configs.map { |sma_config| MessageFilter.new(sma_config) }
137
+ end
138
+
139
+ def create_cma_filter
140
+ MessageFilter.new(create_cma_config)
141
+ end
142
+
143
+ def create_sma_configs
144
+ @src_fpaths.map do |src_fpath|
145
+ SmaConfig.new(@config_fpath, @traits_fpath, src_fpath, @strip_num,
146
+ @output_dpath)
147
+ end
148
+ end
149
+
150
+ def create_cma_config
151
+ CmaConfig.new(@config_fpath, @traits_fpath, @strip_num, @output_dpath,
152
+ create_sma_configs)
153
+ end
154
+
155
+ def stderr_fpath
156
+ Pathname.new("./adlint_postfilter.#{$$}.stderr")
157
+ end
158
+ end
159
+
160
+ class ADLINT < AnalysisCommand
161
+ private
162
+ def create_filters
163
+ create_sma_filters + [create_cma_filter]
164
+ end
165
+ end
166
+
167
+ class ADLINT_SMA < AnalysisCommand
168
+ private
169
+ def create_filters
170
+ create_sma_filters
171
+ end
172
+ end
173
+
174
+ class ADLINT_CMA < AnalysisCommand
175
+ private
176
+ def create_filters
177
+ [create_cma_filter]
178
+ end
179
+ end
180
+
181
+ class ADLINT_CHK < ADLINT_SMA; end
182
+
183
+ end
184
+ end
@@ -0,0 +1,266 @@
1
+ # Message suppression configuration.
2
+ #
3
+ # Author:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
4
+ # Copyright:: Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
5
+ # License:: GPLv3+: GNU General Public License version 3 or later
6
+ #
7
+ # Owner:: Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
8
+
9
+ #--
10
+ # ___ ____ __ ___ _________
11
+ # / | / _ |/ / / / | / /__ __/ Source Code Static Analyzer
12
+ # / /| | / / / / / / / |/ / / / AdLint - Advanced Lint
13
+ # / __ |/ /_/ / /___/ / /| / / /
14
+ # /_/ |_|_____/_____/_/_/ |_/ /_/ Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
15
+ #
16
+ # This file is part of adlint-postfilter.
17
+ #
18
+ # adlint-postfilter is free software: you can redistribute it and/or modify it
19
+ # under the terms of the GNU General Public License as published by the Free
20
+ # Software Foundation, either version 3 of the License, or (at your option) any
21
+ # later version.
22
+ #
23
+ # adlint-postfilter is distributed in the hope that it will be useful, but
24
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
26
+ # more details.
27
+ #
28
+ # You should have received a copy of the GNU General Public License along with
29
+ # adlint-postfilter. If not, see <http://www.gnu.org/licenses/>.
30
+ #
31
+ #++
32
+
33
+ require "yaml"
34
+ require "pathname"
35
+
36
+ require "adlint/postfilter/suppress"
37
+ require "adlint/postfilter/annotate"
38
+ require "adlint/postfilter/path"
39
+
40
+ module AdLint #:nodoc:
41
+ module Postfilter #:nodoc:
42
+
43
+ class Config
44
+ def initialize(config_fpath, traits_fpath, strip_num)
45
+ load_postfilter_config(config_fpath)
46
+ load_adlint_traits(traits_fpath)
47
+
48
+ @initial_header_suppression =
49
+ create_adlint_initial_header_suppression
50
+ @platform_header_suppression =
51
+ create_platform_header_supression(strip_num)
52
+ @project_wide_suppressions = create_project_wide_suppressions
53
+ end
54
+
55
+ def individual_suppression_control_enabled?
56
+ @@config_yaml["message_traits"]["enable_individual_suppression_control"]
57
+ end
58
+
59
+ def msg_fpath
60
+ subclass_responsibility
61
+ end
62
+
63
+ def suppression_list
64
+ subclass_responsibility
65
+ end
66
+
67
+ private
68
+ def create_adlint_initial_header_suppression
69
+ AdLintInitialHeaderSuppression.new(
70
+ Pathname.new(adlint_traits.of_compiler.initial_header).realpath,
71
+ Pathname.new(adlint_traits.of_project.initial_header).realpath)
72
+ end
73
+
74
+ def create_platform_header_supression(strip_num)
75
+ root_dpath = Pathname.getwd.realpath
76
+ strip_num.times { root_dpath = root_dpath.parent }
77
+ PlatformHeaderSuppression.new(root_dpath)
78
+ end
79
+
80
+ def create_project_wide_suppressions
81
+ list = @@config_yaml["message_traits"]["suppression_list"] || []
82
+ list.map { |mesg_id| ProjectWideSuppression.new(mesg_id.to_sym) }
83
+ end
84
+
85
+ def adlint_traits
86
+ @@adlint_traits
87
+ end
88
+
89
+ def load_postfilter_config(config_fpath)
90
+ return if defined?(@@config_yaml)
91
+
92
+ # NOTE: Psych::SyntaxError is a subclass of the builtin ::SyntaxError.
93
+ # The rescue clause without error_type is equivalent to
94
+ # `rescue StandardError'. So, the rescue clause without error_type
95
+ # cannot catch Psych::SyntaxError.
96
+ begin
97
+ @@config_yaml = File.open(config_fpath, "r:utf-8") { |io|
98
+ case array_or_stream = YAML.load_stream(io)
99
+ when Array
100
+ # NOTE: YAML.load_stream returns Array in Ruby 1.9.3-preview1.
101
+ array_or_stream.first
102
+ when YAML::Stream
103
+ array_or_stream.documents.first
104
+ end
105
+ }
106
+ rescue SyntaxError, StandardError => ex
107
+ $stderr.puts "#{command}: Failed to read `#{config_fpath}'."
108
+ $stderr.puts
109
+ $stderr.puts "Detailed message is below;"
110
+ $stderr.puts ex.message, ex.backtrace
111
+ $stderr.puts
112
+ exit 11
113
+ end
114
+ end
115
+
116
+ def load_adlint_traits(traits_fpath)
117
+ return if defined?(@@adlint_traits)
118
+
119
+ # NOTE: Psych::SyntaxError is a subclass of the builtin ::SyntaxError.
120
+ # The rescue clause without error_type is equivalent to
121
+ # `rescue StandardError'. So, the rescue clause without error_type
122
+ # cannot catch Psych::SyntaxError.
123
+ begin
124
+ @@adlint_traits = Traits.instance
125
+ @@adlint_traits.read_from(traits_fpath)
126
+ rescue SyntaxError, StandardError
127
+ # NOTE: If the AdLint's traits file is invalid, child analysis command
128
+ # will encounter the same problem and will notice it.
129
+ # So, nothing to be done.
130
+ end
131
+ end
132
+ end
133
+
134
+ class SmaConfig < Config
135
+ include PathUtil
136
+
137
+ def initialize(config_fpath, traits_fpath,
138
+ src_fpath, strip_num, output_dpath = nil)
139
+ super(config_fpath, traits_fpath, strip_num)
140
+
141
+ @msg_fpath = sma_msg_fpath_of(src_fpath, strip_num, output_dpath)
142
+ @suppression_list =
143
+ create_suppression_list(src_fpath, strip_num, output_dpath)
144
+ end
145
+
146
+ attr_reader :msg_fpath
147
+ attr_reader :suppression_list
148
+ attr_reader :tunit_wide_suppressions
149
+ attr_reader :line_wise_suppressions
150
+
151
+ private
152
+ def create_suppression_list(src_fpath, strip_num, output_dpath)
153
+ @tunit_wide_suppressions =
154
+ create_tunit_wide_suppressions(src_fpath, strip_num, output_dpath)
155
+ @line_wise_suppressions =
156
+ create_line_wise_suppressions(src_fpath, strip_num, output_dpath)
157
+
158
+ MessageSuppressionList.new(@initial_header_suppression,
159
+ @platform_header_suppression,
160
+ @project_wide_suppressions,
161
+ @tunit_wide_suppressions,
162
+ @line_wise_suppressions)
163
+ end
164
+
165
+ def create_tunit_wide_suppressions(src_fpath, strip_num, output_dpath)
166
+ return [] unless individual_suppression_control_enabled?
167
+
168
+ i_fpath = i_fpath_of(src_fpath, strip_num, output_dpath)
169
+ if ann = find_effective_tunit_wide_annotation(src_fpath, i_fpath)
170
+ ann.generate_suppressions
171
+ else
172
+ []
173
+ end
174
+ end
175
+
176
+ def create_line_wise_suppressions(src_fpath, strip_num, output_dpath)
177
+ return [] unless individual_suppression_control_enabled?
178
+
179
+ i_fpath = i_fpath_of(src_fpath, strip_num, output_dpath)
180
+ collect_composing_fpaths_in(i_fpath).each_with_object([]) do |fpath, ary|
181
+ collect_line_wise_annotations(fpath).each do |ann, line_no|
182
+ ary.concat(ann.generate_suppressions)
183
+ end
184
+ end
185
+ end
186
+
187
+ def find_effective_tunit_wide_annotation(src_fpath, i_fpath)
188
+ composing_fpaths = collect_composing_fpaths_in(i_fpath)
189
+
190
+ # NOTE: If multiple tunit-wide-annotations are written in the source
191
+ # file, the first one will be treated as effective.
192
+ File.open(src_fpath, "r:binary") do |io|
193
+ io.each_line do |line|
194
+ if ann = TranslationUnitWideAnnotation.detect(line, composing_fpaths)
195
+ return ann
196
+ end
197
+ end
198
+ end
199
+ nil
200
+ end
201
+
202
+ def collect_composing_fpaths_in(i_fpath)
203
+ File.open(i_fpath, "r:binary") do |io|
204
+ io.each_line.with_object([]) { |line, composing_fpaths|
205
+ if line =~ /\A# \d+ "(.*)"/
206
+ composing_fpaths.push(Pathname.new($1).realpath)
207
+ end
208
+ }.uniq
209
+ end
210
+ end
211
+
212
+ def collect_line_wise_annotations(fpath)
213
+ File.open(fpath, "r:binary") do |io|
214
+ io.each_line.with_index.with_object([]) do |(line, index), annotations|
215
+ if ann = LineWiseAnnotation.detect(line, fpath, index + 1)
216
+ annotations.push(ann)
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ class CmaConfig < Config
224
+ include PathUtil
225
+
226
+ def initialize(config_fpath, traits_fpath,
227
+ strip_num, output_dpath, sma_configs)
228
+ super(config_fpath, traits_fpath, strip_num)
229
+
230
+ project_name = adlint_traits.of_project.project_name
231
+ @msg_fpath = cma_msg_fpath_of(project_name, output_dpath)
232
+ @suppression_list = create_suppression_list(sma_configs)
233
+ end
234
+
235
+ attr_reader :msg_fpath
236
+ attr_reader :suppression_list
237
+
238
+ private
239
+ def create_suppression_list(sma_configs)
240
+ tunit_wide_suppressions =
241
+ collect_all_tunit_wide_suppressions(sma_configs)
242
+ line_wise_suppressions = collect_all_line_wise_suppressions(sma_configs)
243
+
244
+ MessageSuppressionList.new(@initial_header_suppression,
245
+ @platform_header_suppression,
246
+ @project_wide_suppressions,
247
+ tunit_wide_suppressions,
248
+ line_wise_suppressions)
249
+ end
250
+
251
+ def collect_all_tunit_wide_suppressions(sma_configs)
252
+ sma_configs.map { |sma_config|
253
+ sma_config.tunit_wide_suppressions
254
+ }.flatten
255
+ end
256
+
257
+ def collect_all_line_wise_suppressions(sma_configs)
258
+ sma_configs.map { |sma_config|
259
+ sma_config.line_wise_suppressions
260
+ }.flatten
261
+ end
262
+ end
263
+
264
+ end
265
+ end
266
+