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/.
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.
27 bool areSimilar(QualType type1
, QualType type2
) {
28 auto t1
= type1
.getCanonicalType().getTypePtr();
29 auto t2
= type2
.getCanonicalType().getTypePtr();
31 if (t1
->isPointerType()) {
32 if (!t2
->isPointerType()) {
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()) {
43 auto t1a
= t1
->getAs
<MemberPointerType
>();
44 auto t2a
= t2
->getAs
<MemberPointerType
>();
45 if (t1a
->getClass()->getCanonicalTypeInternal()
46 != t2a
->getClass()->getCanonicalTypeInternal())
50 t1
= t1a
->getPointeeType().getTypePtr();
51 t2
= t2a
->getPointeeType().getTypePtr();
52 } else if (t1
->isConstantArrayType()) {
53 if (!t2
->isConstantArrayType()) {
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()) {
63 t1
= t1a
->getElementType().getTypePtr();
64 t2
= t2a
->getElementType().getTypePtr();
65 } else if (t1
->isIncompleteArrayType()) {
66 if (!t2
->isIncompleteArrayType()) {
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();
84 QualType
resolvePointers(QualType type
) {
85 while (type
->isPointerType()) {
86 type
= type
->getAs
<clang::PointerType
>()->getPointeeType();
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
))
99 if (auto const e
= dyn_cast
<DeclRefExpr
>(expr
)) {
100 auto const d
= e
->getDecl();
101 if (isa
<EnumConstantDecl
>(d
)) {
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
);
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());
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());
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());
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()) {
148 if (auto const t
= dyn_cast
<BuiltinType
>(type
)) {
149 if (!(t
->isInteger() || t
->isFloatingPoint())) {
152 auto const loc
= info
->getTypeLoc().castAs
<BuiltinTypeLoc
>();
154 (int(loc
.hasWrittenSignSpec()) + int(loc
.hasWrittenWidthSpec())
155 + int(loc
.hasWrittenTypeSpec()))
158 if (isa
<TagType
>(type
) || isa
<TemplateTypeParmType
>(type
) || isa
<AutoType
>(type
)
159 || isa
<DecltypeType
>(type
) || isa
<TypedefType
>(type
))
163 if (auto const t
= dyn_cast
<ElaboratedType
>(type
)) {
164 return t
->getKeyword() == ETK_None
;
170 public loplugin::FilteringRewritePlugin
<CStyleCast
>
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
);
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
213 std::set
<CStyleCastExpr
const *> rewrittenSubExprs_
;
216 const char * recommendedFix(clang::CastKind 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
228 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
229 assert(externCContexts_
!= 0);
234 bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr
* expr
) {
235 if (ignoreLocation(expr
)) {
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
) {
243 if (isSharedCAndCppCode(expr
->getBeginLoc())) {
246 char const * perf
= nullptr;
247 if( expr
->getCastKind() == CK_IntegralCast
) {
248 if (rewriteArithmeticCast(expr
, &perf
)) {
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
)) {
261 expr
->getSubExprAsWritten()->getType(),
262 expr
->getTypeAsWritten()))
267 reportCast(expr
, perf
);
271 bool CStyleCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
) {
272 if (ignoreLocation(expr
)) {
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
:
285 expr
->getSubExprAsWritten()->getType(),
286 expr
->getTypeAsWritten()))
291 return true; //TODO: really filter out all of these?
295 reportCast(expr
, perf
);
299 bool CStyleCast::isConstCast(QualType from
, QualType to
) {
300 if (to
->isReferenceType()
301 && to
->getAs
<ReferenceType
>()->getPointeeType()->isObjectType())
303 if (!from
->isObjectType()) {
306 from
= compiler
.getASTContext().getPointerType(from
);
307 to
= compiler
.getASTContext().getPointerType(
308 to
->getAs
<ReferenceType
>()->getPointeeType());
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
)
323 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
328 bool CStyleCast::isSharedCAndCppCode(SourceLocation location
) const {
329 while (compiler
.getSourceManager().isMacroArgExpansion(location
)) {
330 location
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
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:
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);
352 && (name
.front() == ' ' || name
.front() == '\t' || name
.front() == '\n'
353 || name
.front() == '\v' || name
.front() == '\f'))
355 name
= name
.drop_front(1);
359 = (compiler
.getPreprocessor().getMacroDefinitionAtLoc(
360 &compiler
.getASTContext().Idents
.get(name
), spell
)
362 assert(MI
!= nullptr);
363 if (spell
== MI
->getDefinitionEndLoc()) {
364 if (macroEnd
!= nullptr) {
365 *macroEnd
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), loc
).second
;
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) {
381 // Doing modifications for a chain of C-style casts as in
385 // leads to unpredictable results, so only rewrite them one at a time, starting with the
387 if (auto const e
= dyn_cast
<CStyleCastExpr
>(sub
)) {
388 rewrittenSubExprs_
.insert(e
);
390 if (rewrittenSubExprs_
.find(expr
) != rewrittenSubExprs_
.end()) {
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
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();
424 // #define FOO(x) (int)x
429 // #define FOO(x) static_cast<int>(x)
434 // #define FOO(x) static_cast<int>x
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
);
447 fourth
= range
.second
;
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
)) {
470 // static_cast<int>(FOO)
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
)))
484 third
= fourth
= SourceLocation();
488 auto const range
= compat::getImmediateExpansionRange(
489 compiler
.getSourceManager(), third
);
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());
504 // Ensure that a cast like
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
513 // instead of trying to add the parentheses to the built-in __LONG_MAX__ definition:
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
526 auto const range
= compat::getImmediateExpansionRange(
527 compiler
.getSourceManager(), third
);
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
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()))
571 DiagnosticsEngine::Fatal
,
572 "TODO: cannot rewrite C-style cast in macro, needs investigation",
574 << expr
->getSourceRange();
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());
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());
603 auto l
= secondBegin
.getLocWithOffset(-1);
604 auto const c
= compiler
.getSourceManager().getCharacterData(l
)[0];
606 if (compiler
.getSourceManager().getCharacterData(l
.getLocWithOffset(-1))[0] == '\\') {
609 } else if (!(c
== ' ' || c
== '\t' || c
== '\v' || c
== '\f')) {
615 if (rewritten_
.insert(firstBegin
).second
) {
616 if (!replaceText(firstBegin
, firstLen
, functional
? "" : "static_cast<")) {
619 DiagnosticsEngine::Fatal
, "TODO: cannot rewrite #1, needs investigation",
622 DiagnosticsEngine::Note
, "when rewriting this C-style cast", expr
->getExprLoc())
623 << expr
->getSourceRange();
627 if (!replaceText(secondBegin
, secondLen
, functional
? "" : ">")) {
631 DiagnosticsEngine::Fatal
, "TODO: cannot rewrite #2, needs investigation",
634 DiagnosticsEngine::Note
, "when rewriting this C-style cast", expr
->getExprLoc())
635 << expr
->getSourceRange();
640 if (third
.isValid()) {
641 if (rewritten_
.insert(third
).second
) {
642 if (!insertTextBefore(third
, "(")) {
646 DiagnosticsEngine::Fatal
, "TODO: cannot rewrite #3, needs investigation",
649 DiagnosticsEngine::Note
, "when rewriting this C-style cast",
651 << expr
->getSourceRange();
655 if (!insertTextAfterToken(fourth
, ")")) {
659 DiagnosticsEngine::Fatal
, "TODO: cannot rewrite #4, needs investigation",
662 DiagnosticsEngine::Note
, "when rewriting this C-style cast",
664 << expr
->getSourceRange();
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
+ ")";
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: */