Example #1
0
 def flush(self):
     # Emit warnings, sorted by source location:
     for loc, node in sorted(self.state_users,
                             key=lambda pair:pair[0]):
         gcc.inform(loc,
                    'use of global state "%s %s" here'
                    % (node.type, node))
    def check_cleanups(self):
        if not self.fun.cfg or not self.fun.decl:
            return 'ignored'
        if is_destructor(self.fun.decl):
            return 'destructor'
        if needs_special_treatment(self.fun.decl):
            return 'special'

        self.is_constructor = is_constructor(self.fun.decl)
        self.is_special_constructor = not self.is_constructor and str(self.fun.decl.name).find('with_cleanup') > -1
        # Yuck.
        if str(self.fun.decl.name) == 'gdb_xml_create_parser_and_cleanup_1':
            self.is_special_constructor = True

        if self.is_special_constructor:
            gcc.inform(self.fun.start, 'function %s is a special constructor' % (self.fun.decl.name))

        # If we only see do_cleanups calls, and this function is not
        # itself a constructor, then we can convert it easily to RAII.
        self.only_do_cleanups_seen = not self.is_constructor
        # If we ever call a constructor, then we are "cleanup-aware".
        self.cleanup_aware = False

        entry_bb = self.fun.cfg.entry
        master_cleanup = MasterCleanup()
        self.traverse_bbs(None, entry_bb, -1, master_cleanup)
        if want_raii_info and self.only_do_cleanups_seen and self.cleanup_aware:
            gcc.inform(self.fun.decl.location,
                       'function %s could be converted to RAII' % (self.fun.decl.name))
        if self.is_constructor:
            return 'constructor'
        return 'OK'
    def check_cleanups(self):
        if not self.fun.cfg or not self.fun.decl:
            return 'ignored'
        if is_destructor(self.fun.decl):
            return 'destructor'
        if needs_special_treatment(self.fun.decl):
            return 'special'

        self.is_constructor = is_constructor(self.fun.decl)
        self.is_special_constructor = not self.is_constructor and str(self.fun.decl.name).find('with_cleanup') > -1
        # Yuck.
        if str(self.fun.decl.name) == 'gdb_xml_create_parser_and_cleanup_1':
            self.is_special_constructor = True

        if self.is_special_constructor:
            gcc.inform(self.fun.start, 'function %s is a special constructor' % (self.fun.decl.name))

        # If we only see do_cleanups calls, and this function is not
        # itself a constructor, then we can convert it easily to RAII.
        self.only_do_cleanups_seen = not self.is_constructor
        # If we ever call a constructor, then we are "cleanup-aware".
        self.cleanup_aware = False

        entry_bb = self.fun.cfg.entry
        master_cleanup = MasterCleanup()
        self.traverse_bbs(None, entry_bb, -1, master_cleanup)
        if want_raii_info and self.only_do_cleanups_seen and self.cleanup_aware:
            gcc.inform(self.fun.decl.location,
                       'function %s could be converted to RAII' % (self.fun.decl.name))
        if self.is_constructor:
            return 'constructor'
        return 'OK'
Example #4
0
def on_pass_execution(p, fn):
    if p.name == '*warn_function_return':
        # Exercise gcc.inform with gcc.RichLocation:
        gcc.inform(gcc.RichLocation(fn.start),
                   'this is the start of the function')
        gcc.inform(gcc.RichLocation(fn.end),
                   'this is the end of the function')
Example #5
0
def on_pass_execution(p, fn):
    if p.name == '*warn_function_return':
        caret = fn.start.offset_column(13)
        start = fn.start.offset_column(9)
        finish = fn.start.offset_column(17)
        compound = gcc.Location(caret, start, finish)
        gcc.inform(compound, "compound location")
        assert compound.caret == caret
        assert compound.start == start
        assert compound.finish == finish
Example #6
0
def on_pass_execution(p, fn):
    if p.name == '*warn_function_return':
        caret = fn.start.offset_column(13)
        start = fn.start.offset_column(9)
        finish = fn.start.offset_column(17)
        compound = gcc.Location(caret, start, finish)
        gcc.inform(compound, "compound location")
        assert compound.caret == caret
        assert compound.start == start
        assert compound.finish == finish
 def push(self, location, lhs):
     if lhs is None:
         obj = Dummy(location)
     else:
         obj = Cleanup(lhs, location)
     log("pushing %s" % lhs, 4)
     idx = self._find_var(lhs)
     if idx >= 0:
         gcc.permerror(location, "reassigning to known cleanup")
         gcc.inform(self.cleanups[idx].location, "previous assignment is here")
     self.cleanups.append(obj)
Example #8
0
 def push(self, location, lhs):
     if lhs is None:
         obj = Dummy(location)
     else:
         obj = Cleanup(lhs, location)
     log('pushing %s' % lhs, 4)
     idx = self._find_var(lhs)
     if idx >= 0:
         gcc.permerror(location, 'reassigning to known cleanup')
         gcc.inform(self.cleanups[idx].location,
                    'previous assignment is here')
     self.cleanups.append(obj)
Example #9
0
    def examine_one_bb_inner(self, this_fun, bb):
        if not bb.gimple:
            return False
        try_catch = False
        for stmt in bb.gimple:
            loc = stmt.loc
            if not loc:
                loc = this_fun.decl.location
            if not isinstance(stmt, gcc.GimpleCall):
                continue
            callee_name = self.handle_one_fndecl(this_fun, stmt.fn, loc)

            if callee_name == 'exceptions_state_mc_action_iter':
                try_catch = True

            global non_passthrough_functions
            if callee_name in non_passthrough_functions:
                continue

            # We have to specially handle calls where an argument to
            # the call is itself a function, e.g., qsort.  In general
            # we model these as "passthrough" -- we assume that in
            # addition to the call the qsort there is also a call to
            # the argument function.
            for arg in stmt.args:
                # We are only interested in arguments which are functions.
                t = arg.type
                if isinstance(t, gcc.PointerType):
                    t = t.dereference
                if not isinstance(t, gcc.FunctionType):
                    continue

                if isinstance(arg, gcc.AddrExpr):
                    arg = arg.operand

                global cleanup_functions
                if callee_name in cleanup_functions:
                    if not isinstance(arg, gcc.FunctionDecl):
                        gcc.inform(
                            loc, 'cleanup argument not a DECL: %s' % repr(arg))
                    else:
                        # Cleanups must be nothrow.
                        self.output_file.write("declare_cleanup(%s)\n" %
                                               repr(str(arg.name)))
                else:
                    # Assume we have a passthrough function, like
                    # qsort or an iterator.  We model this by
                    # pretending there is an ordinary call at this
                    # point.
                    self.handle_one_fndecl(this_fun, arg, loc)
        return try_catch
Example #10
0
    def examine_one_bb_inner(self, this_fun, bb):
        if not bb.gimple:
            return False
        try_catch = False
        for stmt in bb.gimple:
            loc = stmt.loc
            if not loc:
                loc = this_fun.decl.location
            if not isinstance(stmt, gcc.GimpleCall):
                continue
            callee_name = self.handle_one_fndecl(this_fun, stmt.fn, loc)

            if callee_name == 'exceptions_state_mc_action_iter':
                try_catch = True

            global non_passthrough_functions
            if callee_name in non_passthrough_functions:
                continue

            # We have to specially handle calls where an argument to
            # the call is itself a function, e.g., qsort.  In general
            # we model these as "passthrough" -- we assume that in
            # addition to the call the qsort there is also a call to
            # the argument function.
            for arg in stmt.args:
                # We are only interested in arguments which are functions.
                t = arg.type
                if isinstance(t, gcc.PointerType):
                    t = t.dereference
                if not isinstance(t, gcc.FunctionType):
                    continue

                if isinstance(arg, gcc.AddrExpr):
                    arg = arg.operand

                global cleanup_functions
                if callee_name in cleanup_functions:
                    if not isinstance(arg, gcc.FunctionDecl):
                        gcc.inform(loc, 'cleanup argument not a DECL: %s' % repr(arg))
                    else:
                        # Cleanups must be nothrow.
                        self.output_file.write("declare_cleanup(%s)\n"
                                               % repr(str(arg.name)))
                else:
                    # Assume we have a passthrough function, like
                    # qsort or an iterator.  We model this by
                    # pretending there is an ordinary call at this
                    # point.
                    self.handle_one_fndecl(this_fun, arg, loc)
        return try_catch
def on_pass_execution(p, fn):
    # This pass is called fairly early on, per-function, after the
    # CFG has been built.  Skip every other pass.
    if p.name != '*warn_function_return':
        return

    # I don't want notices for system headers.
    if in_system_header(fn.decl.location):
        return

    for parm, parmtype in zip(fn.decl.arguments,
                              fn.decl.type.argument_types):
        if type(parmtype) == gcc.RecordType:
            gcc.inform(parm.location,
                       'parameter type is not trivial')
Example #12
0
 def spellcheck_node(self, node, loc):
     # Spellcheck any textual constants found within the node:
     if isinstance(node, gcc.StringCst):
         words = node.constant.split()
         for word in words:
             if not spellingdict.check(word):
                 # Warn about the spelling error (controlling the warning
                 # with the -Wall command-line option):
                 if gcc.warning(loc,
                                'Possibly misspelt word in string constant: %r' % word,
                                gcc.Option('-Wall')):
                     # and, if the warning was not suppressed at the command line, emit
                     # suggested respellings:
                     suggestions = spellingdict.suggest(word)
                     if suggestions:
                         gcc.inform(loc, 'Suggested respellings: %r' % ', '.join(suggestions))
Example #13
0
def on_pass_execution(p, fn):
    if p.name == '*warn_function_return':
        gcc.error(fn.start, 'this is an error (with positional args)')
        gcc.error(location=fn.start,
                  message='this is an error (with keyword args)')
        gcc.warning(fn.end, 'this is a warning (with positional args)',
                    gcc.Option('-Wdiv-by-zero'))
        gcc.warning(location=fn.end,
                    message='this is a warning (with keyword args)',
                    option=gcc.Option('-Wdiv-by-zero'))
        gcc.error(
            fn.start,
            # These should be passed through, without triggering errors:
            'a warning with some embedded format strings %s and %i')

        # Verify that -Wno-format was honored
        # The behavior of these flags changed in 4.8, so skip this part
        # on gcc 4.8 onwards:
        if gcc.GCCPLUGINS_API_VERSION <= 4007:
            gcc.warning(fn.end, 'this warning ought not to appear',
                        gcc.Option('-Wformat'))

        # Verify that we can issue an unconditional warning, with no option
        # (as per https://fedorahosted.org/gcc-python-plugin/ticket/8 ):
        gcc.warning(fn.end, 'this is an unconditional warning')
        gcc.warning(fn.end, 'this is another unconditional warning', None)

        # Verify that gcc.warning handles an object of the wrong type by
        # raising a TypeError
        try:
            gcc.warning(
                fn.end, 'this is another unconditional warning',
                'this should have been a gcc.Option instance, or None')
        except TypeError:
            err = sys.exc_info()[1]
            sys.stderr.write('expected error was found: %s\n' % err)
        else:
            raise RuntimeError('expected exception was not raised')

        # Exercise gcc.inform:
        gcc.inform(fn.start, 'This is the start of the function')
        gcc.inform(fn.end, 'This is the end of the function')
Example #14
0
def on_pass_execution(p, fn):
    if p.name == '*warn_function_return':
        gcc.error(fn.start, 'this is an error (with positional args)')
        gcc.error(location=fn.start,
                  message='this is an error (with keyword args)')
        gcc.warning(fn.end, 'this is a warning (with positional args)',
                    gcc.Option('-Wdiv-by-zero'))
        gcc.warning(location=fn.end,
                    message='this is a warning (with keyword args)',
                    option=gcc.Option('-Wdiv-by-zero'))
        gcc.error(fn.start,
                  # These should be passed through, without triggering errors:
                  'a warning with some embedded format strings %s and %i')

        # Verify that -Wno-format was honored
        # The behavior of these flags changed in 4.8, so skip this part
        # on gcc 4.8 onwards:
        if gcc.GCCPLUGINS_API_VERSION <= 4007:
            gcc.warning(fn.end,
                        'this warning ought not to appear',
                        gcc.Option('-Wformat'))

        # Verify that we can issue an unconditional warning, with no option
        # (as per https://fedorahosted.org/gcc-python-plugin/ticket/8 ):
        gcc.warning(fn.end, 'this is an unconditional warning')
        gcc.warning(fn.end, 'this is another unconditional warning', None)

        # Verify that gcc.warning handles an object of the wrong type by
        # raising a TypeError
        try:
            gcc.warning(fn.end, 'this is another unconditional warning',
                        'this should have been a gcc.Option instance, or None')
        except TypeError:
            err = sys.exc_info()[1]
            sys.stderr.write('expected error was found: %s\n' % err)
        else:
            raise RuntimeError('expected exception was not raised')

        # Exercise gcc.inform:
        gcc.inform(fn.start, 'This is the start of the function')
        gcc.inform(fn.end, 'This is the end of the function')
def verify_any_PyMethodDef_flags():
    """
    Check all initializers for PyMethodDef arrays.
    Verify that the flags used match the real signature of the callback
    function (albeit usually cast to a PyCFunction):
      http://docs.python.org/c-api/structures.html#PyMethodDef
    """
    methods = get_all_PyMethodDef_initializers()
    #from pprint import pprint
    #pprint(methods)

    for si in methods:
        if 0:
            print(si)
        ml_meth = si.function_ptr_field('ml_meth')
        ml_flags = si.int_field('ml_flags')
        if 0:
            print('  ml_meth: %r' % ml_meth)
            print('  ml_flags: %r' % ml_flags)
        check_isinstance(ml_flags, int)

        if ml_meth is not None:
            check_isinstance(ml_meth, gcc.FunctionDecl)
            if ml_flags & METH_KEYWORDS:
                expargs = 3
                exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag'
            else:
                expargs = 2
                exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)"'
            actualargs = len(ml_meth.type.argument_types)
            if expargs != actualargs:
                gcc.warning(
                    si.get_location(),
                    'flags do not match callback signature for %r'
                    ' within PyMethodDef table' % ml_meth.name)
                gcc.inform(si.get_location(),
                           exptypemsg + ' (%s arguments)' % expargs)
                gcc.inform(
                    si.get_location(),
                    'actual type of underlying callback: %s' % ml_meth.type +
                    ' (%s arguments)' % actualargs)
                gcc.inform(
                    si.get_location(),
                    'see http://docs.python.org/c-api/structures.html#PyMethodDef'
                )
Example #16
0
def verify_any_PyMethodDef_flags():
    """
    Check all initializers for PyMethodDef arrays.
    Verify that the flags used match the real signature of the callback
    function (albeit usually cast to a PyCFunction):
      http://docs.python.org/c-api/structures.html#PyMethodDef
    """
    methods = get_all_PyMethodDef_initializers()
    #from pprint import pprint
    #pprint(methods)

    for si in methods:
        if 0:
            print(si)
        ml_meth = si.function_ptr_field('ml_meth')
        ml_flags = si.int_field('ml_flags')
        if 0:
            print('  ml_meth: %r' % ml_meth)
            print('  ml_flags: %r' % ml_flags)
        check_isinstance(ml_flags, int)

        if ml_meth is not None:
            check_isinstance(ml_meth, gcc.FunctionDecl)
            if ml_flags & METH_KEYWORDS:
                expargs = 3
                exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag'
            else:
                expargs = 2
                exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)"'
            actualargs = len(ml_meth.type.argument_types)
            if expargs != actualargs:
                gcc.warning(si.get_location(),
                            'flags do not match callback signature for %r'
                            ' within PyMethodDef table'
                            % ml_meth.name)
                gcc.inform(si.get_location(),
                           exptypemsg + ' (%s arguments)' % expargs)
                gcc.inform(si.get_location(),
                           'actual type of underlying callback: %s' % ml_meth.type
                            + ' (%s arguments)' % actualargs)
                gcc.inform(si.get_location(),
                           'see http://docs.python.org/c-api/structures.html#PyMethodDef')
Example #17
0
def on_pass_execution(p, fn):
    if p.name == '*free_lang_data':
        for var in gcc.get_variables():
            gcc.inform(var.decl.location,
                       'global state "%s %s" defined here'
                       % (var.decl.type, var.decl))
Example #18
0
 def inform(self):
     for item in reversed(self.cleanups):
         gcc.inform(item.location, 'leaked cleanup')
Example #19
0
 def flush(self):
     # Emit warnings, sorted by source location:
     for loc, node in sorted(self.state_users, key=lambda pair: pair[0]):
         gcc.inform(loc,
                    'use of global state "%s %s" here' % (node.type, node))
Example #20
0
def find_function_calls(p, fn):
    if p.name != "*warn_function_return":
        return

    for bb in fn.cfg.basic_blocks:
        if not bb.gimple:
            continue

        for stmt in bb.gimple:
            # Filter out anything that's not a function call.
            if not isinstance(stmt, gcc.GimpleCall):
                continue

            # Filter out anything that's not a function pointer reference.  We're only
            # looking for "out->message" function calls, which go through a pointer.
            if not isinstance(stmt.fn, gcc.SsaName):
                continue

            if not isinstance(stmt.fn.def_stmt, gcc.GimpleAssign):
                continue

            if len(stmt.fn.def_stmt.rhs) != 1:
                continue

            # Filter out anything that's not a pcmk__output_t and that's not a reference
            # to the message field.
            if not hasattr(stmt.fn.def_stmt.rhs[0], "target") or not hasattr(stmt.fn.def_stmt.rhs[0], "field"):
                continue

            target = stmt.fn.def_stmt.rhs[0].target
            field  = stmt.fn.def_stmt.rhs[0].field

            if str(target.type) != "struct pcmk__output_t" or field.name != "message":
                continue

            # The first two arguments are the pcmk__output_t and the name of the message
            # being called.  The compiler should have caught any cases where this is
            # wrong, but just in case...
            if len(stmt.args) < 2:
                continue

            if isinstance(stmt.args[1], gcc.SsaName) and message_from_fn_call(stmt.args[1]):
                # This is a call to the message function that figures out the message
                # name by calling some function.  We can't figure out exactly which
                # message will be called at compile time, but it's almost certainly
                # going to be one of these four.  Iterate over each and check.  They
                # should all have the same arguments.
                for messageName in ["bundle", "clone", "group", "primitive"]:
                    if messageName not in registeredMessages:
                        gcc.error(stmt.loc, "Message not registered: %s" % messageName)
                        break

                    if not check_arg_count(stmt, messageName):
                        break

                    if not check_arg_types(stmt, messageName):
                        break
            elif not isinstance(stmt.args[1], gcc.AddrExpr):
                # This is a call to the message function that uses some other method
                # to determine the message name.  We can't figure it out, so just
                # print a note and keep going.
                gcc.inform(stmt.loc, "Cannot figure out message name")
                continue
            else:
                # This is a call to the message function that uses a string literal
                # for the message name.  That's easy.
                messageName = str(stmt.args[1]).replace('"', '')

                if messageName not in registeredMessages:
                    gcc.error(stmt.loc, "Unknown format message: %s" % messageName)
                    continue

                # Check that enough arguments were provided.  The expected length does
                # not include the first two arguments, which are not for the message.
                if not check_arg_count(stmt, messageName):
                    continue

                # Check that the types are as expected.
                check_arg_types(stmt, messageName)
Example #21
0
 def flush(self):
     gcc.inform(self.loc, self.msg)
Example #22
0
 def flush(self):
     gcc.inform(self.loc, self.msg)
 def inform(self):
     for item in reversed(self.cleanups):
         gcc.inform(item.location, 'leaked cleanup')