[clang-tidy] support to detect conversion in make_optional for bugprone-optional-value-conversion (#130417)

Add support for std::make_optional.

Fixes #119554
This commit is contained in:
Congcong Cai 2025-03-12 06:12:09 +08:00 committed by GitHub
parent 4d6ca11622
commit 0e4ba47ca8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 3 deletions

View File

@ -12,6 +12,7 @@
#include "../utils/OptionsUtils.h" #include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include <array> #include <array>
using namespace clang::ast_matchers; using namespace clang::ast_matchers;
@ -31,6 +32,7 @@ constexpr std::array<StringRef, 2> MakeSmartPtrList{
"::std::make_unique", "::std::make_unique",
"::std::make_shared", "::std::make_shared",
}; };
constexpr StringRef MakeOptional = "::std::make_optional";
} // namespace } // namespace
@ -83,9 +85,26 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
// known template methods in std // known template methods in std
callExpr( callExpr(
argumentCountIs(1), argumentCountIs(1),
anyOf(
// match std::make_unique std::make_shared
callee(functionDecl( callee(functionDecl(
matchers::matchesAnyListedName(MakeSmartPtrList), matchers::matchesAnyListedName(MakeSmartPtrList),
hasTemplateArgument(0, refersToType(BindOptionalType)))), hasTemplateArgument(
0, refersToType(BindOptionalType)))),
// match first std::make_optional by limit argument count
// (1) and template count (1).
// 1. template< class T > constexpr
// std::optional<decay_t<T>> make_optional(T&& value);
// 2. template< class T, class... Args > constexpr
// std::optional<T> make_optional(Args&&... args);
callee(functionDecl(templateArgumentCountIs(1),
hasName(MakeOptional),
returns(BindOptionalType)))),
hasArgument(0, OptionalDerefMatcher)),
callExpr(
argumentCountIs(1),
hasArgument(0, OptionalDerefMatcher))), hasArgument(0, OptionalDerefMatcher))),
unless(anyOf(hasAncestor(typeLoc()), unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(matchers::hasUnevaluatedContext()))))) hasAncestor(expr(matchers::hasUnevaluatedContext())))))

View File

@ -112,6 +112,10 @@ New check aliases
Changes in existing checks Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
- Improved :doc:`bugprone-optional-value-conversion
<clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
conversion in argument of ``std::make_optional``.
- Improved :doc:`bugprone-string-constructor - Improved :doc:`bugprone-string-constructor
<clang-tidy/checks/bugprone/string-constructor>` check to find suspicious <clang-tidy/checks/bugprone/string-constructor>` check to find suspicious
calls of ``std::string`` constructor with char pointer, start position and calls of ``std::string`` constructor with char pointer, start position and

View File

@ -27,9 +27,19 @@ class unique_ptr {};
template <typename type> template <typename type>
class shared_ptr {}; class shared_ptr {};
template <typename T>
class initializer_list {};
template <class T, class... Args> unique_ptr<T> make_unique(Args &&...args); template <class T, class... Args> unique_ptr<T> make_unique(Args &&...args);
template <class T, class... Args> shared_ptr<T> make_shared(Args &&...args); template <class T, class... Args> shared_ptr<T> make_shared(Args &&...args);
template <class T>
constexpr std::optional<__decay(T)> make_optional(T &&value);
template <class T, class... Args>
constexpr std::optional<T> make_optional(Args &&...args);
template <class T, class U, class... Args>
constexpr std::optional<T> make_optional(std::initializer_list<U> il, Args &&...args);
} // namespace std } // namespace std
struct A { struct A {
@ -45,9 +55,12 @@ void invalid() {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion] // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
std::make_shared<std::optional<int>>(opt.value()); std::make_shared<std::optional<int>>(opt.value());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion] // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
std::make_optional(opt.value());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
} }
void valid() { void valid() {
std::make_unique<A>(opt.value()); std::make_unique<A>(opt.value());
std::make_shared<A>(opt.value()); std::make_shared<A>(opt.value());
std::make_optional<int>(opt.value());
} }