adlint-postfilter 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+