prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  `check-useless-excludes` meta hook doesn't seem to work well with broken symlinks
  Repro steps
  1. Add a submodule
  2. Add a symlink to somewhere inside this submodule
  3. Use `check-symlinks`
  4. Deinit submodule (to simulate the case when people clone without `--recursive`)
  4. Notice that it now complains about the symlink created on 2
  5. Add an exclude entry with the path to that symlink
  6. Use `check-useless-excludes`
  7. Notice that it complains that the added exclude entry doesn't apply.
  
  For a working example, check out https://github.com/pkoch/test-check-useless-excludes (without `--recursive`, or run `git submodule deinit --all`) and run `pre-commit run --all`.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pre_commit/meta_hooks/check_useless_excludes.py`
  Content:
  ```
  1 import argparse
  2 import re
  3 from typing import Optional
  4 from typing import Sequence
  5 
  6 from cfgv import apply_defaults
  7 
  8 import pre_commit.constants as C
  9 from pre_commit import git
  10 from pre_commit.clientlib import load_config
  11 from pre_commit.clientlib import MANIFEST_HOOK_DICT
  12 from pre_commit.commands.run import Classifier
  13 
  14 
  15 def exclude_matches_any(
  16         filenames: Sequence[str],
  17         include: str,
  18         exclude: str,
  19 ) -> bool:
  20     if exclude == '^$':
  21         return True
  22     include_re, exclude_re = re.compile(include), re.compile(exclude)
  23     for filename in filenames:
  24         if include_re.search(filename) and exclude_re.search(filename):
  25             return True
  26     return False
  27 
  28 
  29 def check_useless_excludes(config_file: str) -> int:
  30     config = load_config(config_file)
  31     filenames = git.get_all_files()
  32     classifier = Classifier.from_config(
  33         filenames, config['files'], config['exclude'],
  34     )
  35     retv = 0
  36 
  37     exclude = config['exclude']
  38     if not exclude_matches_any(filenames, '', exclude):
  39         print(
  40             f'The global exclude pattern {exclude!r} does not match any files',
  41         )
  42         retv = 1
  43 
  44     for repo in config['repos']:
  45         for hook in repo['hooks']:
  46             # Not actually a manifest dict, but this more accurately reflects
  47             # the defaults applied during runtime
  48             hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
  49             names = classifier.filenames
  50             types = hook['types']
  51             types_or = hook['types_or']
  52             exclude_types = hook['exclude_types']
  53             names = classifier.by_types(names, types, types_or, exclude_types)
  54             include, exclude = hook['files'], hook['exclude']
  55             if not exclude_matches_any(names, include, exclude):
  56                 print(
  57                     f'The exclude pattern {exclude!r} for {hook["id"]} does '
  58                     f'not match any files',
  59                 )
  60                 retv = 1
  61 
  62     return retv
  63 
  64 
  65 def main(argv: Optional[Sequence[str]] = None) -> int:
  66     parser = argparse.ArgumentParser()
  67     parser.add_argument('filenames', nargs='*', default=[C.CONFIG_FILE])
  68     args = parser.parse_args(argv)
  69 
  70     retv = 0
  71     for filename in args.filenames:
  72         retv |= check_useless_excludes(filename)
  73     return retv
  74 
  75 
  76 if __name__ == '__main__':
  77     exit(main())
  78
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py
  --- a/pre_commit/meta_hooks/check_useless_excludes.py
  +++ b/pre_commit/meta_hooks/check_useless_excludes.py
  @@ -43,6 +43,9 @@
   
       for repo in config['repos']:
           for hook in repo['hooks']:
  +            # the default of manifest hooks is `types: [file]` but we may
  +            # be configuring a symlink hook while there's a broken symlink
  +            hook.setdefault('types', [])
               # Not actually a manifest dict, but this more accurately reflects
               # the defaults applied during runtime
               hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
  
verification_info: {"golden_diff": "diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py\n--- a/pre_commit/meta_hooks/check_useless_excludes.py\n+++ b/pre_commit/meta_hooks/check_useless_excludes.py\n@@ -43,6 +43,9 @@\n \n     for repo in config['repos']:\n         for hook in repo['hooks']:\n+            # the default of manifest hooks is `types: [file]` but we may\n+            # be configuring a symlink hook while there's a broken symlink\n+            hook.setdefault('types', [])\n             # Not actually a manifest dict, but this more accurately reflects\n             # the defaults applied during runtime\n             hook = apply_defaults(hook, MANIFEST_HOOK_DICT)\n", "issue": "`check-useless-excludes` meta hook doesn't seem to work well with broken symlinks\nRepro steps\r\n1. Add a submodule\r\n2. Add a symlink to somewhere inside this submodule\r\n3. Use `check-symlinks`\r\n4. Deinit submodule (to simulate the case when people clone without `--recursive`)\r\n4. Notice that it now complains about the symlink created on 2\r\n5. Add an exclude entry with the path to that symlink\r\n6. Use `check-useless-excludes`\r\n7. Notice that it complains that the added exclude entry doesn't apply.\r\n\r\nFor a working example, check out https://github.com/pkoch/test-check-useless-excludes (without `--recursive`, or run `git submodule deinit --all`) and run `pre-commit run --all`.\n", "before_files": [{"content": "import argparse\nimport re\nfrom typing import Optional\nfrom typing import Sequence\n\nfrom cfgv import apply_defaults\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.clientlib import MANIFEST_HOOK_DICT\nfrom pre_commit.commands.run import Classifier\n\n\ndef exclude_matches_any(\n        filenames: Sequence[str],\n        include: str,\n        exclude: str,\n) -> bool:\n    if exclude == '^$':\n        return True\n    include_re, exclude_re = re.compile(include), re.compile(exclude)\n    for filename in filenames:\n        if include_re.search(filename) and exclude_re.search(filename):\n            return True\n    return False\n\n\ndef check_useless_excludes(config_file: str) -> int:\n    config = load_config(config_file)\n    filenames = git.get_all_files()\n    classifier = Classifier.from_config(\n        filenames, config['files'], config['exclude'],\n    )\n    retv = 0\n\n    exclude = config['exclude']\n    if not exclude_matches_any(filenames, '', exclude):\n        print(\n            f'The global exclude pattern {exclude!r} does not match any files',\n        )\n        retv = 1\n\n    for repo in config['repos']:\n        for hook in repo['hooks']:\n            # Not actually a manifest dict, but this more accurately reflects\n            # the defaults applied during runtime\n            hook = apply_defaults(hook, MANIFEST_HOOK_DICT)\n            names = classifier.filenames\n            types = hook['types']\n            types_or = hook['types_or']\n            exclude_types = hook['exclude_types']\n            names = classifier.by_types(names, types, types_or, exclude_types)\n            include, exclude = hook['files'], hook['exclude']\n            if not exclude_matches_any(names, include, exclude):\n                print(\n                    f'The exclude pattern {exclude!r} for {hook[\"id\"]} does '\n                    f'not match any files',\n                )\n                retv = 1\n\n    return retv\n\n\ndef main(argv: Optional[Sequence[str]] = None) -> int:\n    parser = argparse.ArgumentParser()\n    parser.add_argument('filenames', nargs='*', default=[C.CONFIG_FILE])\n    args = parser.parse_args(argv)\n\n    retv = 0\n    for filename in args.filenames:\n        retv |= check_useless_excludes(filename)\n    return retv\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "pre_commit/meta_hooks/check_useless_excludes.py"}], "after_files": [{"content": "import argparse\nimport re\nfrom typing import Optional\nfrom typing import Sequence\n\nfrom cfgv import apply_defaults\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.clientlib import MANIFEST_HOOK_DICT\nfrom pre_commit.commands.run import Classifier\n\n\ndef exclude_matches_any(\n        filenames: Sequence[str],\n        include: str,\n        exclude: str,\n) -> bool:\n    if exclude == '^$':\n        return True\n    include_re, exclude_re = re.compile(include), re.compile(exclude)\n    for filename in filenames:\n        if include_re.search(filename) and exclude_re.search(filename):\n            return True\n    return False\n\n\ndef check_useless_excludes(config_file: str) -> int:\n    config = load_config(config_file)\n    filenames = git.get_all_files()\n    classifier = Classifier.from_config(\n        filenames, config['files'], config['exclude'],\n    )\n    retv = 0\n\n    exclude = config['exclude']\n    if not exclude_matches_any(filenames, '', exclude):\n        print(\n            f'The global exclude pattern {exclude!r} does not match any files',\n        )\n        retv = 1\n\n    for repo in config['repos']:\n        for hook in repo['hooks']:\n            # the default of manifest hooks is `types: [file]` but we may\n            # be configuring a symlink hook while there's a broken symlink\n            hook.setdefault('types', [])\n            # Not actually a manifest dict, but this more accurately reflects\n            # the defaults applied during runtime\n            hook = apply_defaults(hook, MANIFEST_HOOK_DICT)\n            names = classifier.filenames\n            types = hook['types']\n            types_or = hook['types_or']\n            exclude_types = hook['exclude_types']\n            names = classifier.by_types(names, types, types_or, exclude_types)\n            include, exclude = hook['files'], hook['exclude']\n            if not exclude_matches_any(names, include, exclude):\n                print(\n                    f'The exclude pattern {exclude!r} for {hook[\"id\"]} does '\n                    f'not match any files',\n                )\n                retv = 1\n\n    return retv\n\n\ndef main(argv: Optional[Sequence[str]] = None) -> int:\n    parser = argparse.ArgumentParser()\n    parser.add_argument('filenames', nargs='*', default=[C.CONFIG_FILE])\n    args = parser.parse_args(argv)\n\n    retv = 0\n    for filename in args.filenames:\n        retv |= check_useless_excludes(filename)\n    return retv\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "pre_commit/meta_hooks/check_useless_excludes.py"}]}