This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] fix wrong fixup for bugprone-implicit-widening-of-multiplication-result
AbandonedPublic

Authored by v1nh1shungry on Jan 5 2023, 7:24 AM.

Details

Summary

Relevant issue: https://github.com/llvm/llvm-project/issues/56728

Currently, this check will give some wrong fix-hints, e.g.

long long foobar() {
  return 1024 * 1024;
  // one of the fix-hint will lead to `return static_cast<long long>(1024 * )1024;
  // another one will lead to `return static_cast<long long>()1204 * 1024;
}

Diff Detail

Event Timeline

v1nh1shungry created this revision.Jan 5 2023, 7:24 AM
Herald added a project: Restricted Project. · View Herald Transcript
v1nh1shungry requested review of this revision.Jan 5 2023, 7:24 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 5 2023, 7:24 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Sorry, I don't know how to add tests for such fixes. Could anyone please give me some hints? Thanks!

Eugene.Zelenko added inline comments.
clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
107

Please don't use auto unless type is explicitly stated in same statement or iterator.

221

Ditto.

don't use auto

v1nh1shungry marked 2 inline comments as done.Jan 5 2023, 6:31 PM

Thank you for reviewing and giving suggestions! @Eugene.Zelenko

v1nh1shungry edited the summary of this revision. (Show Details)Jan 5 2023, 9:17 PM
Febbe added a subscriber: Febbe.Jan 7 2023, 5:14 AM

Sorry, I don't know how to add tests for such fixes. Could anyone please give me some hints? Thanks!

It seems like the tests for this check do not contain checks for the fixups itself.

Normally, they are tested with CHECK-FIXES: <Fixed line above, whitespaces truncated at the beginning and the end> for example:

return F? Mov: Movable{}; 
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use] 
// CHECK-FIXES: return F? std::move(Mov): Movable{};

In the case of this test file, you have to append the suffixes to the CHECK-FIXES to CHECK-FIXES-<SUFFIX> where <SUFFIX> is one of C, CXX, ALL

return F? Mov: Movable{}; 
// CHECK-MESSAGES-CXX: [[@LINE-1]]:13: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use] 
// CHECK-FIXES-CXX: return F? std::move(Mov): Movable{};
clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
215

Actually, I find it pretty weird/suspicious, that <AnyBinaryExr*> -> getEndLoc() does not return the end of its Expr. The code looked correct already.
Can you elaborate, if this is a bug in the getEndLoc() of those Exprs? It might be better to fix it there directly.

Thank you for reviewing and giving suggestions! @Febbe

Please take a look at my reply. Sorry if I misunderstood anything!

clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
215

I'm really not an expert on SourceLocation. I just took a rough look at these getEndLoc()s.

Take 1 + 2 as an example, this is a BinaryOperator with two IntegerLiterals. When we call Expr::getEndLoc() which is actually Stmt::getEndLoc() on it, it will call BinaryOperator::getEndLoc() and this actually returns the RHS->getEndLoc(), that is the result of IntegerLiteral(2)->getEndLoc(). And the interesting stuff is that IntegerLiteral::getBeginLoc() and IntegerLiteral::getEndLoc() return the same Loc. Although I'm not sure which location these will return, I guess that's why BinaryOperator::getEndLoc() doesn't return the end of the expression.

Sorry if I misunderstood anything! After all, I didn't find documents saying which location these Exprs' getEndLoc() should return. I think it's better to have experts take a look at this.

clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-array-subscript-expression.cpp
18

Actually I have tried adding

// CHECK-FIXES-C: return &base[(ptrdiff_t)(a * b)];
// CHECK-FIXES-CXX: return &base[static_cast<ptrdiff_t>(a * b)];

under this line, but the test failed, and when I took a look at build/tools/clang/tools/extra/test/clang-tidy/checkers/bugprone/Output/implicit-widening-of-multiplication-result-array-subscript-expression.cpp.tmp.cpp, I found that these codes didn't get modified. And I took a look at other files which have CHECK-FIXES lines, I found the codes in the corresponding temporary files got fixed.

bansan added a subscriber: bansan.Jan 9 2023, 1:49 AM

I just made a test:

#include <cstdint>
#include <vector>

int64_t f(int x) {
  return 1024 * 1024 * 1024 * x;
}

void g(int x) {
  std::vector<int> b;
  b.reserve(1024 * 1024 * 1024 * x);
}

int main() {
  f(1024);
  g(1024);
}

The fixed code:

#include <cstddef>
#include <cstdint>
#include <vector>

int64_t f(int x) {
  return static_cast<int64_t>(1024 * 1024 * 1024 * x);
}

void g(int x) {
  std::vector<int> b;
  b.reserve(static_cast<size_type>(1024 * 1024 * 1024 * x));
}

int main() {
  f(1024);
  g(1024);
}

For the first test, I still think that the auto fix should be return static_cast<int64_t>(1024) * 1024 * 1024 * x; to have the good result.

For the second test, the type is the internal type of std::vector.

Thank you for taking a look at this patch! @bansan

For the first test, I still think that the auto fix should be return static_cast<int64_t>(1024) * 1024 * 1024 * x; to have the good result.

I'd say I'm not the author of this checker. I think this behavior change needs a discussion somewhere, for example, you could raise an issue on GitHub. I just want to fix the wrong fix-hint and I think this is out of what this patch should do. Sorry!

For the second test, the type is the internal type of std::vector.

Good catch, thanks! I didn't realize this problem. Will take a look at it.

For the second test, the type is the internal type of std::vector.

There are two approaches to fixing this issue. One is fixing with the FULLY qualified type name, in the above case that is, std::vector<int, std::allocator<int>>::size_type. Another one is fixing with the desugared type name, that is, unsigned long. I personally don't have a strong opinion on which one is better.

WDYT, @bansan?

There are two approaches to fixing this issue. One is fixing with the FULLY qualified type name, in the above case that is, std::vector<int, std::allocator<int>>::size_type. Another one is fixing with the desugared type name, that is, unsigned long. I personally don't have a strong opinion on which one is better.

IMHO, the first approach can cause redundancy in some cases, like this one. The second one will turn int64_t into long, which I don't have a good feeling about.

bansan added a comment.Jan 9 2023, 4:39 AM

The current rule is quite simple. I tried this example:

template<typename T>
T h(int x) {
  return 1024 * 1024 * 1024 * x;
}

int main() {
  h<int64_t>(1024);
}

The rule does not complain. So I think that the "resolved" type (size_t) should be use instead of the templated one (std::vector<int, std::allocator<int>>::size_type).

The rule does not complain.

Sorry, I'm confused. Could you please explain what you expect this example should achieve and what is "the rule"? Thanks!

So I think that the "resolved" type (size_t) should be use instead of the templated one (std::vector<int, std::allocator<int>>::size_type).

Note: the "desugared type" I mention is the FULLY desugared type, so it will achieve unsigned long instead of size_t. There is a method QualType::getSingleStepDesugaredType() which may get size_t, but I haven't had a try. But even though it works, this will still turn int64_t into long.

BTW, if you think the desugared type is better, which do you think is better, to get single step desugared or to get fully desugared?

Sorry, I'm confused. Could you please explain what you expect this example should achieve and what is "the rule"? Thanks!

Ah, I think I understand it. You mean the bugprone-implicit-widening-of-multiplication-result check doesn't warn anything about this example, right?

If so, although I don't understand what you mean by coming up with this example, to classify, when I said

The second one will turn int64_t into long, which I don't have a good feeling about.

the point is that if we choose to use the desugared type,

int64_t foobar() {
  return 1024 * 1024;
}

will change to

int64_t foobar() {
  return static_cast<long>(1024 * 1024);
                     ^^^^ instead of int64_t
}

Here I think this will break the portability.

There is a method QualType::getSingleStepDesugaredType() which may get size_t

Just did a quick test, and it doesn't work like this. So I can only get the fully desugared type for now. Sorry :(

About my template example: I wanted to say that the actual bugprone-implicit-widening-of-multiplication-result rule looks to not analyze template calculation problem. So I think it's better to use the desugared type (size_t).

It's not acceptable (IMHO) that the hard-coded size_t is resolved as long. According to https://en.cppreference.com/w/cpp/language/types long may be 32 bits.

Unfortunately, I don't know anything about AST and which function you should use :/

About my template example: I wanted to say that the actual bugprone-implicit-widening-of-multiplication-result rule looks to not analyze template calculation problem. So I think it's better to use the desugared type (size_t).

Hmm, I don't think this two are related.

It's not acceptable (IMHO) that the hard-coded size_t is resolved as long. According to https://en.cppreference.com/w/cpp/language/types long may be 32 bits.

Yeah, I don't have a good feeling about it too. The point is that we love desugaring size_type to size_t (which I haven't found a way to achieve yet), and hate desugaring int64_t to long, but how can we classify them? They BOTH are the desugared type.

According to the conversation above, I'd use the qualified alias type name.

Febbe added inline comments.Jan 11 2023, 7:46 PM
clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-array-subscript-expression.cpp
18

Maybe, because the Fixup is marked as Note

LegalizeAdulthood resigned from this revision.Mar 29 2023, 8:07 AM
v1nh1shungry abandoned this revision.Apr 19 2023, 11:40 PM