10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/Lex/Lexer.h"
20 ast_matchers::MatchFinder *Finder) {
26 unless(hasParent(substNonTypeTemplateParmExpr())),
28 unless(isInTemplateInstantiation()))
32 cxxFunctionalCastExpr(unless(hasDescendant(cxxConstructExpr())),
33 unless(hasDescendant(initListExpr())))
39 while ((SourceType->isPointerType() && DestType->isPointerType()) ||
40 (SourceType->isReferenceType() && DestType->isReferenceType())) {
41 SourceType = SourceType->getPointeeType();
42 DestType = DestType->getPointeeType();
43 if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
44 return (SourceType->isPointerType() == DestType->isPointerType()) &&
45 (SourceType->isReferenceType() == DestType->isReferenceType());
52 while ((T1->isPointerType() && T2->isPointerType()) ||
53 (T1->isReferenceType() && T2->isReferenceType())) {
54 T1 = T1->getPointeeType();
55 T2 = T2->getPointeeType();
57 return T1.getUnqualifiedType() == T2.getUnqualifiedType();
61 if (
const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr))
62 return CharSourceRange::getCharRange(
63 CastExpr->getLParenLoc(),
64 CastExpr->getSubExprAsWritten()->getBeginLoc());
65 if (
const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr))
66 return CharSourceRange::getCharRange(CastExpr->getBeginLoc(),
67 CastExpr->getLParenLoc());
68 llvm_unreachable(
"Unsupported CastExpr");
72 const LangOptions &LangOpts,
73 const ExplicitCastExpr *Expr) {
74 SourceLocation BeginLoc;
75 SourceLocation EndLoc;
77 if (
const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr)) {
78 BeginLoc = CastExpr->getLParenLoc().getLocWithOffset(1);
79 EndLoc = CastExpr->getRParenLoc().getLocWithOffset(-1);
80 }
else if (
const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr)) {
81 BeginLoc = CastExpr->getBeginLoc();
82 EndLoc = CastExpr->getLParenLoc().getLocWithOffset(-1);
84 llvm_unreachable(
"Unsupported CastExpr");
86 return Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
91 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>(
"cast");
94 if (CastExpr->getExprLoc().isMacroID())
99 if (CastExpr->getCastKind() == CK_ToVoid)
102 auto IsFunction = [](QualType T) {
103 T = T.getCanonicalType().getNonReferenceType();
104 return T->isFunctionType() || T->isFunctionPointerType() ||
105 T->isMemberFunctionPointerType();
108 const QualType DestTypeAsWritten =
109 CastExpr->getTypeAsWritten().getUnqualifiedType();
110 const QualType SourceTypeAsWritten =
111 CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
112 const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
113 const QualType DestType = DestTypeAsWritten.getCanonicalType();
118 IsFunction(SourceTypeAsWritten) && IsFunction(DestTypeAsWritten);
120 const bool ConstructorCast = !CastExpr->getTypeAsWritten().hasQualifiers() &&
121 DestTypeAsWritten->isRecordType() &&
122 !DestTypeAsWritten->isElaboratedTypeSpecifier();
124 if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
129 QualType
Src = SourceTypeAsWritten, Dst = DestTypeAsWritten;
130 if (
const auto *ElTy = dyn_cast<ElaboratedType>(
Src))
131 Src = ElTy->getNamedType();
132 if (
const auto *ElTy = dyn_cast<ElaboratedType>(Dst))
133 Dst = ElTy->getNamedType();
135 diag(CastExpr->getBeginLoc(),
"redundant cast to the same type")
136 << FixItHint::CreateRemoval(ReplaceRange);
146 if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
154 SourceManager &SM = *Result.SourceManager;
158 if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(
".c"))
166 diag(CastExpr->getBeginLoc(),
"C-style casts are discouraged; use %0");
168 auto ReplaceWithCast = [&](std::string CastText) {
169 const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
170 if (!isa<ParenExpr>(SubExpr) && !isa<CXXFunctionalCastExpr>(CastExpr)) {
171 CastText.push_back(
'(');
172 Diag << FixItHint::CreateInsertion(
173 Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,
177 Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
179 auto ReplaceWithNamedCast = [&](StringRef CastType) {
181 ReplaceWithCast((CastType +
"<" + DestTypeString +
">").str());
183 auto ReplaceWithConstructorCall = [&]() {
184 Diag <<
"constructor call syntax";
186 ReplaceWithCast(DestTypeString.str());
189 switch (CastExpr->getCastKind()) {
190 case CK_FunctionToPointerDecay:
191 ReplaceWithNamedCast(
"static_cast");
193 case CK_ConstructorConversion:
194 if (ConstructorCast) {
195 ReplaceWithConstructorCall();
197 ReplaceWithNamedCast(
"static_cast");
202 ReplaceWithNamedCast(
"static_cast");
205 if (SourceType == DestType) {
206 Diag <<
"static_cast (if needed, the cast may be redundant)";
207 ReplaceWithCast((
"static_cast<" + DestTypeString +
">").str());
212 ReplaceWithNamedCast(
"const_cast");
215 if (ConstructorCast) {
216 ReplaceWithConstructorCall();
219 if (DestType->isReferenceType()) {
220 QualType Dest = DestType.getNonReferenceType();
221 QualType Source = SourceType.getNonReferenceType();
222 if (Source == Dest.withConst() ||
223 SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
224 ReplaceWithNamedCast(
"const_cast");
230 case clang::CK_IntegralCast:
234 if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
235 (DestType->isBuiltinType() || DestType->isEnumeralType())) {
236 ReplaceWithNamedCast(
"static_cast");
243 if (SourceType->isVoidPointerType())
244 ReplaceWithNamedCast(
"static_cast");
246 ReplaceWithNamedCast(
"reinterpret_cast");
254 Diag <<
"static_cast/const_cast/reinterpret_cast";
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
StringRef getCurrentMainFile() const
Returns the main file name of the current translation unit.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static bool needsConstCast(QualType SourceType, QualType DestType)
static StringRef getDestTypeString(const SourceManager &SM, const LangOptions &LangOpts, const ExplicitCastExpr *Expr)
static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2)
static clang::CharSourceRange getReplaceRange(const ExplicitCastExpr *Expr)