-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[clang-tidy] bugprone-unchecked-optional-access: handle BloombergLP::bdlb:NullableValue::makeValue to prevent false-positives
#144313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang-tidy] bugprone-unchecked-optional-access: handle BloombergLP::bdlb:NullableValue::makeValue to prevent false-positives
#144313
Conversation
bugprone-unchecked-optional-access: handle NullableValue::makeValue to prevent false-positivesbugprone-unchecked-optional-access: handle BloombergLP::bdlb:NullableValue::makeValue to prevent false-positives
|
@llvm/pr-subscribers-clang-tidy @llvm/pr-subscribers-clang-analysis Author: Valentyn Yukhymenko (BaLiKfromUA) Changes#101450 added support for However, if (opt.isNull()) {
opt.makeValue(42);
}
opt.value(); // triggers false positive warning from `bugprone-unchecked-optional-access`My patch addresses this issue. Full diff: https://github.com/llvm/llvm-project/pull/144313.diff 3 Files Affected:
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bdlb_nullablevalue.h b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bdlb_nullablevalue.h
index 4411bcfd60a74..0812677111995 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bdlb_nullablevalue.h
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bdlb_nullablevalue.h
@@ -20,6 +20,14 @@ class NullableValue : public bsl::optional<T> {
const T &value() const &;
T &value() &;
+ constexpr T &makeValue();
+
+ template <typename U>
+ constexpr T &makeValue(U&& v);
+
+ template <typename... ARGS>
+ constexpr T &makeValueInplace(ARGS &&... args);
+
// 'operator bool' is inherited from bsl::optional
constexpr bool isNull() const noexcept;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
index 3167b85f0e024..b910db20b3c2e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
@@ -141,6 +141,20 @@ void nullable_value_after_swap(BloombergLP::bdlb::NullableValue<int> &opt1, Bloo
}
}
+void nullable_value_make_value(BloombergLP::bdlb::NullableValue<int> &opt1, BloombergLP::bdlb::NullableValue<int> &opt2) {
+ if (opt1.isNull()) {
+ opt1.makeValue(42);
+ }
+
+ opt1.value();
+
+ if (opt2.isNull()) {
+ opt2.makeValueInplace(42);
+ }
+
+ opt2.value();
+}
+
template <typename T>
void function_template_without_user(const absl::optional<T> &opt) {
opt.value(); // no-warning
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index 164d2574132dd..decb32daa9410 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -985,6 +985,19 @@ auto buildTransferMatchSwitch() {
isOptionalMemberCallWithNameMatcher(hasName("isNull")),
transferOptionalIsNullCall)
+ // NullableValue::makeValue, NullableValue::makeValueInplace
+ // Only NullableValue has these methods, but this
+ // will also pass for other types
+ .CaseOfCFGStmt<CXXMemberCallExpr>(
+ isOptionalMemberCallWithNameMatcher(hasAnyName("makeValue", "makeValueInplace")),
+ [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
+ LatticeTransferState &State) {
+ if (RecordStorageLocation *Loc =
+ getImplicitObjectLocation(*E, State.Env)) {
+ setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
+ }
+ })
+
// optional::emplace
.CaseOfCFGStmt<CXXMemberCallExpr>(
isOptionalMemberCallWithNameMatcher(hasName("emplace")),
|
|
At the moment of the previous PR, there was a concern about configurability of check -- #101450 (review) I am happy to address it separately if it's still a concern and if someone can guide me for first steps :) |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
I think I was about adding an option to configure list of optional classes. Probably |
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Show resolved
Hide resolved
|
Analysis reviewers, could you please take a look at this #144313 (comment). Your opinion is highly appreciated! |
|
Friendly ping to not lose the visibility of conversation. Waiting for follow up on #144313 (comment) Thank you for review! |
|
Still interested in addressing this issue! Is it possible to move the configuration decision to a separate issue or PR? Or is it a blocker? I'm happy to work on the configuration, just don't want to delay the fix to false-positive while we're waiting for a decision on the configuration approach. Thanks! |
All warnings/checks focused on misuse of specific standard C++ classes have to deal with that same problem at some point. If only there was a universal, reusable solution in Clang! Like, it'd be nice if there was some kind of fancy attribute to annotate classes as "similar" to standard classes: [[clang::this_class_is_basically_a_custom("std::optional")]]
class NullableValue {
public:
[[clang::this_method_basically_works_like("std::optional::emplace")]]
template <typename... T> T &makeValueInplace(T &&... args);
};An attribute-based solution would be particularly useful when you're shipping a library and you get to add the attributes to your headers. This way you don't have to ask your users to include stuff in their respective clang-tidy configs whenever they include your library. It'd simply work "out of the box" for all users. But, of course, that's a matter of a much broader discussion. I don't think your work should be blocked on implementing any of this. I'm just saying that you're not alone in this struggle 😅 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just Release Notes formatting need to be fixed
|
@vbvictor thanks for approval! I addressed your remark about the release note. Could you merge this PR, please, when you are available? I don't have merge permissions |
Co-authored-by: Baranov Victor <[email protected]>
|
@haoNoQ sorry for late reply, want to respond to this idea! I agree that attribute-based solution gives much more flexibility and could benefit not only
If I were interested in starting such a discussion, what would be the right process to follow? From what I understand, it might begin with an RFC on Discourse — but since this is related to introducing new attributes, it seems like it could also involve the broader Clang community, not just clang-tidy. Will be interested to hear your opinion on this process, so I can access my capacity and the scope of work! Thanks! cc @vbvictor |
|
Hi! Yes, the usual RFC process can probably handle that, it just needs to go to the Clang Frontend section instead of the clang-tidy section. Just plan ahead a little bit, think about the motivation and the prior art, pros and cons and potential caveats, think how far you're willing to go on your own in this direction and how committed you'd be to maintaining this facility in the future, and lay it out in the RFC. It may be a good idea to have a tiny proof-of-concept patch (implement the attribute and teach the clang-tidy check to consume it) (it's very easy to implement an attribute in clang - just add it to the list) but don't write too much code before you're able to confirm that clang maintainers are interested. When it comes to motivation, yeah, every time you find yourself hardcoding a somewhat-standard function or class name in the checker code, it gets better when you have ways to annotate custom classes or replacements. Also the library headers thing is really useful. Off the top of my head, here's a few places that could benefit:
When it comes to caveats, it's somewhat important to recognize that many custom replacement classes slightly diverge from their standard counterparts.
|
#101450 added support for
BloombergLP::bdlb::NullableValue.However,
NullableValue::makeValueandNullableValue::makeValueInplacehave been missed which impacts code like this:My patch addresses this issue.
Docs that I used for methods mocks