Beispiel #1
0
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)
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
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)