Skip to content

Commit 062aebb

Browse files
committed
[clang-tidy] Add modernize-cleanup-static-cast check
Add a new check that detects and removes redundant static_cast operations where the source and target types are identical. This helps clean up code after type system changes, improving readability and reducing opportunities for errors during future refactoring.
1 parent 866755f commit 062aebb

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
lines changed

clang-tools-extra/clang-tidy/modernize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC
4949
UseTransparentFunctorsCheck.cpp
5050
UseUncaughtExceptionsCheck.cpp
5151
UseUsingCheck.cpp
52+
CleanupStaticCastCheck.cpp
5253

5354
LINK_LIBS
5455
clangTidy
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===--- CleanupStaticCastCheck.cpp - clang-tidy-----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CleanupStaticCastCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/DeclTemplate.h"
12+
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include "clang/ASTMatchers/ASTMatchers.h"
14+
#include "clang/Basic/SourceManager.h"
15+
#include "clang/Lex/Lexer.h"
16+
17+
using namespace clang::ast_matchers;
18+
19+
namespace {
20+
std::string getText(const clang::Expr *E, const clang::ASTContext &Context) {
21+
auto &SM = Context.getSourceManager();
22+
auto Range = clang::CharSourceRange::getTokenRange(E->getSourceRange());
23+
return clang::Lexer::getSourceText(Range, SM, Context.getLangOpts()).str();
24+
}
25+
} // namespace
26+
27+
namespace clang::tidy::modernize {
28+
29+
void CleanupStaticCastCheck::registerMatchers(MatchFinder *Finder) {
30+
// Match static_cast expressions not in templates
31+
Finder->addMatcher(
32+
cxxStaticCastExpr(
33+
unless(hasAncestor(functionTemplateDecl())),
34+
unless(hasAncestor(classTemplateDecl())),
35+
unless(isInTemplateInstantiation()))
36+
.bind("cast"),
37+
this);
38+
}
39+
40+
void CleanupStaticCastCheck::check(const MatchFinder::MatchResult &Result) {
41+
const auto *Cast = Result.Nodes.getNodeAs<CXXStaticCastExpr>("cast");
42+
if (!Cast)
43+
return;
44+
45+
// Get the source expression and its type
46+
const Expr *SubExpr = Cast->getSubExpr()->IgnoreParenImpCasts();
47+
QualType SourceType = SubExpr->getType();
48+
QualType TargetType = Cast->getType();
49+
50+
// Skip if either type is dependent
51+
if (SourceType->isDependentType() || TargetType->isDependentType())
52+
return;
53+
54+
// Compare canonical types and qualifiers
55+
SourceType = SourceType.getCanonicalType();
56+
TargetType = TargetType.getCanonicalType();
57+
58+
if (SourceType == TargetType) {
59+
auto Diag =
60+
diag(Cast->getBeginLoc(),
61+
"redundant static_cast to the same type %0") // Removed single quotes
62+
<< TargetType;
63+
64+
// Use our helper function to get the source text
65+
std::string ReplacementText = getText(SubExpr, *Result.Context);
66+
67+
// Suggest removing the cast
68+
Diag << FixItHint::CreateReplacement(
69+
Cast->getSourceRange(),
70+
ReplacementText);
71+
}
72+
}
73+
74+
} // namespace clang::tidy::modernize
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// CleanupStaticCastCheck.h
2+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CLEANUPSTATICCASTCHECK_H
3+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CLEANUPSTATICCASTCHECK_H
4+
5+
#include "../ClangTidyCheck.h"
6+
7+
namespace clang::tidy::modernize {
8+
9+
/// Finds and removes static_cast where target type exactly matches source type.
10+
///
11+
/// This check helps clean up redundant static_cast operations that remain after
12+
/// type system changes, improving code readability and maintainability.
13+
///
14+
/// For the given code:
15+
/// \code
16+
/// size_t s = 42;
17+
/// foo(static_cast<size_t>(s));
18+
/// \endcode
19+
///
20+
/// The check will suggest removing the redundant cast:
21+
/// \code
22+
/// size_t s = 42;
23+
/// foo(s);
24+
/// \endcode
25+
///
26+
/// Note: This check intentionally ignores redundant casts in template instantiations
27+
/// as they might be needed for other template parameter types.
28+
class CleanupStaticCastCheck : public ClangTidyCheck {
29+
public:
30+
CleanupStaticCastCheck(StringRef Name, ClangTidyContext *Context)
31+
: ClangTidyCheck(Name, Context) {}
32+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
33+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
34+
};
35+
36+
} // namespace clang::tidy::modernize
37+
38+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CLEANUPSTATICCASTCHECK_H

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "UseNullptrCheck.h"
4343
#include "UseOverrideCheck.h"
4444
#include "UseRangesCheck.h"
45+
#include "CleanupStaticCastCheck.h"
4546
#include "UseStartsEndsWithCheck.h"
4647
#include "UseStdFormatCheck.h"
4748
#include "UseStdNumbersCheck.h"
@@ -122,6 +123,8 @@ class ModernizeModule : public ClangTidyModule {
122123
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
123124
"modernize-use-uncaught-exceptions");
124125
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
126+
CheckFactories.registerCheck<CleanupStaticCastCheck>(
127+
"modernize-cleanup-static-cast");
125128
}
126129
};
127130

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %check_clang_tidy %s modernize-cleanup-static-cast %t
2+
3+
void foo(unsigned long x) {}
4+
void bar(int x) {}
5+
6+
void test() {
7+
unsigned long s = 42;
8+
foo(static_cast<unsigned long>(s)); // Should warn
9+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: redundant static_cast to the same type 'unsigned long' [modernize-cleanup-static-cast]
10+
// CHECK-FIXES: foo(s);
11+
12+
// Different types - no warning
13+
int i = 42;
14+
foo(static_cast<unsigned long>(i));
15+
16+
// Test with typedef - should warn
17+
typedef unsigned long my_ul_t;
18+
my_ul_t ms = 42;
19+
foo(static_cast<unsigned long>(ms));
20+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: redundant static_cast to the same type 'unsigned long' [modernize-cleanup-static-cast]
21+
// CHECK-FIXES: foo(ms);
22+
}
23+
24+
// Template - no warnings
25+
template<typename T>
26+
void template_function(T value) {
27+
foo(static_cast<unsigned long>(value));
28+
}
29+
30+
void test_templates() {
31+
template_function<unsigned long>(42);
32+
template_function<int>(42);
33+
}
34+
35+
// Test multiple casts
36+
void test_multiple() {
37+
unsigned long s = 42;
38+
foo(static_cast<unsigned long>(static_cast<unsigned long>(s)));
39+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: redundant static_cast to the same type 'unsigned long' [modernize-cleanup-static-cast]
40+
// CHECK-MESSAGES: [[@LINE-2]]:34: warning: redundant static_cast to the same type 'unsigned long' [modernize-cleanup-static-cast]
41+
// CHECK-FIXES: foo(static_cast<unsigned long>(s));
42+
}

0 commit comments

Comments
 (0)