//===--- OptionalValueConversionCheck.cpp - clang-tidy --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "OptionalValueConversionCheck.h" #include "../utils/LexerUtils.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; namespace clang::tidy::bugprone { namespace { AST_MATCHER_P(QualType, hasCleanType, ast_matchers::internal::Matcher, InnerMatcher) { return InnerMatcher.matches( Node.getNonReferenceType().getUnqualifiedType().getCanonicalType(), Finder, Builder); } } // namespace OptionalValueConversionCheck::OptionalValueConversionCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), OptionalTypes(utils::options::parseStringList( Options.get("OptionalTypes", "::std::optional;::absl::optional;::boost::optional"))), ValueMethods(utils::options::parseStringList( Options.get("ValueMethods", "::value$;::get$"))) {} std::optional OptionalValueConversionCheck::getCheckTraversalKind() const { return TK_AsIs; } void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) { auto ConstructTypeMatcher = qualType(hasCleanType(qualType().bind("optional-type"))); auto CallTypeMatcher = qualType(hasCleanType(equalsBoundNode("optional-type"))); auto OptionalDereferenceMatcher = callExpr( anyOf( cxxOperatorCallExpr(hasOverloadedOperatorName("*"), hasUnaryOperand(hasType(CallTypeMatcher))) .bind("op-call"), cxxMemberCallExpr(thisPointerType(CallTypeMatcher), callee(cxxMethodDecl(anyOf( hasOverloadedOperatorName("*"), matchers::matchesAnyListedName(ValueMethods))))) .bind("member-call")), hasType(qualType().bind("value-type"))); auto StdMoveCallMatcher = callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))), hasArgument(0, ignoringImpCasts(OptionalDereferenceMatcher))); Finder->addMatcher( cxxConstructExpr( argumentCountIs(1U), hasDeclaration(cxxConstructorDecl( ofClass(matchers::matchesAnyListedName(OptionalTypes)))), hasType(ConstructTypeMatcher), hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher, StdMoveCallMatcher)))) .bind("expr"), this); } void OptionalValueConversionCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "OptionalTypes", utils::options::serializeStringList(OptionalTypes)); Options.store(Opts, "ValueMethods", utils::options::serializeStringList(ValueMethods)); } void OptionalValueConversionCheck::check( const MatchFinder::MatchResult &Result) { const auto *MatchedExpr = Result.Nodes.getNodeAs("expr"); const auto *OptionalType = Result.Nodes.getNodeAs("optional-type"); const auto *ValueType = Result.Nodes.getNodeAs("value-type"); diag(MatchedExpr->getExprLoc(), "conversion from %0 into %1 and back into %0, remove potentially " "error-prone optional dereference") << *OptionalType << ValueType->getUnqualifiedType(); if (const auto *OperatorExpr = Result.Nodes.getNodeAs("op-call")) { diag(OperatorExpr->getExprLoc(), "remove '*' to silence this warning", DiagnosticIDs::Note) << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( OperatorExpr->getBeginLoc(), OperatorExpr->getExprLoc())); return; } if (const auto *CallExpr = Result.Nodes.getNodeAs("member-call")) { const SourceLocation Begin = utils::lexer::getPreviousToken(CallExpr->getExprLoc(), *Result.SourceManager, getLangOpts()) .getLocation(); auto Diag = diag(CallExpr->getExprLoc(), "remove call to %0 to silence this warning", DiagnosticIDs::Note); Diag << CallExpr->getMethodDecl() << FixItHint::CreateRemoval( CharSourceRange::getTokenRange(Begin, CallExpr->getEndLoc())); if (const auto *Member = llvm::dyn_cast(CallExpr->getCallee()->IgnoreImplicit()); Member && Member->isArrow()) Diag << FixItHint::CreateInsertion(CallExpr->getBeginLoc(), "*"); return; } } } // namespace clang::tidy::bugprone