Checks the modules in the specified code parser to see if they
    have common, but subtle, pointer bugs in:

    1. subroutines with a parameter of intent(out) and user-derived type
      must* set *all* members of that parameter or they will have an
      *undefined* status.
    2. pointer-type arrays that are not nullified are set to a valid target
      will return 'T' when passed to `associated`. Best practice is to nullify
      pointer arrays in user-derived types as the default value on those types.
    
    :arg parser: [fortpy.code.CodeParser] with the modules to search *already loaded*.
    :arg codedir: specify the full path to the library whose modules should be searched,
      just another way to filter which modules are generating the warnings.
    :arg mfilter: filter to apply to module names; can use the wildcard standard
      from bash.

def check_pointers(parser, codedir=None, mfilter=None, recursive=False):
    """Checks the modules in the specified code parser to see if they
    have common, but subtle, pointer bugs in:

    1. subroutines with a parameter of intent(out) and user-derived type
      must* set *all* members of that parameter or they will have an
      *undefined* status.
    2. pointer-type arrays that are not nullified are set to a valid target
      will return 'T' when passed to `associated`. Best practice is to nullify
      pointer arrays in user-derived types as the default value on those types.
    
    :arg parser: [fortpy.code.CodeParser] with the modules to search *already loaded*.
    :arg codedir: specify the full path to the library whose modules should be searched,
      just another way to filter which modules are generating the warnings.
    :arg mfilter: filter to apply to module names; can use the wildcard standard
      from bash.
    """
    from fnmatch import fnmatch
    from fortpy.msg import std, set_verbosity, info

    set_verbosity(0)
    W1 = "   {} '{}' does not set the value of members '{}' in parameter '{}'."
    W2 = "   Type '{}' does not nullify members '{}' on creation."

    offenders = {}
    for (modname, module) in parser.modules.items():
        if not recursive and codedir is not None and not codedir.lower() in module.filepath.lower():
            continue
        if mfilter is not None and not fnmatch(module.name.lower(), mfilter.lower()):
            continue

        #Test the first condition above for all subroutines in the module; also handle
        #the recursively defined subroutines.
        hprinted = False
        for xname, xvalue in module.executables.items():
            oparams, pmembers = _exec_check_pointers(xvalue)
            if len(oparams) > 0:
                if not hprinted:
                    info("Best practice suggestions: {}".format(module.filepath))
                    hprinted = True
                    
                for oparam in oparams:
                    plist = ', '.join([p.name for p in pmembers[oparam]])                
                    std(W1.format(type(xvalue).__name__, xname, plist, oparam), 0)
                offenders[xvalue.full_name] = (oparams, pmembers)

        for tname, tvalue in module.types.items():
            result = _type_check_pointers(tvalue)
            if len(result) > 0:
                if not hprinted:
                    info("Best practice suggestions: {}".format(module.filepath))
                    hprinted = True

                plist = ', '.join([p.name for p in result])
                std(W2.format(tname, plist), 0)            
                offenders[xvalue.full_name] = result

    return offenders