71static std::optional<FixItHint>
buildFixIt(
const CXXMemberCallExpr *Call,
73 const Expr *FallbackArg,
74 const CXXRecordDecl *OptionalClass,
75 const ASTContext &Ctx) {
76 if (Call->getBeginLoc().isMacroID())
78 if (!ObjExpr->isLValue())
80 if (ObjExpr->HasSideEffects(Ctx))
85 StringRef ObjText = tooling::fixit::getText(*ObjExpr, Ctx);
86 StringRef ArgText = tooling::fixit::getText(*FallbackArg, Ctx);
88 if (ObjText.empty() || ArgText.empty())
91 const std::string Replacement =
92 (
"(" + ObjText +
" ? *" + ObjText +
" : " + ArgText +
")").str();
93 return tooling::fixit::createReplacement(*Call, Replacement);
113 auto OptionalTypesMatcher =
115 auto ValueOrMatcher = hasAnyName(
"value_or",
"valueOr",
"ValueOr");
116 auto ValueOrCall = cxxMemberCallExpr(
117 callee(cxxMethodDecl(ValueOrMatcher, ofClass(OptionalTypesMatcher))),
118 anyOf(on(isLValueExpr()),
119 hasType(qualType(unless(hasNonTrivialMoveCtor())))),
121 anyOf(matchers::isExpensiveToCopy(), isLargerThan(SizeThreshold)))),
122 unless(anyOf(hasAncestor(typeLoc()),
123 hasAncestor(expr(matchers::hasUnevaluatedContext())))));
125 if (WarnOnOwnershipTaking) {
126 Finder->addMatcher(ValueOrCall.bind(
"call"),
this);
132 varDecl(hasType(lValueReferenceType(pointee(isConstQualified()))),
133 hasInitializer(ignoringImplicit(ValueOrCall.bind(
"call")))),
137 Finder->addMatcher(callExpr(forEachArgumentWithParam(
138 ignoringImplicit(ValueOrCall.bind(
"call")),
139 parmVarDecl(hasType(lValueReferenceType(
140 pointee(isConstQualified())))))),
145 cxxMemberCallExpr(on(ignoringImplicit(ValueOrCall.bind(
"call"))),
146 callee(cxxMethodDecl(isConst()))),
151 const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"call");
152 assert(Call &&
"Matcher guaranteed a bound 'call' node");
153 const Expr *ObjExpr = Call->getImplicitObjectArgument();
154 assert(ObjExpr &&
"CXXMemberCallExpr must have an implicit object argument");
156 const ASTContext &Ctx = *Result.Context;
157 const QualType ValueType = Call->getType();
159 const CXXMethodDecl *Method = Call->getMethodDecl();
160 const CXXRecordDecl *OptionalClass = Method->getParent();
161 const Expr *FallbackArg = Call->getArg(0)->IgnoreImplicit();
162 const bool HasSideEffects = FallbackArg->HasSideEffects(Ctx);
165 auto Diag = diag(Call->getExprLoc(),
"'%0' copies expensive type %1; %2")
166 << Method->getName() << ValueType
169 if (!HasSideEffects) {
170 if (
auto Fix =
buildFixIt(Call, ObjExpr, FallbackArg, OptionalClass, Ctx))
176 diag(FallbackArg->getExprLoc(),
177 "the fallback is always evaluated; a conditional rewrite would "
178 "change evaluation semantics",
179 DiagnosticIDs::Note);
static cl::opt< bool > Fix("fix", desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.