[clang-tools-extra] [llvm] [clang-tidy] Remove 'clang-analyzer-*' checks from default checks. (PR #157306)
Baranov Victor via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 21 13:58:26 PDT 2025
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/157306
>From 4ab549990f3dc3f59a6346b32687e5ecbd495586 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sat, 6 Sep 2025 22:54:31 +0300
Subject: [PATCH 1/2] [clang-tidy] Remove 'clang-analyzer-*' checks from
default checks
---
clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp | 3 +--
clang-tools-extra/docs/ReleaseNotes.rst | 2 ++
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index bef3b938b5afd..ed9b195f0dbde 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -102,8 +102,7 @@ Configuration files:
)");
const char DefaultChecks[] = // Enable these checks by default:
- "clang-diagnostic-*," // * compiler diagnostics
- "clang-analyzer-*"; // * Static Analyzer checks
+ "clang-diagnostic-*"; // * compiler diagnostics
static cl::opt<std::string> Checks("checks", desc(R"(
Comma-separated list of globs with optional '-'
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e1b6daf75457d..d97efe667f98e 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -139,6 +139,8 @@ Improvements to clang-tidy
scripts by adding the `-hide-progress` option to suppress progress and
informational messages.
+- Removed `clang-analyzer-*` check from default checks in :program:`clang-tidy`.
+
New checks
^^^^^^^^^^
>From 54d4521e617091a520cf749cc714309891b7b582 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 21 Sep 2025 23:58:12 +0300
Subject: [PATCH 2/2] WIP
---
.clang-tidy | 38 --
.../clang-tidy/add_check_alias.py | 426 ++++++++++++++++++
.../clang-tidy/tool/ClangTidyMain.cpp | 80 ++++
.../config-with-exclusion | 3 +
.../config-with-inclusion | 3 +
.../config-with-wildcard | 3 +
.../config-without-analyzer | 3 +
.../clang-analyzer-soft-deprecation.cpp | 35 ++
clang-tools-extra/test_here.cpp | 1 +
test_warning.cpp | 4 +
10 files changed, 558 insertions(+), 38 deletions(-)
delete mode 100644 .clang-tidy
create mode 100755 clang-tools-extra/clang-tidy/add_check_alias.py
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-exclusion
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-inclusion
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-wildcard
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-without-analyzer
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/clang-analyzer-soft-deprecation.cpp
create mode 100644 clang-tools-extra/test_here.cpp
create mode 100644 test_warning.cpp
diff --git a/.clang-tidy b/.clang-tidy
deleted file mode 100644
index 06bb0f18e9d2e..0000000000000
--- a/.clang-tidy
+++ /dev/null
@@ -1,38 +0,0 @@
-Checks: >
- -*,
- clang-diagnostic-*,
- llvm-*,
- misc-*,
- -misc-const-correctness,
- -misc-include-cleaner,
- -misc-no-recursion,
- -misc-non-private-member-variables-in-classes,
- -misc-unused-parameters,
- -misc-use-anonymous-namespace,
- readability-identifier-naming
-
-CheckOptions:
- - key: readability-identifier-naming.ClassCase
- value: CamelCase
- - key: readability-identifier-naming.EnumCase
- value: CamelCase
- - key: readability-identifier-naming.FunctionCase
- value: camelBack
- # Exclude from scanning as this is an exported symbol used for fuzzing
- # throughout the code base.
- - key: readability-identifier-naming.FunctionIgnoredRegexp
- value: "LLVMFuzzerTestOneInput"
- - key: readability-identifier-naming.MemberCase
- value: CamelCase
- - key: readability-identifier-naming.ParameterCase
- value: CamelCase
- - key: readability-identifier-naming.UnionCase
- value: CamelCase
- - key: readability-identifier-naming.VariableCase
- value: CamelCase
- - key: readability-identifier-naming.IgnoreMainLikeFunctions
- value: 1
- - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors
- value: 1
- - key: modernize-use-default-member-init.UseAssignment
- value: 1
diff --git a/clang-tools-extra/clang-tidy/add_check_alias.py b/clang-tools-extra/clang-tidy/add_check_alias.py
new file mode 100755
index 0000000000000..44d4b956f3686
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/add_check_alias.py
@@ -0,0 +1,426 @@
+#!/usr/bin/env python3
+#
+# ===- add_check_alias.py - clang-tidy check alias generator ----*- python -*--===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===-----------------------------------------------------------------------===#
+
+import argparse
+import io
+import os
+import re
+import sys
+
+# FIXME Python 3.9: Replace typing.Optional with builtins.
+from typing import Optional, Dict
+
+
+def get_camel_name(check_name: str) -> str:
+ return "".join(map(lambda elem: elem.capitalize(), check_name.split("-")))
+
+
+def get_camel_check_name(check_name: str) -> str:
+ return get_camel_name(check_name) + "Check"
+
+
+def get_module_filename(module_path: str, module: str) -> str:
+ modulecpp = list(
+ filter(
+ lambda p: p.lower() == module.lower() + "tidymodule.cpp",
+ os.listdir(module_path),
+ )
+ )[0]
+ return os.path.join(module_path, modulecpp)
+
+
+def add_alias_to_module(
+ module_path: str,
+ alias_module: str,
+ alias_name: str,
+ target_module: str,
+ target_name: str,
+ target_check_class: str,
+ options: Dict[str, str],
+) -> None:
+ """Add the alias registration to the module file."""
+ filename = get_module_filename(module_path, alias_module)
+ with io.open(filename, "r", encoding="utf8") as f:
+ lines = f.readlines()
+
+ alias_check_name = alias_module + "-" + alias_name
+ target_include = f"../{target_module}/{target_check_class}.h"
+
+ print("Updating %s..." % filename)
+ with io.open(filename, "w", encoding="utf8", newline="\n") as f:
+ header_added = False
+ header_found = False
+ check_added = False
+ options_added = False
+
+ alias_registration = (
+ f" CheckFactories.registerCheck<{target_module}::{target_check_class}>(\n"
+ f' "{alias_check_name}");\n'
+ )
+
+ lines_iter = iter(lines)
+ try:
+ while True:
+ line = next(lines_iter)
+
+ # Add include for the target check
+ if not header_added:
+ match = re.search('#include "(.*)"', line)
+ if match:
+ header_found = True
+ if match.group(1) > target_include:
+ header_added = True
+ f.write('#include "' + target_include + '"\n')
+ elif header_found:
+ header_added = True
+ f.write('#include "' + target_include + '"\n')
+
+ # Add check registration
+ if not check_added:
+ if line.strip() == "}":
+ check_added = True
+ f.write(alias_registration)
+ else:
+ match = re.search(
+ r'registerCheck<(.*)> *\( *(?:"([^"]*)")?', line
+ )
+ prev_line = None
+ if match:
+ current_check_name = match.group(2)
+ if current_check_name is None:
+ # If we didn't find the check name on this line, look on the
+ # next one.
+ prev_line = line
+ line = next(lines_iter)
+ match = re.search(' *"([^"]*)"', line)
+ if match:
+ current_check_name = match.group(1)
+ assert current_check_name
+ if current_check_name > alias_check_name:
+ check_added = True
+ f.write(alias_registration)
+ if prev_line:
+ f.write(prev_line)
+
+ # Add options if they exist and we're in the getModuleOptions method
+ if options and not options_added and "getModuleOptions" in line:
+ # Look for the return statement to add options before it
+ while True:
+ f.write(line)
+ line = next(lines_iter)
+ if "return Options;" in line:
+ # Add the options before the return statement
+ for option_key, option_value in options.items():
+ f.write(
+ f' Options.CheckOptions["{alias_check_name}.{option_key}"] = "{option_value}";\n'
+ )
+ options_added = True
+ break
+ elif line.strip() == "}":
+ # If we hit the end of the function without finding return, add the options before the closing brace
+ for option_key, option_value in options.items():
+ f.write(
+ f' Options.CheckOptions["{alias_check_name}.{option_key}"] = "{option_value}";\n'
+ )
+ options_added = True
+ break
+
+ f.write(line)
+ except StopIteration:
+ pass
+
+
+def add_release_notes(
+ clang_tidy_path: str,
+ alias_name: str,
+ target_name: str,
+ alias_module: str,
+ target_module: str,
+) -> None:
+ """Add a release notes entry for the new alias."""
+ filename = os.path.normpath(
+ os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")
+ )
+ with io.open(filename, "r", encoding="utf8") as f:
+ lines = f.readlines()
+
+ alias_check_name = alias_module + "-" + alias_name
+ target_check_name = target_module + "-" + target_name
+
+ lineMatcher = re.compile("New check aliases")
+ nextSectionMatcher = re.compile("Changes in existing checks")
+ checkMatcher = re.compile("- New alias :doc:`(.*)")
+
+ print("Updating %s..." % filename)
+ with io.open(filename, "w", encoding="utf8", newline="\n") as f:
+ note_added = False
+ header_found = False
+ add_note_here = False
+
+ for line in lines:
+ if not note_added:
+ match = lineMatcher.match(line)
+ match_next = nextSectionMatcher.match(line)
+ match_check = checkMatcher.match(line)
+ if match_check:
+ last_check = match_check.group(1)
+ if last_check > alias_check_name:
+ add_note_here = True
+
+ if match_next:
+ add_note_here = True
+
+ if match:
+ header_found = True
+ f.write(line)
+ continue
+
+ if line.startswith("^^^^"):
+ f.write(line)
+ continue
+
+ if header_found and add_note_here:
+ if not line.startswith("^^^^"):
+ f.write(
+ f"""- New alias :doc:`{alias_check_name}
+ <clang-tidy/checks/{alias_module}/{alias_name}>` to
+ :doc:`{target_check_name}
+ <clang-tidy/checks/{target_module}/{target_name}>` was added.
+
+"""
+ )
+ note_added = True
+
+ f.write(line)
+
+
+def write_alias_docs(
+ clang_tidy_path: str,
+ alias_name: str,
+ target_name: str,
+ alias_module: str,
+ target_module: str,
+) -> None:
+ """Create the documentation file for the alias."""
+ alias_check_name = alias_module + "-" + alias_name
+ target_check_name = target_module + "-" + target_name
+
+ docs_dir = os.path.normpath(
+ os.path.join(clang_tidy_path, "../docs/clang-tidy/checks", alias_module)
+ )
+
+ # Create the module directory if it doesn't exist
+ os.makedirs(docs_dir, exist_ok=True)
+
+ filename = os.path.join(docs_dir, alias_name + ".rst")
+ print("Creating %s..." % filename)
+
+ with io.open(filename, "w", encoding="utf8", newline="\n") as f:
+ f.write(
+ f""".. title:: clang-tidy - {alias_check_name}
+.. meta::
+ :http-equiv=refresh: 5;URL=../{target_module}/{target_name}.html
+
+{alias_check_name}
+{"=" * len(alias_check_name)}
+
+The {alias_check_name} check is an alias, please see
+`{target_check_name} <../{target_module}/{target_name}.html>`_
+for more information.
+
+"""
+ )
+
+
+def update_target_docs(
+ clang_tidy_path: str,
+ alias_name: str,
+ target_name: str,
+ alias_module: str,
+ target_module: str,
+ options: Dict[str, str],
+) -> None:
+ """Update the target check documentation to mention the alias."""
+ filename = os.path.normpath(
+ os.path.join(
+ clang_tidy_path,
+ "../docs/clang-tidy/checks",
+ target_module,
+ target_name + ".rst",
+ )
+ )
+
+ if not os.path.exists(filename):
+ print(f"Warning: Target documentation file {filename} not found")
+ return
+
+ alias_check_name = alias_module + "-" + alias_name
+
+ with io.open(filename, "r", encoding="utf8") as f:
+ content = f.read()
+
+ # Check if alias section already exists
+ if f"{alias_module.upper()} alias" in content:
+ print(f"Alias section already exists in {filename}")
+ return
+
+ # Add alias section at the end
+ alias_section = f"""
+
+{alias_module.upper()} alias
+{"-" * (len(alias_module) + 6)}
+
+There is an alias of this check called {alias_check_name}."""
+
+ if options:
+ alias_section += f"""
+In that version the options"""
+ option_names = list(options.keys())
+ if len(option_names) == 1:
+ alias_section += f" :option:`{option_names[0]}` is"
+ else:
+ alias_section += " " + ", ".join(
+ f":option:`{name}`" for name in option_names[:-1]
+ )
+ alias_section += f" and :option:`{option_names[-1]}` are"
+ alias_section += f" set to different defaults."
+
+ alias_section += "\n"
+
+ print("Updating %s..." % filename)
+ with io.open(filename, "w", encoding="utf8", newline="\n") as f:
+ f.write(content + alias_section)
+
+
+def update_checks_list(clang_tidy_path: str) -> None:
+ """Update the main checks list documentation."""
+ add_new_check_script = os.path.join(clang_tidy_path, "add_new_check.py")
+ os.system(f"python3 {add_new_check_script} --update-docs")
+
+
+def parse_options(options_str: Optional[str]) -> Dict[str, str]:
+ """Parse the options string into a dictionary."""
+ if not options_str:
+ return {}
+
+ options = {}
+ for option in options_str.split(","):
+ if "=" in option:
+ key, value = option.split("=", 1)
+ options[key.strip()] = value.strip()
+ else:
+ print(f"Warning: Invalid option format '{option}', expected 'key=value'")
+
+ return options
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description="Add a new clang-tidy check alias.")
+ parser.add_argument(
+ "alias_name", help="Name of the new alias (e.g., 'llvm-else-after-return')"
+ )
+ parser.add_argument(
+ "target_name",
+ help="Name of the target check to alias (e.g., 'readability-else-after-return')",
+ )
+ parser.add_argument(
+ "--options",
+ help="Comma-separated list of option overrides in the form key=value (e.g., 'WarnOnUnfixable=0,RefactorConditionVariables=0')",
+ default=None,
+ )
+ parser.add_argument(
+ "--description",
+ help="Description for the alias (used in release notes)",
+ default="",
+ )
+ parser.add_argument(
+ "--check-class-name",
+ help="Name of the check class (e.g., 'StrToNumCheck'). If not provided, will be inferred from target check name.",
+ default=None,
+ )
+
+ args = parser.parse_args()
+
+ # Parse the check names
+ alias_parts = args.alias_name.split("-", 1)
+ target_parts = args.target_name.split("-", 1)
+
+ if len(alias_parts) != 2 or len(target_parts) != 2:
+ print("Error: Check names must be in format 'module-checkname'")
+ return 1
+
+ alias_module, alias_name = alias_parts
+ target_module, target_name = target_parts
+
+ # Parse options
+ options = parse_options(args.options)
+
+ clang_tidy_path = os.path.dirname(sys.argv[0])
+ alias_module_path = os.path.join(clang_tidy_path, alias_module)
+
+ if not os.path.isdir(alias_module_path):
+ print(f"Error: Module directory '{alias_module_path}' does not exist")
+ return 1
+
+ # Determine the target check class name
+ if args.check_class_name:
+ target_check_class = args.check_class_name
+ else:
+ target_check_class = get_camel_check_name(target_name)
+
+ # Check if target check exists
+ target_module_path = os.path.join(clang_tidy_path, target_module)
+ target_header = os.path.join(target_module_path, target_check_class + ".h")
+
+ if not os.path.isfile(target_header):
+ print(f"Error: Target check header '{target_header}' does not exist")
+ print(
+ f" (Looking for class '{target_check_class}' in module '{target_module}')"
+ )
+ if not args.check_class_name:
+ print(
+ f" You may need to specify --check-class-name if the class name doesn't match the check name"
+ )
+ return 1
+
+ print(f"Adding alias '{args.alias_name}' -> '{args.target_name}'...")
+
+ # Add alias to the module
+ add_alias_to_module(
+ alias_module_path,
+ alias_module,
+ alias_name,
+ target_module,
+ target_name,
+ target_check_class,
+ options,
+ )
+
+ # Create alias documentation
+ write_alias_docs(
+ clang_tidy_path, alias_name, target_name, alias_module, target_module
+ )
+
+ # Note: Skipping target documentation update as requested
+
+ # Add release notes
+ add_release_notes(
+ clang_tidy_path, alias_name, target_name, alias_module, target_module
+ )
+
+ # Update the main documentation list
+ update_checks_list(clang_tidy_path)
+
+ print("Done! Alias has been created successfully.")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index ed9b195f0dbde..661b7f08a5360 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -632,6 +632,86 @@ int clangTidyMain(int argc, const char **argv) {
std::vector<std::string> EnabledChecks =
getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
+ // Check for soft deprecation warning for clang-analyzer-* checks
+ if (!Quiet) {
+ bool HasAnalyzerChecks = llvm::any_of(EnabledChecks, [](const std::string &Check) {
+ return StringRef(Check).starts_with("clang-analyzer-");
+ });
+
+ if (!HasAnalyzerChecks) {
+ // Check if user explicitly mentioned clang-analyzer-* in their configuration
+ bool ExplicitlyMentioned = false;
+ std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
+ RawOptions = OptionsProvider->getRawOptions(FilePath);
+
+ for (const auto &[Opts, Source] : RawOptions) {
+ if (Opts.Checks) {
+ GlobList CheckGlobs(*Opts.Checks);
+ for (const auto &Item : CheckGlobs.getItems()) {
+ if (Item.Text.contains("clang-analyzer-")) {
+ ExplicitlyMentioned = true;
+ break;
+ }
+ }
+ if (ExplicitlyMentioned) break;
+ }
+ }
+
+ // Only show warning if user is using default checks or wildcard patterns
+ // that would have included clang-analyzer-* in the past
+ bool ShouldWarn = false;
+ if (!ExplicitlyMentioned && !EnabledChecks.empty()) {
+ // Show warning if using defaults (no -checks specified and no config file checks)
+ if (Checks.getNumOccurrences() == 0) {
+ bool HasConfigChecks = false;
+ for (const auto &[Opts, Source] : RawOptions) {
+ if (Opts.Checks && !Opts.Checks->empty()) {
+ HasConfigChecks = true;
+ break;
+ }
+ }
+ if (!HasConfigChecks) {
+ ShouldWarn = true;
+ }
+ }
+ // Or if using broad wildcard patterns
+ else {
+ // Check command line checks
+ if (Checks.getNumOccurrences() > 0) {
+ StringRef CheckStr = Checks;
+ if ((CheckStr == "*" || CheckStr.starts_with("*,") ||
+ CheckStr.contains(",*,") || CheckStr.ends_with(",*")) &&
+ !CheckStr.contains("clang-analyzer-")) {
+ ShouldWarn = true;
+ }
+ }
+ // Check config file checks if no command line checks
+ if (!ShouldWarn) {
+ for (const auto &[Opts, Source] : RawOptions) {
+ if (Opts.Checks) {
+ StringRef CheckStr = *Opts.Checks;
+ if ((CheckStr == "*" || CheckStr.starts_with("*,") ||
+ CheckStr.contains(",*,") || CheckStr.ends_with(",*")) &&
+ !CheckStr.contains("clang-analyzer-")) {
+ ShouldWarn = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (ShouldWarn) {
+ llvm::WithColor::warning(llvm::errs())
+ << "clang-analyzer-* checks are no longer enabled by default.\n"
+ << "If you want to enable clang-analyzer-* checks, use "
+ << "-checks='*,clang-analyzer-*' or add 'clang-analyzer-*' to "
+ << "your .clang-tidy configuration file.\n";
+ }
+ }
+ }
+
if (ExplainConfig) {
// FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-exclusion b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-exclusion
new file mode 100644
index 0000000000000..a2084db3da707
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-exclusion
@@ -0,0 +1,3 @@
+---
+Checks: '*,-clang-analyzer-*'
+---
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-inclusion b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-inclusion
new file mode 100644
index 0000000000000..1f38e120b7f43
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-inclusion
@@ -0,0 +1,3 @@
+---
+Checks: '*,clang-analyzer-*'
+---
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-wildcard b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-wildcard
new file mode 100644
index 0000000000000..cbea8802814ce
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-with-wildcard
@@ -0,0 +1,3 @@
+---
+Checks: '*'
+---
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-without-analyzer b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-without-analyzer
new file mode 100644
index 0000000000000..7678af6337c2f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/clang-analyzer-soft-deprecation/config-without-analyzer
@@ -0,0 +1,3 @@
+---
+Checks: 'readability-*,bugprone-*'
+---
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/clang-analyzer-soft-deprecation.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/clang-analyzer-soft-deprecation.cpp
new file mode 100644
index 0000000000000..1a2a7a65f6a35
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/clang-analyzer-soft-deprecation.cpp
@@ -0,0 +1,35 @@
+// This test verifies the soft deprecation warning for clang-analyzer-* checks
+// which are no longer enabled by default.
+
+// Test that warning appears with wildcard patterns that would have included clang-analyzer-*
+// RUN: clang-tidy -checks='*' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-WILDCARD-WARNING
+// RUN: clang-tidy -checks='*,readability-*' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-WILDCARD-WARNING
+// RUN: clang-tidy -config-file=%S/Inputs/clang-analyzer-soft-deprecation/config-with-wildcard %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-WILDCARD-WARNING
+
+// Test that warning does not appear when clang-analyzer-* is explicitly mentioned
+// RUN: clang-tidy -checks=-clang-analyzer-* %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-EXPLICIT-EXCLUSION --allow-empty
+// RUN: clang-tidy -checks=clang-analyzer-* %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-EXPLICIT-INCLUSION --allow-empty
+// RUN: clang-tidy -checks='*,clang-analyzer-*' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-EXPLICIT-INCLUSION --allow-empty
+// RUN: clang-tidy -checks='*,-clang-analyzer-*' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-EXPLICIT-EXCLUSION --allow-empty
+// RUN: clang-tidy -config-file=%S/Inputs/clang-analyzer-soft-deprecation/config-with-exclusion %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-CONFIG-EXCLUSION --allow-empty
+// RUN: clang-tidy -config-file=%S/Inputs/clang-analyzer-soft-deprecation/config-with-inclusion %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-CONFIG-INCLUSION --allow-empty
+
+// Test that warning does not appear in quiet mode
+// RUN: clang-tidy --quiet -checks='*' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-QUIET --allow-empty
+
+// Test that warning does not appear with specific checks (not wildcards)
+// RUN: clang-tidy -checks='readability-magic-numbers' %s -- 2>&1 | FileCheck %s --check-prefix=CHECK-SPECIFIC-CHECKS --allow-empty
+
+// CHECK-WILDCARD-WARNING: warning: clang-analyzer-* checks are no longer enabled by default.
+// CHECK-WILDCARD-WARNING-NEXT: If you want to enable clang-analyzer-* checks, use -checks='*,clang-analyzer-*' or add 'clang-analyzer-*' to your .clang-tidy configuration file.
+
+// CHECK-EXPLICIT-EXCLUSION-NOT: clang-analyzer-* checks are no longer enabled by default
+// CHECK-EXPLICIT-INCLUSION-NOT: clang-analyzer-* checks are no longer enabled by default
+// CHECK-CONFIG-EXCLUSION-NOT: clang-analyzer-* checks are no longer enabled by default
+// CHECK-CONFIG-INCLUSION-NOT: clang-analyzer-* checks are no longer enabled by default
+// CHECK-QUIET-NOT: clang-analyzer-* checks are no longer enabled by default
+// CHECK-SPECIFIC-CHECKS-NOT: clang-analyzer-* checks are no longer enabled by default
+
+int main() {
+ return 0;
+}
\ No newline at end of file
diff --git a/clang-tools-extra/test_here.cpp b/clang-tools-extra/test_here.cpp
new file mode 100644
index 0000000000000..76e8197013aab
--- /dev/null
+++ b/clang-tools-extra/test_here.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/test_warning.cpp b/test_warning.cpp
new file mode 100644
index 0000000000000..2846e71306b39
--- /dev/null
+++ b/test_warning.cpp
@@ -0,0 +1,4 @@
+// Simple test file for testing clang-tidy soft deprecation warning
+int main() {
+ return 0;
+}
\ No newline at end of file
More information about the llvm-commits
mailing list