Avi Drissman | db497b3 | 2022-09-15 19:47:28 | [diff] [blame] | 1 | # Copyright 2012 The Chromium Authors |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """ Lint for IDL """ |
| 6 | |
| 7 | import os |
| 8 | import sys |
| 9 | |
| 10 | from idl_log import ErrOut, InfoOut, WarnOut |
| 11 | from idl_node import IDLAttribute, IDLNode |
| 12 | from idl_ast import IDLAst |
| 13 | from idl_option import GetOption, Option, ParseOptions |
| 14 | from idl_outfile import IDLOutFile |
| 15 | from idl_visitor import IDLVisitor |
| 16 | |
| 17 | |
| 18 | Option('wcomment', 'Disable warning for missing comment.') |
| 19 | Option('wenum', 'Disable warning for missing enum value.') |
| 20 | Option('winline', 'Disable warning for inline blocks.') |
noelallen@google.com | fb94d1b | 2011-08-22 22:18:27 | [diff] [blame] | 21 | Option('wname', 'Disable warning for inconsistent interface name.') |
| 22 | Option('wnone', 'Disable all warnings.') |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 23 | Option('wparam', 'Disable warning for missing [in|out|inout] on param.') |
| 24 | Option('wpass', 'Disable warning for mixed passByValue and returnByValue.') |
| 25 | |
| 26 | # |
| 27 | # IDLLinter |
| 28 | # |
| 29 | # Once the AST is build, we need to resolve the namespace and version |
| 30 | # information. |
| 31 | # |
| 32 | class IDLLinter(IDLVisitor): |
| 33 | def VisitFilter(self, node, data): |
| 34 | __pychecker__ = 'unusednames=node,data' |
| 35 | return not node.IsA('Comment', 'Copyright') |
| 36 | |
| 37 | def Arrive(self, node, errors): |
| 38 | __pychecker__ = 'unusednames=node,errors' |
| 39 | warnings = 0 |
| 40 | if node.IsA('Interface', 'Member', 'Struct', 'Enum', 'EnumItem', 'Typedef'): |
| 41 | comments = node.GetListOf('Comment') |
| 42 | if not comments and not node.GetProperty('wcomment'): |
| 43 | node.Warning('Expecting a comment.') |
| 44 | warnings += 1 |
| 45 | |
noelallen@chromium.org | 9b100596 | 2012-06-17 05:37:15 | [diff] [blame] | 46 | if node.IsA('File'): |
| 47 | labels = node.GetListOf('Label') |
| 48 | interfaces = node.GetListOf('Interface') |
| 49 | if interfaces and not labels: |
| 50 | node.Warning('Expecting a label in a file containing interfaces.') |
| 51 | |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 52 | if node.IsA('Struct', 'Typedef') and not node.GetProperty('wpass'): |
| 53 | if node.GetProperty('passByValue'): |
| 54 | pbv = 'is' |
| 55 | else: |
| 56 | pbv = 'is not' |
| 57 | if node.GetProperty('returnByValue'): |
| 58 | ret = 'is' |
| 59 | else: |
| 60 | ret = 'is not' |
| 61 | if pbv != ret: |
| 62 | node.Warning('%s passByValue but %s returnByValue.' % (pbv, ret)) |
| 63 | warnings += 1 |
| 64 | |
| 65 | if node.IsA('EnumItem'): |
| 66 | if not node.GetProperty('VALUE') and not node.GetProperty('wenum'): |
| 67 | node.Warning('Expecting value for enumeration.') |
| 68 | warnings += 1 |
| 69 | |
| 70 | if node.IsA('Interface'): |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 71 | macro = node.GetProperty('macro') |
noelallen@google.com | fb94d1b | 2011-08-22 22:18:27 | [diff] [blame] | 72 | if macro and not node.GetProperty('wname'): |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 73 | node.Warning('Interface name inconsistent: %s' % macro) |
| 74 | warnings += 1 |
| 75 | |
| 76 | if node.IsA('Inline') and not node.GetProperty('winline'): |
| 77 | inline_type = node.GetProperty('NAME') |
| 78 | node.parent.Warning('Requires an inline %s block.' % inline_type) |
| 79 | warnings += 1 |
| 80 | |
noelallen@google.com | fb94d1b | 2011-08-22 22:18:27 | [diff] [blame] | 81 | if node.IsA('Callspec') and not node.GetProperty('wparam'): |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 82 | out = False |
| 83 | for arg in node.GetListOf('Param'): |
| 84 | if arg.GetProperty('out'): |
| 85 | out = True |
| 86 | if arg.GetProperty('in') and out: |
| 87 | arg.Warning('[in] parameter after [out] parameter') |
| 88 | warnings += 1 |
| 89 | |
| 90 | if node.IsA('Param') and not node.GetProperty('wparam'): |
| 91 | found = False; |
| 92 | for form in ['in', 'inout', 'out']: |
| 93 | if node.GetProperty(form): found = True |
| 94 | if not found: |
| 95 | node.Warning('Missing argument type: [in|out|inout]') |
| 96 | warnings += 1 |
| 97 | |
| 98 | return warnings |
| 99 | |
| 100 | def Depart(self, node, warnings, childdata): |
| 101 | __pychecker__ = 'unusednames=node' |
| 102 | for child in childdata: |
| 103 | warnings += child |
| 104 | return warnings |
| 105 | |
| 106 | def Lint(ast): |
noelallen@google.com | fb94d1b | 2011-08-22 22:18:27 | [diff] [blame] | 107 | options = ['wcomment', 'wenum', 'winline', 'wparam', 'wpass', 'wname'] |
| 108 | wnone = GetOption('wnone') |
| 109 | for opt in options: |
| 110 | if wnone or GetOption(opt): ast.SetProperty(opt, True) |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 111 | |
| 112 | skipList = [] |
| 113 | for filenode in ast.GetListOf('File'): |
| 114 | name = filenode.GetProperty('NAME') |
| 115 | if filenode.GetProperty('ERRORS') > 0: |
noelallen@google.com | d6afb58 | 2011-07-27 17:03:52 | [diff] [blame] | 116 | ErrOut.Log('%s : Skipped due to errors.' % name) |
noelallen@google.com | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 117 | skipList.append(filenode) |
| 118 | continue |
| 119 | warnings = IDLLinter().Visit(filenode, 0) |
| 120 | if warnings: |
| 121 | WarnOut.Log('%s warning(s) for %s\n' % (warnings, name)) |
| 122 | return skipList |