From e1636b83e323415539d6b487dc1571728d13eb81 Mon Sep 17 00:00:00 2001
From: Andrew Pollack <andrewpkq@gmail.com>
Date: Tue, 1 Nov 2022 17:02:30 +0000
Subject: [PATCH 01/20] Adding Fuchsia compiler testing script, docs

---
 src/ci/docker/scripts/fuchsia-test-runner.py  | 1041 +++++++++++++++++
 src/doc/rustc/src/platform-support/fuchsia.md |   56 +-
 2 files changed, 1095 insertions(+), 2 deletions(-)
 create mode 100644 src/ci/docker/scripts/fuchsia-test-runner.py

diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
new file mode 100644
index 0000000000000..a2708d16947f0
--- /dev/null
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
@@ -0,0 +1,1041 @@
+#!/usr/bin/env python3
+
+"""
+The Rust toolchain test runner for Fuchsia.
+
+For instructions on running the compiler test suite, see
+https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-fuchsia-and-x86_64-fuchsia
+"""
+
+import argparse
+from dataclasses import dataclass
+import glob
+import hashlib
+import json
+import os
+import platform
+import re
+import shutil
+import signal
+import subprocess
+import sys
+from typing import ClassVar, List
+
+
+@dataclass
+class TestEnvironment:
+    rust_dir: str
+    sdk_dir: str
+    target_arch: str
+    package_server_pid: int = None
+    emu_addr: str = None
+    libstd_name: str = None
+    libtest_name: str = None
+    verbose: bool = False
+
+    @staticmethod
+    def tmp_dir():
+        tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR")
+        if tmp_dir is not None:
+            return os.path.abspath(tmp_dir)
+        return os.path.join(os.path.dirname(__file__), "tmp~")
+
+    @classmethod
+    def env_file_path(cls):
+        return os.path.join(cls.tmp_dir(), "test_env.json")
+
+    @classmethod
+    def from_args(cls, args):
+        return cls(
+            os.path.abspath(args.rust),
+            os.path.abspath(args.sdk),
+            args.target_arch,
+            verbose=args.verbose,
+        )
+
+    @classmethod
+    def read_from_file(cls):
+        with open(cls.env_file_path(), encoding="utf-8") as f:
+            test_env = json.loads(f.read())
+            return cls(
+                test_env["rust_dir"],
+                test_env["sdk_dir"],
+                test_env["target_arch"],
+                libstd_name=test_env["libstd_name"],
+                libtest_name=test_env["libtest_name"],
+                emu_addr=test_env["emu_addr"],
+                package_server_pid=test_env["package_server_pid"],
+                verbose=test_env["verbose"],
+            )
+
+    def image_name(self):
+        if self.target_arch == "x64":
+            return "qemu-x64"
+        if self.target_arch == "arm64":
+            return "qemu-arm64"
+        raise Exception(f"Unrecognized target architecture {self.target_arch}")
+
+    def write_to_file(self):
+        with open(self.env_file_path(), "w", encoding="utf-8") as f:
+            f.write(json.dumps(self.__dict__))
+
+    def ssh_dir(self):
+        return os.path.join(self.tmp_dir(), "ssh")
+
+    def ssh_keyfile_path(self):
+        return os.path.join(self.ssh_dir(), "fuchsia_ed25519")
+
+    def ssh_authfile_path(self):
+        return os.path.join(self.ssh_dir(), "fuchsia_authorized_keys")
+
+    def vdl_output_path(self):
+        return os.path.join(self.tmp_dir(), "vdl_output")
+
+    def package_server_log_path(self):
+        return os.path.join(self.tmp_dir(), "package_server_log")
+
+    def emulator_log_path(self):
+        return os.path.join(self.tmp_dir(), "emulator_log")
+
+    def packages_dir(self):
+        return os.path.join(self.tmp_dir(), "packages")
+
+    def output_dir(self):
+        return os.path.join(self.tmp_dir(), "output")
+
+    TEST_REPO_NAME: ClassVar[str] = "rust-testing"
+
+    def repo_dir(self):
+        return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME)
+
+    def rustlib_dir(self):
+        if self.target_arch == "x64":
+            return "x86_64-fuchsia"
+        if self.target_arch == "arm64":
+            return "aarch64-fuchsia"
+        raise Exception(f"Unrecognized target architecture {self.target_arch}")
+
+    def libs_dir(self):
+        return os.path.join(
+            self.rust_dir,
+            "lib",
+        )
+
+    def rustlibs_dir(self):
+        return os.path.join(
+            self.libs_dir(),
+            "rustlib",
+            self.rustlib_dir(),
+            "lib",
+        )
+
+    def sdk_arch(self):
+        machine = platform.machine()
+        if machine == "x86_64":
+            return "x64"
+        if machine == "arm":
+            return "a64"
+        raise Exception(f"Unrecognized host architecture {machine}")
+
+    def tool_path(self, tool):
+        return os.path.join(self.sdk_dir, "tools", self.sdk_arch(), tool)
+
+    def host_arch_triple(self):
+        machine = platform.machine()
+        if machine == "x86_64":
+            return "x86_64-unknown-linux-gnu"
+        if machine == "arm":
+            return "aarch64-unknown-linux-gnu"
+        raise Exception(f"Unrecognized host architecture {machine}")
+
+    def zxdb_script_path(self):
+        return os.path.join(self.tmp_dir(), "zxdb_script")
+
+    def log_info(self, msg):
+        print(msg)
+
+    def log_debug(self, msg):
+        if self.verbose:
+            print(msg)
+
+    def subprocess_output(self):
+        if self.verbose:
+            return sys.stdout
+        return subprocess.DEVNULL
+
+    def ffx_daemon_log_path(self):
+        return os.path.join(self.tmp_dir(), "ffx_daemon_log")
+
+    def ffx_isolate_dir(self):
+        return os.path.join(self.tmp_dir(), "ffx_isolate")
+
+    def ffx_home_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "user-home")
+
+    def ffx_tmp_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "tmp")
+
+    def ffx_log_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "log")
+
+    def ffx_user_config_dir(self):
+        return os.path.join(self.ffx_xdg_config_home(), "Fuchsia", "ffx", "config")
+
+    def ffx_user_config_path(self):
+        return os.path.join(self.ffx_user_config_dir(), "config.json")
+
+    def ffx_xdg_config_home(self):
+        if platform.system() == "Darwin":
+            return os.path.join(self.ffx_home_dir(), "Library", "Preferences")
+        return os.path.join(self.ffx_home_dir(), ".local", "share")
+
+    def ffx_ascendd_path(self):
+        return os.path.join(self.ffx_tmp_dir(), "ascendd")
+
+    def start_ffx_isolation(self):
+        # Most of this is translated directly from ffx's isolate library
+        os.mkdir(self.ffx_isolate_dir())
+        os.mkdir(self.ffx_home_dir())
+        os.mkdir(self.ffx_tmp_dir())
+        os.mkdir(self.ffx_log_dir())
+
+        fuchsia_dir = os.path.join(self.ffx_home_dir(), ".fuchsia")
+        os.mkdir(fuchsia_dir)
+
+        fuchsia_debug_dir = os.path.join(fuchsia_dir, "debug")
+        os.mkdir(fuchsia_debug_dir)
+
+        metrics_dir = os.path.join(fuchsia_dir, "metrics")
+        os.mkdir(metrics_dir)
+
+        analytics_path = os.path.join(metrics_dir, "analytics-status")
+        with open(analytics_path, "w", encoding="utf-8") as analytics_file:
+            print("0", file=analytics_file)
+
+        ffx_path = os.path.join(metrics_dir, "ffx")
+        with open(ffx_path, "w", encoding="utf-8") as ffx_file:
+            print("1", file=ffx_file)
+
+        os.makedirs(self.ffx_user_config_dir())
+
+        with open(
+            self.ffx_user_config_path(), "w", encoding="utf-8"
+        ) as config_json_file:
+            user_config_for_test = {
+                "log": {
+                    "enabled": True,
+                    "dir": self.ffx_log_dir(),
+                },
+                "overnet": {
+                    "socket": self.ffx_ascendd_path(),
+                },
+                "ssh": {
+                    "pub": self.ssh_authfile_path(),
+                    "priv": self.ssh_keyfile_path(),
+                },
+                "test": {
+                    "is_isolated": True,
+                    "experimental_structured_output": True,
+                },
+            }
+            print(json.dumps(user_config_for_test), file=config_json_file)
+
+        ffx_env_path = os.path.join(self.ffx_user_config_dir(), ".ffx_env")
+        with open(ffx_env_path, "w", encoding="utf-8") as ffx_env_file:
+            ffx_env_config_for_test = {
+                "user": self.ffx_user_config_path(),
+                "build": None,
+                "global": None,
+            }
+            print(json.dumps(ffx_env_config_for_test), file=ffx_env_file)
+
+        # Start ffx daemon
+        # We want this to be a long-running process that persists after the script finishes
+        # pylint: disable=consider-using-with
+        with open(
+            self.ffx_daemon_log_path(), "w", encoding="utf-8"
+        ) as ffx_daemon_log_file:
+            subprocess.Popen(
+                [
+                    self.tool_path("ffx"),
+                    "--config",
+                    self.ffx_user_config_path(),
+                    "daemon",
+                    "start",
+                ],
+                env=self.ffx_cmd_env(),
+                stdout=ffx_daemon_log_file,
+                stderr=ffx_daemon_log_file,
+            )
+
+    def ffx_cmd_env(self):
+        result = {
+            "HOME": self.ffx_home_dir(),
+            "XDG_CONFIG_HOME": self.ffx_xdg_config_home(),
+            "ASCENDD": self.ffx_ascendd_path(),
+            "FUCHSIA_SSH_KEY": self.ssh_keyfile_path(),
+            # We want to use our own specified temp directory
+            "TMP": self.tmp_dir(),
+            "TEMP": self.tmp_dir(),
+            "TMPDIR": self.tmp_dir(),
+            "TEMPDIR": self.tmp_dir(),
+        }
+
+        return result
+
+    def stop_ffx_isolation(self):
+        subprocess.check_call(
+            [
+                self.tool_path("ffx"),
+                "--config",
+                self.ffx_user_config_path(),
+                "daemon",
+                "stop",
+            ],
+            env=self.ffx_cmd_env(),
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+    def start(self):
+        """Sets up the testing environment and prepares to run tests.
+
+        Args:
+            args: The command-line arguments to this command.
+
+        During setup, this function will:
+        - Locate necessary shared libraries
+        - Create a new temp directory (this is where all temporary files are stored)
+        - Start an emulator
+        - Start an update server
+        - Create a new package repo and register it with the emulator
+        - Write test environment settings to a temporary file
+        """
+
+        # Initialize temp directory
+        if not os.path.exists(self.tmp_dir()):
+            os.mkdir(self.tmp_dir())
+        elif len(os.listdir(self.tmp_dir())) != 0:
+            raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})")
+
+        os.mkdir(self.ssh_dir())
+        os.mkdir(self.output_dir())
+
+        # Find libstd and libtest
+        libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so"))
+        libtest_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libtest-*.so"))
+
+        if not libstd_paths:
+            raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})")
+
+        if not libtest_paths:
+            raise Exception(f"Failed to locate libtest (in {self.rustlibs_dir()})")
+
+        self.libstd_name = os.path.basename(libstd_paths[0])
+        self.libtest_name = os.path.basename(libtest_paths[0])
+
+        # Generate SSH keys for the emulator to use
+        self.log_info("Generating SSH keys...")
+        subprocess.check_call(
+            [
+                "ssh-keygen",
+                "-N",
+                "",
+                "-t",
+                "ed25519",
+                "-f",
+                self.ssh_keyfile_path(),
+                "-C",
+                "Generated by test_toolchain.py",
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+        authfile_contents = subprocess.check_output(
+            [
+                "ssh-keygen",
+                "-y",
+                "-f",
+                self.ssh_keyfile_path(),
+            ],
+            stderr=self.subprocess_output(),
+        )
+        with open(self.ssh_authfile_path(), "wb") as authfile:
+            authfile.write(authfile_contents)
+
+        # Start ffx isolation
+        self.log_info("Starting ffx isolation...")
+        self.start_ffx_isolation()
+
+        # Start emulator (this will generate the vdl output)
+        self.log_info("Starting emulator...")
+        subprocess.check_call(
+            [
+                self.tool_path("fvdl"),
+                "--sdk",
+                "start",
+                "--tuntap",
+                "--headless",
+                "--nointeractive",
+                "--ssh",
+                self.ssh_dir(),
+                "--vdl-output",
+                self.vdl_output_path(),
+                "--emulator-log",
+                self.emulator_log_path(),
+                "--image-name",
+                self.image_name(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Parse vdl output for relevant information
+        with open(self.vdl_output_path(), encoding="utf-8") as f:
+            vdl_content = f.read()
+            matches = re.search(
+                r'network_address:\s+"\[([0-9a-f]{1,4}:(:[0-9a-f]{1,4}){4}%qemu)\]"',
+                vdl_content,
+            )
+            self.emu_addr = matches.group(1)
+
+        # Create new package repo
+        self.log_info("Creating package repo...")
+        subprocess.check_call(
+            [
+                self.tool_path("pm"),
+                "newrepo",
+                "-repo",
+                self.repo_dir(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Start package server
+        self.log_info("Starting package server...")
+        with open(
+            self.package_server_log_path(), "w", encoding="utf-8"
+        ) as package_server_log:
+            # We want this to be a long-running process that persists after the script finishes
+            # pylint: disable=consider-using-with
+            self.package_server_pid = subprocess.Popen(
+                [
+                    self.tool_path("pm"),
+                    "serve",
+                    "-vt",
+                    "-repo",
+                    self.repo_dir(),
+                    "-l",
+                    ":8084",
+                ],
+                stdout=package_server_log,
+                stderr=package_server_log,
+            ).pid
+
+        # Register package server with emulator
+        self.log_info("Registering package server...")
+        ssh_client = subprocess.check_output(
+            [
+                "ssh",
+                "-i",
+                self.ssh_keyfile_path(),
+                "-o",
+                "StrictHostKeyChecking=accept-new",
+                self.emu_addr,
+                "-f",
+                "echo $SSH_CLIENT",
+            ],
+            text=True,
+        )
+        repo_addr = ssh_client.split()[0].replace("%", "%25")
+        repo_url = f"http://[{repo_addr}]:8084/config.json"
+        subprocess.check_call(
+            [
+                "ssh",
+                "-i",
+                self.ssh_keyfile_path(),
+                "-o",
+                "StrictHostKeyChecking=accept-new",
+                self.emu_addr,
+                "-f",
+                f"pkgctl repo add url -f 1 -n {self.TEST_REPO_NAME} {repo_url}",
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Write to file
+        self.write_to_file()
+
+        self.log_info("Success! Your environment is ready to run tests.")
+
+    # FIXME: shardify this
+    # `facet` statement required for TCP testing via
+    # protocol `fuchsia.posix.socket.Provider`. See
+    # https://fuchsia.dev/fuchsia-src/development/testing/components/test_runner_framework?hl=en#legacy_non-hermetic_tests
+    CML_TEMPLATE: ClassVar[
+        str
+    ] = """
+    {{
+        program: {{
+            runner: "elf_test_runner",
+            binary: "bin/{exe_name}",
+            forward_stderr_to: "log",
+            forward_stdout_to: "log",
+            environ: [{env_vars}
+            ]
+        }},
+        capabilities: [
+            {{ protocol: "fuchsia.test.Suite" }},
+        ],
+        expose: [
+            {{
+                protocol: "fuchsia.test.Suite",
+                from: "self",
+            }},
+        ],
+        use: [
+            {{ storage: "data", path: "/data" }},
+            {{ protocol: [ "fuchsia.process.Launcher" ] }},
+            {{ protocol: [ "fuchsia.posix.socket.Provider" ] }}
+        ],
+        facets: {{
+            "fuchsia.test": {{ type: "system" }},
+        }},
+    }}
+    """
+
+    MANIFEST_TEMPLATE = """
+    meta/package={package_dir}/meta/package
+    meta/{package_name}.cm={package_dir}/meta/{package_name}.cm
+    bin/{exe_name}={bin_path}
+    lib/{libstd_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libstd_name}
+    lib/{libtest_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libtest_name}
+    lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/lib/libc.so
+    lib/libzircon.so={sdk_dir}/arch/{target_arch}/sysroot/lib/libzircon.so
+    lib/libfdio.so={sdk_dir}/arch/{target_arch}/lib/libfdio.so
+    """
+
+    TEST_ENV_VARS: ClassVar[List[str]] = [
+        "TEST_EXEC_ENV",
+        "RUST_MIN_STACK",
+        "RUST_BACKTRACE",
+        "RUST_NEWRT",
+        "RUST_LOG",
+        "RUST_TEST_THREADS",
+    ]
+
+    def run(self, args):
+        """Runs the requested test in the testing environment.
+
+        Args:
+        args: The command-line arguments to this command.
+        Returns:
+        The return code of the test (0 for success, else failure).
+
+        To run a test, this function will:
+        - Create, compile, archive, and publish a test package
+        - Run the test package on the emulator
+        - Forward the test's stdout and stderr as this script's stdout and stderr
+        """
+
+        bin_path = os.path.abspath(args.bin_path)
+
+        # Build a unique, deterministic name for the test using the name of the
+        # binary and the last 6 hex digits of the hash of the full path
+        def path_checksum(path):
+            m = hashlib.sha256()
+            m.update(path.encode("utf-8"))
+            return m.hexdigest()[0:6]
+
+        base_name = os.path.basename(os.path.dirname(args.bin_path))
+        exe_name = base_name.lower().replace(".", "_")
+        package_name = f"{exe_name}_{path_checksum(bin_path)}"
+
+        package_dir = os.path.join(self.packages_dir(), package_name)
+        cml_path = os.path.join(package_dir, "meta", f"{package_name}.cml")
+        cm_path = os.path.join(package_dir, "meta", f"{package_name}.cm")
+        manifest_path = os.path.join(package_dir, f"{package_name}.manifest")
+        far_path = os.path.join(package_dir, f"{package_name}-0.far")
+
+        shared_libs = args.shared_libs[: args.n]
+        arguments = args.shared_libs[args.n :]
+
+        test_output_dir = os.path.join(self.output_dir(), package_name)
+
+        # Clean and create temporary output directory
+        if os.path.exists(test_output_dir):
+            shutil.rmtree(test_output_dir)
+
+        os.mkdir(test_output_dir)
+
+        # Open log file
+        log_path = os.path.join(test_output_dir, "log")
+        with open(log_path, "w", encoding="utf-8") as log_file:
+
+            def log(msg):
+                print(msg, file=log_file)
+                log_file.flush()
+
+            log(f"Bin path: {bin_path}")
+
+            log("Setting up package...")
+
+            # Set up package
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-n",
+                    package_name,
+                    "init",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Writing CML...")
+
+            # Write and compile CML
+            with open(cml_path, "w", encoding="utf-8") as cml:
+                # Collect environment variables
+                env_vars = ""
+                for var_name in self.TEST_ENV_VARS:
+                    var_value = os.getenv(var_name)
+                    if var_value is not None:
+                        env_vars += f'\n            "{var_name}={var_value}",'
+
+                # Default to no backtrace for test suite
+                if os.getenv("RUST_BACKTRACE") == None:
+                    env_vars += f'\n            "RUST_BACKTRACE=0",'
+
+                cml.write(
+                    self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name)
+                )
+
+            log("Compiling CML...")
+
+            subprocess.check_call(
+                [
+                    self.tool_path("cmc"),
+                    "compile",
+                    cml_path,
+                    "--includepath",
+                    ".",
+                    "--output",
+                    cm_path,
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Writing manifest...")
+
+            # Write, build, and archive manifest
+            with open(manifest_path, "w", encoding="utf-8") as manifest:
+                manifest.write(
+                    self.MANIFEST_TEMPLATE.format(
+                        bin_path=bin_path,
+                        exe_name=exe_name,
+                        package_dir=package_dir,
+                        package_name=package_name,
+                        rust_dir=self.rust_dir,
+                        rustlib_dir=self.rustlib_dir(),
+                        sdk_dir=self.sdk_dir,
+                        libstd_name=self.libstd_name,
+                        libtest_name=self.libtest_name,
+                        target_arch=self.target_arch,
+                    )
+                )
+                for shared_lib in shared_libs:
+                    manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n")
+
+            log("Compiling and archiving manifest...")
+
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-m",
+                    manifest_path,
+                    "build",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-m",
+                    manifest_path,
+                    "archive",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Publishing package to repo...")
+
+            # Publish package to repo
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "publish",
+                    "-a",
+                    "-repo",
+                    self.repo_dir(),
+                    "-f",
+                    far_path,
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Running ffx test...")
+
+            # Run test on emulator
+            subprocess.run(
+                [
+                    self.tool_path("ffx"),
+                    "--config",
+                    self.ffx_user_config_path(),
+                    "test",
+                    "run",
+                    f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm",
+                    "--min-severity-logs",
+                    "TRACE",
+                    "--output-directory",
+                    test_output_dir,
+                    "--",
+                ]
+                + arguments,
+                env=self.ffx_cmd_env(),
+                check=False,
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Reporting test suite output...")
+
+            # Read test suite output
+            run_summary_path = os.path.join(test_output_dir, "run_summary.json")
+            if os.path.exists(run_summary_path):
+                with open(run_summary_path, encoding="utf-8") as f:
+                    run_summary = json.loads(f.read())
+
+                suite = run_summary["data"]["suites"][0]
+                case = suite["cases"][0]
+
+                return_code = 0 if case["outcome"] == "PASSED" else 1
+
+                artifacts = case["artifacts"]
+                artifact_dir = case["artifact_dir"]
+                stdout_path = None
+                stderr_path = None
+
+                for path, artifact in artifacts.items():
+                    artifact_path = os.path.join(test_output_dir, artifact_dir, path)
+                    artifact_type = artifact["artifact_type"]
+
+                    if artifact_type == "STDERR":
+                        stderr_path = artifact_path
+                    elif artifact_type == "STDOUT":
+                        stdout_path = artifact_path
+
+                if stdout_path is not None and os.path.exists(stdout_path):
+                    with open(stdout_path, encoding="utf-8") as f:
+                        print(f.read(), file=sys.stdout, end="")
+
+                if stderr_path is not None and os.path.exists(stderr_path):
+                    with open(stderr_path, encoding="utf-8") as f:
+                        print(f.read(), file=sys.stderr, end="")
+            else:
+                log("Failed to open test run summary")
+                return_code = 254
+
+            log("Done!")
+
+        return return_code
+
+    def stop(self):
+        """Shuts down and cleans up the testing environment.
+
+        Args:
+        args: The command-line arguments to this command.
+        Returns:
+        The return code of the test (0 for success, else failure).
+
+        During cleanup, this function will stop the emulator, package server, and
+        update server, then delete all temporary files. If an error is encountered
+        while stopping any running processes, the temporary files will not be deleted.
+        Passing --delete-tmp will force the process to delete the files anyway.
+        """
+
+        self.log_debug("Reporting logs...")
+
+        # Print test log files
+        for test_dir in os.listdir(self.output_dir()):
+            log_path = os.path.join(self.output_dir(), test_dir, "log")
+            self.log_debug(f"\n---- Logs for test '{test_dir}' ----\n")
+            if os.path.exists(log_path):
+                with open(log_path, encoding="utf-8") as log:
+                    self.log_debug(log.read())
+            else:
+                self.log_debug("No logs found")
+
+        # Print the emulator log
+        self.log_debug("\n---- Emulator logs ----\n")
+        if os.path.exists(self.emulator_log_path()):
+            with open(self.emulator_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No emulator logs found")
+
+        # Print the package server log
+        self.log_debug("\n---- Package server log ----\n")
+        if os.path.exists(self.package_server_log_path()):
+            with open(self.package_server_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No package server log found")
+
+        # Print the ffx daemon log
+        self.log_debug("\n---- ffx daemon log ----\n")
+        if os.path.exists(self.ffx_daemon_log_path()):
+            with open(self.ffx_daemon_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No ffx daemon log found")
+
+        # Stop package server
+        self.log_info("Stopping package server...")
+        os.kill(self.package_server_pid, signal.SIGTERM)
+
+        # Shut down the emulator
+        self.log_info("Stopping emulator...")
+        subprocess.check_call(
+            [
+                self.tool_path("fvdl"),
+                "--sdk",
+                "kill",
+                "--launched-proto",
+                self.vdl_output_path(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Stop ffx isolation
+        self.log_info("Stopping ffx isolation...")
+        self.stop_ffx_isolation()
+
+    def delete_tmp(self):
+        # Remove temporary files
+        self.log_info("Deleting temporary files...")
+        shutil.rmtree(self.tmp_dir(), ignore_errors=True)
+
+    def debug(self, args):
+        command = [
+            self.tool_path("ffx"),
+            "--config",
+            self.ffx_user_config_path(),
+            "debug",
+            "connect",
+            "--",
+            "--build-id-dir",
+            os.path.join(self.sdk_dir, ".build-id"),
+            "--build-id-dir",
+            os.path.join(self.libs_dir(), ".build-id"),
+        ]
+
+        # Add rust source if it's available
+        if args.rust_src is not None:
+            command += [
+                "--build-dir",
+                args.rust_src,
+            ]
+
+        # Add fuchsia source if it's available
+        if args.fuchsia_src is not None:
+            command += [
+                "--build-dir",
+                os.path.join(args.fuchsia_src, "out", "default"),
+            ]
+
+        # Load debug symbols for the test binary and automatically attach
+        if args.test is not None:
+            if args.rust_src is None:
+                raise Exception(
+                    "A Rust source path is required with the `test` argument"
+                )
+
+            test_name = os.path.splitext(os.path.basename(args.test))[0]
+
+            build_dir = os.path.join(
+                args.rust_src,
+                "fuchsia-build",
+                self.host_arch_triple(),
+            )
+            test_dir = os.path.join(
+                build_dir,
+                "test",
+                os.path.dirname(args.test),
+                test_name,
+            )
+
+            with open(self.zxdb_script_path(), mode="w", encoding="utf-8") as f:
+                print(f"attach {test_name[:31]}", file=f)
+
+            command += [
+                "--symbol-path",
+                test_dir,
+                "-S",
+                self.zxdb_script_path(),
+            ]
+
+        # Add any other zxdb arguments the user passed
+        if args.zxdb_args is not None:
+            command += args.zxdb_args
+
+        # Connect to the running emulator with zxdb
+        subprocess.run(command, env=self.ffx_cmd_env(), check=False)
+
+
+def start(args):
+    test_env = TestEnvironment.from_args(args)
+    test_env.start()
+    return 0
+
+
+def run(args):
+    test_env = TestEnvironment.read_from_file()
+    return test_env.run(args)
+
+
+def stop(args):
+    test_env = TestEnvironment.read_from_file()
+    test_env.stop()
+    if not args.no_delete:
+        test_env.delete_tmp()
+    return 0
+
+
+def delete_tmp(args):
+    del args
+    test_env = TestEnvironment.read_from_file()
+    test_env.delete_tmp()
+    return 0
+
+
+def debug(args):
+    test_env = TestEnvironment.read_from_file()
+    test_env.debug(args)
+    return 0
+
+
+def main():
+    parser = argparse.ArgumentParser()
+
+    def print_help(args):
+        del args
+        parser.print_help()
+        return 0
+
+    parser.set_defaults(func=print_help)
+
+    subparsers = parser.add_subparsers(help="valid sub-commands")
+
+    start_parser = subparsers.add_parser(
+        "start", help="initializes the testing environment"
+    )
+    start_parser.add_argument(
+        "--rust",
+        help="the directory of the installed Rust compiler for Fuchsia",
+        required=True,
+    )
+    start_parser.add_argument(
+        "--sdk",
+        help="the directory of the fuchsia SDK",
+        required=True,
+    )
+    start_parser.add_argument(
+        "--verbose",
+        help="prints more output from executed processes",
+        action="store_true",
+    )
+    start_parser.add_argument(
+        "--target-arch",
+        help="the architecture of the image to test",
+        required=True,
+    )
+    start_parser.set_defaults(func=start)
+
+    run_parser = subparsers.add_parser(
+        "run", help="run a test in the testing environment"
+    )
+    run_parser.add_argument(
+        "n", help="the number of shared libs passed along with the executable", type=int
+    )
+    run_parser.add_argument("bin_path", help="path to the binary to run")
+    run_parser.add_argument(
+        "shared_libs",
+        help="the shared libs passed along with the binary",
+        nargs=argparse.REMAINDER,
+    )
+    run_parser.set_defaults(func=run)
+
+    stop_parser = subparsers.add_parser(
+        "stop", help="shuts down and cleans up the testing environment"
+    )
+    stop_parser.add_argument(
+        "--no-delete",
+        default=False,
+        action="store_true",
+        help="don't delete temporary files after stopping",
+    )
+    stop_parser.set_defaults(func=stop)
+
+    delete_parser = subparsers.add_parser(
+        "delete-tmp",
+        help="deletes temporary files after the testing environment has been manually cleaned up",
+    )
+    delete_parser.set_defaults(func=delete_tmp)
+
+    debug_parser = subparsers.add_parser(
+        "debug",
+        help="connect to the active testing environment with zxdb",
+    )
+    debug_parser.add_argument(
+        "--rust-src",
+        default=None,
+        help="the path to the Rust source being tested",
+    )
+    debug_parser.add_argument(
+        "--fuchsia-src",
+        default=None,
+        help="the path to the Fuchsia source",
+    )
+    debug_parser.add_argument(
+        "--test",
+        default=None,
+        help="the path to the test to debug (e.g. ui/box/new.rs)",
+    )
+    debug_parser.add_argument(
+        "zxdb_args",
+        default=None,
+        nargs=argparse.REMAINDER,
+        help="any additional arguments to pass to zxdb",
+    )
+    debug_parser.set_defaults(func=debug)
+
+    args = parser.parse_args()
+    return args.func(args)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md
index 1ff6003c121cd..62cad19d0ec33 100644
--- a/src/doc/rustc/src/platform-support/fuchsia.md
+++ b/src/doc/rustc/src/platform-support/fuchsia.md
@@ -641,8 +641,60 @@ available on the [Fuchsia devsite].
 
 ### Running the compiler test suite
 
-Running the Rust test suite on Fuchsia is [not currently supported], but work is
-underway to enable it.
+Pre-requisites for running the Rust test suite on Fuchsia are:
+1. Checkout of Rust source.
+1. Setup of `config-env.sh` and `config.toml` from "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)".
+1. Download of the Fuchsia SDK. Minimum supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1)
+
+Interfacing with the Fuchsia emulator is handled by our test runner script located
+at `${RUST_SRC_PATH}/src/ci/docker/scripts/fuchsia-test-runner.py`.
+
+We start by activating our Fuchsia test environment. From a terminal:
+
+**Issue command from ${RUST_SRC_PATH}**
+```sh
+src/ci/docker/scripts/fuchsia-test-runner.py start
+    --rust .
+    --sdk ${SDK_PATH}
+    --target-arch {x64,arm64}
+```
+
+Next, for ease of commands, we copy `config-env.sh` and `config.toml` into our Rust source
+code path, `${RUST_SRC_PATH}`.
+
+From there, we utilize `x.py` to run our tests, using the test runner script to
+run the tests on our emulator. To run the full `src/test/ui` test suite:
+
+**Run from ${RUST_SRC_PATH}**
+```sh
+( \
+    source config-env.sh &&                                                   \
+    ./x.py                                                                    \
+    --config config.toml                                                      \
+    --stage=2                                                                 \
+    test src/test/ui                                                          \
+    --target x86_64-fuchsia                                                   \
+    --run=always --jobs 1                                                     \
+    --test-args --target-rustcflags -L                                        \
+    --test-args --target-rustcflags ${SDK_PATH}/arch/{x64|arm64}/sysroot/lib  \
+    --test-args --target-rustcflags -L                                        \
+    --test-args --target-rustcflags ${SDK_PATH}/arch/{x64|arm64}/lib          \
+    --test-args --target-rustcflags -Cpanic=abort                             \
+    --test-args --target-rustcflags -Zpanic_abort_tests                       \
+    --test-args --remote-test-client                                          \
+    --test-args src/ci/docker/scripts/fuchsia-test-runner.py                  \
+)
+```
+
+*Note: The test suite cannot be run in parallel at the moment, so `x.py`
+must be run with `--jobs 1` to ensure only one test runs at a time.*
+
+When finished, stop the test environment:
+
+**Issue command from ${RUST_SRC_PATH}**
+```sh
+src/ci/docker/scripts/fuchsia-test-runner.py stop
+```
 
 ## Debugging
 

From ce10ac0d6a047d9a537b68b75c89ddc6859c7d4d Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Sun, 13 Nov 2022 11:32:57 +0100
Subject: [PATCH 02/20] remove leading newlines from `NonZero*` doc examples

---
 library/core/src/num/nonzero.rs | 26 ++++++--------------------
 1 file changed, 6 insertions(+), 20 deletions(-)

diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 5b7521220acdb..6a417b54daa93 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -321,7 +321,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -356,7 +355,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -391,8 +389,8 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 /// #![feature(nonzero_ops)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -420,7 +418,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -461,7 +458,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(7).unwrap().ilog2(), 2);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(8).unwrap().ilog2(), 3);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(9).unwrap().ilog2(), 3);")]
@@ -486,7 +482,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(99).unwrap().ilog10(), 1);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(100).unwrap().ilog10(), 2);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(101).unwrap().ilog10(), 2);")]
@@ -526,7 +521,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -556,7 +550,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -591,7 +584,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -626,7 +618,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -662,7 +653,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -905,7 +895,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -941,7 +930,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -986,8 +974,8 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 /// #![feature(nonzero_ops)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -1014,7 +1002,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
@@ -1058,7 +1045,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
@@ -1162,8 +1148,8 @@ macro_rules! nonzero_min_max_unsigned {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), 1", stringify!($Int), ");")]
                 /// ```
                 #[unstable(feature = "nonzero_min_max", issue = "89065")]
@@ -1177,8 +1163,8 @@ macro_rules! nonzero_min_max_unsigned {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
                 /// ```
                 #[unstable(feature = "nonzero_min_max", issue = "89065")]
@@ -1204,8 +1190,8 @@ macro_rules! nonzero_min_max_signed {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), ", stringify!($Int), "::MIN);")]
                 /// ```
                 #[unstable(feature = "nonzero_min_max", issue = "89065")]
@@ -1223,8 +1209,8 @@ macro_rules! nonzero_min_max_signed {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
                 /// ```
                 #[unstable(feature = "nonzero_min_max", issue = "89065")]

From cd2fb430daa487c1a904a2e3dc8494f23dea6df6 Mon Sep 17 00:00:00 2001
From: Ayush Singh <ayushsingh1325@gmail.com>
Date: Sun, 13 Nov 2022 22:20:54 +0530
Subject: [PATCH 03/20] Update compiler-builtins

This was originally a part of https://github.com/rust-lang/rust/pull/100316.
However, extracting it to a seperate PR should help with any extra
testing that might be needed.

Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
---
 library/std/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index daa6976c30143..c10bfde4ddf79 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core" }
 libc = { version = "0.2.135", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.73" }
+compiler_builtins = { version = "0.1.82" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }

From 409c3ce44165d61ad573cacccf3f462ad834d524 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 13 Nov 2022 18:49:21 +0000
Subject: [PATCH 04/20] Remove unused diagnostic items

---
 compiler/rustc_span/src/symbol.rs | 15 ---------------
 library/core/src/fmt/mod.rs       |  1 -
 library/core/src/macros/mod.rs    | 12 ------------
 library/core/src/ops/function.rs  |  6 ------
 library/core/src/panic.rs         |  2 --
 library/std/src/net/ip_addr.rs    |  2 --
 6 files changed, 38 deletions(-)

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b48db73618b0b..4d1d25889f1dd 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -190,9 +190,6 @@ symbols! {
         Error,
         File,
         FileType,
-        Fn,
-        FnMut,
-        FnOnce,
         FormatSpec,
         Formatter,
         From,
@@ -462,7 +459,6 @@ symbols! {
         cfg_doctest,
         cfg_eval,
         cfg_hide,
-        cfg_macro,
         cfg_panic,
         cfg_sanitize,
         cfg_target_abi,
@@ -495,19 +491,16 @@ symbols! {
         cold,
         collapse_debuginfo,
         column,
-        column_macro,
         compare_and_swap,
         compare_exchange,
         compare_exchange_weak,
         compile_error,
-        compile_error_macro,
         compiler,
         compiler_builtins,
         compiler_fence,
         concat,
         concat_bytes,
         concat_idents,
-        concat_macro,
         conservative_impl_trait,
         console,
         const_allocate,
@@ -724,7 +717,6 @@ symbols! {
         field,
         field_init_shorthand,
         file,
-        file_macro,
         fill,
         finish,
         flags,
@@ -826,7 +818,6 @@ symbols! {
         include,
         include_bytes,
         include_bytes_macro,
-        include_macro,
         include_str,
         include_str_macro,
         inclusive_range_syntax,
@@ -881,7 +872,6 @@ symbols! {
         lifetimes,
         likely,
         line,
-        line_macro,
         link,
         link_args,
         link_cfg,
@@ -926,7 +916,6 @@ symbols! {
         masked,
         match_beginning_vert,
         match_default_bindings,
-        matches_macro,
         maxnumf32,
         maxnumf64,
         may_dangle,
@@ -965,7 +954,6 @@ symbols! {
         modifiers,
         module,
         module_path,
-        module_path_macro,
         more_qualified_paths,
         more_struct_aliases,
         movbe_target_feature,
@@ -1103,7 +1091,6 @@ symbols! {
         plugins,
         pointee_trait,
         pointer,
-        pointer_trait_fmt,
         poll,
         position,
         post_dash_lto: "post-lto",
@@ -1449,7 +1436,6 @@ symbols! {
         str_trim_start,
         strict_provenance,
         stringify,
-        stringify_macro,
         struct_field_attributes,
         struct_inherit,
         struct_variant,
@@ -1568,7 +1554,6 @@ symbols! {
         unreachable_2015,
         unreachable_2015_macro,
         unreachable_2021,
-        unreachable_2021_macro,
         unreachable_code,
         unreachable_display,
         unreachable_macro,
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index c8d2855056757..2adc968bd4692 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -1054,7 +1054,6 @@ pub trait UpperHex {
 pub trait Pointer {
     /// Formats the value using the given formatter.
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_diagnostic_item = "pointer_trait_fmt"]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
 
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 32bdc26bc51ad..34247c0584508 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -338,7 +338,6 @@ pub macro debug_assert_matches($($arg:tt)*) {
 /// ```
 #[macro_export]
 #[stable(feature = "matches_macro", since = "1.42.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
 macro_rules! matches {
     ($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {
         match $expression {
@@ -820,7 +819,6 @@ pub(crate) mod builtin {
     #[stable(feature = "compile_error_macro", since = "1.20.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "compile_error_macro")]
     macro_rules! compile_error {
         ($msg:expr $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -944,7 +942,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "env_macro")]
     macro_rules! env {
         ($name:expr $(,)?) => {{ /* compiler built-in */ }};
         ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
@@ -973,7 +970,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "option_env_macro")]
     macro_rules! option_env {
         ($name:expr $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -1058,7 +1054,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "concat_macro")]
     macro_rules! concat {
         ($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -1084,7 +1079,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "line_macro")]
     macro_rules! line {
         () => {
             /* compiler built-in */
@@ -1124,7 +1118,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "column_macro")]
     macro_rules! column {
         () => {
             /* compiler built-in */
@@ -1150,7 +1143,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "file_macro")]
     macro_rules! file {
         () => {
             /* compiler built-in */
@@ -1175,7 +1167,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "stringify_macro")]
     macro_rules! stringify {
         ($($t:tt)*) => {
             /* compiler built-in */
@@ -1282,7 +1273,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "module_path_macro")]
     macro_rules! module_path {
         () => {
             /* compiler built-in */
@@ -1316,7 +1306,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "cfg_macro")]
     macro_rules! cfg {
         ($($cfg:tt)*) => {
             /* compiler built-in */
@@ -1367,7 +1356,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "include_macro")]
     macro_rules! include {
         ($file:expr $(,)?) => {{ /* compiler built-in */ }};
     }
diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs
index 11b43b621c7b8..2bcc9779ec7f2 100644
--- a/library/core/src/ops/function.rs
+++ b/library/core/src/ops/function.rs
@@ -57,7 +57,6 @@ use crate::marker::Tuple;
 #[cfg(bootstrap)]
 #[lang = "fn"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "Fn"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -137,7 +136,6 @@ pub trait Fn<Args>: FnMut<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "Fn"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -226,7 +224,6 @@ pub trait Fn<Args: Tuple>: FnMut<Args> {
 #[cfg(bootstrap)]
 #[lang = "fn_mut"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnMut"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -314,7 +311,6 @@ pub trait FnMut<Args>: FnOnce<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn_mut"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnMut"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -395,7 +391,6 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> {
 #[cfg(bootstrap)]
 #[lang = "fn_once"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnOnce"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -480,7 +475,6 @@ pub trait FnOnce<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn_once"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnOnce"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs
index 00b63dfbd0693..959bdea7dba98 100644
--- a/library/core/src/panic.rs
+++ b/library/core/src/panic.rs
@@ -43,7 +43,6 @@ pub macro panic_2015 {
 #[doc(hidden)]
 #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
 #[allow_internal_unstable(core_panic, const_format_args)]
-#[rustc_diagnostic_item = "core_panic_2021_macro"]
 #[rustc_macro_transparency = "semitransparent"]
 pub macro panic_2021 {
     () => (
@@ -80,7 +79,6 @@ pub macro unreachable_2015 {
 #[doc(hidden)]
 #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
 #[allow_internal_unstable(core_panic)]
-#[rustc_diagnostic_item = "unreachable_2021_macro"]
 #[rustc_macro_transparency = "semitransparent"]
 pub macro unreachable_2021 {
     () => (
diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs
index 4f14fc28038ad..5453853e1381a 100644
--- a/library/std/src/net/ip_addr.rs
+++ b/library/std/src/net/ip_addr.rs
@@ -73,7 +73,6 @@ pub enum IpAddr {
 /// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
 /// ```
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv4Addr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv4Addr {
     octets: [u8; 4],
@@ -156,7 +155,6 @@ pub struct Ipv4Addr {
 /// assert_eq!(localhost.is_loopback(), true);
 /// ```
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv6Addr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv6Addr {
     octets: [u8; 16],

From 4ed2bf68021dac59a9a32537aa983aed8d2c0439 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 13 Nov 2022 20:05:32 +0000
Subject: [PATCH 05/20] Remove unused symbols

---
 compiler/rustc_span/src/symbol.rs | 37 -------------------------------
 1 file changed, 37 deletions(-)

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 4d1d25889f1dd..d136d19431e61 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -170,8 +170,6 @@ symbols! {
         Count,
         Cow,
         Debug,
-        DebugStruct,
-        DebugTuple,
         Decodable,
         Decoder,
         DecorateLint,
@@ -208,7 +206,6 @@ symbols! {
         Input,
         Into,
         IntoDiagnostic,
-        IntoFuture,
         IntoIterator,
         IoRead,
         IoWrite,
@@ -253,7 +250,6 @@ symbols! {
         Pointer,
         Poll,
         ProcMacro,
-        ProcMacroHack,
         ProceduralMasqueradeDummyType,
         Range,
         RangeFrom,
@@ -329,7 +325,6 @@ symbols! {
         abi_vectorcall,
         abi_x86_interrupt,
         abort,
-        aborts,
         add,
         add_assign,
         add_with_overflow,
@@ -341,7 +336,6 @@ symbols! {
         align,
         align_offset,
         alignment,
-        alignstack,
         all,
         alloc,
         alloc_error_handler,
@@ -430,7 +424,6 @@ symbols! {
         bool,
         borrowck_graphviz_format,
         borrowck_graphviz_postflow,
-        borrowck_graphviz_preflow,
         box_free,
         box_patterns,
         box_syntax,
@@ -466,7 +459,6 @@ symbols! {
         cfg_target_feature,
         cfg_target_has_atomic,
         cfg_target_has_atomic_equal_alignment,
-        cfg_target_has_atomic_load_store,
         cfg_target_thread_local,
         cfg_target_vendor,
         cfg_version,
@@ -491,7 +483,6 @@ symbols! {
         cold,
         collapse_debuginfo,
         column,
-        compare_and_swap,
         compare_exchange,
         compare_exchange_weak,
         compile_error,
@@ -521,7 +512,6 @@ symbols! {
         const_fn_unsize,
         const_for,
         const_format_args,
-        const_generic_defaults,
         const_generics,
         const_generics_defaults,
         const_if_match,
@@ -540,20 +530,16 @@ symbols! {
         const_trait,
         const_trait_bound_opt_out,
         const_trait_impl,
-        const_transmute,
         const_try,
         constant,
         constructor,
-        contents,
         context,
-        convert,
         copy,
         copy_closures,
         copy_nonoverlapping,
         copysignf32,
         copysignf64,
         core,
-        core_intrinsics,
         core_panic,
         core_panic_2015_macro,
         core_panic_macro,
@@ -591,7 +577,6 @@ symbols! {
         debug_assertions,
         debug_struct,
         debug_struct_fields_finish,
-        debug_trait_builder,
         debug_tuple,
         debug_tuple_fields_finish,
         debugger_visualizer,
@@ -623,7 +608,6 @@ symbols! {
         discriminant_type,
         discriminant_value,
         dispatch_from_dyn,
-        display_trait,
         div,
         div_assign,
         doc,
@@ -637,7 +621,6 @@ symbols! {
         doc_primitive,
         doc_spotlight,
         doctest,
-        document_private_items,
         dotdot: "..",
         dotdot_in_tuple_patterns,
         dotdoteq_in_patterns,
@@ -654,7 +637,6 @@ symbols! {
         dyn_star,
         dyn_trait,
         e,
-        edition_macro_pats,
         edition_panic,
         eh_catch_typeinfo,
         eh_personality,
@@ -667,7 +649,6 @@ symbols! {
         encode,
         end,
         env,
-        env_macro,
         eprint_macro,
         eprintln_macro,
         eq,
@@ -718,7 +699,6 @@ symbols! {
         field_init_shorthand,
         file,
         fill,
-        finish,
         flags,
         float,
         float_to_int_unchecked,
@@ -727,8 +707,6 @@ symbols! {
         fmaf32,
         fmaf64,
         fmt,
-        fmt_as_str,
-        fmt_internals,
         fmul_fast,
         fn_align,
         fn_must_use,
@@ -743,7 +721,6 @@ symbols! {
         format_args_macro,
         format_args_nl,
         format_macro,
-        fp,
         freeze,
         freg,
         frem_fast,
@@ -806,7 +783,6 @@ symbols! {
         ignore,
         impl_header_lifetime_elision,
         impl_lint_pass,
-        impl_macros,
         impl_trait_in_bindings,
         impl_trait_in_fn_trait_return,
         implied_by,
@@ -835,7 +811,6 @@ symbols! {
         instruction_set,
         integer_: "integer",
         integral,
-        intel,
         into_future,
         into_iter,
         intra_doc_pointers,
@@ -1048,7 +1023,6 @@ symbols! {
         optin_builtin_traits,
         option,
         option_env,
-        option_env_macro,
         options,
         or,
         or_patterns,
@@ -1117,7 +1091,6 @@ symbols! {
         proc_dash_macro: "proc-macro",
         proc_macro,
         proc_macro_attribute,
-        proc_macro_def_site,
         proc_macro_derive,
         proc_macro_expr,
         proc_macro_gen,
@@ -1218,9 +1191,6 @@ symbols! {
         rust_cold_cc,
         rust_eh_catch_typeinfo,
         rust_eh_personality,
-        rust_eh_register_frames,
-        rust_eh_unregister_frames,
-        rust_oom,
         rustc,
         rustc_allocator,
         rustc_allocator_zeroed,
@@ -1293,7 +1263,6 @@ symbols! {
         rustc_serialize,
         rustc_skip_array_during_method_dispatch,
         rustc_specialization_trait,
-        rustc_stable,
         rustc_std_internal_symbol,
         rustc_strict_coherence,
         rustc_symbol_name,
@@ -1421,7 +1390,6 @@ symbols! {
         static_recursion,
         staticlib,
         std,
-        std_inject,
         std_panic,
         std_panic_2015_macro,
         std_panic_macro,
@@ -1463,10 +1431,8 @@ symbols! {
         target_has_atomic_load_store,
         target_os,
         target_pointer_width,
-        target_target_vendor,
         target_thread_local,
         target_vendor,
-        task,
         tbm_target_feature,
         termination,
         termination_trait,
@@ -1478,7 +1444,6 @@ symbols! {
         test_removed_feature,
         test_runner,
         test_unstable_lint,
-        then_with,
         thread,
         thread_local,
         thread_local_macro,
@@ -1510,7 +1475,6 @@ symbols! {
         try_trait_v2,
         tt,
         tuple,
-        tuple_from_req,
         tuple_indexing,
         tuple_trait,
         two_phase,
@@ -1572,7 +1536,6 @@ symbols! {
                           from crates.io via `Cargo.toml` instead?",
         untagged_unions,
         unused_imports,
-        unused_qualifications,
         unwind,
         unwind_attributes,
         unwind_safe_trait,

From 25acee5494db43bd010c7a2615fae71ce905afe7 Mon Sep 17 00:00:00 2001
From: mejrs <>
Date: Sun, 13 Nov 2022 22:48:27 +0100
Subject: [PATCH 06/20] Remove dead NoneError error handling

---
 compiler/rustc_span/src/symbol.rs             |  1 -
 .../src/traits/error_reporting/mod.rs         | 43 ++++---------------
 2 files changed, 8 insertions(+), 36 deletions(-)

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b48db73618b0b..c35bb0cc475d7 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1035,7 +1035,6 @@ symbols! {
         non_exhaustive,
         non_exhaustive_omitted_patterns_lint,
         non_modrs_mods,
-        none_error,
         nontemporal_store,
         noop_method_borrow,
         noop_method_clone,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 98c13ffdafb02..41b252a82651c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -650,41 +650,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 ))
                         );
 
-                        if is_try_conversion {
-                            let none_error = self
-                                .tcx
-                                .get_diagnostic_item(sym::none_error)
-                                .map(|def_id| tcx.type_of(def_id));
-                            let should_convert_option_to_result =
-                                Some(trait_ref.skip_binder().substs.type_at(1)) == none_error;
-                            let should_convert_result_to_option =
-                                Some(trait_ref.self_ty().skip_binder()) == none_error;
-                            if should_convert_option_to_result {
-                                err.span_suggestion_verbose(
-                                    span.shrink_to_lo(),
-                                    "consider converting the `Option<T>` into a `Result<T, _>` \
-                                     using `Option::ok_or` or `Option::ok_or_else`",
-                                    ".ok_or_else(|| /* error value */)",
-                                    Applicability::HasPlaceholders,
-                                );
-                            } else if should_convert_result_to_option {
-                                err.span_suggestion_verbose(
-                                    span.shrink_to_lo(),
-                                    "consider converting the `Result<T, _>` into an `Option<T>` \
-                                     using `Result::ok`",
-                                    ".ok()",
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                            if let Some(ret_span) = self.return_type_span(&obligation) {
-                                err.span_label(
-                                    ret_span,
-                                    &format!(
-                                        "expected `{}` because of this",
-                                        trait_ref.skip_binder().self_ty()
-                                    ),
-                                );
-                            }
+                        if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
+                            err.span_label(
+                                ret_span,
+                                &format!(
+                                    "expected `{}` because of this",
+                                    trait_ref.skip_binder().self_ty()
+                                ),
+                            );
                         }
 
                         if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {

From 2575e4da3b09974efd84aa94ca98555bcd898965 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Sun, 13 Nov 2022 14:56:22 -0700
Subject: [PATCH 07/20] rustdoc: remove unused CSS `code { opacity: 1 }`

According to https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/CSS.20cleanup
this style was added for declarations that no longer use opacity.
---
 src/librustdoc/html/static/css/rustdoc.css | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 3f295b96dc56d..d195c9cf6f9ff 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1262,10 +1262,6 @@ h3.variant {
 	margin-left: 24px;
 }
 
-:target > code, :target > .code-header {
-	opacity: 1;
-}
-
 :target {
 	padding-right: 3px;
 	background-color: var(--target-background-color);

From 29fe28fcfc01f6b850744cc918dbc07a82978755 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 13 Nov 2022 22:58:20 +0000
Subject: [PATCH 08/20] Fix clippy and rustdoc

please, please, don't match on `Symbol::as_str`s, every time you do,
somewhere in the world another waffle becomes sad...
---
 compiler/rustc_span/src/symbol.rs           |  2 ++
 library/core/src/panic.rs                   |  1 +
 src/tools/clippy/clippy_utils/src/macros.rs | 12 ++++++------
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d136d19431e61..58e323d42ce8c 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -542,6 +542,7 @@ symbols! {
         core,
         core_panic,
         core_panic_2015_macro,
+        core_panic_2021_macro,
         core_panic_macro,
         cosf32,
         cosf64,
@@ -621,6 +622,7 @@ symbols! {
         doc_primitive,
         doc_spotlight,
         doctest,
+        document_private_items,
         dotdot: "..",
         dotdot_in_tuple_patterns,
         dotdoteq_in_patterns,
diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs
index 959bdea7dba98..461b70c32f36e 100644
--- a/library/core/src/panic.rs
+++ b/library/core/src/panic.rs
@@ -43,6 +43,7 @@ pub macro panic_2015 {
 #[doc(hidden)]
 #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
 #[allow_internal_unstable(core_panic, const_format_args)]
+#[rustc_diagnostic_item = "core_panic_2021_macro"]
 #[rustc_macro_transparency = "semitransparent"]
 pub macro panic_2021 {
     () => (
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 9a682fbe604ff..d13b34a66cca7 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<
 pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
     let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
     matches!(
-        name.as_str(),
-        "core_panic_macro"
-            | "std_panic_macro"
-            | "core_panic_2015_macro"
-            | "std_panic_2015_macro"
-            | "core_panic_2021_macro"
+        name,
+        sym::core_panic_macro
+            | sym::std_panic_macro
+            | sym::core_panic_2015_macro
+            | sym::std_panic_2015_macro
+            | sym::core_panic_2021_macro
     )
 }
 

From 9985f4611b0d52e7faf6fc68adcdbced786871a9 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Mon, 14 Nov 2022 13:49:08 +1100
Subject: [PATCH 09/20] Clarify `expand_struct_method_body`.

Spotted by @RalfJung. This causes no behavioural changes.
---
 compiler/rustc_builtin_macros/src/deriving/generic/mod.rs | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 16ee3aa89bb12..7b3c73de6de7d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1060,18 +1060,17 @@ impl<'a> MethodDef<'a> {
                 trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true);
             mk_body(cx, selflike_fields)
         } else {
-            // Neither packed nor copy. Need to use ref patterns.
+            // Packed and not copy. Need to use ref patterns.
             let prefixes: Vec<_> =
                 (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
-            let addr_of = always_copy;
+            let addr_of = false;
             let selflike_fields =
                 trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
             let mut body = mk_body(cx, selflike_fields);
 
             let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
-            let by_ref = ByRef::from(is_packed && !always_copy);
             let patterns =
-                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, by_ref);
+                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, ByRef::Yes);
 
             // Do the let-destructuring.
             let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)

From 96280b6a1d39af0d6305cfeb53a2468964e04155 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Mon, 14 Nov 2022 13:59:54 +1100
Subject: [PATCH 10/20] Remove `addr_of` argument from
 `create_struct_pattern_fields`.

Because it's always false.
---
 .../rustc_builtin_macros/src/deriving/generic/mod.rs | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 7b3c73de6de7d..73cf1989341e8 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1063,9 +1063,7 @@ impl<'a> MethodDef<'a> {
             // Packed and not copy. Need to use ref patterns.
             let prefixes: Vec<_> =
                 (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
-            let addr_of = false;
-            let selflike_fields =
-                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
+            let selflike_fields = trait_.create_struct_pattern_fields(cx, struct_def, &prefixes);
             let mut body = mk_body(cx, selflike_fields);
 
             let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
@@ -1251,9 +1249,7 @@ impl<'a> MethodDef<'a> {
                 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
                 // (see "Final wrinkle" note below for why.)
 
-                let addr_of = false; // because enums can't be repr(packed)
-                let fields =
-                    trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of);
+                let fields = trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes);
 
                 let sp = variant.span.with_ctxt(trait_.span.ctxt());
                 let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
@@ -1516,15 +1512,13 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        addr_of: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, _struct_field, sp| {
             prefixes
                 .iter()
                 .map(|prefix| {
                     let ident = self.mk_pattern_ident(prefix, i);
-                    let expr = cx.expr_path(cx.path_ident(sp, ident));
-                    if addr_of { cx.expr_addr_of(sp, expr) } else { expr }
+                    cx.expr_path(cx.path_ident(sp, ident))
                 })
                 .collect()
         })

From 111db7d3a87ff0bfc8753e4d03563de2539dd9da Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Mon, 14 Nov 2022 15:43:10 +1100
Subject: [PATCH 11/20] Remove TraitDef::generics.

Because it's always empty.
---
 compiler/rustc_ast/src/ast.rs                 | 16 +++---
 .../src/deriving/bounds.rs                    |  2 -
 .../src/deriving/clone.rs                     |  1 -
 .../src/deriving/cmp/eq.rs                    |  1 -
 .../src/deriving/cmp/ord.rs                   |  1 -
 .../src/deriving/cmp/partial_eq.rs            |  1 -
 .../src/deriving/cmp/partial_ord.rs           |  1 -
 .../src/deriving/debug.rs                     |  1 -
 .../src/deriving/decodable.rs                 |  1 -
 .../src/deriving/default.rs                   |  1 -
 .../src/deriving/encodable.rs                 |  1 -
 .../src/deriving/generic/mod.rs               | 52 +++++++++----------
 .../rustc_builtin_macros/src/deriving/hash.rs |  1 -
 13 files changed, 33 insertions(+), 47 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4ef43735a62c8..9cd954e3199de 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -392,15 +392,7 @@ pub struct Generics {
 impl Default for Generics {
     /// Creates an instance of `Generics`.
     fn default() -> Generics {
-        Generics {
-            params: Vec::new(),
-            where_clause: WhereClause {
-                has_where_token: false,
-                predicates: Vec::new(),
-                span: DUMMY_SP,
-            },
-            span: DUMMY_SP,
-        }
+        Generics { params: Vec::new(), where_clause: Default::default(), span: DUMMY_SP }
     }
 }
 
@@ -415,6 +407,12 @@ pub struct WhereClause {
     pub span: Span,
 }
 
+impl Default for WhereClause {
+    fn default() -> WhereClause {
+        WhereClause { has_where_token: false, predicates: Vec::new(), span: DUMMY_SP }
+    }
+}
+
 /// A single predicate in a where-clause.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum WherePredicate {
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
index 7bd344467d032..a5aa46aae77a7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
@@ -1,4 +1,3 @@
-use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::path_std;
 
@@ -18,7 +17,6 @@ pub fn expand_deriving_copy(
         path: path_std!(marker::Copy),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: Vec::new(),
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index fa8685f5f4e56..70e28d29dc1a8 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -74,7 +74,6 @@ pub fn expand_deriving_clone(
         path: path_std!(clone::Clone),
         skip_path_as_bound: false,
         additional_bounds: bounds,
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: vec![MethodDef {
             name: sym::clone,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index eab67b0d354cf..4ed32c4274b6a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -27,7 +27,6 @@ pub fn expand_deriving_eq(
         path: path_std!(cmp::Eq),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: vec![MethodDef {
             name: sym::assert_receiver_is_total_eq,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 7f117981a9a2f..7e36389fd168d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -21,7 +21,6 @@ pub fn expand_deriving_ord(
         path: path_std!(cmp::Ord),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::cmp,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index 236cbccaf9fee..53d7b05e59373 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -85,7 +85,6 @@ pub fn expand_deriving_partial_eq(
         path: path_std!(cmp::PartialEq),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods,
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 4173403a1b84a..b619719570c16 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -39,7 +39,6 @@ pub fn expand_deriving_partial_ord(
         path: path_std!(cmp::PartialOrd),
         skip_path_as_bound: false,
         additional_bounds: vec![],
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![partial_cmp_def],
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 2cf614ed9476c..faffb9f8abc5b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -22,7 +22,6 @@ pub fn expand_deriving_debug(
         path: path_std!(fmt::Debug),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::fmt,
diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
index d669f616802fe..71b523ac6aca9 100644
--- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
@@ -25,7 +25,6 @@ pub fn expand_deriving_rustc_decodable(
         path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::decode,
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 17df9fb279ad6..66007e040479e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -26,7 +26,6 @@ pub fn expand_deriving_default(
         path: Path::new(vec![kw::Default, sym::Default]),
         skip_path_as_bound: has_a_default_variant(item),
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: kw::Default,
diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
index f83f58b97d38f..b5ed4bfb1c7a7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
@@ -109,7 +109,6 @@ pub fn expand_deriving_rustc_encodable(
         path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::encode,
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 73cf1989341e8..b873018660e75 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -195,9 +195,6 @@ pub struct TraitDef<'a> {
     /// other than the current trait
     pub additional_bounds: Vec<Ty>,
 
-    /// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder`
-    pub generics: Bounds,
-
     /// Can this trait be derived for unions?
     pub supports_unions: bool,
 
@@ -581,19 +578,21 @@ impl<'a> TraitDef<'a> {
             })
         });
 
-        let Generics { mut params, mut where_clause, .. } =
-            self.generics.to_generics(cx, self.span, type_ident, generics);
+        let mut where_clause = ast::WhereClause::default();
         where_clause.span = generics.where_clause.span;
         let ctxt = self.span.ctxt();
         let span = generics.span.with_ctxt(ctxt);
 
         // Create the generic parameters
-        params.extend(generics.params.iter().map(|param| match &param.kind {
-            GenericParamKind::Lifetime { .. } => param.clone(),
-            GenericParamKind::Type { .. } => {
-                // I don't think this can be moved out of the loop, since
-                // a GenericBound requires an ast id
-                let bounds: Vec<_> =
+        let params: Vec<_> = generics
+            .params
+            .iter()
+            .map(|param| match &param.kind {
+                GenericParamKind::Lifetime { .. } => param.clone(),
+                GenericParamKind::Type { .. } => {
+                    // I don't think this can be moved out of the loop, since
+                    // a GenericBound requires an ast id
+                    let bounds: Vec<_> =
                     // extra restrictions on the generics parameters to the
                     // type being derived upon
                     self.additional_bounds.iter().map(|p| {
@@ -606,21 +605,22 @@ impl<'a> TraitDef<'a> {
                         param.bounds.iter().cloned()
                     ).collect();
 
-                cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
-            }
-            GenericParamKind::Const { ty, kw_span, .. } => {
-                let const_nodefault_kind = GenericParamKind::Const {
-                    ty: ty.clone(),
-                    kw_span: kw_span.with_ctxt(ctxt),
-
-                    // We can't have default values inside impl block
-                    default: None,
-                };
-                let mut param_clone = param.clone();
-                param_clone.kind = const_nodefault_kind;
-                param_clone
-            }
-        }));
+                    cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
+                }
+                GenericParamKind::Const { ty, kw_span, .. } => {
+                    let const_nodefault_kind = GenericParamKind::Const {
+                        ty: ty.clone(),
+                        kw_span: kw_span.with_ctxt(ctxt),
+
+                        // We can't have default values inside impl block
+                        default: None,
+                    };
+                    let mut param_clone = param.clone();
+                    param_clone.kind = const_nodefault_kind;
+                    param_clone
+                }
+            })
+            .collect();
 
         // and similarly for where clauses
         where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 6e9d5f08b9443..53b6620efd6c0 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -24,7 +24,6 @@ pub fn expand_deriving_hash(
         path,
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::hash,

From a9e4176bc868bd0d5b1f9a4dcff5aed5c22ad3f8 Mon Sep 17 00:00:00 2001
From: koka <koka.code@gmail.com>
Date: Mon, 14 Nov 2022 22:55:50 +0900
Subject: [PATCH 12/20] Specify language of code comment to generate document

---
 compiler/rustc_lint/src/let_underscore.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
index 78f355ec3d0ae..c8939256bbbf6 100644
--- a/compiler/rustc_lint/src/let_underscore.rs
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -11,7 +11,8 @@ declare_lint! {
     /// scope.
     ///
     /// ### Example
-    /// ```
+    ///
+    /// ```rust
     /// struct SomeStruct;
     /// impl Drop for SomeStruct {
     ///     fn drop(&mut self) {

From e6baae576c9b474d1946a1d0f98692c7a3e62549 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Mon, 14 Nov 2022 15:14:54 +0100
Subject: [PATCH 13/20] Fix missing minification for static files

---
 src/librustdoc/html/static_files.rs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index afe920b7fa1e0..b4d4150cddbb8 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -19,9 +19,13 @@ impl StaticFile {
     }
 
     pub(crate) fn minified(&self) -> Vec<u8> {
-        if self.filename.ends_with(".css") {
+        let extension = match self.filename.extension() {
+            Some(e) => e,
+            None => return self.bytes.to_owned(),
+        };
+        if extension == "css" {
             minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into()
-        } else if self.filename.ends_with(".js") {
+        } else if extension == "js" {
             minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into()
         } else {
             self.bytes.to_owned()

From 212ce1f10cba1a1c9abd856664dfa5c0a47a59bc Mon Sep 17 00:00:00 2001
From: Krasimir Georgiev <krasimir@google.com>
Date: Mon, 14 Nov 2022 14:59:32 +0000
Subject: [PATCH 14/20] [llvm-wrapper] adapt for LLVM API change

---
 compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 0d9b5a57b69a2..85c520a7911f3 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -9,7 +9,7 @@
 #include "llvm/IR/IntrinsicsARM.h"
 #include "llvm/IR/Mangler.h"
 #if LLVM_VERSION_GE(16, 0)
-#include "llvm/IR/ModRef.h"
+#include "llvm/Support/ModRef.h"
 #endif
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/COFFImportFile.h"

From 3f697b85f2196ea7d2a5d4fa1f88c39e63871d39 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 14 Nov 2022 17:25:37 +0000
Subject: [PATCH 15/20] Drive-by: actual -> rcvr_ty

---
 .../rustc_hir_typeck/src/method/suggest.rs    | 58 ++++++++++---------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 43a5145b7e74d..94df6817bf526 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -263,15 +263,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }) => {
                 let tcx = self.tcx;
 
-                let actual = self.resolve_vars_if_possible(rcvr_ty);
-                let ty_str = self.ty_to_string(actual);
+                let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
+                let ty_str = self.ty_to_string(rcvr_ty);
                 let is_method = mode == Mode::MethodCall;
                 let item_kind = if is_method {
                     "method"
-                } else if actual.is_enum() {
+                } else if rcvr_ty.is_enum() {
                     "variant or associated item"
                 } else {
-                    match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
+                    match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) {
                         (Some(name), false) if name.is_lowercase() => "function or associated item",
                         (Some(_), false) => "associated item",
                         (Some(_), true) | (None, false) => "variant or associated item",
@@ -280,9 +280,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 };
 
                 if self.suggest_wrapping_range_with_parens(
-                    tcx, actual, source, span, item_name, &ty_str,
+                    tcx, rcvr_ty, source, span, item_name, &ty_str,
                 ) || self.suggest_constraining_numerical_ty(
-                    tcx, actual, source, span, item_kind, item_name, &ty_str,
+                    tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str,
                 ) {
                     return None;
                 }
@@ -290,9 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Don't show generic arguments when the method can't be found in any implementation (#81576).
                 let mut ty_str_reported = ty_str.clone();
-                if let ty::Adt(_, generics) = actual.kind() {
+                if let ty::Adt(_, generics) = rcvr_ty.kind() {
                     if generics.len() > 0 {
-                        let mut autoderef = self.autoderef(span, actual);
+                        let mut autoderef = self.autoderef(span, rcvr_ty);
                         let candidate_found = autoderef.any(|(ty, _)| {
                             if let ty::Adt(adt_def, _) = ty.kind() {
                                 self.tcx
@@ -321,16 +321,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     "no {} named `{}` found for {} `{}` in the current scope",
                     item_kind,
                     item_name,
-                    actual.prefix_string(self.tcx),
+                    rcvr_ty.prefix_string(self.tcx),
                     ty_str_reported,
                 );
-                if actual.references_error() {
+                if rcvr_ty.references_error() {
                     err.downgrade_to_delayed_bug();
                 }
 
                 if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
                     self.suggest_await_before_method(
-                        &mut err, item_name, actual, cal, span,
+                        &mut err, item_name, rcvr_ty, cal, span,
                     );
                 }
                 if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
@@ -341,7 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Applicability::MachineApplicable,
                     );
                 }
-                if let ty::RawPtr(_) = &actual.kind() {
+                if let ty::RawPtr(_) = &rcvr_ty.kind() {
                     err.note(
                         "try using `<*const T>::as_ref()` to get a reference to the \
                          type behind the pointer: https://doc.rust-lang.org/std/\
@@ -353,7 +353,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
 
-                let ty_span = match actual.kind() {
+                let ty_span = match rcvr_ty.kind() {
                     ty::Param(param_type) => Some(
                         param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()),
                     ),
@@ -365,7 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         span,
                         format!(
                             "{item_kind} `{item_name}` not found for this {}",
-                            actual.prefix_string(self.tcx)
+                            rcvr_ty.prefix_string(self.tcx)
                         ),
                     );
                 }
@@ -406,7 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // original type that has the associated function for accurate suggestions.
                         // (#61411)
                         let ty = tcx.at(span).type_of(*impl_did);
-                        match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
+                        match (&ty.peel_refs().kind(), &rcvr_ty.peel_refs().kind()) {
                             (ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
                                 // If there are any inferred arguments, (`{integer}`), we should replace
                                 // them with underscores to allow the compiler to infer them
@@ -451,7 +451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             _ => self.ty_to_value_string(ty.peel_refs()),
                         }
                     } else {
-                        self.ty_to_value_string(actual.peel_refs())
+                        self.ty_to_value_string(rcvr_ty.peel_refs())
                     };
                     if let SelfSource::MethodCall(_) = source {
                         let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) &&
@@ -523,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let mut bound_spans = vec![];
                 let mut restrict_type_params = false;
                 let mut unsatisfied_bounds = false;
-                if item_name.name == sym::count && self.is_slice_ty(actual, span) {
+                if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
                     let msg = "consider using `len` instead";
                     if let SelfSource::MethodCall(_expr) = source {
                         err.span_suggestion_short(
@@ -537,7 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
                         let iterator_trait = self.tcx.def_path_str(iterator_trait);
-                        err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
+                        err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"));
                     }
                 } else if !unsatisfied_predicates.is_empty() {
                     let mut type_params = FxHashMap::default();
@@ -876,7 +876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .map(|(_, path)| path)
                             .collect::<Vec<_>>()
                             .join("\n");
-                        let actual_prefix = actual.prefix_string(self.tcx);
+                        let actual_prefix = rcvr_ty.prefix_string(self.tcx);
                         info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
                         let (primary_message, label) =
                             if unimplemented_traits.len() == 1 && unimplemented_traits_only {
@@ -885,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     .next()
                                     .map(|(_, (trait_ref, obligation))| {
                                         if trait_ref.self_ty().references_error()
-                                            || actual.references_error()
+                                            || rcvr_ty.references_error()
                                         {
                                             // Avoid crashing.
                                             return (None, None);
@@ -921,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let label_span_not_found = |err: &mut Diagnostic| {
                     if unsatisfied_predicates.is_empty() {
                         err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
-                        let is_string_or_ref_str = match actual.kind() {
+                        let is_string_or_ref_str = match rcvr_ty.kind() {
                             ty::Ref(_, ty, _) => {
                                 ty.is_str()
                                     || matches!(
@@ -957,7 +957,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                 // different from the received one
                                                 // So we avoid suggestion method with Box<Self>
                                                 // for instance
-                                                self.tcx.at(span).type_of(*def_id) != actual
+                                                self.tcx.at(span).type_of(*def_id) != rcvr_ty
                                                     && self.tcx.at(span).type_of(*def_id) != rcvr_ty
                                             }
                                             (Mode::Path, false, _) => true,
@@ -1017,10 +1017,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
                 // can't be called due to `typeof(expr): Clone` not holding.
                 if unsatisfied_predicates.is_empty() {
-                    self.suggest_calling_method_on_field(&mut err, source, span, actual, item_name);
+                    self.suggest_calling_method_on_field(
+                        &mut err, source, span, rcvr_ty, item_name,
+                    );
                 }
 
-                self.check_for_inner_self(&mut err, source, span, actual, item_name);
+                self.check_for_inner_self(&mut err, source, span, rcvr_ty, item_name);
 
                 bound_spans.sort();
                 bound_spans.dedup();
@@ -1028,7 +1030,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     err.span_label(span, &msg);
                 }
 
-                if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
+                if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
                 } else {
                     self.suggest_traits_to_import(
                         &mut err,
@@ -1046,8 +1048,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Don't emit a suggestion if we found an actual method
                 // that had unsatisfied trait bounds
-                if unsatisfied_predicates.is_empty() && actual.is_enum() {
-                    let adt_def = actual.ty_adt_def().expect("enum is not an ADT");
+                if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
+                    let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
                     if let Some(suggestion) = lev_distance::find_best_match_for_name(
                         &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
                         item_name.name,
@@ -1062,7 +1064,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
 
-                if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+                if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
                     let msg = "remove this method call";
                     let mut fallback_span = true;
                     if let SelfSource::MethodCall(expr) = source {

From bf607dae3f555f714c814a03478be560ae1c37e8 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 14 Nov 2022 18:40:20 +0000
Subject: [PATCH 16/20] Move logic into method

---
 .../rustc_hir_typeck/src/method/suggest.rs    | 256 ++++++++++--------
 1 file changed, 140 insertions(+), 116 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 94df6817bf526..821492179136b 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -398,122 +398,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     custom_span_label = true;
                 }
                 if static_candidates.len() == 1 {
-                    let mut has_unsuggestable_args = false;
-                    let ty_str = if let Some(CandidateSource::Impl(impl_did)) =
-                        static_candidates.get(0)
-                    {
-                        // When the "method" is resolved through dereferencing, we really want the
-                        // original type that has the associated function for accurate suggestions.
-                        // (#61411)
-                        let ty = tcx.at(span).type_of(*impl_did);
-                        match (&ty.peel_refs().kind(), &rcvr_ty.peel_refs().kind()) {
-                            (ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
-                                // If there are any inferred arguments, (`{integer}`), we should replace
-                                // them with underscores to allow the compiler to infer them
-                                let infer_substs: Vec<GenericArg<'_>> = substs
-                                    .into_iter()
-                                    .map(|arg| {
-                                        if !arg.is_suggestable(tcx, true) {
-                                            has_unsuggestable_args = true;
-                                            match arg.unpack() {
-                                            GenericArgKind::Lifetime(_) => self
-                                                .next_region_var(RegionVariableOrigin::MiscVariable(
-                                                    rustc_span::DUMMY_SP,
-                                                ))
-                                                .into(),
-                                            GenericArgKind::Type(_) => self
-                                                .next_ty_var(TypeVariableOrigin {
-                                                    span: rustc_span::DUMMY_SP,
-                                                    kind: TypeVariableOriginKind::MiscVariable,
-                                                })
-                                                .into(),
-                                            GenericArgKind::Const(arg) => self
-                                                .next_const_var(
-                                                    arg.ty(),
-                                                    ConstVariableOrigin {
-                                                        span: rustc_span::DUMMY_SP,
-                                                        kind: ConstVariableOriginKind::MiscVariable,
-                                                    },
-                                                )
-                                                .into(),
-                                            }
-                                        } else {
-                                            arg
-                                        }
-                                    })
-                                    .collect::<Vec<_>>();
-
-                                tcx.value_path_str_with_substs(
-                                    def_actual.did(),
-                                    tcx.intern_substs(&infer_substs),
-                                )
-                            }
-                            _ => self.ty_to_value_string(ty.peel_refs()),
-                        }
-                    } else {
-                        self.ty_to_value_string(rcvr_ty.peel_refs())
-                    };
-                    if let SelfSource::MethodCall(_) = source {
-                        let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) &&
-                            let Some(assoc) = self.associated_value(*impl_did, item_name) {
-                            let sig = self.tcx.fn_sig(assoc.def_id);
-                            if let Some(first) = sig.inputs().skip_binder().get(0) {
-                                if first.peel_refs() == rcvr_ty.peel_refs() {
-                                    None
-                                } else {
-                                    Some(if first.is_region_ptr() {
-                                        if first.is_mutable_ptr() { "&mut " } else { "&" }
-                                    } else {
-                                        ""
-                                    })
-                                }
-                            } else {
-                                None
-                            }
-                        } else {
-                            None
-                        };
-                        let mut applicability = Applicability::MachineApplicable;
-                        let args = if let Some((receiver, args)) = args {
-                            // The first arg is the same kind as the receiver
-                            let explicit_args = if first_arg.is_some() {
-                                std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
-                            } else {
-                                // There is no `Self` kind to infer the arguments from
-                                if has_unsuggestable_args {
-                                    applicability = Applicability::HasPlaceholders;
-                                }
-                                args.iter().collect()
-                            };
-                            format!(
-                                "({}{})",
-                                first_arg.unwrap_or(""),
-                                explicit_args
-                                    .iter()
-                                    .map(|arg| tcx
-                                        .sess
-                                        .source_map()
-                                        .span_to_snippet(arg.span)
-                                        .unwrap_or_else(|_| {
-                                            applicability = Applicability::HasPlaceholders;
-                                            "_".to_owned()
-                                        }))
-                                    .collect::<Vec<_>>()
-                                    .join(", "),
-                            )
-                        } else {
-                            applicability = Applicability::HasPlaceholders;
-                            "(...)".to_owned()
-                        };
-                        err.span_suggestion(
-                            sugg_span,
-                            "use associated function syntax instead",
-                            format!("{}::{}{}", ty_str, item_name, args),
-                            applicability,
-                        );
-                    } else {
-                        err.help(&format!("try with `{}::{}`", ty_str, item_name,));
-                    }
+                    self.suggest_associated_call_syntax(
+                        &mut err,
+                        &static_candidates,
+                        rcvr_ty,
+                        source,
+                        item_name,
+                        args,
+                        sugg_span,
+                    );
 
                     report_candidates(span, &mut err, &mut static_candidates, sugg_span);
                 } else if static_candidates.len() > 1 {
@@ -1180,6 +1073,137 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    /// Suggest calling `Ty::method` if `.method()` isn't found because the method
+    /// doesn't take a `self` receiver.
+    fn suggest_associated_call_syntax(
+        &self,
+        err: &mut Diagnostic,
+        static_candidates: &Vec<CandidateSource>,
+        rcvr_ty: Ty<'tcx>,
+        source: SelfSource<'tcx>,
+        item_name: Ident,
+        args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
+        sugg_span: Span,
+    ) {
+        let mut has_unsuggestable_args = false;
+        let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) {
+            // When the "method" is resolved through dereferencing, we really want the
+            // original type that has the associated function for accurate suggestions.
+            // (#61411)
+            let ty = self.tcx.type_of(*impl_did);
+            match (&ty.peel_refs().kind(), &rcvr_ty.peel_refs().kind()) {
+                (ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
+                    // If there are any inferred arguments, (`{integer}`), we should replace
+                    // them with underscores to allow the compiler to infer them
+                    let infer_substs: Vec<GenericArg<'_>> = substs
+                        .into_iter()
+                        .map(|arg| {
+                            if !arg.is_suggestable(self.tcx, true) {
+                                has_unsuggestable_args = true;
+                                match arg.unpack() {
+                                    GenericArgKind::Lifetime(_) => self
+                                        .next_region_var(RegionVariableOrigin::MiscVariable(
+                                            rustc_span::DUMMY_SP,
+                                        ))
+                                        .into(),
+                                    GenericArgKind::Type(_) => self
+                                        .next_ty_var(TypeVariableOrigin {
+                                            span: rustc_span::DUMMY_SP,
+                                            kind: TypeVariableOriginKind::MiscVariable,
+                                        })
+                                        .into(),
+                                    GenericArgKind::Const(arg) => self
+                                        .next_const_var(
+                                            arg.ty(),
+                                            ConstVariableOrigin {
+                                                span: rustc_span::DUMMY_SP,
+                                                kind: ConstVariableOriginKind::MiscVariable,
+                                            },
+                                        )
+                                        .into(),
+                                }
+                            } else {
+                                arg
+                            }
+                        })
+                        .collect::<Vec<_>>();
+
+                    self.tcx.value_path_str_with_substs(
+                        def_actual.did(),
+                        self.tcx.intern_substs(&infer_substs),
+                    )
+                }
+                _ => self.ty_to_value_string(ty.peel_refs()),
+            }
+        } else {
+            self.ty_to_value_string(rcvr_ty.peel_refs())
+        };
+        if let SelfSource::MethodCall(_) = source {
+            let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0)
+                && let Some(assoc) = self.associated_value(*impl_did, item_name)
+                && assoc.kind == ty::AssocKind::Fn
+            {
+                let sig = self.tcx.fn_sig(assoc.def_id);
+                if let Some(first) = sig.inputs().skip_binder().get(0) {
+                    if first.peel_refs() == rcvr_ty.peel_refs() {
+                        None
+                    } else {
+                        Some(if first.is_region_ptr() {
+                            if first.is_mutable_ptr() { "&mut " } else { "&" }
+                        } else {
+                            ""
+                        })
+                    }
+                } else {
+                    None
+                }
+            } else {
+                None
+            };
+            let mut applicability = Applicability::MachineApplicable;
+            let args = if let Some((receiver, args)) = args {
+                // The first arg is the same kind as the receiver
+                let explicit_args = if first_arg.is_some() {
+                    std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
+                } else {
+                    // There is no `Self` kind to infer the arguments from
+                    if has_unsuggestable_args {
+                        applicability = Applicability::HasPlaceholders;
+                    }
+                    args.iter().collect()
+                };
+                format!(
+                    "({}{})",
+                    first_arg.unwrap_or(""),
+                    explicit_args
+                        .iter()
+                        .map(|arg| self
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_to_snippet(arg.span)
+                            .unwrap_or_else(|_| {
+                                applicability = Applicability::HasPlaceholders;
+                                "_".to_owned()
+                            }))
+                        .collect::<Vec<_>>()
+                        .join(", "),
+                )
+            } else {
+                applicability = Applicability::HasPlaceholders;
+                "(...)".to_owned()
+            };
+            err.span_suggestion(
+                sugg_span,
+                "use associated function syntax instead",
+                format!("{}::{}{}", ty_str, item_name, args),
+                applicability,
+            );
+        } else {
+            err.help(&format!("try with `{}::{}`", ty_str, item_name,));
+        }
+    }
+
     /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with
     /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`.
     fn suggest_calling_field_as_fn(

From 5497317aa542c490b1540edf5907dd29739bcecb Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 14 Nov 2022 19:18:38 +0000
Subject: [PATCH 17/20] Do autoderef to match impl against rcvr

---
 .../rustc_hir_typeck/src/method/suggest.rs    | 92 +++++++++----------
 .../suggest-assoc-fn-call-deref.fixed         | 15 +++
 .../suggest-assoc-fn-call-deref.rs            | 15 +++
 .../suggest-assoc-fn-call-deref.stderr        | 19 ++++
 4 files changed, 95 insertions(+), 46 deletions(-)
 create mode 100644 src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed
 create mode 100644 src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs
 create mode 100644 src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 821492179136b..edfe12963dc63 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -20,11 +20,10 @@ use rustc_infer::infer::{
 };
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::traits::util::supertraits;
+use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::print::with_crate_prefix;
-use rustc_middle::ty::{
-    self, DefIdTree, GenericArg, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable,
-};
+use rustc_middle::ty::{self, DefIdTree, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable};
 use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
@@ -1090,50 +1089,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // When the "method" is resolved through dereferencing, we really want the
             // original type that has the associated function for accurate suggestions.
             // (#61411)
-            let ty = self.tcx.type_of(*impl_did);
-            match (&ty.peel_refs().kind(), &rcvr_ty.peel_refs().kind()) {
-                (ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
-                    // If there are any inferred arguments, (`{integer}`), we should replace
-                    // them with underscores to allow the compiler to infer them
-                    let infer_substs: Vec<GenericArg<'_>> = substs
-                        .into_iter()
-                        .map(|arg| {
-                            if !arg.is_suggestable(self.tcx, true) {
-                                has_unsuggestable_args = true;
-                                match arg.unpack() {
-                                    GenericArgKind::Lifetime(_) => self
-                                        .next_region_var(RegionVariableOrigin::MiscVariable(
-                                            rustc_span::DUMMY_SP,
-                                        ))
-                                        .into(),
-                                    GenericArgKind::Type(_) => self
-                                        .next_ty_var(TypeVariableOrigin {
-                                            span: rustc_span::DUMMY_SP,
-                                            kind: TypeVariableOriginKind::MiscVariable,
-                                        })
-                                        .into(),
-                                    GenericArgKind::Const(arg) => self
-                                        .next_const_var(
-                                            arg.ty(),
-                                            ConstVariableOrigin {
-                                                span: rustc_span::DUMMY_SP,
-                                                kind: ConstVariableOriginKind::MiscVariable,
-                                            },
-                                        )
-                                        .into(),
-                                }
-                            } else {
-                                arg
-                            }
-                        })
-                        .collect::<Vec<_>>();
+            let impl_ty = self.tcx.type_of(*impl_did);
+            let target_ty = self
+                .autoderef(sugg_span, rcvr_ty)
+                .find(|(rcvr_ty, _)| {
+                    DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }
+                        .types_may_unify(*rcvr_ty, impl_ty)
+                })
+                .map_or(impl_ty, |(ty, _)| ty)
+                .peel_refs();
+            if let ty::Adt(def, substs) = target_ty.kind() {
+                // If there are any inferred arguments, (`{integer}`), we should replace
+                // them with underscores to allow the compiler to infer them
+                let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| {
+                    if !arg.is_suggestable(self.tcx, true) {
+                        has_unsuggestable_args = true;
+                        match arg.unpack() {
+                            GenericArgKind::Lifetime(_) => self
+                                .next_region_var(RegionVariableOrigin::MiscVariable(
+                                    rustc_span::DUMMY_SP,
+                                ))
+                                .into(),
+                            GenericArgKind::Type(_) => self
+                                .next_ty_var(TypeVariableOrigin {
+                                    span: rustc_span::DUMMY_SP,
+                                    kind: TypeVariableOriginKind::MiscVariable,
+                                })
+                                .into(),
+                            GenericArgKind::Const(arg) => self
+                                .next_const_var(
+                                    arg.ty(),
+                                    ConstVariableOrigin {
+                                        span: rustc_span::DUMMY_SP,
+                                        kind: ConstVariableOriginKind::MiscVariable,
+                                    },
+                                )
+                                .into(),
+                        }
+                    } else {
+                        arg
+                    }
+                }));
 
-                    self.tcx.value_path_str_with_substs(
-                        def_actual.did(),
-                        self.tcx.intern_substs(&infer_substs),
-                    )
-                }
-                _ => self.ty_to_value_string(ty.peel_refs()),
+                self.tcx.value_path_str_with_substs(def.did(), infer_substs)
+            } else {
+                self.ty_to_value_string(target_ty)
             }
         } else {
             self.ty_to_value_string(rcvr_ty.peel_refs())
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed
new file mode 100644
index 0000000000000..8d96cf590c398
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo<T>(T);
+
+impl<T> Foo<T> {
+    fn test() -> i32 { 1 }
+}
+
+fn main() {
+    let x = Box::new(Foo(1i32));
+    Foo::<i32>::test();
+    //~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+}
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs
new file mode 100644
index 0000000000000..186901f75a84b
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo<T>(T);
+
+impl<T> Foo<T> {
+    fn test() -> i32 { 1 }
+}
+
+fn main() {
+    let x = Box::new(Foo(1i32));
+    x.test();
+    //~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+}
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr
new file mode 100644
index 0000000000000..00fb96f032668
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr
@@ -0,0 +1,19 @@
+error[E0599]: no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+  --> $DIR/suggest-assoc-fn-call-deref.rs:13:7
+   |
+LL |     x.test();
+   |     --^^^^--
+   |     | |
+   |     | this is an associated function, not a method
+   |     help: use associated function syntax instead: `Foo::<i32>::test()`
+   |
+   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+note: the candidate is defined in an impl for the type `Foo<T>`
+  --> $DIR/suggest-assoc-fn-call-deref.rs:8:5
+   |
+LL |     fn test() -> i32 { 1 }
+   |     ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.

From 540e12f7daddec4581c5ff0f89c0172cc5005422 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 14 Nov 2022 19:29:17 +0000
Subject: [PATCH 18/20] Add regression test

---
 .../ui/suggestions/dont-suggest-ufcs-for-const.rs |  4 ++++
 .../dont-suggest-ufcs-for-const.stderr            | 15 +++++++++++++++
 2 files changed, 19 insertions(+)
 create mode 100644 src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs
 create mode 100644 src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr

diff --git a/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs
new file mode 100644
index 0000000000000..06cf243f1b4fa
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs
@@ -0,0 +1,4 @@
+fn main() {
+    1_u32.MAX();
+    //~^ ERROR no method named `MAX` found for type `u32` in the current scope
+}
diff --git a/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr
new file mode 100644
index 0000000000000..04e0511d788ed
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr
@@ -0,0 +1,15 @@
+error[E0599]: no method named `MAX` found for type `u32` in the current scope
+  --> $DIR/dont-suggest-ufcs-for-const.rs:2:11
+   |
+LL |     1_u32.MAX();
+   |     ------^^^--
+   |     |     |
+   |     |     this is an associated function, not a method
+   |     help: use associated function syntax instead: `u32::MAX()`
+   |
+   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+   = note: the candidate is defined in an impl for the type `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.

From 7b00534952c9d2685a960e2d501cc4eefab6c075 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Mon, 14 Nov 2022 10:41:07 -0700
Subject: [PATCH 19/20] rustdoc: fix corner case in search keyboard commands

---
 src/librustdoc/html/static/js/search.js   |  1 +
 src/test/rustdoc-gui/search-keyboard.goml | 28 +++++++++++++++++++++++
 2 files changed, 29 insertions(+)
 create mode 100644 src/test/rustdoc-gui/search-keyboard.goml

diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index dd0531c5e70e4..4999bb3599487 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1491,6 +1491,7 @@ function initSearch(rawSearchIndex) {
         const target = searchState.focusedByTab[searchState.currentTab] ||
             document.querySelectorAll(".search-results.active a").item(0) ||
             document.querySelectorAll("#titles > button").item(searchState.currentTab);
+        searchState.focusedByTab[searchState.currentTab] = null;
         if (target) {
             target.focus();
         }
diff --git a/src/test/rustdoc-gui/search-keyboard.goml b/src/test/rustdoc-gui/search-keyboard.goml
new file mode 100644
index 0000000000000..be642fc49971f
--- /dev/null
+++ b/src/test/rustdoc-gui/search-keyboard.goml
@@ -0,0 +1,28 @@
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "Foo")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#titles"
+
+// Now use the keyboard commands to switch to the third result.
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(3)"
+
+// Now switch to the second tab, then back to the first one, then arrow back up.
+press-key: "ArrowRight"
+assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)"
+press-key: "ArrowLeft"
+assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(2)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(1)"
+press-key: "ArrowUp"
+assert: ".search-input:focus"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(1)"

From 9214673331295f249c75d51f16f8b48bf6ca96c1 Mon Sep 17 00:00:00 2001
From: Nixon Enraght-Moony <nixon.emoony@gmail.com>
Date: Mon, 14 Nov 2022 22:42:57 +0000
Subject: [PATCH 20/20] Add test for #102154

---
 src/test/rustdoc/issue-102154.rs | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 src/test/rustdoc/issue-102154.rs

diff --git a/src/test/rustdoc/issue-102154.rs b/src/test/rustdoc/issue-102154.rs
new file mode 100644
index 0000000000000..b36f270806ff3
--- /dev/null
+++ b/src/test/rustdoc/issue-102154.rs
@@ -0,0 +1,13 @@
+trait A<Y, N> {
+    type B;
+}
+type MaybeBox<T> = <T as A<T, Box<T>>>::B;
+struct P {
+    t: MaybeBox<P>
+}
+impl<Y, N> A<Y, N> for P {
+    type B = N;
+}
+fn main() {
+    let t: MaybeBox<P>;
+}