1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
16 // We don't like using C-style casts in C++ code
21 bool areSimilar(QualType type1
, QualType type2
) {
22 auto t1
= type1
.getCanonicalType().getTypePtr();
23 auto t2
= type2
.getCanonicalType().getTypePtr();
25 if (t1
->isPointerType()) {
26 if (!t2
->isPointerType()) {
29 auto t1a
= t1
->getAs
<PointerType
>();
30 auto t2a
= t2
->getAs
<PointerType
>();
31 t1
= t1a
->getPointeeType().getTypePtr();
32 t2
= t2a
->getPointeeType().getTypePtr();
33 } else if (t1
->isMemberPointerType()) {
34 if (!t2
->isMemberPointerType()) {
37 auto t1a
= t1
->getAs
<MemberPointerType
>();
38 auto t2a
= t2
->getAs
<MemberPointerType
>();
39 if (t1a
->getClass()->getCanonicalTypeInternal()
40 != t2a
->getClass()->getCanonicalTypeInternal())
44 t1
= t1a
->getPointeeType().getTypePtr();
45 t2
= t2a
->getPointeeType().getTypePtr();
46 } else if (t1
->isConstantArrayType()) {
47 if (!t2
->isConstantArrayType()) {
50 auto t1a
= static_cast<ConstantArrayType
const *>(
51 t1
->getAsArrayTypeUnsafe());
52 auto t2a
= static_cast<ConstantArrayType
const *>(
53 t2
->getAsArrayTypeUnsafe());
54 if (t1a
->getSize() != t2a
->getSize()) {
57 t1
= t1a
->getElementType().getTypePtr();
58 t2
= t2a
->getElementType().getTypePtr();
59 } else if (t1
->isIncompleteArrayType()) {
60 if (!t2
->isIncompleteArrayType()) {
63 auto t1a
= static_cast<IncompleteArrayType
const *>(
64 t1
->getAsArrayTypeUnsafe());
65 auto t2a
= static_cast<IncompleteArrayType
const *>(
66 t2
->getAsArrayTypeUnsafe());
67 t1
= t1a
->getElementType().getTypePtr();
68 t2
= t2a
->getElementType().getTypePtr();
78 QualType
resolvePointers(QualType type
) {
79 while (type
->isPointerType()) {
80 type
= type
->getAs
<PointerType
>()->getPointeeType();
86 public RecursiveASTVisitor
<CStyleCast
>, public loplugin::Plugin
89 explicit CStyleCast(InstantiationData
const & data
): Plugin(data
) {}
91 virtual void run() override
{
92 if (compiler
.getLangOpts().CPlusPlus
) {
93 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
97 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
);
99 bool VisitCStyleCastExpr(const CStyleCastExpr
* expr
);
102 bool isConstCast(QualType from
, QualType to
);
104 bool isFromCIncludeFile(SourceLocation spellingLocation
) const;
106 bool isSharedCAndCppCode(SourceLocation location
) const;
108 unsigned int externCContexts_
= 0;
111 const char * recommendedFix(clang::CastKind ck
) {
113 case CK_IntegralToPointer
: return "reinterpret_cast";
114 case CK_PointerToIntegral
: return "reinterpret_cast";
115 case CK_BaseToDerived
: return "static_cast";
116 default: return nullptr;
120 bool CStyleCast::TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
) {
121 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
123 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
124 assert(externCContexts_
!= 0);
129 bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr
* expr
) {
130 if (ignoreLocation(expr
)) {
133 // casting to void is typically used when a parameter or field is only used in
134 // debug mode, and we want to eliminate an "unused" warning
135 if( expr
->getCastKind() == CK_ToVoid
) {
138 // ignore integral-type conversions for now, there is insufficient agreement about
139 // the merits of C++ style casting in this case
140 if( expr
->getCastKind() == CK_IntegralCast
) {
143 char const * perf
= nullptr;
144 if( expr
->getCastKind() == CK_NoOp
) {
145 QualType t1
= expr
->getSubExpr()->getType();
146 QualType t2
= expr
->getType();
147 if (t1
->isPointerType() && t2
->isPointerType()) {
148 t1
= t1
->getAs
<PointerType
>()->getPointeeType();
149 t2
= t2
->getAs
<PointerType
>()->getPointeeType();
150 } else if (t1
->isLValueReferenceType() && t2
->isLValueReferenceType()) {
151 t1
= t1
->getAs
<LValueReferenceType
>()->getPointeeType();
152 t2
= t2
->getAs
<LValueReferenceType
>()->getPointeeType();
157 expr
->getSubExprAsWritten()->getType(),
158 expr
->getTypeAsWritten()))
163 if (isSharedCAndCppCode(expr
->getLocStart())) {
166 std::string incompFrom
;
167 std::string incompTo
;
168 if( expr
->getCastKind() == CK_BitCast
) {
169 if (resolvePointers(expr
->getSubExprAsWritten()->getType())
170 ->isIncompleteType())
172 incompFrom
= "incomplete ";
174 if (resolvePointers(expr
->getType())->isIncompleteType()) {
175 incompTo
= "incomplete ";
178 if (perf
== nullptr) {
179 perf
= recommendedFix(expr
->getCastKind());
181 std::string performs
;
182 if (perf
!= nullptr) {
183 performs
= std::string(" (performs: ") + perf
+ ")";
186 DiagnosticsEngine::Warning
, "C-style cast from %0%1 to %2%3%4 (%5)",
187 expr
->getSourceRange().getBegin())
188 << incompFrom
<< expr
->getSubExprAsWritten()->getType()
189 << incompTo
<< expr
->getType() << performs
190 << expr
->getCastKindName()
191 << expr
->getSourceRange();
195 bool CStyleCast::isConstCast(QualType from
, QualType to
) {
196 if (to
->isReferenceType()
197 && to
->getAs
<ReferenceType
>()->getPointeeType()->isObjectType())
199 if (!from
->isObjectType()) {
202 from
= compiler
.getASTContext().getPointerType(from
);
203 to
= compiler
.getASTContext().getPointerType(
204 to
->getAs
<ReferenceType
>()->getPointeeType());
206 if (from
->isArrayType()) {
207 from
= compiler
.getASTContext().getPointerType(
208 from
->getAsArrayTypeUnsafe()->getElementType());
209 } else if (from
->isFunctionType()) {
210 compiler
.getASTContext().getPointerType(from
);
213 return areSimilar(from
, to
);
216 bool CStyleCast::isFromCIncludeFile(SourceLocation spellingLocation
) const {
217 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
219 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
224 bool CStyleCast::isSharedCAndCppCode(SourceLocation location
) const {
225 while (compiler
.getSourceManager().isMacroArgExpansion(location
)) {
226 location
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
229 // Assume that code is intended to be shared between C and C++ if it comes
230 // from an include file ending in .h, and is either in an extern "C" context
231 // or the body of a macro definition:
233 isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
234 && (externCContexts_
!= 0
235 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
238 loplugin::Plugin::Registration
< CStyleCast
> X("cstylecast");
242 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */