llvm-project
128 строк · 5.0 Кб
1//===--- OptionalValueConversionCheck.cpp - clang-tidy --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "OptionalValueConversionCheck.h"10#include "../utils/LexerUtils.h"11#include "../utils/Matchers.h"12#include "../utils/OptionsUtils.h"13#include "clang/AST/ASTContext.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15
16using namespace clang::ast_matchers;17
18namespace clang::tidy::bugprone {19
20namespace {21
22AST_MATCHER_P(QualType, hasCleanType, ast_matchers::internal::Matcher<QualType>,23InnerMatcher) {24return InnerMatcher.matches(25Node.getNonReferenceType().getUnqualifiedType().getCanonicalType(),26Finder, Builder);27}
28
29} // namespace30
31OptionalValueConversionCheck::OptionalValueConversionCheck(32StringRef Name, ClangTidyContext *Context)33: ClangTidyCheck(Name, Context),34OptionalTypes(utils::options::parseStringList(35Options.get("OptionalTypes",36"::std::optional;::absl::optional;::boost::optional"))),37ValueMethods(utils::options::parseStringList(38Options.get("ValueMethods", "::value$;::get$"))) {}39
40std::optional<TraversalKind>41OptionalValueConversionCheck::getCheckTraversalKind() const {42return TK_AsIs;43}
44
45void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {46auto ConstructTypeMatcher =47qualType(hasCleanType(qualType().bind("optional-type")));48
49auto CallTypeMatcher =50qualType(hasCleanType(equalsBoundNode("optional-type")));51
52auto OptionalDereferenceMatcher = callExpr(53anyOf(54cxxOperatorCallExpr(hasOverloadedOperatorName("*"),55hasUnaryOperand(hasType(CallTypeMatcher)))56.bind("op-call"),57cxxMemberCallExpr(thisPointerType(CallTypeMatcher),58callee(cxxMethodDecl(anyOf(59hasOverloadedOperatorName("*"),60matchers::matchesAnyListedName(ValueMethods)))))61.bind("member-call")),62hasType(qualType().bind("value-type")));63
64auto StdMoveCallMatcher =65callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))),66hasArgument(0, ignoringImpCasts(OptionalDereferenceMatcher)));67Finder->addMatcher(68cxxConstructExpr(69argumentCountIs(1U),70hasDeclaration(cxxConstructorDecl(71ofClass(matchers::matchesAnyListedName(OptionalTypes)))),72hasType(ConstructTypeMatcher),73hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher,74StdMoveCallMatcher))),75unless(anyOf(hasAncestor(typeLoc()),76hasAncestor(expr(matchers::hasUnevaluatedContext())))))77.bind("expr"),78this);79}
80
81void OptionalValueConversionCheck::storeOptions(82ClangTidyOptions::OptionMap &Opts) {83Options.store(Opts, "OptionalTypes",84utils::options::serializeStringList(OptionalTypes));85Options.store(Opts, "ValueMethods",86utils::options::serializeStringList(ValueMethods));87}
88
89void OptionalValueConversionCheck::check(90const MatchFinder::MatchResult &Result) {91const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>("expr");92const auto *OptionalType = Result.Nodes.getNodeAs<QualType>("optional-type");93const auto *ValueType = Result.Nodes.getNodeAs<QualType>("value-type");94
95diag(MatchedExpr->getExprLoc(),96"conversion from %0 into %1 and back into %0, remove potentially "97"error-prone optional dereference")98<< *OptionalType << ValueType->getUnqualifiedType();99
100if (const auto *OperatorExpr =101Result.Nodes.getNodeAs<CXXOperatorCallExpr>("op-call")) {102diag(OperatorExpr->getExprLoc(), "remove '*' to silence this warning",103DiagnosticIDs::Note)104<< FixItHint::CreateRemoval(CharSourceRange::getTokenRange(105OperatorExpr->getBeginLoc(), OperatorExpr->getExprLoc()));106return;107}108if (const auto *CallExpr =109Result.Nodes.getNodeAs<CXXMemberCallExpr>("member-call")) {110const SourceLocation Begin =111utils::lexer::getPreviousToken(CallExpr->getExprLoc(),112*Result.SourceManager, getLangOpts())113.getLocation();114auto Diag =115diag(CallExpr->getExprLoc(),116"remove call to %0 to silence this warning", DiagnosticIDs::Note);117Diag << CallExpr->getMethodDecl()118<< FixItHint::CreateRemoval(119CharSourceRange::getTokenRange(Begin, CallExpr->getEndLoc()));120if (const auto *Member =121llvm::dyn_cast<MemberExpr>(CallExpr->getCallee()->IgnoreImplicit());122Member && Member->isArrow())123Diag << FixItHint::CreateInsertion(CallExpr->getBeginLoc(), "*");124return;125}126}
127
128} // namespace clang::tidy::bugprone129