54 auto BindOptionalType = qualType(
55 hasCleanType(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)))
67 cxxMemberCallExpr(thisPointerType(EqualsBoundOptionalType),
68 callee(cxxMethodDecl(anyOf(
69 hasOverloadedOperatorName(
"*"),
71 .bind(
"member-call")),
72 hasType(qualType().bind(
"value-type")));
74 auto StdMoveCallMatcher =
75 callExpr(argumentCountIs(1), callee(functionDecl(hasName(
"::std::move"))),
76 hasArgument(0, ignoringImpCasts(OptionalDerefMatcherImpl)));
77 auto OptionalDerefMatcher =
78 ignoringImpCasts(anyOf(OptionalDerefMatcherImpl, StdMoveCallMatcher));
83 cxxConstructExpr(argumentCountIs(1), hasType(BindOptionalType),
84 hasArgument(0, OptionalDerefMatcher)),
93 0, refersToType(BindOptionalType)))),
100 callee(functionDecl(templateArgumentCountIs(1),
101 hasName(MakeOptional),
102 returns(BindOptionalType)))),
103 hasArgument(0, OptionalDerefMatcher)),
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.