bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / cstylecast.cxx
blobfe3b2a19c5617eff0cc8b4e1a7e42190ec6d5605
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
23 namespace {
25 bool areSimilar(QualType type1, QualType type2) {
26 auto t1 = type1.getCanonicalType().getTypePtr();
27 auto t2 = type2.getCanonicalType().getTypePtr();
28 for (;;) {
29 if (t1->isPointerType()) {
30 if (!t2->isPointerType()) {
31 return false;
33 auto t1a = t1->getAs<clang::PointerType>();
34 auto t2a = t2->getAs<clang::PointerType>();
35 t1 = t1a->getPointeeType().getTypePtr();
36 t2 = t2a->getPointeeType().getTypePtr();
37 } else if (t1->isMemberPointerType()) {
38 if (!t2->isMemberPointerType()) {
39 return false;
41 auto t1a = t1->getAs<MemberPointerType>();
42 auto t2a = t2->getAs<MemberPointerType>();
43 if (t1a->getClass()->getCanonicalTypeInternal()
44 != t2a->getClass()->getCanonicalTypeInternal())
46 return false;
48 t1 = t1a->getPointeeType().getTypePtr();
49 t2 = t2a->getPointeeType().getTypePtr();
50 } else if (t1->isConstantArrayType()) {
51 if (!t2->isConstantArrayType()) {
52 return false;
54 auto t1a = static_cast<ConstantArrayType const *>(
55 t1->getAsArrayTypeUnsafe());
56 auto t2a = static_cast<ConstantArrayType const *>(
57 t2->getAsArrayTypeUnsafe());
58 if (t1a->getSize() != t2a->getSize()) {
59 return false;
61 t1 = t1a->getElementType().getTypePtr();
62 t2 = t2a->getElementType().getTypePtr();
63 } else if (t1->isIncompleteArrayType()) {
64 if (!t2->isIncompleteArrayType()) {
65 return false;
67 auto t1a = static_cast<IncompleteArrayType const *>(
68 t1->getAsArrayTypeUnsafe());
69 auto t2a = static_cast<IncompleteArrayType const *>(
70 t2->getAsArrayTypeUnsafe());
71 t1 = t1a->getElementType().getTypePtr();
72 t2 = t2a->getElementType().getTypePtr();
73 } else {
74 return false;
76 if (t1 == t2) {
77 return true;
82 QualType resolvePointers(QualType type) {
83 while (type->isPointerType()) {
84 type = type->getAs<clang::PointerType>()->getPointeeType();
86 return type;
89 bool isLiteralLike(Expr const * expr) {
90 expr = expr->IgnoreParenImpCasts();
91 if (isa<IntegerLiteral>(expr) || isa<CharacterLiteral>(expr) || isa<FloatingLiteral>(expr)
92 || isa<ImaginaryLiteral>(expr) || isa<CXXBoolLiteralExpr>(expr)
93 || isa<CXXNullPtrLiteralExpr>(expr) || isa<ObjCBoolLiteralExpr>(expr))
95 return true;
97 if (auto const e = dyn_cast<DeclRefExpr>(expr)) {
98 auto const d = e->getDecl();
99 if (isa<EnumConstantDecl>(d)) {
100 return true;
102 if (auto const v = dyn_cast<VarDecl>(d)) {
103 if (d->getType().isConstQualified()) {
104 if (auto const init = v->getAnyInitializer()) {
105 return isLiteralLike(init);
109 return false;
111 if (auto const e = dyn_cast<UnaryExprOrTypeTraitExpr>(expr)) {
112 auto const k = e->getKind();
113 return k == UETT_SizeOf || k == UETT_AlignOf;
115 if (auto const e = dyn_cast<UnaryOperator>(expr)) {
116 auto const k = e->getOpcode();
117 if (k == UO_Plus || k == UO_Minus || k == UO_Not || k == UO_LNot) {
118 return isLiteralLike(e->getSubExpr());
120 return false;
122 if (auto const e = dyn_cast<BinaryOperator>(expr)) {
123 auto const k = e->getOpcode();
124 if (k == BO_Mul || k == BO_Div || k == BO_Rem || k == BO_Add || k == BO_Sub || k == BO_Shl
125 || k == BO_Shr || k == BO_And || k == BO_Xor || k == BO_Or)
127 return isLiteralLike(e->getLHS()) && isLiteralLike(e->getRHS());
129 return false;
131 if (auto const e = dyn_cast<ExplicitCastExpr>(expr)) {
132 auto const t = e->getTypeAsWritten();
133 return (t->isArithmeticType() || t->isEnumeralType())
134 && isLiteralLike(e->getSubExprAsWritten());
136 return false;
139 bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) {
140 // Must be <simple-type-specifier> or <typename-specifier>, lets approximate that here:
141 assert(info != nullptr);
142 auto const type = info->getType();
143 if (type.hasLocalQualifiers()) {
144 return false;
146 if (auto const t = dyn_cast<BuiltinType>(type)) {
147 if (!(t->isInteger() || t->isFloatingPoint())) {
148 return false;
150 auto const loc = info->getTypeLoc().castAs<BuiltinTypeLoc>();
151 return
152 (int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec())
153 + int(loc.hasWrittenTypeSpec()))
154 == 1;
156 if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
157 || isa<DecltypeType>(type) || isa<TypedefType>(type))
159 return true;
161 if (auto const t = dyn_cast<ElaboratedType>(type)) {
162 return t->getKeyword() == ETK_None;
164 return false;
167 class CStyleCast:
168 public loplugin::FilteringRewritePlugin<CStyleCast>
170 public:
171 explicit CStyleCast(loplugin::InstantiationData const & data): FilteringRewritePlugin(data)
174 virtual void run() override {
175 if (compiler.getLangOpts().CPlusPlus) {
176 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
180 bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
181 return WalkUpFromInitListExpr(expr)
182 && TraverseSynOrSemInitListExpr(
183 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
186 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
188 bool VisitCStyleCastExpr(const CStyleCastExpr * expr);
190 private:
191 bool isConstCast(QualType from, QualType to);
193 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
195 bool isSharedCAndCppCode(SourceLocation location) const;
197 bool isLastTokenOfImmediateMacroBodyExpansion(
198 SourceLocation loc, SourceLocation * macroEnd = nullptr) const;
200 bool rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement);
202 unsigned int externCContexts_ = 0;
203 std::set<SourceLocation> rewritten_;
204 // needed when rewriting in macros, in general to avoid "double code replacement, possible
205 // plugin error" warnings, and in particular to avoid adding multiple sets of parens around
206 // sub-exprs
207 std::set<CStyleCastExpr const *> rewrittenSubExprs_;
210 const char * recommendedFix(clang::CastKind ck) {
211 switch(ck) {
212 case CK_IntegralToPointer: return "reinterpret_cast";
213 case CK_PointerToIntegral: return "reinterpret_cast";
214 case CK_BaseToDerived: return "static_cast";
215 default: return nullptr;
219 bool CStyleCast::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
220 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
221 ++externCContexts_;
222 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
223 assert(externCContexts_ != 0);
224 --externCContexts_;
225 return ret;
228 bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) {
229 if (ignoreLocation(expr)) {
230 return true;
232 // casting to void is typically used when a parameter or field is only used in
233 // debug mode, and we want to eliminate an "unused" warning
234 if( expr->getCastKind() == CK_ToVoid ) {
235 return true;
237 if (isSharedCAndCppCode(compat::getBeginLoc(expr))) {
238 return true;
240 char const * perf = nullptr;
241 if( expr->getCastKind() == CK_IntegralCast ) {
242 if (rewriteArithmeticCast(expr, &perf)) {
243 return true;
245 } else if( expr->getCastKind() == CK_NoOp ) {
246 if (!((expr->getSubExpr()->getType()->isPointerType()
247 && expr->getType()->isPointerType())
248 || expr->getTypeAsWritten()->isReferenceType()))
250 if (rewriteArithmeticCast(expr, &perf)) {
251 return true;
254 if (isConstCast(
255 expr->getSubExprAsWritten()->getType(),
256 expr->getTypeAsWritten()))
258 perf = "const_cast";
261 std::string incompFrom;
262 std::string incompTo;
263 if( expr->getCastKind() == CK_BitCast ) {
264 if (resolvePointers(expr->getSubExprAsWritten()->getType())
265 ->isIncompleteType())
267 incompFrom = "incomplete ";
269 if (resolvePointers(expr->getType())->isIncompleteType()) {
270 incompTo = "incomplete ";
273 if (perf == nullptr) {
274 perf = recommendedFix(expr->getCastKind());
276 std::string performs;
277 if (perf != nullptr) {
278 performs = std::string(" (performs: ") + perf + ")";
280 report(
281 DiagnosticsEngine::Warning, "C-style cast from %0%1 to %2%3%4 (%5)",
282 expr->getSourceRange().getBegin())
283 << incompFrom << expr->getSubExprAsWritten()->getType()
284 << incompTo << expr->getTypeAsWritten() << performs
285 << expr->getCastKindName()
286 << expr->getSourceRange();
287 return true;
290 bool CStyleCast::isConstCast(QualType from, QualType to) {
291 if (to->isReferenceType()
292 && to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
294 if (!from->isObjectType()) {
295 return false;
297 from = compiler.getASTContext().getPointerType(from);
298 to = compiler.getASTContext().getPointerType(
299 to->getAs<ReferenceType>()->getPointeeType());
300 } else {
301 if (from->isArrayType()) {
302 from = compiler.getASTContext().getPointerType(
303 from->getAsArrayTypeUnsafe()->getElementType());
304 } else if (from->isFunctionType()) {
305 compiler.getASTContext().getPointerType(from);
308 return areSimilar(from, to);
311 bool CStyleCast::isFromCIncludeFile(SourceLocation spellingLocation) const {
312 return !compiler.getSourceManager().isInMainFile(spellingLocation)
313 && (StringRef(
314 compiler.getSourceManager().getPresumedLoc(spellingLocation)
315 .getFilename())
316 .endswith(".h"));
319 bool CStyleCast::isSharedCAndCppCode(SourceLocation location) const {
320 while (compiler.getSourceManager().isMacroArgExpansion(location)) {
321 location = compiler.getSourceManager().getImmediateMacroCallerLoc(
322 location);
324 // Assume that code is intended to be shared between C and C++ if it comes
325 // from an include file ending in .h, and is either in an extern "C" context
326 // or the body of a macro definition:
327 return
328 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
329 && (externCContexts_ != 0
330 || compiler.getSourceManager().isMacroBodyExpansion(location));
333 bool CStyleCast::isLastTokenOfImmediateMacroBodyExpansion(
334 SourceLocation loc, SourceLocation * macroEnd) const
336 assert(compiler.getSourceManager().isMacroBodyExpansion(loc));
337 auto const spell = compiler.getSourceManager().getSpellingLoc(loc);
338 auto name = Lexer::getImmediateMacroName(
339 loc, compiler.getSourceManager(), compiler.getLangOpts());
340 while (name.startswith("\\\n")) {
341 name = name.drop_front(2);
342 while (!name.empty()
343 && (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
344 || name.front() == '\v' || name.front() == '\f'))
346 name = name.drop_front(1);
349 auto const MI
350 = (compiler.getPreprocessor().getMacroDefinitionAtLoc(
351 &compiler.getASTContext().Idents.get(name), spell)
352 .getMacroInfo());
353 assert(MI != nullptr);
354 if (spell == MI->getDefinitionEndLoc()) {
355 if (macroEnd != nullptr) {
356 *macroEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).second;
358 return true;
360 return false;
363 bool CStyleCast::rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement) {
364 assert(replacement != nullptr);
365 auto const sub = expr->getSubExprAsWritten();
366 auto const functional = isLiteralLike(sub)
367 && canBeUsedForFunctionalCast(expr->getTypeInfoAsWritten());
368 *replacement = functional ? "functional cast" : "static_cast";
369 if (rewriter == nullptr) {
370 return false;
372 // Doing modifications for a chain of C-style casts as in
374 // (foo)(bar)(baz)x
376 // leads to unpredictable results, so only rewrite them one at a time, starting with the
377 // outermost:
378 if (auto const e = dyn_cast<CStyleCastExpr>(sub)) {
379 rewrittenSubExprs_.insert(e);
381 if (rewrittenSubExprs_.find(expr) != rewrittenSubExprs_.end()) {
382 return false;
384 // Two or four ranges to replace:
385 // First is the CStyleCast's LParen, plus following whitespace, replaced with either "" or
386 // "static_cast<". (TODO: insert space before "static_cast<" when converting "else(int)...".)
387 // Second is the CStyleCast's RParen, plus preceding and following whitespace, replaced with
388 // either "" or ">".
389 // If the sub expr is not a ParenExpr, third is the sub expr's begin, inserting "(", and fourth
390 // is the sub expr's end, inserting ")".
391 // (The reason the second and third are not combined is in case there's a comment between them.)
392 auto firstBegin = expr->getLParenLoc();
393 auto secondBegin = expr->getRParenLoc();
394 while (compiler.getSourceManager().isMacroArgExpansion(firstBegin)
395 && compiler.getSourceManager().isMacroArgExpansion(secondBegin)
396 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), firstBegin)
397 == compat::getImmediateExpansionRange(compiler.getSourceManager(), secondBegin)))
399 firstBegin = compiler.getSourceManager().getImmediateSpellingLoc(firstBegin);
400 secondBegin = compiler.getSourceManager().getImmediateSpellingLoc(secondBegin);
402 if (compiler.getSourceManager().isMacroBodyExpansion(firstBegin)
403 && compiler.getSourceManager().isMacroBodyExpansion(secondBegin)
404 && (compiler.getSourceManager().getImmediateMacroCallerLoc(firstBegin)
405 == compiler.getSourceManager().getImmediateMacroCallerLoc(secondBegin)))
407 firstBegin = compiler.getSourceManager().getSpellingLoc(firstBegin);
408 secondBegin = compiler.getSourceManager().getSpellingLoc(secondBegin);
410 auto third = compat::getBeginLoc(sub);
411 auto fourth = compat::getEndLoc(sub);
412 bool macro = false;
413 // Ensure that
415 // #define FOO(x) (int)x
416 // FOO(y)
418 // is changed to
420 // #define FOO(x) static_cast<int>(x)
421 // FOO(y)
423 // instead of
425 // #define FOO(x) static_cast<int>x
426 // FOO((y))
427 while (compiler.getSourceManager().isMacroArgExpansion(third)
428 && compiler.getSourceManager().isMacroArgExpansion(fourth)
429 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
430 == compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth))
431 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third))
432 //TODO: check fourth is at end of immediate macro expansion, but
433 // SourceManager::isAtEndOfImmediateMacroExpansion requires a location pointing at the
434 // character end of the last token
436 auto const range = compat::getImmediateExpansionRange(compiler.getSourceManager(), third);
437 third = range.first;
438 fourth = range.second;
439 macro = true;
440 assert(third.isValid());
442 while (compiler.getSourceManager().isMacroArgExpansion(third)
443 && compiler.getSourceManager().isMacroArgExpansion(fourth)
444 && (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
445 == compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth)))
447 third = compiler.getSourceManager().getImmediateSpellingLoc(third);
448 fourth = compiler.getSourceManager().getImmediateSpellingLoc(fourth);
450 if (isa<ParenExpr>(sub)) {
451 // Ensure that with
453 // #define FOO (x)
455 // a cast like
457 // (int) FOO
459 // is changed to
461 // static_cast<int>(FOO)
463 // instead of
465 // static_cast<int>FOO
466 for (;; macro = true) {
467 if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
468 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
469 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
470 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
471 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)
472 && isLastTokenOfImmediateMacroBodyExpansion(fourth)))
474 if (!macro) {
475 third = fourth = SourceLocation();
477 break;
479 auto const range = compat::getImmediateExpansionRange(
480 compiler.getSourceManager(), third);
481 third = range.first;
482 fourth = range.second;
483 assert(third.isValid());
485 if (third.isValid() && compiler.getSourceManager().isMacroBodyExpansion(third)
486 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
487 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
488 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
490 third = compiler.getSourceManager().getSpellingLoc(third);
491 fourth = compiler.getSourceManager().getSpellingLoc(fourth);
492 assert(third.isValid());
494 } else {
495 // Ensure that a cast like
497 // (int)LONG_MAX
499 // (where LONG_MAX expands to __LONG_MAX__, which in turn is a built-in expanding to a value
500 // like 9223372036854775807L) is changed to
502 // int(LONG_MAX)
504 // instead of trying to add the parentheses to the built-in __LONG_MAX__ definition:
505 for (;;) {
506 if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
507 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
508 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
509 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
510 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)))
511 // TODO: check that fourth is at end of immediate macro expansion (but
512 // SourceManager::isAtEndOfImmediateMacroExpansion wants a location pointing at the
513 // character end)
515 break;
517 auto const range = compat::getImmediateExpansionRange(
518 compiler.getSourceManager(), third);
519 third = range.first;
520 fourth = range.second;
522 // ...and additionally asymmetrically unwind macros only at the start or end, for code like
524 // (long)ubidi_getVisualIndex(...)
526 // (in editeng/source/editeng/impedit2.cxx) where ubidi_getVisualIndex is an object-like
527 // macro, or
529 // #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
531 // (in hwpfilter/source/lexer.cxx):
532 if (!fourth.isMacroID()) {
533 while (compiler.getSourceManager().isMacroBodyExpansion(third)
534 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third))
536 } else if (compiler.getSourceManager().isMacroBodyExpansion(fourth)) {
537 while (compiler.getSourceManager().isMacroArgExpansion(third)
538 && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third)) {}
540 if (!third.isMacroID()) {
541 while (compiler.getSourceManager().isMacroBodyExpansion(fourth)
542 && isLastTokenOfImmediateMacroBodyExpansion(fourth, &fourth))
544 } else if (compiler.getSourceManager().isMacroBodyExpansion(third)) {
545 while (compiler.getSourceManager().isMacroArgExpansion(fourth, &fourth)) {}
547 if (compiler.getSourceManager().isMacroBodyExpansion(third)
548 && compiler.getSourceManager().isMacroBodyExpansion(fourth)
549 && (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
550 == compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
552 third = compiler.getSourceManager().getSpellingLoc(third);
553 fourth = compiler.getSourceManager().getSpellingLoc(fourth);
555 assert(third.isValid());
557 if (firstBegin.isMacroID() || secondBegin.isMacroID() || (third.isValid() && third.isMacroID())
558 || (fourth.isValid() && fourth.isMacroID()))
560 if (isDebugMode()) {
561 report(
562 DiagnosticsEngine::Fatal,
563 "TODO: cannot rewrite C-style cast in macro, needs investigation",
564 expr->getExprLoc())
565 << expr->getSourceRange();
567 return false;
569 unsigned firstLen = Lexer::MeasureTokenLength(
570 firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
571 for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
572 l = l.getLocWithOffset(1))
574 unsigned n = Lexer::MeasureTokenLength(
575 l, compiler.getSourceManager(), compiler.getLangOpts());
576 if (n != 0) {
577 break;
579 ++firstLen;
581 unsigned secondLen = Lexer::MeasureTokenLength(
582 secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
583 for (auto l = secondBegin.getLocWithOffset(std::max<unsigned>(secondLen, 1));;
584 l = l.getLocWithOffset(1))
586 unsigned n = Lexer::MeasureTokenLength(
587 l, compiler.getSourceManager(), compiler.getLangOpts());
588 if (n != 0) {
589 break;
591 ++secondLen;
593 for (;;) {
594 auto l = secondBegin.getLocWithOffset(-1);
595 auto const c = compiler.getSourceManager().getCharacterData(l)[0];
596 if (c == '\n') {
597 if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
598 break;
600 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
601 break;
603 secondBegin = l;
604 ++secondLen;
606 if (rewritten_.insert(firstBegin).second) {
607 if (!replaceText(firstBegin, firstLen, functional ? "" : "static_cast<")) {
608 if (isDebugMode()) {
609 report(
610 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #1, needs investigation",
611 firstBegin);
612 report(
613 DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
614 << expr->getSourceRange();
616 return false;
618 if (!replaceText(secondBegin, secondLen, functional ? "" : ">")) {
619 //TODO: roll back
620 if (isDebugMode()) {
621 report(
622 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #2, needs investigation",
623 secondBegin);
624 report(
625 DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
626 << expr->getSourceRange();
628 return false;
631 if (third.isValid()) {
632 if (rewritten_.insert(third).second) {
633 if (!insertTextBefore(third, "(")) {
634 //TODO: roll back
635 if (isDebugMode()) {
636 report(
637 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #3, needs investigation",
638 third);
639 report(
640 DiagnosticsEngine::Note, "when rewriting this C-style cast",
641 expr->getExprLoc())
642 << expr->getSourceRange();
644 return false;
646 if (!insertTextAfterToken(fourth, ")")) {
647 //TODO: roll back
648 if (isDebugMode()) {
649 report(
650 DiagnosticsEngine::Fatal, "TODO: cannot rewrite #4, needs investigation",
651 third);
652 report(
653 DiagnosticsEngine::Note, "when rewriting this C-style cast",
654 expr->getExprLoc())
655 << expr->getSourceRange();
657 return false;
661 return true;
664 loplugin::Plugin::Registration< CStyleCast > X("cstylecast", true);
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */