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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 96147 - (hide 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 jasonw 94983 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 jasonw 95331 create rules.make_caseless (100) -- Rule IDs should be case insensitive.
25    
26 jasonw 94983 -- Adding the rules.
27 jasonw 95331 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 schmisam 95832 add_rule (create {CA_UNNEEDED_OBJECT_TEST_RULE}.make)
35 jasonw 95331 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 schmisam 95832 add_rule (create {CA_ITERABLE_LOOP_RULE}.make)
59     add_rule (create {CA_COUNT_EQUALS_ZERO_RULE}.make)
60 jasonw 95331 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 apaolo 95408 add_rule (create {CA_UNDESIRABLE_COMMENT_CONTENT_RULE}.make (settings.preference_manager))
75     add_rule (create {CA_INHERIT_FROM_ANY_RULE}.make)
76 schmisam 95429 add_rule (create {CA_DOUBLE_NEGATION_RULE}.make)
77 schmisam 95440 add_rule (create {CA_EMPTY_LOOP_RULE}.make)
78 schmisam 95442 add_rule (create {CA_MISSING_CREATION_PROC_WITHOUT_ARGS_RULE}.make)
79 schmisam 95461 add_rule (create {CA_COMMENT_NOT_WELL_PHRASED_RULE}.make)
80 schmisam 95465 add_rule (create {CA_OBJECT_CREATION_WITHIN_LOOP_RULE}.make)
81 schmisam 95466 add_rule (create {CA_EMPTY_CREATION_PROC_RULE}.make)
82 schmisam 95467 add_rule (create {CA_VOID_CHECK_USING_IS_EQUAL_RULE}.make)
83 schmisam 95832 add_rule (create {CA_COMPARISON_OF_OBJECT_REFS_RULE}.make)
84 schmisam 95701 add_rule (create {CA_ATTRIBUTE_CAN_BE_CONSTANT_RULE}.make)
85 schmisam 95757 add_rule (create {CA_LOOP_INVARIANT_COMPUTATION_RULE}.make)
86     add_rule (create {CA_UNREACHABLE_CODE_RULE}.make)
87 schmisam 95832 add_rule (create {CA_OBJECT_TEST_FAILING_RULE}.make)
88 schmisam 95830 add_rule (create {CA_USELESS_CONTRACT_RULE}.make)
89 schmisam 95832 add_rule (create {CA_REAL_NAN_COMPARISON_RULE}.make)
90 schmisam 95855 add_rule (create {CA_LOCAL_USED_FOR_RESULT_RULE}.make)
91     add_rule (create {CA_MERGEABLE_CONDITIONALS_RULE}.make)
92 schmisam 96147 add_rule (create {CA_UNUSED_INHERITANCE_RULE}.make_with_defaults)
93     add_rule (create {CA_EXPORTED_FEATURE_NEVER_CALLED_RULE}.make_with_defaults)
94 jasonw 94983
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 schmisam 96078 create checked_only_by.make (25)
104 jasonw 94983 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 jasonw 95203 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 jasonw 94983 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 schmisam 96147
173     -- TODO Check if ignoredby or checkonly lists.
174 jasonw 94983 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 jasonw 95331
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 jasonw 95252 local
260 jasonw 95331 l_full_preference_name: STRING
261     l_preference: PREFERENCE
262 jasonw 95252 do
263     -- Disable all rules.
264     across rules as ic loop
265     ic.item.is_enabled.set_value (False)
266     end
267    
268 jasonw 95331 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 jasonw 95252
273 jasonw 95331 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 jasonw 95252 else
280 jasonw 95331 output_actions.call ([ca_messages.rule_not_found (ic.item.rule_id)])
281 jasonw 95252 end
282     end
283     end
284    
285 jasonw 95331
286 jasonw 94983 feature -- Properties
287    
288     is_running: BOOLEAN
289     -- Is code analysis running?
290    
291 jasonw 95331 rules: STRING_TABLE [CA_RULE]
292     -- Table containing the rules that will be used for analysis. Rules are indexed by ID.
293 jasonw 94983
294 jasonw 95331 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 jasonw 94983 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 jasonw 95203 create l_csv_writer.make (eiffel_project.project_location.target_path.extended (csv_file_name), csv_header)
331 jasonw 94983
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 schmisam 96078 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 jasonw 94983 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 schmisam 96078 l_ignoredby, l_check_only: LINKED_LIST [STRING_32]
422 jasonw 94983 do
423     create l_ignoredby.make
424 schmisam 96078 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 jasonw 94983 -- 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 schmisam 96078 search_indexing_tags (l_top, a_class, l_ignoredby, l_check_only)
435 jasonw 94983 end
436     if attached l_ast.internal_bottom_indexes as l_bottom then
437 schmisam 96078 search_indexing_tags (l_bottom, a_class, l_ignoredby, l_check_only)
438 jasonw 94983 end
439    
440     ignoredby.force (l_ignoredby, a_class)
441 schmisam 96078 checked_only_by.force (l_check_only, a_class)
442 jasonw 94983 end
443    
444 schmisam 96078 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 jasonw 94983 -- 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 jasonw 95203 if attached ic.item.tag as l_tag then
451 schmisam 96078 if l_tag.name_32.same_string_general ("ca_ignore") then
452 jasonw 95203 -- 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 schmisam 96078 l_item.prune_all ('"')
456 jasonw 95203 a_ignoredby.extend (l_item)
457 jasonw 94983 end
458 jasonw 95203 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 schmisam 96078 l_item.prune_all ('"')
464 jasonw 95203 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 schmisam 96078 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 jasonw 94983 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 schmisam 96078 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 jasonw 94983 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