diff --git a/README.md b/README.md
index 567b484..cfa3def 100644
--- a/README.md
+++ b/README.md
@@ -284,6 +284,20 @@ six.assertRaisesRegex(self, e, r, fn)   # self.assertRaisesRegex(e, r, fn)
 six.assertRegex(self, s, r)             # self.assertRegex(s, r)
 ```
 
+### `open` alias
+
+Availability:
+- `--py3-plus` is passed on the commandline.
+
+```python
+# input
+with io.open('f.txt') as f:
+    pass
+# output
+with open('f.txt') as f:
+    pass
+```
+
 ### `OSError` aliases
 
 Availability:
diff --git a/pyupgrade.py b/pyupgrade.py
index d61250e..2124804 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -1115,6 +1115,7 @@ class FindPy3Plus(ast.NodeVisitor):
         self._from_imported_names = collections.defaultdict(
             set,
         )  # type: Dict[str, Set[str]]
+        self.io_open_calls = {}  # type: Dict[Offset, ast.Call]
         self.os_error_alias_calls = {}  # type: Dict[Offset, ast.Call]
         self.os_error_alias_simple = {}  # type: Dict[Offset, NameOrAttr]
 
@@ -1147,6 +1148,15 @@ class FindPy3Plus(ast.NodeVisitor):
             node.attr in names
         )
 
+    def _is_io_open(self, node):
+        # type: (ast.expr) -> bool
+        return (
+            isinstance(node, ast.Attribute) and
+            isinstance(node.value, ast.Name) and
+            node.value.id == 'io' and
+            node.attr == 'open'
+        )
+
     def _is_os_error_alias(self, node):
         # type: (ast.expr) -> bool
         return (
@@ -1352,6 +1362,8 @@ class FindPy3Plus(ast.NodeVisitor):
                 _is_codec(node.args[0].s, 'utf-8')
         ):
             self.encode_calls[_ast_to_offset(node)] = node
+        elif self._is_io_open(node.func):
+            self.io_open_calls[_ast_to_offset(node)] = node
 
         self.generic_visit(node)
 
@@ -1715,6 +1727,7 @@ def _fix_py3_plus(contents_text):  # type: (str) -> str
             visitor.if_py2_blocks,
             visitor.if_py3_blocks,
             visitor.native_literals,
+            visitor.io_open_calls,
             visitor.os_error_alias_calls,
             visitor.os_error_alias_simple,
             visitor.six_add_metaclass,
@@ -1858,6 +1871,9 @@ def _fix_py3_plus(contents_text):  # type: (str) -> str
                 _replace_call(tokens, i, end, func_args, '{args[0]}')
             else:
                 tokens[i:end] = [token._replace(name='STRING', src="''")]
+        elif token.offset in visitor.io_open_calls:
+            j = _find_open_paren(tokens, i)
+            tokens[i:j] = [token._replace(name='NAME', src='open')]
         elif token.offset in visitor.os_error_alias_calls:
             j = _find_open_paren(tokens, i)
             tokens[i:j] = [token._replace(name='NAME', src='OSError')]

--py3-plus: io.open => open
diff --git a/tests/io_open_test.py b/tests/io_open_test.py
new file mode 100644
index 0000000..231dec9
--- /dev/null
+++ b/tests/io_open_test.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import pytest
+
+from pyupgrade import _fix_py3_plus
+
+
+@pytest.mark.parametrize(
+    's',
+    (
+        # when using open without referencing io we don't need to rewrite
+        'from io import open\n\n'
+        'with open("f.txt") as f:\n'
+        '     print(f.read())\n',
+    ),
+)
+def test_fix_io_open_noop(s):
+    assert _fix_py3_plus(s) == s
+
+
+@pytest.mark.parametrize(
+    ('s', 'expected'),
+    (
+        (
+            'import io\n\n'
+            'with io.open("f.txt", mode="r", buffering=-1, **kwargs) as f:\n'
+            '   print(f.read())\n',
+
+            'import io\n\n'
+            'with open("f.txt", mode="r", buffering=-1, **kwargs) as f:\n'
+            '   print(f.read())\n',
+        ),
+    ),
+)
+def test_fix_io_open(s, expected):
+    assert _fix_py3_plus(s) == expected

pip install -e .[dev]
parse_log_pytest
pytest
coverage flake8 mock pre-commit pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
cfgv==3.4.0
coverage==7.8.0
distlib==0.3.9
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
filelock==3.18.0
flake8==7.2.0
identify==2.6.9
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mccabe==0.7.0
mock==5.2.0
nodeenv==1.9.1
packaging @ file:///croot/packaging_1734472117206/work
platformdirs==4.3.7
pluggy @ file:///croot/pluggy_1733169602837/work
pre_commit==4.2.0
pycodestyle==2.13.0
pyflakes==3.3.1
pytest @ file:///croot/pytest_1738938843180/work
-e git+https://github.com/asottile/pyupgrade.git@0c6149e665aeef2a5db423cfd145e6b7356fc160#egg=pyupgrade
PyYAML==6.0.2
tokenize_rt==6.1.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
virtualenv==20.29.3

name: pyupgrade
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cfgv==3.4.0
      - coverage==7.8.0
      - distlib==0.3.9
      - filelock==3.18.0
      - flake8==7.2.0
      - identify==2.6.9
      - mccabe==0.7.0
      - mock==5.2.0
      - nodeenv==1.9.1
      - platformdirs==4.3.7
      - pre-commit==4.2.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.1
      - pyyaml==6.0.2
      - tokenize-rt==6.1.0
      - virtualenv==20.29.3
prefix: /opt/conda/envs/pyupgrade

tests/io_open_test.py::test_fix_io_open[import
tests/io_open_test.py::test_fix_io_open_noop[from
README.md pyupgrade.py
README.md pyupgrade.py