diff --git a/dev-tools/cc-test/build.rs b/dev-tools/cc-test/build.rs
index 4adb870c..ea60d3ab 100644
--- a/dev-tools/cc-test/build.rs
+++ b/dev-tools/cc-test/build.rs
@@ -23,7 +23,10 @@ fn main() {
     run_forked_capture_output(&out, "metadata-off");
 
     run_forked_capture_output(&out, "warnings-off");
-    if cc::Build::new().get_compiler().is_like_msvc() {
+    if matches!(
+        cc::Build::new().get_compiler().family(),
+        cc::ToolFamily::Msvc { .. }
+    ) {
         // MSVC doesn't output warnings to stderr, so we can't capture them.
         // the test will use this env var to know whether to run the test.
         println!("cargo:rustc-env=TEST_WARNINGS_ON=0");
@@ -86,12 +89,12 @@ fn main() {
     }
 
     if target.contains("msvc") {
-        let cc_frontend = if compiler.is_like_msvc() {
-            "MSVC"
-        } else if compiler.is_like_clang() {
-            "CLANG"
-        } else {
-            unimplemented!("Unknown compiler that targets msvc but isn't clang-like or msvc-like")
+        let cc_frontend = match compiler.family() {
+            cc::ToolFamily::Clang { .. } => "CLANG",
+            cc::ToolFamily::Msvc { .. } => "MSVC",
+            f => unimplemented!(
+                "Unknown compiler `{f:?}` that targets msvc but isn't clang-like or msvc-like"
+            ),
         };
 
         // Test that the `windows_registry` module will set PATH by looking for
diff --git a/src/lib.rs b/src/lib.rs
index 280ff7bb..e8d8bc47 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -253,8 +253,7 @@ mod command_helpers;
 use command_helpers::*;
 
 mod tool;
-pub use tool::Tool;
-use tool::ToolFamily;
+pub use tool::{Tool, ToolFamily};
 
 mod tempfile;
 
@@ -701,7 +700,8 @@ impl Build {
         if compiler.family.verbose_stderr() {
             compiler.remove_arg("-v".into());
         }
-        if compiler.is_like_clang() {
+        let clang = matches!(compiler.family(), ToolFamily::Clang { .. });
+        if clang {
             // Avoid reporting that the arg is unsupported just because the
             // compiler complains that it wasn't used.
             compiler.push_cc_arg("-Wno-unused-command-line-argument".into());
@@ -709,17 +709,16 @@ impl Build {
 
         let mut cmd = compiler.to_command();
         let is_arm = matches!(target.arch, "aarch64" | "arm");
-        let clang = compiler.is_like_clang();
-        let gnu = compiler.family == ToolFamily::Gnu;
+        let msvc = matches!(compiler.family(), ToolFamily::Msvc { .. });
         command_add_output_file(
             &mut cmd,
             &obj,
             CmdAddOutputFileArgs {
                 cuda: self.cuda,
                 is_assembler_msvc: false,
-                msvc: compiler.is_like_msvc(),
+                msvc,
                 clang,
-                gnu,
+                gnu: matches!(compiler.family(), ToolFamily::Gnu),
                 is_asm: false,
                 is_arm,
             },
@@ -733,7 +732,7 @@ impl Build {
 
         // On MSVC skip the CRT by setting the entry point to `main`.
         // This way we don't need to add the default library paths.
-        if compiler.is_like_msvc() {
+        if msvc {
             // Flags from _LINK_ are appended to the linker arguments.
             cmd.env("_LINK_", "-entry:main");
         }
@@ -1753,8 +1752,6 @@ impl Build {
         let target = self.get_target()?;
         let msvc = target.env == "msvc";
         let compiler = self.try_get_compiler()?;
-        let clang = compiler.is_like_clang();
-        let gnu = compiler.family == ToolFamily::Gnu;
 
         let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm);
         let (mut cmd, name) = if is_assembler_msvc {
@@ -1782,9 +1779,9 @@ impl Build {
             CmdAddOutputFileArgs {
                 cuda: self.cuda,
                 is_assembler_msvc,
-                msvc: compiler.is_like_msvc(),
-                clang,
-                gnu,
+                msvc: matches!(compiler.family(), ToolFamily::Msvc { .. }),
+                clang: matches!(compiler.family(), ToolFamily::Clang { .. }),
+                gnu: matches!(compiler.family(), ToolFamily::Gnu),
                 is_asm,
                 is_arm,
             },
@@ -2036,15 +2033,16 @@ impl Build {
                 }
             }
             ToolFamily::Gnu | ToolFamily::Clang { .. } => {
+                let clang = matches!(cmd.family, ToolFamily::Clang { .. });
                 // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
                 // not support '-Oz'
-                if opt_level == "z" && !cmd.is_like_clang() {
+                if opt_level == "z" && !clang {
                     cmd.push_opt_unless_duplicate("-Os".into());
                 } else {
                     cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());
                 }
 
-                if cmd.is_like_clang() && target.os == "android" {
+                if clang && target.os == "android" {
                     // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro.
                     // If compiler used via ndk-build or cmake (officially supported build methods)
                     // this macros is defined.
@@ -2141,7 +2139,9 @@ impl Build {
             family.add_force_frame_pointer(cmd);
         }
 
-        if !cmd.is_like_msvc() {
+        let msvc = matches!(cmd.family, ToolFamily::Msvc { .. });
+
+        if !msvc {
             if target.arch == "x86" {
                 cmd.args.push("-m32".into());
             } else if target.abi == "x32" {
@@ -2653,7 +2653,8 @@ impl Build {
         // it does not support iOS in general), but we specify them anyhow in
         // case we actually have a Clang-like compiler disguised as a GNU-like
         // compiler, or in case GCC adds support for these in the future.
-        if !cmd.is_like_clang() {
+        let clang = matches!(cmd.family, ToolFamily::Clang { .. });
+        if !clang {
             let min_version = self.apple_deployment_target(&target);
             cmd.args
                 .push(target.apple_version_flag(&min_version).into());
@@ -3225,7 +3226,8 @@ impl Build {
                     // And even extend it to gcc targets by searching for "ar" instead
                     // of "llvm-ar"...
                     let compiler = self.get_base_compiler().ok()?;
-                    if compiler.is_like_clang() {
+                    let clang = matches!(compiler.family, ToolFamily::Clang { .. });
+                    if clang {
                         name = format!("llvm-{}", tool).into();
                         self.search_programs(
                             &mut self.cmd(&compiler.path),
diff --git a/src/tool.rs b/src/tool.rs
index af43a918..3842473f 100644
--- a/src/tool.rs
+++ b/src/tool.rs
@@ -281,11 +281,11 @@ impl Tool {
         let mut chars = flag.chars();
 
         // Only duplicate check compiler flags
-        if self.is_like_msvc() {
-            if chars.next() != Some('/') {
-                return false;
-            }
-        } else if (self.is_like_gnu() || self.is_like_clang()) && chars.next() != Some('-') {
+        let flag_start = match self.family {
+            ToolFamily::Msvc { .. } => '/',
+            ToolFamily::Gnu | ToolFamily::Clang { .. } => '-',
+        };
+        if chars.next() != Some(flag_start) {
             return false;
         }
 
@@ -395,12 +395,19 @@ impl Tool {
         flags
     }
 
+    /// The family of this tool, representing convention of arguments etc.
+    pub fn family(&self) -> ToolFamily {
+        self.family
+    }
+
     /// Whether the tool is GNU Compiler Collection-like.
+    #[deprecated = "Consider matching against the ToolFamily returned by family() instead"]
     pub fn is_like_gnu(&self) -> bool {
         self.family == ToolFamily::Gnu
     }
 
     /// Whether the tool is Clang-like.
+    #[deprecated = "Consider matching against the ToolFamily returned by family() instead"]
     pub fn is_like_clang(&self) -> bool {
         matches!(self.family, ToolFamily::Clang { .. })
     }
@@ -417,15 +424,11 @@ impl Tool {
     }
 
     /// Whether the tool is MSVC-like.
+    #[deprecated = "Consider matching against the ToolFamily returned by family() instead"]
     pub fn is_like_msvc(&self) -> bool {
         matches!(self.family, ToolFamily::Msvc { .. })
     }
 
-    /// Whether the tool is `clang-cl`-based MSVC-like.
-    pub fn is_like_clang_cl(&self) -> bool {
-        matches!(self.family, ToolFamily::Msvc { clang_cl: true })
-    }
-
     /// Supports using `--` delimiter to separate arguments and path to source files.
     pub(crate) fn supports_path_delimiter(&self) -> bool {
         matches!(
@@ -441,14 +444,24 @@ impl Tool {
 ///
 /// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
 #[derive(Copy, Clone, Debug, PartialEq)]
+#[non_exhaustive]
 pub enum ToolFamily {
     /// Tool is GNU Compiler Collection-like.
+    #[non_exhaustive]
     Gnu,
     /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
     /// and its cross-compilation approach is different.
-    Clang { zig_cc: bool },
-    /// Tool is the MSVC cl.exe.
-    Msvc { clang_cl: bool },
+    #[non_exhaustive]
+    Clang {
+        /// Tool provided by zig
+        zig_cc: bool,
+    },
+    /// Tool is the MSVC `cl.exe`.
+    #[non_exhaustive]
+    Msvc {
+        /// Whether this is `clang-cl` provided by LLVM
+        clang_cl: bool,
+    },
 }
 
 impl ToolFamily {