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)),
104 callExpr(argumentCountIs(1),
106 hasArgument(0, OptionalDerefMatcher))),
107 unless(anyOf(hasAncestor(typeLoc()),
108 hasAncestor(expr(matchers::hasUnevaluatedContext())))))
122 const MatchFinder::MatchResult &Result) {
123 const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
124 const auto *OptionalType = Result.Nodes.getNodeAs<QualType>(
"optional-type");
125 const auto *ValueType = Result.Nodes.getNodeAs<QualType>(
"value-type");
127 diag(MatchedExpr->getExprLoc(),
128 "conversion from %0 into %1 and back into %0, remove potentially "
129 "error-prone optional dereference")
130 << *OptionalType << ValueType->getUnqualifiedType();
132 if (
const auto *OperatorExpr =
133 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"op-call")) {
134 diag(OperatorExpr->getExprLoc(),
"remove '*' to silence this warning",
136 << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
137 OperatorExpr->getBeginLoc(), OperatorExpr->getExprLoc()));
140 if (
const auto *CallExpr =
141 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"member-call")) {
142 const SourceLocation Begin =
144 *Result.SourceManager, getLangOpts())
147 diag(CallExpr->getExprLoc(),
148 "remove call to %0 to silence this warning", DiagnosticIDs::Note);
149 Diag << CallExpr->getMethodDecl()
150 << FixItHint::CreateRemoval(
151 CharSourceRange::getTokenRange(Begin, CallExpr->getEndLoc()));
152 if (
const auto *Member =
153 llvm::dyn_cast<MemberExpr>(CallExpr->getCallee()->IgnoreImplicit());
154 Member && Member->isArrow())
155 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.