OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Checks C++ and Objective-C files for illegal includes.""" | |
6 | |
7 import codecs | |
8 import os | |
9 import re | |
10 | |
11 import results | |
12 from rules import Rule, MessageRule | |
13 | |
14 | |
15 class CppChecker(object): | |
16 | |
17 EXTENSIONS = [ | |
18 '.h', | |
19 '.cc', | |
20 '.cpp', | |
21 '.m', | |
22 '.mm', | |
23 ] | |
24 | |
25 # The maximum number of non-include lines we can see before giving up. | |
26 _MAX_UNINTERESTING_LINES = 50 | |
27 | |
28 # The maximum line length, this is to be efficient in the case of very long | |
29 # lines (which can't be #includes). | |
30 _MAX_LINE_LENGTH = 128 | |
31 | |
32 # This regular expression will be used to extract filenames from include | |
33 # statements. | |
34 _EXTRACT_INCLUDE_PATH = re.compile( | |
35 '[ \t]*#[ \t]*(?:include|import)[ \t]+"(.*)"') | |
36 | |
37 def __init__(self, verbose): | |
38 self._verbose = verbose | |
39 | |
40 def CheckLine(self, rules, line, dependee_path, fail_on_temp_allow=False): | |
41 """Checks the given line with the given rule set. | |
42 | |
43 Returns a tuple (is_include, dependency_violation) where | |
44 is_include is True only if the line is an #include or #import | |
45 statement, and dependency_violation is an instance of | |
46 results.DependencyViolation if the line violates a rule, or None | |
47 if it does not. | |
48 """ | |
49 found_item = self._EXTRACT_INCLUDE_PATH.match(line) | |
50 if not found_item: | |
51 return False, None # Not a match | |
52 | |
53 include_path = found_item.group(1) | |
54 | |
55 if '\\' in include_path: | |
56 return True, results.DependencyViolation( | |
57 include_path, | |
58 MessageRule('Include paths may not include backslashes.'), | |
59 rules) | |
60 | |
61 if '/' not in include_path: | |
62 # Don't fail when no directory is specified. We may want to be more | |
63 # strict about this in the future. | |
64 if self._verbose: | |
65 print ' WARNING: include specified with no directory: ' + include_path | |
66 return True, None | |
67 | |
68 rule = rules.RuleApplyingTo(include_path, dependee_path) | |
69 if (rule.allow == Rule.DISALLOW or | |
70 (fail_on_temp_allow and rule.allow == Rule.TEMP_ALLOW)): | |
71 return True, results.DependencyViolation(include_path, rule, rules) | |
72 return True, None | |
73 | |
74 def CheckFile(self, rules, filepath): | |
75 if self._verbose: | |
76 print 'Checking: ' + filepath | |
77 | |
78 dependee_status = results.DependeeStatus(filepath) | |
79 ret_val = '' # We'll collect the error messages in here | |
80 last_include = 0 | |
81 with codecs.open(filepath, encoding='utf-8') as f: | |
82 in_if0 = 0 | |
83 for line_num, line in enumerate(f): | |
84 if line_num - last_include > self._MAX_UNINTERESTING_LINES: | |
85 break | |
86 | |
87 line = line.strip() | |
88 | |
89 # Check to see if we're at / inside an #if 0 block | |
90 if line.startswith('#if 0'): | |
91 in_if0 += 1 | |
92 continue | |
93 if in_if0 > 0: | |
94 if line.startswith('#if'): | |
95 in_if0 += 1 | |
96 elif line.startswith('#endif'): | |
97 in_if0 -= 1 | |
98 continue | |
99 | |
100 is_include, violation = self.CheckLine(rules, line, filepath) | |
101 if is_include: | |
102 last_include = line_num | |
103 if violation: | |
104 dependee_status.AddViolation(violation) | |
105 | |
106 return dependee_status | |
107 | |
108 @staticmethod | |
109 def IsCppFile(file_path): | |
110 """Returns True iff the given path ends in one of the extensions | |
111 handled by this checker. | |
112 """ | |
113 return os.path.splitext(file_path)[1] in CppChecker.EXTENSIONS | |
OLD | NEW |