AMS21 added inline comments.

================
Comment at: 
clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.cpp:45-49
+    const auto *ProtoType = Decl->getType()->castAs<FunctionProtoType>();
+    const Expr *NoexceptExpr = ProtoType->getNoexceptExpr();
+    if (NoexceptExpr) {
+      NoexceptExpr = NoexceptExpr->IgnoreImplicit();
+      if (!isa<CXXBoolLiteralExpr>(NoexceptExpr)) {
----------------
PiotrZSL wrote:
> woudn't getExceptionSpecInfo() != EST_NoexceptFalse do a trick here ?
I've tested it and it seem that a struct like this:

```
struct B {
  static constexpr bool kFalse = false;
  B(B &&) noexcept(kFalse);
};
```

Also has the the `ExceptionSpecType` set to `EST_NoexceptFalse`. So changing 
this would change the previous behavior.


================
Comment at: clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp:95
+                                     DefaultableMemberKind Kind,
+                                     SkipMethods SkipMethods) {
+  if (!RecordDecl)
----------------
PiotrZSL wrote:
> Can we hit some endless recursion here ?
> Maybe so protection against checking Record that we currently checking.
Yes we can hit an infinite recursion here.
Take this class:

```
struct A {
    A(A &&) = default;
};
```

Since the move constructor is defaulted, we need to call 
`analyzeUnresolvedOrDefaulted`. There we determine the kind of defaulted member 
function which in this case is a move constructor.
Then we call `analyzeRecord`. Without setting `SkipMethods::Yes` we would try 
to check the move constructor of the class which in this case is `A`. We would 
call `analyze` on the move constructor of `A` which is exactly where we started 
and we'd have an infinite loop.

That is why I've added the `SkipMethods` parameter. While analyzing the 
defaulted move constructor of `A` we only want to look at its base classes and 
it's fields and determine if they are declared as throwing or not. While for 
their the base classes and fields of `A` we also want to check their move 
constructor (if they have any).

I hope my explanation was at least a bit helpful.

There might be a better solution to this, Before this I had essentially the 
same code twice for checking the bases and field and wanted to combine them.


================
Comment at: clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp:95
+                                     DefaultableMemberKind Kind,
+                                     SkipMethods SkipMethods) {
+  if (!RecordDecl)
----------------
AMS21 wrote:
> PiotrZSL wrote:
> > Can we hit some endless recursion here ?
> > Maybe so protection against checking Record that we currently checking.
> Yes we can hit an infinite recursion here.
> Take this class:
> 
> ```
> struct A {
>     A(A &&) = default;
> };
> ```
> 
> Since the move constructor is defaulted, we need to call 
> `analyzeUnresolvedOrDefaulted`. There we determine the kind of defaulted 
> member function which in this case is a move constructor.
> Then we call `analyzeRecord`. Without setting `SkipMethods::Yes` we would try 
> to check the move constructor of the class which in this case is `A`. We 
> would call `analyze` on the move constructor of `A` which is exactly where we 
> started and we'd have an infinite loop.
> 
> That is why I've added the `SkipMethods` parameter. While analyzing the 
> defaulted move constructor of `A` we only want to look at its base classes 
> and it's fields and determine if they are declared as throwing or not. While 
> for their the base classes and fields of `A` we also want to check their move 
> constructor (if they have any).
> 
> I hope my explanation was at least a bit helpful.
> 
> There might be a better solution to this, Before this I had essentially the 
> same code twice for checking the bases and field and wanted to combine them.
Another way to defiantly have an infinite recursion is if to resolve a `struct 
A;` we needed to resolve `struct B;` and to resolve that we needed to resolve 
`struct A`.  You would need something like `A` inheriting from `B` and `B` 
inheriting from `A` or having the other type as a member variable. Same goes 
for a struct which contains itself.

But I'm currently unaware on how to create such a scenario with legal C++.


================
Comment at: clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp:108-114
+  BasesToVisit Bases = isConstructor(Kind)
+                           ? BasesToVisit::VisitPotentiallyConstructedBases
+                           : BasesToVisit::VisitAllBases;
+
+  if (Bases == BasesToVisit::VisitPotentiallyConstructedBases)
+    Bases = RecordDecl->isAbstract() ? BasesToVisit::VisitNonVirtualBases
+                                     : BasesToVisit::VisitAllBases;
----------------
PiotrZSL wrote:
> I'm not sure if we need to be so picky...
> In short we could check all bases.
> Virtual, Abstract or not...
Honestly I not sure about it. I just tried to copy what `Sema` does when 
resolving noexcept.
Removing it definitely makes the code easier to read.


================
Comment at: clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.h:23
+public:
+  enum class State : std::int8_t {
+    Throwing,    ///< This function has been declared as possibly throwing.
----------------
PiotrZSL wrote:
> No need for std::int8_t, and if you really want then use std::uint8_t
I prefer to not use it, but saw `ExceptionAnalyzer` use it. So I just copied it 
from there.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146922/new/

https://reviews.llvm.org/D146922

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to