Skip to content

Commit 990b13e

Browse files
author
z1_cciauto
authored
merge main into amd-staging (llvm#3407)
2 parents c5986b7 + 0abd0b9 commit 990b13e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3310
-438
lines changed

clang-tools-extra/clang-tidy/.clang-tidy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ Checks: >
1616
-modernize-use-trailing-return-type,
1717
performance-*,
1818
-performance-enum-size,
19-
-performance-move-const-arg,
2019
-performance-no-int-to-ptr,
2120
-performance-type-promotion-in-math-fn,
2221
-performance-unnecessary-value-param,
@@ -38,3 +37,7 @@ Checks: >
3837
-readability-static-definition-in-anonymous-namespace,
3938
-readability-suspicious-call-argument,
4039
-readability-use-anyofallof
40+
41+
CheckOptions:
42+
- key: performance-move-const-arg.CheckTriviallyCopyableMove
43+
value: false

clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
132132
hasAncestor(functionDecl().bind("func")),
133133
hasAncestor(functionDecl(
134134
isDefinition(), equalsBoundNode("func"), ToParam,
135-
unless(anyOf(isDeleted(),
136-
hasDescendant(std::move(ForwardCallMatcher))))))),
135+
unless(anyOf(isDeleted(), hasDescendant(ForwardCallMatcher)))))),
137136
this);
138137
}
139138

clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) {
6767
hasParent(fieldDecl(
6868
hasParent(recordDecl(isExternCContext())))),
6969
hasAncestor(functionDecl(isExternC())))),
70-
std::move(IgnoreStringArrayIfNeededMatcher))
70+
IgnoreStringArrayIfNeededMatcher)
7171
.bind("typeloc"),
7272
this);
7373
}

clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ void UseStdNumbersCheck::registerMatchers(MatchFinder *const Finder) {
319319

320320
Finder->addMatcher(
321321
expr(
322-
anyOfExhaustive(std::move(ConstantMatchers)),
322+
anyOfExhaustive(ConstantMatchers),
323323
unless(hasParent(explicitCastExpr(hasDestinationType(isFloating())))),
324324
hasType(qualType(hasCanonicalTypeUnqualified(
325325
anyOf(qualType(asString("float")).bind("float"),

clang-tools-extra/clang-tidy/tool/run-clang-tidy.py

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
import time
5050
import traceback
5151
from types import ModuleType
52-
from typing import Any, Awaitable, Callable, List, Optional, TypeVar
52+
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, TypeVar
5353

5454

5555
yaml: Optional[ModuleType] = None
@@ -105,6 +105,7 @@ def get_tidy_invocation(
105105
warnings_as_errors: Optional[str],
106106
exclude_header_filter: Optional[str],
107107
allow_no_checks: bool,
108+
store_check_profile: Optional[str],
108109
) -> List[str]:
109110
"""Gets a command line for clang-tidy."""
110111
start = [clang_tidy_binary]
@@ -147,6 +148,9 @@ def get_tidy_invocation(
147148
start.append(f"--warnings-as-errors={warnings_as_errors}")
148149
if allow_no_checks:
149150
start.append("--allow-no-checks")
151+
if store_check_profile:
152+
start.append("--enable-check-profile")
153+
start.append(f"--store-check-profile={store_check_profile}")
150154
if f:
151155
start.append(f)
152156
return start
@@ -178,6 +182,124 @@ def merge_replacement_files(tmpdir: str, mergefile: str) -> None:
178182
open(mergefile, "w").close()
179183

180184

185+
def aggregate_profiles(profile_dir: str) -> Dict[str, float]:
186+
"""Aggregate timing data from multiple profile JSON files"""
187+
aggregated: Dict[str, float] = {}
188+
189+
for profile_file in glob.iglob(os.path.join(profile_dir, "*.json")):
190+
try:
191+
with open(profile_file, "r", encoding="utf-8") as f:
192+
data = json.load(f)
193+
profile_data: Dict[str, float] = data.get("profile", {})
194+
195+
for key, value in profile_data.items():
196+
if key.startswith("time.clang-tidy."):
197+
if key in aggregated:
198+
aggregated[key] += value
199+
else:
200+
aggregated[key] = value
201+
except (json.JSONDecodeError, KeyError, IOError) as e:
202+
print(f"Error: invalid json file {profile_file}: {e}", file=sys.stderr)
203+
continue
204+
205+
return aggregated
206+
207+
208+
def print_profile_data(aggregated_data: Dict[str, float]) -> None:
209+
"""Print aggregated checks profile data in the same format as clang-tidy"""
210+
if not aggregated_data:
211+
return
212+
213+
# Extract checker names and their timing data
214+
checkers: Dict[str, Dict[str, float]] = {}
215+
for key, value in aggregated_data.items():
216+
parts = key.split(".")
217+
if len(parts) >= 4 and parts[0] == "time" and parts[1] == "clang-tidy":
218+
checker_name = ".".join(
219+
parts[2:-1]
220+
) # Everything between "clang-tidy" and the timing type
221+
timing_type = parts[-1] # wall, user, or sys
222+
223+
if checker_name not in checkers:
224+
checkers[checker_name] = {"wall": 0.0, "user": 0.0, "sys": 0.0}
225+
226+
checkers[checker_name][timing_type] = value
227+
228+
if not checkers:
229+
return
230+
231+
total_user = sum(data["user"] for data in checkers.values())
232+
total_sys = sum(data["sys"] for data in checkers.values())
233+
total_wall = sum(data["wall"] for data in checkers.values())
234+
235+
sorted_checkers: List[Tuple[str, Dict[str, float]]] = sorted(
236+
checkers.items(), key=lambda x: x[1]["user"] + x[1]["sys"], reverse=True
237+
)
238+
239+
def print_stderr(*args, **kwargs) -> None:
240+
print(*args, file=sys.stderr, **kwargs)
241+
242+
print_stderr(
243+
"===-------------------------------------------------------------------------==="
244+
)
245+
print_stderr(" clang-tidy checks profiling")
246+
print_stderr(
247+
"===-------------------------------------------------------------------------==="
248+
)
249+
print_stderr(
250+
f" Total Execution Time: {total_user + total_sys:.4f} seconds ({total_wall:.4f} wall clock)\n"
251+
)
252+
253+
# Calculate field widths based on the Total line which has the largest values
254+
total_combined = total_user + total_sys
255+
user_width = len(f"{total_user:.4f}")
256+
sys_width = len(f"{total_sys:.4f}")
257+
combined_width = len(f"{total_combined:.4f}")
258+
wall_width = len(f"{total_wall:.4f}")
259+
260+
# Header with proper alignment
261+
additional_width = 9 # for " (100.0%)"
262+
user_header = "---User Time---".center(user_width + additional_width)
263+
sys_header = "--System Time--".center(sys_width + additional_width)
264+
combined_header = "--User+System--".center(combined_width + additional_width)
265+
wall_header = "---Wall Time---".center(wall_width + additional_width)
266+
267+
print_stderr(
268+
f" {user_header} {sys_header} {combined_header} {wall_header} --- Name ---"
269+
)
270+
271+
for checker_name, data in sorted_checkers:
272+
user_time = data["user"]
273+
sys_time = data["sys"]
274+
wall_time = data["wall"]
275+
combined_time = user_time + sys_time
276+
277+
user_percent = (user_time / total_user * 100) if total_user > 0 else 0
278+
sys_percent = (sys_time / total_sys * 100) if total_sys > 0 else 0
279+
combined_percent = (
280+
(combined_time / total_combined * 100) if total_combined > 0 else 0
281+
)
282+
wall_percent = (wall_time / total_wall * 100) if total_wall > 0 else 0
283+
284+
user_str = f"{user_time:{user_width}.4f} ({user_percent:5.1f}%)"
285+
sys_str = f"{sys_time:{sys_width}.4f} ({sys_percent:5.1f}%)"
286+
combined_str = f"{combined_time:{combined_width}.4f} ({combined_percent:5.1f}%)"
287+
wall_str = f"{wall_time:{wall_width}.4f} ({wall_percent:5.1f}%)"
288+
289+
print_stderr(
290+
f" {user_str} {sys_str} {combined_str} {wall_str} {checker_name}"
291+
)
292+
293+
user_total_str = f"{total_user:{user_width}.4f} (100.0%)"
294+
sys_total_str = f"{total_sys:{sys_width}.4f} (100.0%)"
295+
combined_total_str = f"{total_combined:{combined_width}.4f} (100.0%)"
296+
wall_total_str = f"{total_wall:{wall_width}.4f} (100.0%)"
297+
298+
print_stderr(
299+
f" {user_total_str} {sys_total_str} {combined_total_str} {wall_total_str} Total"
300+
)
301+
302+
181303
def find_binary(arg: str, name: str, build_path: str) -> str:
182304
"""Get the path for a binary or exit"""
183305
if arg:
@@ -240,6 +362,7 @@ async def run_tidy(
240362
clang_tidy_binary: str,
241363
tmpdir: str,
242364
build_path: str,
365+
store_check_profile: Optional[str],
243366
) -> ClangTidyResult:
244367
"""
245368
Runs clang-tidy on a single file and returns the result.
@@ -263,6 +386,7 @@ async def run_tidy(
263386
args.warnings_as_errors,
264387
args.exclude_header_filter,
265388
args.allow_no_checks,
389+
store_check_profile,
266390
)
267391

268392
try:
@@ -447,6 +571,11 @@ async def main() -> None:
447571
action="store_true",
448572
help="Allow empty enabled checks.",
449573
)
574+
parser.add_argument(
575+
"-enable-check-profile",
576+
action="store_true",
577+
help="Enable per-check timing profiles, and print a report",
578+
)
450579
args = parser.parse_args()
451580

452581
db_path = "compile_commands.json"
@@ -489,6 +618,10 @@ async def main() -> None:
489618
export_fixes_dir = tempfile.mkdtemp()
490619
delete_fixes_dir = True
491620

621+
profile_dir: Optional[str] = None
622+
if args.enable_check_profile:
623+
profile_dir = tempfile.mkdtemp()
624+
492625
try:
493626
invocation = get_tidy_invocation(
494627
None,
@@ -509,6 +642,7 @@ async def main() -> None:
509642
args.warnings_as_errors,
510643
args.exclude_header_filter,
511644
args.allow_no_checks,
645+
None, # No profiling for the list-checks invocation
512646
)
513647
invocation.append("-list-checks")
514648
invocation.append("-")
@@ -567,6 +701,7 @@ async def main() -> None:
567701
clang_tidy_binary,
568702
export_fixes_dir,
569703
build_path,
704+
profile_dir,
570705
)
571706
)
572707
for f in files
@@ -593,8 +728,19 @@ async def main() -> None:
593728
if delete_fixes_dir:
594729
assert export_fixes_dir
595730
shutil.rmtree(export_fixes_dir)
731+
if profile_dir:
732+
shutil.rmtree(profile_dir)
596733
return
597734

735+
if args.enable_check_profile and profile_dir:
736+
# Ensure all clang-tidy stdout is flushed before printing profiling
737+
sys.stdout.flush()
738+
aggregated_data = aggregate_profiles(profile_dir)
739+
if aggregated_data:
740+
print_profile_data(aggregated_data)
741+
else:
742+
print("No profiling data found.")
743+
598744
if combine_fixes:
599745
print(f"Writing fixes to {args.export_fixes} ...")
600746
try:
@@ -618,6 +764,8 @@ async def main() -> None:
618764
if delete_fixes_dir:
619765
assert export_fixes_dir
620766
shutil.rmtree(export_fixes_dir)
767+
if profile_dir:
768+
shutil.rmtree(profile_dir)
621769
sys.exit(returncode)
622770

623771

clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
149149
}
150150
Finder->addMatcher(
151151
callExpr(
152-
callee(functionDecl(hasAnyName(std::move(Names)))
152+
callee(functionDecl(hasAnyName(Names))
153153
.bind((FuncDecl + Twine(Replacers.size() - 1).str()))),
154154
ast_matchers::internal::DynTypedMatcher::constructVariadic(
155155
ast_matchers::internal::DynTypedMatcher::VO_AnyOf,

clang-tools-extra/clangd/HeaderSourceSwitch.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,24 @@ namespace clangd {
2020

2121
std::optional<Path> getCorrespondingHeaderOrSource(
2222
PathRef OriginalFile, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
23-
llvm::StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx",
24-
".c++", ".m", ".mm"};
25-
llvm::StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx",
26-
".inc", ".cppm", ".ccm", ".cxxm",
27-
".c++m", ".ixx"};
23+
static constexpr llvm::StringRef SourceExtensions[] = {
24+
".cpp", ".c", ".cc", ".cxx", ".c++", ".m", ".mm"};
25+
static constexpr llvm::StringRef HeaderExtensions[] = {
26+
".h", ".hh", ".hpp", ".hxx", ".inc",
27+
".cppm", ".ccm", ".cxxm", ".c++m", ".ixx"};
2828

2929
llvm::StringRef PathExt = llvm::sys::path::extension(OriginalFile);
3030

3131
// Lookup in a list of known extensions.
32-
bool IsSource = llvm::any_of(SourceExtensions, [&PathExt](PathRef SourceExt) {
33-
return SourceExt.equals_insensitive(PathExt);
34-
});
32+
const bool IsSource =
33+
llvm::any_of(SourceExtensions, [&PathExt](PathRef SourceExt) {
34+
return SourceExt.equals_insensitive(PathExt);
35+
});
3536

36-
bool IsHeader = llvm::any_of(HeaderExtensions, [&PathExt](PathRef HeaderExt) {
37-
return HeaderExt.equals_insensitive(PathExt);
38-
});
37+
const bool IsHeader =
38+
llvm::any_of(HeaderExtensions, [&PathExt](PathRef HeaderExt) {
39+
return HeaderExt.equals_insensitive(PathExt);
40+
});
3941

4042
// We can only switch between the known extensions.
4143
if (!IsSource && !IsHeader)
@@ -94,7 +96,7 @@ std::optional<Path> getCorrespondingHeaderOrSource(PathRef OriginalFile,
9496
//
9597
// For each symbol in the original file, we get its target location (decl or
9698
// def) from the index, then award that target file.
97-
bool IsHeader = isHeaderFile(OriginalFile, AST.getLangOpts());
99+
const bool IsHeader = isHeaderFile(OriginalFile, AST.getLangOpts());
98100
Index->lookup(Request, [&](const Symbol &Sym) {
99101
if (IsHeader)
100102
AwardTarget(Sym.Definition.FileURI);

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,8 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
22872287
Decl->getKind() != Decl::Kind::FunctionTemplate &&
22882288
!(Decl->getKind() == Decl::Kind::Var &&
22892289
!cast<VarDecl>(Decl)->isLocalVarDecl()) &&
2290-
Decl->getKind() != Decl::Kind::Field)
2290+
Decl->getKind() != Decl::Kind::Field &&
2291+
Decl->getKind() != Decl::Kind::EnumConstant)
22912292
continue;
22922293
if (auto CHI = declToCallHierarchyItem(*Decl, AST.tuPath()))
22932294
Result.emplace_back(std::move(*CHI));

clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,35 @@ TEST(CallHierarchy, HierarchyOnVar) {
633633
iFromRanges(Source.range("Callee")))));
634634
}
635635

636+
TEST(CallHierarchy, HierarchyOnEnumConstant) {
637+
// Tests that the call hierarchy works on enum constants.
638+
Annotations Source(R"cpp(
639+
enum class Coin { heads$Heads^ , tai$Tails^ls };
640+
void caller() {
641+
Coin::$CallerH[[heads]];
642+
Coin::$CallerT[[tails]];
643+
}
644+
)cpp");
645+
TestTU TU = TestTU::withCode(Source.code());
646+
auto AST = TU.build();
647+
auto Index = TU.index();
648+
649+
std::vector<CallHierarchyItem> Items =
650+
prepareCallHierarchy(AST, Source.point("Heads"), testPath(TU.Filename));
651+
ASSERT_THAT(Items, ElementsAre(withName("heads")));
652+
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
653+
ASSERT_THAT(IncomingLevel1,
654+
ElementsAre(AllOf(from(withName("caller")),
655+
iFromRanges(Source.range("CallerH")))));
656+
Items =
657+
prepareCallHierarchy(AST, Source.point("Tails"), testPath(TU.Filename));
658+
ASSERT_THAT(Items, ElementsAre(withName("tails")));
659+
IncomingLevel1 = incomingCalls(Items[0], Index.get());
660+
ASSERT_THAT(IncomingLevel1,
661+
ElementsAre(AllOf(from(withName("caller")),
662+
iFromRanges(Source.range("CallerT")))));
663+
}
664+
636665
TEST(CallHierarchy, CallInDifferentFileThanCaller) {
637666
Annotations Header(R"cpp(
638667
#define WALDO void caller() {

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ Improvements to clang-tidy
105105
now run checks in parallel by default using all available hardware threads.
106106
Both scripts display the number of threads being used in their output.
107107

108+
- Improved :program:`run-clang-tidy.py` by adding a new option
109+
`enable-check-profile` to enable per-check timing profiles and print a
110+
report based on all analyzed files.
111+
108112
New checks
109113
^^^^^^^^^^
110114

0 commit comments

Comments
 (0)