54 auto BindOptionalType = qualType(hasCleanType(
55 qualType(hasDeclaration(namedDecl(
57 .bind(
"optional-type")));
59 auto EqualsBoundOptionalType =
60 qualType(hasCleanType(equalsBoundNode(
"optional-type")));
62 auto OptionalDerefMatcherImpl = callExpr(
64 cxxOperatorCallExpr(hasOverloadedOperatorName(
"*"),
65 hasUnaryOperand(hasType(EqualsBoundOptionalType)))
68 thisPointerType(EqualsBoundOptionalType),
70 anyOf(hasOverloadedOperatorName(
"*"),
72 .bind(
"member-call")),
73 hasType(qualType().bind(
"value-type")));
75 auto StdMoveCallMatcher =
76 callExpr(argumentCountIs(1), callee(functionDecl(hasName(
"::std::move"))),
77 hasArgument(0, ignoringImpCasts(OptionalDerefMatcherImpl)));
78 auto OptionalDerefMatcher =
79 ignoringImpCasts(anyOf(OptionalDerefMatcherImpl, StdMoveCallMatcher));
85 cxxConstructExpr(argumentCountIs(1), hasType(BindOptionalType),
86 hasArgument(0, OptionalDerefMatcher)),
94 hasTemplateArgument(0,
95 refersToType(BindOptionalType)))),
102 callee(functionDecl(templateArgumentCountIs(1),
103 hasName(MakeOptional),
104 returns(BindOptionalType)))),
105 hasArgument(0, OptionalDerefMatcher)),
106 callExpr(argumentCountIs(1),
108 hasArgument(0, OptionalDerefMatcher))),
109 unless(anyOf(hasAncestor(typeLoc()),
110 hasAncestor(expr(matchers::hasUnevaluatedContext())))))
124 const MatchFinder::MatchResult &Result) {
125 const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
126 const auto *OptionalType = Result.Nodes.getNodeAs<QualType>(
"optional-type");
127 const auto *ValueType = Result.Nodes.getNodeAs<QualType>(
"value-type");
129 diag(MatchedExpr->getExprLoc(),
130 "conversion from %0 into %1 and back into %0, remove potentially "
131 "error-prone optional dereference")
132 << *OptionalType << ValueType->getUnqualifiedType();
134 if (
const auto *OperatorExpr =
135 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"op-call")) {
136 diag(OperatorExpr->getExprLoc(),
"remove '*' to silence this warning",
138 << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
139 OperatorExpr->getBeginLoc(), OperatorExpr->getExprLoc()));
142 if (
const auto *CallExpr =
143 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"member-call")) {
144 const SourceLocation Begin =
146 *Result.SourceManager, getLangOpts())
149 diag(CallExpr->getExprLoc(),
150 "remove call to %0 to silence this warning", DiagnosticIDs::Note);
151 Diag << CallExpr->getMethodDecl()
152 << FixItHint::CreateRemoval(
153 CharSourceRange::getTokenRange(Begin, CallExpr->getEndLoc()));
154 if (
const auto *Member =
155 llvm::dyn_cast<MemberExpr>(CallExpr->getCallee()->IgnoreImplicit());
156 Member && Member->isArrow())
157 Diag << FixItHint::CreateInsertion(CallExpr->getBeginLoc(),
"*");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.