def _lint_script_part(script_offset, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): def parse_error(offset, msg, msg_args): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): parse_errors.append((offset, msg, msg_args)) def report(node, errname, offset=0, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: if pass_.start_offset > node.start_offset and \ pass_.end_offset < node.end_offset: passes.remove(pass_) return if errname == 'missing_break': # Find the end of the previous case/default and the beginning of # the next case/default. assert node.kind in (tok.CASE, tok.DEFAULT) prevnode = node.parent.kids[node.node_index-1] expectedfallthru = prevnode.end_offset, node.start_offset elif errname == 'missing_break_for_last_case': # Find the end of the current case/default and the end of the # switch. assert node.parent.kind == tok.LC expectedfallthru = node.end_offset, node.parent.end_offset else: expectedfallthru = None if expectedfallthru: start, end = expectedfallthru for fallthru in fallthrus: # Look for a fallthru between the end of the current case or # default statement and the beginning of the next token. if fallthru.start_offset > start and fallthru.end_offset < end: fallthrus.remove(fallthru) return report_lint(node, errname, offset, **errargs) parse_errors = [] declares = [] unused_identifiers = [] import_paths = [] fallthrus = [] passes = [] possible_comments = jsparse.findpossiblecomments(script, script_offset) # Check control comments for the correct version. It may be this comment # isn't a valid comment (for example, it might be inside a string literal) # After parsing, validate that it's legitimate. jsversionnode = None for comment in possible_comments: cc = _parse_control_comment(comment) if cc: node, keyword, parms = cc if keyword == 'content-type': ccversion = util.JSVersion.fromtype(parms) if ccversion: jsversion = ccversion jsversionnode = node else: report(node, 'unsupported_version', version=parms) if not jsparse.isvalidversion(jsversion): report_lint(jsversionnode, 'unsupported_version', script_offset, version=jsversion.version) return root = jsparse.parse(script, jsversion, parse_error, script_offset) if not root: # Report errors and quit. for offset, msg, msg_args in parse_errors: report_native(offset, msg, msg_args) return comments = jsparse.filtercomments(possible_comments, root) if jsversionnode is not None and jsversionnode not in comments: # TODO report(jsversionnode, 'incorrect_version') start_ignore = None for comment in comments: cc = _parse_control_comment(comment) if cc: node, keyword, parms = cc if keyword == 'declare': if not util.isidentifier(parms): report(node, 'jsl_cc_not_understood') else: declares.append((parms, node)) elif keyword == 'unused': if not util.isidentifier(parms): report(node, 'jsl_cc_not_understood') else: unused_identifiers.append((parms, node)) elif keyword == 'ignore': if start_ignore: report(node, 'mismatch_ctrl_comments') else: start_ignore = node elif keyword == 'end': if start_ignore: ignores.append((start_ignore.start_offset, node.end_offset)) start_ignore = None else: report(node, 'mismatch_ctrl_comments') elif keyword == 'import': if not parms: report(node, 'jsl_cc_not_understood') else: import_paths.append(parms) elif keyword == 'fallthru': fallthrus.append(node) elif keyword == 'pass': passes.append(node) else: if comment.opcode == op.C_COMMENT: # Look for nested C-style comments. nested_comment = comment.atom.find('/*') if nested_comment < 0 and comment.atom.endswith('/'): nested_comment = len(comment.atom) - 1 # Report at the actual error of the location. Add two # characters for the opening two characters. if nested_comment >= 0: offset = comment.start_offset + 2 + nested_comment report(comment, 'nested_comment', offset=offset) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): report(comment, 'legacy_cc_not_understood') if start_ignore: report(start_ignore, 'mismatch_ctrl_comments') # Wait to report parse errors until loading jsl:ignore directives. for offset, msg in parse_errors: report_native(offset, msg) # Find all visitors and convert them into "onpush" callbacks that call "report" visitors = { 'push': warnings.make_visitors(conf) } for event in visitors: for kind, callbacks in visitors[event].items(): visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks] # Push the scope/variable checks. visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) # kickoff! _lint_node(root, visitors) for fallthru in fallthrus: report(fallthru, 'invalid_fallthru') for fallthru in passes: report(fallthru, 'invalid_pass') # Process imports by copying global declarations into the universal scope. for path in import_paths: script_cache.importscript(import_callback(path, jsversion)) for name, node in declares: declare_scope = script_cache.scope.find_scope(node) _warn_or_declare(declare_scope, name, 'var', node, report) for name, node in unused_identifiers: unused_scope = script_cache.scope.find_scope(node) unused_scope.set_unused(name, node) for node in jsparse.find_trailing_whitespace(script, script_offset): report(node, 'trailing_whitespace')
def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): def parse_error(row, col, msg): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): parse_errors.append((jsparse.NodePos(row, col), msg)) def report(node, errname, pos=None, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: if pass_.start_pos() > node.start_pos() and \ pass_.end_pos() < node.end_pos(): passes.remove(pass_) return if errname == 'missing_break': # Find the end of the previous case/default and the beginning of # the next case/default. assert node.kind in (tok.CASE, tok.DEFAULT) prevnode = node.parent.kids[node.node_index - 1] expectedfallthru = prevnode.end_pos(), node.start_pos() elif errname == 'missing_break_for_last_case': # Find the end of the current case/default and the end of the # switch. assert node.parent.kind == tok.LC expectedfallthru = node.end_pos(), node.parent.end_pos() else: expectedfallthru = None if expectedfallthru: start, end = expectedfallthru for fallthru in fallthrus: # Look for a fallthru between the end of the current case or # default statement and the beginning of the next token. if fallthru.start_pos() > start and fallthru.end_pos() < end: fallthrus.remove(fallthru) return report_lint(node, errname, pos, **errargs) parse_errors = [] declares = [] unused_identifiers = [] import_paths = [] fallthrus = [] passes = [] node_positions = jsparse.NodePositions(script, scriptpos) possible_comments = jsparse.findpossiblecomments(script, node_positions) # Check control comments for the correct version. It may be this comment # isn't a valid comment (for example, it might be inside a string literal) # After parsing, validate that it's legitimate. jsversionnode = None for comment in possible_comments: cc = _parse_control_comment(comment) if cc: node, keyword, parms = cc if keyword == 'content-type': ccversion = util.JSVersion.fromtype(parms) if ccversion: jsversion = ccversion jsversionnode = node else: report(node, 'unsupported_version', version=parms) if not jsparse.isvalidversion(jsversion): report_lint(jsversionnode, 'unsupported_version', scriptpos, version=jsversion.version) return root = jsparse.parse(script, jsversion, parse_error, scriptpos) if not root: # Report errors and quit. for pos, msg in parse_errors: report_native(pos, msg) return comments = jsparse.filtercomments(possible_comments, node_positions, root) if jsversionnode is not None and jsversionnode not in comments: # TODO report(jsversionnode, 'incorrect_version') start_ignore = None for comment in comments: cc = _parse_control_comment(comment) if cc: node, keyword, parms = cc if keyword == 'declare': if not util.isidentifier(parms): report(node, 'jsl_cc_not_understood') else: declares.append((parms, node)) elif keyword == 'unused': if not util.isidentifier(parms): report(node, 'jsl_cc_not_understood') else: unused_identifiers.append((parms, node)) elif keyword == 'ignore': if start_ignore: report(node, 'mismatch_ctrl_comments') else: start_ignore = node elif keyword == 'end': if start_ignore: ignores.append((start_ignore.start_pos(), node.end_pos())) start_ignore = None else: report(node, 'mismatch_ctrl_comments') elif keyword == 'import': if not parms: report(node, 'jsl_cc_not_understood') else: import_paths.append(parms) elif keyword == 'fallthru': fallthrus.append(node) elif keyword == 'pass': passes.append(node) else: if comment.opcode == 'c_comment': # Look for nested C-style comments. nested_comment = comment.atom.find('/*') if nested_comment < 0 and comment.atom.endswith('/'): nested_comment = len(comment.atom) - 1 # Report at the actual error of the location. Add two # characters for the opening two characters. if nested_comment >= 0: pos = node_positions.from_offset( node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) report(comment, 'nested_comment', pos=pos) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): report(comment, 'legacy_cc_not_understood') if start_ignore: report(start_ignore, 'mismatch_ctrl_comments') # Wait to report parse errors until loading jsl:ignore directives. for pos, msg in parse_errors: report_native(pos, msg) # Find all visitors and convert them into "onpush" callbacks that call "report" visitors = {'push': warnings.make_visitors()} for event in visitors: for kind, callbacks in visitors[event].items(): visitors[event][kind] = [ _getreporter(callback, report) for callback in callbacks ] # Push the scope/variable checks. visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) # kickoff! _lint_node(root, visitors) for fallthru in fallthrus: report(fallthru, 'invalid_fallthru') for fallthru in passes: report(fallthru, 'invalid_pass') # Process imports by copying global declarations into the universal scope. for path in import_paths: script_cache.importscript(import_callback(path, jsversion)) for name, node in declares: declare_scope = script_cache.scope.find_scope(node) _warn_or_declare(declare_scope, name, 'var', node, report) for name, node in unused_identifiers: unused_scope = script_cache.scope.find_scope(node) unused_scope.set_unused(name, node)
def useless_quotes(node): if node.node_index == 0 and node.parent.kind == tok.COLON: # Only warn if the quotes could safely be removed. if util.isidentifier(node.atom): raise LintWarning, node
def useless_quotes(node): if node.node_index == 0 and node.parent.kind == tok.COLON: # Only warn if the quotes could safely be removed. if util.isidentifier(node.atom): raise LintWarning(node)
def _lint_script_part(scriptpos, script, script_cache, conf, ignores, report_native, report_lint, import_callback): def parse_error(row, col, msg): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): parse_errors.append((jsparse.NodePos(row, col), msg)) def report(node, errname, pos=None, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: if pass_.start_pos() > node.start_pos() and \ pass_.end_pos() < node.end_pos(): passes.remove(pass_) return if errname == 'missing_break': # Find the end of the previous case/default and the beginning of # the next case/default. assert node.kind in (tok.CASE, tok.DEFAULT) prevnode = node.parent.kids[node.node_index-1] expectedfallthru = prevnode.end_pos(), node.start_pos() elif errname == 'missing_break_for_last_case': # Find the end of the current case/default and the end of the # switch. assert node.parent.kind == tok.LC expectedfallthru = node.end_pos(), node.parent.end_pos() else: expectedfallthru = None if expectedfallthru: start, end = expectedfallthru for fallthru in fallthrus: # Look for a fallthru between the end of the current case or # default statement and the beginning of the next token. if fallthru.start_pos() > start and fallthru.end_pos() < end: fallthrus.remove(fallthru) return report_lint(node, errname, pos, **errargs) parse_errors = [] declares = [] import_paths = [] fallthrus = [] passes = [] node_positions = jsparse.NodePositions(script, scriptpos) possible_comments = jsparse.findpossiblecomments(script, node_positions) root = jsparse.parse(script, parse_error, scriptpos) if not root: # Report errors and quit. for pos, msg in parse_errors: report_native(pos, msg) return comments = jsparse.filtercomments(possible_comments, node_positions, root) start_ignore = None for comment in comments: cc = _parse_control_comment(comment) if cc: node, keyword, parms = cc if keyword == 'declare': if not util.isidentifier(parms): report(node, 'jsl_cc_not_understood') else: declares.append((parms, node)) elif keyword == 'ignore': if start_ignore: report(node, 'mismatch_ctrl_comments') else: start_ignore = node elif keyword == 'end': if start_ignore: ignores.append((start_ignore.start_pos(), node.end_pos())) start_ignore = None else: report(node, 'mismatch_ctrl_comments') elif keyword == 'import': if not parms: report(node, 'jsl_cc_not_understood') else: import_paths.append(parms) elif keyword == 'fallthru': fallthrus.append(node) elif keyword == 'pass': passes.append(node) else: if comment.opcode == 'c_comment': # Look for nested C-style comments. nested_comment = comment.atom.find('/*') if nested_comment < 0 and comment.atom.endswith('/'): nested_comment = len(comment.atom) - 1 # Report at the actual error of the location. Add two # characters for the opening two characters. if nested_comment >= 0: pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) report(comment, 'nested_comment', pos=pos) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): report(comment, 'legacy_cc_not_understood') if start_ignore: report(start_ignore, 'mismatch_ctrl_comments') # Wait to report parse errors until loading jsl:ignore directives. for pos, msg in parse_errors: report_native(pos, msg) # Find all visitors and convert them into "onpush" callbacks that call "report" visitors = { 'push': warnings.make_visitors() } for event in visitors: for kind, callbacks in visitors[event].items(): visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks] # Push the scope/variable checks. visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) # kickoff! _lint_node(root, visitors) for fallthru in fallthrus: report(fallthru, 'invalid_fallthru') for fallthru in passes: report(fallthru, 'invalid_pass') # Process imports by copying global declarations into the universal scope. for path in import_paths: script_cache.importscript(import_callback(path)) for name, node in declares: declare_scope = script_cache.scope.find_scope(node) _warn_or_declare(declare_scope, name, 'var', node, report)