void UseUncaughtExceptionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus17) return; std::string MatchText = "::std::uncaught_exception"; // Using declaration: warning and fix-it. Finder->addMatcher( usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(hasName(MatchText)))) .bind("using_decl"), this); // DeclRefExpr: warning, no fix-it. Finder->addMatcher(declRefExpr(allOf(to(functionDecl(hasName(MatchText))), unless(callExpr()))) .bind("decl_ref_expr"), this); // CallExpr: warning, fix-it. Finder->addMatcher( callExpr(allOf(hasDeclaration(functionDecl(hasName(MatchText))), unless(hasAncestor(initListExpr())))) .bind("call_expr"), this); // CallExpr in initialisation list: warning, fix-it with avoiding narrowing // conversions. Finder->addMatcher( callExpr(allOf(hasAncestor(initListExpr()), hasDeclaration(functionDecl(hasName(MatchText))))) .bind("init_call_expr"), this); }
Transform * const Transform::addChildAtIndex(NodeChild * const _child, int _index, bool _underNewTransform){ // Check to see if the child is one of the ancestors of this node // (Cannot parent a node to one of its descendants) if(hasAncestor(_child->asTransform())) { ST_LOG_ERROR_V("Cannot parent a node to one of it's ancestors"); assert(false); } // if negative, count from the end if(_index < 0){ _index = children.size() + _index; } if(_underNewTransform){ Transform * t = new Transform(); t->addChild(_child, false); children.insert(children.begin() + _index, t); t->addParent(this); return t; } removeChild(_child); children.insert(children.begin() + _index, _child); _child->addParent(this); return nullptr; }
void AvoidGotoCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; // TODO: This check does not recognize `IndirectGotoStmt` which is a // GNU extension. These must be matched separately and an AST matcher // is currently missing for them. // Check if the 'goto' is used for control flow other than jumping // out of a nested loop. auto Loop = stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt())); auto NestedLoop = stmt(anyOf(forStmt(hasAncestor(Loop)), cxxForRangeStmt(hasAncestor(Loop)), whileStmt(hasAncestor(Loop)), doStmt(hasAncestor(Loop)))); Finder->addMatcher(gotoStmt(anyOf(unless(hasAncestor(NestedLoop)), unless(isForwardJumping()))) .bind("goto"), this); }
void StringReferenceMemberCheck::registerMatchers( ast_matchers::MatchFinder *Finder) { // Look for const references to std::string or ::string. auto String = anyOf(recordDecl(hasName("::std::basic_string")), recordDecl(hasName("::string"))); auto ConstString = qualType(isConstQualified(), hasDeclaration(String)); // Ignore members in template instantiations. auto InTemplateInstantiation = hasAncestor( decl(anyOf(recordDecl(ast_matchers::isTemplateInstantiation()), functionDecl(ast_matchers::isTemplateInstantiation())))); Finder->addMatcher(fieldDecl(hasType(references(ConstString)), unless(InTemplateInstantiation)).bind("member"), this); }
void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) { // Find all TypeLocs. The relevant Style Guide rule only applies to C++. if (!getLangOpts().CPlusPlus) return; // Match any integer types, unless they are passed to a printf-based API: // // http://google.github.io/styleguide/cppguide.html#64-bit_Portability // "Where possible, avoid passing arguments of types specified by // bitwidth typedefs to printf-based APIs." Finder->addMatcher(typeLoc(loc(isInteger()), unless(hasAncestor(callExpr( callee(functionDecl(hasAttr(attr::Format))))))) .bind("tl"), this); IdentTable = llvm::make_unique<IdentifierTable>(getLangOpts()); }
void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( expr(unless(isInTemplateInstantiation()), expr(sizeOfExpr(has(ignoringParenImpCasts( expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl( matchesName("^(::std::|::string)"), unless(matchesName("^::std::(bitset|array)$")), hasMethod(cxxMethodDecl(hasName("size"), isPublic(), isConst()))))))))))) .bind("sizeof"), // Ignore ARRAYSIZE(<array of containers>) pattern. unless(hasAncestor(binaryOperator( anyOf(hasOperatorName("/"), hasOperatorName("%")), hasLHS(ignoringParenCasts(sizeOfExpr(expr()))), hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))), this); }
void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) { if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions)) return; // Match any static or thread_local variable declaration that has an // initializer that can throw. Finder->addMatcher( varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()), unless(anyOf(isConstexpr(), hasType(cxxRecordDecl(isLambda())), hasAncestor(functionDecl()))), anyOf(hasDescendant(cxxConstructExpr(hasDeclaration( cxxConstructorDecl(unless(isNoThrow())).bind("func")))), hasDescendant(cxxNewExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))), hasDescendant(callExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))))) .bind("var"), this); }
void UnusedParametersCheck::warnOnUnusedParameter( const MatchFinder::MatchResult &Result, const FunctionDecl *Function, unsigned ParamIndex) { const auto *Param = Function->getParamDecl(ParamIndex); auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused") << Param->getName(); auto UsedByRef = [&] { return !ast_matchers::match( decl(hasDescendant( declRefExpr(to(equalsNode(Function)), unless(hasAncestor( callExpr(callee(equalsNode(Function)))))))), *Result.Context->getTranslationUnitDecl(), *Result.Context) .empty(); }; // Comment out parameter name for non-local functions. if (Function->isExternallyVisible() || !Result.SourceManager->isInMainFile(Function->getLocation()) || UsedByRef()) { SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd()); // Note: We always add a space before the '/*' to not accidentally create a // '*/*' for pointer types, which doesn't start a comment. clang-format will // clean this up afterwards. MyDiag << FixItHint::CreateReplacement( RemovalRange, (Twine(" /*") + Param->getName() + "*/").str()); return; } // Fix all redeclarations. for (const FunctionDecl *FD : Function->redecls()) if (FD->param_size()) MyDiag << removeParameter(FD, ParamIndex); // Fix all call sites. auto CallMatches = ast_matchers::match( decl(forEachDescendant( callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))), *Result.Context->getTranslationUnitDecl(), *Result.Context); for (const auto &Match : CallMatches) MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex); }
void UpgradeDurationConversionsCheck::check( const MatchFinder::MatchResult &Result) { const llvm::StringRef Message = "implicit conversion to 'int64_t' is deprecated in this context; use an " "explicit cast instead"; const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); SourceLocation Loc = ArgExpr->getBeginLoc(); if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) { // For each location matched in a template instantiation, we check if the // location can also be found in `MatchedTemplateLocations`. If it is not // found, that means the expression did not create a match without the // instantiation and depends on template parameters. A manual fix is // probably required so we provide only a warning. diag(Loc, Message); } return; } // We gather source locations from template matches not in template // instantiations for future matches. internal::Matcher<Stmt> IsInsideTemplate = hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) MatchedTemplateLocations.insert(Loc.getRawEncoding()); DiagnosticBuilder Diag = diag(Loc, Message); CharSourceRange SourceRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), *Result.SourceManager, Result.Context->getLangOpts()); if (SourceRange.isInvalid()) // An invalid source range likely means we are inside a macro body. A manual // fix is likely needed so we do not create a fix-it hint. return; Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), "static_cast<int64_t>(") << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); }
Transform * const Transform::addChild(NodeChild * const _child, bool _underNewTransform){ // Check to see if the child is one of the ancestors of this node // (Cannot parent a node to one of its descendants) if(hasAncestor(_child->asTransform())) { ST_LOG_ERROR_V("Cannot parent a node to one of it's ancestors"); assert(false); } if(_underNewTransform){ Transform * t = new Transform(); t->addChild(_child, false); children.push_back(t); t->addParent(this); return t; } removeChild(_child); children.push_back(_child); _child->addParent(this); return nullptr; }
void FunctionNamingCheck::registerMatchers(MatchFinder *Finder) { // This check should only be applied to Objective-C sources. if (!getLangOpts().ObjC) return; // Enforce Objective-C function naming conventions on all functions except: // • Functions defined in system headers. // • C++ member functions. // • Namespaced functions. // • Implicitly defined functions. // • The main function. Finder->addMatcher( functionDecl( unless(anyOf(isExpansionInSystemHeader(), cxxMethodDecl(), hasAncestor(namespaceDecl()), isMain(), isImplicit(), matchesName(validFunctionNameRegex(true)), allOf(isStaticStorageClass(), matchesName(validFunctionNameRegex(false)))))) .bind("function"), this); }
void InefficientStringConcatenationCheck::registerMatchers( MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto BasicStringType = hasType(qualType(hasUnqualifiedDesugaredType(recordType( hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))))); const auto BasicStringPlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType)))); const auto PlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))), hasDescendant(BasicStringPlusOperator)) .bind("plusOperator"); const auto AssignOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("="), hasArgument(0, declRefExpr(BasicStringType, hasDeclaration(decl().bind("lhsStrT"))) .bind("lhsStr")), hasArgument(1, stmt(hasDescendant(declRefExpr( hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))), hasDescendant(BasicStringPlusOperator)); if (StrictMode) { Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)), this); } else { Finder->addMatcher( cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator), hasAncestor(stmt(anyOf(cxxForRangeStmt(), whileStmt(), forStmt())))), this); } }
void UnusedParametersCheck::warnOnUnusedParameter( const MatchFinder::MatchResult &Result, const FunctionDecl *Function, unsigned ParamIndex) { const auto *Param = Function->getParamDecl(ParamIndex); auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused") << Param->getName(); auto UsedByRef = [&] { return !ast_matchers::match( decl(hasDescendant( declRefExpr(to(equalsNode(Function)), unless(hasAncestor( callExpr(callee(equalsNode(Function)))))))), *Result.Context->getTranslationUnitDecl(), *Result.Context) .empty(); }; // Comment out parameter name for non-local functions. if ((Function->isExternallyVisible() && Function->getStorageClass() != StorageClass::SC_Static) || UsedByRef()) { SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd()); MyDiag << FixItHint::CreateReplacement( RemovalRange, (Twine(" /*") + Param->getName() + "*/").str()); return; } // Fix all redeclarations. for (const FunctionDecl *FD : Function->redecls()) MyDiag << removeParameter(FD, ParamIndex); // Fix all call sites. auto CallMatches = ast_matchers::match( decl(forEachDescendant( callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))), *Result.Context->getTranslationUnitDecl(), *Result.Context); for (const auto &Match : CallMatches) MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex); }
BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; LLMouseHandler* other_captor = gFocusMgr.getMouseCapture(); mNeedsHighlight = other_captor == NULL || other_captor == this || // this following bit is to support modal dialogs (other_captor->isView() && hasAncestor((LLView*)other_captor)); if (mMouseDownTimer.getStarted() && NULL != mHeldDownCallback) { F32 elapsed = mMouseDownTimer.getElapsedTimeF32(); if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= LLFrameTimer::getFrameCount() - mMouseDownFrame) { mHeldDownCallback( mCallbackUserData ); } } // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) { handled = TRUE; } else if( getVisible() ) { // Opaque handled = TRUE; } if( handled ) { getWindow()->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; } return handled; }
void InheritanceBuilder::tackOnto(MatchFinder &MF) { MF.addMatcher(cxxRecordDecl(isDefinition(), unless(hasAncestor(namespaceDecl(isAnonymous())))) .bind("class"), this); }
void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) { //////////////////////////////////////// // Quick annotation conflict checkers // //////////////////////////////////////// AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // and which is && ref-qualified. isRValueRefQualified(), decl().bind("invalidMethodRefQualified")), this); AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // which returns a primitive type, returns(builtinType()), // and which doesn't return a pointer. unless(returns(pointerType())), decl().bind("invalidMethodPointer")), this); ////////////////// // Main checker // ////////////////// auto hasParentCall = hasParent(expr(anyOf( cxxOperatorCallExpr( // If we're in a lamda, we may have an operator call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentOperatorCallExpr")), callExpr( // If we're in a lamda, we may have a call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentCallExpr")), objcMessageExpr( // If we're in a lamda, we may have an objc message expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentObjCMessageExpr")), cxxConstructExpr( // If we're in a lamda, we may have a construct expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentConstructExpr"))))); AstMatcher->addMatcher( // This is a matcher on a method call, cxxMemberCallExpr( // which is in first party code, isFirstParty(), // and which is performed on a temporary, on(allOf( unless(hasType(pointerType())), isTemporary(), // but which is not `this`. unless(cxxThisExpr()))), // and which is marked as no dangling on temporaries. callee(cxxMethodDecl(noDanglingOnTemporaries())), expr().bind("memberCallExpr"), // We optionally match a parent call expression or a parent construct // expression because using a temporary inside a call is fine as long // as the pointer doesn't escape the function call. anyOf( // This is the case where the call is the direct parent, so we // know that the member call expression is the argument. allOf(hasParentCall, expr().bind("parentCallArg")), // This is the case where the call is not the direct parent, so we // get its child to know in which argument tree we are. hasAncestor(expr( hasParentCall, expr().bind("parentCallArg"))), // To make it optional. anything())), this); }
void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) { // Match on PredefinedExprs inside a lambda. Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"), this); }
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(), CastExpr->getRParenLoc()); // Ignore casts in macros. if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID()) return; // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. if (CastExpr->getTypeAsWritten()->isVoidType()) return; QualType SourceType = CastExpr->getSubExprAsWritten()->getType(); QualType DestType = CastExpr->getTypeAsWritten(); if (SourceType == DestType) { diag(CastExpr->getLocStart(), "redundant cast to the same type") << FixItHint::CreateRemoval(ParenRange); return; } SourceType = SourceType.getCanonicalType(); DestType = DestType.getCanonicalType(); if (SourceType == DestType) { diag(CastExpr->getLocStart(), "possibly redundant cast between typedefs of the same type"); return; } // The rest of this check is only relevant to C++. if (!Result.Context->getLangOpts().CPlusPlus) return; // Ignore code inside extern "C" {} blocks. if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context) .empty()) return; // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). StringRef DestTypeString = Lexer::getSourceText( CharSourceRange::getTokenRange( CastExpr->getLParenLoc().getLocWithOffset(1), CastExpr->getRParenLoc().getLocWithOffset(-1)), *Result.SourceManager, Result.Context->getLangOpts()); auto diag_builder = diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0"); auto ReplaceWithCast = [&](StringRef CastType) { diag_builder << ("Use " + CastType).str(); const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); std::string CastText = (CastType + "<" + DestTypeString + ">").str(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); diag_builder << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, *Result.SourceManager, Result.Context->getLangOpts()), ")"); } diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. switch (CastExpr->getCastKind()) { case CK_NoOp: if (needsConstCast(SourceType, DestType) && pointedTypesAreEqual(SourceType, DestType)) { ReplaceWithCast("const_cast"); return; } if (DestType->isReferenceType() && (SourceType.getNonReferenceType() == DestType.getNonReferenceType().withConst() || SourceType.getNonReferenceType() == DestType.getNonReferenceType())) { ReplaceWithCast("const_cast"); return; } // FALLTHROUGH case clang::CK_IntegralCast: // Convert integral and no-op casts between builtin types and enums to // static_cast. A cast from enum to integer may be unnecessary, but it's // still retained. if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && (DestType->isBuiltinType() || DestType->isEnumeralType())) { ReplaceWithCast("static_cast"); return; } break; case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { ReplaceWithCast("reinterpret_cast"); return; } break; default: break; } diag_builder << "Use static_cast/const_cast/reinterpret_cast"; }
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt) { return stmt(hasAncestor(cxxForRangeStmt( hasRangeBeginEndStmt(hasDescendant(equalsNode(&Node)))))) .matches(Node, Finder, Builder); }
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); // Ignore casts in macros. if (CastExpr->getExprLoc().isMacroID()) return; // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. if (CastExpr->getCastKind() == CK_ToVoid) return; auto isFunction = [](QualType T) { T = T.getCanonicalType().getNonReferenceType(); return T->isFunctionType() || T->isFunctionPointerType() || T->isMemberFunctionPointerType(); }; const QualType DestTypeAsWritten = CastExpr->getTypeAsWritten().getUnqualifiedType(); const QualType SourceTypeAsWritten = CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType(); const QualType SourceType = SourceTypeAsWritten.getCanonicalType(); const QualType DestType = DestTypeAsWritten.getCanonicalType(); auto ReplaceRange = CharSourceRange::getCharRange( CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc()); bool FnToFnCast = isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten); if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) { // Function pointer/reference casts may be needed to resolve ambiguities in // case of overloaded functions, so detection of redundant casts is trickier // in this case. Don't emit "redundant cast" warnings for function // pointer/reference types. if (SourceTypeAsWritten == DestTypeAsWritten) { diag(CastExpr->getBeginLoc(), "redundant cast to the same type") << FixItHint::CreateRemoval(ReplaceRange); return; } } // The rest of this check is only relevant to C++. // We also disable it for Objective-C++. if (!getLangOpts().CPlusPlus || getLangOpts().ObjC1 || getLangOpts().ObjC2) return; // Ignore code inside extern "C" {} blocks. if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context) .empty()) return; // Ignore code in .c files and headers included from them, even if they are // compiled as C++. if (getCurrentMainFile().endswith(".c")) return; SourceManager &SM = *Result.SourceManager; // Ignore code in .c files #included in other files (which shouldn't be done, // but people still do this for test and other purposes). if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c")) return; // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). StringRef DestTypeString = Lexer::getSourceText(CharSourceRange::getTokenRange( CastExpr->getLParenLoc().getLocWithOffset(1), CastExpr->getRParenLoc().getLocWithOffset(-1)), SM, getLangOpts()); auto Diag = diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0"); auto ReplaceWithCast = [&](std::string CastText) { const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); Diag << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM, getLangOpts()), ")"); } Diag << FixItHint::CreateReplacement(ReplaceRange, CastText); }; auto ReplaceWithNamedCast = [&](StringRef CastType) { Diag << CastType; ReplaceWithCast((CastType + "<" + DestTypeString + ">").str()); }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. switch (CastExpr->getCastKind()) { case CK_FunctionToPointerDecay: ReplaceWithNamedCast("static_cast"); return; case CK_ConstructorConversion: if (!CastExpr->getTypeAsWritten().hasQualifiers() && DestTypeAsWritten->isRecordType() && !DestTypeAsWritten->isElaboratedTypeSpecifier()) { Diag << "constructor call syntax"; // FIXME: Validate DestTypeString, maybe. ReplaceWithCast(DestTypeString.str()); } else { ReplaceWithNamedCast("static_cast"); } return; case CK_NoOp: if (FnToFnCast) { ReplaceWithNamedCast("static_cast"); return; } if (SourceType == DestType) { Diag << "static_cast (if needed, the cast may be redundant)"; ReplaceWithCast(("static_cast<" + DestTypeString + ">").str()); return; } if (needsConstCast(SourceType, DestType) && pointedUnqualifiedTypesAreEqual(SourceType, DestType)) { ReplaceWithNamedCast("const_cast"); return; } if (DestType->isReferenceType()) { QualType Dest = DestType.getNonReferenceType(); QualType Source = SourceType.getNonReferenceType(); if (Source == Dest.withConst() || SourceType.getNonReferenceType() == DestType.getNonReferenceType()) { ReplaceWithNamedCast("const_cast"); return; } break; } // FALLTHROUGH case clang::CK_IntegralCast: // Convert integral and no-op casts between builtin types and enums to // static_cast. A cast from enum to integer may be unnecessary, but it's // still retained. if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && (DestType->isBuiltinType() || DestType->isEnumeralType())) { ReplaceWithNamedCast("static_cast"); return; } break; case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { if (SourceType->isVoidPointerType()) ReplaceWithNamedCast("static_cast"); else ReplaceWithNamedCast("reinterpret_cast"); return; } break; default: break; } Diag << "static_cast/const_cast/reinterpret_cast"; }