/[eiffelstudio]/branches/eth/eve/Src/framework/code_analysis/ca_code_analyzer.e
ViewVC logotype

Contents of /branches/eth/eve/Src/framework/code_analysis/ca_code_analyzer.e

Parent Directory Parent Directory | Revision Log Revision Log


Revision 96147 - (show annotations)
Sun Nov 23 21:14:32 2014 UTC (5 years, 1 month ago) by schmisam
File size: 16975 byte(s)
Bugfixes for Undo Button for rule-fixing of code analyzer.
Added fixes for rules 50 and 07.
Added basic classes for rules 75 and 77 (WIP).
Added basic classes for fixes for rules 75 and 77 (WIP).
Adjusted test cases for rules 50 and 07.

1 note
2 description: "THE Code Analyzer."
3 author: "Stefan Zurfluh"
4 date: "$Date$"
5 revision: "$Revision$"
6
7 class
8 CA_CODE_ANALYZER
9
10 inherit
11 SHARED_EIFFEL_PROJECT
12
13 CA_SHARED_NAMES
14
15 create
16 make
17
18 feature {NONE} -- Initialization
19
20 make
21 -- Initialization for `Current'.
22 do
23 create settings.make
24 create rules.make_caseless (100) -- Rule IDs should be case insensitive.
25
26 -- Adding the rules.
27 add_rule (create {CA_SELF_ASSIGNMENT_RULE}.make)
28 add_rule (create {CA_UNUSED_ARGUMENT_RULE}.make)
29 add_rule (create {CA_NPATH_RULE}.make (settings.preference_manager))
30 add_rule (create {CA_EMPTY_IF_RULE}.make)
31 add_rule (create {CA_FEATURE_NEVER_CALLED_RULE}.make)
32 add_rule (create {CA_CQ_SEPARATION_RULE}.make)
33 add_rule (create {CA_UNNEEDED_OT_LOCAL_RULE}.make)
34 add_rule (create {CA_UNNEEDED_OBJECT_TEST_RULE}.make)
35 add_rule (create {CA_NESTED_COMPLEXITY_RULE}.make (settings.preference_manager))
36 add_rule (create {CA_MANY_ARGUMENTS_RULE}.make (settings.preference_manager))
37 add_rule (create {CA_CREATION_PROC_EXPORTED_RULE}.make)
38 add_rule (create {CA_VARIABLE_NOT_READ_RULE}.make)
39 add_rule (create {CA_SEMICOLON_ARGUMENTS_RULE}.make)
40 add_rule (create {CA_VERY_LONG_ROUTINE_RULE}.make (settings.preference_manager))
41 add_rule (create {CA_VERY_BIG_CLASS_RULE}.make (settings.preference_manager))
42 add_rule (create {CA_FEATURE_SECTION_COMMENT_RULE}.make)
43 add_rule (create {CA_FEATURE_NOT_COMMENTED_RULE}.make)
44 add_rule (create {CA_BOOLEAN_RESULT_RULE}.make)
45 add_rule (create {CA_BOOLEAN_COMPARISON_RULE}.make)
46 add_rule (create {CA_VERY_SHORT_IDENTIFIER_RULE}.make (settings.preference_manager))
47 add_rule (create {CA_VERY_LONG_IDENTIFIER_RULE}.make (settings.preference_manager))
48 add_rule (create {CA_MISSING_IS_EQUAL_RULE}.make)
49 add_rule (create {CA_SIMPLIFIABLE_BOOLEAN_RULE}.make)
50 add_rule (create {CA_SELF_COMPARISON_RULE}.make)
51 add_rule (create {CA_TODO_RULE}.make)
52 add_rule (create {CA_WRONG_LOOP_ITERATION_RULE}.make)
53 add_rule (create {CA_INSPECT_INSTRUCTIONS_RULE}.make (settings.preference_manager))
54 add_rule (create {CA_ATTRIBUTE_TO_LOCAL_RULE}.make)
55 add_rule (create {CA_EMPTY_EFFECTIVE_ROUTINE_RULE}.make)
56 add_rule (create {CA_IF_ELSE_NOT_EQUAL_RULE}.make)
57 add_rule (create {CA_SHORT_CIRCUIT_IF_RULE}.make)
58 add_rule (create {CA_ITERABLE_LOOP_RULE}.make)
59 add_rule (create {CA_COUNT_EQUALS_ZERO_RULE}.make)
60 add_rule (create {CA_DEEPLY_NESTED_IF_RULE}.make (settings.preference_manager))
61 add_rule (create {CA_UNNEEDED_HELPER_VARIABLE_RULE}.make (settings.preference_manager))
62 add_rule (create {CA_UNNEEDED_PARENTHESES_RULE}.make)
63 add_rule (create {CA_CLASS_NAMING_CONVENTION_RULE}.make)
64 add_rule (create {CA_FEATURE_NAMING_CONVENTION_RULE}.make)
65 add_rule (create {CA_LOCAL_NAMING_CONVENTION_RULE}.make (settings.preference_manager))
66 add_rule (create {CA_ARGUMENT_NAMING_CONVENTION_RULE}.make (settings.preference_manager))
67 add_rule (create {CA_UNNECESSARY_SIGN_OPERATOR_RULE}.make)
68 add_rule (create {CA_EMPTY_UNCOMMENTED_ROUTINE_RULE}.make)
69 add_rule (create {CA_UNNEEDED_ACCESSOR_FUNCTION_RULE}.make)
70 add_rule (create {CA_MERGEABLE_FEATURE_CLAUSES_RULE}.make)
71 add_rule (create {CA_EMPTY_RESCUE_CLAUSE_RULE}.make)
72 add_rule (create {CA_INSPECT_NO_WHEN_RULE}.make)
73 add_rule (create {CA_EXPLICIT_REDUNDANT_INHERITANCE_RULE}.make)
74 add_rule (create {CA_UNDESIRABLE_COMMENT_CONTENT_RULE}.make (settings.preference_manager))
75 add_rule (create {CA_INHERIT_FROM_ANY_RULE}.make)
76 add_rule (create {CA_DOUBLE_NEGATION_RULE}.make)
77 add_rule (create {CA_EMPTY_LOOP_RULE}.make)
78 add_rule (create {CA_MISSING_CREATION_PROC_WITHOUT_ARGS_RULE}.make)
79 add_rule (create {CA_COMMENT_NOT_WELL_PHRASED_RULE}.make)
80 add_rule (create {CA_OBJECT_CREATION_WITHIN_LOOP_RULE}.make)
81 add_rule (create {CA_EMPTY_CREATION_PROC_RULE}.make)
82 add_rule (create {CA_VOID_CHECK_USING_IS_EQUAL_RULE}.make)
83 add_rule (create {CA_COMPARISON_OF_OBJECT_REFS_RULE}.make)
84 add_rule (create {CA_ATTRIBUTE_CAN_BE_CONSTANT_RULE}.make)
85 add_rule (create {CA_LOOP_INVARIANT_COMPUTATION_RULE}.make)
86 add_rule (create {CA_UNREACHABLE_CODE_RULE}.make)
87 add_rule (create {CA_OBJECT_TEST_FAILING_RULE}.make)
88 add_rule (create {CA_USELESS_CONTRACT_RULE}.make)
89 add_rule (create {CA_REAL_NAN_COMPARISON_RULE}.make)
90 add_rule (create {CA_LOCAL_USED_FOR_RESULT_RULE}.make)
91 add_rule (create {CA_MERGEABLE_CONDITIONALS_RULE}.make)
92 add_rule (create {CA_UNUSED_INHERITANCE_RULE}.make_with_defaults)
93 add_rule (create {CA_EXPORTED_FEATURE_NEVER_CALLED_RULE}.make_with_defaults)
94
95 settings.initialize_rule_settings (rules)
96
97 create classes_to_analyze.make
98 create rule_violations.make (100)
99 create completed_actions
100 create output_actions
101
102 create ignoredby.make (25)
103 create checked_only_by.make (25)
104 create library_class.make (25)
105 create nonlibrary_class.make (25)
106 end
107
108 feature -- Analysis interface
109
110 add_completed_action (a_action: attached PROCEDURE [ANY, TUPLE [ITERABLE [TUPLE [detachable EXCEPTION, CLASS_C]]]])
111 -- Adds `a_action' to the list of procedures that will be
112 -- called when analysis has completed.
113 do
114 completed_actions.extend (a_action)
115 end
116
117 add_output_action (a_action: attached PROCEDURE [ANY, TUPLE [READABLE_STRING_GENERAL]])
118 -- Adds `a_action' to the procedures that are called for outputting status. The final results
119 -- (rule violations) are not given to these procedures.
120 do
121 output_actions.extend (a_action)
122 end
123
124 analyze
125 -- Analyze all the classes that have been added.
126 require
127 not is_running
128 local
129 l_rules_checker: CA_ALL_RULES_CHECKER
130 l_task: CA_RULE_CHECKING_TASK
131 l_rules_to_check: LINKED_LIST [CA_RULE]
132 do
133 is_running := True
134
135 create l_rules_checker.make
136 create l_rules_to_check.make
137 across rules as l_rules loop
138 l_rules.item.clear_violations
139 if is_rule_checkable (l_rules.item) then
140 l_rules_to_check.extend (l_rules.item)
141 -- Here we only prepare standard rules. The rule checking task will iterate again
142 -- through the rules and run the analysis on the enabled rules.
143 if attached {CA_STANDARD_RULE} l_rules.item as l_std_rule then
144 l_std_rule.prepare_checking (l_rules_checker)
145 end
146 end
147 end
148
149 create l_task.make (l_rules_checker, l_rules_to_check, classes_to_analyze, agent analysis_completed)
150 l_task.set_output_actions (output_actions)
151 if attached rota as l_rota then
152 rota.run_task (l_task)
153 else
154 -- No ROTA task is available, we execute the task synchronously.
155 from
156
157 until
158 not l_task.has_next_step
159 loop
160 l_task.step
161 end
162 end
163 end
164
165 is_rule_checkable (a_rule: attached CA_RULE): BOOLEAN
166 -- Will `a_rule' be checked based on the current preferences and based on the current
167 -- checking scope?
168 do
169 Result := a_rule.is_enabled.value
170 and then (system_wide_check or else (not a_rule.is_system_wide))
171 and then is_severity_enabled (a_rule.severity)
172
173 -- TODO Check if ignoredby or checkonly lists.
174 end
175
176 clear_classes_to_analyze
177 -- Removes all classes that have been added to the list of classes
178 -- to analyze.
179 do
180 classes_to_analyze.wipe_out
181 end
182
183 add_whole_system
184 -- Adds all the classes that are part of the current system. Classes of referenced libraries
185 -- will not be added.
186 do
187 across
188 eiffel_universe.groups as l_groups
189 loop
190 -- Only load top-level clusters, as the others will be loaded recursively afterwards.
191 if attached {CLUSTER_I} l_groups.item as l_cluster and then l_cluster.parent_cluster = Void then
192 add_cluster (l_cluster)
193 end
194 end
195
196 system_wide_check := True
197 end
198
199 add_cluster (a_cluster: attached CLUSTER_I)
200 -- Add all classes of cluster `a_cluster'.
201 do
202 system_wide_check := False
203
204 if a_cluster.classes /= Void then
205 across a_cluster.classes as ic loop
206 add_class (ic.item)
207 end
208 end
209
210 if a_cluster.sub_clusters /= Void then
211 across a_cluster.sub_clusters as ic loop
212 add_cluster (ic.item)
213 end
214 end
215 end
216
217 add_group (a_group: attached CONF_GROUP)
218 -- Add all classes of the configuration group `a_group'.
219 require
220 a_group_not_void: a_group /= Void
221 do
222 if a_group.classes /= Void then
223 across a_group.classes as ic loop
224 add_class (ic.item)
225 end
226 end
227 end
228
229 add_classes (a_classes: attached ITERABLE [attached CONF_CLASS])
230 -- Add the classes `a_classes'.
231 do
232 system_wide_check := False
233
234 across a_classes as l_classes loop
235 add_class (l_classes.item)
236 end
237 end
238
239 add_class (a_class: attached CONF_CLASS)
240 -- Adds class `a_class'.
241 do
242 system_wide_check := False
243
244 if attached {EIFFEL_CLASS_I} a_class as l_eiffel_class
245 and then attached l_eiffel_class.compiled_class as l_compiled
246 then
247 classes_to_analyze.extend (l_compiled)
248
249 extract_indexes (l_compiled)
250 else
251 output_actions.call ([ca_messages.class_skipped (a_class.name)])
252 end
253 end
254
255
256 force_preferences (a_preferences: LIST [TUPLE [rule_id: READABLE_STRING_GENERAL; preference_name: STRING; preference_value: READABLE_STRING_GENERAL]])
257 -- Forcefully set the preferences in `a_preferences' to the specified values,
258 -- overwriting the current ones.
259 local
260 l_full_preference_name: STRING
261 l_preference: PREFERENCE
262 do
263 -- Disable all rules.
264 across rules as ic loop
265 ic.item.is_enabled.set_value (False)
266 end
267
268 across a_preferences as ic loop
269 if rules.has_key (ic.item.rule_id) then
270 l_full_preference_name := rules.found_item.full_preference_name (ic.item.preference_name)
271 l_preference := preferences.get_preference (l_full_preference_name)
272
273 if attached l_preference then
274 l_preference.set_value_from_string (ic.item.preference_value)
275 else
276 output_actions.call ([ca_messages.preference_not_found (l_full_preference_name)])
277 end
278
279 else
280 output_actions.call ([ca_messages.rule_not_found (ic.item.rule_id)])
281 end
282 end
283 end
284
285
286 feature -- Properties
287
288 is_running: BOOLEAN
289 -- Is code analysis running?
290
291 rules: STRING_TABLE [CA_RULE]
292 -- Table containing the rules that will be used for analysis. Rules are indexed by ID.
293
294 add_rule (a_rule: CA_RULE)
295 -- Adds `a_rule' to the rules list.
296 require
297 not_added_yet: not rules.has (a_rule.id)
298 do
299 rules.extend (a_rule, a_rule.id)
300 ensure
301 added: rules.has (a_rule.id)
302 end
303
304 rule_violations: detachable HASH_TABLE [SORTED_TWO_WAY_LIST [CA_RULE_VIOLATION], CLASS_C]
305 -- All found violations from the last analysis.
306
307 settings: CA_SETTINGS
308 -- The settings manager for Code Analysis.
309
310 preferences: PREFERENCES
311 -- Code Analysis preferences.
312 do Result := settings.preferences end
313
314 class_list: ITERABLE [CLASS_C]
315 -- List of classes that have been added.
316 do Result := classes_to_analyze end
317
318 feature {NONE} -- Implementation
319
320 csv_file_name: STRING = "last_analysis_result.csv"
321
322 csv_header: STRING = "Severity;Class;Location;Title;Description;Rule ID;Severity Score"
323
324 analysis_completed (a_exceptions: detachable ITERABLE [TUPLE [detachable EXCEPTION, CLASS_C]])
325 -- Will be called when the analysis task has finished. `a_exceptions'
326 -- contains a list of exception that occurred during analysis.
327 local
328 l_csv_writer: CA_CSV_WRITER
329 do
330 create l_csv_writer.make (eiffel_project.project_location.target_path.extended (csv_file_name), csv_header)
331
332 across rules as l_rules loop
333 across l_rules.item.violations as l_v loop
334 -- Check the ignore list.
335 if is_violation_valid (l_v.item) then
336 -- Make sure a list for this class exists in the hash table:
337 rule_violations.put (create {SORTED_TWO_WAY_LIST [CA_RULE_VIOLATION]}.make, l_v.item.affected_class)
338 -- Add the violation.
339 rule_violations.at (l_v.item.affected_class).extend (l_v.item)
340 -- Log it.
341 l_csv_writer.add_line (l_v.item.csv_line)
342 end
343 end
344 end
345
346 l_csv_writer.close_file
347
348 clear_classes_to_analyze
349
350 is_running := False
351 completed_actions.call ([a_exceptions])
352 completed_actions.wipe_out
353 end
354
355 is_violation_valid (a_viol: attached CA_RULE_VIOLATION): BOOLEAN
356 -- Is the violation `a_viol' valid under the current settings
357 -- such as the rule ignore list of a class, or the library or
358 -- non-library status of a class?
359 local
360 l_affected_class: CLASS_C
361 l_rule: CA_RULE
362 do
363 l_affected_class := a_viol.affected_class
364 l_rule := a_viol.rule
365
366 Result := True
367
368 if checked_only_by.at(l_affected_class).is_empty then
369 if (ignoredby.at (l_affected_class)).has (l_rule.id) then
370 Result := False
371 end
372 elseif not checked_only_by.at (l_affected_class).has (l_rule.id) then
373 Result := False
374 end
375 if (not l_rule.checks_library_classes) and then library_class.at (l_affected_class) then
376 Result := False
377 end
378
379 if (not l_rule.checks_nonlibrary_classes) and then nonlibrary_class.at (l_affected_class) then
380 Result := False
381 end
382 end
383
384 classes_to_analyze: LINKED_SET [CLASS_C]
385 -- List of classes that shall be analyzed.
386
387 system_wide_check: BOOLEAN
388 -- Shall the whole system be analyzed?
389
390 completed_actions: ACTION_SEQUENCE [TUPLE [ITERABLE [TUPLE [detachable EXCEPTION, CLASS_C]]]]
391 -- List of procedures to call when analysis has completed.
392
393 frozen rota: detachable ROTA_S
394 -- Accesses the rota service.
395 local
396 l_service_consumer: SERVICE_CONSUMER [ROTA_S]
397 do
398 create l_service_consumer
399 if attached l_service_consumer.service as l_service and then l_service.is_interface_usable then
400 Result := l_service
401 end
402 end
403
404 is_severity_enabled (a_severity: attached CA_RULE_SEVERITY): BOOLEAN
405 do
406 Result := (attached {CA_HINT} a_severity and settings.are_hints_enabled.value)
407 or else (attached {CA_SUGGESTION} a_severity and settings.are_suggestions_enabled.value)
408 or else (attached {CA_WARNING} a_severity and settings.are_warnings_enabled.value)
409 or else (attached {CA_ERROR} a_severity and settings.are_errors_enabled.value)
410 end
411
412 output_actions: ACTION_SEQUENCE [TUPLE [READABLE_STRING_GENERAL]]
413 -- Will be called whenever there is a message to output.
414
415 feature {NONE} -- Class-wide Options (From Indexing Clauses)
416
417 extract_indexes (a_class: attached CLASS_C)
418 -- Extracts options from the indexing clause of class `a_class'.
419 local
420 l_ast: CLASS_AS
421 l_ignoredby, l_check_only: LINKED_LIST [STRING_32]
422 do
423 create l_ignoredby.make
424 create l_check_only.make
425 -- We want to compare the actual strings.
426 l_ignoredby.compare_objects
427 l_check_only.compare_objects
428 -- Reset the class flags.
429 library_class.force (False, a_class)
430 nonlibrary_class.force (False, a_class)
431 l_ast := a_class.ast
432
433 if attached l_ast.internal_top_indexes as l_top then
434 search_indexing_tags (l_top, a_class, l_ignoredby, l_check_only)
435 end
436 if attached l_ast.internal_bottom_indexes as l_bottom then
437 search_indexing_tags (l_bottom, a_class, l_ignoredby, l_check_only)
438 end
439
440 ignoredby.force (l_ignoredby, a_class)
441 checked_only_by.force (l_check_only, a_class)
442 end
443
444 search_indexing_tags (a_clause: attached INDEXING_CLAUSE_AS; a_class: attached CLASS_C; a_ignoredby, a_check_only: attached LINKED_LIST [STRING_32])
445 -- Searches `a_clause' for settings relevant to code analysis.
446 local
447 l_item: STRING_32
448 do
449 across a_clause as ic loop
450 if attached ic.item.tag as l_tag then
451 if l_tag.name_32.same_string_general ("ca_ignore") then
452 -- Class wants to ignore certain rules.
453 across ic.item.index_list as l_list loop
454 l_item := l_list.item.string_value_32
455 l_item.prune_all ('"')
456 a_ignoredby.extend (l_item)
457 end
458 elseif l_tag.name_32.is_equal ("ca_library") then
459 -- Class has information on whether it is a library class.
460 if not ic.item.index_list.is_empty then
461 l_item := ic.item.index_list.first.string_value_32
462 l_item.to_lower
463 l_item.prune_all ('"')
464 if l_item.is_equal ("true") then
465 library_class.force (True, a_class)
466 elseif l_item.is_equal ("false") then
467 nonlibrary_class.force (True, a_class)
468 end
469 end
470 elseif l_tag.name_32.is_equal ("ca_only") then
471 -- Class wants to check only certain rules.
472 across ic.item.index_list as l_list loop
473 l_item := l_list.item.string_value_32
474 l_item.prune_all ('"')
475 a_check_only.extend (l_item)
476 end
477 end
478 end
479 end
480 end
481
482 ignoredby: HASH_TABLE [LINKED_LIST [STRING_32], CLASS_C]
483 -- Maps classes to lists of rules (rule IDs) the class wants to be ignored by.
484
485 checked_only_by: HASH_TABLE [LINKED_LIST [STRING_32], CLASS_C]
486 -- Maps classes to lists of rules (rule IDs) the class wants to be checked by exclusively.
487
488
489 library_class, nonlibrary_class: HASH_TABLE [BOOLEAN, CLASS_C]
490 -- Stores classes that are marked as library or non-library classes.
491
492 invariant
493 -- law_of_non_contradiction: one class must not be both a library_class and a nonlibrary_class
494
495 end

Properties

Name Value
svn:eol-style native
svn:keywords Author Date ID Revision

  ViewVC Help
Powered by ViewVC 1.1.23