cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / cstylecast.cxx
blobaddf5edd28ba618bfc98fbcbe34bdbb6331fc18a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <cassert>
11 #include <limits>
12 #include <string>
13 #include "plugin.hxx"
16 // We don't like using C-style casts in C++ code
19 namespace {
21 bool areSimilar(QualType type1, QualType type2) {
22 auto t1 = type1.getCanonicalType().getTypePtr();
23 auto t2 = type2.getCanonicalType().getTypePtr();
24 for (;;) {
25 if (t1->isPointerType()) {
26 if (!t2->isPointerType()) {
27 return false;
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()) {
35 return false;
37 auto t1a = t1->getAs<MemberPointerType>();
38 auto t2a = t2->getAs<MemberPointerType>();
39 if (t1a->getClass()->getCanonicalTypeInternal()
40 != t2a->getClass()->getCanonicalTypeInternal())
42 return false;
44 t1 = t1a->getPointeeType().getTypePtr();
45 t2 = t2a->getPointeeType().getTypePtr();
46 } else if (t1->isConstantArrayType()) {
47 if (!t2->isConstantArrayType()) {
48 return false;
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()) {
55 return false;
57 t1 = t1a->getElementType().getTypePtr();
58 t2 = t2a->getElementType().getTypePtr();
59 } else if (t1->isIncompleteArrayType()) {
60 if (!t2->isIncompleteArrayType()) {
61 return false;
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();
69 } else {
70 return false;
72 if (t1 == t2) {
73 return true;
78 QualType resolvePointers(QualType type) {
79 while (type->isPointerType()) {
80 type = type->getAs<PointerType>()->getPointeeType();
82 return type;
85 class CStyleCast:
86 public RecursiveASTVisitor<CStyleCast>, public loplugin::Plugin
88 public:
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);
101 private:
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) {
112 switch(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
122 ++externCContexts_;
123 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
124 assert(externCContexts_ != 0);
125 --externCContexts_;
126 return ret;
129 bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) {
130 if (ignoreLocation(expr)) {
131 return true;
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 ) {
136 return true;
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 ) {
141 return true;
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();
153 } else {
154 return true;
156 if (isConstCast(
157 expr->getSubExprAsWritten()->getType(),
158 expr->getTypeAsWritten()))
160 perf = "const_cast";
163 if (isSharedCAndCppCode(expr->getLocStart())) {
164 return true;
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 + ")";
185 report(
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();
192 return true;
195 bool CStyleCast::isConstCast(QualType from, QualType to) {
196 if (to->isReferenceType()
197 && to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
199 if (!from->isObjectType()) {
200 return false;
202 from = compiler.getASTContext().getPointerType(from);
203 to = compiler.getASTContext().getPointerType(
204 to->getAs<ReferenceType>()->getPointeeType());
205 } else {
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)
218 && (StringRef(
219 compiler.getSourceManager().getPresumedLoc(spellingLocation)
220 .getFilename())
221 .endswith(".h"));
224 bool CStyleCast::isSharedCAndCppCode(SourceLocation location) const {
225 while (compiler.getSourceManager().isMacroArgExpansion(location)) {
226 location = compiler.getSourceManager().getImmediateMacroCallerLoc(
227 location);
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:
232 return
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: */