[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-10-17 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

I've updated https://github.com/llvm/llvm-project/pull/68572  to do as 
suggested.

To sum up: we have a new flag `-Wthread-safety-reference-return`, which is on 
by default under `-Wthread-safety-reference`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-10-12 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4653664 , @aaronpuchert 
wrote:

> In D153131#4653564 , @courbet wrote:
>
>> We have a large number of users of `-Werror -Wthread-safety-analysis` 
>> internally. When we make the new warnings part of that flag we cannot 
>> integrate because we're breaking all these users.
>
> The proposal was to include it in `-Wthread-safety-reference`, not 
> `-Wthread-safety-analysis`. See 
> https://clang.llvm.org/docs/DiagnosticsReference.html#wthread-safety for the 
> existing flags and their relations.

Sorry, I meant `-Wthread-safety-reference`.

>> If we don't integrate we can't run the new analysis to see what we would 
>> need to fix.
>
> Can you not add `-Wno-error=thread-safety-reference-return` together with the 
> integration? Or are there too many places adding it independently?

Yes, we have way too many instances. I'm going to discuss with people dealing 
with integrates to see whether disabling the new flag globally is a possibility.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-10-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4653456 , @aaronpuchert 
wrote:

> In D153131#4653412 , @courbet wrote:
>
>> I also had some push back internally on adding this to the existing flag. 
>> I'm going to add `-Wthread-safety-reference-return`, can we start by not 
>> temporarily including it in `-Wthread-safety-reference` so that we can see 
>> how much work it it to fix those warnings ?
>
> Can you elaborate on this? What's the reasoning? Here are two reasons for 
> having it as part of `-Wthread-safety-reference` right from the beginning:
>
> - `-Wthread-safety-reference` is already separate from 
> `-Wthread-safety-analysis` because passing a reference does not imply an 
> access. If you have the warning you're arguably already opting into this, and 
> I don't see much of a difference between passing via parameter versus passing 
> by return.
> - Most users don't follow all reviews or read the release notes in detail and 
> won't notice the new flag until it shows up in their build log. So we'd just 
> lose time.
>
> Since warning messages always indicate the warning flag and thus make 
> disabling it easy, I don't see an issue with enabling it right away as part 
> of `-Wthread-safety-reference`.
>
> Lastly, this doesn't seem complicated enough to warrant extended beta testing.

We have a large number of users of `-Werror -Wthread-safety-analysis` 
internally. When we make the new warnings part of that flag we cannot integrate 
because we're breaking all these users. If we don't integrate we can't run the 
new analysis to see what we would need to fix.

Introducing a new flag allows us to:

- keep the current analysis running for users of `-Wthread-safety-analysis`.
- progressively add `-Wthread-safety-analysis-reference-return` to these users 
across the codebase, fixing them or disabling analysis as needed.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-10-09 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4653362 , @aaronpuchert 
wrote:

> In D153131#4653345 , @aeubanks 
> wrote:
>
>> This is finding lots of real issues in code, which is awesome, but could I 
>> request that this be put under a separate warning flag so we can toggle off 
>> just the new functionality and turn it on as we clean our codebase? e.g. 
>> `-W[no-]thread-safety-analysis-return`
>
> Fine for me, but we might want to remove it again after one or two releases. 
> I'm not sure how to communicate that this is just a “transitory” flag.
>
> And it should be included by default in `-Wthread-safety-reference`, so that 
> users of that flag see the new warnings/errors, and can demote them to 
> warnings while fixing them. To emphasize the subflag status, I'd suggest 
> something like `-Wthread-safety-reference-return`.

I also had some push back internally on adding this to the existing flag. I'm 
going to add `-Wthread-safety-reference-return`, can we start by not 
temporarily including it in `-Wthread-safety-reference` so that we can see how 
much work it it to fix those warnings ?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-09-29 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/lib/Analysis/ThreadSafety.cpp:1604-1606
+Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+  (!Cp).toString(), Loc);
+return;

aaronpuchert wrote:
> Something went wrong with the indentation here, also in the following `if`s. 
> Should be two spaces only.
This is weird. `clang-format` insists on putting 4 spaces there. I've reverted 
the changes manually.


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

https://reviews.llvm.org/D153132

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


[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-09-29 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 557475.
courbet marked 2 inline comments as done.
courbet added a comment.

Address comments


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

https://reviews.llvm.org/D153132

Files:
  clang/lib/Analysis/ThreadSafety.cpp

Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1055,6 +1055,19 @@
   }
 
   void runAnalysis(AnalysisDeclContext );
+
+  void warnIfMutexNotHeld(const FactSet , const NamedDecl *D,
+  const Expr *Exp, AccessKind AK, Expr *MutexExp,
+  ProtectedOperationKind POK, til::LiteralPtr *Self,
+  SourceLocation Loc);
+  void warnIfMutexHeld(const FactSet , const NamedDecl *D, const Expr *Exp,
+   Expr *MutexExp, til::LiteralPtr *Self,
+   SourceLocation Loc);
+
+  void checkAccess(const FactSet , const Expr *Exp, AccessKind AK,
+   ProtectedOperationKind POK);
+  void checkPtAccess(const FactSet , const Expr *Exp, AccessKind AK,
+ ProtectedOperationKind POK);
 };
 
 } // namespace
@@ -1534,16 +1547,15 @@
   unsigned CtxIndex;
 
   // helper functions
-  void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
-  Expr *MutexExp, ProtectedOperationKind POK,
-  til::LiteralPtr *Self, SourceLocation Loc);
-  void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
-   til::LiteralPtr *Self, SourceLocation Loc);
 
   void checkAccess(const Expr *Exp, AccessKind AK,
-   ProtectedOperationKind POK = POK_VarAccess);
+   ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkAccess(FSet, Exp, AK, POK);
+  }
   void checkPtAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK = POK_VarAccess);
+ ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkPtAccess(FSet, Exp, AK, POK);
+  }
 
   void handleCall(const Expr *Exp, const NamedDecl *D,
   til::LiteralPtr *Self = nullptr,
@@ -1571,17 +1583,14 @@
 
 /// Warn if the LSet does not contain a lock sufficient to protect access
 /// of at least the passed in AccessKind.
-void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
-  AccessKind AK, Expr *MutexExp,
-  ProtectedOperationKind POK,
-  til::LiteralPtr *Self,
-  SourceLocation Loc) {
+void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
+const FactSet , const NamedDecl *D, const Expr *Exp, AccessKind AK,
+Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,
+SourceLocation Loc) {
   LockKind LK = getLockKindFromAccessKind(AK);
-
-  CapabilityExpr Cp =
-  Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
+  CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
   if (Cp.isInvalid()) {
-warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, Cp.getKind());
+warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());
 return;
   } else if (Cp.shouldIgnore()) {
 return;
@@ -1589,68 +1598,67 @@
 
   if (Cp.negative()) {
 // Negative capabilities act like locks excluded
-const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
 if (LDat) {
-  Analyzer->Handler.handleFunExcludesLock(
-  Cp.getKind(), D->getNameAsString(), (!Cp).toString(), Loc);
+  Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+(!Cp).toString(), Loc);
   return;
 }
 
 // If this does not refer to a negative capability in the same class,
 // then stop here.
-if (!Analyzer->inCurrentScope(Cp))
+if (!inCurrentScope(Cp))
   return;
 
 // Otherwise the negative requirement must be propagated to the caller.
-LDat = FSet.findLock(Analyzer->FactMan, Cp);
+LDat = FSet.findLock(FactMan, Cp);
 if (!LDat) {
-  Analyzer->Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
+  Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
 }
 return;
   }
 
-  const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
+  const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
   bool NoError = true;
   if (!LDat) {
 // No exact match found.  Look for a partial match.
-LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
+LDat = FSet.findPartialMatch(FactMan, Cp);
 if (LDat) {
   // Warn that there's no precise match.
   std::string PartMatchStr = LDat->toString();
   StringRef   PartMatchName(PartMatchStr);
-  

[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-09-27 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153132#4651204 , @courbet wrote:

> In D153132#4647557 , @aaronpuchert 
> wrote:
>
>> In D153132#4431627 , @courbet 
>> wrote:
>>
 Is this actually required for the subsequent change? I don't see the 
 connection.
>>>
>>> In the followup change, we have to check the returns after the enter and 
>>> exit CFG block are computed. We can't analyze the returns as they are seen 
>>> because , because what matters for the returns is the locks that are live 
>>> at the end of the function, not those that are live at the point where the 
>>> `return` happens.
>>
>> Is this still the case? Or do we not need this anymore.
>
> So the change we still actually need is for `checkAccess()` to take the fact 
> set as a parameter (because we're checking two different fact sets depending 
> on whether we're processing a return statement or not). I think that 
> design-wise it's better if `checkAccess` is in the `Analyzer` rather than the 
> `BuildLockset`, because `checkAccess()` no longer needs access to any state 
> within `BuildLockset`.

The patch without those base changes for reference: 
https://github.com/llvm/llvm-project/commit/11d3339daf6f3543d73292b307057769711bce2e.

>>> From a design perspective I think it might actually make more sens for them 
>>> to be in the analyzer as `warnIfMutexNotHeld` and friends actually inspects 
>>> quite a lot of the `Analyzer` state.
>>
>> On that I agree.




Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153132

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-27 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4651198 , @courbet wrote:

> In D153131#4651074 , @aaronpuchert 
> wrote:
>
>> Looks still good to me. As I wrote on D153132 
>> , I don't think we need it anymore, but if 
>> you disagree I think I can accept it as well.
>
> Sorry, I misunderstood the last comment as an endorsement of the change. I've 
> reverted the base commit and I'll rebase this on main without the changes.

After trying a rebase I think the code is better with the base change. See my 
comments in https://reviews.llvm.org/D153132.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-09-27 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153132#4647557 , @aaronpuchert 
wrote:

> In D153132#4431627 , @courbet wrote:
>
>>> Is this actually required for the subsequent change? I don't see the 
>>> connection.
>>
>> In the followup change, we have to check the returns after the enter and 
>> exit CFG block are computed. We can't analyze the returns as they are seen 
>> because , because what matters for the returns is the locks that are live at 
>> the end of the function, not those that are live at the point where the 
>> `return` happens.
>
> Is this still the case? Or do we not need this anymore.

So the change we still actually need is for `checkAccess()` to take the fact 
set as a parameter (because we're checking two different fact sets depending on 
whether we're processing a return statement or not). I think that design-wise 
it's better if `checkAccess` is in the `Analyzer` rather than the 
`BuildLockset`, because `checkAccess()` no longer needs access to any state 
within `BuildLockset`.

>> From a design perspective I think it might actually make more sens for them 
>> to be in the analyzer as `warnIfMutexNotHeld` and friends actually inspects 
>> quite a lot of the `Analyzer` state.
>
> On that I agree.




Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153132

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-27 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4651074 , @aaronpuchert 
wrote:

> Looks still good to me. As I wrote on D153132 
> , I don't think we need it anymore, but if 
> you disagree I think I can accept it as well.

Sorry, I misunderstood the last comment as an endorsement of the change. I've 
reverted the base commit and I'll rebase this on main without the changes.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-26 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 557354.
courbet added a comment.

rebase


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -5580,6 +5580,85 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+  Foo* foo_ptr PT_GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;
+  }
+
+  Foo returns_value_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+  
+  Foo returns_value_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_not_locked() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo _ref_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo& returns_ref_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+  
+  Foo _ref_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+  
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+
+  Foo _ref2() {
+return *foo_ptr;  // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}}
+  }
+
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1983,6 +1983,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2013,6 +2019,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1008,7 +1008,7 @@
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler 
-  const CXXMethodDecl *CurrentMethod = nullptr;
+  const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
   FactManager FactMan;
   std::vector BlockInfo;
@@ -1243,10 +1243,10 @@
 
   // Members are in scope from methods of the same class.
   if (const auto *P = dyn_cast(SExp)) {
-if (!CurrentMethod)
+   

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-26 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks for the review.




Comment at: clang/lib/Analysis/ThreadSafety.cpp:2304-2305
 
+  CFGBlockInfo  = BlockInfo[CFGraph->getEntry().getBlockID()];
+  CFGBlockInfo  = BlockInfo[CFGraph->getExit().getBlockID()];
+

aaronpuchert wrote:
> You might want to do the `*` -> `&` in a separate commit.
SG, done in https://github.com/llvm/llvm-project/pull/66750


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 557015.
courbet marked an inline comment as done.
courbet added a comment.

Rebase on NFC changes


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -5580,6 +5580,85 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+  Foo* foo_ptr PT_GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;
+  }
+
+  Foo returns_value_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+  
+  Foo returns_value_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_not_locked() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo _ref_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo& returns_ref_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+  
+  Foo _ref_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+  
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+
+  Foo _ref2() {
+return *foo_ptr;  // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}}
+  }
+
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1983,6 +1983,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2013,6 +2019,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1008,7 +1008,7 @@
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler 
-  const CXXMethodDecl *CurrentMethod = nullptr;
+  const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
   FactManager FactMan;
   std::vector BlockInfo;
@@ -1243,10 +1243,10 @@
 
   // Members are in scope from methods of the same class.
   if (const 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-18 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

> I think we should use the expected exit set instead of the entry set.

Indeed, I've added  a few tests to check this. Let me know if you see any other 
tests that might be valuable.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-09-18 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 556940.
courbet marked 7 inline comments as done.
courbet added a comment.

- Check return values against the exit set rather than the entry set, add unit 
tests.
- Address other cosmetic review comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -5580,6 +5580,85 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+  Foo* foo_ptr PT_GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;
+  }
+
+  Foo returns_value_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+  
+  Foo returns_value_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_not_locked() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo _ref_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
+MutexLock lock(, true);
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo& returns_ref_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
+mu.Unlock();
+return foo;   // // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+  
+  Foo _ref_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
+mu.Lock();
+return foo;
+  }
+  
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+  
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+
+  Foo _ref2() {
+return *foo_ptr;  // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}}
+  }
+
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1983,6 +1983,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2013,6 +2019,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1008,7 +1008,7 @@
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler 
-  const CXXMethodDecl *CurrentMethod = nullptr;
+  const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
   FactManager FactMan;
   std::vector 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-30 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticGroups.td:1046
 def ThreadSafetyPrecise: DiagGroup<"thread-safety-precise">;
 def ThreadSafetyReference  : DiagGroup<"thread-safety-reference">;
+def ThreadSafetyReturn : DiagGroup<"thread-safety-return">;

aaronpuchert wrote:
> courbet wrote:
> > aaronpuchert wrote:
> > > courbet wrote:
> > > > aaronpuchert wrote:
> > > > > Why not under `-Wthread-safety-reference`, as it's 
> > > > > return-by-reference that you're warning on? This seems too small for 
> > > > > a separate flag to me.
> > > > The main reason it so that we provide a soft transition period for 
> > > > users: If we put that in `-Wthread-safety-reference`, we'll start 
> > > > breaking compile for people who use `-Werror`, while a separate flag 
> > > > allows a transition period where people opt into the new feature.
> > > Transition flags can end up resulting in more churn, assuming that we 
> > > eventually want to put this under `-Wthread-safety-reference`, because 
> > > then you have two categories of users:
> > > * those that opted in will eventually have to remove the flag again, and
> > > * those that didn't will get hard errors on updating the compiler at that 
> > > point.
> > > Of course you might argue that the latter case can be prevented by 
> > > carefully reading the release notes, but we know how often that happens.
> > > 
> > > I'd argue that if you're using `-Wthread-safety-reference`, you're 
> > > already opting into warnings on escaping references, and not warning on 
> > > `return` is a false negative.
> > > 
> > > A separate flag would make sense to me if we want to keep it, for example 
> > > because this produces a substantial amount of false positives under some 
> > > circumstances. Did you try this on a larger code base that's using the 
> > > annotations? I could try it on our code, and maybe we can get some 
> > > Googler to test it on theirs, which is also heavily using Thread Safety 
> > > Analysis. (Though I'm not sure whether they use 
> > > `-Wthread-safety-reference`.)
> > I don't have a strong opinion for where the warning should go.  We are 
> > indeed using `-Wthread-safety-reference`, though we're not enabling -Werror 
> > on these, so adding more warnings is fine for us.
> > 
> > I've run the check on a small sample of our codebase (which I don;t claim 
> > to be representative, I can do a larger analysis if needed). The warnings 
> > are more or less evenly split between missing annotations and actual bugs. 
> > I don't think any of the things I've seen qualify as false positives.
> > 
> > Among the missing annotations, most of the warnings are missing 
> > `ABSL_EXCLUSIVE_LOCKS_REQUIRED` on the function. In a small number of 
> > cases, the pattern is that a variable is lazily initialized under a lock 
> > and then returned by reference:
> > 
> > ```
> > struct LazyImmutableThing {
> >   const Thing& Get() {
> > {
> >   MutexLock lock(_);
> >   thing_->Initialize();
> > }
> > 
> > return thing_;
> >   }
> >   
> >   Mutex mutex_;
> >   Thing thing_ GUARDED_BY(mutex_);
> > };
> > ```
> > 
> > I consider this to be a missing annotation as the returned value is 
> > dynamically immutable, the proper fix would be `return 
> > TS_UNCHECKED_READ(thing_)`.
> > 
> > 
> > Most actual bugs are along the lines of:
> > 
> > ```
> > struct S {
> >   T& Get() const {
> > MutexLock lock(_);
> > return obj_;
> >   }
> > 
> >   Mutex mutex_;
> >   T obj_ GUARDED_BY(mutex_);
> > };
> > ```
> > 
> > though some are missing the guard altogether (`T& Get() const { return 
> > obj_; }`).
> > 
> > There are a few possible fixes. In rough order of occurrence:
> >  - Return by value as the copy is not too expensive and memory ordering 
> > does not matter.
> >  - Let the caller take the lock and annotate with 
> > `ABSL_EXCLUSIVE_LOCKS_REQUIRED` when the `Get` method is not called too 
> > often.
> >  - Let `Get` take a callback and process the value under the lock instead 
> > of returning it (when ordering matters).
> > 
> > In a small number of cases, the pattern is that a variable is lazily 
> > initialized under a lock and then returned by reference:
> 
> I wonder why that's safe, is the initialization guarded to happen only once? 
> Some kind of double-checked locking pattern perhaps? Otherwise it seems that 
> reads could happen in parallel to writes. If it's a checked initialization, 
> then I think the proper way to model this is:
> * The initialization acquires a lock to exclude other initializations running 
> in parallel. Reads cannot happen, because the reference has not yet escaped.
> * After initialization, we essentially acquire an implicit shared lock. This 
> is not tracked as a proper lock, but it doesn't need to: there are no more 
> writes until the end of lifetime, so nobody will acquire another exclusive 
> lock.
> One could model this by 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-28 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D153131#4456019 , @aaronpuchert 
wrote:

> Tried this on our code base, and the number of new warnings seems acceptable. 
> I'll still need to look through them in more detail, but there is one 
> suspicious warning that boils down to this:
>
>   > cat reference-bug.cpp
>   struct __attribute__((capability("mutex"))) Mutex {} mu;
>   int* p __attribute__((pt_guarded_by(mu)));
>   
>   int& f() {
> return *p;
>   }
>   > clang-16 -fsyntax-only -Wthread-safety-analysis reference-bug.cpp
>   > clang-16-D153131 -fsyntax-only -Wthread-safety-analysis reference-bug.cpp
>   reference-bug.cpp:5:11: warning: writing the value pointed to by 'p' 
> requires holding mutex 'mu' exclusively [-Wthread-safety-analysis]
> return *p;
> ^
>   1 warning generated.
>
> That we're warning here is correct, but the warning message is a bit off 
> (we're not quite writing here), and it's under `-Wthread-safety-analysis` 
> instead of `-Wthread-safety-reference`.

Right. I was relying on the fallback "imprecise" warning for this one. I added 
a `pt_garded` value just like for `pass_by_value` and added a test.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-28 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 535418.
courbet added a comment.

Add a specific message for return-by-ref of `pt_guarded_by` variable.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -5580,6 +5580,55 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+  Foo* foo_ptr PT_GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+
+  Foo _ref2() {
+return *foo_ptr;  // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}}
+  }
+
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1971,6 +1971,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2001,6 +2007,12 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
+case POK_PtReturnByRef:
+  DiagID = diag::warn_pt_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
@@ -1009,7 +1010,7 @@
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler 
-  const CXXMethodDecl *CurrentMethod;
+  const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
   FactManager FactMan;
   std::vector BlockInfo;
@@ -1244,10 +1245,10 @@
 
   // Members are in scope from methods of the same class.
   if (const auto *P = dyn_cast(SExp)) {
-if (!CurrentMethod)
+if (CurrentFunction == nullptr || !isa(CurrentFunction))
   return false;
 const ValueDecl *VD = P->clangDecl();
-return VD->getDeclContext() == CurrentMethod->getDeclContext();
+return VD->getDeclContext() == CurrentFunction->getDeclContext();
   }
 
   return false;
@@ -1538,11 +1539,12 @@
 /// output error messages related to missing locks.
 /// FIXME: In future, we may be able to not inherit from a visitor.
 class BuildLockset : public ConstStmtVisitor {
-  using VisitorBase = ConstStmtVisitor;
   friend class ThreadSafetyAnalyzer;
 
   ThreadSafetyAnalyzer *Analyzer;
   FactSet FSet;
+  // The fact set for the function (i.e., its entry block).
+  const FactSet 
   /// Maps 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-26 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 534539.
courbet added a comment.

Put the new warnings in `-Wtread-safety-reference`


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -5580,6 +5580,49 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1971,6 +1971,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2001,6 +2004,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
@@ -1009,7 +1010,7 @@
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler 
-  const CXXMethodDecl *CurrentMethod;
+  const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
   FactManager FactMan;
   std::vector BlockInfo;
@@ -1244,10 +1245,10 @@
 
   // Members are in scope from methods of the same class.
   if (const auto *P = dyn_cast(SExp)) {
-if (!CurrentMethod)
+if (CurrentFunction == nullptr || !isa(CurrentFunction))
   return false;
 const ValueDecl *VD = P->clangDecl();
-return VD->getDeclContext() == CurrentMethod->getDeclContext();
+return VD->getDeclContext() == CurrentFunction->getDeclContext();
   }
 
   return false;
@@ -1538,11 +1539,12 @@
 /// output error messages related to missing locks.
 /// FIXME: In future, we may be able to not inherit from a visitor.
 class BuildLockset : public ConstStmtVisitor {
-  using VisitorBase = ConstStmtVisitor;
   friend class ThreadSafetyAnalyzer;
 
   ThreadSafetyAnalyzer *Analyzer;
   FactSet FSet;
+  // The fact set for the function (i.e., its entry block).
+  const FactSet 
   /// Maps constructed objects to `this` placeholder prior to initialization.
   llvm::SmallDenseMap ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
@@ -1568,9 +1570,11 @@
 bool SkipFirstParam = false);
 
 public:
-  BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo )
-  : VisitorBase(), Analyzer(Anlzr), FSet(Info.EntrySet),
-LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
+  BuildLockset(ThreadSafetyAnalyzer 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-26 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticGroups.td:1046
 def ThreadSafetyPrecise: DiagGroup<"thread-safety-precise">;
 def ThreadSafetyReference  : DiagGroup<"thread-safety-reference">;
+def ThreadSafetyReturn : DiagGroup<"thread-safety-return">;

aaronpuchert wrote:
> courbet wrote:
> > aaronpuchert wrote:
> > > Why not under `-Wthread-safety-reference`, as it's return-by-reference 
> > > that you're warning on? This seems too small for a separate flag to me.
> > The main reason it so that we provide a soft transition period for users: 
> > If we put that in `-Wthread-safety-reference`, we'll start breaking compile 
> > for people who use `-Werror`, while a separate flag allows a transition 
> > period where people opt into the new feature.
> Transition flags can end up resulting in more churn, assuming that we 
> eventually want to put this under `-Wthread-safety-reference`, because then 
> you have two categories of users:
> * those that opted in will eventually have to remove the flag again, and
> * those that didn't will get hard errors on updating the compiler at that 
> point.
> Of course you might argue that the latter case can be prevented by carefully 
> reading the release notes, but we know how often that happens.
> 
> I'd argue that if you're using `-Wthread-safety-reference`, you're already 
> opting into warnings on escaping references, and not warning on `return` is a 
> false negative.
> 
> A separate flag would make sense to me if we want to keep it, for example 
> because this produces a substantial amount of false positives under some 
> circumstances. Did you try this on a larger code base that's using the 
> annotations? I could try it on our code, and maybe we can get some Googler to 
> test it on theirs, which is also heavily using Thread Safety Analysis. 
> (Though I'm not sure whether they use `-Wthread-safety-reference`.)
I don't have a strong opinion for where the warning should go.  We are indeed 
using `-Wthread-safety-reference`, though we're not enabling -Werror on these, 
so adding more warnings is fine for us.

I've run the check on a small sample of our codebase (which I don;t claim to be 
representative, I can do a larger analysis if needed). The warnings are more or 
less evenly split between missing annotations and actual bugs. I don't think 
any of the things I've seen qualify as false positives.

Among the missing annotations, most of the warnings are missing 
`ABSL_EXCLUSIVE_LOCKS_REQUIRED` on the function. In a small number of cases, 
the pattern is that a variable is lazily initialized under a lock and then 
returned by reference:

```
struct LazyImmutableThing {
  const Thing& Get() {
{
  MutexLock lock(_);
  thing_->Initialize();
}

return thing_;
  }
  
  Mutex mutex_;
  Thing thing_ GUARDED_BY(mutex_);
};
```

I consider this to be a missing annotation as the returned value is dynamically 
immutable, the proper fix would be `return TS_UNCHECKED_READ(thing_)`.


Most actual bugs are along the lines of:

```
struct S {
  T& Get() const {
MutexLock lock(_);
return obj_;
  }

  Mutex mutex_;
  T obj_ GUARDED_BY(mutex_);
};
```

though some are missing the guard altogether (`T& Get() const { return obj_; 
}`).

There are a few possible fixes. In rough order of occurrence:
 - Return by value as the copy is not too expensive and memory ordering does 
not matter.
 - Let the caller take the lock and annotate with 
`ABSL_EXCLUSIVE_LOCKS_REQUIRED` when the `Get` method is not called too often.
 - Let `Get` take a callback and process the value under the lock instead of 
returning it (when ordering matters).



Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 532559.
courbet added a comment.

format


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
 
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
@@ -5580,6 +5580,49 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1971,6 +1971,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2001,6 +2004,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
@@ -1009,7 +1010,7 

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks.




Comment at: clang/include/clang/Basic/DiagnosticGroups.td:1046
 def ThreadSafetyPrecise: DiagGroup<"thread-safety-precise">;
 def ThreadSafetyReference  : DiagGroup<"thread-safety-reference">;
+def ThreadSafetyReturn : DiagGroup<"thread-safety-return">;

aaronpuchert wrote:
> Why not under `-Wthread-safety-reference`, as it's return-by-reference that 
> you're warning on? This seems too small for a separate flag to me.
The main reason it so that we provide a soft transition period for users: If we 
put that in `-Wthread-safety-reference`, we'll start breaking compile for 
people who use `-Werror`, while a separate flag allows a transition period 
where people opt into the new feature.



Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:3805
 
+// Thread safety warnings on return
+def warn_guarded_return_by_reference : Warning<

aaronpuchert wrote:
> Or do you expect more warnings on return?
We could do pointers too, but arguably pointers and references are the same.



Comment at: clang/lib/Analysis/ThreadSafety.cpp:2159-2161
+  // If returning by reference, add the return value to the set to check on
+  // function exit.
+  if (RetVal->isLValue()) {

aaronpuchert wrote:
> Wouldn't it be more straightforward to check the actual return type? We have 
> the `FunctionDecl` and could store it in `ThreadSafetyAnalyzer` instead of 
> `CurrentMethod`.
Good point. I've also added better checking and diagnostics for `const` 
(shared) vs `mutable` (exclusive) locks, with more tests.



Comment at: clang/lib/Analysis/ThreadSafety.cpp:2162
+  if (RetVal->isLValue()) {
+Analyzer->ReturnValues.insert(RetVal);
+  }

aaronpuchert wrote:
> You're presumably collecting them because automatic destructor calls are 
> after `return` in the CFG, right?
> 
> If that's the case, can't we immediately check against the declared exit set? 
> It should be known before we walk the CFG, unless I'm missing something.
> You're presumably collecting them because automatic destructor calls are 
> after return in the CFG, right?

Exactly.

> If that's the case, can't we immediately check against the declared exit set? 
> It should be known before we walk the CFG, unless I'm missing something.

Given how the code was written I was under the impression that we only knew the 
entry set after walking the whole CFG (we're getting `ExpectedExitSet` after we 
walk the CFG). But now I see that we're actually adressing the entry blok 
beforehand. Thanks for  the suggestion, this makes the code much simpler indeed 
!





Comment at: clang/lib/Analysis/ThreadSafety.cpp:2165
+
+  VisitorBase::VisitReturnStmt(S);
+}

aaronpuchert wrote:
> Also wondering why we're doing this—no other visitor function seems to bother 
> the `VisitorBase = ConstStmtVisitor`. Are these not just empty 
> fallbacks?
The base code is hard to read because i't full of macros, but it looks like 
it't probably empty indeed - done.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

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


[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 532558.
courbet marked an inline comment as done.
courbet added a comment.

Address review comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
 
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
@@ -5580,6 +5580,49 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
+  }
+
+  Foo _ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  const Foo _constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1971,6 +1971,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2001,6 +2004,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include 

[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-06-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 532534.
courbet added a comment.

remove type alias


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153132

Files:
  clang/lib/Analysis/ThreadSafety.cpp

Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1016,6 +1016,19 @@
 
   BeforeSet *GlobalBeforeSet;
 
+  void warnIfMutexNotHeld(const FactSet , const NamedDecl *D,
+  const Expr *Exp, AccessKind AK, Expr *MutexExp,
+  ProtectedOperationKind POK, til::LiteralPtr *Self,
+  SourceLocation Loc);
+  void warnIfMutexHeld(const FactSet , const NamedDecl *D, const Expr *Exp,
+   Expr *MutexExp, til::LiteralPtr *Self,
+   SourceLocation Loc);
+
+  void checkAccess(const FactSet , const Expr *Exp, AccessKind AK,
+   ProtectedOperationKind POK);
+  void checkPtAccess(const FactSet , const Expr *Exp, AccessKind AK,
+ ProtectedOperationKind POK);
+
 public:
   ThreadSafetyAnalyzer(ThreadSafetyHandler , BeforeSet* Bset)
   : Arena(), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
@@ -1535,16 +1548,15 @@
   unsigned CtxIndex;
 
   // helper functions
-  void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
-  Expr *MutexExp, ProtectedOperationKind POK,
-  til::LiteralPtr *Self, SourceLocation Loc);
-  void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
-   til::LiteralPtr *Self, SourceLocation Loc);
 
   void checkAccess(const Expr *Exp, AccessKind AK,
-   ProtectedOperationKind POK = POK_VarAccess);
+   ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkAccess(FSet, Exp, AK, POK);
+  }
   void checkPtAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK = POK_VarAccess);
+ ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkPtAccess(FSet, Exp, AK, POK);
+  }
 
   void handleCall(const Expr *Exp, const NamedDecl *D,
   til::LiteralPtr *Self = nullptr,
@@ -1572,17 +1584,14 @@
 
 /// Warn if the LSet does not contain a lock sufficient to protect access
 /// of at least the passed in AccessKind.
-void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
-  AccessKind AK, Expr *MutexExp,
-  ProtectedOperationKind POK,
-  til::LiteralPtr *Self,
-  SourceLocation Loc) {
+void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
+const FactSet , const NamedDecl *D, const Expr *Exp, AccessKind AK,
+Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,
+SourceLocation Loc) {
   LockKind LK = getLockKindFromAccessKind(AK);
-
-  CapabilityExpr Cp =
-  Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
+  CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
   if (Cp.isInvalid()) {
-warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, Cp.getKind());
+warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());
 return;
   } else if (Cp.shouldIgnore()) {
 return;
@@ -1590,68 +1599,67 @@
 
   if (Cp.negative()) {
 // Negative capabilities act like locks excluded
-const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
 if (LDat) {
-  Analyzer->Handler.handleFunExcludesLock(
-  Cp.getKind(), D->getNameAsString(), (!Cp).toString(), Loc);
-  return;
+Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+  (!Cp).toString(), Loc);
+return;
 }
 
 // If this does not refer to a negative capability in the same class,
 // then stop here.
-if (!Analyzer->inCurrentScope(Cp))
-  return;
+if (!inCurrentScope(Cp))
+return;
 
 // Otherwise the negative requirement must be propagated to the caller.
-LDat = FSet.findLock(Analyzer->FactMan, Cp);
+LDat = FSet.findLock(FactMan, Cp);
 if (!LDat) {
-  Analyzer->Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
+Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
 }
 return;
   }
 
-  const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
+  const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
   bool NoError = true;
   if (!LDat) {
 // No exact match found.  Look for a partial match.
-LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
+LDat = FSet.findPartialMatch(FactMan, Cp);
 if (LDat) {
   // Warn that 

[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-06-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

> Is this actually required for the subsequent change? I don't see the 
> connection.

In the followup change, we have to check the returns after the enter and exit 
CFG block are computed. We can't analyze the returns as they are seen because , 
because what matters for the returns is the locks that are live at the end of 
the function, not those that are live at the point where the `return` happens.

The `BuildLockset` class only lives for the duration of the analysis of a 
single block, while the `ThreadSafetyAnalyzer` lives for the whole function. So 
return checking is done in the `ThreadSafetyAnalyzer`, so we need the 
check/warn functions to be available here.

From a design perspective I think it might actually make more sens for them to 
be in the analyzer as `warnIfMutexNotHeld` and friends actually inspects quite 
a lot of the `Analyzer` state.




Comment at: clang/lib/Analysis/ThreadSafety.cpp:1541
 class BuildLockset : public ConstStmtVisitor {
+  using VisitorBase = ConstStmtVisitor;
   friend class ThreadSafetyAnalyzer;

aaronpuchert wrote:
> Why the alias? I find this just obfuscates.
I'm using it one more time in the followup patch, but no strong opinion, 
removed.



Comment at: clang/lib/Analysis/ThreadSafety.cpp:1588
 /// of at least the passed in AccessKind.
-void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
-  AccessKind AK, Expr *MutexExp,
-  ProtectedOperationKind POK,
-  til::LiteralPtr *Self,
-  SourceLocation Loc) {
+void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
+const FactSet , const NamedDecl *D, const Expr *Exp, AccessKind AK,

aaronpuchert wrote:
> Hmm, functions of the same class naturally want to be together, but if you 
> move them, it "destroys" the Git history.
Yes, I decided to make review/history tracking esaier by not moving them, but I 
can move them if you want.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153132

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


[PATCH] D153132: [clang analysis][NFCI] Preparatory work for D153131.

2023-06-16 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: delesley.
Herald added a reviewer: NoQ.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang.

Refactoring in preparation for D153131 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153132

Files:
  clang/lib/Analysis/ThreadSafety.cpp

Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -1016,6 +1016,19 @@
 
   BeforeSet *GlobalBeforeSet;
 
+  void warnIfMutexNotHeld(const FactSet , const NamedDecl *D,
+  const Expr *Exp, AccessKind AK, Expr *MutexExp,
+  ProtectedOperationKind POK, til::LiteralPtr *Self,
+  SourceLocation Loc);
+  void warnIfMutexHeld(const FactSet , const NamedDecl *D, const Expr *Exp,
+   Expr *MutexExp, til::LiteralPtr *Self,
+   SourceLocation Loc);
+
+  void checkAccess(const FactSet , const Expr *Exp, AccessKind AK,
+   ProtectedOperationKind POK);
+  void checkPtAccess(const FactSet , const Expr *Exp, AccessKind AK,
+ ProtectedOperationKind POK);
+
 public:
   ThreadSafetyAnalyzer(ThreadSafetyHandler , BeforeSet* Bset)
   : Arena(), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
@@ -1525,6 +1538,7 @@
 /// output error messages related to missing locks.
 /// FIXME: In future, we may be able to not inherit from a visitor.
 class BuildLockset : public ConstStmtVisitor {
+  using VisitorBase = ConstStmtVisitor;
   friend class ThreadSafetyAnalyzer;
 
   ThreadSafetyAnalyzer *Analyzer;
@@ -1535,16 +1549,15 @@
   unsigned CtxIndex;
 
   // helper functions
-  void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
-  Expr *MutexExp, ProtectedOperationKind POK,
-  til::LiteralPtr *Self, SourceLocation Loc);
-  void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
-   til::LiteralPtr *Self, SourceLocation Loc);
 
   void checkAccess(const Expr *Exp, AccessKind AK,
-   ProtectedOperationKind POK = POK_VarAccess);
+   ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkAccess(FSet, Exp, AK, POK);
+  }
   void checkPtAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK = POK_VarAccess);
+ ProtectedOperationKind POK = POK_VarAccess) {
+Analyzer->checkPtAccess(FSet, Exp, AK, POK);
+  }
 
   void handleCall(const Expr *Exp, const NamedDecl *D,
   til::LiteralPtr *Self = nullptr,
@@ -1556,7 +1569,7 @@
 
 public:
   BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo )
-  : ConstStmtVisitor(), Analyzer(Anlzr), FSet(Info.EntrySet),
+  : VisitorBase(), Analyzer(Anlzr), FSet(Info.EntrySet),
 LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
 
   void VisitUnaryOperator(const UnaryOperator *UO);
@@ -1572,17 +1585,14 @@
 
 /// Warn if the LSet does not contain a lock sufficient to protect access
 /// of at least the passed in AccessKind.
-void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
-  AccessKind AK, Expr *MutexExp,
-  ProtectedOperationKind POK,
-  til::LiteralPtr *Self,
-  SourceLocation Loc) {
+void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
+const FactSet , const NamedDecl *D, const Expr *Exp, AccessKind AK,
+Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,
+SourceLocation Loc) {
   LockKind LK = getLockKindFromAccessKind(AK);
-
-  CapabilityExpr Cp =
-  Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
+  CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
   if (Cp.isInvalid()) {
-warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, Cp.getKind());
+warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());
 return;
   } else if (Cp.shouldIgnore()) {
 return;
@@ -1590,68 +1600,67 @@
 
   if (Cp.negative()) {
 // Negative capabilities act like locks excluded
-const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
 if (LDat) {
-  Analyzer->Handler.handleFunExcludesLock(
-  Cp.getKind(), D->getNameAsString(), (!Cp).toString(), Loc);
-  return;
+Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+  (!Cp).toString(), Loc);
+return;
 }
 
 // If this does not refer to a negative capability in the same class,
 // then stop here.
-if (!Analyzer->inCurrentScope(Cp))
-  

[PATCH] D153131: [clang analysis][thread-safety] Handle return-by-reference...

2023-06-16 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: delesley.
Herald added a reviewer: NoQ.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang.

...of guarded variables, when the function is not marked as requiring locks:

  class Return {
Mutex mu;
Foo foo GUARDED_BY(mu);
  
Foo _ref_locked() {
  MutexLock lock();
  return foo;  // BAD
}
  
Foo _ref_locks_required() SHARED_LOCKS_REQUIRED(mu) {
  return foo;  // OK
}
  };

This is implemented as `-Wthread-safety-return` and not part of
`-Wthread-safety` for now.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153131

Files:
  clang/include/clang/Analysis/Analyses/ThreadSafety.h
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/ThreadSafety.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Index: clang/test/SemaCXX/warn-thread-safety-analysis.cpp
===
--- clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-return -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
 
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s
 // FIXME: should also run  %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
@@ -5580,6 +5580,41 @@
   }
 };
 
+class Return {
+  Mutex mu;
+  Foo foo GUARDED_BY(mu);
+
+  Foo returns_value_locked() {
+MutexLock lock();
+return foo;
+  }
+
+  Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo returns_value_not_locked() {
+return foo;   // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+  }
+
+  Foo _ref() {
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locked() {
+MutexLock lock();
+return foo;   // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
+  }
+
+  Foo _ref_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+return foo;
+  }
+
+  Foo *returns_ptr() {
+return   // FIXME -- Do we want to warn on this ?
+  }
+};
+
 
 }  // end namespace PassByRefTest
 
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1971,6 +1971,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2001,6 +2004,9 @@
 case POK_PtPassByRef:
   DiagID = diag::warn_pt_guarded_pass_by_reference;
   break;
+case POK_ReturnByRef:
+  DiagID = diag::warn_guarded_return_by_reference;
+  break;
   }
   PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
Index: clang/lib/Analysis/ThreadSafety.cpp
===
--- clang/lib/Analysis/ThreadSafety.cpp
+++ clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include 

[PATCH] D151092: [clang-tidy]performance-no-automatic-move: fix false negative on `const T&&` ctors.

2023-05-24 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D151092#4368151 , @aaron.ballman 
wrote:

> The release note references documentation that doesn't exist, so Sphinx is 
> failing: https://lab.llvm.org/buildbot/#/builders/115/builds/46942

Sorry, fixed in rG62dc3ba8442f 



Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151092

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


[PATCH] D151092: [clang-tidy]performance-no-automatic-move: fix false negative on `const T&&` ctors.

2023-05-24 Thread Clement Courbet via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG9182c679dde7: [clang-tidy]performance-no-automatic-move: fix 
false negative on `const T`… (authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151092

Files:
  clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -7,30 +7,42 @@
   virtual ~Obj();
 };
 
-template 
-struct StatusOr {
-  StatusOr(const T &);
-  StatusOr(T &&);
-};
-
 struct NonTemplate {
   NonTemplate(const Obj &);
   NonTemplate(Obj &&);
 };
 
+template  struct TemplateCtorPair {
+  TemplateCtorPair(const T &);
+  TemplateCtorPair(T &);
+};
+
+template  struct UrefCtor {
+  template  UrefCtor(U &);
+};
+
 template 
 T Make();
 
-StatusOr PositiveStatusOrConstValue() {
+NonTemplate PositiveNonTemplate() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `NonTemplate(const Obj&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
-NonTemplate PositiveNonTemplateConstValue() {
+TemplateCtorPair PositiveTemplateCtorPair() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `TemplateCtorPair(const T&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
+}
+
+UrefCtor PositiveUrefCtor() {
+  const Obj obj = Make();
+  return obj; // selects `UrefCtor(const T&&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
 Obj PositiveCantNrvo(bool b) {
@@ -51,22 +63,18 @@
 }
 
 // FIXME: Ideally we would warn here too.
-StatusOr PositiveStatusOrLifetimeExtension() {
+UrefCtor PositiveUrefCtorLifetimeExtension() {
   const Obj  = Make();
   return obj;
 }
 
 // Negatives.
 
-StatusOr Temporary() {
-  return Make();
-}
+UrefCtor Temporary() { return Make(); }
 
-StatusOr ConstTemporary() {
-  return Make();
-}
+UrefCtor ConstTemporary() { return Make(); }
 
-StatusOr ConvertingMoveConstructor() {
+UrefCtor ConvertingMoveConstructor() {
   Obj obj = Make();
   return obj;
 }
@@ -85,21 +93,19 @@
   return obj2;
 }
 
-StatusOr Ref() {
+UrefCtor Ref() {
   Obj  = Make();
   return obj;
 }
 
-StatusOr ConstRef() {
+UrefCtor ConstRef() {
   const Obj  = Make();
   return obj;
 }
 
 const Obj global;
 
-StatusOr Global() {
-  return global;
-}
+UrefCtor Global() { return global; }
 
 struct FromConstRefOnly {
   FromConstRefOnly(const Obj &);
Index: clang-tools-extra/docs/ReleaseNotes.rst
===
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -387,6 +387,10 @@
   ` when warning would be
   emitted for a const local variable to which NRVO is applied.
 
+- Improved :doc:`performance-no-automatic-move
+  `: warn on ``const &&``
+  constructors.
+
 Removed checks
 ^^
 
Index: clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
===
--- clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -48,15 +48,23 @@
   hasParameter(0, hasType(rValueReferenceType(
   pointee(type(equalsBoundNode("SrcT")));
 
+  // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
+  // instantiation of `template  DstT::DstT(U&&)`.
+  const auto ConstRefRefCtor = cxxConstructorDecl(
+  parameterCountIs(1),
+  hasParameter(0,
+   hasType(rValueReferenceType(pointee(isConstQualified());
+
   Finder->addMatcher(
-  traverse(TK_AsIs,
-   returnStmt(hasReturnValue(
-   ignoringElidableConstructorCall(ignoringParenImpCasts(
-   cxxConstructExpr(
-   hasDeclaration(LValueRefCtor),
-   hasArgument(0, ignoringParenImpCasts(declRefExpr(
-  to(NonNrvoConstLocalVariable)
-

[PATCH] D151092: [clang-tidy]performance-no-automatic-move: fix false negative on `const T&&` ctors.

2023-05-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 524288.
courbet marked 2 inline comments as done.
courbet added a comment.

address review comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151092

Files:
  clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -7,30 +7,42 @@
   virtual ~Obj();
 };
 
-template 
-struct StatusOr {
-  StatusOr(const T &);
-  StatusOr(T &&);
-};
-
 struct NonTemplate {
   NonTemplate(const Obj &);
   NonTemplate(Obj &&);
 };
 
+template  struct TemplateCtorPair {
+  TemplateCtorPair(const T &);
+  TemplateCtorPair(T &);
+};
+
+template  struct UrefCtor {
+  template  UrefCtor(U &);
+};
+
 template 
 T Make();
 
-StatusOr PositiveStatusOrConstValue() {
+NonTemplate PositiveNonTemplate() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `NonTemplate(const Obj&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
-NonTemplate PositiveNonTemplateConstValue() {
+TemplateCtorPair PositiveTemplateCtorPair() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `TemplateCtorPair(const T&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
+}
+
+UrefCtor PositiveUrefCtor() {
+  const Obj obj = Make();
+  return obj; // selects `UrefCtor(const T&&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
 Obj PositiveCantNrvo(bool b) {
@@ -51,22 +63,18 @@
 }
 
 // FIXME: Ideally we would warn here too.
-StatusOr PositiveStatusOrLifetimeExtension() {
+UrefCtor PositiveUrefCtorLifetimeExtension() {
   const Obj  = Make();
   return obj;
 }
 
 // Negatives.
 
-StatusOr Temporary() {
-  return Make();
-}
+UrefCtor Temporary() { return Make(); }
 
-StatusOr ConstTemporary() {
-  return Make();
-}
+UrefCtor ConstTemporary() { return Make(); }
 
-StatusOr ConvertingMoveConstructor() {
+UrefCtor ConvertingMoveConstructor() {
   Obj obj = Make();
   return obj;
 }
@@ -85,21 +93,19 @@
   return obj2;
 }
 
-StatusOr Ref() {
+UrefCtor Ref() {
   Obj  = Make();
   return obj;
 }
 
-StatusOr ConstRef() {
+UrefCtor ConstRef() {
   const Obj  = Make();
   return obj;
 }
 
 const Obj global;
 
-StatusOr Global() {
-  return global;
-}
+UrefCtor Global() { return global; }
 
 struct FromConstRefOnly {
   FromConstRefOnly(const Obj &);
Index: clang-tools-extra/docs/ReleaseNotes.rst
===
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -381,6 +381,10 @@
   ` when warning would be
   emitted for a const local variable to which NRVO is applied.
 
+- Improved :doc:`performance-no-automatic-move
+  `: warn on ``const &&``
+  constructors.
+
 Removed checks
 ^^
 
Index: clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
===
--- clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -48,15 +48,23 @@
   hasParameter(0, hasType(rValueReferenceType(
   pointee(type(equalsBoundNode("SrcT")));
 
+  // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
+  // instantiation of `template  DstT::DstT(U&&)`.
+  const auto ConstRefRefCtor = cxxConstructorDecl(
+  parameterCountIs(1),
+  hasParameter(0,
+   hasType(rValueReferenceType(pointee(isConstQualified());
+
   Finder->addMatcher(
-  traverse(TK_AsIs,
-   returnStmt(hasReturnValue(
-   ignoringElidableConstructorCall(ignoringParenImpCasts(
-   cxxConstructExpr(
-   hasDeclaration(LValueRefCtor),
-   hasArgument(0, ignoringParenImpCasts(declRefExpr(
-  to(NonNrvoConstLocalVariable)
-   .bind("ctor_call")),
+  traverse(
+  TK_AsIs,
+  returnStmt(hasReturnValue(
+  

[PATCH] D151092: [clang-tidy]performance-no-automatic-move: fix false negative on `const T&&` ctors.

2023-05-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 524276.
courbet added a comment.

fix release note placemenfix release note placement


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151092

Files:
  clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -7,30 +7,42 @@
   virtual ~Obj();
 };
 
-template 
-struct StatusOr {
-  StatusOr(const T &);
-  StatusOr(T &&);
-};
-
 struct NonTemplate {
   NonTemplate(const Obj &);
   NonTemplate(Obj &&);
 };
 
+template  struct TemplateCtorPair {
+  TemplateCtorPair(const T &);
+  TemplateCtorPair(T &);
+};
+
+template  struct UrefCtor {
+  template  UrefCtor(U &);
+};
+
 template 
 T Make();
 
-StatusOr PositiveStatusOrConstValue() {
+NonTemplate PositiveNonTemplate() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `NonTemplate(const Obj&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
-NonTemplate PositiveNonTemplateConstValue() {
+TemplateCtorPair PositiveTemplateCtorPair() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `TemplateCtorPair(const T&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
+}
+
+UrefCtor PositiveUrefCtor() {
+  const Obj obj = Make();
+  return obj; // selects `UrefCtor(const T&&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
 Obj PositiveCantNrvo(bool b) {
@@ -51,22 +63,18 @@
 }
 
 // FIXME: Ideally we would warn here too.
-StatusOr PositiveStatusOrLifetimeExtension() {
+UrefCtor PositiveUrefCtorLifetimeExtension() {
   const Obj  = Make();
   return obj;
 }
 
 // Negatives.
 
-StatusOr Temporary() {
-  return Make();
-}
+UrefCtor Temporary() { return Make(); }
 
-StatusOr ConstTemporary() {
-  return Make();
-}
+UrefCtor ConstTemporary() { return Make(); }
 
-StatusOr ConvertingMoveConstructor() {
+UrefCtor ConvertingMoveConstructor() {
   Obj obj = Make();
   return obj;
 }
@@ -85,21 +93,19 @@
   return obj2;
 }
 
-StatusOr Ref() {
+UrefCtor Ref() {
   Obj  = Make();
   return obj;
 }
 
-StatusOr ConstRef() {
+UrefCtor ConstRef() {
   const Obj  = Make();
   return obj;
 }
 
 const Obj global;
 
-StatusOr Global() {
-  return global;
-}
+UrefCtor Global() { return global; }
 
 struct FromConstRefOnly {
   FromConstRefOnly(const Obj &);
@@ -109,3 +115,4 @@
   const Obj obj = Make();
   return obj;
 }
+
Index: clang-tools-extra/docs/ReleaseNotes.rst
===
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -381,6 +381,10 @@
   ` when warning would be
   emitted for a const local variable to which NRVO is applied.
 
+- Improved :doc:`performance-no-automatic-move
+  `: warn on `const &&`
+  constructors.
+
 Removed checks
 ^^
 
Index: clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
===
--- clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -48,15 +48,23 @@
   hasParameter(0, hasType(rValueReferenceType(
   pointee(type(equalsBoundNode("SrcT")));
 
+  // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
+  // instantiation of `template  DstT::DstT(U&&)`.
+  const auto ConstRefRefCtor = cxxConstructorDecl(
+  parameterCountIs(1),
+  hasParameter(0,
+   hasType(rValueReferenceType(pointee(isConstQualified());
+
   Finder->addMatcher(
-  traverse(TK_AsIs,
-   returnStmt(hasReturnValue(
-   ignoringElidableConstructorCall(ignoringParenImpCasts(
-   cxxConstructExpr(
-   hasDeclaration(LValueRefCtor),
-   hasArgument(0, ignoringParenImpCasts(declRefExpr(
-  to(NonNrvoConstLocalVariable)
-   .bind("ctor_call")),
+  traverse(
+  TK_AsIs,
+   

[PATCH] D151092: [clang-tidy]performance-no-automatic-move: fix false negative on `const T&&` ctors.

2023-05-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: gnanabit.
Herald added subscribers: PiotrZSL, carlosgalvezp, xazax.hun.
Herald added a reviewer: njames93.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

We were only handling `const T&`/`T&&` ctor pairs, and we were missing 
uref-based ctors.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D151092

Files:
  clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -7,30 +7,42 @@
   virtual ~Obj();
 };
 
-template 
-struct StatusOr {
-  StatusOr(const T &);
-  StatusOr(T &&);
-};
-
 struct NonTemplate {
   NonTemplate(const Obj &);
   NonTemplate(Obj &&);
 };
 
+template  struct TemplateCtorPair {
+  TemplateCtorPair(const T &);
+  TemplateCtorPair(T &);
+};
+
+template  struct UrefCtor {
+  template  UrefCtor(U &);
+};
+
 template 
 T Make();
 
-StatusOr PositiveStatusOrConstValue() {
+NonTemplate PositiveNonTemplate() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `NonTemplate(const Obj&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
-NonTemplate PositiveNonTemplateConstValue() {
+TemplateCtorPair PositiveTemplateCtorPair() {
   const Obj obj = Make();
-  return obj;
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+  return obj; // selects `TemplateCtorPair(const T&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
+}
+
+UrefCtor PositiveUrefCtor() {
+  const Obj obj = Make();
+  return obj; // selects `UrefCtor(const T&&)`
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: constness of 'obj' prevents
+  // automatic move [performance-no-automatic-move]
 }
 
 Obj PositiveCantNrvo(bool b) {
@@ -51,22 +63,18 @@
 }
 
 // FIXME: Ideally we would warn here too.
-StatusOr PositiveStatusOrLifetimeExtension() {
+UrefCtor PositiveUrefCtorLifetimeExtension() {
   const Obj  = Make();
   return obj;
 }
 
 // Negatives.
 
-StatusOr Temporary() {
-  return Make();
-}
+UrefCtor Temporary() { return Make(); }
 
-StatusOr ConstTemporary() {
-  return Make();
-}
+UrefCtor ConstTemporary() { return Make(); }
 
-StatusOr ConvertingMoveConstructor() {
+UrefCtor ConvertingMoveConstructor() {
   Obj obj = Make();
   return obj;
 }
@@ -85,21 +93,19 @@
   return obj2;
 }
 
-StatusOr Ref() {
+UrefCtor Ref() {
   Obj  = Make();
   return obj;
 }
 
-StatusOr ConstRef() {
+UrefCtor ConstRef() {
   const Obj  = Make();
   return obj;
 }
 
 const Obj global;
 
-StatusOr Global() {
-  return global;
-}
+UrefCtor Global() { return global; }
 
 struct FromConstRefOnly {
   FromConstRefOnly(const Obj &);
@@ -109,3 +115,4 @@
   const Obj obj = Make();
   return obj;
 }
+
Index: clang-tools-extra/docs/ReleaseNotes.rst
===
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -106,6 +106,9 @@
 - Support specifying `SystemHeaders` in the `.clang-tidy` configuration file,
   with the same functionality as the command-line option `--system-headers`.
 
+- Check :doc:`performance-no-automatic-move
+  ` now warns on `const &&` constructors.
+
 New checks
 ^^
 
Index: clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
===
--- clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -48,15 +48,23 @@
   hasParameter(0, hasType(rValueReferenceType(
   pointee(type(equalsBoundNode("SrcT")));
 
+  // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
+  // instantiation of `template  DstT::DstT(U&&)`.
+  const auto ConstRefRefCtor = cxxConstructorDecl(
+  parameterCountIs(1),
+  hasParameter(0,
+   hasType(rValueReferenceType(pointee(isConstQualified());
+
   Finder->addMatcher(
-  traverse(TK_AsIs,
-   returnStmt(hasReturnValue(
-   ignoringElidableConstructorCall(ignoringParenImpCasts(
-   cxxConstructExpr(
-   hasDeclaration(LValueRefCtor),
-  

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-07 Thread Clement Courbet via Phabricator via cfe-commits
courbet marked an inline comment as done.
courbet added inline comments.



Comment at: clang-tools-extra/docs/ReleaseNotes.rst:294
 
+- Improved :doc:`bugprone-fold-init-type
+  ` to handle iterators that do not

Eugene.Zelenko wrote:
> Please keep alphabetical order (by check name) in this section.
Done (in d880d4e7c228).


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

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


[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-07 Thread Clement Courbet via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG1012677284b8: [clang-tidy]bugprone-fold-init-type (authored 
by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int 

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-07 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks for the review.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

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


[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-07 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 511634.
courbet added a comment.

Fix release note placement and phrase it more consistenly with other notes in 
that section.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int 

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-06 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 511424.
courbet marked 2 inline comments as done.
courbet added a comment.

- Address review comments
- Add release notes.

Thanks !


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2023-04-06 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

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


[PATCH] D147419: [clang-tidy] ignore NRVO const variables in performance-no-automatic-move.

2023-04-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet accepted this revision.
courbet added a comment.
This revision is now accepted and ready to land.

Thanks for the patch.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D147419

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


[PATCH] D142459: [clang] Deprecate uses of GlobalObject::getAlignment

2023-01-24 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/include/clang/AST/CharUnits.h:73
 
+  /// fromQuantity - Construct a CharUnits quantity from an llvm::Align
+  /// quantity.

`Maybe`*



Comment at: clang/lib/CodeGen/CGObjCMac.cpp:1991
+return ConstantAddress(C, C->getValueType(),
+   CharUnits::fromQuantity(C->getAlign()));
 

I'm wondering whetehr we could just do away with this conversion and store a 
`MaybeAlign` in `GlobalValue` instead. 

The only uses of `Address::getAlign()` is to creata another `Address` or do 
`Address::getAlign()->getAsAlign()`


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D142459

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


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-23 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 491348.
courbet added a comment.

add matcher to registry and add release note


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

Files:
  clang/docs/LibASTMatchersReference.html
  clang/docs/ReleaseNotes.rst
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -391,6 +391,7 @@
   REGISTER_MATCHER(isDefinition);
   REGISTER_MATCHER(isDelegatingConstructor);
   REGISTER_MATCHER(isDeleted);
+  REGISTER_MATCHER(isDirectInit);
   REGISTER_MATCHER(isEnum);
   REGISTER_MATCHER(isExceptionVariable);
   REGISTER_MATCHER(isExpandedFromMacro);
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer that is a direct-initializer.
+///
+/// Example matches i2 and i2, but not i1 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -156,7 +156,8 @@
 AST Matchers
 
 
-- ...
+- `isDirectInit` matches variables that are direct-initialized (`int i2(3);`,
+  but not `int i1 = 3;`).
 
 clang-format
 
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -5446,6 +5446,20 @@
 
 
 
+Matcherhttps://clang.llvm.org/doxygen/classclang_1_1VarDecl.html;>VarDeclisDirectInit
+Matches a variable initializer that is a direct-initializer.
+
+Example matches i2 and i2, but not i1 or i4
+(matcher = varDecl(isStaticLocal()))
+void f() {
+  int i1 = 3;
+  int i2(3);
+  int i3{3};
+  int i4;
+}
+
+
+
 Matcherhttps://clang.llvm.org/doxygen/classclang_1_1VarDecl.html;>VarDeclisExceptionVariable
 Matches a variable declaration that is an exception variable from
 a C++ catch block, or an Objective-C statement.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-23 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D141925#4073385 , @aaron.ballman 
wrote:

> You also need to add the new matcher to Registry.cpp so it's usable from the 
> dynamic matchers, and you should add a release note for the new matcher.

Thanks, will do.

> (We don't usually add new matchers until there's a need for them given how 
> expensive AST matchers are on compile times.)

Ah, that's too bad.

> Are you planning to use this matcher in-tree for something?

I was going to use it out-of-tree but figured it's a nice thing to have as many 
matchers as possible available for common use. There's also always the use case 
of people using clang-query (typically through godbolt).

I can live with the matcher being out-of-tree though, so you decide :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

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


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-23 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.
Herald added a subscriber: ChuanqiXu.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

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


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-17 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 489796.
courbet added a comment.

Regenerate doc


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer that is a direct-initializer.
+///
+/// Example matches i2 and i2, but not i1 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -5446,6 +5446,20 @@
 
 
 
+Matcherhttps://clang.llvm.org/doxygen/classclang_1_1VarDecl.html;>VarDeclisDirectInit
+Matches a variable 
initializer that is a direct-initializer.
+
+Example matches i2 and i2, but not i1 or i4
+(matcher = varDecl(isStaticLocal()))
+void f() {
+  int i1 = 3;
+  int i2(3);
+  int i3{3};
+  int i4;
+}
+
+
+
 Matcherhttps://clang.llvm.org/doxygen/classclang_1_1VarDecl.html;>VarDeclisExceptionVariable
 Matches a 
variable declaration that is an exception variable from
 a C++ catch block, or an Objective-C statement.


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer that is a direct-initializer.
+///
+/// Example matches i2 and i2, but not i1 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -5446,6 +5446,20 @@
 
 
 
+Matcherhttps://clang.llvm.org/doxygen/classclang_1_1VarDecl.html;>VarDeclisDirectInit
+Matches a variable initializer that is a direct-initializer.
+
+Example matches i2 and i2, but not i1 or i4
+(matcher = varDecl(isStaticLocal()))
+void f() {
+  int i1 = 3;
+  int i2(3);
+  int i3{3};
+  int i4;
+}
+
+
+
 

[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-17 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 489794.
courbet added a comment.

fix doc


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

Files:
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer that is a direct-initializer.
+///
+/// Example matches i2 and i2, but not i1 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer that is a direct-initializer.
+///
+/// Example matches i2 and i2, but not i1 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-17 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 489790.
courbet added a comment.

fix ddoc


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141925

Files:
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer is a direct-initializer.
+///
+/// Example matches i3 and a2, but not i1, or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer is a direct-initializer.
+///
+/// Example matches i3 and a2, but not i1, or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D141925: [ASTMatchers] Add `isDirectInit` matcher.

2023-01-17 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: hokein, alexfh.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang.

To match variables that are direct-initialized.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D141925

Files:
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer is a direct-initializer.
+///
+/// Example matches i1, but not i2, i3 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))


Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,20 @@
   EXPECT_TRUE(notMatches("int X;", M));
 }
 
+TEST_P(ASTMatchersTest, IsDirectInit) {
+  auto M = varDecl(isDirectInit());
+  EXPECT_TRUE(notMatches("int i1 = 3;", M));
+  if (!GetParam().isCXX()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i2(3);", M));
+  if (!GetParam().isCXX11OrLater()) {
+return;
+  }
+  EXPECT_TRUE(matches("int i3{3};", M));
+  EXPECT_TRUE(notMatches("int i4;", M));
+}
+
 TEST_P(ASTMatchersTest, StorageDuration) {
   StringRef T =
   "void f() { int x; static int y; } int a;static int b;extern int c;";
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4114,6 +4114,22 @@
   InnerMatcher.matches(*Initializer, Finder, Builder));
 }
 
+/// \brief Matches a variable initializer is a direct-initializer.
+///
+/// Example matches i1, but not i2, i3 or i4
+/// (matcher = varDecl(isStaticLocal()))
+/// \code
+/// void f() {
+///   int i1 = 3;
+///   int i2(3);
+///   int i3{3};
+///   int i4;
+/// }
+/// \endcode
+AST_MATCHER(VarDecl, isDirectInit) {
+  return Node.isDirectInit();
+}
+
 /// \brief Matches a static variable with local scope.
 ///
 /// Example matches y (matcher = varDecl(isStaticLocal()))
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2022-11-28 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

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


[PATCH] D97854: [RFC][nsan] A Floating-point numerical sanitizer.

2022-11-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a subscriber: titeup.
courbet added a comment.

In D97854#3944804 , @olologin wrote:

> @courbet 
> Hi, sorry for pinging you.
> Is this review stalled? Is there a reason for this? Judging by paper this 
> sanitizer looks promising. I was recently planning to try out NSAN on huge 
> codebase but I only found this review, so I wonder if I should try to build 
> clang with your patch myself and try it.
>
> Maybe I could help with something if you don't have time for completing this 
> review, but I doubt my knowledge of LLVM codebase is good enough for serious 
> tasks :)

The interest for this in the LLVM community did not seem overwhelming, so I did 
not pursue this.

There was some work out-of-tree by the interflop 
 (in particular, @titeup), e.g 
https://github.com/Thukisdo/NSan-interflop-runtime, I'm not sure if they are 
still working on it.

If you want to try to get this submitted, feel free to take over !


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D97854

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


[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2022-11-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 474548.
courbet added a comment.

remove extra training newline


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive10() {
+  AutoByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
-  // 

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2022-11-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 474520.
courbet added a comment.

fix typo


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
   return std::accumulate(it, it, 0);
-  // FIXME: this one should trigger too.
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive10() {
+  AutoByRefTemplateIterator it;
+  return 

[PATCH] D137782: [clang-tidy]bugprone-fold-init-type

2022-11-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun.
Herald added a reviewer: njames93.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

Handle iterators that do not define a `value_type` type alias.

Get the value type from the return type of `Iter::operator*` instead.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137782

Files:
  clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -1,39 +1,67 @@
-// RUN: %check_clang_tidy %s bugprone-fold-init-type %t
+// RUN: %check_clang_tidy %s bugprone-fold-init-type -std=c++17 %t
 
 namespace std {
 template 
-T accumulate(InputIt first, InputIt last, T init);
+T accumulate(InputIt first, InputIt last, T init) {
+  // When `InputIt::operator*` returns a deduced `auto` type that refers to a
+  // dependent type, the return type is deduced only if `InputIt::operator*`
+  // is instantiated. In practice this happens somewhere in the implementation
+  // of `accumulate`. For tests, do it here.
+  (void)*first;
+}
 
 template 
-T reduce(InputIt first, InputIt last, T init);
+T reduce(InputIt first, InputIt last, T init) { (void)*first; }
 template 
 T reduce(ExecutionPolicy &,
- InputIt first, InputIt last, T init);
+ InputIt first, InputIt last, T init) { (void)*first; }
 
 struct parallel_execution_policy {};
 constexpr parallel_execution_policy par{};
 
 template 
 T inner_product(InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 template 
 T inner_product(ExecutionPolicy &, InputIt1 first1, InputIt1 last1,
-InputIt2 first2, T value);
+InputIt2 first2, T value) { (void)*first1; (void)*first2; }
 
 } // namespace std
 
 struct FloatIterator {
-  typedef float value_type;
+  const float *() const;
 };
+
+struct DerivedFloatIterator : public FloatIterator {
+};
+
+template  struct ByValueTemplateIterator {
+  ValueType operator*() const;
+};
+
+template  struct ByRefTemplateIterator {
+  ValueType *();
+};
+
+template  struct ByRefTemplateIteratorWithAlias {
+  using reference = const ValueType&;
+  reference operator*();
+};
+
+template  struct AutoByValueTemplateIterator {
+  auto operator*() const { return ValueType{}; }
+};
+
+template  struct AutoByRefTemplateIterator {
+  decltype(auto) operator*() const { return value_; }
+  ValueType value_;
+};
+
 template 
-struct TypedefTemplateIterator { typedef ValueType value_type; };
-template 
-struct UsingTemplateIterator { using value_type = ValueType; };
-template 
-struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
-template 
-struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; };
+struct InheritingByConstRefTemplateIterator
+: public ByRefTemplateIterator {};
+
 using TypedeffedIterator = FloatIterator;
 
 // Positives.
@@ -51,39 +79,57 @@
 }
 
 int accumulatePositive3() {
+  DerivedFloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive4() {
   double a[1] = {0.0};
   return std::accumulate(a, a + 1, 0.0f);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
 }
 
-int accumulatePositive4() {
-  TypedefTemplateIterator it;
+int accumulatePositive5() {
+  ByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive5() {
-  UsingTemplateIterator it;
+int accumulatePositive6() {
+  ByRefTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive6() {
-  DependentTypedefTemplateIterator> it;
+int accumulatePositive7() {
+  AutoByValueTemplateIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
 }
 
-int accumulatePositive7() {
+int accumulatePositive8() {
   TypedeffedIterator it;
   return std::accumulate(it, it, 0);
   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
 }
 
-int accumulatePositive8() {
-  DependentUsingTemplateIterator it;
+int accumulatePositive9() {
+  InheritingByConstRefTemplateIterator it;
   

[PATCH] D126903: [clang] Add support for __builtin_memset_inline

2022-10-26 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

I think there's a bug in the code though:

  // If this is memset, we just need to see if the offset is valid in the size
// of the memset..
if (MI->getIntrinsicID() == Intrinsic::memset)

should really be:

  // If this is memset, we just need to see if the offset is valid in the size
// of the memset..
if (isa(MI))




Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126903

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


[PATCH] D121365: [CFG] Fix crash on CFG building when deriving from a template.

2022-08-16 Thread Clement Courbet via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG672311bd77c5: [CFG] Fix crash on CFG building when deriving 
from a template. (authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D121365

Files:
  clang/lib/Analysis/CFG.cpp
  clang/unittests/Analysis/CFGBuildResult.h
  clang/unittests/Analysis/CFGTest.cpp


Index: clang/unittests/Analysis/CFGTest.cpp
===
--- clang/unittests/Analysis/CFGTest.cpp
+++ clang/unittests/Analysis/CFGTest.cpp
@@ -70,6 +70,27 @@
   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
 }
 
+// Constructing a CFG with a dependent base should not crash.
+TEST(CFG, DependantBaseAddImplicitDtors) {
+  const char *Code = R"(
+template 
+struct Base {
+  virtual ~Base() {}
+};
+
+template 
+struct Derived : public Base {
+  virtual ~Derived() {}
+};
+  )";
+  CFG::BuildOptions Options;
+  Options.AddImplicitDtors = true;
+  Options.setAllAlwaysAdd();
+  EXPECT_EQ(BuildResult::BuiltCFG,
+BuildCFG(Code, Options, ast_matchers::hasName("~Derived"))
+.getStatus());
+}
+
 TEST(CFG, IsLinear) {
   auto expectLinear = [](bool IsLinear, const char *Code) {
 BuildResult B = BuildCFG(Code);
Index: clang/unittests/Analysis/CFGBuildResult.h
===
--- clang/unittests/Analysis/CFGBuildResult.h
+++ clang/unittests/Analysis/CFGBuildResult.h
@@ -56,13 +56,15 @@
 TheBuildResult = BuildResult::SawFunctionBody;
 Options.AddImplicitDtors = true;
 if (std::unique_ptr Cfg =
-CFG::buildCFG(nullptr, Body, Result.Context, Options))
+CFG::buildCFG(Func, Body, Result.Context, Options))
   TheBuildResult = {BuildResult::BuiltCFG, Func, std::move(Cfg),
 std::move(AST)};
   }
 };
 
-inline BuildResult BuildCFG(const char *Code, CFG::BuildOptions Options = {}) {
+template 
+BuildResult BuildCFG(const char *Code, CFG::BuildOptions Options = {},
+ FuncMatcherT FuncMatcher = ast_matchers::anything()) {
   std::vector Args = {"-std=c++11",
"-fno-delayed-template-parsing"};
   std::unique_ptr AST = tooling::buildASTFromCodeWithArgs(Code, Args);
@@ -72,7 +74,8 @@
   CFGCallback Callback(std::move(AST));
   Callback.Options = Options;
   ast_matchers::MatchFinder Finder;
-  Finder.addMatcher(ast_matchers::functionDecl().bind("func"), );
+  Finder.addMatcher(ast_matchers::functionDecl(FuncMatcher).bind("func"),
+);
 
   Finder.matchAST(Callback.AST->getASTContext());
   return std::move(Callback.TheBuildResult);
Index: clang/lib/Analysis/CFG.cpp
===
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -1891,7 +1891,7 @@
 // (which is different from the current class) is responsible for
 // destroying them.
 const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl();
-if (!CD->hasTrivialDestructor()) {
+if (CD && !CD->hasTrivialDestructor()) {
   autoCreateBlock();
   appendBaseDtor(Block, );
 }
@@ -1901,7 +1901,7 @@
   for (const auto  : RD->bases()) {
 if (!BI.isVirtual()) {
   const CXXRecordDecl *CD = BI.getType()->getAsCXXRecordDecl();
-  if (!CD->hasTrivialDestructor()) {
+  if (CD && !CD->hasTrivialDestructor()) {
 autoCreateBlock();
 appendBaseDtor(Block, );
   }


Index: clang/unittests/Analysis/CFGTest.cpp
===
--- clang/unittests/Analysis/CFGTest.cpp
+++ clang/unittests/Analysis/CFGTest.cpp
@@ -70,6 +70,27 @@
   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
 }
 
+// Constructing a CFG with a dependent base should not crash.
+TEST(CFG, DependantBaseAddImplicitDtors) {
+  const char *Code = R"(
+template 
+struct Base {
+  virtual ~Base() {}
+};
+
+template 
+struct Derived : public Base {
+  virtual ~Derived() {}
+};
+  )";
+  CFG::BuildOptions Options;
+  Options.AddImplicitDtors = true;
+  Options.setAllAlwaysAdd();
+  EXPECT_EQ(BuildResult::BuiltCFG,
+BuildCFG(Code, Options, ast_matchers::hasName("~Derived"))
+.getStatus());
+}
+
 TEST(CFG, IsLinear) {
   auto expectLinear = [](bool IsLinear, const char *Code) {
 BuildResult B = BuildCFG(Code);
Index: clang/unittests/Analysis/CFGBuildResult.h
===
--- clang/unittests/Analysis/CFGBuildResult.h
+++ clang/unittests/Analysis/CFGBuildResult.h
@@ -56,13 +56,15 @@
 TheBuildResult = BuildResult::SawFunctionBody;
 Options.AddImplicitDtors = true;
 if (std::unique_ptr Cfg =
-CFG::buildCFG(nullptr, Body, Result.Context, Options))
+ 

[PATCH] D121365: [CFG] Fix crash on CFG building when deriving from a template.

2022-08-16 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks !

In D121365#3720171 , @NoQ wrote:

> Hi, interesting, the code looks great but at a glance I don't see why would 
> you even want a CFG for an uninstantiated template. Every time you want to 
> analyze the actual runtime behavior of a program, you'll have a fully 
> instantiated template AST to build the CFG from.

I'm hitting this for an analysis that is eagerly analyzing templates before 
instantiation, and does not require the base classes. It's always better to not 
crash anyway :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D121365

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


[PATCH] D121365: [CFG] Fix crash on CFG building when deriving from a template.

2022-08-12 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D121365

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


[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-08-11 Thread Clement Courbet via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG156c0754bc2e: [clang][transformer] Finish plumbing `Note` 
all the way to the output. (authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

Files:
  clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/include/clang/Tooling/Transformer/RewriteRule.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp

Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -59,6 +59,12 @@
 return Replacement.takeError();
   T.Replacement = std::move(*Replacement);
 }
+if (E.Note) {
+  auto Note = E.Note->eval(Result);
+  if (!Note)
+return Note.takeError();
+  T.Note = std::move(*Note);
+}
 if (E.Metadata) {
   auto Metadata = E.Metadata(Result);
   if (!Metadata)
@@ -125,6 +131,13 @@
   return E;
 }
 
+ASTEdit transformer::note(RangeSelector Anchor, TextGenerator Note) {
+  ASTEdit E;
+  E.TargetRange = transformer::before(Anchor);
+  E.Note = std::move(Note);
+  return E;
+}
+
 namespace {
 /// A \c TextGenerator that always returns a fixed string.
 class SimpleTextGenerator : public MatchComputation {
Index: clang/include/clang/Tooling/Transformer/RewriteRule.h
===
--- clang/include/clang/Tooling/Transformer/RewriteRule.h
+++ clang/include/clang/Tooling/Transformer/RewriteRule.h
@@ -46,6 +46,7 @@
   EditKind Kind = EditKind::Range;
   CharSourceRange Range;
   std::string Replacement;
+  std::string Note;
   llvm::Any Metadata;
 };
 
@@ -138,6 +139,10 @@
 /// diagnostic `Explanation` with the rule.
 EditGenerator noopEdit(RangeSelector Anchor);
 
+/// Generates a single, no-op edit with the associated note anchored at the
+/// start location of the specified range.
+ASTEdit note(RangeSelector Anchor, TextGenerator Note);
+
 /// Version of `ifBound` specialized to `ASTEdit`.
 inline EditGenerator ifBound(std::string ID, ASTEdit TrueEdit,
  ASTEdit FalseEdit) {
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -28,6 +28,7 @@
 using transformer::makeRule;
 using transformer::node;
 using transformer::noopEdit;
+using transformer::note;
 using transformer::RewriteRuleWith;
 using transformer::RootID;
 using transformer::statement;
@@ -85,6 +86,7 @@
   EXPECT_EQ(Errors.size(), 1U);
   EXPECT_EQ(Errors[0].Message.Message, "message");
   EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+  EXPECT_THAT(Errors[0].Notes, testing::IsEmpty());
 
   // The diagnostic is anchored to the match, "return 5".
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
@@ -116,6 +118,27 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+TEST(TransformerClangTidyCheckTest, NotesCorrectlyGenerated) {
+  class DiagAndNoteCheck : public TransformerClangTidyCheck {
+  public:
+DiagAndNoteCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(),
+   note(node(RootID), cat("some note")),
+   cat("message")),
+  Name, Context) {}
+  };
+  std::string Input = "int h() { return 5; }";
+  std::vector Errors;
+  EXPECT_EQ(Input, test::runCheckOnCode(Input, ));
+  EXPECT_EQ(Errors.size(), 1U);
+  EXPECT_EQ(Errors[0].Notes.size(), 1U);
+  EXPECT_EQ(Errors[0].Notes[0].Message, "some note");
+
+  // The note is anchored to the match, "return 5".
+  EXPECT_EQ(Errors[0].Notes[0].FileOffset, 10U);
+}
+
 TEST(TransformerClangTidyCheckTest, DiagnosticMessageEscaped) {
   class GiveDiagWithPercentSymbol : public TransformerClangTidyCheck {
   public:
Index: clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
===
--- clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
+++ clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
@@ -7,6 +7,7 @@
 //===--===//
 
 #include "TransformerClangTidyCheck.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/STLExtras.h"
 
@@ -126,18 +127,28 @@
   }
 
   // Associate the diagnostic with the location of the first change.
-  DiagnosticBuilder Diag =
-  

[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-08-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks for the comments.




Comment at: clang/include/clang/Tooling/Transformer/RewriteRule.h:250-251
 
+// Adds a note to the given edit or edits. If there are several edits, the note
+// is added to each one of them.
+// \code

ymandel wrote:
> li.zhe.hua wrote:
> > Are we sure that this is what someone would want? Perhaps I am not creative 
> > enough, but this seems like it could explode a large number of notes that 
> > coincide to edits, which seems like an odd thing to want.
> Agreed. I'm more inclined to only include the ASTEdit version.  I see the 
> value of the other one, but seems easy to get wrong. Alternatively, implement 
> it to either only add to the first edit or append/prepend a separate edit 
> with the note.
> 
> Either way, I think the primary use should be with a standalone note 
> generator like `ASTEdit note(TextGenerator Note)`. The idea is that the new 
> combinator allows adding notes to a rule and we happen to store notes in 
> ASTEdits.  Then, `withNote` is really the uncommon case where you want to 
> supplement an existing edit or set of edits with a note. WDYT?

>  I'm more inclined to only include the ASTEdit version. I see the value of 
> the other one, but seems easy to get wrong. Alternatively, implement it to 
> either only add to the first edit or append/prepend a separate edit with the 
> note.

The `EditGenerator` version was so that we can always add a note. Some 
generators return an `EditGenerator` instead of an `ASTEdit`. For example, 
`noopEdit` needs access to the match result to generate an empty range . The 
second version is required to work with these:

```
withNote(changeTo(anchor1, ...));   // OK, changeTo returns an `ASTEdit`.
withNote(noopEdit(...));   // not OK, noopEdit returns an `EditGenerator`, we 
need the second version.
```

> Either way, I think the primary use should be with a standalone note 
> generator like ASTEdit note(TextGenerator Note). The idea is that the new 
> combinator allows adding notes to a rule and we happen to store notes in 
> ASTEdits. Then, withNote is really the uncommon case where you want to 
> supplement an existing edit or set of edits with a note. WDYT?

I think you're right, we don't need the notes to be attached to a particular 
edit. I expect most notes to be standalone: that's actually what I need in my 
case, and it's also actually what the `diag` API presents to the user.

```
diag(loc1, "some warning") << FixItHint::CreateReplacement(...);
diag(loc2, "some note", clang::DiagnosticIDs::Note)
```

becomes, in transformerland:

```
makeRule(matcher, flatten(changeTo(...), note(anchor2, cat("some note"))), 
cat("some warning"))
```

So I think we only really need an ` ASTEdit note()` generator. I went with that 
solution.






Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

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


[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-08-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 451425.
courbet added a comment.

Replace both versions of `withNote()` with a single `ASTEdit note()` generator.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

Files:
  clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/include/clang/Tooling/Transformer/RewriteRule.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp

Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -59,6 +59,12 @@
 return Replacement.takeError();
   T.Replacement = std::move(*Replacement);
 }
+if (E.Note) {
+  auto Note = E.Note->eval(Result);
+  if (!Note)
+return Note.takeError();
+  T.Note = std::move(*Note);
+}
 if (E.Metadata) {
   auto Metadata = E.Metadata(Result);
   if (!Metadata)
@@ -125,6 +131,13 @@
   return E;
 }
 
+ASTEdit transformer::note(RangeSelector Anchor, TextGenerator Note) {
+  ASTEdit E;
+  E.TargetRange = transformer::before(Anchor);
+  E.Note = std::move(Note);
+  return E;
+}
+
 namespace {
 /// A \c TextGenerator that always returns a fixed string.
 class SimpleTextGenerator : public MatchComputation {
Index: clang/include/clang/Tooling/Transformer/RewriteRule.h
===
--- clang/include/clang/Tooling/Transformer/RewriteRule.h
+++ clang/include/clang/Tooling/Transformer/RewriteRule.h
@@ -46,6 +46,7 @@
   EditKind Kind = EditKind::Range;
   CharSourceRange Range;
   std::string Replacement;
+  std::string Note;
   llvm::Any Metadata;
 };
 
@@ -138,6 +139,10 @@
 /// diagnostic `Explanation` with the rule.
 EditGenerator noopEdit(RangeSelector Anchor);
 
+/// Generates a single, no-op edit with the associated note anchored at the
+/// start location of the specified range.
+ASTEdit note(RangeSelector Anchor, TextGenerator Note);
+
 /// Version of `ifBound` specialized to `ASTEdit`.
 inline EditGenerator ifBound(std::string ID, ASTEdit TrueEdit,
  ASTEdit FalseEdit) {
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -28,6 +28,7 @@
 using transformer::makeRule;
 using transformer::node;
 using transformer::noopEdit;
+using transformer::note;
 using transformer::RewriteRuleWith;
 using transformer::RootID;
 using transformer::statement;
@@ -85,6 +86,7 @@
   EXPECT_EQ(Errors.size(), 1U);
   EXPECT_EQ(Errors[0].Message.Message, "message");
   EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+  EXPECT_THAT(Errors[0].Notes, testing::IsEmpty());
 
   // The diagnostic is anchored to the match, "return 5".
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
@@ -116,6 +118,27 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+TEST(TransformerClangTidyCheckTest, NotesCorrectlyGenerated) {
+  class DiagAndNoteCheck : public TransformerClangTidyCheck {
+  public:
+DiagAndNoteCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(),
+   note(node(RootID), cat("some note")),
+   cat("message")),
+  Name, Context) {}
+  };
+  std::string Input = "int h() { return 5; }";
+  std::vector Errors;
+  EXPECT_EQ(Input, test::runCheckOnCode(Input, ));
+  EXPECT_EQ(Errors.size(), 1U);
+  EXPECT_EQ(Errors[0].Notes.size(), 1U);
+  EXPECT_EQ(Errors[0].Notes[0].Message, "some note");
+
+  // The note is anchored to the match, "return 5".
+  EXPECT_EQ(Errors[0].Notes[0].FileOffset, 10U);
+}
+
 TEST(TransformerClangTidyCheckTest, DiagnosticMessageEscaped) {
   class GiveDiagWithPercentSymbol : public TransformerClangTidyCheck {
   public:
Index: clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
===
--- clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
+++ clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
@@ -7,6 +7,7 @@
 //===--===//
 
 #include "TransformerClangTidyCheck.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/STLExtras.h"
 
@@ -126,18 +127,28 @@
   }
 
   // Associate the diagnostic with the location of the first change.
-  DiagnosticBuilder Diag =
-  diag((*Edits)[0].Range.getBegin(), escapeForDiagnostic(*Explanation));
-  for (const auto  : *Edits)
-switch (T.Kind) {
-case 

[PATCH] D128887: [clang][transformer] Fix crash on replacement-less ASTEdit.

2022-08-10 Thread Clement Courbet via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5331e1229aa6: [clang][transformer] Fix crash on 
replacement-less ASTEdit. (authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128887

Files:
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/lib/Tooling/Transformer/RewriteRule.cpp


Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,21 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Metadata) {
+  auto Metadata = E.Metadata(Result);
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -90,6 +90,32 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+transformer::ASTEdit noReplacementEdit(transformer::RangeSelector Target) {
+  transformer::ASTEdit E;
+  E.TargetRange = std::move(Target);
+  return E;
+}
+
+TEST(TransformerClangTidyCheckTest, EmptyReplacement) {
+  class DiagOnlyCheck : public TransformerClangTidyCheck {
+  public:
+DiagOnlyCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(), edit(noReplacementEdit(node(RootID))),
+   cat("message")),
+  Name, Context) {}
+  };
+  std::string Input = "int h() { return 5; }";
+  std::vector Errors;
+  EXPECT_EQ("int h() { }", test::runCheckOnCode(Input, 
));
+  EXPECT_EQ(Errors.size(), 1U);
+  EXPECT_EQ(Errors[0].Message.Message, "message");
+  EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+
+  // The diagnostic is anchored to the match, "return 5".
+  EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
+}
+
 TEST(TransformerClangTidyCheckTest, DiagnosticMessageEscaped) {
   class GiveDiagWithPercentSymbol : public TransformerClangTidyCheck {
   public:


Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,21 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Metadata) {
+  auto Metadata = E.Metadata(Result);
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -90,6 +90,32 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+transformer::ASTEdit noReplacementEdit(transformer::RangeSelector Target) {
+  transformer::ASTEdit E;
+  E.TargetRange = std::move(Target);
+  return E;
+}
+
+TEST(TransformerClangTidyCheckTest, EmptyReplacement) {
+  class DiagOnlyCheck : public TransformerClangTidyCheck {
+  public:
+

[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-07-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D128807#3625676 , @ymandel wrote:

>> For my particular case, I just need multiple notes per rule. I don't need 
>> them to be associated to a particular edit (and in that very particular 
>> case, I don't even need a source location).
>
> I'm not sure I understand: you need this additional note, but it doesn't need 
> to be tied to a source location, so, why can't you concatenate it to the main 
> note?

For my specific check the notes carry structure beyond what's typically in a 
warning, so I need the information to be separate.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

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


[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-07-01 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D128807#3622779 , @ymandel wrote:

> In D128807#3622727 , @li.zhe.hua 
> wrote:
>
>> - A note being a part of an edit seems weird at best. An `ASTEdit` and 
>> `Edit` are fragments of a greater, logical change. That a note should 
>> semantically be associated with the insertion of an open paren "`(`" but not 
>> the close paren "`)`" seems odd to me.
>> - The note takes the location of the edit it is attached to. Perhaps that 
>> could be of some convenience when those coincide, but I don't believe that 
>> should necessarily be the case. I'm imagining notes could be used to point 
>> out //other// parts of the source that are interesting.
>
> Eric, these are great points. Originally, the idea for note (IIRC) came from 
> the observation that sometimes a single match will generate edits in multiple 
> locations, each which deserve a different diagnostic note (perhaps the same 
> text, but appearing at the respective location). The intent was *not* to 
> allow noting the insertion of paren, for example.  So, I think it was a 
> mistake. Yes, sometimes an ASTEdit is a coherent change, but sometimes (as 
> the paren example demonstrates) its just a fragment.
>
> So, from my original intent, I think that we'd just want to support multiple 
> notes per rule, with each keyed on a source range.
>
> Clement -- what was your intended application? That may help clarify.

Thank for the comments.

For my particular case, I just need multiple notes per rule. I don't need them 
to be associated to a particular edit (and in that very particular case, I 
don't even need a source location).
Right now I have a check that is implemented as a transformer check. I want to 
emit an extra note. However, because `transformer` do not support notes I will 
have to rewrite the check to a traditional non-`transformer` one, which is a 
large change and could be a one-liner if `transformer` supported notes.

In terms of the overall design requirements, non-`transformer` checks can have 
multiple notes per rule, associated to a source location (and multiple checks 
are using the note location to point to somewhere else in the code, so that is 
a required feature if we want to be as powerful as the original clang-tidies, 
which I think we do). Notes cannot be associated to a particular `FixitHint`, 
and I'm not sure whether that's useful.

I  went with the design in this patch (notes associated to edits) because:

- it looked like associating a note with an `ASTEdit` was the original intent, 
given that `ASTEdit` already had a `Note` field.
- we can plumb notes inside `Metadata`, but `Metadata` is already used for the 
warning, so that looks a bit more involved.

No strong opinion on that front though :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

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


[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-06-30 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/lib/Tooling/Transformer/RewriteRule.cpp:56
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);

ymandel wrote:
> Can this ever be null? I assume the intent was "no" (in which case I'd 
> recommend an assert instead), but I'm open to the idea that it's valid, just 
> want to understand better.
Given that we provide an `EditGenerator edit(ASTEdit)`, we can't ever be sure 
that the user won't give us an empty replacement.

I've split this off to a separate patch here with a test to show the issue: 
https://reviews.llvm.org/D128887


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

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


[PATCH] D128887: [clang][transformer] Fix crash on replacement-less ASTEdit.

2022-06-30 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: ymandel.
Herald added a project: All.
courbet requested review of this revision.
Herald added projects: clang, clang-tools-extra.

Given that we provide an EditGenerator edit(ASTEdit), we can't ever be
sure that the user won't give us an empty replacement.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128887

Files:
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/lib/Tooling/Transformer/RewriteRule.cpp


Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,21 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Metadata) {
+  auto Metadata = E.Metadata(Result);
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -90,6 +90,32 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+transformer::ASTEdit noReplacementEdit(transformer::RangeSelector Target) {
+  transformer::ASTEdit E;
+  E.TargetRange = std::move(Target);
+  return E;
+}
+
+TEST(TransformerClangTidyCheckTest, EmptyReplacement) {
+  class DiagOnlyCheck : public TransformerClangTidyCheck {
+  public:
+DiagOnlyCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(), edit(noReplacementEdit(node(RootID))),
+   cat("message")),
+  Name, Context) {}
+  };
+  std::string Input = "int h() { return 5; }";
+  std::vector Errors;
+  EXPECT_EQ("int h() { }", test::runCheckOnCode(Input, 
));
+  EXPECT_EQ(Errors.size(), 1U);
+  EXPECT_EQ(Errors[0].Message.Message, "message");
+  EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+
+  // The diagnostic is anchored to the match, "return 5".
+  EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
+}
+
 TEST(TransformerClangTidyCheckTest, DiagnosticMessageEscaped) {
   class GiveDiagWithPercentSymbol : public TransformerClangTidyCheck {
   public:


Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,21 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Metadata) {
+  auto Metadata = E.Metadata(Result);
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -90,6 +90,32 @@
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+transformer::ASTEdit noReplacementEdit(transformer::RangeSelector Target) {
+  transformer::ASTEdit E;
+  E.TargetRange = std::move(Target);
+  return E;
+}
+
+TEST(TransformerClangTidyCheckTest, EmptyReplacement) {
+  class DiagOnlyCheck : public TransformerClangTidyCheck {
+  public:
+

[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-06-29 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 440948.
courbet added a comment.

Format patch


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

Files:
  clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/include/clang/Tooling/Transformer/RewriteRule.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp

Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,26 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Note) {
+  auto Note = E.Note->eval(Result);
+  if (!Note)
+return Note.takeError();
+  T.Note = std::move(*Note);
+}
+if (auto Metadata = E.Metadata(Result)) {
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
@@ -121,6 +130,29 @@
   return E;
 }
 
+ASTEdit transformer::withNote(ASTEdit Edit, TextGenerator Note) {
+  Edit.Note = std::move(Note);
+  return Edit;
+}
+
+EditGenerator transformer::withNote(EditGenerator Generator,
+TextGenerator Note) {
+  return
+  [G = std::move(Generator), N = std::move(Note)](
+  const MatchResult ) -> llvm::Expected> {
+llvm::Expected> Edits = G(Result);
+if (!Edits)
+  return Edits.takeError();
+llvm::Expected Note = N->eval(Result);
+if (!Note)
+  return Note.takeError();
+for (Edit  : *Edits) {
+  E.Note = *Note;
+}
+return Edits;
+  };
+}
+
 namespace {
 /// A \c TextGenerator that always returns a fixed string.
 class SimpleTextGenerator : public MatchComputation {
Index: clang/include/clang/Tooling/Transformer/RewriteRule.h
===
--- clang/include/clang/Tooling/Transformer/RewriteRule.h
+++ clang/include/clang/Tooling/Transformer/RewriteRule.h
@@ -46,6 +46,7 @@
   EditKind Kind = EditKind::Range;
   CharSourceRange Range;
   std::string Replacement;
+  std::string Note;
   llvm::Any Metadata;
 };
 
@@ -246,6 +247,14 @@
   return Edit;
 }
 
+// Adds a note to the given edit or edits. If there are several edits, the note
+// is added to each one of them.
+// \code
+//   withNote(noopEdit(), cat("some note"))
+// \endcode
+EditGenerator withNote(EditGenerator Generator, TextGenerator Note);
+ASTEdit withNote(ASTEdit Edit, TextGenerator Note);
+
 /// Assuming that the inner range is enclosed by the outer range, creates
 /// precision edits to remove the parts of the outer range that are not included
 /// in the inner range.
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -31,6 +31,7 @@
 using transformer::RewriteRuleWith;
 using transformer::RootID;
 using transformer::statement;
+using transformer::withNote;
 
 // Invert the code of an if-statement, while maintaining its semantics.
 RewriteRuleWith invertIf() {
@@ -85,11 +86,33 @@
   EXPECT_EQ(Errors.size(), 1U);
   EXPECT_EQ(Errors[0].Message.Message, "message");
   EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+  EXPECT_THAT(Errors[0].Notes, testing::IsEmpty());
 
   // The diagnostic is anchored to the match, "return 5".
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+TEST(TransformerClangTidyCheckTest, NotesCorrectlyGenerated) {
+  class DiagAndNoteCheck : public TransformerClangTidyCheck {
+  public:
+DiagAndNoteCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(),
+   withNote(noopEdit(node(RootID)), cat("some note")),
+   cat("message")),
+  Name, Context) {}
+  };
+  std::string Input = "int h() { return 5; }";
+  std::vector Errors;
+  EXPECT_EQ(Input, 

[PATCH] D128807: [clang][transformer] Finish plumbing `Note` all the way to the output.

2022-06-29 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added a reviewer: ymandel.
Herald added a subscriber: carlosgalvezp.
Herald added a project: All.
courbet requested review of this revision.
Herald added projects: clang, clang-tools-extra.

Right now we can only add a single warning, notes are not possible.

Apparently some provisions were made to allow notes, but they were never
propagated all the way to the diagnostics.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128807

Files:
  clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp
  clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
  clang/include/clang/Tooling/Transformer/RewriteRule.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp

Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -50,17 +50,26 @@
 // produces a bad range, whereas the latter will simply ignore A.
 if (!EditRange)
   return SmallVector();
-auto Replacement = E.Replacement->eval(Result);
-if (!Replacement)
-  return Replacement.takeError();
-auto Metadata = E.Metadata(Result);
-if (!Metadata)
-  return Metadata.takeError();
 transformer::Edit T;
 T.Kind = E.Kind;
 T.Range = *EditRange;
-T.Replacement = std::move(*Replacement);
-T.Metadata = std::move(*Metadata);
+if (E.Replacement) {
+  auto Replacement = E.Replacement->eval(Result);
+  if (!Replacement)
+return Replacement.takeError();
+  T.Replacement = std::move(*Replacement);
+}
+if (E.Note) {
+  auto Note = E.Note->eval(Result);
+  if (!Note)
+return Note.takeError();
+  T.Note = std::move(*Note);
+}
+if (auto Metadata = E.Metadata(Result)) {
+  if (!Metadata)
+return Metadata.takeError();
+  T.Metadata = std::move(*Metadata);
+}
 Edits.push_back(std::move(T));
   }
   return Edits;
@@ -121,6 +130,28 @@
   return E;
 }
 
+ASTEdit transformer::withNote(ASTEdit Edit, TextGenerator Note) {
+  Edit. Note = std::move(Note);
+  return Edit;
+}
+
+EditGenerator transformer::withNote(EditGenerator Generator, TextGenerator Note) {
+  return
+  [G = std::move(Generator), N=std::move(Note)](
+  const MatchResult ) -> llvm::Expected> {
+llvm::Expected> Edits = G(Result);
+if (!Edits)
+  return Edits.takeError();
+llvm::Expected Note = N->eval(Result);
+if (!Note)
+  return Note.takeError();
+for (Edit& E : *Edits) {
+  E.Note = *Note;
+}
+return Edits;
+  };
+}
+
 namespace {
 /// A \c TextGenerator that always returns a fixed string.
 class SimpleTextGenerator : public MatchComputation {
Index: clang/include/clang/Tooling/Transformer/RewriteRule.h
===
--- clang/include/clang/Tooling/Transformer/RewriteRule.h
+++ clang/include/clang/Tooling/Transformer/RewriteRule.h
@@ -46,6 +46,7 @@
   EditKind Kind = EditKind::Range;
   CharSourceRange Range;
   std::string Replacement;
+  std::string Note;
   llvm::Any Metadata;
 };
 
@@ -246,6 +247,14 @@
   return Edit;
 }
 
+// Adds a note to the given edit or edits. If there are several edits, the note
+// is added to each one of them.
+// \code
+//   withNote(noopEdit(), cat("some note"))
+// \endcode
+EditGenerator withNote(EditGenerator Generator, TextGenerator Note);
+ASTEdit withNote(ASTEdit Edit, TextGenerator Note);
+
 /// Assuming that the inner range is enclosed by the outer range, creates
 /// precision edits to remove the parts of the outer range that are not included
 /// in the inner range.
Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
@@ -28,6 +28,7 @@
 using transformer::makeRule;
 using transformer::node;
 using transformer::noopEdit;
+using transformer::withNote;
 using transformer::RewriteRuleWith;
 using transformer::RootID;
 using transformer::statement;
@@ -85,11 +86,33 @@
   EXPECT_EQ(Errors.size(), 1U);
   EXPECT_EQ(Errors[0].Message.Message, "message");
   EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty());
+  EXPECT_THAT(Errors[0].Notes, testing::IsEmpty());
 
   // The diagnostic is anchored to the match, "return 5".
   EXPECT_EQ(Errors[0].Message.FileOffset, 10U);
 }
 
+TEST(TransformerClangTidyCheckTest, NotesCorrectlyGenerated) {
+  class DiagAndNoteCheck : public TransformerClangTidyCheck {
+  public:
+DiagAndNoteCheck(StringRef Name, ClangTidyContext *Context)
+: TransformerClangTidyCheck(
+  makeRule(returnStmt(),
+   withNote(noopEdit(node(RootID)), 

[PATCH] D126903: [clang] Add support for __builtin_memset_inline

2022-06-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet accepted this revision.
courbet added inline comments.
This revision is now accepted and ready to land.



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:7355
+ isVol, true, DstPtrInfo, AAInfo);
+assert(Result && "getMemsetStores must return a valid sequence");
+return Result;

maybe add `when AlwaysInline` to make the issue more obvious.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126903

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


[PATCH] D126903: [clang] Add support for __builtin_memset_inline

2022-06-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h:79
   /// SDValue if the target declines to use custom code and a different
   /// lowering strategy should be used.
   virtual SDValue EmitTargetCodeForMemset(SelectionDAG , const SDLoc ,

Add comment about honoring `AlwaysInline` ?



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:6990
 /// \param isVol True if destination is volatile.
+/// \param AlwaysInline Makes sure a chain of stores is generated.
 /// \param DstPtrInfo IR information on the memory pointer.

"Makes sure no function call is generated", because technically the code can 
generate an accelerator call (e.g. `rep stos`)



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:7352
+assert(ConstantSize && "AlwaysInline requires a constant size!");
+return getMemsetStores(*this, dl, Chain, Dst, Src,
+   ConstantSize->getZExtValue(), Alignment, isVol, 
true,

Let's assert that the returned value is non-default before returning.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126903

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


[PATCH] D126903: [clang] Add support for __builtin_memset_inline

2022-06-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/test/Sema/builtins-memcpy-inline.cpp:11
+void test_memcpy_inline_invalid_arg_types() {
+  __builtin_memcpy_inline(1, 2, 3); // expected-error {{cannot initialize a 
parameter of type 'void *' with an rvalue of type 'int'}}
+}

can you split this off (and the corresponding code) to a separate patch ?



Comment at: llvm/docs/LangRef.rst:13898
+Note that, unlike the standard libc function, the ``llvm.memset.inline.*``
+intrinsics do not return a value, takes extra isvolatile
+arguments and the pointers can be in specified address spaces.

courbet wrote:
> "an extra `isvolatile` argument"
take*



Comment at: llvm/docs/LangRef.rst:13898-13899
+Note that, unlike the standard libc function, the ``llvm.memset.inline.*``
+intrinsics do not return a value, takes extra isvolatile
+arguments and the pointers can be in specified address spaces.
+

"an extra `isvolatile` argument"



Comment at: llvm/docs/LangRef.rst:13899
+intrinsics do not return a value, takes extra isvolatile
+arguments and the pointers can be in specified address spaces.
+

pointer*



Comment at: llvm/include/llvm/IR/IntrinsicInst.h:993
 
 /// This class wraps the llvm.memset intrinsic.
 class MemSetInst : public MemSetBase {

update comment



Comment at: llvm/include/llvm/IR/Intrinsics.td:654
 
+// Memset semantic that is guaranteed to be inlined.
+// In particular this means that the generated code is not allowed to call any

"version" ? or "semantics"



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:7334
   if (TSI) {
 SDValue Result = TSI->EmitTargetCodeForMemset(
 *this, dl, Chain, Dst, Src, Size, Alignment, isVol, DstPtrInfo);

There is nothing preveting a target from emitting a call to `memset` here when 
`AlwaysInline` is `true`. Actually, `X86SelectionDAGInfo` does just that (this 
very patch is adding `/* AlwaysInline */ false,` to the `getMemset` call that 
handles the trailing bytes). It happens that because trailing bytes are 
typically small and therefore inline. it happens to work, but this should be 
verified somehow (or, maybe easier,  `AlwaysInline` should be passed to 
`EmitTargetCodeForMemset` so it can do the right thing).



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:7344
+assert(ConstantSize && "AlwaysInline requires a constant size!");
+return getMemsetStores(*this, dl, Chain, Dst, Src,
+   ConstantSize->getZExtValue(), Alignment, isVol, 
true,

Not that this is not strictly required to return a valid `SDValue`: Even with 
an "infinite" `Limit` in `TLI.findOptimalMemOpLowering`, a target that 
overrides this function could decide to just return false. I'm not sure what we 
want to do in this case. 
So I think we should document `findOptimalMemOpLowering` to mention that the 
default implementation always returns a valid memop lowering if `Limit` is 
`UINT_MAX` and that target that decide to not provide a memop lowering *must* 
emit a valid one in `EmitTargetCodeForMemset`. Another option would be to call 
the generic `TargetLowering::findOptimalMemOpLowering` when the target declines 
to generate eithe ra memop lowering or target-specific code.



Comment at: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp:5928
+bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
+// FIXME: Support passing different dest/src alignments to the memcpy DAG
+// node.

memset



Comment at: llvm/test/CodeGen/X86/memset-inline.ll:1
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -mcpu=core2 | FileCheck %s

Add tests for other targets ?



Comment at: llvm/test/CodeGen/X86/memset-inline.ll:21
+define void @regular_memset_calls_external_function(i8* %a, i8 %value) 
nounwind {
+; CHECK-LABEL: regular_memset_calls_external_function:
+; CHECK:   # %bb.0:

I could see memset deciding to inline 129 bytes in the future. What about using 
a more absurdly large number ?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126903

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


[PATCH] D121365: [CFG] Fix crash on CFG building when deriving from a template.

2022-03-10 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: gribozavr, alexfh.
Herald added a project: All.
courbet requested review of this revision.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D121365

Files:
  clang/lib/Analysis/CFG.cpp
  clang/unittests/Analysis/CFGBuildResult.h
  clang/unittests/Analysis/CFGTest.cpp


Index: clang/unittests/Analysis/CFGTest.cpp
===
--- clang/unittests/Analysis/CFGTest.cpp
+++ clang/unittests/Analysis/CFGTest.cpp
@@ -70,6 +70,27 @@
   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
 }
 
+// Constructing a CFG with a dependent base should not crash.
+TEST(CFG, DependantBaseAddImplicitDtors) {
+  const char *Code = R"(
+template 
+struct Base {
+  virtual ~Base() {}
+};
+
+template 
+struct Derived : public Base {
+  virtual ~Derived() {}
+};
+  )";
+  CFG::BuildOptions Options;
+  Options.AddImplicitDtors = true;
+  Options.setAllAlwaysAdd();
+  EXPECT_EQ(BuildResult::BuiltCFG,
+BuildCFG(Code, Options, ast_matchers::hasName("~Derived"))
+.getStatus());
+}
+
 TEST(CFG, IsLinear) {
   auto expectLinear = [](bool IsLinear, const char *Code) {
 BuildResult B = BuildCFG(Code);
Index: clang/unittests/Analysis/CFGBuildResult.h
===
--- clang/unittests/Analysis/CFGBuildResult.h
+++ clang/unittests/Analysis/CFGBuildResult.h
@@ -56,13 +56,15 @@
 TheBuildResult = BuildResult::SawFunctionBody;
 Options.AddImplicitDtors = true;
 if (std::unique_ptr Cfg =
-CFG::buildCFG(nullptr, Body, Result.Context, Options))
+CFG::buildCFG(Func, Body, Result.Context, Options))
   TheBuildResult = {BuildResult::BuiltCFG, Func, std::move(Cfg),
 std::move(AST)};
   }
 };
 
-inline BuildResult BuildCFG(const char *Code, CFG::BuildOptions Options = {}) {
+template 
+BuildResult BuildCFG(const char *Code, CFG::BuildOptions Options = {},
+ FuncMatcherT FuncMatcher = ast_matchers::anything()) {
   std::vector Args = {"-std=c++11",
"-fno-delayed-template-parsing"};
   std::unique_ptr AST = tooling::buildASTFromCodeWithArgs(Code, Args);
@@ -72,7 +74,8 @@
   CFGCallback Callback(std::move(AST));
   Callback.Options = Options;
   ast_matchers::MatchFinder Finder;
-  Finder.addMatcher(ast_matchers::functionDecl().bind("func"), );
+  Finder.addMatcher(ast_matchers::functionDecl(FuncMatcher).bind("func"),
+);
 
   Finder.matchAST(Callback.AST->getASTContext());
   return std::move(Callback.TheBuildResult);
Index: clang/lib/Analysis/CFG.cpp
===
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -1884,7 +1884,7 @@
 // (which is different from the current class) is responsible for
 // destroying them.
 const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl();
-if (!CD->hasTrivialDestructor()) {
+if (CD && !CD->hasTrivialDestructor()) {
   autoCreateBlock();
   appendBaseDtor(Block, );
 }
@@ -1894,7 +1894,7 @@
   for (const auto  : RD->bases()) {
 if (!BI.isVirtual()) {
   const CXXRecordDecl *CD = BI.getType()->getAsCXXRecordDecl();
-  if (!CD->hasTrivialDestructor()) {
+  if (CD && !CD->hasTrivialDestructor()) {
 autoCreateBlock();
 appendBaseDtor(Block, );
   }


Index: clang/unittests/Analysis/CFGTest.cpp
===
--- clang/unittests/Analysis/CFGTest.cpp
+++ clang/unittests/Analysis/CFGTest.cpp
@@ -70,6 +70,27 @@
   EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
 }
 
+// Constructing a CFG with a dependent base should not crash.
+TEST(CFG, DependantBaseAddImplicitDtors) {
+  const char *Code = R"(
+template 
+struct Base {
+  virtual ~Base() {}
+};
+
+template 
+struct Derived : public Base {
+  virtual ~Derived() {}
+};
+  )";
+  CFG::BuildOptions Options;
+  Options.AddImplicitDtors = true;
+  Options.setAllAlwaysAdd();
+  EXPECT_EQ(BuildResult::BuiltCFG,
+BuildCFG(Code, Options, ast_matchers::hasName("~Derived"))
+.getStatus());
+}
+
 TEST(CFG, IsLinear) {
   auto expectLinear = [](bool IsLinear, const char *Code) {
 BuildResult B = BuildCFG(Code);
Index: clang/unittests/Analysis/CFGBuildResult.h
===
--- clang/unittests/Analysis/CFGBuildResult.h
+++ clang/unittests/Analysis/CFGBuildResult.h
@@ -56,13 +56,15 @@
 TheBuildResult = BuildResult::SawFunctionBody;
 Options.AddImplicitDtors = true;
 if (std::unique_ptr Cfg =
-CFG::buildCFG(nullptr, Body, Result.Context, Options))
+CFG::buildCFG(Func, Body, Result.Context, Options))
   

[PATCH] D116535: [clang-tidy] Fix false positive in modernize-pass-by-value

2022-01-05 Thread Clement Courbet via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGed8ff29aa683: [clang-tidy] Fix false positive in 
modernize-pass-by-value (authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D116535

Files:
  clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
@@ -246,3 +246,20 @@
 V::V(const Movable ) : M(M) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
 // CHECK-FIXES: V::V(const Movable ) : M(M) {}
+
+// Test with paired lvalue/rvalue overloads.
+struct W1 {
+  W1(const Movable ) : M(M) {}
+  W1(Movable &);
+  Movable M;
+};
+struct W2 {
+  W2(const Movable , int) : M(M) {}
+  W2(Movable &, int);
+  Movable M;
+};
+struct W3 {
+  W3(const W1 &, const Movable ) : M(M) {}
+  W3(W1 &&, Movable &);
+  Movable M;
+};
Index: clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
===
--- clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
@@ -105,6 +105,75 @@
   return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
 }
 
+/// Returns true if the given constructor is part of a lvalue/rvalue reference
+/// pair, i.e. `Param` is of lvalue reference type, and there exists another
+/// constructor such that:
+///  - it has the same number of parameters as `Ctor`.
+///  - the parameter at the same index as `Param` is an rvalue reference
+///of the same pointee type
+///  - all other parameters have the same type as the corresponding parameter in
+///`Ctor` or are rvalue references with the same pointee type.
+/// Examples:
+///  A::A(const B& Param)
+///  A::A(B&&)
+///
+///  A::A(const B& Param, const C&)
+///  A::A(B&& Param, C&&)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(B&&, C&& Param)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(const B&, C&& Param)
+///
+///  A::A(const B& Param, int)
+///  A::A(B&& Param, int)
+static bool hasRValueOverload(const CXXConstructorDecl *Ctor,
+  const ParmVarDecl *Param) {
+  if (!Param->getType().getCanonicalType()->isLValueReferenceType()) {
+// The parameter is passed by value.
+return false;
+  }
+  const int ParamIdx = Param->getFunctionScopeIndex();
+  const CXXRecordDecl *Record = Ctor->getParent();
+
+  // Check whether a ctor `C` forms a pair with `Ctor` under the aforementionned
+  // rules.
+  const auto IsRValueOverload = [, ParamIdx](const CXXConstructorDecl *C) {
+if (C == Ctor || C->isDeleted() ||
+C->getNumParams() != Ctor->getNumParams())
+  return false;
+for (int I = 0, E = C->getNumParams(); I < E; ++I) {
+  const clang::QualType CandidateParamType =
+  C->parameters()[I]->getType().getCanonicalType();
+  const clang::QualType CtorParamType =
+  Ctor->parameters()[I]->getType().getCanonicalType();
+  const bool IsLValueRValuePair =
+  CtorParamType->isLValueReferenceType() &&
+  CandidateParamType->isRValueReferenceType() &&
+  CandidateParamType->getPointeeType()->getUnqualifiedDesugaredType() ==
+  CtorParamType->getPointeeType()->getUnqualifiedDesugaredType();
+  if (I == ParamIdx) {
+// The parameter of interest must be paired.
+if (!IsLValueRValuePair)
+  return false;
+  } else {
+// All other parameters can be similar or paired.
+if (!(CandidateParamType == CtorParamType || IsLValueRValuePair))
+  return false;
+  }
+}
+return true;
+  };
+
+  for (const auto *Candidate : Record->ctors()) {
+if (IsRValueOverload(Candidate)) {
+  return true;
+}
+  }
+  return false;
+}
+
 /// Find all references to \p ParamDecl across all of the
 /// redeclarations of \p Ctor.
 static SmallVector
@@ -188,6 +257,10 @@
   *Result.Context))
 return;
 
+  // Do not trigger if we find a paired constructor with an rvalue.
+  if (hasRValueOverload(Ctor, ParamDecl))
+return;
+
   auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
 
   // If we received a `const&` type, we need to rewrite the function
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D116535: [clang-tidy] Fix false positive in modernize-pass-by-value

2022-01-05 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 397543.
courbet added a comment.

Address review comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D116535

Files:
  clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
@@ -246,3 +246,20 @@
 V::V(const Movable ) : M(M) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
 // CHECK-FIXES: V::V(const Movable ) : M(M) {}
+
+// Test with paired lvalue/rvalue overloads.
+struct W1 {
+  W1(const Movable ) : M(M) {}
+  W1(Movable &);
+  Movable M;
+};
+struct W2 {
+  W2(const Movable , int) : M(M) {}
+  W2(Movable &, int);
+  Movable M;
+};
+struct W3 {
+  W3(const W1 &, const Movable ) : M(M) {}
+  W3(W1 &&, Movable &);
+  Movable M;
+};
Index: clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
===
--- clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
@@ -105,6 +105,75 @@
   return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
 }
 
+/// Returns true if the given constructor is part of a lvalue/rvalue reference
+/// pair, i.e. `Param` is of lvalue reference type, and there exists another
+/// constructor such that:
+///  - it has the same number of parameters as `Ctor`.
+///  - the parameter at the same index as `Param` is an rvalue reference
+///of the same pointee type
+///  - all other parameters have the same type as the corresponding parameter in
+///`Ctor` or are rvalue references with the same pointee type.
+/// Examples:
+///  A::A(const B& Param)
+///  A::A(B&&)
+///
+///  A::A(const B& Param, const C&)
+///  A::A(B&& Param, C&&)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(B&&, C&& Param)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(const B&, C&& Param)
+///
+///  A::A(const B& Param, int)
+///  A::A(B&& Param, int)
+static bool hasRValueOverload(const CXXConstructorDecl *Ctor,
+  const ParmVarDecl *Param) {
+  if (!Param->getType().getCanonicalType()->isLValueReferenceType()) {
+// The parameter is passed by value.
+return false;
+  }
+  const int ParamIdx = Param->getFunctionScopeIndex();
+  const CXXRecordDecl *Record = Ctor->getParent();
+
+  // Check whether a ctor `C` forms a pair with `Ctor` under the aforementionned
+  // rules.
+  const auto IsRValueOverload = [, ParamIdx](const CXXConstructorDecl *C) {
+if (C == Ctor || C->isDeleted() ||
+C->getNumParams() != Ctor->getNumParams())
+  return false;
+for (int I = 0, E = C->getNumParams(); I < E; ++I) {
+  const clang::QualType CandidateParamType =
+  C->parameters()[I]->getType().getCanonicalType();
+  const clang::QualType CtorParamType =
+  Ctor->parameters()[I]->getType().getCanonicalType();
+  const bool IsLValueRValuePair =
+  CtorParamType->isLValueReferenceType() &&
+  CandidateParamType->isRValueReferenceType() &&
+  CandidateParamType->getPointeeType()->getUnqualifiedDesugaredType() ==
+  CtorParamType->getPointeeType()->getUnqualifiedDesugaredType();
+  if (I == ParamIdx) {
+// The parameter of interest must be paired.
+if (!IsLValueRValuePair)
+  return false;
+  } else {
+// All other parameters can be similar or paired.
+if (!(CandidateParamType == CtorParamType || IsLValueRValuePair))
+  return false;
+  }
+}
+return true;
+  };
+
+  for (const auto *Candidate : Record->ctors()) {
+if (IsRValueOverload(Candidate)) {
+  return true;
+}
+  }
+  return false;
+}
+
 /// Find all references to \p ParamDecl across all of the
 /// redeclarations of \p Ctor.
 static SmallVector
@@ -188,6 +257,10 @@
   *Result.Context))
 return;
 
+  // Do not trigger if we find a paired constructor with an rvalue.
+  if (hasRValueOverload(Ctor, ParamDecl))
+return;
+
   auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
 
   // If we received a `const&` type, we need to rewrite the function
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D116535: [clang-tidy] Fix false positive in modernize-pass-by-value

2022-01-05 Thread Clement Courbet via Phabricator via cfe-commits
courbet marked 2 inline comments as done.
courbet added a comment.

Thanks.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D116535

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


[PATCH] D116535: [clang-tidy] Fix false positive in modernize-pass-by-value

2022-01-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: alexfh, flx, aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

The check should not trigger on lvalue/rvalue overload pairs:

  struct S {
S(const A& a) : a(a) {}
S(A&& a) : a(std::move(a)) {}
  
A a;
  }


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D116535

Files:
  clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-pass-by-value.cpp
@@ -246,3 +246,20 @@
 V::V(const Movable ) : M(M) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
 // CHECK-FIXES: V::V(const Movable ) : M(M) {}
+
+// Test with paired lvalue/rvalue overloads.
+struct W1 {
+  W1(const Movable ) : M(M) {}
+  W1(Movable &);
+  Movable M;
+};
+struct W2 {
+  W2(const Movable , int) : M(M) {}
+  W2(Movable &, int);
+  Movable M;
+};
+struct W3 {
+  W3(const W1 &, const Movable ) : M(M) {}
+  W3(W1 &&, Movable &);
+  Movable M;
+};
Index: clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
===
--- clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
@@ -105,6 +105,73 @@
   return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
 }
 
+/// Returns true the given constructor is part of a lvalue/rvalue reference
+/// pair, i.e. `Param` is of lvalue reference type, and there exists another
+/// constructor such that:
+///  - it has the same number of parameters as `Ctor`.
+///  - the parameter at the same index as `Param` is an rvalue reference
+///of the same pointee type
+///  - all other parameters have the same type as the corresponding parameter in
+///`Ctor` or are rvalue references with the same pointee type.
+/// Examples:
+///  A::A(const B& Param)
+///  A::A(B&&)
+///
+///  A::A(const B& Param, const C&)
+///  A::A(B&& Param, C&&)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(B&&, C&& Param)
+///
+///  A::A(const B&, const C& Param)
+///  A::A(const B&, C&& Param)
+///
+///  A::A(const B& Param, int)
+///  A::A(B&& Param, int)
+static bool hasRValueOverload(const CXXConstructorDecl *Ctor,
+  const ParmVarDecl *Param) {
+  if (!Param->getType().getCanonicalType()->isLValueReferenceType()) {
+// The parameter is passed by value.
+return false;
+  }
+  const int ParamIdx = Param->getFunctionScopeIndex();
+  const CXXRecordDecl *Record = Ctor->getParent();
+
+  // Check whether a ctor `C` forms a pair with `Ctor` under the aforementionned
+  // rules.
+  const auto IsRValueOverload = [, ParamIdx](const CXXConstructorDecl *C) {
+if (C == Ctor || C->isDeleted() ||
+C->getNumParams() != Ctor->getNumParams())
+  return false;
+for (int I = 0, E = C->getNumParams(); I < E; ++I) {
+  const clang::QualType CandidateParamType =
+  C->parameters()[I]->getType().getCanonicalType();
+  const clang::QualType CtorParamType =
+  Ctor->parameters()[I]->getType().getCanonicalType();
+  const bool IsLValueRValuePair =
+  CtorParamType->isLValueReferenceType() &&
+  CandidateParamType->isRValueReferenceType() &&
+  CandidateParamType->getPointeeType()->getUnqualifiedDesugaredType() ==
+  CtorParamType->getPointeeType()->getUnqualifiedDesugaredType();
+  if (I == ParamIdx) {
+if (!IsLValueRValuePair)
+  return false;
+  } else {
+if (!(CandidateParamType == CtorParamType || IsLValueRValuePair))
+  return false;
+  }
+}
+return true;
+  };
+
+  for (const auto *Candidate : Record->ctors()) {
+if (IsRValueOverload(Candidate)) {
+  return true;
+}
+  }
+  return false;
+}
+
 /// Find all references to \p ParamDecl across all of the
 /// redeclarations of \p Ctor.
 static SmallVector
@@ -188,6 +255,10 @@
   *Result.Context))
 return;
 
+  // Do not trigger if we find a paired constructor with an rvalue.
+  if (hasRValueOverload(Ctor, ParamDecl))
+return;
+
   auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
 
   // If we received a `const&` type, we need to rewrite the function
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-12-20 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: clang/lib/Sema/SemaExpr.cpp:13824
 SourceLocation OpLoc) {
-  if (Op->isTypeDependent())
+  if (Op->isTypeDependent() && !Op->getType()->isPointerType())
 return S.Context.DependentTy;

aaron.ballman wrote:
> One thing that makes me a bit uncomfortable is that the logic for these used 
> to be far more understandable when it was just checking for a dependent type. 
> Now we need to sprinkle "and not a pointer type" in places, but it's not 
> particularly clear as to why for a naïve reader of the code.
> 
> I wonder if we want some sort of type predicate `isDeferrablyDependentType()` 
> or something along those lines, or whether that's not really plausible? 
> Basically, I'm hoping to find a way that, as a code reviewer, I can more 
> easily spot places where `isTypeDependent()` should really be caring about 
> type dependent pointers as a special case.
Right, so in this function `Op->isTypeDependent()` is always used to bail out 
from type checking. The structure is typically:

```
if (E->isTypeDependent()) {
  // bail out
}
// Actual type checking on provable types.
if (E->hasSomeTypeProperty1()) {
  // OK case, do some property1-specific checking
} else if (E->hasSomeTypeProperty2()) {
  // OK case, do some property2-specific checking
} else {
  // Emit some error
}
```

My original approach was to do:

```
if (E->isTypeDependent() && !E->isPointerType()) {
  // bail out
}
// Actual type checking on provable types or pointer types.
if (E->hasSomeTypeProperty1()) {
  // OK case, do some property1-specific checking
} else if (E->hasSomeTypeProperty2()) {
  // OK case, do some property2-specific checking
} else {
  // Emit some error
}
```

It turns out that in all cases, `hasSomeTypeProperty1` is actually 
`isPointerType` (or stuff like `isScalarType`, which includes pointers), so the 
current code is actually already checking for pointerness, so in a sense my new 
pointer type cheking is already included in subsequent checks, I'm not adding 
anything new.

But maybe another change like this one will be able to  prove more stuff about 
dependent types (e.g. a dependent type could have propery2 too ), so what about 
we only bail out on dependent types *after* we are done with the type checking:

```
if (E->isTypeDependent() && !E->isPointerType()) {
  // bail out
}
// Actual type checking on provable types or pointer types.
if (E->hasSomeTypeProperty1()) {
  // OK case, do some property1-specific checking
} else if (E->hasSomeTypeProperty2()) {
  // OK case, do some property2-specific checking
} else if (if (E->isTypeDependent()) {
  // bail out
} else {
  // Emit some error
}
```

This essentially goes from "If the type is not dependent, try to prove stuff 
about the type, else return error" to "try to prove stuff about the type, else 
if not dependent, return error".

I'm modified the code here to use this approach, what do you think ?





Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-12-20 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 395410.
courbet added a comment.

Try to get rid of "dependent and not a pointer" checks checks.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

Files:
  clang/include/clang/AST/Type.h
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/test/AST/ast-dump-expr-json.cpp
  clang/test/AST/ast-dump-expr.cpp
  clang/test/AST/ast-dump-lambda.cpp
  clang/test/CXX/over/over.built/ast-20.cpp
  clang/test/CXX/over/over.built/ast.cpp
  clang/test/CXX/over/over.built/p10.cpp
  clang/test/CXX/over/over.built/p11.cpp
  clang/test/CXX/over/over.built/p13.cpp
  clang/test/CXX/over/over.built/p14.cpp
  clang/test/CXX/over/over.built/p18.cpp
  clang/test/CXX/over/over.built/p19.cpp
  clang/test/CXX/over/over.built/p20.cpp
  clang/test/CXX/over/over.built/p22.cpp
  clang/test/CXX/over/over.built/p23.cpp
  clang/test/CXX/over/over.built/p4.cpp
  clang/test/CXX/over/over.built/p5.cpp
  clang/test/CXX/over/over.built/p7.cpp
  clang/test/CXX/over/over.built/spaceship.cpp
  clang/test/Frontend/noderef_templates.cpp
  clang/test/SemaTemplate/dependent-type-identity.cpp

Index: clang/test/SemaTemplate/dependent-type-identity.cpp
===
--- clang/test/SemaTemplate/dependent-type-identity.cpp
+++ clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -69,6 +69,16 @@
   void f8(typename N::X2::template apply *);
   void f8(typename N::X2::template apply *);
   void f8(typename ::Nalias::X2::template apply *); // expected-error{{redeclar}}
+
+  // (17.4.2): If an expression e is type-dependent (17.6.2.2), decltype(e)
+  // denotes a unique dependent type. Two such decltype-specifiers refer to the
+  // same type only if their expressions are equivalent (17.5.6.1)
+  T* a;
+  T* b;
+  using V = decltype(*a);
+  void f9(decltype(*a)); // expected-note{{previous}}
+  void f9(decltype(*b));
+  void f9(V); // expected-error{{redeclar}}
 };
 
 namespace PR6851 {
Index: clang/test/Frontend/noderef_templates.cpp
===
--- clang/test/Frontend/noderef_templates.cpp
+++ clang/test/Frontend/noderef_templates.cpp
@@ -3,8 +3,8 @@
 #define NODEREF __attribute__((noderef))
 
 template 
-int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
-  return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
+int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
+  return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
 }
 
 void func() {
Index: clang/test/CXX/over/over.built/spaceship.cpp
===
--- clang/test/CXX/over/over.built/spaceship.cpp
+++ clang/test/CXX/over/over.built/spaceship.cpp
@@ -11,11 +11,10 @@
 }
 
 template 
-void f(int i, float f, int* pi, T* pt, T t) {
+void f(int i, int* pi, T* pt, T t) {
   (void)(i <=> i);
-  (void)(i <=> f); // expected-error {{invalid argument type}}
-  (void)(i <=> pi); // expected-error {{invalid argument type}}
-  (void)(i <=> pt); // expected-error {{invalid argument type}}
+  (void)(i <=> pi); // expected-error {{comparison between pointer and integer}}
+  (void)(i <=> pt); // expected-error {{comparison between pointer and integer}}
   (void)(pi <=> pt);
   (void)(pi <=> t);
 }
Index: clang/test/CXX/over/over.built/p7.cpp
===
--- clang/test/CXX/over/over.built/p7.cpp
+++ clang/test/CXX/over/over.built/p7.cpp
@@ -3,10 +3,11 @@
 struct A{};
 
 template 
-void f(int* pi, A* pa, T* pt) {
+void f(int* pi, A* pa, T* pt, T t) {
   (void)*pi;
   (void)*pa;
   (void)*pt;
+  (void)*t;  // `T` might be a `U*`.
 }
 // expected-no-diagnostics
 
Index: clang/test/CXX/over/over.built/p5.cpp
===
--- clang/test/CXX/over/over.built/p5.cpp
+++ clang/test/CXX/over/over.built/p5.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)--i;
   (void)i--;
 
   (void)--b; // expected-error {{cannot decrement expression of type bool}}
   (void)b--; // expected-error {{cannot decrement expression of type bool}}
+
+  (void)--t;
+  (void)t--;
 }
 
Index: clang/test/CXX/over/over.built/p4.cpp
===
--- clang/test/CXX/over/over.built/p4.cpp
+++ clang/test/CXX/over/over.built/p4.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++17 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)++i;
   (void)i++;
 
   (void)++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}}
   (void)b++; // expected-error {{ISO C++17 does not 

[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-12-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D114105: [clang-tidy] Ignore narrowing conversions in case of bitfields

2021-11-25 Thread Clement Courbet via Phabricator via cfe-commits
courbet accepted this revision.
courbet added inline comments.



Comment at: 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-narrowing-conversions-bitfields.cpp:62
+void take(T);
+void test_parameter_passing(NoBitfield x) {
+  take(x.id);

[nit] newline after take


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

https://reviews.llvm.org/D114105

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


[PATCH] D114105: [clang-tidy] Ignore narrowing conversions in case of bitfields

2021-11-25 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Yes, I think the new approach is what we want. The comments also make it much 
clearer.




Comment at: 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-narrowing-conversions-bitfields.cpp:1
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN:   -std=c++17 -- -target x86_64-unknown-linux

Can you add a test with:

```

void takesInt(int);

...

takesInt(x.id);
```

I think this suffers from the same problem currently.


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

https://reviews.llvm.org/D114105

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


[PATCH] D114539: [clang-tidy] performance-unnecessary-copy-initialization: handle pointer containers.

2021-11-25 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 389690.
courbet marked an inline comment as done.
courbet added a comment.

address comment


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114539

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
  clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
@@ -101,23 +101,23 @@
 void f(const S target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -127,23 +127,23 @@
 void f(const S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -153,8 +153,8 @@
 void f(S target, const S& other) {
   useConstRef(/*const*/target);
   useVal(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -167,15 +167,15 @@
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
   (void)(/*const*/target == other);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -185,8 +185,8 @@
 void f(S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -194,15 +194,15 @@
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -210,26 +210,26 @@
 TEST(ConstReferenceDeclRefExprsTest, PtrVar) 

[PATCH] D114539: [clang-tidy] performance-unnecessary-copy-initialization: handle pointer containers.

2021-11-25 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 389667.
courbet added a comment.

Add more comments on the approach.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114539

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
  clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
@@ -101,23 +101,23 @@
 void f(const S target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -127,23 +127,23 @@
 void f(const S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -153,8 +153,8 @@
 void f(S target, const S& other) {
   useConstRef(/*const*/target);
   useVal(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -167,15 +167,15 @@
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
   (void)(/*const*/target == other);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -185,8 +185,8 @@
 void f(S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -194,15 +194,15 @@
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -210,26 +210,26 @@
 TEST(ConstReferenceDeclRefExprsTest, PtrVar) {
   RunTest(R"(
 

[PATCH] D114539: [clang-tidy] performance-unnecessary-copy-initialization: handle pointer containers.

2021-11-24 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 389504.
courbet added a comment.

Rebase on submitted unit tests so that we can see the changes better.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114539

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
  clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
===
--- clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
@@ -101,23 +101,23 @@
 void f(const S target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -127,23 +127,23 @@
 void f(const S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   /*const*/target(ConstTag{});
   /*const*/target[42];
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -153,8 +153,8 @@
 void f(S target, const S& other) {
   useConstRef(/*const*/target);
   useVal(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -167,15 +167,15 @@
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
   (void)(/*const*/target == other);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -185,8 +185,8 @@
 void f(S& target) {
   useVal(/*const*/target);
   useConstRef(/*const*/target);
-  useConstPtr();
-  useConstPtrConstRef();
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
   /*const*/target.constMethod();
   target.nonConstMethod();
   /*const*/target(ConstTag{});
@@ -194,15 +194,15 @@
   useConstRef((/*const*/target));
   (/*const*/target).constMethod();
   (void)(/*const*/target == /*const*/target);
-  (void)target;
-  (void)
-  (void)*
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
   S copy1 = /*const*/target;
   S copy2(/*const*/target);
-  useInt(target.int_member);
-  useIntConstRef(target.int_member);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
   useIntPtr(target.ptr_member);
-  useIntConstPtr(_member);
+  useIntConstPtr(&/*const*/target.int_member);
 }
 )");
 }
@@ -210,26 +210,26 @@
 

[PATCH] D114539: [clang-tidy] performance-unnecessary-copy-initialization: handle pointer containers.

2021-11-24 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: flx, aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun, mgorny.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

This includes modifying `DeclRefExprUtils` to handle more cases.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D114539

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
  clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
  clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
===
--- /dev/null
+++ clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
@@ -0,0 +1,315 @@
+#include "../clang-tidy/utils/DeclRefExprUtils.h"
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+
+namespace {
+using namespace clang::ast_matchers;
+
+class ConstReferenceDeclRefExprsTransform : public ClangTidyCheck {
+public:
+  ConstReferenceDeclRefExprsTransform(StringRef CheckName,
+  ClangTidyContext *Context)
+  : ClangTidyCheck(CheckName, Context) {}
+
+  void registerMatchers(MatchFinder *Finder) override {
+Finder->addMatcher(varDecl(hasName("target")).bind("var"), this);
+  }
+
+  void check(const MatchFinder::MatchResult ) override {
+const auto *D = Result.Nodes.getNodeAs("var");
+using utils::decl_ref_expr::constReferenceDeclRefExprs;
+const auto const_decrefexprs = constReferenceDeclRefExprs(
+*D, *cast(D->getDeclContext())->getBody(),
+*Result.Context);
+
+for (const DeclRefExpr *const Expr : const_decrefexprs) {
+  assert(Expr);
+  diag(Expr->getBeginLoc(), "const usage")
+  << FixItHint::CreateInsertion(Expr->getBeginLoc(), "/*const*/");
+}
+  }
+};
+} // namespace
+
+namespace test {
+
+void RunTest(StringRef Snippet) {
+
+  StringRef CommonCode = R"(
+struct ConstTag{};
+struct NonConstTag{};
+
+struct S {
+  void constMethod() const;
+  void nonConstMethod();
+
+  void operator()(ConstTag) const;
+  void operator()(NonConstTag);
+
+  void operator[](int);
+  void operator[](int) const;
+
+  bool operator==(const S&) const;
+
+  int int_member;
+  int* ptr_member;
+
+};
+
+struct Derived : public S {
+
+};
+
+void useVal(S);
+void useRef(S&);
+void usePtr(S*);
+void usePtrPtr(S**);
+void usePtrConstPtr(S* const*);
+void useConstRef(const S&);
+void useConstPtr(const S*);
+void useConstPtrRef(const S*&);
+void useConstPtrPtr(const S**);
+void useConstPtrConstRef(const S* const&);
+void useConstPtrConstPtr(const S* const*);
+
+void useInt(int);
+void useIntRef(int&);
+void useIntConstRef(const int&);
+void useIntPtr(int*);
+void useIntConstPtr(const int*);
+
+)";
+
+  std::string Code = (CommonCode + Snippet).str();
+
+  llvm::SmallVector Parts;
+  StringRef(Code).split(Parts, "/*const*/");
+
+  EXPECT_EQ(Code, runCheckOnCode(
+  join(Parts, "")));
+}
+
+TEST(ConstReferenceDeclRefExprsTest, ConstValueVar) {
+  RunTest(R"(
+void f(const S target) {
+  useVal(/*const*/target);
+  useConstRef(/*const*/target);
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
+  /*const*/target.constMethod();
+  /*const*/target(ConstTag{});
+  /*const*/target[42];
+  useConstRef((/*const*/target));
+  (/*const*/target).constMethod();
+  (void)(/*const*/target == /*const*/target);
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
+  S copy1 = /*const*/target;
+  S copy2(/*const*/target);
+  useInt(/*const*/target.int_member);
+  useIntConstRef(/*const*/target.int_member);
+  useIntPtr(target.ptr_member);
+  useIntConstPtr(&/*const*/target.int_member);
+}
+)");
+}
+
+TEST(ConstReferenceDeclRefExprsTest, ConstRefVar) {
+  RunTest(R"(
+void f(const S& target) {
+  useVal(/*const*/target);
+  useConstRef(/*const*/target);
+  useConstPtr(&/*const*/target);
+  useConstPtrConstRef(&/*const*/target);
+  /*const*/target.constMethod();
+  /*const*/target(ConstTag{});
+  /*const*/target[42];
+  useConstRef((/*const*/target));
+  (/*const*/target).constMethod();
+  (void)(/*const*/target == /*const*/target);
+  (void)/*const*/target;
+  (void)&/*const*/target;
+  (void)*&/*const*/target;
+  S copy1 = /*const*/target;
+  S 

[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-23 Thread Clement Courbet via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGba4411e7c6a5: [clang-tidy] 
performance-unnecessary-copy-initialization: Fix false negative. (authored by 
courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114249

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization-excluded-container-types.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -3,11 +3,19 @@
 template 
 struct Iterator {
   void operator++();
-  const T *() const;
+  T *() const;
   bool operator!=(const Iterator &) const;
   typedef const T _reference;
 };
 
+template 
+struct ConstIterator {
+  void operator++();
+  const T *() const;
+  bool operator!=(const ConstIterator &) const;
+  typedef const T _reference;
+};
+
 struct ExpensiveToCopyType {
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
@@ -15,8 +23,6 @@
   using ConstRef = const ExpensiveToCopyType &;
   ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
-  Iterator begin() const;
-  Iterator end() const;
   void nonConstMethod();
   bool constMethod() const;
   template 
@@ -24,6 +30,21 @@
   operator int() const; // Implicit conversion to int.
 };
 
+template 
+struct Container {
+  bool empty() const;
+  const T& operator[](int) const;
+  const T& operator[](int);
+  Iterator begin();
+  Iterator end();
+  ConstIterator begin() const;
+  ConstIterator end() const;
+  void nonConstMethod();
+  bool constMethod() const;
+};
+
+using ExpensiveToCopyContainerAlias = Container;
+
 struct TrivialToCopyType {
   const TrivialToCopyType () const;
 };
@@ -138,6 +159,94 @@
   VarCopyConstructed.constMethod();
 }
 
+void PositiveOperatorCallConstReferenceParam(const Container ) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParam(const Container C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlias C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+ 

[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-23 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 389197.
courbet added a comment.

add container exclusion tests for operators.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114249

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization-excluded-container-types.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -3,11 +3,19 @@
 template 
 struct Iterator {
   void operator++();
-  const T *() const;
+  T *() const;
   bool operator!=(const Iterator &) const;
   typedef const T _reference;
 };
 
+template 
+struct ConstIterator {
+  void operator++();
+  const T *() const;
+  bool operator!=(const ConstIterator &) const;
+  typedef const T _reference;
+};
+
 struct ExpensiveToCopyType {
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
@@ -15,8 +23,6 @@
   using ConstRef = const ExpensiveToCopyType &;
   ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
-  Iterator begin() const;
-  Iterator end() const;
   void nonConstMethod();
   bool constMethod() const;
   template 
@@ -24,6 +30,21 @@
   operator int() const; // Implicit conversion to int.
 };
 
+template 
+struct Container {
+  bool empty() const;
+  const T& operator[](int) const;
+  const T& operator[](int);
+  Iterator begin();
+  Iterator end();
+  ConstIterator begin() const;
+  ConstIterator end() const;
+  void nonConstMethod();
+  bool constMethod() const;
+};
+
+using ExpensiveToCopyContainerAlias = Container;
+
 struct TrivialToCopyType {
   const TrivialToCopyType () const;
 };
@@ -138,6 +159,94 @@
   VarCopyConstructed.constMethod();
 }
 
+void PositiveOperatorCallConstReferenceParam(const Container ) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParam(const Container C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlias C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  

[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-23 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 389184.
courbet added a comment.

Canonicalize more types and add more container tests.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114249

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -3,11 +3,19 @@
 template 
 struct Iterator {
   void operator++();
-  const T *() const;
+  T *() const;
   bool operator!=(const Iterator &) const;
   typedef const T _reference;
 };
 
+template 
+struct ConstIterator {
+  void operator++();
+  const T *() const;
+  bool operator!=(const ConstIterator &) const;
+  typedef const T _reference;
+};
+
 struct ExpensiveToCopyType {
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
@@ -15,8 +23,6 @@
   using ConstRef = const ExpensiveToCopyType &;
   ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
-  Iterator begin() const;
-  Iterator end() const;
   void nonConstMethod();
   bool constMethod() const;
   template 
@@ -24,6 +30,21 @@
   operator int() const; // Implicit conversion to int.
 };
 
+template 
+struct Container {
+  bool empty() const;
+  const T& operator[](int) const;
+  const T& operator[](int);
+  Iterator begin();
+  Iterator end();
+  ConstIterator begin() const;
+  ConstIterator end() const;
+  void nonConstMethod();
+  bool constMethod() const;
+};
+
+using ExpensiveToCopyContainerAlias = Container;
+
 struct TrivialToCopyType {
   const TrivialToCopyType () const;
 };
@@ -138,6 +159,94 @@
   VarCopyConstructed.constMethod();
 }
 
+void PositiveOperatorCallConstReferenceParam(const Container ) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParam(const Container C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlias C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: 

[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 388821.
courbet added a comment.

rebase


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114249

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -24,6 +24,11 @@
   operator int() const; // Implicit conversion to int.
 };
 
+struct ExpensiveToCopyContainer {
+  const ExpensiveToCopyType [](int) const;
+  const ExpensiveToCopyType [](int);
+};
+
 struct TrivialToCopyType {
   const TrivialToCopyType () const;
 };
@@ -138,6 +143,50 @@
   VarCopyConstructed.constMethod();
 }
 
+void PositiveOperatorCallConstReferenceParam(const ExpensiveToCopyContainer 
) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParam(const ExpensiveToCopyContainer C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
 void PositiveLocalConstValue() {
   const ExpensiveToCopyType Obj;
   const auto UnnecessaryCopy = Obj.reference();
Index: 
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -83,13 +83,19 @@
   // variable being declared. The assumption is that the const reference being
   // returned either points to a global static variable or to a member of the
   // called object.
-  return cxxMemberCallExpr(
-  callee(cxxMethodDecl(
- returns(hasCanonicalType(matchers::isReferenceToConst(
- .bind(MethodDeclId)),
-  on(declRefExpr(to(varDecl().bind(ObjectArgId,
-  thisPointerType(namedDecl(
-  unless(matchers::matchesAnyListedName(ExcludedContainerTypes);
+  const auto MethodDecl =
+  cxxMethodDecl(returns(hasCanonicalType(matchers::isReferenceToConst(
+  .bind(MethodDeclId);
+  const auto ReceiverExpr = declRefExpr(to(varDecl().bind(ObjectArgId)));
+  const auto ReceiverTypeDecl =
+  
namedDecl(unless(matchers::matchesAnyListedName(ExcludedContainerTypes)));
+
+  return expr(anyOf(
+  cxxMemberCallExpr(callee(MethodDecl), on(ReceiverExpr),
+thisPointerType(ReceiverTypeDecl)),
+  cxxOperatorCallExpr(callee(MethodDecl), hasArgument(0, ReceiverExpr),
+  hasArgument(0, hasType(recordType(hasDeclaration(
+ ReceiverTypeDecl)));
 }
 
 AST_MATCHER_FUNCTION(StatementMatcher, isConstRefReturningFunctionCall) {


Index: 

[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-22 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: 
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp:97
+  cxxOperatorCallExpr(callee(MethodDecl), hasArgument(0, ReceiverExpr),
+  hasArgument(0, hasType(recordType(hasDeclaration(
+ ReceiverTypeDecl)));

flx wrote:
> Does this work if if the object argument is a pointer or a type alias? Could 
> you add a test to confirm?
It does not work with a pointer. I plan to make it work in a separate cl, but 
first I need to fix `isOnlyUsedAsConst` to make more general (specifically, 
handle unary `*`).



Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D114249

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


[PATCH] D114249: [clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.

2021-11-19 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: flx, aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

`isConstRefReturningMethodCall` should be considering
`CXXOperatorCallExpr` in addition to `CXXMemberCallExpr`. Clang considers
these to be distinct (`CXXOperatorCallExpr` derives from `CallExpr`, not
`CXXMemberCallExpr`), but we don't care in the context of this
check.

This is important because of
`std::vector::operator[](size_t) const`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D114249

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -24,6 +24,11 @@
   operator int() const; // Implicit conversion to int.
 };
 
+struct ExpensiveToCopyContainer {
+  const ExpensiveToCopyType [](int) const;
+  const ExpensiveToCopyType [](int);
+};
+
 struct TrivialToCopyType {
   const TrivialToCopyType () const;
 };
@@ -138,6 +143,50 @@
   VarCopyConstructed.constMethod();
 }
 
+void PositiveOperatorCallConstReferenceParam(const ExpensiveToCopyContainer 
) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
+void PositiveOperatorCallConstValueParam(const ExpensiveToCopyContainer C) {
+  const auto AutoAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = C[42];
+  AutoAssigned.constMethod();
+
+  const auto AutoCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 
'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
+  AutoCopyConstructed.constMethod();
+
+  const ExpensiveToCopyType VarAssigned = C[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
+  VarAssigned.constMethod();
+
+  const ExpensiveToCopyType VarCopyConstructed(C[42]);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 
'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
+  VarCopyConstructed.constMethod();
+}
+
 void PositiveLocalConstValue() {
   const ExpensiveToCopyType Obj;
   const auto UnnecessaryCopy = Obj.reference();
Index: 
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -83,15 +83,19 @@
   // variable being declared. The assumption is that the const reference being
   // returned either points to a global static variable or to a member of the
   // called object.
-  return cxxMemberCallExpr(
-  callee(cxxMethodDecl(
- returns(hasCanonicalType(matchers::isReferenceToConst(
- .bind(MethodDeclId)),
-  on(declRefExpr(to(
-  varDecl(
-  
unless(hasType(qualType(hasCanonicalType(hasDeclaration(namedDecl(
-  matchers::matchesAnyListedName(ExcludedContainerTypes
-  .bind(ObjectArgId);
+  const auto MethodDecl =
+  cxxMethodDecl(returns(hasCanonicalType(matchers::isReferenceToConst(
+  .bind(MethodDeclId);
+  const auto ReceiverExpr = declRefExpr(to(varDecl().bind(ObjectArgId)));
+  const auto ReceiverTypeDecl =
+  
namedDecl(unless(matchers::matchesAnyListedName(ExcludedContainerTypes)));
+
+  return expr(anyOf(
+ 

[PATCH] D114105: [clang-tidy] Ignore narrowing conversions in case of bitfields

2021-11-18 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Thanks for the details, this explains the motivation well. I think the key 
point is the combination of:

> It turns out doing anything useful with a bitfield will eventually result in 
> an integral promotion, which mandates that the type must be 'int' if it fits
> conv.prom/5: 
>
>> A prvalue for an integral bit-field ([class.bit]) can be converted to a 
>> prvalue of type **int if int can represent all the values of the 
>> bit-field**; otherwise, it can be converted to unsigned int if unsigned int 
>> can represent all the values of the bit-field. If the bit-field is larger 
>> yet, no integral promotion applies to it. If the bit-field has an enumerated 
>> type, it is treated as any other value of that type for promotion purposes.

and

> shift expression is the only one whose return type depends only on the 
> //left// operand

So adding that as a comment above `ShiftingWidenedBitfieldValue` would help.

Maybe also add a note in the comment that we are not interested in whether the 
shift itself might overflow.


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

https://reviews.llvm.org/D114105

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


[PATCH] D114105: [clang-tidy] Ignore narrowing conversions in case of bitfields

2021-11-18 Thread Clement Courbet via Phabricator via cfe-commits
courbet added inline comments.



Comment at: 
clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp:104
+  castExpr(hasCastKind(CK_LValueToRValue),
+   has(memberExpr(hasDeclaration(fieldDecl(isBitField(,
+  hasParent(

There needs to be some kind of check of the size of the bit field vs the size 
of the integer, because `struct SmallBitfield { unsigned int id : 31; };` 
should warn.



Comment at: 
clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp:105
+   has(memberExpr(hasDeclaration(fieldDecl(isBitField(,
+  hasParent(
+  binaryOperator(anyOf(hasOperatorName("<<"), 
hasOperatorName(">>");

What is specific about shifting operators ? What about `x+1` for example ? You 
might have opened a pandora box here: you'll need to tech the check about the 
semantics of all binary operations.

```
BinaryOperator  'int' '+'
  |-ImplicitCastExpr  'int' 
  | `-ImplicitCastExpr  'unsigned int' 
  |   `-MemberExpr  'unsigned int' lvalue bitfield .id 
0x55e144e7eaa0
  | `-DeclRefExpr  'SmallBitfield' lvalue Var 0x55e144e7eb18 'x' 
'SmallBitfield'
  `-IntegerLiteral  'int' 1
```



Comment at: 
clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp:106
+  hasParent(
+  binaryOperator(anyOf(hasOperatorName("<<"), 
hasOperatorName(">>");
+

There also needs to be some check of the constant value of the RHS, because 
`x.id << 30` can actually overflow, so we want to warn in that case.


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

https://reviews.llvm.org/D114105

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


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-16 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-09 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 385768.
courbet added a comment.

one more spaceship operator fix.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

Files:
  clang/include/clang/AST/Type.h
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/test/AST/ast-dump-expr-json.cpp
  clang/test/AST/ast-dump-expr.cpp
  clang/test/AST/ast-dump-lambda.cpp
  clang/test/CXX/over/over.built/ast-20.cpp
  clang/test/CXX/over/over.built/ast.cpp
  clang/test/CXX/over/over.built/p10.cpp
  clang/test/CXX/over/over.built/p11.cpp
  clang/test/CXX/over/over.built/p13.cpp
  clang/test/CXX/over/over.built/p14.cpp
  clang/test/CXX/over/over.built/p18.cpp
  clang/test/CXX/over/over.built/p19.cpp
  clang/test/CXX/over/over.built/p20.cpp
  clang/test/CXX/over/over.built/p22.cpp
  clang/test/CXX/over/over.built/p23.cpp
  clang/test/CXX/over/over.built/p4.cpp
  clang/test/CXX/over/over.built/p5.cpp
  clang/test/CXX/over/over.built/p7.cpp
  clang/test/CXX/over/over.built/spaceship.cpp
  clang/test/Frontend/noderef_templates.cpp
  clang/test/SemaTemplate/dependent-type-identity.cpp

Index: clang/test/SemaTemplate/dependent-type-identity.cpp
===
--- clang/test/SemaTemplate/dependent-type-identity.cpp
+++ clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -69,6 +69,16 @@
   void f8(typename N::X2::template apply *);
   void f8(typename N::X2::template apply *);
   void f8(typename ::Nalias::X2::template apply *); // expected-error{{redeclar}}
+
+  // (17.4.2): If an expression e is type-dependent (17.6.2.2), decltype(e)
+  // denotes a unique dependent type. Two such decltype-specifiers refer to the
+  // same type only if their expressions are equivalent (17.5.6.1)
+  T* a;
+  T* b;
+  using V = decltype(*a);
+  void f9(decltype(*a)); // expected-note{{previous}}
+  void f9(decltype(*b));
+  void f9(V); // expected-error{{redeclar}}
 };
 
 namespace PR6851 {
Index: clang/test/Frontend/noderef_templates.cpp
===
--- clang/test/Frontend/noderef_templates.cpp
+++ clang/test/Frontend/noderef_templates.cpp
@@ -3,8 +3,8 @@
 #define NODEREF __attribute__((noderef))
 
 template 
-int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
-  return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
+int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
+  return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
 }
 
 void func() {
Index: clang/test/CXX/over/over.built/spaceship.cpp
===
--- clang/test/CXX/over/over.built/spaceship.cpp
+++ clang/test/CXX/over/over.built/spaceship.cpp
@@ -11,11 +11,10 @@
 }
 
 template 
-void f(int i, float f, int* pi, T* pt, T t) {
+void f(int i, int* pi, T* pt, T t) {
   (void)(i <=> i);
-  (void)(i <=> f); // expected-error {{invalid argument type}}
-  (void)(i <=> pi); // expected-error {{invalid argument type}}
-  (void)(i <=> pt); // expected-error {{invalid argument type}}
+  (void)(i <=> pi); // expected-error {{comparison between pointer and integer}}
+  (void)(i <=> pt); // expected-error {{comparison between pointer and integer}}
   (void)(pi <=> pt);
   (void)(pi <=> t);
 }
Index: clang/test/CXX/over/over.built/p7.cpp
===
--- clang/test/CXX/over/over.built/p7.cpp
+++ clang/test/CXX/over/over.built/p7.cpp
@@ -3,10 +3,11 @@
 struct A{};
 
 template 
-void f(int* pi, A* pa, T* pt) {
+void f(int* pi, A* pa, T* pt, T t) {
   (void)*pi;
   (void)*pa;
   (void)*pt;
+  (void)*t;  // `T` might be a `U*`.
 }
 // expected-no-diagnostics
 
Index: clang/test/CXX/over/over.built/p5.cpp
===
--- clang/test/CXX/over/over.built/p5.cpp
+++ clang/test/CXX/over/over.built/p5.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)--i;
   (void)i--;
 
   (void)--b; // expected-error {{cannot decrement expression of type bool}}
   (void)b--; // expected-error {{cannot decrement expression of type bool}}
+
+  (void)--t;
+  (void)t--;
 }
 
Index: clang/test/CXX/over/over.built/p4.cpp
===
--- clang/test/CXX/over/over.built/p4.cpp
+++ clang/test/CXX/over/over.built/p4.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++17 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)++i;
   (void)i++;
 
   (void)++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}}
   (void)b++; // expected-error {{ISO C++17 does not allow incrementing expression 

[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-04 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

> Is this change observable in some way?

With the new changes, we are now catching more typing errors before 
instantiation. I've added more tests to show that.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-04 Thread Clement Courbet via Phabricator via cfe-commits
courbet updated this revision to Diff 384771.
courbet added a comment.

Implement the proposed changes.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

Files:
  clang/include/clang/AST/Type.h
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/test/AST/ast-dump-expr-json.cpp
  clang/test/AST/ast-dump-expr.cpp
  clang/test/AST/ast-dump-lambda.cpp
  clang/test/CXX/over/over.built/ast.cpp
  clang/test/CXX/over/over.built/p10.cpp
  clang/test/CXX/over/over.built/p11.cpp
  clang/test/CXX/over/over.built/p13.cpp
  clang/test/CXX/over/over.built/p14.cpp
  clang/test/CXX/over/over.built/p18.cpp
  clang/test/CXX/over/over.built/p19.cpp
  clang/test/CXX/over/over.built/p20.cpp
  clang/test/CXX/over/over.built/p22.cpp
  clang/test/CXX/over/over.built/p23.cpp
  clang/test/CXX/over/over.built/p4.cpp
  clang/test/CXX/over/over.built/p5.cpp
  clang/test/CXX/over/over.built/p7.cpp
  clang/test/Frontend/noderef_templates.cpp
  clang/test/SemaTemplate/dependent-type-identity.cpp

Index: clang/test/SemaTemplate/dependent-type-identity.cpp
===
--- clang/test/SemaTemplate/dependent-type-identity.cpp
+++ clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -69,6 +69,16 @@
   void f8(typename N::X2::template apply *);
   void f8(typename N::X2::template apply *);
   void f8(typename ::Nalias::X2::template apply *); // expected-error{{redeclar}}
+
+  // (17.4.2): If an expression e is type-dependent (17.6.2.2), decltype(e)
+  // denotes a unique dependent type. Two such decltype-specifiers refer to the
+  // same type only if their expressions are equivalent (17.5.6.1)
+  T* a;
+  T* b;
+  using V = decltype(*a);
+  void f9(decltype(*a)); // expected-note{{previous}}
+  void f9(decltype(*b));
+  void f9(V); // expected-error{{redeclar}}
 };
 
 namespace PR6851 {
Index: clang/test/Frontend/noderef_templates.cpp
===
--- clang/test/Frontend/noderef_templates.cpp
+++ clang/test/Frontend/noderef_templates.cpp
@@ -3,8 +3,8 @@
 #define NODEREF __attribute__((noderef))
 
 template 
-int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
-  return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
+int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
+  return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
 }
 
 void func() {
Index: clang/test/CXX/over/over.built/p7.cpp
===
--- clang/test/CXX/over/over.built/p7.cpp
+++ clang/test/CXX/over/over.built/p7.cpp
@@ -3,10 +3,11 @@
 struct A{};
 
 template 
-void f(int* pi, A* pa, T* pt) {
+void f(int* pi, A* pa, T* pt, T t) {
   (void)*pi;
   (void)*pa;
   (void)*pt;
+  (void)*t;  // `T` might be a `U*`.
 }
 // expected-no-diagnostics
 
Index: clang/test/CXX/over/over.built/p5.cpp
===
--- clang/test/CXX/over/over.built/p5.cpp
+++ clang/test/CXX/over/over.built/p5.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)--i;
   (void)i--;
 
   (void)--b; // expected-error {{cannot decrement expression of type bool}}
   (void)b--; // expected-error {{cannot decrement expression of type bool}}
+
+  (void)--t;
+  (void)t--;
 }
 
Index: clang/test/CXX/over/over.built/p4.cpp
===
--- clang/test/CXX/over/over.built/p4.cpp
+++ clang/test/CXX/over/over.built/p4.cpp
@@ -1,10 +1,14 @@
 // RUN: %clang_cc1 -std=c++17 -verify %s -Wno-tautological-compare
 
-void f(int i, bool b) {
+template 
+void f(int i, bool b, T t) {
   (void)++i;
   (void)i++;
 
   (void)++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}}
   (void)b++; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}}
+
+  (void)++t;
+  (void)t++;
 }
 
Index: clang/test/CXX/over/over.built/p23.cpp
===
--- clang/test/CXX/over/over.built/p23.cpp
+++ clang/test/CXX/over/over.built/p23.cpp
@@ -6,41 +6,41 @@
   f %= 3;  // expected-error {{invalid operands}}
   b %= 3;
   pi %= 3; // expected-error {{invalid operands}}
-  pt %= 3; // FIXME
+  pt %= 3; // expected-error {{invalid operands}}
   t %= 3;
 
   i &= 3;
   f &= 3;  // expected-error {{invalid operands}}
   b &= 3;
   pi &= 3; // expected-error {{invalid operands}}
-  pt &= 3; // FIXME
+  pt &= 3; // expected-error {{invalid operands}}
   t &= 3;
 
   i ^= 3;
   f ^= 3;  // expected-error {{invalid operands}}
   b ^= 3;
   pi ^= 3; // expected-error {{invalid operands}}
-  pt ^= 3; // FIXME
+  pt ^= 3; // expected-error 

[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-03 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

In D112453#3103515 , @rsmith wrote:

> I think this change is being made in an imperfect place, though. Instead of 
> putting a special case in here, how would we feel about making 
> `Type::isOverloadableType()` smarter, to return `false` for dependent types 
> that can't possibly be class or enum types (eg, dependent pointer types, 
> array types, and maybe more exotic things like function types, atomic types, 
> and complex types)? This would then apply to all operators, not only unary 
> `*`. Eg, we know the type of `` where `p` is `T*`, and we know the type of 
> `p + n` where  `p` is `T*` and  `n` is integral. That change might have a lot 
> more fallout, but it'd certainly be interesting to see what it breaks.

Thanks for the suggestion ! I've improved test coverage for unary operators in 
rG1427742750ed 
. There 
was a minor breakage with this change in `Sema::DefaultLvalueConversion` which 
did not handle dependant pointers.

Binary operators look like they break more things. Working on it now.

> Is this change observable in some way? We should include a test with this 
> change that demonstrates what effect it has. (The existing test in this patch 
> passes with or without the change.)

Yes, I agree. The idea of this test was just to check that adding this did not 
have side effects on how clang was handling what the standard mandates, but 
you've covered this in the first part of your answer, thanks.
I don't think it's observable directly (I'm observing the type using 
`getType()` directly in my case), so I'll add an AST dump test, thanks for the 
suggestion.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-11-02 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

Ping ?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


[PATCH] D112722: [clang-tidy]performance-unnecessary-copy-initialization: fix false negative

2021-10-29 Thread Clement Courbet via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGfc1b24d7360f: 
[clang-tidy]performance-unnecessary-copy-initialization: fix false negative 
(authored by courbet).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112722

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -12,6 +12,8 @@
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
   const ExpensiveToCopyType () const;
+  using ConstRef = const ExpensiveToCopyType &;
+  ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
   Iterator begin() const;
   Iterator end() const;
@@ -206,6 +208,15 @@
   }
 }
 
+void positiveNonConstVarInCodeBlockWithAlias(const ExpensiveToCopyType ) {
+  {
+const ExpensiveToCopyType Assigned = Obj.referenceWithAlias();
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: the const qualified variable
+// CHECK-FIXES: const ExpensiveToCopyType& Assigned = 
Obj.referenceWithAlias();
+useAsConstReference(Assigned);
+  }
+}
+
 void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType ) {
   {
 auto NonConstInvoked = Obj.reference();
Index: 
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -84,7 +84,8 @@
   // returned either points to a global static variable or to a member of the
   // called object.
   return cxxMemberCallExpr(
-  callee(cxxMethodDecl(returns(matchers::isReferenceToConst()))
+  callee(cxxMethodDecl(
+ returns(hasCanonicalType(matchers::isReferenceToConst(
  .bind(MethodDeclId)),
   on(declRefExpr(to(
   varDecl(
@@ -97,7 +98,8 @@
   // Only allow initialization of a const reference from a free function if it
   // has no arguments. Otherwise it could return an alias to one of its
   // arguments and the arguments need to be checked for const use as well.
-  return callExpr(callee(functionDecl(returns(matchers::isReferenceToConst()))
+  return callExpr(callee(functionDecl(returns(hasCanonicalType(
+  matchers::isReferenceToConst(
  .bind(FunctionDeclId)),
   argumentCountIs(0), unless(callee(cxxMethodDecl(
   .bind(InitFunctionCallId);


Index: clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -12,6 +12,8 @@
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
   const ExpensiveToCopyType () const;
+  using ConstRef = const ExpensiveToCopyType &;
+  ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
   Iterator begin() const;
   Iterator end() const;
@@ -206,6 +208,15 @@
   }
 }
 
+void positiveNonConstVarInCodeBlockWithAlias(const ExpensiveToCopyType ) {
+  {
+const ExpensiveToCopyType Assigned = Obj.referenceWithAlias();
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: the const qualified variable
+// CHECK-FIXES: const ExpensiveToCopyType& Assigned = Obj.referenceWithAlias();
+useAsConstReference(Assigned);
+  }
+}
+
 void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType ) {
   {
 auto NonConstInvoked = Obj.reference();
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -84,7 +84,8 @@
   // returned either points to a global static variable or to a member of the
   // called object.
   return cxxMemberCallExpr(
-  callee(cxxMethodDecl(returns(matchers::isReferenceToConst()))
+  callee(cxxMethodDecl(
+ returns(hasCanonicalType(matchers::isReferenceToConst(
  .bind(MethodDeclId)),
   on(declRefExpr(to(
   varDecl(
@@ -97,7 +98,8 @@
   // Only allow initialization of a 

[PATCH] D112722: [clang-tidy]performance-unnecessary-copy-initialization: fix false negative

2021-10-28 Thread Clement Courbet via Phabricator via cfe-commits
courbet created this revision.
courbet added reviewers: flx, ymandel, aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun.
courbet requested review of this revision.
Herald added a project: clang-tools-extra.

We're missing all cases where the return value is a type alias.

Unfortunately, this includes things we care about, such as
`std::vector::operator[]` (return value is `const_reference`,
not `const T&`).

Match the canonical type instead.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112722

Files:
  clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
  
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -12,6 +12,8 @@
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
   const ExpensiveToCopyType () const;
+  using ConstRef = const ExpensiveToCopyType &;
+  ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
   Iterator begin() const;
   Iterator end() const;
@@ -206,6 +208,15 @@
   }
 }
 
+void positiveNonConstVarInCodeBlockWithAlias(const ExpensiveToCopyType ) {
+  {
+const ExpensiveToCopyType Assigned = Obj.referenceWithAlias();
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: the const qualified variable
+// CHECK-FIXES: const ExpensiveToCopyType& Assigned = 
Obj.referenceWithAlias();
+useAsConstReference(Assigned);
+  }
+}
+
 void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType ) {
   {
 auto NonConstInvoked = Obj.reference();
Index: 
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -84,7 +84,8 @@
   // returned either points to a global static variable or to a member of the
   // called object.
   return cxxMemberCallExpr(
-  callee(cxxMethodDecl(returns(matchers::isReferenceToConst()))
+  callee(cxxMethodDecl(
+ returns(hasCanonicalType(matchers::isReferenceToConst(
  .bind(MethodDeclId)),
   on(declRefExpr(to(
   varDecl(
@@ -97,7 +98,8 @@
   // Only allow initialization of a const reference from a free function if it
   // has no arguments. Otherwise it could return an alias to one of its
   // arguments and the arguments need to be checked for const use as well.
-  return callExpr(callee(functionDecl(returns(matchers::isReferenceToConst()))
+  return callExpr(callee(functionDecl(returns(hasCanonicalType(
+  matchers::isReferenceToConst(
  .bind(FunctionDeclId)),
   argumentCountIs(0), unless(callee(cxxMethodDecl(
   .bind(InitFunctionCallId);


Index: clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-copy-initialization.cpp
@@ -12,6 +12,8 @@
   ExpensiveToCopyType();
   virtual ~ExpensiveToCopyType();
   const ExpensiveToCopyType () const;
+  using ConstRef = const ExpensiveToCopyType &;
+  ConstRef referenceWithAlias() const;
   const ExpensiveToCopyType *pointer() const;
   Iterator begin() const;
   Iterator end() const;
@@ -206,6 +208,15 @@
   }
 }
 
+void positiveNonConstVarInCodeBlockWithAlias(const ExpensiveToCopyType ) {
+  {
+const ExpensiveToCopyType Assigned = Obj.referenceWithAlias();
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: the const qualified variable
+// CHECK-FIXES: const ExpensiveToCopyType& Assigned = Obj.referenceWithAlias();
+useAsConstReference(Assigned);
+  }
+}
+
 void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType ) {
   {
 auto NonConstInvoked = Obj.reference();
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -84,7 +84,8 @@
   // returned either points to a global static variable or to a member of the
   // called object.
   return cxxMemberCallExpr(
-  callee(cxxMethodDecl(returns(matchers::isReferenceToConst()))
+  callee(cxxMethodDecl(
+ 

[PATCH] D112453: [Sema] When dereferencing a pointer of dependent type, infer the result type.

2021-10-25 Thread Clement Courbet via Phabricator via cfe-commits
courbet added a comment.

As per the comment in BuiltinTypes.def (see below), `Dependent` is
allowed in context where the type is deducible, but is there any reason
**not** to deduce the type if we can do it cheaply in some cases ?

  // This represents the type of an expression whose type is
  // totally unknown, e.g. 'T::foo'.  It is permitted for this to
  // appear in situations where the structure of the type is
  // theoretically deducible.
  BUILTIN_TYPE(Dependent, DependentTy)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D112453

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


  1   2   3   >