Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / cstylecast.cxx
blob35292ecd8fb65c13d51034aac3d23b05270c9b40
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 <algorithm>
11 #include <cassert>
12 #include <limits>
13 #include <set>
14 #include <string>
16 #include "compat.hxx"
17 #include "plugin.hxx"
20 // We don't like using C-style casts in C++ code. Similarly, warn about function-style casts (which
21 // are semantically equivalent to C-style casts) that are not semantically equivalent to static_cast
22 // and should rather be written as const_cast or reinterpret_cast.
25 namespace {
27 bool areSimilar(QualType type1, QualType type2) {
28 auto t1 = type1.getCanonicalType().getTypePtr();
29 auto t2 = type2.getCanonicalType().getTypePtr();
30 for (;;) {
31 if (t1->isPointerType()) {
32 if (!t2->isPointerType()) {
33 return false;
35 auto t1a = t1->getAs<clang::PointerType>();
36 auto t2a = t2->getAs<clang::PointerType>();
37 t1 = t1a->getPointeeType().getTypePtr();
38 t2 = t2a->getPointeeType().getTypePtr();
39 } else if (t1->isMemberPointerType()) {
40 if (!t2->isMemberPointerType()) {
41 return false;
43 auto t1a = t1->getAs<MemberPointerType>();
44 auto t2a = t2->getAs<MemberPointerType>();
45 if (t1a->getClass()->getCanonicalTypeInternal()
46 != t2a->getClass()->getCanonicalTypeInternal())
48 return false;
50 t1 = t1a->getPointeeType().getTypePtr();
51 t2 = t2a->getPointeeType().getTypePtr();
52 } else if (t1->isConstantArrayType()) {
53 if (!t2->isConstantArrayType()) {
54 return false;
56 auto t1a = static_cast<ConstantArrayType const *>(
57 t1->getAsArrayTypeUnsafe());
58 auto t2a = static_cast<ConstantArrayType const *>(
59 t2->getAsArrayTypeUnsafe());
60 if (t1a->getSize() != t2a->getSize()) {
61 return false;
63 t1 = t1a->getElementType().getTypePtr();
64 t2 = t2a->getElementType().getTypePtr();
65 } else if (t1->isIncompleteArrayType()) {
66 if (!t2->isIncompleteArrayType()) {
67 return false;
69 auto t1a = static_cast<IncompleteArrayType const *>(
70 t1->getAsArrayTypeUnsafe());
71 auto t2a = static_cast<IncompleteArrayType const *>(
72 t2->getAsArrayTypeUnsafe());
73 t1 = t1a->getElementType().getTypePtr();
74 t2 = t2a->getElementType().getTypePtr();
75 } else {
76 return false;
78 if (t1 == t2) {
79 return true;
84 QualType resolvePointers(QualType type) {
85 while (type->isPointerType()) {
86 type = type->getAs<clang::PointerType>()->getPointeeType();
88 return type;
91 bool isLiteralLike(Expr const * expr) {
92 expr = expr->IgnoreParenImpCasts();
93 if (isa<IntegerLiteral>(expr) || isa<CharacterLiteral>(expr) || isa<FloatingLiteral>(expr)
94 || isa<ImaginaryLiteral>(expr) || isa<CXXBoolLiteralExpr>(expr)
95 || isa<CXXNullPtrLiteralExpr>(expr) || isa<ObjCBoolLiteralExpr>(expr))
97 return true;
99 if (auto const e = dyn_cast<DeclRefExpr>(expr)) {
100 auto const d = e->getDecl();
101 if (isa<EnumConstantDecl>(d)) {
102 return true;
104 if (auto const v = dyn_cast<VarDecl>(d)) {
105 if (d->getType().isConstQualified()) {
106 if (auto const init = v->getAnyInitializer()) {
107 return isLiteralLike(init);
111 return false;
113 if (auto const e = dyn_cast<UnaryExprOrTypeTraitExpr>(expr)) {
114 auto const k = e->getKind();
115 return k == UETT_SizeOf || k == UETT_AlignOf;
117 if (auto const e = dyn_cast<UnaryOperator>(expr)) {
118 auto const k = e->getOpcode();
119 if (k == UO_Plus || k == UO_Minus || k == UO_Not || k == UO_LNot) {
120 return isLiteralLike(e->getSubExpr());
122 return false;
124 if (auto const e = dyn_cast<BinaryOperator>(expr)) {
125 auto const k = e->getOpcode();
126 if (k == BO_Mul || k == BO_Div || k == BO_Rem || k == BO_Add || k == BO_Sub || k == BO_Shl
127 || k == BO_Shr || k == BO_And || k == BO_Xor || k == BO_Or)
129 return isLiteralLike(e->getLHS()) && isLiteralLike(e->getRHS());
131 return false;
133 if (auto const e = dyn_cast<ExplicitCastExpr>(expr)) {
134 auto const t = e->getTypeAsWritten();
135 return (t->isArithmeticType() || t->isEnumeralType())
136 && isLiteralLike(e->getSubExprAsWritten());
138 return false;
141 bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) {
142 // Must be <simple-type-specifier> or <typename-specifier>, lets approximate that here:
143 assert(info != nullptr);
144 auto const type = info->getType();
145 if (type.hasLocalQualifiers()) {
146 return false;
148 if (auto const t = dyn_cast<BuiltinType>(type)) {
149 if (!(t->isInteger() || t->isFloatingPoint())) {
150 return false;
152 auto const loc = info->getTypeLoc().castAs<BuiltinTypeLoc>();
153 return
154 (int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec())
155 + int(loc.hasWrittenTypeSpec()))
156 == 1;
158 if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
159 || isa<DecltypeType>(type) || isa<TypedefType>(type))
161 return true;
163 if (auto const t = dyn_cast<ElaboratedType>(type)) {
164 return t->getKeyword() == ETK_None;
166 return false;
169 class CStyleCast:
170 public loplugin::FilteringRewritePlugin<CStyleCast>
172 public:
173 explicit CStyleCast(loplugin::InstantiationData const & data): FilteringRewritePlugin(data)
176 virtual void run() override {
177 if (compiler.getLangOpts().CPlusPlus) {
178 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
182 bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
183 return WalkUpFromInitListExpr(expr)
184 && TraverseSynOrSemInitListExpr(
185 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
188 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
190 bool VisitCStyleCastExpr(const CStyleCastExpr * expr);
192 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr);
194 private:
195 bool isConstCast(QualType from, QualType to);
197 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
199 bool isSharedCAndCppCode(SourceLocation location) const;
201 bool isLastTokenOfImmediateMacroBodyExpansion(
202 SourceLocation loc, SourceLocation * macroEnd = nullptr) const;
204 bool rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement);
206 void reportCast(ExplicitCastExpr const * expr, char const * performsHint);
208 unsigned int externCContexts_ = 0;
209 std::set<SourceLocation> rewritten_;
210 // needed when rewriting in macros, in general to avoid "double code replacement, possible
211 // plugin error" warnings, and in particular to avoid adding multiple sets of parens around
212 // sub-exprs
213 std::set<CStyleCastExpr const *> rewrittenSubExprs_;
216 const char * recommendedFix(clang::CastKind ck) {
217 switch(ck) {
218 case CK_IntegralToPointer: return "reinterpret_cast";
219 case CK_PointerToIntegral: return "reinterpret_cast";
220 case CK_BaseToDerived: return "static_cast";
221 default: return nullptr;
225 bool CStyleCast::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
226 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
227 ++externCContexts_;
228 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
229 assert(externCContexts_ != 0);
230 --externCContexts_;
231 return ret;
234 bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) {
235 if (ignoreLocation(expr)) {
236 return true;
238 // casting to void is typically used when a parameter or field is only used in
239 // debug mode, and we want to eliminate an "unused" warning
240 if( expr->getCastKind() == CK_ToVoid ) {
241 return true;
243 if (isSharedCAndCppCode(expr->getBeginLoc())) {
244 return true;
246 char const * perf = nullptr;
247 if( expr->getCastKind() == CK_IntegralCast ) {
248 if (rewriteArithmeticCast(expr, &perf)) {
249 return true;
251 } else if( expr->getCastKind() == CK_NoOp ) {
252 if (!((expr->getSubExpr()->getType()->isPointerType()
253 && expr->getType()->isPointerType())
254 || expr->getTypeAsWritten()->isReferenceType()))
256 if (rewriteArithmeticCast(expr, &perf)) {
257 return true;
260 if (isConstCast(
261 expr->getSubExprAsWritten()->getType(),
262 expr->getTypeAsWritten()))
264 perf = "const_cast";
267 reportCast(expr, perf);
268 return true;
271 bool CStyleCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
272 if (ignoreLocation(expr)) {
273 return true;
275 char const * perf = nullptr;
276 switch (expr->getCastKind()) {
277 case CK_ConstructorConversion:
278 case CK_Dependent: //TODO: really filter out all of these?
279 case CK_IntegralCast:
280 case CK_IntegralToBoolean:
281 case CK_ToVoid:
282 return true;
283 case CK_NoOp:
284 if (isConstCast(
285 expr->getSubExprAsWritten()->getType(),
286 expr->getTypeAsWritten()))
288 perf = "const_cast";
289 break;
291 return true; //TODO: really filter out all of these?
292 default:
293 break;
295 reportCast(expr, perf);
296 return true;
299 bool CStyleCast::isConstCast(QualType from, QualType to) {
300 if (to->isReferenceType()
301 && to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
303 if (!from->isObjectType()) {
304 return false;
306 from = compiler.getASTContext().getPointerType(from);
307 to = compiler.getASTContext().getPointerType(
308 to->getAs<ReferenceType>()->getPointeeType());
309 } else {
310 if (from->isArrayType()) {
311 from = compiler.getASTContext().getPointerType(
312 from->getAsArrayTypeUnsafe()->getElementType());
313 } else if (from->isFunctionType()) {
314 compiler.getASTContext().getPointerType(from);
317 return areSimilar(from, to);
320 bool CStyleCast::isFromCIncludeFile(SourceLocation spellingLocation) const {
321 return !compiler.getSourceManager().isInMainFile(spellingLocation)
322 && (StringRef(
323 compiler.getSourceManager().getPresumedLoc(spellingLocation)
324 .getFilename())
325 .endswith(".h"));
328 bool CStyleCast::isSharedCAndCppCode(SourceLocation location) const {
329 while (compiler.getSourceManager().isMacroArgExpansion(location)) {
330 location = compiler.getSourceManager().getImmediateMacroCallerLoc(
331 location);
333 // Assume that code is intended to be shared between C and C++ if it comes
334 // from an include file ending in .h, and is either in an extern "C" context
335 // or the body of a macro definition:
336 return
337 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
338 && (externCContexts_ != 0
339 || compiler.getSourceManager().isMacroBodyExpansion(location));
342 bool CStyleCast::isLastTokenOfImmediateMacroBodyExpansion(
343 SourceLocation loc, SourceLocation * macroEnd) const
345 assert(compiler.getSourceManager().isMacroBodyExpansion(loc));
346 auto const spell = compiler.getSourceManager().getSpellingLoc(loc);
347 auto name = Lexer::getImmediateMacroName(
348 loc, compiler.getSourceManager(), compiler.getLangOpts());
349 while (name.startswith("\\\n")) {
350 name = name.drop_front(2);
351 while (!name.empty()
352 && (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
353 || name.front() == '\v' || name.front() == '\f'))
355 name = name.drop_front(1);
358 auto const MI
359 = (compiler.getPreprocessor().getMacroDefinitionAtLoc(
360 &compiler.getASTContext().Idents.get(name), spell)
361 .getMacroInfo());
362 assert(MI != nullptr);
363 if (spell == MI->getDefinitionEndLoc()) {
364 if (macroEnd != nullptr) {
365 *macroEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).second;
367 return true;
369 return false;
372 bool CStyleCast::rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement) {
373 assert(replacement != nullptr);
374 auto const sub = expr->getSubExprAsWritten();
375 auto const functional = isLiteralLike(sub)
376 && canBeUsedForFunctionalCast(expr->getTypeInfoAsWritten());
377 *replacement = functional ? "functional cast" : "static_cast";
378 if (rewriter == nullptr) {
379 return false;
381 // Doing modifications for a chain of C-style casts as in
383 // (foo)(bar)(baz)x
385 // leads to unpredictable results, so only rewrite them one at a time, starting with the
386 // outermost:
387 if (auto const e = dyn_cast<CStyleCastExpr>(sub)) {
388 rewrittenSubExprs_.insert(e);
390 if (rewrittenSubExprs_.find(expr) != rewrittenSubExprs_.end()) {
391 return false;
393 // Two or four ranges to replace:
394 // First is the CStyleCast's LParen, plus following whitespace, replaced with either "" or
395 // "static_cast<". (TODO: insert space before "static_cast<" when converting "else(int)...".)
396 // Second is the CStyleCast's RParen, plus preceding and following whitespace, replaced with
397 // either "" or ">".
398 // If the sub expr is not a ParenExpr, third is the sub expr's begin, inserting "(", and fourth
399 // is the sub expr's end, inserting ")".
400 // (The reason the second and third are not combined is in case there's a comment between them.)
401 auto firstBegin = expr->getLParenLoc();
402 auto secondBegin = expr->getRParenLoc();
403 while (compiler.getSourceManager().isMacroArgExpansion(firstBegin)
404 && compiler.getSourceManager().isMacroArgExpansion(secondBegin)
405 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), firstBegin)
406 == compat::getImmediateExpansionRange(compiler.getSourceManager(), secondBegin)))
408 firstBegin = compiler.getSourceManager().getImmediateSpellingLoc(firstBegin);
409 secondBegin = compiler.getSourceManager().getImmediateSpellingLoc(secondBegin);
411 if (compiler.getSourceManager().isMacroBodyExpansion(firstBegin)
412 && compiler.getSourceManager().isMacroBodyExpansion(secondBegin)
413 && (compiler.getSourceManager().getImmediateMacroCallerLoc(firstBegin)
414 == compiler.getSourceManager().getImmediateMacroCallerLoc(secondBegin)))
416 firstBegin = compiler.getSourceManager().getSpellingLoc(firstBegin);
417 secondBegin = compiler.getSourceManager().getSpellingLoc(secondBegin);
419 auto third = sub->getBeginLoc();
420 auto fourth = sub->getEndLoc();
421 bool macro = false;
422 // Ensure that
424 // #define FOO(x) (int)x
425 // FOO(y)
427 // is changed to
429 // #define FOO(x) static_cast<int>(x)
430 // FOO(y)
432 // instead of
434 // #define FOO(x) static_cast<int>x
435 // FOO((y))
436 while (compiler.getSourceManager().isMacroArgExpansion(third)
437 && compiler.getSourceManager().isMacroArgExpansion(fourth)
438 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
439 == compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth))
440 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third))
441 //TODO: check fourth is at end of immediate macro expansion, but
442 // SourceManager::isAtEndOfImmediateMacroExpansion requires a location pointing at the
443 // character end of the last token
445 auto const range = compat::getImmediateExpansionRange(compiler.getSourceManager(), third);
446 third = range.first;
447 fourth = range.second;
448 macro = true;
449 assert(third.isValid());
451 while (compiler.getSourceManager().isMacroArgExpansion(third)
452 && compiler.getSourceManager().isMacroArgExpansion(fourth)
453 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
454 == compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth)))
456 third = compiler.getSourceManager().getImmediateSpellingLoc(third);
457 fourth = compiler.getSourceManager().getImmediateSpellingLoc(fourth);
459 if (isa<ParenExpr>(sub)) {
460 // Ensure that with
462 // #define FOO (x)
464 // a cast like
466 // (int) FOO
468 // is changed to
470 // static_cast<int>(FOO)
472 // instead of
474 // static_cast<int>FOO
475 for (;; macro = true) {
476 if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
477 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
478 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
479 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
480 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)
481 && isLastTokenOfImmediateMacroBodyExpansion(fourth)))
483 if (!macro) {
484 third = fourth = SourceLocation();
486 break;
488 auto const range = compat::getImmediateExpansionRange(
489 compiler.getSourceManager(), third);
490 third = range.first;
491 fourth = range.second;
492 assert(third.isValid());
494 if (third.isValid() && compiler.getSourceManager().isMacroBodyExpansion(third)
495 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
496 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
497 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
499 third = compiler.getSourceManager().getSpellingLoc(third);
500 fourth = compiler.getSourceManager().getSpellingLoc(fourth);
501 assert(third.isValid());
503 } else {
504 // Ensure that a cast like
506 // (int)LONG_MAX
508 // (where LONG_MAX expands to __LONG_MAX__, which in turn is a built-in expanding to a value
509 // like 9223372036854775807L) is changed to
511 // int(LONG_MAX)
513 // instead of trying to add the parentheses to the built-in __LONG_MAX__ definition:
514 for (;;) {
515 if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
516 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
517 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
518 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
519 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)))
520 // TODO: check that fourth is at end of immediate macro expansion (but
521 // SourceManager::isAtEndOfImmediateMacroExpansion wants a location pointing at the
522 // character end)
524 break;
526 auto const range = compat::getImmediateExpansionRange(
527 compiler.getSourceManager(), third);
528 third = range.first;
529 fourth = range.second;
531 // ...and additionally asymmetrically unwind macros only at the start or end, for code like
533 // (long)ubidi_getVisualIndex(...)
535 // (in editeng/source/editeng/impedit2.cxx) where ubidi_getVisualIndex is an object-like
536 // macro, or
538 // #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
540 // (in hwpfilter/source/lexer.cxx):
541 if (!fourth.isMacroID()) {
542 while (compiler.getSourceManager().isMacroBodyExpansion(third)
543 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third))
545 } else if (compiler.getSourceManager().isMacroBodyExpansion(fourth)) {
546 while (compiler.getSourceManager().isMacroArgExpansion(third)
547 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third)) {}
549 if (!third.isMacroID()) {
550 while (compiler.getSourceManager().isMacroBodyExpansion(fourth)
551 && isLastTokenOfImmediateMacroBodyExpansion(fourth, &fourth))
553 } else if (compiler.getSourceManager().isMacroBodyExpansion(third)) {
554 while (compiler.getSourceManager().isMacroArgExpansion(fourth, &fourth)) {}
556 if (compiler.getSourceManager().isMacroBodyExpansion(third)
557 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
558 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
559 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
561 third = compiler.getSourceManager().getSpellingLoc(third);
562 fourth = compiler.getSourceManager().getSpellingLoc(fourth);
564 assert(third.isValid());
566 if (firstBegin.isMacroID() || secondBegin.isMacroID() || (third.isValid() && third.isMacroID())
567 || (fourth.isValid() && fourth.isMacroID()))
569 if (isDebugMode()) {
570 report(
571 DiagnosticsEngine::Fatal,
572 "TODO: cannot rewrite C-style cast in macro, needs investigation",
573 expr->getExprLoc())
574 << expr->getSourceRange();
576 return false;
578 unsigned firstLen = Lexer::MeasureTokenLength(
579 firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
580 for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
581 l = l.getLocWithOffset(1))
583 unsigned n = Lexer::MeasureTokenLength(
584 l, compiler.getSourceManager(), compiler.getLangOpts());
585 if (n != 0) {
586 break;
588 ++firstLen;
590 unsigned secondLen = Lexer::MeasureTokenLength(
591 secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
592 for (auto l = secondBegin.getLocWithOffset(std::max<unsigned>(secondLen, 1));;
593 l = l.getLocWithOffset(1))
595 unsigned n = Lexer::MeasureTokenLength(
596 l, compiler.getSourceManager(), compiler.getLangOpts());
597 if (n != 0) {
598 break;
600 ++secondLen;
602 for (;;) {
603 auto l = secondBegin.getLocWithOffset(-1);
604 auto const c = compiler.getSourceManager().getCharacterData(l)[0];
605 if (c == '\n') {
606 if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
607 break;
609 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
610 break;
612 secondBegin = l;
613 ++secondLen;
615 if (rewritten_.insert(firstBegin).second) {
616 if (!replaceText(firstBegin, firstLen, functional ? "" : "static_cast<")) {
617 if (isDebugMode()) {
618 report(
619 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #1, needs investigation",
620 firstBegin);
621 report(
622 DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
623 << expr->getSourceRange();
625 return false;
627 if (!replaceText(secondBegin, secondLen, functional ? "" : ">")) {
628 //TODO: roll back
629 if (isDebugMode()) {
630 report(
631 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #2, needs investigation",
632 secondBegin);
633 report(
634 DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
635 << expr->getSourceRange();
637 return false;
640 if (third.isValid()) {
641 if (rewritten_.insert(third).second) {
642 if (!insertTextBefore(third, "(")) {
643 //TODO: roll back
644 if (isDebugMode()) {
645 report(
646 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #3, needs investigation",
647 third);
648 report(
649 DiagnosticsEngine::Note, "when rewriting this C-style cast",
650 expr->getExprLoc())
651 << expr->getSourceRange();
653 return false;
655 if (!insertTextAfterToken(fourth, ")")) {
656 //TODO: roll back
657 if (isDebugMode()) {
658 report(
659 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #4, needs investigation",
660 third);
661 report(
662 DiagnosticsEngine::Note, "when rewriting this C-style cast",
663 expr->getExprLoc())
664 << expr->getSourceRange();
666 return false;
670 return true;
673 void CStyleCast::reportCast(ExplicitCastExpr const * expr, char const * performsHint) {
674 std::string incompFrom;
675 std::string incompTo;
676 if( expr->getCastKind() == CK_BitCast ) {
677 if (resolvePointers(expr->getSubExprAsWritten()->getType())
678 ->isIncompleteType())
680 incompFrom = "incomplete ";
682 if (resolvePointers(expr->getType())->isIncompleteType()) {
683 incompTo = "incomplete ";
686 if (performsHint == nullptr) {
687 performsHint = recommendedFix(expr->getCastKind());
689 std::string performs;
690 if (performsHint != nullptr) {
691 performs = std::string(" (performs: ") + performsHint + ")";
693 report(
694 DiagnosticsEngine::Warning, "%select{C|Function}0-style cast from %1%2 to %3%4%5 (%6)",
695 expr->getSourceRange().getBegin())
696 << isa<CXXFunctionalCastExpr>(expr)
697 << incompFrom << expr->getSubExprAsWritten()->getType()
698 << incompTo << expr->getTypeAsWritten() << performs
699 << expr->getCastKindName()
700 << expr->getSourceRange();
703 loplugin::Plugin::Registration< CStyleCast > X("cstylecast", true);
707 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */