[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-08-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:306
+
+  if (From->isMemberPointerType() || To->isMemberPointerType())
 return false;

PiotrZSL wrote:
> isuckatcs wrote:
> > isuckatcs wrote:
> > > PiotrZSL wrote:
> > > > isuckatcs wrote:
> > > > > isuckatcs wrote:
> > > > > > Please cover this line with both positive and negative test cases.
> > > > > > 
> > > > > > Also upon looking up both [[ 
> > > > > > https://www.open-std.org/jtc1/sc22/wg21/docs/standards | N4849 ]] 
> > > > > > (C++ 20 draft) and [[ https://github.com/cplusplus/draft/releases | 
> > > > > > N4917]] (C++ 23 draft), they both say for qualification conversion 
> > > > > > that 
> > > > > > 
> > > > > > 
> > > > > > > each P_i is ... pointer to member of class C_i of type, ...
> > > > > > 
> > > > > > Why are we not allowing them if the standard is at least C++ 20?
> > > > > Please resolve this thread.
> > > > Positive case is tested by throw_basefn_catch_derivedfn test and 
> > > > throw_derivedfn_catch_basefn.
> > > > Negative is covered by throw_basefn_catch_basefn.
> > > > 
> > > > For members there are tests throw_basem_catch_basem, 
> > > > throw_basem_catch_derivedm, throw_derivedm_catch_basem that also covers 
> > > > this correctly.
> > > > 
> > > > Tested this with GCC, and behavior is proper and independent to C++ 
> > > > standard.
> > > > Tested this with GCC, and behavior is proper and independent to C++ 
> > > > standard.
> > > 
> > > I don't know how to deal with this tbh. In a static analyzer we usually 
> > > want to consider what the standard says and not what a specific compiler 
> > > implementation does, as the compiler can still be buggy, lack the proper 
> > > support for the standard, etc.
> > > 
> > > Maybe we can add a FIXME here that explains, why this check is here and 
> > > that it's the opposite of what the standard says. Then later if it causes 
> > > issues, it will be easy to see why that happens.
> > > 
> > > @xazax.hun do you have a prefered method to deal with these situations in 
> > > the analyzer? If there is one, we could also try it here.
> > @PiotrZSL this is still unanswered. The standard says these are allowed, so 
> > according to the standard we model something wrong here, unless I'm 
> > misunderstanding something.
> If you talk about about '7.3.5 Qualification conversions' then this is only 
> about cv conversion, not about overall conversion from base to derived type, 
> and this is verify by SatisfiesCVRules lambda.
> 
> Without this line, check fails to detect issue in 
> throw_basefn_catch_derivedfn function under C++20.
> 
> Other interesting thing is that even this code:
> ```
>try {
>  throw ::foo;
>} catch(const void(baseMember::*)()) {
>}
> ```
> 
> Causes exception not to be catch, but if you remove const then it's catch.
> Same behavior is in GCC. I do not sure if 7.3.5 even apply to exceptions, and 
> personally I do not care what standard says, but I care how compilers work.
> 
> Right now both GCC and Clang in C++20 and C++17 work in same way, and this 
> ``if`` should be here so check would be align with how compiler works.
> There is
> This is anyway a insignificant scenario that most probably wont be used in 
> field.
> I do not sure if 7.3.5 (7.3.5 Qualification conversions) even apply to 
> exceptions



> [N4917 Post-Summer 2022 C++ working draft] 14.4 Handling an exception
> [...]
> - the handler is of type cv T or const T& where T is a pointer or 
> pointer-to-member type and E is a pointer or pointer-to-member type that can 
> be converted to T by one or more of
>  - a standard pointer conversion (7.3.12) not involving conversions to 
> pointers to private or protected or ambiguous classes
>  - a function pointer conversion (7.3.14)
>  - a qualification conversion (7.3.6), or <-- here it is
> [...]


>  I do not care what standard says, but I care how compilers work.

Then how would you reason about [[ https://godbolt.org/z/oTr3fbG8T | this ]] 
snippet? MSVC detects that the exception won't be caught and reports a warning, 
while gcc and clang fails to do it. In this case different compilers work 
differently. To be fair, the exception should be caught, since the thrown 
object is convertible to the handler (which is probably why gcc and clang don't 
report a warning).

> Without this line, check fails to detect issue in 
> throw_basefn_catch_derivedfn function under C++20.

Then do a proper investigation why that happens instead of adding random lines, 
which contradict what should happen, so that the test can pass. Probably the 
condition is wrong here and we should return `false` if `!isSameP_i` even if 
the standard is C++20 except for some cases with arrays. Look up the C++17 and 
the C++20 standards and compare them.


Repository:
  rG LLVM Github Monorepo

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


[PATCH] D153298: [clang-tidy] Extend bugprone-exception-escape diagnostics

2023-08-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:542
+   CaughtExceptions)) {
+  CaughtExceptions.emplace(CaughtType, SourceLocation());
   ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),

isuckatcs wrote:
> Here we rethrow something from a `try` block if I understand it correctly.
> 
> ```lang=c++
> void foo() {
> throw 3;
> }
> 
> void bar() {
> try {
> foo();
> } catch(short) {
> }
> }
> ```
> 
> The best way would be to set the `SourceLocation` to the point, where `foo()` 
> is called.
> I think you would need to create a new `struct` called `ThrowableInfo`, which 
> contains, 
> every information that we need to know about the `Throwable` e.g.: type, 
> location, parent
> context, etc. That would also be more extensible.
> 
> If there's no way to deduce this location for some reason, you can still set 
> the location 
> to the `try` block and create messages like `rethrown from try here`, etc. 
> Just don't create
> invalid `SourceLocation`s please, because besides them being incorrect, they 
> are also
> a source of bugs and crashes.
Also, is this branch tested? I don't see any new test case with a `try` block.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153298

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


[PATCH] D153298: [clang-tidy] Extend bugprone-exception-escape diagnostics

2023-08-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp:88
+
+  for (auto [ThrowType, ThrowLoc] : AnalyzeResult.getExceptionTypes())
+diag(ThrowLoc, "may throw %0 here", DiagnosticIDs::Note)





Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:14
 void ExceptionAnalyzer::ExceptionInfo::registerException(
-const Type *ExceptionType) {
+const Type *ExceptionType, const SourceLocation Loc) {
   assert(ExceptionType != nullptr && "Only valid types are accepted");

Is there any particular reason for taking `SourceLocation` by value? A `const 
SourceLocation &` would avoid an unnecessary copy.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:21
 void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
-const Throwables ) {
-  if (Exceptions.size() == 0)
+const Throwables , const SourceLocation Loc) {
+  if (Exceptions.empty())

Same question here regarding the argument type.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:25
   Behaviour = State::Throwing;
-  ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
+  for (const auto [ThrowType, ThrowLoc] : Exceptions)
+ThrownExceptions.emplace(ThrowType, ThrowLoc.isInvalid() ? Loc : ThrowLoc);





Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:26
+  for (const auto [ThrowType, ThrowLoc] : Exceptions)
+ThrownExceptions.emplace(ThrowType, ThrowLoc.isInvalid() ? Loc : ThrowLoc);
 }

There shouldn't be any invalid location in the container. An exception is 
thrown by a statement, so we know where in source that happens.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:352
+
+  auto ShouldRemoveFnt = [HandlerTy, ](const Type *ExceptionTy) {
 CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();

For a moment it wasn't entirely clear for me what `Fnt` meant, I suggest using 
naming like `FnType` or `FunctionType`.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:391
+return true;
+  return false;
 }





Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:408
 
-  for (const Type *T : TypesToDelete)
-ThrownExceptions.erase(T);
+CaughtExceptions.emplace(*It);
+It = ThrownExceptions.erase(It);

This introduces a side effect to this function and makes it do 2 things at the 
same time. Besides filtering the caught exceptions, it also collects and 
returns them through a reference parameter.

I don't mind the latter, but in that case I'd like to see the caught exceptions 
returned from the function as a part of the return statement. Also notice that 
if `CaughtExceptions` is not empty, `Result` is `true`, which makes the latter 
redundant. I suggest dropping the `bool` return type and returning the 
`Throwables` instead.





Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:428
+(IgnoredTypes.count(TD->getName()) > 0)) {
+  It = ThrownExceptions.erase(It);
+  continue;

You are erasing something from a container on which you're iteration over. Are 
you sure the iterators are not invalidated, when the internal representation 
changes?



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:542
+   CaughtExceptions)) {
+  CaughtExceptions.emplace(CaughtType, SourceLocation());
   ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),

Here we rethrow something from a `try` block if I understand it correctly.

```lang=c++
void foo() {
throw 3;
}

void bar() {
try {
foo();
} catch(short) {
}
}
```

The best way would be to set the `SourceLocation` to the point, where `foo()` 
is called.
I think you would need to create a new `struct` called `ThrowableInfo`, which 
contains, 
every information that we need to know about the `Throwable` e.g.: type, 
location, parent
context, etc. That would also be more extensible.

If there's no way to deduce this location for some reason, you can still set 
the location 
to the `try` block and create messages like `rethrown from try here`, etc. Just 
don't create
invalid `SourceLocation`s please, because besides them being incorrect, they 
are also
a source of bugs and crashes.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:573
   Excs.getExceptionTypes(), CallStack));
-for (const Type *Throwable : Excs.getExceptionTypes()) {
+for (auto [Throwable, ThrowLoc] : Excs.getExceptionTypes()) {
   if (const auto 

[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-08-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:306
+
+  if (From->isMemberPointerType() || To->isMemberPointerType())
 return false;

isuckatcs wrote:
> PiotrZSL wrote:
> > isuckatcs wrote:
> > > isuckatcs wrote:
> > > > Please cover this line with both positive and negative test cases.
> > > > 
> > > > Also upon looking up both [[ 
> > > > https://www.open-std.org/jtc1/sc22/wg21/docs/standards | N4849 ]] (C++ 
> > > > 20 draft) and [[ https://github.com/cplusplus/draft/releases | N4917]] 
> > > > (C++ 23 draft), they both say for qualification conversion that 
> > > > 
> > > > 
> > > > > each P_i is ... pointer to member of class C_i of type, ...
> > > > 
> > > > Why are we not allowing them if the standard is at least C++ 20?
> > > Please resolve this thread.
> > Positive case is tested by throw_basefn_catch_derivedfn test and 
> > throw_derivedfn_catch_basefn.
> > Negative is covered by throw_basefn_catch_basefn.
> > 
> > For members there are tests throw_basem_catch_basem, 
> > throw_basem_catch_derivedm, throw_derivedm_catch_basem that also covers 
> > this correctly.
> > 
> > Tested this with GCC, and behavior is proper and independent to C++ 
> > standard.
> > Tested this with GCC, and behavior is proper and independent to C++ 
> > standard.
> 
> I don't know how to deal with this tbh. In a static analyzer we usually want 
> to consider what the standard says and not what a specific compiler 
> implementation does, as the compiler can still be buggy, lack the proper 
> support for the standard, etc.
> 
> Maybe we can add a FIXME here that explains, why this check is here and that 
> it's the opposite of what the standard says. Then later if it causes issues, 
> it will be easy to see why that happens.
> 
> @xazax.hun do you have a prefered method to deal with these situations in the 
> analyzer? If there is one, we could also try it here.
@PiotrZSL this is still unanswered. The standard says these are allowed, so 
according to the standard we model something wrong here, unless I'm 
misunderstanding something.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148461

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


[PATCH] D153458: [clang-tidy] Model noexcept more properly in bugprone-exception-escape

2023-06-23 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:319
 
+static bool cannotThrow(const FunctionDecl *Func) {
+  const auto *FunProto = Func->getType()->getAs();

Put this in the anonymous namespace above please to remain consistent.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:446
+  if (!Func || CallStack.count(Func) ||
+  (!CallStack.empty() && cannotThrow(Func)))
 return ExceptionInfo::createNonThrowing();

Is `cannotThrow(Func)` really needed here? Isn't it possible to bail out after 
the body of the function has been analyzed? 

I understand that you want to prevent some recursive calls and bail out early, 
but I don't think that it worths adding some additional logic, which is not 
needed anyway. 

If you really want to optimize this or you're worried about stack overflows, 
consider rewriting the recursive solution to an iterative one.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153458

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


[PATCH] D153423: [clang-tidy] Allow explicit throwing in bugprone-exception-escape for special functions

2023-06-23 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

Apart from some nits I think this patch looks good.




Comment at: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp:26
+
+AST_MATCHER(FunctionDecl, isExplicitThrow) {
+  switch (Node.getExceptionSpecType()) {

How about creating an `isExplicitThrow()` helper function inside 
`ExceptionSpecificationType.h`, where these enums are defined? That way if one 
more enum is added, it will be more convenient to update the explicit throw 
checks, not to mention it can also be reused in other checkers if needed.



Comment at: 
clang-tools-extra/docs/clang-tidy/checks/bugprone/exception-escape.rst:14
 * ``swap()`` functions
-* Functions marked with ``throw()`` or ``noexcept``
+* Functions marked with ``throw()``, ``noexcept``, ``noexcept(true)``
 * Other functions given as option

Why remove `noexcept(true)`?



Comment at: 
clang-tools-extra/docs/clang-tidy/checks/bugprone/exception-escape.rst:25-29
+Functions declared explicitly with ``noexcept(false)`` or ``throw(exception)``
+will be excluded from the analysis, as even though it is not recommended for
+functions like ``swap``, ``main``, move constructors and assignment operators,
+and destructors, it is a clear indication of the developer's intention and
+should be respected..




Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153423

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


[PATCH] D153298: [clang-tidy] Extend bugprone-exception-escape diagnostics

2023-06-23 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:390
   for (const Type *T : TypesToDelete)
-ThrownExceptions.erase(T);
+ThrownExceptions.erase({T, SourceLocation()});
 

PiotrZSL wrote:
> isuckatcs wrote:
> > This line makes me wonder if it's worth using a `map` instead of a `set` 
> > for `ThrownExceptions`. You could map the type to the location. I mean 
> > technically, that happens now too, but not with the appropriate data 
> > structure. 
> > 
> > Also I wonder what happens if a function can throw the same type from 
> > multiple locations. E.g.:
> > ```lang=c++
> > void foo(int x) {
> >   if(x == 0) 
> > throw 1;
> >   
> >   if(x == 1)
> > throw 2;
> > }
> > ```
> > Here only the last location will be preserved, so maybe mapping `Type` to 
> > `vector` would be better.
> We use llvm::SmallSet, but there is no llvm::SmallMap. I wanted to preserve 
> memory optimizations, as using some map could hit performance, after all we 
> create many of those temporary containers.
> And with map I run into some issues (it didn't like const pointer as key).
> 
> As for double throw of same type. I agree, but I don't think its worth being 
> implemented currently.
> Once user remove one exception, check will show second. Many checks work like 
> that.
> This is just to provide small hint.
I still feel like creating these `{T, SourceLocation()}` entries is bloated. In 
case of a map, you wouldn't need to create dummy `SourceLocation`s. 

We have `DenseMap`, which is documented like this:
> DenseMap is a simple quadratically probed hash table. It excels at supporting 
> small keys and values: [...] DenseMap is a great way to map pointers to 
> pointers, or map other small types to each other.

Here you would map pointers to `SourceLocation`s, which are basically `int`s, 
so they are smaller than pointers. I think it worths giving `DenseMap` a try.

Also note that the number of exceptions we store is very low, so even 
`std::map<>` wouldn't cause a significant performance loss. Also currently our 
`SmallSet<>` is set to 2 elements, which means if we store more than 2 
elements, it will switch to `std::set<>` instead. 


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153298

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


[PATCH] D153298: [clang-tidy] Extend bugprone-exception-escape diagnostics

2023-06-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp:84-86
+  if (AnalyzeResult.containsUnknownElements())
+diag(MatchedDecl->getLocation(), "may throw unknown exceptions",
+ DiagnosticIDs::Note);

I'm still not sure how I feel about this message though.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:334
+  CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+  continue;
 }

This `continue` and the other one change the behaviour of this function.  
Without this some additional conditions are also checked after the `else if` 
block. Shouldn't we preserve the old behaviour?



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:390
   for (const Type *T : TypesToDelete)
-ThrownExceptions.erase(T);
+ThrownExceptions.erase({T, SourceLocation()});
 

This line makes me wonder if it's worth using a `map` instead of a `set` for 
`ThrownExceptions`. You could map the type to the location. I mean technically, 
that happens now too, but not with the appropriate data structure. 

Also I wonder what happens if a function can throw the same type from multiple 
locations. E.g.:
```lang=c++
void foo(int x) {
  if(x == 0) 
throw 1;
  
  if(x == 1)
throw 2;
}
```
Here only the last location will be preserved, so maybe mapping `Type` to 
`vector` would be better.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h:46
+
+  friend bool operator<(const ThrowInfo , const ThrowInfo ) noexcept {
+return L.ThrowType < R.ThrowType;

What is the reason behind declaring this operator and the one below as `friend`?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153298

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


[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-06-11 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:306
+
+  if (From->isMemberPointerType() || To->isMemberPointerType())
 return false;

PiotrZSL wrote:
> isuckatcs wrote:
> > isuckatcs wrote:
> > > Please cover this line with both positive and negative test cases.
> > > 
> > > Also upon looking up both [[ 
> > > https://www.open-std.org/jtc1/sc22/wg21/docs/standards | N4849 ]] (C++ 20 
> > > draft) and [[ https://github.com/cplusplus/draft/releases | N4917]] (C++ 
> > > 23 draft), they both say for qualification conversion that 
> > > 
> > > 
> > > > each P_i is ... pointer to member of class C_i of type, ...
> > > 
> > > Why are we not allowing them if the standard is at least C++ 20?
> > Please resolve this thread.
> Positive case is tested by throw_basefn_catch_derivedfn test and 
> throw_derivedfn_catch_basefn.
> Negative is covered by throw_basefn_catch_basefn.
> 
> For members there are tests throw_basem_catch_basem, 
> throw_basem_catch_derivedm, throw_derivedm_catch_basem that also covers this 
> correctly.
> 
> Tested this with GCC, and behavior is proper and independent to C++ standard.
> Tested this with GCC, and behavior is proper and independent to C++ standard.

I don't know how to deal with this tbh. In a static analyzer we usually want to 
consider what the standard says and not what a specific compiler implementation 
does, as the compiler can still be buggy, lack the proper support for the 
standard, etc.

Maybe we can add a FIXME here that explains, why this check is here and that 
it's the opposite of what the standard says. Then later if it causes issues, it 
will be easy to see why that happens.

@xazax.hun do you have a prefered method to deal with these situations in the 
analyzer? If there is one, we could also try it here.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148461

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


[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-06-07 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:306
+
+  if (From->isMemberPointerType() || To->isMemberPointerType())
 return false;

isuckatcs wrote:
> Please cover this line with both positive and negative test cases.
> 
> Also upon looking up both [[ 
> https://www.open-std.org/jtc1/sc22/wg21/docs/standards | N4849 ]] (C++ 20 
> draft) and [[ https://github.com/cplusplus/draft/releases | N4917]] (C++ 23 
> draft), they both say for qualification conversion that 
> 
> 
> > each P_i is ... pointer to member of class C_i of type, ...
> 
> Why are we not allowing them if the standard is at least C++ 20?
Please resolve this thread.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148461

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


[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-05-22 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

Please cover the changes in as much test cases as possible.




Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:151
 
+bool isFunctionTypeEqual(const FunctionType *From, const FunctionType *To) {
+  if (From == To)

The naming suggests that we check for equality, however in reality we check for 
convertability here. For example if one of the prototypes has exception spec 
and the other doesn't have one, this can still return true. 

Also instead of manually matching everything, you could take a look at 
`ODRHash` or `StructuralEquivalenceContext`. `ODRHash` for example doesn't take 
the `FunctionProtoType` into account when it generates the hashes AFAIK, so 
maybe you could use it to avoid these manual checks. I don't know if 
`StructuralEquivalenceContext` uses it or not, but you might consider taking a 
look at that too.

Maybe you can also consider pasting the appropriate section of the standard 
here, so that whenever someone reads the code they'll understand why are we 
doing what we're doing.

And please cover this function in positive and negative test cases too.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:185
 if (From->isFunctionType())
-  return To->getPointeeType() == From;
-
-return false;
-  }
-
-  if (To->isMemberFunctionPointerType()) {
-if (!From->isMemberFunctionPointerType())
-  return false;
-
+  return isFunctionTypeEqual(ToFunctionTypePtr,
+ From->castAs());

I think we want to call this function for member function pointers too.

The[[ https://github.com/cplusplus/draft/releases |  C++ N4917 draft ]] says 
the following:
```
7.3.14 Function pointer conversions [conv.fctptr]
A prvalue of type “pointer to noexcept function” can be converted to a prvalue 
of type “pointer to function”.
The result is a pointer to the function. A prvalue of type “pointer to member 
of type noexcept function” can
be converted to a prvalue of type “pointer to member of type function”. The 
result designates the member
function.
```



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:306
+
+  if (From->isMemberPointerType() || To->isMemberPointerType())
 return false;

Please cover this line with both positive and negative test cases.

Also upon looking up both [[ 
https://www.open-std.org/jtc1/sc22/wg21/docs/standards | N4849 ]] (C++ 20 
draft) and [[ https://github.com/cplusplus/draft/releases | N4917]] (C++ 23 
draft), they both say for qualification conversion that 


> each P_i is ... pointer to member of class C_i of type, ...

Why are we not allowing them if the standard is at least C++ 20?



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:160-162
+// FIXME: Two function pointers can differ in 'noexcept', but they still
+// should be considered to be same, now this triggers false-positive 
because
+// Type* != Type*.

PiotrZSL wrote:
> isuckatcs wrote:
> > Are you sure `noexcept` is stored in the type? The test case you modified 
> > `throw_noexcept_catch_regular()` tests this scenario and in that case the 
> > types seem to be the same even though one of the is noexcept an the other 
> > is not.
> > 
> > If the FIXME is valid the proper way would be to implement it in this patch.
> Yes I'm sure.
> C++11 - no warning
> C++14 - no warning
> C++17 - warning
> Looks like since C++17 noexcept is part of type.
> Initially I wanted only to enable tests under C++17/20.
> That's why "FIXME". I will look into this.
I did some investigation regarding this and if I dump the type of `void 
no_throw() noexcept` both with `-std=c++11` and `-std=c++17` I get the same 
result.
```
FunctionProtoType 'void (void) noexcept' cdecl
`-BuiltinType 'void'
```

I agree however that function pointer conversion was not properly implemented.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148461

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


[PATCH] D51057: [analyzer][UninitializedObjectChecker] Fixed dereferencing

2023-05-20 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.
Herald added subscribers: steakhal, manas, ASDenysPetrov, martong, gamesh411, 
dkrupp, donat.nagy, baloghadamsoftware.
Herald added a project: All.



Comment at: 
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:222
 
-if (isVoidPointer(DynT)) {
+  while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
+

@Szelethus @NoQ  @xazax.hun I ran into an issue related to this line.

Consider this test case:
```lang=c++
struct CyclicPointerTest1 {
  int *ptr; // expected-note{{object references itself 'this->ptr'}}
  int dontGetFilteredByNonPedanticMode = 0;

  CyclicPointerTest1() : ptr(reinterpret_cast()) {} // 
expected-warning{{1 uninitialized field}}
};
```

At this point, with the example above, the store looks like this:
```
{"kind": "Direct", "offset": 0, "extent": 64, value: 
{temp_object{CyclicPointerTest1, S915}.ptr,0 S64b,int}}
{"kind": "Direct", "offset": 64, "extent": 32, value: 0 S32b}
```

which means the values are the following:
```
V = {temp_object{CyclicPointerTest1, S915}.ptr,0 S64b,int}
R = Element{temp_object{CyclicPointerTest1, S915}.ptr,0 S64b,int}
DynT = PointerType ... 'int *'
```
when `State->getSVal(R, DynT)` is called, eventually 
`RegionStoreManager::getBinding()` will also be invoked, where because `R` is 
an `ElementRegion` we reach these lines:
```lang=c++
  if (const ElementRegion *ER = dyn_cast(R)) {
// FIXME: Here we actually perform an implicit conversion from the loaded
// value to the element type.  Eventually we want to compose these values
// more intelligently.  For example, an 'element' can encompass multiple
// bound regions (e.g., several bound bytes), or could be a subset of
// a larger value.
return svalBuilder.evalCast(getBindingForElement(B, ER), T, QualType{});
  }
```
What happens here is that we call `getBindingForElement(B, ER)` and whatever 
value it returns, we cast to `T`, which is `int *` in this example. The type of 
the element inside the `ER` however is an `int`, so the following lookup will 
be performed:
```
Binding being looked up: 
  {"kind": "Direct", "offset": 0, "extent": 32}

Store:
  {"kind": "Direct", "offset": 0, "extent": 64 value: 
{temp_object{CyclicPointerTest1, S915}.ptr,0 S64b,int}}
  {"kind": "Direct", "offset": 64, "extent": 32 value: 0 S32b}
```
Since extents are ignored by default, the returned value will be 
`{temp_object{CyclicPointerTest1, S915}.ptr,0 S64b,int}}`, which is 
indeed a pointer, however I'm not sure that the lookup here is actually 
correct. 

As far as I understand, we want to lookup an `int` element based on it's region 
and we receive a pointer value. Is it because I misunderstand something, or 
it's an actual issue that is hid by how region store works right now? 


Repository:
  rL LLVM

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

https://reviews.llvm.org/D51057

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


[PATCH] D46415: [analyzer] pr36458: Fix retrieved value cast for symbolic void pointers.

2023-05-20 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.
Herald added subscribers: steakhal, manas, ASDenysPetrov, martong, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho.
Herald added a project: All.



Comment at: test/Analysis/casts.c:166
+  *x = 1;
+  clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+}

@NoQ why is this true for both x86_64 and i386? 

On x86_64 `sizeof(int *) == 8` and `sizeof(int) == 4`. This means that `(*((int 
*)())) = (int)` writes to the lower 4 bytes of `x` and leaves the upper 4 
bytes uninitialized. See this [[ https://godbolt.org/z/E6ocPE9Gr | godbolt 
example ]]. If I compile and run this function on my machine it segfaults.

On i386 `sizeof(int *) == 4` and `sizeof(int) == 4`, so on that platform this 
example is correct. See on [[https://godbolt.org/z/GaW73Wod7 | godbolt]].

In the x86_64 case don't we want the analyzer to report a warning a instead, as 
on that platform `u` is only partially initialized?


Repository:
  rC Clang

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

https://reviews.llvm.org/D46415

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


[PATCH] D124349: [analyzer] Get direct binding for specific punned case

2023-05-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang/test/Analysis/array-punned-region.c:23
+  BITFIELD_CAST *pff = 
+  int a = *((int *)pff + 2); // expected-warning{{Assigned value is garbage or 
undefined [core.uninitialized.Assign]}}
+  return a;

@steakhal @martong @NoQ 

Isn't this actually a false positive here?

`(int *)pff + 2` points to `ff.b[1]`, which is initialized to `0`. 

https://godbolt.org/z/Gh8a4aMe8







Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124349

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


[PATCH] D148458: [clang-tidy][NFC] Split bugprone-exception-escape tests

2023-05-02 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> Yes, throw specifier is removed in C++17, split allows to support C++17 and 
> above in main test file

A lot of our test files uses macros to differentiate between specific C++ 
standards, why not do that here too?
There are only a few occurences of functions with `throw()`.

  #if __cplusplus  < 201703L
// put functions with throw() here
  #endif



> Those tests never tested caching (not failing if you disable cache)

They weren't tested explicitly, but it still happened in the background by 
default.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148458

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


[PATCH] D148458: [clang-tidy][NFC] Split bugprone-exception-escape tests

2023-04-30 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

Do we really want to split these two functions apart to different test files? 
Is this really NFC?

The way `ExceptionEscapeCheck` works is that it creates an `ExceptionAnalyzer` 
upon instantiation.

//By the way upon looking at the constructor of the check I see that 
std::bad_alloc is always ignored.
Maybe we want to turn this into an option, so that users can enable it if they 
want.//

`ExceptionAnalyzer` caches functions based on their `FunctionDecl *` in
`std::map FunctionCache;`.

The `FunctionDecl` lives in the AST of the translation unit, so the same 
function declaration in two
different translation units will have different `FunctionDecl *`s. //Maybe 
`ODRHash` would be more
suitable to be used as key in the map.//

By moving throwing functions into a different TU than non-throwing functions, I 
think the correctness
of the caching inside `ExceptionAnalyzer` no longer will be tested properly. 
Maybe this is something
we want to preserve.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148458

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


[PATCH] D148458: [clang-tidy][NFC] Split bugprone-exception-escape tests

2023-04-30 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

Do we really want to split these two functions apart to different test files? 
Is this really NFC?

The way `ExceptionEscapeCheck` works is that it creates an `ExceptionAnalyzer` 
upon instantiation.

//By the way upon looking at the constructor of the check I see that 
`std::bad_alloc` is always ignored.
Maybe we want to turn this into an option, so that users can enable it if they 
want.//

`ExceptionAnalyzer` caches functions based on their `FunctionDecl *` in 
`std::map FunctionCache;`.

The `FunctionDecl` lives in the AST of the translation unit, so the same 
function declaration in two 
different translation units will have different `FunctionDecl *`s. //Maybe 
`ODRHash` would be more
suitable to be used as key in the map.//

By moving throwing functions into a different TU than non-throwing functions I 
think the caching


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148458

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


[PATCH] D148462: [clang-tidy] Ignore declarations in bugprone-exception-escape

2023-04-28 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs accepted this revision.
isuckatcs added a comment.
This revision is now accepted and ready to land.

I think there's no point of holding back this patch. Even though I'm not 100% 
sure we want this change, I say let's merge it and see how our users react.

It's a one line change anyway, so if we receive a lot of complaints, it will be 
easy to revert.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148462

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


[PATCH] D148462: [clang-tidy] Ignore declarations in bugprone-exception-escape

2023-04-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> No because indirectly_recursive called from recursion_helper is noexcept, so 
> there will be std::terminate called.

I missed that in `noexcept` functions thrown exceptions are not propagated. In 
this case I agree that `recursion_helper()` shouldn't emit a warning.

As for the forward declaration part I think we should wait and see what others 
think about it.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148462

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


[PATCH] D148462: [clang-tidy] Ignore declarations in bugprone-exception-escape

2023-04-17 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> "The warning was emitted at every occurence of the function. It might be 
> confusing if it's only emitted for the definition."
> Why ? Issue is in definition, not declaration.

For me it would be confusing, because the forward declaration is naming the 
same function as the definition.
If I see that something is reported for the definition but not for the 
declaration I might think it's a bug in the 
tool, like once there is a problem with the function and the other time there 
isn't. Note that this particular 
warning is reported for the function and not for something inside the 
definition.

Also we have cross translation unit analysis, though I'm not sure this 
particular checker works there too.
Assuming it does, what happens if I forward declare the function in one 
translation unit and define it in
a different one? With this change the warning will only be output in the 
translation unit,the function is
defined in and this might silently hide some other problems in the TU the 
function is forward declared in.

> recursion_helper does not throw, it crashes.

Technically the exception is propagated through the function until a handler is 
found that catches it.

> Example with indirectly_recursive & recursion_helper behave in same way, only 
> difference is that warning is emitted only for definition.

Please add a test case for this regardless of the behaviour to see that the 
checker handles exception propagation.

> This is other bug that I'm fixing (not under this patch) related to properly 
> handling noexcept keyword.

I'm not sure what you mean by bug here.

  int recursion_helper(int n) noexcept {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in
// function 'recursion_helper' which should not throw exceptions
indirectly_recursive(n);
  }
  
  int indirectly_recursive(int n) noexcept {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in
// function 'indirectly_recursive' which should not throw exceptions
if (n == 0)
  throw n;
return recursion_helper(n);
  }

Because `recursion_helper()` propagates the thrown object it makes sense to 
emit a warning for that too.
Also because the warning is emitted upon every propagation it is easy to trace 
where the exception actually
comes from. Think of it like a stack trace for example.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148462

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


[PATCH] D148462: [clang-tidy] Ignore declarations in bugprone-exception-escape

2023-04-16 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

I think the original behaviour was fine. The warning was emitted at every 
occurence of the function. It might be confusing if it's only emitted for the 
definition.

Also what happens in the following scenario:

  int indirectly_recursive(int n) noexcept;
  
  int recursion_helper(int n) noexcept {
indirectly_recursive(n);
  }

We know that `indirectly_recursive(int n)` throws when it shouldn't and that 
means `recursion_helper(int n)` will also throw when it shouldn't either.

Is it reported properly with this change?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148462

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


[PATCH] D148461: [clang-tidy] Support C++17/20 in bugprone-exception-escape

2023-04-16 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:160-162
+// FIXME: Two function pointers can differ in 'noexcept', but they still
+// should be considered to be same, now this triggers false-positive 
because
+// Type* != Type*.

Are you sure `noexcept` is stored in the type? The test case you modified 
`throw_noexcept_catch_regular()` tests this scenario and in that case the types 
seem to be the same even though one of the is noexcept an the other is not.

If the FIXME is valid the proper way would be to implement it in this patch.



Comment at: 
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp:414
   throw 
-} catch(int (*)()) {
+} catch(int (*)() noexcept) {
 }

The name of the function suggests that we throw a noexcept function pointer and 
catch a non-noexcept one. Please don't change it.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148461

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


[PATCH] D143328: [analyzer] Remove the loop from the exploded graph caused by missing information in program points

2023-03-03 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGd65379c8d476: [analyzer] Remove the loop from the exploded 
graph caused by missing… (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D143328

Files:
  clang/include/clang/Analysis/ProgramPoint.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  
clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
  clang/test/Analysis/PR60412.cpp
  clang/test/Analysis/analysis-after-multiple-dtors.cpp
  clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp

Index: clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp
===
--- clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp
+++ clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp
@@ -89,11 +89,14 @@
 
 CallEventManager  = Eng.getStateManager().getCallEventManager();
 CallEventRef<> Call = [=, ]() -> CallEventRef {
+  CFGBlock::ConstCFGElementRef ElemRef = {SFC->getCallSiteBlock(),
+  SFC->getIndex()};
   if (std::is_base_of::value)
-return CEMgr.getCall(E, State, SFC);
+return CEMgr.getCall(E, State, SFC, ElemRef);
   if (std::is_same::value)
 return CEMgr.getCXXConstructorCall(cast(E),
-   /*Target=*/nullptr, State, SFC);
+   /*Target=*/nullptr, State, SFC,
+   ElemRef);
   llvm_unreachable("Only these expressions are supported for now.");
 }();
 
Index: clang/test/Analysis/analysis-after-multiple-dtors.cpp
===
--- /dev/null
+++ clang/test/Analysis/analysis-after-multiple-dtors.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct Test {
+  Test() {}
+  ~Test();
+};
+
+int foo() {
+  struct a {
+  // The dtor invocation of 'b' and 'c' used to create
+  // a loop in the egraph and the analysis stopped after
+  // this point.
+Test b, c;
+  } d;
+  return 1;
+}
+
+int main() {
+  if (foo()) {
+  }
+
+  int x;
+  int y = x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+  (void)y;
+}
Index: clang/test/Analysis/PR60412.cpp
===
--- /dev/null
+++ clang/test/Analysis/PR60412.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.deadcode.UnreachableCode -verify %s
+// expected-no-diagnostics
+
+struct Test {
+  Test() {}
+  ~Test();
+};
+
+int foo() {
+  struct a {
+Test b, c;
+  } d;
+  return 1;
+}
+
+int main() {
+  if (foo()) return 1; // <- this used to warn as unreachable
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -148,8 +148,8 @@
   ExplodedNode *Pred,
   ExplodedNodeSet ) {
   CallEventManager  = getStateManager().getCallEventManager();
-  CallEventRef Msg =
-CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+  CallEventRef Msg = CEMgr.getObjCMethodCall(
+  ME, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
 
   // There are three cases for the receiver:
   //   (1) it is definitely nil,
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -610,8 +610,8 @@
   // Get the call in its initial state. We use this as a template to perform
   // all the checks.
   CallEventManager  = getStateManager().getCallEventManager();
-  CallEventRef<> CallTemplate
-= CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
+  CallEventRef<> CallTemplate = CEMgr.getSimpleCall(
+  CE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
 
   // Evaluate the function call.  We try each of the checkers
   // to see if the can evaluate the function call.
@@ -837,7 +837,8 @@
   State = bindReturnValue(Call, 

[PATCH] D144977: [analyzer] Fix of the initialization list parsing.

2023-03-02 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp:432
+  const LocationContext *LCtx = C.getLocationContext();
+  SVal SrcVal = State->getSVal(Buffer.Expression, LCtx);
+  QualType SourceValType = SrcVal.getType(C.getASTContext());

I did some digging into the other parts of the checker and I'm not sure that we 
guarantee that every time
 `AccessKind::read` is performed, `Buffer` will be the source and not the 
destination. We just check
an arbitrary element in an arbitrary buffer.

For example if you look at `CStringChecker::evalMemcmp()`, it has the following 
content:
```lang=c++
  // int memcmp(const void *s1, const void *s2, size_t n);
  CurrentFunctionDescription = "memory comparison function";

  AnyArgExpr Left = {CE->getArg(0), 0};
  AnyArgExpr Right = {CE->getArg(1), 1};
  SizeArgExpr Size = {CE->getArg(2), 2};
  
  ...

   State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK);
   State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK);
```

`CStringChecker::CheckBufferAccess()` will eventually call your code and in the 
first case, you will deduce the type
based on the destination and not the source. This logic might needs to be moved 
to a higher level function unless
you can guarantee that always the correct type will be deduced. Or if the 
intention is not to deduce some information
about the source array in a `memcpy` like function, it might be clearer to 
rename this variable to something that 
doesn't have either src or dst in it.



Comment at: clang/lib/StaticAnalyzer/Core/RegionStore.cpp:564-565
 
-  SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R);
+  SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R,
+QualType T = QualType());
 

earnol wrote:
> NoQ wrote:
> > There's a bit of a confusing tradition here, mostly documented in my 
> > unsorted long rants in earlier patches, let me try to explain.
> > 
> > `RegionStore` was built to recognize that type punning is a thing. You can 
> > write a value while treating the target location/region R as if it's 
> > supposed to hold a value of type T₁ and then load the value by treating the 
> > same memory location as if it holds a value of type T₂≠T₁. You can achieve 
> > it by having unions or by reinterpret-casting pointers. RegionStore needs 
> > to handle this reasonably well, no matter what T₁ and T₂ are. Even though 
> > strict aliasing rules put some restrictions on what T₁ and T₂ could be, the 
> > analyzer is still required to work correctly when the programmer actively 
> > relies on `-fno-strict-aliasing`.
> > 
> > On top of that, every `MemRegion` R can have a type T(R) associated with it 
> > (accessed as `R->getValueType()`). This was probably a bad idea from the 
> > start, for at least three reasons:
> > - Again, type punning exists. Just because the region has a type, doesn't 
> > mean it holds a value of that type.
> > - In many cases we deal with regions with no associated type. For example, 
> > an unknown (symbolic) pointer of type `void *` definitely points to a 
> > certain memory region (we call it `SymbolicRegion`), but the type of the 
> > object in that region is most certainly not `void`.
> > - Finally, polymorphism: C++ pointers to base class type may actually point 
> > to objects of any derived class type. Not only the specific derived type is 
> > often unknown, but also the true derived type may not be necessarily 
> > available in the current translation unit, so we often don't have the AST 
> > to even describe it.
> > 
> > This makes the type of `MemRegion` largely immaterial and hard to rely 
> > upon. Which is why `State->getSVal(R)` accepts an optional parameter T₂ 
> > which may be different from both T₁ and T(R).
> > 
> > However, after some back-and-forth bugfixing, we've settled on agreeing 
> > that when T(R) exists, it doesn't make sense to pass any T₂ into 
> > `State->getSVal(R, T₂)` other than `T₂=T(R)`. Basically, if R already 
> > carries a type, then you don't have a good reason to pass a different type 
> > as T₂, given that you can always pass a different region R' instead, that 
> > represents the same memory location as R but has the desired value type T₂. 
> > So T₂ is only required when it's fundamentally impossible to discover the 
> > type from the region. So in order to eliminate the confusion, `getSVal()` 
> > tries to get rid of the extra parameter `T₂` as early as possible, and have 
> > a single source of truth going further down the call stack.
> > 
> > I honestly no longer think this is a healthy strategy long-term. Ideally I 
> > think we should get rid of region types entirely; they cause more problems 
> > than they solve. But that's a much bigger change, and I'm worried that a 
> > partial step in this direction, not supported by any 

[PATCH] D144977: [analyzer] Fix of the initialization list parsing.

2023-03-02 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/RegionStore.cpp:1787
+  if (V &&
+  (!targetType->isStructureOrClassType() && !targetType->isUnionType()))
 return *V;

I assume `targetType` is the type we want to interpret the region as. Below 
this condition we seem to work with arrays. If `targetType` is an array, then 
we return something here instead of going further and returning something else 
we probably want. 

Why aren't we going further in that case?



Comment at: clang/lib/StaticAnalyzer/Core/RegionStore.cpp:1869
+  // if we are here we have struct or union?
+  if (!VarT->isStructureType()) {
+// TODO: support other options like unions or arrays or VLAs

What about classes? 

```
class A { 
public:
  int x;
};

struct B {
  int x;
}
```

`A` and `B` are technically the same, but `A` will fall into the true branch, 
`B` will fall into the false branch.



Comment at: clang/lib/StaticAnalyzer/Core/RegionStore.cpp:1891
+ElemExpr = IL->getInit(Idx);
+std::optional ConstVal = svalBuilder.getConstantVal(ElemExpr);
+// if there is no value create a zero one

This crashes if `ElemExpr` is a `nullptr`.



Comment at: clang/lib/StaticAnalyzer/Core/RegionStore.cpp:1914
+  }
+  RecIter++;
+  continue;

Consider moving this into the for loop to avoid confusion.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D144977

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


[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-26 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG0303eafcb346: [clang-tidy] handle exceptions properly in 
`ExceptionAnalyzer` (authored by isuckatcs).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,265 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public 

[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-24 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> As long as you don't need the library (which, if you have a preprocessed file 
> or a test, should absolutely not be necessary), you can just do the -cc1 
> option of -triple , or the non- -cc1 option of -target .
>
> Both use basically the same options list, and that bot seems to be 
> "aarch64-unknown-linux-gnu" (see under cmake-stage-1 the llvm host triple)

Thanks for this hint, it helped a lot!


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-24 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 500299.
isuckatcs added a comment.

Fixed the timed out test


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,265 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public aliasedD {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be 

[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-24 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

I'm not sure if I can run clang targetting AArch64 on an X86 machine but I 
tried it regardless and I get compiler errors on startup, so I can't debug this 
issue at the moment. 
Besided that I don't know why it times out on that buildbot. The build job that 
is performed on every revision passed without any problems.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-24 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG6b0cf1e15f8f: [clang-tidy] handle exceptions properly in 
`ExceptionAnalyzer` (authored by isuckatcs).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,229 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public 

[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-23 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 40.
isuckatcs added a comment.

fixed failing test


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,229 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public aliasedD {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown 

[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-22 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

@xazax.hun can we merge this or should we wait for someone else's approval too?


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly in `ExceptionAnalyzer`

2023-02-22 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 499453.
isuckatcs added a comment.

Added tests for function pointers


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,229 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public aliasedD {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception 

[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-02-21 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:66
+   CXXBasePath &) {
+if (BS->getType()->getAsCXXRecordDecl() == BaseClass &&
+BS->getAccessSpecifier() == AS_public) {

xazax.hun wrote:
> Will this work with typedefs and other sugar or do you need to get the 
> canonical type here? I do not see test coverage for that scenario.
In this specific case it will work because `getAsCXXRecordDecl()` comes from 
the `CanProxyBase` utility class, which already canonicalizes the type, but I 
agree that it's not intuitive and unreadable. 

I also modified the current test cases to have aliases.


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-02-21 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 499234.
isuckatcs marked an inline comment as done.
isuckatcs added a comment.

Addressed comments


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,173 @@
   }
 }
 
+void throw_derived_alias_catch_base() noexcept {
+  using alias = derived;
+
+  try {
+throw alias();
+  } catch(base &) {
+  }
+}
+
+void throw_derived_catch_base_alias() noexcept {
+  using alias = base;
+
+  try {
+throw derived();
+  } catch(alias &) {
+  }
+}
+
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+
+// The following alias hell is deliberately created for testing.
+using aliasedA = A;
+class C : protected aliasedA {};
+
+typedef aliasedA moreAliasedA;
+class D : public moreAliasedA {};
+
+using moreMoreAliasedA = moreAliasedA;
+using aliasedD = D;
+class E : public moreMoreAliasedA, public aliasedD {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: 

[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-02-21 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs marked an inline comment as done.
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:84
+
+QualType getPointeeOrArrayElementQualType(QualType T) {
+  if (T->isAnyPointerType())

xazax.hun wrote:
> What about `Type::getPointeeOrArrayElementType`?
I didn't use that for two reasons.


  # It returns a `Type` instead of a `QualType`, so the qualifiers are lost.
  # It uses `Type::getBaseElementTypeUnsafe()` for arrays, which discards all 
array types for multi-dimensional arrays. E.g.: `int[2][3]` will become `int` 
and I want to take only one step backward, so I want `int[2][3]` to become 
`int[2]`.

I was thinking about inserting this function into `Type` or `QualType` but 
because of the different behaviour with arrays I decided to use it as a helper 
in this source file only instead. I agree that the naming is confusing a bit, 
but it is literally what the function does.


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-02-19 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

ping


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-01-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 492215.

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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,84 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+class C : protected A {};
+class D : public A {};
+class E : public A, public D {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions
+  try {
+B b;
+throw b; 
+  } catch(A) {
+  }
+}
+
+void throw_derived_catch_base_private_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private_ptr' which should not throw exceptions
+  try {
+B b;
+throw  
+  } catch(A *) {
+  }
+}
+
+void throw_derived_catch_base_protected() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 

[PATCH] D135495: [clang-tidy] handle exceptions properly `ExceptionAnalyzer`

2023-01-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 492214.
isuckatcs added a comment.

applied clang-format


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -102,7 +102,6 @@
 }
 
 void throw_catch_pointer_c() noexcept {
-  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_c' which should not throw exceptions
   try {
 int a = 1;
 throw 
@@ -110,7 +109,6 @@
 }
 
 void throw_catch_pointer_v() noexcept {
-  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_v' which should not throw exceptions
   try {
 int a = 1;
 throw 
@@ -118,13 +116,57 @@
 }
 
 void throw_catch_pointer_cv() noexcept {
-  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_cv' which should not throw exceptions
   try {
 int a = 1;
 throw 
   } catch(const volatile int *) {}
 }
 
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
 void throw_c_catch_pointer() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
   try {
@@ -191,7 +233,6 @@
 }
 
 void throw_derived_catch_base_ptr_c() noexcept {
-  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr_c' which should not throw exceptions
   try {
 derived d;
 throw  
@@ -209,6 +250,66 @@
   }
 }
 
+class A {};
+class B : A {};
+class C : protected A {};
+class D : public A {};
+class E : public A, public D {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions
+  try {
+B b;
+throw b; 
+  } catch(A) {
+  }
+}
+
+void throw_derived_catch_base_private_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private_ptr' which should not throw exceptions
+  try {
+B b;
+throw  
+  } catch(A *) {
+  }
+}
+
+void throw_derived_catch_base_protected() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected' which should not throw exceptions
+  try {
+C c;
+throw c; 
+  } catch(A) {
+  }
+}
+
+void throw_derived_catch_base_protected_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected_ptr' which should not throw exceptions
+  try {
+C c;
+throw  
+  } catch(A *) {
+  }
+}
+
+void throw_derived_catch_base_ambiguous() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous' which should not throw exceptions
+  try {
+E e;
+throw e; 
+  } catch(A) {
+  }
+}
+
+void throw_derived_catch_base_ambiguous_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous_ptr' which should not throw exceptions
+  try {
+E e;
+throw e; 
+  } catch(A) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 492115.
isuckatcs added a comment.

- Reverted the changes related to clang itself
- Added more checks for exception handling
- `isQualificationConvertiblePointer` is now iterative, not recursive
- Added more test cases


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,84 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
+class A {};
+class B : A {};
+class C : protected A {};
+class D : public A {};
+class E : public A, public D {};
+
+void throw_derived_catch_base_private() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions
+  try {
+B b;
+throw b; 
+  } catch(A) {
+  }
+}
+
+void throw_derived_catch_base_private_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private_ptr' which should not 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

This patch got a little bit out of control at the last revision, so I decided 
to remove every change from clang and add everything to the `ExceptionAnalyzer` 
only.
The reason for that is with exceptions we have less conversions to check than 
we have inside the compiler, which can lead to confusion.

For example:

  class A {};
  class B : public A {};
  
  int A::* pa;
  int B::* pb = pa; <-- valid outside of exception handler, invalid in 
exception handler

We can have the conversion `int B::* pb = pa;` because of `7.3.12 
Pointer-to-member conversions`, which is by standard not performed when an 
exception needs to be caught.
See godbolt . (MSVC does catch `A::*` with a 
`B::*` handler for some reason, maybe I miss some flag)

For the above reason, sadly we can't test the changes the way you suggested 
@xazax.hun, like checking if the assigment compiles or not.


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-24 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> E.g., instead of asserting true/false, checking if the assignment would 
> compile.

This is actually biased. If the result of the compilation is different from the 
result we get from this function, it can also mean a bug in the compiler.

Take a look at this example on godbolt . The 
snippet here is only valid from C++20, but GCC compiles it even in C++17.




Comment at: clang/include/clang/AST/ASTContext.h:2828
 
+  static bool isQualificationConvertiblePointer(QualType From, QualType To,
+LangOptions LangOpts,

erichkeane wrote:
> Please document what this is doing...
This was actually documented, but at the definition. My bad.



Comment at: clang/include/clang/AST/ASTContext.h:2829
+  static bool isQualificationConvertiblePointer(QualType From, QualType To,
+LangOptions LangOpts,
+unsigned CurrentLevel = 0,

erichkeane wrote:
> Why is this static if you need lang-opts?  This should be retrieved by the 
> ASTContext itself.
By taking the language options externally, we might be able to produce more 
descriptive warning messages in the future. 
E.g.: `This conversion is only valid from C++20`, etc.

Also calling this function doesn't depend on `ASTContext` any other way, so it 
can be called even if we don't have access to the `ASTContext` for some reason.

I don't know however if it makes sense to worry about these uses at all.




Comment at: clang/include/clang/AST/ASTContext.h:2831
+unsigned CurrentLevel = 0,
+bool IsToConstSoFar = false);
+

erichkeane wrote:
> What does this name mean here?  It isn't clear to me.
This is documented at the use site of this variable. I couldn't come up with a 
better name for it.
```lang=c++
  // If at the current level To is more cv-qualified than From [...],
  // then there must be a 'const' at every single level (other than level zero)
  // of To up until the current level
  bool MoreCVQualified =
  To.getQualifiers().isStrictSupersetOf(From.getQualifiers());
  if (MoreCVQualified)
Convertible &= IsToConstSoFar;
```



Comment at: clang/include/clang/AST/Type.h:7364
+  else if (isArrayType())
+return getAsArrayTypeUnsafe()->getElementType();
+

erichkeane wrote:
> This changes the meaning here, and this is a commonly used thing.  Why are 
> you doing this?
I missed that it changes the meaning. Though I need this use case, so I 
reverted these changes and created a static global function instead.



Comment at: clang/lib/AST/ASTContext.cpp:13465
+//
+// The function should only be called in C++ mode.
+bool ASTContext::isQualificationConvertiblePointer(QualType From, QualType To,

erichkeane wrote:
> Perhaps this should be asserted on!
I personally don't want to assert it, as it won't crash in C mode, it's just 
the fact that some rules here are different in C.
E.g.  (from [[ 
https://en.cppreference.com/w/cpp/language/implicit_conversion#Qualification_conversions
 | cppreference ]]):
```lang=c++
char** p = 0;
const char* const * p2 = p; // error in C, OK in C++
```

(The example above is checked properly but I didn't dig deeper into the other C 
rules, that's why I said that it shouldn't be called)



Comment at: clang/lib/AST/ASTContext.cpp:13466
+// The function should only be called in C++ mode.
+bool ASTContext::isQualificationConvertiblePointer(QualType From, QualType To,
+   LangOptions LangOpts,

erichkeane wrote:
> I find myself shocked we don't have something like this already, but what do 
> we mean by 'qualification convertible'?  Is that a term of art I'm missing?
> 
> 
I didn't come up with this name. It is what this conversion is called by the 
standard.

It is §7.3.5 in [[ 
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4849.pdf | N4849 ]] 
and you can also find it under the same name on [[ 
https://en.cppreference.com/w/cpp/language/implicit_conversion#Qualification_conversions
 | cppreference ]].





Comment at: clang/lib/AST/ASTContext.cpp:13485
+
+if (!To->isPointerType() ||
+!(From->canDecayToPointerType() || From->isPointerType()))

erichkeane wrote:
> I would expect at least the 'to' here to assert as well.  Passing a 'not 
> pointer' as the 'two' when youre testing 'convertible pointer' is odd and a 
> mistake?
Well, technically `MemberPointerType` is also accepted and it's not derived 
from `PointerType`. Though I added an assertion here, but I left the check so 
that we don't crash in release mode.



Comment at: 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-24 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 491910.
isuckatcs marked 9 inline comments as done.
isuckatcs added a comment.

Addressed comments
Updated tests


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
  clang/include/clang/AST/ASTContext.h
  clang/lib/AST/ASTContext.cpp
  clang/unittests/AST/CMakeLists.txt
  clang/unittests/AST/PointerConversionTest.cpp

Index: clang/unittests/AST/PointerConversionTest.cpp
===
--- /dev/null
+++ clang/unittests/AST/PointerConversionTest.cpp
@@ -0,0 +1,285 @@
+//===- unittests/AST/PointerConversionTest.cpp - Pointer conversion tests -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file contains tests for ASTContext::isQualificationConvertiblePointer(),
+// which checks if one pointer can be converted to the other.
+//
+//===--===//
+
+#include "gtest/gtest.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "clang/Frontend/FrontendActions.h"
+
+namespace clang {
+
+auto DeclMatcher = ast_matchers::declaratorDecl(ast_matchers::unless(ast_matchers::parmVarDecl())).bind("id");
+
+class TypeExtractor : public ast_matchers::MatchFinder::MatchCallback {
+  int MatchCount = 0;
+
+public:
+  QualType T1, T2;
+
+  void run(const ast_matchers::MatchFinder::MatchResult ) override {
+if (const auto *VD = Result.Nodes.getNodeAs("id")) {
+  if (MatchCount == 0)
+T1 = VD->getType();
+  else {
+assert(VD->hasInit() && "Second declaration must be initialized because of compiler error messages.");
+T2 = VD->getType();
+  }
+
+  ++MatchCount;
+} else if (const auto *FD =
+   Result.Nodes.getNodeAs("id")) {
+  if (MatchCount == 0)
+T1 = FD->getType();
+  else
+T2 = FD->getType();
+
+  ++MatchCount;
+}
+  }
+};
+
+void prepareArgAndLangOptionsFromStd(LangStandard Std, std::string , LangOptions ) {
+  Arg = "-std=";
+  Arg += Std.getName();
+
+  std::vector Tmp;
+  LangOptions::setLangDefaults(Opts, Std.Language, {}, Tmp,
+   Std.getLangKind(Std.getName()));
+}
+
+bool CheckIfCompiles(llvm::StringRef Source, LangStandard Std) {
+  std::string StdArg;
+  LangOptions Opts;
+  prepareArgAndLangOptionsFromStd(Std, StdArg, Opts);
+
+  return tooling::runToolOnCodeWithArgs(tooling::newFrontendActionFactory().get()->create(), Source, {StdArg});
+}
+
+bool CheckConvertible(llvm::StringRef Source, LangStandard Std) {
+  TypeExtractor Extractor;
+  ast_matchers::MatchFinder Finder;
+
+  Finder.addMatcher(DeclMatcher, );
+  std::unique_ptr Factory(
+  tooling::newFrontendActionFactory());
+
+  std::string StdArg;
+  LangOptions Opts;
+  prepareArgAndLangOptionsFromStd(Std, StdArg, Opts);
+
+  tooling::runToolOnCodeWithArgs(Factory->create(), Source, {StdArg});
+
+  return ASTContext::isQualificationConvertiblePointer(Extractor.T1,
+   Extractor.T2, Opts);
+}
+
+#define TEST_CONVERSION(ASSERTION, SNIPPET, LANG) { const char* Snippet = SNIPPET; \
+  ASSERTION(CheckConvertible(Snippet, LANG));  \
+  ASSERTION(CheckIfCompiles(Snippet, LANG)); }
+
+const LangStandard & StdCXX98 = LangStandard::getLangStandardForKind(LangStandard::lang_cxx98);
+const LangStandard & StdCXX17 = LangStandard::getLangStandardForKind(LangStandard::lang_cxx17);
+const LangStandard & StdCXX20 = LangStandard::getLangStandardForKind(LangStandard::lang_cxx20);
+const LangStandard & StdCXX2b = LangStandard::getLangStandardForKind(LangStandard::lang_cxx2b);
+
+
+TEST(QualificationConversion, builtin) {
+  TEST_CONVERSION(ASSERT_FALSE, R"cpp(char **p; const char **p1 = p;)cpp", StdCXX98);
+}
+
+TEST(QualificationConversion, builtin2) {
+  TEST_CONVERSION(ASSERT_TRUE, R"cpp(char **p; const char* const * p2 = p;)cpp", StdCXX98);
+}
+
+TEST(QualificationConversion, builtin3) {
+  TEST_CONVERSION(ASSERT_TRUE, R"cpp(char **p; volatile char * const * p3 = p;)cpp", StdCXX98);
+}
+
+TEST(QualificationConversion, builtin4) {
+  TEST_CONVERSION(ASSERT_TRUE, R"cpp(char **p; volatile const char* const* p4 = p;)cpp", StdCXX98);
+}
+
+TEST(QualificationConversion, builtin5) {
+  TEST_CONVERSION(ASSERT_TRUE, R"cpp(double *a[2][3]; double const * const (*ap)[3] = a;)cpp", StdCXX98);
+}
+

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-17 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 489940.
isuckatcs added a comment.

- Renamed and moved `isMultiLevelConvertiblePointer()` to `ASTContext`
- Added a unit test to test the said function


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/AST/Type.h
  clang/lib/AST/ASTContext.cpp
  clang/unittests/AST/CMakeLists.txt
  clang/unittests/AST/PointerConversionTest.cpp

Index: clang/unittests/AST/PointerConversionTest.cpp
===
--- /dev/null
+++ clang/unittests/AST/PointerConversionTest.cpp
@@ -0,0 +1,245 @@
+//===- unittests/AST/PointerConversionTest.cpp - Pointer conversion tests -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file contains tests for ASTContext::isQualificationConvertiblePointer(),
+// which checks if one pointer can be converted to the other.
+//
+//===--===//
+
+#include "gtest/gtest.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+
+auto DeclMatcher = ast_matchers::declaratorDecl().bind("id");
+
+class TypeExtractor : public ast_matchers::MatchFinder::MatchCallback {
+  int MatchCount = 0;
+
+public:
+  QualType T1, T2;
+
+  void run(const ast_matchers::MatchFinder::MatchResult ) override {
+if (const auto *VD = Result.Nodes.getNodeAs("id")) {
+  if (MatchCount == 0)
+T1 = VD->getType();
+  else
+T2 = VD->getType();
+
+  ++MatchCount;
+} else if (const auto *FD =
+   Result.Nodes.getNodeAs("id")) {
+  if (MatchCount == 0)
+T1 = FD->getType();
+  else
+T2 = FD->getType();
+
+  ++MatchCount;
+}
+  }
+};
+
+bool CheckConvertible(llvm::StringRef Source, LangStandard Std) {
+  TypeExtractor Extractor;
+  ast_matchers::MatchFinder Finder;
+
+  Finder.addMatcher(DeclMatcher, );
+  std::unique_ptr Factory(
+  tooling::newFrontendActionFactory());
+
+  std::string StdArg = "-std=";
+  StdArg += Std.getName();
+
+  LangOptions Opts;
+  std::vector Tmp;
+  LangOptions::setLangDefaults(Opts, Std.Language, {}, Tmp,
+   Std.getLangKind(Std.getName()));
+
+  tooling::runToolOnCodeWithArgs(Factory->create(), Source, {StdArg});
+
+  return ASTContext::isQualificationConvertiblePointer(Extractor.T1,
+   Extractor.T2, Opts);
+}
+
+TEST(QualificationConversion, builtin) {
+  ASSERT_FALSE(CheckConvertible(
+  R"cpp(char **p; const char **p1;)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin2) {
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(char **p; const char* const * p2;)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin3) {
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(char **p; volatile char * const * p3;)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin4) {
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(char **p; volatile const char* const* p4;)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin5) {
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(double *a[2][3]; double const * const (*ap)[3];)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin6) {
+  ASSERT_FALSE(CheckConvertible(
+  R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx17)));
+
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx20)));
+
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx2b)));
+}
+
+TEST(QualificationConversion, builtin7) {
+  ASSERT_TRUE(CheckConvertible(
+  R"cpp(int arr[3]; int *p;)cpp",
+  LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin8) {
+  ASSERT_FALSE(CheckConvertible(
+  R"cpp(int arr[3][3]; int **p;)cpp",
+  

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-16 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:95
+
+  if (P1->isArrayType() && P2->isArrayType()) {
+// At every level that array type is involved in, at least

xazax.hun wrote:
> isuckatcs wrote:
> > xazax.hun wrote:
> > > If none of the functions I recommended works for you, I still strongly 
> > > suggest reusing utilities from ASTContext, like `UnwrapSimilarTypes` and 
> > > `UnwrapSimilarArrayTypes`.
> > I didn't check all of the functions inside `ASTContext`, but here we have 
> > very specific rules, we need to check. One utility might help with one 
> > check or two, but won't replace the need to go ove all of them. Also I 
> > think it's easier to understand which section checks what, if I keep them 
> > separated.
> > 
> > In an ealier comment I link to [[ 
> > https://en.cppreference.com/w/cpp/language/implicit_conversion#Qualification_conversions
> >  | the section on cppreference ]], which discusses what these rules are. 
> > Also there I found one controversial example, that's only detected by MSVC. 
> > I wonder if you know why that happens.
> I see. It is unfortunate that we cannot simply reuse the corresponding 
> functionality from Sema. The code mostly looks good to me, apart from some 
> nits.
> It is unfortunate that we cannot simply reuse the corresponding functionality 
> from Sema.

It is indeed unfortunate, though I wonder if we want to move this functionality 
to `ASTContext`, so that it can be reused outside the `ExceptionAnalyzer`.


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-16 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 489626.
isuckatcs marked 7 inline comments as done.
isuckatcs added a comment.

Addressed comments


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,24 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
===
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -80,7 +80,7 @@
 /// possible to catch multiple exception types by one 'catch' if they
 /// are a subclass of the 'catch'ed exception type.
 /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
-bool filterByCatch(const Type 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2023-01-12 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:60
+// Checks if T1 is convertible to T2.
+static bool isMultiLevelConvertiblePointer(QualType P1, QualType P2,
+   unsigned CurrentLevel = 0,

xazax.hun wrote:
> xazax.hun wrote:
> > Did you take a look at `ASTContext::hasCvrSimilarType`? I wonder if it is 
> > already doing what you want. 
> Oh, maybe it is not, but it might also make sense to take a look at 
> `ASTContext::typesAreCompatible`.
> Did you take a look at ASTContext::hasCvrSimilarType? I wonder if it is 
> already doing what you want.

This function compares the types by removing the CVR qualifiers.

> Oh, maybe it is not, but it might also make sense to take a look at 
> ASTContext::typesAreCompatible.

This one only compares the canonical types if the language is C++.

```lang=c++
if (getLangOpts().CPlusPlus)
return hasSameType(LHS, RHS);

bool hasSameType(QualType T1, QualType T2) const {
return getCanonicalType(T1) == getCanonicalType(T2);
  }

```



Comment at: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp:95
+
+  if (P1->isArrayType() && P2->isArrayType()) {
+// At every level that array type is involved in, at least

xazax.hun wrote:
> If none of the functions I recommended works for you, I still strongly 
> suggest reusing utilities from ASTContext, like `UnwrapSimilarTypes` and 
> `UnwrapSimilarArrayTypes`.
I didn't check all of the functions inside `ASTContext`, but here we have very 
specific rules, we need to check. One utility might help with one check or two, 
but won't replace the need to go ove all of them. Also I think it's easier to 
understand which section checks what, if I keep them separated.

In an ealier comment I link to [[ 
https://en.cppreference.com/w/cpp/language/implicit_conversion#Qualification_conversions
 | the section on cppreference ]], which discusses what these rules are. Also 
there I found one controversial example, that's only detected by MSVC. I wonder 
if you know why that happens.


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

https://reviews.llvm.org/D135495

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


[PATCH] D141224: [ODRHash] Handle `Integral` and `NullPtr` template parameters in `ODRHash`

2023-01-12 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGd78a7c5012c7: [ODRHash] Handle `Integral` and `NullPtr` 
template parameters in `ODRHash` (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141224

Files:
  clang/lib/AST/ODRHash.cpp
  clang/test/Modules/odr_hash.cpp

Index: clang/test/Modules/odr_hash.cpp
===
--- clang/test/Modules/odr_hash.cpp
+++ clang/test/Modules/odr_hash.cpp
@@ -1939,6 +1939,164 @@
 // expected-note@first.h:* {{but in 'FirstModule' found method 'run' with 2 template arguments}}
 #endif
 
+#if defined(FIRST)
+struct S12 {
+  template  void f(){};
+  template <> void f<1>(){};
+};
+#elif defined(SECOND)
+struct S12 {
+  template  void f(){};
+  template <> void f<2>(){};
+};
+#else
+S12 s12;
+// expected-error@second.h:* {{'TemplateArgument::S12' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with 2 for 1st template argument}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 1 for 1st template argument}}
+#endif
+
+#if defined(FIRST)
+struct S13 {
+  template  void f(){};
+  template <> void f<10>(){};
+};
+#elif defined(SECOND)
+struct S13 {
+  template  void f(){};
+  template <> void f<10>(){};
+};
+#else
+S13 s13;
+#endif
+
+#if defined(FIRST)
+struct S14 {
+  template  void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S14 {
+  template  void f(){};
+  template <> void f(){};
+};
+#else
+S14 s14;
+// expected-error@second.h:* {{'TemplateArgument::S14' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with 0 for 1st template argument}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 1 for 1st template argument}}
+#endif
+
+#if defined(FIRST)
+struct S15 {
+  template  void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S15 {
+  template  void f(){};
+  template <> void f(){};
+};
+#else
+S15 s15;
+#endif
+
+#if defined(FIRST)
+struct S16 {
+  template  void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S16 {
+  template  void f(){};
+  template <> void f(){};
+};
+#else
+S16 s16;
+#endif
+
+#if defined(FIRST)
+struct S17 {
+  static int x;
+  template  void f(){};
+  template <> void f<>(){};
+};
+#elif defined(SECOND)
+struct S17 {
+  static int x;
+  template  void f(){};
+  template <> void f(){};
+};
+#else
+S17 s17;
+// expected-error@second.h:* {{'TemplateArgument::S17' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with nullptr for 1st template argument}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 'x' for 1st template argument}}
+#endif
+
+#if defined(FIRST)
+struct S18 {
+  static int x;
+  template  void f(){};
+  template <> void f<>(){};
+};
+#elif defined(SECOND)
+struct S18 {
+  static int x;
+  template  void f(){};
+  template <> void f<>(){};
+};
+#else
+S18 s18;
+#endif
+
+#if defined(FIRST)
+struct S19 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S19 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f(){};
+};
+#else
+S19 s19;
+#endif
+
+#if defined(FIRST)
+struct S20 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S20 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f(){};
+};
+#else
+S20 s20;
+// expected-error@second.h:* {{'TemplateArgument::S20' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with 18446744073709551615 for 1st template argument}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 36893488147419103230 for 1st template argument}}
+#endif
+
+#if defined(FIRST)
+struct S21 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f(){};
+};
+#elif defined(SECOND)
+struct S21 {
+  static constexpr _BitInt(128) x = static_cast<_BitInt(128)>((unsigned long long)-1);
+  template <_BitInt(128)> void f(){};
+  template <> void f<(unsigned long long)-1>(){};
+};
+#else
+S21 s21;
+#endif
+
 #define DECLS   \
   OneClass a;  \
   OneInt<1> b;  

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-12-18 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

ping


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

https://reviews.llvm.org/D135495

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


[PATCH] D139544: [clang][dataflow] Add support for structured bindings of tuple-like types.

2022-12-09 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> Sorry, I should fix my response above: I *never* see the BindingDecls in the 
> CFG, whether or not they are used.

It was my fault, I was wrong with the wording. I meant the `DeclRefExpr`s that 
refer to the `BindingDecl`s. You can 
access the `BindingDecl`s through the `DeclRefExpr`s, they are not present in 
the CFG directly.

That's also how the static analyzer handles the `BindingDecl`s in 
`ExprEngine::VisitCommonDeclRefExpr()`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139544

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


[PATCH] D139544: [clang][dataflow] Add support for structured bindings of tuple-like types.

2022-12-09 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

Those `BindingDecl`s never appear in the CFG unless one of the is used.

Take a look at the other cases.

1.) Arrays

  void foo() {
  int arr[2];
  
  auto [i0, i1] = arr;
  }

  [B1]
 1: int arr[2];
 2: arr
 3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *)
 4: *
 5: [B1.3][[B1.4]]
 6: [B1.5] (ImplicitCastExpr, LValueToRValue, int)
 7: {[B1.6]}
 8: auto = {arr[*]};
 Preds (1): B2
 Succs (1): B0

2.)  Structs

  struct S {
  int x;
  int y;
  };
  
  void foo() {
  S s;
  
  auto [i0, i1] = s;
  }

  [B1]
 1:  (CXXConstructExpr, [B1.2], S)
 2: S s;
 3: s
 4: [B1.3] (ImplicitCastExpr, NoOp, const struct S)
 5: [B1.4] (CXXConstructExpr, [B1.6], S)
 6: auto = s;
 Preds (1): B2
 Succs (1): B0

`i0` and `i1` are not in the CFG. They only get added, when one of them is 
actually used.

  int x = i0;

   9: i0
  10: [B1.9] (ImplicitCastExpr, LValueToRValue, int)
  11: int x = i0;

That means. when you see the `DeclRefExpr` you mentioned, the synthesized 
variable has already been created and the `BindingDecl` can see it.

  -DeclRefExpr  'std::tuple_element<0, std::tuple>::type':'int' lvalue Binding 0x559942acf350 'i0' 'std::tuple_element<0, 
std::tuple>::type':'int'

By the time you see this node, the variable has already been created and you 
can access it using `BindingDecl::getHoldingVar()`. You want to analyze these 
`BindingDecl`s when they are used not when you encounter the 
`DecompositionDecl`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139544

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


[PATCH] D139544: [clang][dataflow] Add support for structured bindings of tuple-like types.

2022-12-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> the temporary's construction should appear before, but the binding decls, 
> which use the synthetic variables, should appear after

I'm confused a bit here. Right now the CFG looks like this:

  
  
  ...
  

Based on what you say I assume you want something to happen between 
`constructor_call` and `binding_decl_0`. 
Could you visualize somehow how you think the ideal CFG here looks like?

> What do you think of duplicating it in the CFG, possibly with a new 
> (statement) element kind (to mark it as a special)? 
> There are other workarounds that we could do on our end, but the duplication 
> would avoid the need for such -- for us and any other clients.

Well, I don't think duplicating a statement in the CFG is a good idea as long 
as there are other solutions for this problem. This seems to be a
very specific problem, so I think a local solution should be pursued instead if 
possible. What do you think @xazax.hun  @NoQ ?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139544

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


[PATCH] D139544: [clang][dataflow] Add support for structured bindings of tuple-like types.

2022-12-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> I'm particularly interested in the case of tuple-like containers, which is a 
> little different (though I think the same point holds)

`std::pair<>` is actually tuple-like if that's what you meant. Only tuple-like 
types have holding vars inside a `DecompositionDecl`.

> Here, both lines 7 and 13 are blank but seem to be the underlying tuple 
> that's being deconstructed. Are these different instances of the 
> DecompositionDecl or are they (unprinted) references back to line 4?

Those lines are what you see as `DeclRefExpr Decomposition ''` in the AST. 
Which are indeed references back to line 4 as you mentioned.

> I'm still bothered that this seems to violate the design/philosophy of the 
> CFG, which I take as ensuring that elements proceed in the order of operation.

I think in this case the elements do proceed in order of operation. When you 
create a structured binding by value to a tuple like type, the copy constructor 
of the type is invoked first.

Let's assume that you have a tuple-like `std::pair p{1, 2};` and you 
create the structured binding `auto [a, b] = p;`.

1. The copy constructor of `p` is invoked first and a copy get's created. Let's 
call this copy `tmp`. Note that if the copy constructor has side effects, they 
will also be visible.
2. A new variable for each element in the tuple is created and is initialized 
by `get(tmp);`. It is important that the initialization happens from the 
copy, `tmp` and not from the original tuple `p`.

The CFG resembles this behaviour. If you look at both of the example CFGs in 
this discussion, you can see that both of them begin with the constructor 
invocation.

   7: p
   8: [B1.7] (ImplicitCastExpr, NoOp, const pair)
   9: [B1.8] (CXXConstructExpr, [B1.10], std::pair)
  10: auto = p;

  1: mk
  2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, std::tuple<_Bool, int> 
(*)(void))
  3: [B1.2]() (CXXRecordTypedCall, [B1.4])
  4: auto = mk();

Points `4` and `10` are a bit weird, but that's where the copy is created, 
however the copy is unnamed. This unnamed copy is what I refered to as `tmp`.

Note that when you do a regular copy-constructor invocation like

  std::tuple mk;
  std::tuple mk2 = mk;

you would get

  3: mk
  4: [B1.3] (ImplicitCastExpr, NoOp, const class std::tuple<_Bool, int>)
  5: [B1.4] (CXXConstructExpr, [B1.6], std::tuple<_Bool, int>)
  6: std::tuple mk2 = mk;
   ^^^This is the name that's missing from the CFGs 
above.

After this the `get<>()` calls simply use this unnamed copy to initialize the 
elements from first to last, so everything seems to proceed in order in the CFG.

> Finally, a nit: why doesn't line 13 in your example, and lines 7 and 13 in my 
> example, print? Is that something that I could add to the CFG printer?

If I remember correctly (and my intuition is correct based on the examples) a 
`DeclRefExpr` in the CFG is the name of the variable being referenced. 
So for example `DeclRefExpr 'p'`  is simply `p` in the CFG.

If you look at the AST in my example, you can see that the `DeclRefExpr` to the 
decomposition is `DeclRefExpr Decomposition ''`, where  `''` 
is supposed to be the name of the variable. So I think the empty line comes 
from here.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139544

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


[PATCH] D139544: [clang][dataflow] Add support for structured bindings of tuple-like types.

2022-12-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> If CFG can be updated so that the
> synthesized variables occur in the block *before* the DeclStmt containing the
> DecompositionDecl, then we can drop the workaround that is included in this
> patch.

I'm not sure that this is possible.

If you look at the AST of the following example

  std::pair p{1, 2};
  
  auto [a, b] = p;

you get

  `-DecompositionDecl 0x55b0ac862428 'std::pair':'std::pair' cinit
|-CXXConstructExpr 
| `-ImplicitCastExpr
|   `-DeclRefExpr  'p' 
|-BindingDecl a
| |-VarDecl a
| | `-CallExpr 
| |   |-ImplicitCastExpr 
| |   | `-DeclRefExpr  'get' 
| |   `-ImplicitCastExpr 
| | `-DeclRefExpr Decomposition 0x55b0ac862428 '' 
| `-DeclRefExpr 'a' 
` ...

Basically `get<>()` is getting the element from the copy of the struct, that's 
being created when the `DecompositionDecl` happens.
The CFG reflects this accordingly.

   7: p
   8: [B1.7] (ImplicitCastExpr, NoOp, const pair)
   9: [B1.8] (CXXConstructExpr, [B1.10], std::pair)
  10: auto = p;
  11: get<0UL>
  12: [B1.11] (ImplicitCastExpr, FunctionToPointerDecay, typename 
tuple_element<0UL, pair >::type &&(*)(pair &&) noexcept)
  13:
  14: [B1.13] (ImplicitCastExpr, NoOp, std::pair)
  15: [B1.12]([B1.14])
  16: std::tuple_element<0, std::pair>::type a = get<0UL>();

Here `13` would be the `DecompositionDecl`. The variable declarations must 
appear after the `DecompositionDecl`, 
otherwhise the tuple, from which the variables are initialized cannot be 
accessed.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139544

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


[PATCH] D136671: [analyzer] Fix crash for array-delete of UnknownVal values.

2022-11-07 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs accepted this revision.
isuckatcs added a comment.
This revision is now accepted and ready to land.

I have nothing else to add, this patch looks good to me. If the others don't 
have anything to add either, feel free to commit.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D136671

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


[PATCH] D136671: [analyzer] Fix crash for array-delete of UnknownVal values.

2022-11-01 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp:1244
 SVal *ElementCountVal) {
+  assert(Region != nullptr && "Null-regions passed to 
prepareStateForArrayDestruction.");
 

I meant to assert this inside `getDynamicElementCount()` as that's the function 
that dereferences the pointer.

```
DefinedOrUnknownSVal getDynamicElementCount(..., const MemRegion *MR, ...) {
  MR = MR->StripCasts();
<-- crash here if MR is a nullptr

  DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB);
  SVal ElementSize = getElementExtent(ElementTy, SVB);

  SVal ElementCount =
  SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()); 
<-- this could be the origin of 

a crash too

  return ElementCount.castAs();
}
```

Also `SVB.evalBinOp()` can technically return an `UndefinedVal` and if it does 
that, then `ElementCount.castAs()` will also cause a 
crash as it happened in D130974.

Also since this function can return an `UnknownSVal`, what do you think about 
returning that if `MR` is a `nullptr` instead of stopping execution? Would that 
behaviour even make sense?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D136671

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


[PATCH] D136671: [analyzer] Fix crash for array-delete of UnknownVal values.

2022-10-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> For me, it makes sense that getDynamicElementCount() is not intended to be 
> called on a null MemRegion.

It would still be helpful to have an assertion on that inside 
`getDynamicElementCount()`, so the caller will know it right away.

What I want to say is that it seems there are multiple error prone snippets 
involved in this crash and I think we should address all 
of them, because some might cause another crash somewhere else.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D136671

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


[PATCH] D136671: [analyzer] Fix crash for array-delete of UnknownVal values.

2022-10-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> This eliminate the crash in `getDynamicElementCount` on that region

I think that if the crash comes from `getDynamicElementCount`, we should 
address it there too, so 
when the function is called elsewhere under the same circumstances it won't 
crash again.

What do you think?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D136671

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


[PATCH] D135965: [analyzer] Move unexecuted test block into it's own source file

2022-10-20 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGea8aebf9eb7f: [analyzer] Move unexecuted test block into 
its own source file (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135965

Files:
  clang/test/Analysis/lambdas-modern.cpp
  clang/test/Analysis/lambdas.cpp


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -205,29 +205,6 @@
   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
 }
 
-#if __cplusplus >= 201402L
-// Capture copy elided object.
-
-struct Elided{
-  int x = 0;
-  Elided(int) {}
-};
-
-void testCopyElidedObjectCaptured(int x) {
-  [e = Elided(x)] {
-clang_analyzer_eval(e.x == 0); // expected-warning{{TRUE}}
-  }();
-}
-
-static auto MakeUniquePtr() { return std::make_unique>(); }
-
-void testCopyElidedUniquePtr() {
-  [uniquePtr = MakeUniquePtr()] {}();
-  clang_analyzer_warnIfReached(); // expected-warning{{TRUE}}
-}
-
-#endif
-
 // Test inline defensive checks
 int getNum();
 
Index: clang/test/Analysis/lambdas-modern.cpp
===
--- /dev/null
+++ clang/test/Analysis/lambdas-modern.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -std=c++14 
-analyzer-checker=core,debug.ExprInspection -analyzer-config 
inline-lambdas=true -verify %s
+// RUN: %clang_analyze_cc1 -std=c++17 
-analyzer-checker=core,debug.ExprInspection -analyzer-config 
inline-lambdas=true -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(int);
+
+// Capture copy elided object.
+struct Elided{
+  int x = 14;
+  Elided(int) {}
+};
+
+void testCopyElidedObjectCaptured(int x) {
+  int r = [e = Elided(x)] {
+return e.x;
+  }();
+  
+  clang_analyzer_eval(r == 14); // expected-warning{{TRUE}}
+}
+
+static auto MakeUniquePtr() { return std::make_unique>(); }
+
+void testCopyElidedUniquePtr() {
+  [uniquePtr = MakeUniquePtr()] {}();
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -205,29 +205,6 @@
   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
 }
 
-#if __cplusplus >= 201402L
-// Capture copy elided object.
-
-struct Elided{
-  int x = 0;
-  Elided(int) {}
-};
-
-void testCopyElidedObjectCaptured(int x) {
-  [e = Elided(x)] {
-clang_analyzer_eval(e.x == 0); // expected-warning{{TRUE}}
-  }();
-}
-
-static auto MakeUniquePtr() { return std::make_unique>(); }
-
-void testCopyElidedUniquePtr() {
-  [uniquePtr = MakeUniquePtr()] {}();
-  clang_analyzer_warnIfReached(); // expected-warning{{TRUE}}
-}
-
-#endif
-
 // Test inline defensive checks
 int getNum();
 
Index: clang/test/Analysis/lambdas-modern.cpp
===
--- /dev/null
+++ clang/test/Analysis/lambdas-modern.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
+// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(int);
+
+// Capture copy elided object.
+struct Elided{
+  int x = 14;
+  Elided(int) {}
+};
+
+void testCopyElidedObjectCaptured(int x) {
+  int r = [e = Elided(x)] {
+return e.x;
+  }();
+  
+  clang_analyzer_eval(r == 14); // expected-warning{{TRUE}}
+}
+
+static auto MakeUniquePtr() { return std::make_unique>(); }
+
+void testCopyElidedUniquePtr() {
+  [uniquePtr = MakeUniquePtr()] {}();
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131944: [analyzer] Remove pattern matching of lambda capture initializers

2022-10-14 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs marked an inline comment as done.
isuckatcs added inline comments.



Comment at: clang/test/Analysis/lambdas.cpp:226
+  [uniquePtr = MakeUniquePtr()] {}();
+  clang_analyzer_warnIfReached(); // expected-warning{{TRUE}}
+}

steakhal wrote:
> It should have said `REACHABLE`.
> How does this pass? @isuckatcs 
Oh, it's a nice catch. Yes, it should say `REACHABLE` indeed.

Also it seems we didn't get a failing test case because this statement is not 
checked at all. It is inside a block that only runs if the standard is at least 
`c++14`, however the tests are only executed with an `-std=c++11` flag.

I moved these test cases into their own test file in D135965.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131944

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


[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-10-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

I've found a strange scenario.

The following conversions are allowed

  double *a[2][3];
  double const * const (*ap)[3] = a; // OK
  double * const (*ap1)[] = a;   // OK since C++20

However if the same conversion is supposed to be performed in a `catch()` 
statement, it's not happening and the thrown object is not caught.
See it on godbolt .

Quoteing cppreference :

  When an exception is thrown by any statement in compound-statement, the 
exception object of type E is matched against the types of the formal 
parameters T of each catch-clause in handler-seq, in the order in which the 
catch clauses are listed. The exception is a match if any of the following is 
true:
  
  ...
  - T is (possibly cv-qualified) U or const U& (since C++14), and U is a 
pointer or pointer to member type, and E is also a pointer or pointer to member 
type that is implicitly convertible to U by one or more of
- a standard pointer conversion other than one to a private, protected, or 
ambiguous base class
- **a qualification conversion**
- a function pointer conversion (since C++17)
  ...

Checking the quality conversion related part of cppreference 

 lists the examples I quoted before as performable conversions.


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

https://reviews.llvm.org/D135495

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


[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-10-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 466312.
isuckatcs marked 2 inline comments as done.
isuckatcs added a comment.

Addressed comments and added support for multi-level pointers too.


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+char **p = 0;
+throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+char **p = 0;
+throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+double *a[2][3];
+throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,24 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -56,11 +56,108 @@
   [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
 }
 
+// Checks if T1 is convertible to T2.
+static bool isMultiLevelConvertiblePointer(QualType P1, QualType P2,
+   unsigned 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-10-08 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 466269.
isuckatcs marked an inline comment as done.
isuckatcs added a comment.

Addressed inline comment


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,84 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_cv' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +190,25 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr_c' which should not throw exceptions
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -61,6 +61,27 @@
   for (const Type *T : ThrownExceptions) {
 if (T == BaseClass || isBaseOf(T, BaseClass))
   TypesToDelete.push_back(T);
+else if (T->isPointerType() && BaseClass->isPointerType()) {
+  QualType BPointeeTy = BaseClass->getAs()->getPointeeType();
+  QualType TPointeeTy = T->getAs()->getPointeeType();
+
+  const Type *BPointeeUQTy = BPointeeTy->getUnqualifiedDesugaredType();
+  const Type *TPointeeUQTy = TPointeeTy->getUnqualifiedDesugaredType();
+
+  unsigned BCVR = BPointeeTy.getCVRQualifiers();
+  unsigned TCVR = TPointeeTy.getCVRQualifiers();
+
+  // In case the unqualified types are the same, the exception will be
+ 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-10-07 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 466204.
isuckatcs edited the summary of this revision.
isuckatcs added a comment.

Updated


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

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,84 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_cv' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +190,25 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr_c' which should not throw exceptions
+  try {
+derived d;
+throw  
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+derived d;
+const derived *p = 
+throw p; 
+  } catch(base *) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -61,6 +61,27 @@
   for (const Type *T : ThrownExceptions) {
 if (T == BaseClass || isBaseOf(T, BaseClass))
   TypesToDelete.push_back(T);
+else if (T->isPointerType() && BaseClass->isPointerType()) {
+  auto BPointeeTy = BaseClass->getAs()->getPointeeType();
+  auto TPointeeTy = T->getAs()->getPointeeType();
+
+  auto BPointeeUQTy = BPointeeTy->getUnqualifiedDesugaredType();
+  auto TPointeeUQTy = TPointeeTy->getUnqualifiedDesugaredType();
+
+  auto BCVR = BPointeeTy.getCVRQualifiers();
+  auto TCVR = TPointeeTy.getCVRQualifiers();
+
+  // In case the unqualified types are the same, the exception will be
+  // caught if
+  //  1.) the thrown 

[PATCH] D135495: [clang-tidy] handle pointers in `ExceptionAnalyzer`

2022-10-07 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs created this revision.
isuckatcs added reviewers: njames93, baloghadamsoftware, aaron.ballman, 
LegalizeAdulthood.
Herald added subscribers: carlosgalvezp, manas, ASDenysPetrov, dkrupp, 
donat.nagy, Szelethus, a.sidorin, rnkovacs, xazax.hun.
Herald added a project: All.
isuckatcs requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

`ExceptionAnalyzer` only compared exact types in case of pointer,
which is incorrect. An `int *` can be caught by a `const int *` handler,
but `ExceptionAnalyzer` falsely reported it an escaping exception.

For example:

  void foo() noexcept {
try {
  int a = 1;
  throw 
} catch (const int *) {
}
  }

In function `foo()` the `` is caught by the handler, but clang-tidy 
reports the following warning:

  warning: an exception may be thrown in function 'foo' which should not throw 
exceptions [bugprone-exception-escape]


https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,84 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_pointer_cv' which should not throw exceptions
+  try {
+int a = 1;
+throw 
+  } catch(const volatile int *) {}
+}
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+int a = 1;
+const volatile int *p = 
+throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -61,6 +61,24 @@
   for (const Type *T : ThrownExceptions) {
 if (T == BaseClass || isBaseOf(T, BaseClass))
   TypesToDelete.push_back(T);
+else if (T->isPointerType() && BaseClass->isPointerType()) {
+  auto BPointeeTy = BaseClass->getAs()->getPointeeType();
+  auto TPointeeTy = T->getAs()->getPointeeType();
+
+  auto BCVR = BPointeeTy.getCVRQualifiers();
+  auto TCVR = TPointeeTy.getCVRQualifiers();
+
+  // In case the unqualified types are the same, the exception will be
+  // caught if
+  //  1.) the thrown type doesn't have qualifiers
+  //  2.) the handler has the same qualifiers as the thrown type
+  //  3.) the handle has more qualifiers than the thrown type
+  if 

[PATCH] D135290: [analyzer] Fix static code analysis concerns

2022-10-07 Thread Domján Dániel 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 rG3b652fc6d644: [analyzer] Fix static code analysis concerns 
(authored by Manna, committed by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135290

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp


Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1378,10 +1378,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, );
   Pred = Bldr.generateNode(PP, state, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
  /*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1452,10 +1452,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, );
   Pred = Bldr.generateNode(PP, State, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
 }
@@ -1528,10 +1528,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, );
   Pred = Bldr.generateNode(PP, State, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
  /*IsBase=*/false, Pred, Dst, CallOpts);


Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1378,10 +1378,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, );
   Pred = Bldr.generateNode(PP, state, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
  /*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1452,10 +1452,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, );
   Pred = Bldr.generateNode(PP, State, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
 }
@@ -1528,10 +1528,10 @@
   "Prepare for object destruction");
   PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, );
   Pred = Bldr.generateNode(PP, State, Pred);
-  Bldr.takeNodes(Pred);
 
   if (!Pred)
 return;
+  Bldr.takeNodes(Pred);
 
   VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
  /*IsBase=*/false, Pred, Dst, CallOpts);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D133643: [analyzer] Cleanup some artifacts from non-POD array evaluation

2022-09-17 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG6931d311eaf4: [analyzer] Cleanup some artifacts from non-POD 
array evaluation (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D133643

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -222,6 +222,26 @@
   return 0;
 }
 
+ProgramStateRef ExprEngine::removeStateTraitsUsedForArrayEvaluation(
+ProgramStateRef State, const CXXConstructExpr *E,
+const LocationContext *LCtx) {
+
+  assert(LCtx && "Location context must be provided!");
+
+  if (E) {
+if (getPendingInitLoop(State, E, LCtx))
+  State = removePendingInitLoop(State, E, LCtx);
+
+if (getIndexOfElementToConstruct(State, E, LCtx))
+  State = removeIndexOfElementToConstruct(State, E, LCtx);
+  }
+
+  if (getPendingArrayDestruction(State, LCtx))
+State = removePendingArrayDestruction(State, LCtx);
+
+  return State;
+}
+
 /// The call exit is simulated with a sequence of nodes, which occur between
 /// CallExitBegin and CallExitEnd. The following operations occur between the
 /// two program points:
@@ -268,9 +288,6 @@
 
   auto ThisVal = svalBuilder.getCXXThis(DtorDecl->getParent(), calleeCtx);
   state = state->killBinding(ThisVal);
-
-  if (!ShouldRepeatCall)
-state = removePendingArrayDestruction(state, callerCtx);
 }
   }
 
@@ -304,14 +321,6 @@
   state = state->BindExpr(CCE, callerCtx, ThisV);
 
   ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx);
-
-  if (!ShouldRepeatCall) {
-if (getIndexOfElementToConstruct(state, CCE, callerCtx))
-  state = removeIndexOfElementToConstruct(state, CCE, callerCtx);
-
-if (getPendingInitLoop(state, CCE, callerCtx))
-  state = removePendingInitLoop(state, CCE, callerCtx);
-  }
 }
 
 if (const auto *CNE = dyn_cast(CE)) {
@@ -330,6 +339,11 @@
 }
   }
 
+  if (!ShouldRepeatCall) {
+state = removeStateTraitsUsedForArrayEvaluation(
+state, dyn_cast_or_null(CE), callerCtx);
+  }
+
   // Step 3: BindedRetNode -> CleanedNodes
   // If we can find a statement and a block in the inlined function, run remove
   // dead bindings before returning from the call. This is important to ensure
@@ -1151,7 +1165,7 @@
 
   // Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small.
   if (auto Size = getPendingInitLoop(State, CE, LCtx))
-return *Size <= AMgr.options.maxBlockVisitOnPath;
+return shouldInlineArrayDestruction(*Size);
 
   return false;
 }
@@ -1246,7 +1260,12 @@
 }
   }
 
-  // If we can't inline it, handle the return value and invalidate the regions.
+  // If we can't inline it, clean up the state traits used only if the function
+  // is inlined.
+  State = removeStateTraitsUsedForArrayEvaluation(
+  State, dyn_cast_or_null(E), Call->getLocationContext());
+
+  // Also handle the return value and invalidate the regions.
   conservativeEvalCall(*Call, Bldr, Pred, State);
 }
 
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -970,6 +970,11 @@
const CXXConstructExpr *E,
const LocationContext *LCtx);
 
+  static ProgramStateRef
+  removeStateTraitsUsedForArrayEvaluation(ProgramStateRef State,
+  const CXXConstructExpr *E,
+  const LocationContext *LCtx);
+
   /// Store the location of a C++ object corresponding to a statement
   /// until the statement is actually encountered. For example, if a DeclStmt
   /// has CXXConstructExpr as its initializer, the object would be considered
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131262: [analyzer] Track trivial copy/move constructors and initializer lists in the BugReporter

2022-09-05 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa11e51e91f73: [analyzer] Track trivial copy/move 
constructors and initializer lists in the… (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131262

Files:
  clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  clang/test/Analysis/ctor-bug-path.cpp

Index: clang/test/Analysis/ctor-bug-path.cpp
===
--- /dev/null
+++ clang/test/Analysis/ctor-bug-path.cpp
@@ -0,0 +1,271 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-output=text -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-output=text -std=c++17 -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+namespace copyMoveTrackCtor {
+struct S {
+  int *p1, *p2;
+  S(int *a, int *b) : p1(a), p2(b) {} // expected-note{{Null pointer value stored to 's.p1'}}
+};
+
+void CtorDirect() {
+  int *x = nullptr, *y = nullptr; 
+  // expected-note@-1{{'x' initialized to a null pointer value}}
+
+  S s(x, y); 
+  // expected-note@-1{{Passing null pointer value via 1st parameter 'a'}}
+  // expected-note@-2{{Calling constructor for 'S'}}
+  // expected-note@-3{{Returning from constructor for 'S'}}
+  // expected-note@-4{{'s' initialized here}}
+  S s2 = s; // expected-note{{Null pointer value stored to 's2.p1'}}
+  // expected-note@-1{{'s2' initialized here}}
+  S s3 = s2;  // expected-note{{Null pointer value stored to 's3.p1'}}
+  // expected-note@-1{{'s3' initialized here}}
+  S s4 = std::move(s3); // expected-note{{Null pointer value stored to 's4.p1'}}
+  // expected-note@-1{{'s4' initialized here}}
+  S s5 = s4; // expected-note{{Null pointer value stored to 's5.p1'}}
+
+  int i = *s5.p1; // expected-warning{{Dereference of null pointer}}
+  // expected-note@-1{{Dereference of null pointer (loaded from field 'p1')}}
+
+  (void) i;
+}
+} // namespace copyMoveTrackCtor
+
+namespace copyMoveTrackInitList {
+struct S {
+  int *p1, *p2;
+};
+
+void InitListDirect() {
+  int *x = nullptr, *y = nullptr; //expected-note{{'x' initialized to a null pointer value}}
+
+  S s{x, y}; //expected-note{{'s.p1' initialized to a null pointer value}}
+  //expected-note@-1{{'s' initialized here}}
+  S s2 = s; // expected-note{{Null pointer value stored to 's2.p1'}}
+  // expected-note@-1{{'s2' initialized here}}
+  S s3 = s2; // expected-note{{Null pointer value stored to 's3.p1'}}
+  // expected-note@-1{{'s3' initialized here}}
+  S s4 = std::move(s3); // expected-note{{Null pointer value stored to 's4.p1'}}
+  // expected-note@-1{{'s4' initialized here}}
+  S s5 = s4; // expected-note{{Null pointer value stored to 's5.p1'}}
+
+  int i = *s5.p1; // expected-warning{{Dereference of null pointer}}
+  // expected-note@-1{{Dereference of null pointer (loaded from field 'p1')}}
+
+  (void) i;
+}
+
+void InitListAssign() {
+  int *x = nullptr, *y = nullptr; //expected-note{{'x' initialized to a null pointer value}}
+
+  S s = {x, y}; //expected-note{{'s.p1' initialized to a null pointer value}}
+  //expected-note@-1{{'s' initialized here}}
+  S s2 = s; // expected-note{{Null pointer value stored to 's2.p1'}}
+  // expected-note@-1{{'s2' initialized here}}
+  S s3 = s2; // expected-note{{Null pointer value stored to 's3.p1'}}
+  // expected-note@-1{{'s3' initialized here}}
+  S s4 = std::move(s3); // expected-note{{Null pointer value stored to 's4.p1'}}
+  // expected-note@-1{{'s4' initialized here}}
+  S s5 = s4; // expected-note{{Null pointer value stored to 's5.p1'}}
+
+  int i = *s5.p1; // expected-warning{{Dereference of null pointer}}
+  // expected-note@-1{{Dereference of null pointer (loaded from field 'p1')}}
+
+  (void) i;
+}
+
+} // namespace copyMoveTrackInitList
+
+namespace copyMoveTrackCtorMemberInitList {
+struct S {
+  int *p1, *p2;
+  S(int *a, int *b) : p1{a}, p2{b} {} // expected-note{{Null pointer value stored to 's.p1'}}
+};
+
+void CtorDirect() {
+  int *x = nullptr, *y = nullptr; 
+  // expected-note@-1{{'x' initialized to a null pointer value}}
+
+  S s{x, y}; 
+  // expected-note@-1{{Passing null pointer value via 1st parameter 'a'}}
+  // expected-note@-2{{Calling constructor for 'S'}}
+  // expected-note@-3{{Returning from constructor for 'S'}}
+  // expected-note@-4{{'s' initialized here}}
+  S s2 = s; // expected-note{{Null pointer value stored to 's2.p1'}}
+  // expected-note@-1{{'s2' initialized here}}
+  S s3 = s2;  // expected-note{{Null pointer value stored to 's3.p1'}}
+  // expected-note@-1{{'s3' initialized here}}
+  S s4 = std::move(s3); // expected-note{{Null pointer value stored to 's4.p1'}}
+  // expected-note@-1{{'s4' initialized here}}
+  S s5 = s4; // expected-note{{Null pointer value stored to 's5.p1'}}
+
+  int 

[PATCH] D131299: [analyzer] Warn if the size of the array in `new[]` is undefined

2022-09-04 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa46154cb1cd0: [analyzer] Warn if the size of the array in 
`new[]` is undefined (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131299

Files:
  clang/docs/analyzer/checkers.rst
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
  clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
  clang/test/Analysis/Issue56873.cpp
  clang/test/Analysis/undefined-new-element.cpp

Index: clang/test/Analysis/undefined-new-element.cpp
===
--- /dev/null
+++ clang/test/Analysis/undefined-new-element.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_analyze_cc1 %s -analyzer-checker=core.uninitialized.NewArraySize -analyzer-output=text -verify
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void checkUndefinedElmenetCountValue() {
+  int n;
+  // expected-note@-1{{'n' declared without an initial value}}
+
+  int *arr = new int[n]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+void checkUndefinedElmenetCountMultiDimensionalValue() {
+  int n;
+  // expected-note@-1{{'n' declared without an initial value}}
+
+  auto *arr = new int[n][5]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+void checkUndefinedElmenetCountReference() {
+  int n;
+  // expected-note@-1{{'n' declared without an initial value}}
+
+  int  = n;
+  // expected-note@-1{{'ref' initialized here}}
+
+  int *arr = new int[ref]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+void checkUndefinedElmenetCountMultiDimensionalReference() {
+  int n;
+  // expected-note@-1{{'n' declared without an initial value}}
+
+  int  = n;
+  // expected-note@-1{{'ref' initialized here}}
+
+  auto *arr = new int[ref][5]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+int foo() {
+  int n;
+
+  return n;
+}
+
+void checkUndefinedElmenetCountFunction() {
+  int *arr = new int[foo()]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+void checkUndefinedElmenetCountMultiDimensionalFunction() {
+  auto *arr = new int[foo()][5]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
+
+void *malloc(size_t);
+
+void checkUndefinedPlacementElementCount() {
+  int n;
+  // expected-note@-1{{'n' declared without an initial value}}
+  
+  void *buffer = malloc(sizeof(std::string) * 10);
+  std::string *p =
+  ::new (buffer) std::string[n]; // expected-warning{{Element count in new[] is a garbage value}}
+  // expected-note@-1{{Element count in new[] is a garbage value}}
+}
Index: clang/test/Analysis/Issue56873.cpp
===
--- clang/test/Analysis/Issue56873.cpp
+++ clang/test/Analysis/Issue56873.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
 
 void clang_analyzer_warnIfReached();
 
Index: clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
===
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
@@ -0,0 +1,80 @@
+//===--- UndefinedNewArraySizeChecker.cpp ---*- C++ -*--==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine
+// that checks if the size of the array in a new[] expression is undefined.
+//
+//===--===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include 

[PATCH] D131187: [analyzer] Add more information to the Exploded Graph

2022-09-02 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb5147937b2a9: [analyzer] Add more information to the 
Exploded Graph (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131187

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
  clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/environment.dot
  clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/program_state_traits.dot
  clang/test/Analysis/exploded-graph-rewriter/store.dot
  clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/topology.dot
  clang/test/Analysis/expr-inspection.c
  clang/utils/analyzer/exploded-graph-rewriter.py

Index: clang/utils/analyzer/exploded-graph-rewriter.py
===
--- clang/utils/analyzer/exploded-graph-rewriter.py
+++ clang/utils/analyzer/exploded-graph-rewriter.py
@@ -261,48 +261,74 @@
 class ProgramState:
 def __init__(self, state_id, json_ps):
 logging.debug('Adding ProgramState ' + str(state_id))
+
+store_key = 'store'
+env_key = 'environment'
+constraints_key = 'constraints'
+dyn_ty_key = 'dynamic_types'
+ctor_key = 'constructing_objects'
+ind_key = 'index_of_element'
+init_loop_key = 'pending_init_loops'
+dtor_key = 'pending_destructors'
+msg_key = 'checker_messages'
 
 if json_ps is None:
 json_ps = {
-'store': None,
-'environment': None,
-'constraints': None,
-'dynamic_types': None,
-'constructing_objects': None,
-'index_of_element': None,
-'checker_messages': None
+store_key: None,
+env_key: None,
+constraints_key: None,
+dyn_ty_key: None,
+ctor_key: None,
+ind_key: None,
+init_loop_key: None,
+dtor_key: None,
+msg_key: None
 }
 
 self.state_id = state_id
 
-self.store = Store(json_ps['store']) \
-if json_ps['store'] is not None else None
+self.store = Store(json_ps[store_key]) \
+if json_ps[store_key] is not None else None
 
 self.environment = \
-GenericEnvironment(json_ps['environment']['items']) \
-if json_ps['environment'] is not None else None
+GenericEnvironment(json_ps[env_key]['items']) \
+if json_ps[env_key] is not None else None
 
 self.constraints = GenericMap([
-(c['symbol'], c['range']) for c in json_ps['constraints']
-]) if json_ps['constraints'] is not None else None
+(c['symbol'], c['range']) for c in json_ps[constraints_key]
+]) if json_ps[constraints_key] is not None else None
 
 self.dynamic_types = GenericMap([
 (t['region'], '%s%s' % (t['dyn_type'],
 ' (or a sub-class)'
 if t['sub_classable'] else ''))
-for t in json_ps['dynamic_types']]) \
-if json_ps['dynamic_types'] is not None else None
+for t in json_ps[dyn_ty_key]]) \
+if json_ps[dyn_ty_key] is not None else None
+
+self.checker_messages = CheckerMessages(json_ps[msg_key]) \
+if json_ps[msg_key] is not None else None
+
+# State traits
+# 
+# For traits we always check if a key exists because if a trait
+# has no imformation, nothing will be printed in the .dot file 
+# we parse. 
 
 self.constructing_objects = \
-GenericEnvironment(json_ps['constructing_objects']) \
-if json_ps['constructing_objects'] is not None else None
+GenericEnvironment(json_ps[ctor_key]) \
+if ctor_key in json_ps and json_ps[ctor_key] is not None else None
 
 self.index_of_element = \
-GenericEnvironment(json_ps['index_of_element']) \
-if json_ps['index_of_element'] is not None else None
+GenericEnvironment(json_ps[ind_key]) \
+if ind_key in json_ps and json_ps[ind_key] is not None else None
+
+self.pending_init_loops = \
+GenericEnvironment(json_ps[init_loop_key]) \
+if init_loop_key in json_ps and json_ps[init_loop_key] is not 

[PATCH] D132654: [clang-tidy] Fix false positive on `ArrayInitIndexExpr` inside `ProBoundsConstantArrayIndexCheck`

2022-08-30 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGcd40245f549b: [clang-tidy] Fix false positive on 
ArrayInitIndexExpr inside… (authored by isuckatcs).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D132654

Files:
  
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -75,13 +75,34 @@
   s[i] = 3; // OK, custom operator
 }
 
+namespace ArrayInitIndexExpr {
 struct A {
   // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't 
warn.
   int x[3];
 };
 
-void use_A() {
+void implicitCopyMoveCtor() {
   // Force the compiler to generate a copy constructor.
   A a;
   A a2(a);
+
+  // Force the compiler to generate a move constructor.
+  A a3 = (A&&) a;
+}
+
+void lambdaCapture() {
+  int arr[3];
+
+  // Capturing an array by value uses an ArraySubscriptExpr. Don't warn. 
+  [arr](){};
+}
+
+#if __cplusplus >= 201703L
+void structuredBindings() {
+  int arr[3];
+
+  // Creating structured bindings by value uses an ArraySubscriptExpr. Don't 
warn.
+  auto [a,b,c] = arr;
 }
+#endif
+} // namespace ArrayInitIndexExpr
Index: 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
===
--- 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
+++ 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
@@ -61,6 +61,12 @@
   const auto *Matched = Result.Nodes.getNodeAs("expr");
   const auto *IndexExpr = Result.Nodes.getNodeAs("index");
 
+  // This expression can only appear inside ArrayInitLoopExpr, which
+  // is always implicitly generated. ArrayInitIndexExpr is not a
+  // constant, but we shouldn't report a warning for it.
+  if (isa(IndexExpr))
+return;
+
   if (IndexExpr->isValueDependent())
 return; // We check in the specialization.
 


Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -75,13 +75,34 @@
   s[i] = 3; // OK, custom operator
 }
 
+namespace ArrayInitIndexExpr {
 struct A {
   // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't warn.
   int x[3];
 };
 
-void use_A() {
+void implicitCopyMoveCtor() {
   // Force the compiler to generate a copy constructor.
   A a;
   A a2(a);
+
+  // Force the compiler to generate a move constructor.
+  A a3 = (A&&) a;
+}
+
+void lambdaCapture() {
+  int arr[3];
+
+  // Capturing an array by value uses an ArraySubscriptExpr. Don't warn. 
+  [arr](){};
+}
+
+#if __cplusplus >= 201703L
+void structuredBindings() {
+  int arr[3];
+
+  // Creating structured bindings by value uses an ArraySubscriptExpr. Don't warn.
+  auto [a,b,c] = arr;
 }
+#endif
+} // namespace ArrayInitIndexExpr
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
===
--- clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
@@ -61,6 +61,12 @@
   const auto *Matched = Result.Nodes.getNodeAs("expr");
   const auto *IndexExpr = Result.Nodes.getNodeAs("index");
 
+  // This expression can only appear inside ArrayInitLoopExpr, which
+  // is always implicitly generated. ArrayInitIndexExpr is not a
+  // constant, but we shouldn't report a warning for it.
+  if (isa(IndexExpr))
+return;
+
   if (IndexExpr->isValueDependent())
 return; // We check in the specialization.
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D132654: [clang-tidy] Fix false positive on `ArrayInitIndexExpr` inside `ProBoundsConstantArrayIndexCheck`

2022-08-30 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs updated this revision to Diff 456715.
isuckatcs marked an inline comment as done.
isuckatcs added a comment.

Removed the unnecessary extra RUN command.


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

https://reviews.llvm.org/D132654

Files:
  
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -75,13 +75,34 @@
   s[i] = 3; // OK, custom operator
 }
 
+namespace ArrayInitIndexExpr {
 struct A {
   // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't 
warn.
   int x[3];
 };
 
-void use_A() {
+void implicitCopyMoveCtor() {
   // Force the compiler to generate a copy constructor.
   A a;
   A a2(a);
+
+  // Force the compiler to generate a move constructor.
+  A a3 = (A&&) a;
+}
+
+void lambdaCapture() {
+  int arr[3];
+
+  // Capturing an array by value uses an ArraySubscriptExpr. Don't warn. 
+  [arr](){};
+}
+
+#if __cplusplus >= 201703L
+void structuredBindings() {
+  int arr[3];
+
+  // Creating structured bindings by value uses an ArraySubscriptExpr. Don't 
warn.
+  auto [a,b,c] = arr;
 }
+#endif
+} // namespace ArrayInitIndexExpr
Index: 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
===
--- 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
+++ 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
@@ -61,6 +61,12 @@
   const auto *Matched = Result.Nodes.getNodeAs("expr");
   const auto *IndexExpr = Result.Nodes.getNodeAs("index");
 
+  // This expression can only appear inside ArrayInitLoopExpr, which
+  // is always implicitly generated. ArrayInitIndexExpr is not a
+  // constant, but we shouldn't report a warning for it.
+  if (isa(IndexExpr))
+return;
+
   if (IndexExpr->isValueDependent())
 return; // We check in the specialization.
 


Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -75,13 +75,34 @@
   s[i] = 3; // OK, custom operator
 }
 
+namespace ArrayInitIndexExpr {
 struct A {
   // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't warn.
   int x[3];
 };
 
-void use_A() {
+void implicitCopyMoveCtor() {
   // Force the compiler to generate a copy constructor.
   A a;
   A a2(a);
+
+  // Force the compiler to generate a move constructor.
+  A a3 = (A&&) a;
+}
+
+void lambdaCapture() {
+  int arr[3];
+
+  // Capturing an array by value uses an ArraySubscriptExpr. Don't warn. 
+  [arr](){};
+}
+
+#if __cplusplus >= 201703L
+void structuredBindings() {
+  int arr[3];
+
+  // Creating structured bindings by value uses an ArraySubscriptExpr. Don't warn.
+  auto [a,b,c] = arr;
 }
+#endif
+} // namespace ArrayInitIndexExpr
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
===
--- clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
@@ -61,6 +61,12 @@
   const auto *Matched = Result.Nodes.getNodeAs("expr");
   const auto *IndexExpr = Result.Nodes.getNodeAs("index");
 
+  // This expression can only appear inside ArrayInitLoopExpr, which
+  // is always implicitly generated. ArrayInitIndexExpr is not a
+  // constant, but we shouldn't report a warning for it.
+  if (isa(IndexExpr))
+return;
+
   if (IndexExpr->isValueDependent())
 return; // We check in the specialization.
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D127973: [analyzer] Eval construction of non POD type arrays.

2022-08-27 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> I'm wondering if we should have a window for backward compatibility; There 
> are a couple of places already where we check for the presence of some 
> metadata and display it only if it exists.
> Do you think it would be useful for this change as well?

It has been fixed in D131187  as a part of 
another change.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127973

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


[PATCH] D127973: [analyzer] Eval construction of non POD type arrays.

2022-08-26 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> I had the time for reducing it. Luckily it wasn't that bad.

This egraph doesn't contain an entry with key "index_of_element", which is 
strange.

I still feel like I need a code snippet, which generates such egraph.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127973

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


[PATCH] D127973: [analyzer] Eval construction of non POD type arrays.

2022-08-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

@steakhal

Can you send me a snippet please, which reproduces this issue? For me the 
egraph rewriter works fine.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127973

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


[PATCH] D132654: [clang-tidy] Fix false positive on `ArrayInitIndexExpr` inside `ProBoundsConstantArrayIndexCheck`

2022-08-25 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs created this revision.
isuckatcs added reviewers: aaron.ballman, LegalizeAdulthood, njames93.
Herald added subscribers: carlosgalvezp, arphaman, kbarton, xazax.hun, nemanjai.
Herald added a project: All.
isuckatcs requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

There are 3 different cases when an `ArrayInitLoopExpr` is used to initialize 
an array.

- in the implicit copy/move constructor for a class with an array member
- when a lambda-expression captures an array by value
- when a decomposition declaration decomposes an array

The AST of such expression (usually) looks like this:

  |-ArrayInitLoopExpr 'int[3]'
  | | |-OpaqueValueExpr 'int[3]' lvalue
  | | | `-DeclRefExpr 'int[3]' lvalue Var 'arr' 'int[3]'
  | | `-ImplicitCastExpr 'int' 
  | |   `-ArraySubscriptExpr 'int' lvalue
  | | |-ImplicitCastExpr 'int *' 
  | | | `-OpaqueValueExpr 'int[3]' lvalue
  | | |   `-DeclRefExpr 'int[3]' lvalue Var 'arr' 'int[3]'
  | | `-ArrayInitIndexExpr <> 'unsigned long'

We basically always have an `ArraySubscriptExpr`, where the index is an 
`ArrayInitIndexExpr`.
`ArrayInitIndexExpr` is not a constant, so the checker mentioned in the title 
reports a warning.
This false positive warning only happens in case of decomposition declaration 
and lambda capture.

Some example without this patch:

  void foo() {
  int arr[3];
  
  auto [a, b, c] = arr;
  }
  ---
  warning: do not use array subscript when the index is not an integer constant 
expression [cppcoreguidelines-pro-bounds-constant-array-index]
  auto [a, b, c] = arr;
   ^



  void foo() {
  int arr[3];
  
  [arr]() {};
  }
  ---
  warning: do not use array subscript when the index is not an integer constant 
expression [cppcoreguidelines-pro-bounds-constant-array-index]
  [arr]() {};
   ^


https://reviews.llvm.org/D132654

Files:
  
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp


Index: 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ 
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -1,4 +1,5 @@
 // RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index 
%t
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index 
-extra-arg=-std=c++17 %t
 
 typedef __SIZE_TYPE__ size_t;
 
@@ -75,13 +76,34 @@
   s[i] = 3; // OK, custom operator
 }
 
+namespace ArrayInitIndexExpr {
 struct A {
   // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't 
warn.
   int x[3];
 };
 
-void use_A() {
+void implicitCopyMoveCtor() {
   // Force the compiler to generate a copy constructor.
   A a;
   A a2(a);
+
+  // Force the compiler to generate a move constructor.
+  A a3 = (A&&) a;
+}
+
+void lambdaCapture() {
+  int arr[3];
+
+  // Capturing an array by value uses an ArraySubscriptExpr. Don't warn. 
+  [arr](){};
+}
+
+#if __cplusplus >= 201703L
+void structuredBindings() {
+  int arr[3];
+
+  // Creating structured bindings by value uses an ArraySubscriptExpr. Don't 
warn.
+  auto [a,b,c] = arr;
 }
+#endif
+} // namespace ArrayInitIndexExpr
Index: 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
===
--- 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
+++ 
clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
@@ -61,6 +61,12 @@
   const auto *Matched = Result.Nodes.getNodeAs("expr");
   const auto *IndexExpr = Result.Nodes.getNodeAs("index");
 
+  // This expression can only appear inside ArrayInitLoopExpr, which
+  // is always implicitly generated. ArrayInitIndexExpr is not a
+  // constant, but we shouldn't report a warning for it.
+  if (isa(IndexExpr))
+return;
+
   if (IndexExpr->isValueDependent())
 return; // We check in the specialization.
 


Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-constant-array-index.cpp
@@ -1,4 +1,5 @@
 // RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index %t
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index 

[PATCH] D131501: [analyzer] Fix for incorrect handling of 0 length non-POD array construction

2022-08-25 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGe3e9082b013b: [analyzer] Fix for incorrect handling of 0 
length non-POD array construction (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131501

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/array-init-loop.cpp
  clang/test/Analysis/dtor-array.cpp
  clang/test/Analysis/flexible-array-member.cpp
  clang/test/Analysis/zero-size-non-pod-array.cpp

Index: clang/test/Analysis/zero-size-non-pod-array.cpp
===
--- /dev/null
+++ clang/test/Analysis/zero-size-non-pod-array.cpp
@@ -0,0 +1,189 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
+
+void clang_analyzer_eval(bool);
+
+struct S{
+static int CtorInvocationCount;
+static int DtorInvocationCount;
+
+S(){CtorInvocationCount++;}
+~S(){DtorInvocationCount++;}
+};
+
+int S::CtorInvocationCount = 0;
+int S::DtorInvocationCount = 0;
+
+void zeroSizeArrayStack() {
+S::CtorInvocationCount = 0;
+
+S arr[0];
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+void zeroSizeMultidimensionalArrayStack() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+{
+S arr[2][0];
+S arr2[0][2];
+
+S arr3[0][2][2];
+S arr4[2][2][0];
+S arr5[2][0][2];
+}
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+void zeroSizeArrayStackInLambda() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+[]{
+S arr[0];
+}();
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+void zeroSizeArrayHeap() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+auto *arr = new S[0];
+delete[] arr;
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+void zeroSizeMultidimensionalArrayHeap() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+auto *arr = new S[2][0];
+delete[] arr;
+
+auto *arr2 = new S[0][2];
+delete[] arr2;
+
+auto *arr3 = new S[0][2][2];
+delete[] arr3;
+
+auto *arr4 = new S[2][2][0];
+delete[] arr4;
+
+auto *arr5 = new S[2][0][2];
+delete[] arr5;
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+#if __cplusplus >= 201703L
+
+void zeroSizeArrayBinding() {
+S::CtorInvocationCount = 0;
+
+S arr[0];
+
+// Note: This is an error in gcc but a warning in clang.
+// In MSVC the declaration of 'S arr[0]' is already an error
+// and it doesn't recognize this syntax as a structured binding.
+auto [] = arr; //expected-warning{{ISO C++17 does not allow a decomposition group to be empty}}
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+}
+
+#endif
+
+void zeroSizeArrayLambdaCapture() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+S arr[0];
+
+auto l = [arr]{};
+[arr]{}();
+
+//FIXME: These should be TRUE. We should avoid calling the destructor 
+// of the temporary that is materialized as the lambda.
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}} expected-warning{{FALSE}}
+clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}} expected-warning{{FALSE}}
+}
+
+// FIXME: Report a warning if the standard is at least C++17.
+#if __cplusplus < 201703L
+void zeroSizeArrayLambdaCaptureUndefined1() {
+S arr[0];
+int n;
+
+auto l = [arr, n]{
+int x = n; //expected-warning{{Assigned value is garbage or undefined}}
+(void) x;
+};
+
+l();
+}
+#endif
+
+void zeroSizeArrayLambdaCaptureUndefined2() {
+S arr[0];
+int n;
+
+[arr, n]{
+int x = n; //expected-warning{{Assigned value is garbage or undefined}}
+(void) x;
+}();
+}
+
+struct Wrapper{
+S arr[0];
+};
+
+void zeroSizeArrayMember() {
+S::CtorInvocationCount = 0;
+S::DtorInvocationCount = 0;
+
+{
+Wrapper W;
+}
+
+clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
+

[PATCH] D130737: [analyzer] Process non-POD array element destructors

2022-08-23 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGaac73a31add5: [analyzer] Process non-POD array element 
destructors (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130737

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/Analysis/CFG.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/lib/StaticAnalyzer/Core/ProgramState.cpp
  clang/test/Analysis/dtor-array.cpp
  clang/test/Analysis/new.cpp

Index: clang/test/Analysis/new.cpp
===
--- clang/test/Analysis/new.cpp
+++ clang/test/Analysis/new.cpp
@@ -3,6 +3,7 @@
 #include "Inputs/system-header-simulator-cxx.h"
 
 void clang_analyzer_eval(bool);
+void clang_analyzer_warnIfReached();
 
 typedef __typeof__(sizeof(int)) size_t;
 extern "C" void *malloc(size_t);
@@ -323,9 +324,8 @@
 
 void testArrayDestr() {
   NoReturnDtor *p = new NoReturnDtor[2];
-  delete[] p;// Calls the base destructor which aborts, checked below
- // TODO: clang_analyzer_eval should not be called
-  clang_analyzer_eval(true); // expected-warning{{TRUE}}
+  delete[] p;
+  clang_analyzer_warnIfReached(); // no-warning
 }
 
 // Invalidate Region even in case of default destructor
Index: clang/test/Analysis/dtor-array.cpp
===
--- /dev/null
+++ clang/test/Analysis/dtor-array.cpp
@@ -0,0 +1,347 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++11 %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++17 %s
+
+using size_t =  __typeof(sizeof(int));
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
+void clang_analyzer_warnIfReached();
+void clang_analyzer_explain(int);
+
+int a, b, c, d;
+
+struct InlineDtor {
+  static int cnt;
+  static int dtorCalled;
+  ~InlineDtor() {
+switch (dtorCalled % 4) {
+case 0:
+  a = cnt++;
+  break;
+case 1:
+  b = cnt++;
+  break;
+case 2:
+  c = cnt++;
+  break;
+case 3:
+  d = cnt++;
+  break;
+}
+
+++dtorCalled;
+  }
+};
+
+int InlineDtor::cnt = 0;
+int InlineDtor::dtorCalled = 0;
+
+void foo() {
+  InlineDtor::cnt = 0;
+  InlineDtor::dtorCalled = 0;
+  InlineDtor arr[4];
+}
+
+void testAutoDtor() {
+  foo();
+
+  clang_analyzer_eval(a == 0); // expected-warning {{TRUE}}
+  clang_analyzer_eval(b == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(c == 2); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d == 3); // expected-warning {{TRUE}}
+}
+
+void testDeleteDtor() {
+  InlineDtor::cnt = 10;
+  InlineDtor::dtorCalled = 0;
+
+  InlineDtor *arr = new InlineDtor[4];
+  delete[] arr;
+
+  clang_analyzer_eval(a == 10); // expected-warning {{TRUE}}
+  clang_analyzer_eval(b == 11); // expected-warning {{TRUE}}
+  clang_analyzer_eval(c == 12); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d == 13); // expected-warning {{TRUE}}
+}
+
+struct MemberDtor {
+  InlineDtor arr[4];
+};
+
+void testMemberDtor() {
+  InlineDtor::cnt = 5;
+  InlineDtor::dtorCalled = 0;
+
+  MemberDtor *MD = new MemberDtor{};
+  delete MD;
+
+  clang_analyzer_eval(a == 5); // expected-warning {{TRUE}}
+  clang_analyzer_eval(b == 6); // expected-warning {{TRUE}}
+  clang_analyzer_eval(c == 7); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d == 8); // expected-warning {{TRUE}}
+}
+
+struct MultipleMemberDtor
+{
+  InlineDtor arr[4];
+  InlineDtor arr2[4];
+};
+
+void testMultipleMemberDtor() {
+  InlineDtor::cnt = 30;
+  InlineDtor::dtorCalled = 0;
+
+  MultipleMemberDtor *MD = new MultipleMemberDtor{};
+  delete MD;
+
+  clang_analyzer_eval(a == 34); // expected-warning {{TRUE}}
+  clang_analyzer_eval(b == 35); // expected-warning {{TRUE}}
+  clang_analyzer_eval(c == 36); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d == 37); // expected-warning {{TRUE}}
+}
+
+int EvalOrderArr[4];
+
+struct EvalOrder
+{
+  int ctor = 0;
+  static int dtorCalled;
+  static int ctorCalled;
+
+  EvalOrder() { ctor = ctorCalled++; };
+
+  ~EvalOrder() { EvalOrderArr[ctor] = dtorCalled++; }
+};
+
+int EvalOrder::ctorCalled = 0;
+int EvalOrder::dtorCalled = 0;
+
+void dtorEvaluationOrder() {
+  EvalOrder::ctorCalled = 0;
+  EvalOrder::dtorCalled = 0;
+  
+  EvalOrder* eptr = new EvalOrder[4];
+  delete[] eptr;
+
+  clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}}
+  clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}}
+
+  clang_analyzer_eval(EvalOrderArr[0] == 

[PATCH] D131840: [analyzer] Handling non-POD multidimensional arrays in ArrayInitLoopExpr

2022-08-22 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc81bf940c77e: [analyzer] Handling non-POD multidimensional 
arrays in ArrayInitLoopExpr (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131840

Files:
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/Analysis/CFG.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/Analysis/CFG.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/test/Analysis/array-init-loop.cpp

Index: clang/test/Analysis/array-init-loop.cpp
===
--- clang/test/Analysis/array-init-loop.cpp
+++ clang/test/Analysis/array-init-loop.cpp
@@ -223,3 +223,86 @@
   clang_analyzer_eval(moved.arr[2].i == 5); // expected-warning{{TRUE}}
   clang_analyzer_eval(moved.arr[3].i == 6); // expected-warning{{TRUE}}
 }
+
+//Note: This is the only solution I could find to check the values without 
+// crashing clang. For more details on the crash see Issue #57135.
+void lambda_capture_multi_array() {
+  S3 arr[2][2] = {1,2,3,4};
+
+  {
+int x = [arr] { return arr[0][0].i; }();
+clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+  }
+
+  {
+int x = [arr] { return arr[0][1].i; }();
+clang_analyzer_eval(x == 2); // expected-warning{{TRUE}}
+  }
+
+  {
+int x = [arr] { return arr[1][0].i; }();
+clang_analyzer_eval(x == 3); // expected-warning{{TRUE}}
+  }
+
+  {
+int x = [arr] { return arr[1][1].i; }();
+clang_analyzer_eval(x == 4); // expected-warning{{TRUE}}
+  }
+}
+
+// This struct will force constructor inlining in MultiWrapper.
+struct UserDefinedCtor {
+  int i;
+  UserDefinedCtor() {}
+  UserDefinedCtor(const UserDefinedCtor ) {
+int j = 1;
+i = copy.i;
+  }
+};
+
+struct MultiWrapper {
+  UserDefinedCtor arr[2][2];
+};
+
+void copy_ctor_multi() {
+  MultiWrapper MW;
+
+  MW.arr[0][0].i = 0;
+  MW.arr[0][1].i = 1;
+  MW.arr[1][0].i = 2;
+  MW.arr[1][1].i = 3;
+
+  MultiWrapper MWCopy = MW;
+  
+  clang_analyzer_eval(MWCopy.arr[0][0].i == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWCopy.arr[0][1].i == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWCopy.arr[1][0].i == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWCopy.arr[1][1].i == 3); // expected-warning{{TRUE}}
+} 
+
+void move_ctor_multi() {
+  MultiWrapper MW;
+
+  MW.arr[0][0].i = 0;
+  MW.arr[0][1].i = 1;
+  MW.arr[1][0].i = 2;
+  MW.arr[1][1].i = 3;
+
+  MultiWrapper MWMove = (MultiWrapper &&) MW;
+  
+  clang_analyzer_eval(MWMove.arr[0][0].i == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWMove.arr[0][1].i == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWMove.arr[1][0].i == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(MWMove.arr[1][1].i == 3); // expected-warning{{TRUE}}
+} 
+
+void structured_binding_multi() {
+  S3 arr[2][2] = {1,2,3,4};
+
+  auto [a,b] = arr;
+
+  clang_analyzer_eval(a[0].i == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a[1].i == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b[0].i == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b[1].i == 4); // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -524,16 +524,32 @@
   //   | `-DeclRefExpr
   //   `-ArrayInitIndexExpr
   //
+  // The resulting expression for a multidimensional array.
+  // ArrayInitLoopExpr  <-- we're here
+  // |-OpaqueValueExpr
+  // | `-DeclRefExpr<-- match this
+  // `-ArrayInitLoopExpr
+  //   |-OpaqueValueExpr
+  //   | `-ArraySubscriptExpr
+  //   |   |-ImplicitCastExpr
+  //   |   | `-OpaqueValueExpr
+  //   |   |   `-DeclRefExpr
+  //   |   `-ArrayInitIndexExpr
+  //   `-CXXConstructExpr <-- extract this
+  // ` ...
+
+  const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
+
   // HACK: There is no way we can put the index of the array element into the
   // CFG unless we unroll the loop, so we manually select and bind the required
   // parameter to the environment.
-  const auto *CE = cast(AILE->getSubExpr());
-  const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
+  const auto *CE =
+  cast(extractElementInitializerFromNestedAILE(AILE));
 
   SVal Base = UnknownVal();
   if (const auto *ME = dyn_cast(OVESrc))
 Base = State->getSVal(ME, LCtx);
-  else if (const auto *DRE = cast(OVESrc))
+  else if (const auto *DRE = dyn_cast(OVESrc))
 Base = State->getLValue(cast(DRE->getDecl()), LCtx);
   else
 llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");
@@ 

[PATCH] D131944: [analyzer] Remove pattern matching of lambda capture initializers

2022-08-22 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG3c482632e64c: [analyzer] Remove pattern matching of lambda 
capture initializers (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131944

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/test/Analysis/lambdas.cpp


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -3,6 +3,8 @@
 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpCFG 
-analyzer-config inline-lambdas=true %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
+#include "Inputs/system-header-simulator-cxx.h"
+
 void clang_analyzer_warnIfReached();
 void clang_analyzer_eval(int);
 
@@ -217,6 +219,13 @@
   }();
 }
 
+static auto MakeUniquePtr() { return std::make_unique>(); }
+
+void testCopyElidedUniquePtr() {
+  [uniquePtr = MakeUniquePtr()] {}();
+  clang_analyzer_warnIfReached(); // expected-warning{{TRUE}}
+}
+
 #endif
 
 // Test inline defensive checks
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1147,23 +1147,12 @@
 
   assert(InitExpr && "Capture missing initialization expression");
 
-  if (const auto AILE = dyn_cast(InitExpr)) {
-// If the AILE initializes a POD array, we need to keep it as the
-// InitExpr.
-if (dyn_cast(AILE->getSubExpr()))
-  InitExpr = AILE->getSubExpr();
-  }
-
-  // With C++17 copy elision this can happen.
-  if (const auto *FC = dyn_cast(InitExpr))
-InitExpr = FC->getSubExpr();
-
-  assert(InitExpr &&
- "Extracted capture initialization expression is missing");
-
-  if (dyn_cast(InitExpr)) {
-InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
-InitVal = State->getSVal(InitVal.getAsRegion());
+  // With C++17 copy elision the InitExpr can be anything, so instead of
+  // pattern matching all cases, we simple check if the current field is
+  // under construction or not, regardless what it's InitExpr is.
+  if (const auto OUC =
+  getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) {
+InitVal = State->getSVal(OUC->getAsRegion());
 
 State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
   } else


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -3,6 +3,8 @@
 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
+#include "Inputs/system-header-simulator-cxx.h"
+
 void clang_analyzer_warnIfReached();
 void clang_analyzer_eval(int);
 
@@ -217,6 +219,13 @@
   }();
 }
 
+static auto MakeUniquePtr() { return std::make_unique>(); }
+
+void testCopyElidedUniquePtr() {
+  [uniquePtr = MakeUniquePtr()] {}();
+  clang_analyzer_warnIfReached(); // expected-warning{{TRUE}}
+}
+
 #endif
 
 // Test inline defensive checks
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1147,23 +1147,12 @@
 
   assert(InitExpr && "Capture missing initialization expression");
 
-  if (const auto AILE = dyn_cast(InitExpr)) {
-// If the AILE initializes a POD array, we need to keep it as the
-// InitExpr.
-if (dyn_cast(AILE->getSubExpr()))
-  InitExpr = AILE->getSubExpr();
-  }
-
-  // With C++17 copy elision this can happen.
-  if (const auto *FC = dyn_cast(InitExpr))
-InitExpr = FC->getSubExpr();
-
-  assert(InitExpr &&
- "Extracted capture initialization expression is missing");
-
-  if (dyn_cast(InitExpr)) {
-InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
-InitVal = State->getSVal(InitVal.getAsRegion());
+  // With C++17 copy elision the InitExpr can be anything, so instead of
+  // pattern matching all cases, we simple check if the current field is
+  // under construction or not, regardless what it's InitExpr is.
+  if (const auto OUC =
+  getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) {
+InitVal = State->getSVal(OUC->getAsRegion());
 
 State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
   } else
___
cfe-commits mailing 

[PATCH] D132246: [analyzer][NFC] Be more descriptive when we replay without inlining

2022-08-19 Thread Domján Dániel 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 rGa47ec1b79797: [analyzer][NFC] Be more descriptive when we 
replay without inlining (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D132246

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp


Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2117,8 +2117,9 @@
 
   // Build an Epsilon node from which we will restart the analyzes.
   // Note that CE is permitted to be NULL!
-  ProgramPoint NewNodeLoc =
-   EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+  static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining");
+  ProgramPoint NewNodeLoc = EpsilonPoint(
+  BeforeProcessingCall->getLocationContext(), CE, nullptr, );
   // Add the special flag to GDM to signal retrying with no inlining.
   // Note, changing the state ensures that we are not going to cache out.
   ProgramStateRef NewNodeState = BeforeProcessingCall->getState();


Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2117,8 +2117,9 @@
 
   // Build an Epsilon node from which we will restart the analyzes.
   // Note that CE is permitted to be NULL!
-  ProgramPoint NewNodeLoc =
-   EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+  static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining");
+  ProgramPoint NewNodeLoc = EpsilonPoint(
+  BeforeProcessingCall->getLocationContext(), CE, nullptr, );
   // Add the special flag to GDM to signal retrying with no inlining.
   // Note, changing the state ensures that we are not going to cache out.
   ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131784: [analyzer] Fix a crash on copy elided initialized lambda captures

2022-08-12 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb4e3e3a3eb77: [analyzer] Fix a crash on copy elided 
initialized lambda captures (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131784

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/test/Analysis/lambdas.cpp


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -203,6 +203,22 @@
   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
 }
 
+#if __cplusplus >= 201402L
+// Capture copy elided object.
+
+struct Elided{
+  int x = 0;
+  Elided(int) {}
+};
+
+void testCopyElidedObjectCaptured(int x) {
+  [e = Elided(x)] {
+clang_analyzer_eval(e.x == 0); // expected-warning{{TRUE}}
+  }();
+}
+
+#endif
+
 // Test inline defensive checks
 int getNum();
 
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1143,7 +1143,9 @@
 
 SVal InitVal;
 if (!FieldForCapture->hasCapturedVLAType()) {
-  Expr *InitExpr = *i;
+  const Expr *InitExpr = *i;
+
+  assert(InitExpr && "Capture missing initialization expression");
 
   if (const auto AILE = dyn_cast(InitExpr)) {
 // If the AILE initializes a POD array, we need to keep it as the
@@ -1152,7 +1154,12 @@
   InitExpr = AILE->getSubExpr();
   }
 
-  assert(InitExpr && "Capture missing initialization expression");
+  // With C++17 copy elision this can happen.
+  if (const auto *FC = dyn_cast(InitExpr))
+InitExpr = FC->getSubExpr();
+
+  assert(InitExpr &&
+ "Extracted capture initialization expression is missing");
 
   if (dyn_cast(InitExpr)) {
 InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);


Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -203,6 +203,22 @@
   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
 }
 
+#if __cplusplus >= 201402L
+// Capture copy elided object.
+
+struct Elided{
+  int x = 0;
+  Elided(int) {}
+};
+
+void testCopyElidedObjectCaptured(int x) {
+  [e = Elided(x)] {
+clang_analyzer_eval(e.x == 0); // expected-warning{{TRUE}}
+  }();
+}
+
+#endif
+
 // Test inline defensive checks
 int getNum();
 
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1143,7 +1143,9 @@
 
 SVal InitVal;
 if (!FieldForCapture->hasCapturedVLAType()) {
-  Expr *InitExpr = *i;
+  const Expr *InitExpr = *i;
+
+  assert(InitExpr && "Capture missing initialization expression");
 
   if (const auto AILE = dyn_cast(InitExpr)) {
 // If the AILE initializes a POD array, we need to keep it as the
@@ -1152,7 +1154,12 @@
   InitExpr = AILE->getSubExpr();
   }
 
-  assert(InitExpr && "Capture missing initialization expression");
+  // With C++17 copy elision this can happen.
+  if (const auto *FC = dyn_cast(InitExpr))
+InitExpr = FC->getSubExpr();
+
+  assert(InitExpr &&
+ "Extracted capture initialization expression is missing");
 
   if (dyn_cast(InitExpr)) {
 InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130974: [analyzer] Fix for the crash in #56873

2022-08-10 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> If so, shouldn't be some dependencies across these revisions?

I don't think they are that closely related.

This patch is about fixing an assertion failure. This assertion failure happens 
because we don't handle a case not because the checker doesn't exist.
Also the checker alone wouldn't solve the problem, because if it's turned off, 
the same crash will happen.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130974

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


[PATCH] D130974: [analyzer] Fix for the crash in #56873

2022-08-09 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

> Some checker should have caught the uninitialized value earlier than the 
> defaultEvalCall().
> I guess, the MallocCkecher could have checked for it in PreStmt.
> Or alternatively, the CallAndMessageChecker::preCall() already does something 
> like this in the PreVisitProcessArg(). I know that CXXNewExpr is not a call, 
> but you get the idea.
> WDYT, worth catching it?

I definitely think it's worth catching it. I'm working on a checker which 
addresses this in D131299 . It was originally 
intended to be a part of MallocChecker but has been moved to a separate one.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130974

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


[PATCH] D130974: [analyzer] Fix for the crash in #56873

2022-08-03 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG10a7ee0bac21: [analyzer] Fix for the crash in #56873 
(authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130974

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/test/Analysis/Issue56873.cpp


Index: clang/test/Analysis/Issue56873.cpp
===
--- /dev/null
+++ clang/test/Analysis/Issue56873.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify 
%s
+
+void clang_analyzer_warnIfReached();
+
+struct S {
+};
+
+void Issue56873_1() {
+int n;
+
+// This line used to crash
+S *arr = new S[n];
+
+clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+}
+
+void Issue56873_2() {
+int n;
+
+// This line used to crash
+int *arr = new int[n];
+
+clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -762,6 +762,11 @@
   svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
 svalBuilder.getArrayIndexType());
 
+  // FIXME: This line is to prevent a crash. For more details please check
+  // issue #56264.
+  if (Size.isUndef())
+Size = UnknownVal();
+
   State = setDynamicExtent(State, MR, Size.castAs(),
svalBuilder);
 } else {


Index: clang/test/Analysis/Issue56873.cpp
===
--- /dev/null
+++ clang/test/Analysis/Issue56873.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+struct S {
+};
+
+void Issue56873_1() {
+int n;
+
+// This line used to crash
+S *arr = new S[n];
+
+clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+}
+
+void Issue56873_2() {
+int n;
+
+// This line used to crash
+int *arr = new int[n];
+
+clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -762,6 +762,11 @@
   svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
 svalBuilder.getArrayIndexType());
 
+  // FIXME: This line is to prevent a crash. For more details please check
+  // issue #56264.
+  if (Size.isUndef())
+Size = UnknownVal();
+
   State = setDynamicExtent(State, MR, Size.castAs(),
svalBuilder);
 } else {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D128837: [analyzer] Structured binding to tuple-like types

2022-07-26 Thread Domján Dániel 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 rGa618d5e0dd5d: [analyzer] Structured binding to tuple-like 
types (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128837

Files:
  clang/lib/Analysis/CFG.cpp
  clang/lib/Analysis/LiveVariables.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/Analysis/live-bindings-test.cpp
  clang/test/Analysis/uninit-structured-binding-tuple.cpp

Index: clang/test/Analysis/uninit-structured-binding-tuple.cpp
===
--- /dev/null
+++ clang/test/Analysis/uninit-structured-binding-tuple.cpp
@@ -0,0 +1,580 @@
+// RUN: %clang_analyze_cc1 -Wno-ignored-reference-qualifiers -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_eval(bool);
+
+namespace std {
+template 
+struct tuple_size {
+};
+
+template 
+struct tuple_element {
+};
+
+// The std::pair in our system header simulator is not tuple-like, so a tuple-like mock is created here
+template 
+struct mock_pair {
+  T1 first;
+  T2 second;
+};
+template 
+struct tuple_size> {
+  static const std::size_t value = 2;
+};
+
+template 
+struct tuple_element<0, mock_pair> {
+  using type = T1;
+};
+
+template 
+struct tuple_element<1, mock_pair> {
+  using type = T2;
+};
+
+template 
+using tuple_element_t = typename tuple_element::type;
+
+template 
+constexpr std::tuple_element_t> &
+get(std::mock_pair ) noexcept {
+  if (I == 0)
+return p.first;
+  else
+return p.second;
+}
+
+template 
+constexpr const std::tuple_element_t> &
+get(const std::mock_pair ) noexcept {
+  if (I == 0)
+return p.first;
+  else
+return p.second;
+}
+
+template 
+constexpr std::tuple_element_t> &&
+get(std::mock_pair &) noexcept {
+
+  if (I == 0)
+return static_cast> &&>(p.first);
+  else
+return static_cast> &&>(p.second);
+}
+
+template 
+constexpr const std::tuple_element_t> &&
+get(const std::mock_pair &) noexcept {
+  if (I == 0)
+return static_cast> &&>(p.first);
+  else
+return static_cast> &&>(p.second);
+}
+
+} // namespace std
+// A utility that generates a tuple-like struct with 2 fields
+//  of the same type. The fields are 'first' and 'second'
+#define GENERATE_TUPLE_LIKE_STRUCT(name, element_type) \
+  struct name {\
+element_type first;\
+element_type second;   \
+  };   \
+   \
+  namespace std {  \
+  template <>  \
+  struct tuple_size {\
+static const std::size_t value = 2;\
+  };   \
+   \
+  template  \
+  struct tuple_element {  \
+using type = element_type; \
+  };   \
+  }
+
+void non_user_defined_by_value(void) {
+  std::mock_pair p = {1, 2};
+
+  auto [u, v] = p;
+
+  clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v == 2); // expected-warning{{TRUE}}
+
+  int x = u;
+  u = 10;
+  int y = u;
+
+  clang_analyzer_eval(x == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y == 10);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(p.first == 1); // expected-warning{{TRUE}}
+
+  p.first = 5;
+
+  clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_by_lref(void) {
+  std::mock_pair p = {1, 2};
+
+  auto &[u, v] = p;
+
+  int x = u;
+  u = 10;
+  int y = u;
+
+  clang_analyzer_eval(x == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y == 10);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(p.first == 10); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(v == 2);// expected-warning{{TRUE}}
+  clang_analyzer_eval(p.second == 2); // expected-warning{{TRUE}}
+
+  p.first = 5;
+
+  clang_analyzer_eval(u == 5); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_by_rref(void) {
+  std::mock_pair p = {1, 2};
+
+  auto &&[u, v] = p;
+
+  int x = u;
+  u = 10;
+  int y = u;
+
+  clang_analyzer_eval(x == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y == 10);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(p.first == 10); // 

[PATCH] D129967: [analyzer] Lambda capture non-POD type array

2022-07-26 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG996b092c5e17: [analyzer] Lambda capture non-POD type array 
(authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129967

Files:
  clang/include/clang/Analysis/CFG.h
  clang/include/clang/Analysis/ConstructionContext.h
  clang/lib/Analysis/CFG.cpp
  clang/lib/Analysis/ConstructionContext.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/test/Analysis/array-init-loop.cpp
  clang/test/Analysis/lambdas.cpp

Index: clang/test/Analysis/lambdas.cpp
===
--- clang/test/Analysis/lambdas.cpp
+++ clang/test/Analysis/lambdas.cpp
@@ -400,7 +400,7 @@
 // CHECK: [B1]
 // CHECK:   1: x
 // CHECK:   2: [B1.1] (ImplicitCastExpr, NoOp, const struct X)
-// CHECK:   3: [B1.2] (CXXConstructExpr, struct X)
+// CHECK:   3: [B1.2] (CXXConstructExpr[B1.4]+0, struct X)
 // CHECK:   4: [x] {
 // CHECK:}
 // CHECK:   5: (void)[B1.4] (CStyleCastExpr, ToVoid, void)
@@ -408,4 +408,3 @@
 // CHECK:   Succs (1): B0
 // CHECK: [B0 (EXIT)]
 // CHECK:   Preds (1): B1
-
Index: clang/test/Analysis/array-init-loop.cpp
===
--- clang/test/Analysis/array-init-loop.cpp
+++ clang/test/Analysis/array-init-loop.cpp
@@ -160,6 +160,11 @@
   int i;
 };
 
+// The duplicate is required to emit a warning at 2 different places. 
+struct S3_duplicate {
+  int i;
+};
+
 void array_uninit_non_pod() {
   S3 arr[1];
 
@@ -170,24 +175,23 @@
   S2::c = 0;
   S2 arr[4];
 
-  // FIXME: These should be TRUE, but we fail to capture the array properly.
   auto l = [arr] { return arr[0].i; }();
-  clang_analyzer_eval(l == 2); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
+  clang_analyzer_eval(l == 2); // expected-warning{{TRUE}}
 
   l = [arr] { return arr[1].i; }();
-  clang_analyzer_eval(l == 3); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
+  clang_analyzer_eval(l == 3); // expected-warning{{TRUE}}
 
   l = [arr] { return arr[2].i; }();
-  clang_analyzer_eval(l == 4); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
+  clang_analyzer_eval(l == 4); // expected-warning{{TRUE}}
 
   l = [arr] { return arr[3].i; }();
-  clang_analyzer_eval(l == 5); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
+  clang_analyzer_eval(l == 5); // expected-warning{{TRUE}}
 }
 
 void lambda_uninit_non_pod() {
-  S3 arr[4];
+  S3_duplicate arr[4];
 
-  int l = [arr] { return arr[3].i; }();
+  int l = [arr] { return arr[3].i; }(); // expected-warning@164{{ in implicit constructor is garbage or undefined }}
 }
 
 // If this struct is being copy/move constructed by the implicit ctors, ArrayInitLoopExpr
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -290,6 +290,23 @@
 
   return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
 }
+case ConstructionContext::LambdaCaptureKind: {
+  CallOpts.IsTemporaryCtorOrDtor = true;
+
+  const auto *LCC = cast(CC);
+
+  SVal Base = loc::MemRegionVal(
+  MRMgr.getCXXTempObjectRegion(LCC->getInitializer(), LCtx));
+
+  const auto *CE = dyn_cast_or_null(E);
+  if (getIndexOfElementToConstruct(State, CE, LCtx)) {
+CallOpts.IsArrayCtorOrDtor = true;
+Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx),
+Base);
+  }
+
+  return Base;
+}
 case ConstructionContext::ArgumentKind: {
   // Arguments are technically temporaries.
   CallOpts.IsTemporaryCtorOrDtor = true;
@@ -450,6 +467,17 @@
 
   return State;
 }
+case ConstructionContext::LambdaCaptureKind: {
+  const auto *LCC = cast(CC);
+
+  // If we capture and array, we want to store the super region, not a
+  // sub-region.
+  if (const auto *EL = dyn_cast_or_null(V.getAsRegion()))
+V = loc::MemRegionVal(EL->getSuperRegion());
+
+  return addObjectUnderConstruction(
+  State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V);
+}
 case ConstructionContext::ArgumentKind: {
   const auto *ACC = cast(CC);
   if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
@@ -1105,19 +1133,40 @@
 
   // If we created a new MemRegion for the lambda, we should explicitly bind
   // the captures.
+  unsigned Idx = 0;
   CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
   for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
e = LE->capture_init_end();
-   i != e; ++i, ++CurField) {
+   i 

[PATCH] D129496: [analyzer] ArrayInitLoopExpr with array of non-POD type.

2022-07-26 Thread Domján Dániel 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 rG8a13326d184c: [analyzer] ArrayInitLoopExpr with array of 
non-POD type (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129496

Files:
  clang/include/clang/Analysis/ConstructionContext.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/Analysis/CFG.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/test/Analysis/array-init-loop.cpp
  clang/test/Analysis/ctor.mm
  clang/test/Analysis/uninit-structured-binding-array.cpp

Index: clang/test/Analysis/uninit-structured-binding-array.cpp
===
--- clang/test/Analysis/uninit-structured-binding-array.cpp
+++ clang/test/Analysis/uninit-structured-binding-array.cpp
@@ -292,3 +292,97 @@
   clang_analyzer_eval(e == 5); // expected-warning{{UNKNOWN}}
   clang_analyzer_eval(f == 6); // expected-warning{{UNKNOWN}}
 }
+
+struct S {
+  int a = 1;
+  int b = 2;
+};
+
+void non_pod_val(void) {
+  S arr[2];
+
+  auto [x, y] = arr;
+
+  clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}}
+}
+
+void non_pod_val_syntax_2(void) {
+  S arr[2];
+
+  auto [x, y](arr);
+
+  clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}}
+}
+
+void non_pod_lref(void) {
+  S arr[2];
+
+  auto &[x, y] = arr;
+
+  clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}}
+}
+
+void non_pod_rref(void) {
+  S arr[2];
+
+  auto &&[x, y] = arr;
+
+  clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}}
+}
+
+struct SUD {
+  inline static int c = 0;
+
+  int a = 1;
+  int b = 2;
+
+  SUD() { ++c; };
+
+  SUD(const SUD ) {
+a = copy.a + 1;
+b = copy.b + 1;
+  }
+};
+
+void non_pod_user_defined_val(void) {
+  SUD arr[2];
+
+  auto [x, y] = arr;
+
+  clang_analyzer_eval(x.a == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 3); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 3); // expected-warning{{TRUE}}
+}
+
+void non_pod_user_defined_val_syntax_2(void) {
+  SUD::c = 0;
+  SUD arr[2];
+
+  auto [x, y](arr);
+
+  clang_analyzer_eval(SUD::c == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(x.a == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(x.b == 3); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(y.a == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(y.b == 3); // expected-warning{{TRUE}}
+}
Index: clang/test/Analysis/ctor.mm
===
--- clang/test/Analysis/ctor.mm
+++ clang/test/Analysis/ctor.mm
@@ -439,16 +439,16 @@
 
 NonPOD b = a;
 
-clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{UNKNOWN}}
-clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{UNKNOWN}}
-clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{UNKNOWN}}
+clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}}
+clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}}
+clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}}
 
 NonPOD c;
 c = b;
 
-clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{UNKNOWN}}
-clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{UNKNOWN}}
-clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{UNKNOWN}}
+clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}}
+clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}}
+clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}}
   }
 
   struct NestedNonPOD {
@@ -502,16 +502,16 @@
 
 NonPODDefaulted b = a;
 
-clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{UNKNOWN}}
-clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{UNKNOWN}}
-

[PATCH] D129280: [analyzer] PlacementNewChecker, properly handle array overhead (cookie)

2022-07-15 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp:157
 "Storage provided to placement new is only {0} bytes, "
-"whereas the allocated array type requires more space for "
-"internal needs",
-SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+"whereas the allocated array type might require more space for "
+"allocation overhead",

martong wrote:
> NoQ wrote:
> > "might" is not very convincing, it may cause a reaction like "I've no idea 
> > what it's talking about and the compiler itself isn't sure, must be false 
> > positive". Can we do anything to point the user in the right direction? 
> > Say, if this is implementation-defined, are we looking at a portability 
> > issue?
> Okay, I see your point. Let's dissect the corresponding sections of the 
> standard:
> ```
> new(2, f) T[5] results in a call of operator new[](sizeof(T) * 5 + y, 2, f).
> 
> Here, ... and y are non-negative unspecified values representing array 
> allocation overhead;
> ```
> The array overhead is an **unspecified value**. What does it mean exactly? My 
> understanding is that this means it is implementation defined and the 
> implementation is not required to document or guarantee anything. I came to 
> this conclusion based on this [[ 
> https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior
>  | stackoverflow question ]]. 
> 
> My interpretation could be wrong, but that does not matter. I think, we 
> should just simply display the user what the standard says, and let them 
> digest themselves the meaning of "unspecified". I am updating the patch like 
> so.
I also looked into this overhead question. In this [[ 
https://stackoverflow.com/a/8721932 | stackoverflow answer]] someone links the 
same defect report that you did, and claims that since it's a defect report, it 
has been applied retroactively. 

Apparently [[ https://en.cppreference.com/w/cpp/language/new | cppreference ]] 
says the same. If you check the defect reports section on the bottom of the 
page you can see:
```
The following behavior-changing defect reports were applied retroactively to 
previously published C++ standards.

...
DR | Applied to | Behavior as published | Correct behavior
CWG 2382 | C++98 | non-allocating placement array new could require allocation 
overhead | such allocation overhead disallowed
```

So in my understanding you don't need to worry about this overhead at all. I 
hope this helps.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129280

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


[PATCH] D127973: [analyzer] Eval construction of non POD type arrays.

2022-07-14 Thread Domján Dániel 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 rGb032e3ff6121: [analyzer] Evaluate construction of non-POD 
type arrays (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127973

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/test/Analysis/ctor-array.cpp
  clang/test/Analysis/ctor.mm
  clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp
  clang/test/Analysis/dtor.cpp
  clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
  clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/environment.dot
  clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/store.dot
  clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/topology.dot
  clang/test/Analysis/expr-inspection.c
  clang/test/Analysis/handle_constructors_with_new_array.cpp
  clang/test/Analysis/new-ctor-conservative.cpp
  clang/test/Analysis/new.cpp
  clang/test/Analysis/operator-calls.cpp
  clang/utils/analyzer/exploded-graph-rewriter.py

Index: clang/utils/analyzer/exploded-graph-rewriter.py
===
--- clang/utils/analyzer/exploded-graph-rewriter.py
+++ clang/utils/analyzer/exploded-graph-rewriter.py
@@ -144,7 +144,8 @@
 
 
 # A deserialized Environment. This class can also hold other entities that
-# are similar to Environment, such as Objects Under Construction.
+# are similar to Environment, such as Objects Under Construction or 
+# Indices Of Elements Under Construction.
 class GenericEnvironment:
 def __init__(self, json_e):
 self.frames = [EnvironmentFrame(f) for f in json_e]
@@ -269,6 +270,7 @@
 'constraints': None,
 'dynamic_types': None,
 'constructing_objects': None,
+'index_of_element': None,
 'checker_messages': None
 }
 
@@ -296,6 +298,10 @@
 GenericEnvironment(json_ps['constructing_objects']) \
 if json_ps['constructing_objects'] is not None else None
 
+self.index_of_element = \
+GenericEnvironment(json_ps['index_of_element']) \
+if json_ps['index_of_element'] is not None else None
+
 self.checker_messages = CheckerMessages(json_ps['checker_messages']) \
 if json_ps['checker_messages'] is not None else None
 
@@ -796,6 +802,9 @@
 self.visit_environment_in_state('constructing_objects',
 'Objects Under Construction',
 s, prev_s)
+self.visit_environment_in_state('index_of_element',
+'Indices Of Elements Under Construction',
+s, prev_s)
 self.visit_checker_messages_in_state(s, prev_s)
 
 def visit_node(self, node):
Index: clang/test/Analysis/operator-calls.cpp
===
--- clang/test/Analysis/operator-calls.cpp
+++ clang/test/Analysis/operator-calls.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core,debug.ExprInspection -verify %s
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-output=text -verify %s
 void clang_analyzer_eval(bool);
 
 struct X0 { };
@@ -29,6 +29,7 @@
 
 void testMemberOperator(IntComparable B) {
   clang_analyzer_eval(B == 0); // expected-warning{{TRUE}}
+  // expected-note@-1{{TRUE}}
 }
 
 
@@ -46,7 +47,9 @@
 
   void test(const Convertible ) {
 clang_analyzer_eval((int)obj == 42); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
 clang_analyzer_eval(obj); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
   }
 }
 
@@ -82,7 +85,15 @@
 // Force a cache-out when we try to conjure a temporary region for the operator call.
 // ...then, don't crash.
 clang_analyzer_eval(+(coin ? getSmallOpaque() : getSmallOpaque())); // expected-warning{{UNKNOWN}}
+// expected-note@-1{{Assuming 'coin' is 0}}
+// expected-note@-2{{'?' condition is false}}
+// expected-note@-3{{UNKNOWN}}
+// expected-note@-4{{Assuming 'coin' is 0}}
+// expected-note@-5{{'?' condition is false}}
 clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // 

[PATCH] D128716: [analyzer] Fix BindingDecl evaluation for reference types.

2022-06-29 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG9d2e830737bc: [analyzer] Fix BindingDecl evaluation for 
reference types (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128716

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/Analysis/structured_bindings.cpp


Index: clang/test/Analysis/structured_bindings.cpp
===
--- clang/test/Analysis/structured_bindings.cpp
+++ clang/test/Analysis/structured_bindings.cpp
@@ -1,9 +1,32 @@
-// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core -verify %s
+// RUN: %clang_analyze_cc1 -std=c++17 
-analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(bool);
 
 struct s { int a; };
 int foo() {
-auto[a] = s{1}; // FIXME: proper modelling
-if (a) {
-}
+  auto [a] = s{1};
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
 } // expected-warning{{non-void function does not return a value}}
 
+struct s2 {
+  int 
+};
+
+int *foo2(s2 in) {
+  auto [a] = in;
+  return 
+}
+
+void bar() {
+  int i = 1;
+  s2 a{i};
+
+  auto *x = foo2(a);
+
+  clang_analyzer_eval(*x == i); // expected-warning{{TRUE}}
+
+  *x = 2;
+
+  clang_analyzer_eval(*x == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(i == 2);  // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2630,6 +2630,9 @@
 } else
   llvm_unreachable("An unknown case of structured binding encountered!");
 
+if (BD->getType()->isReferenceType())
+  V = state->getSVal(V.getAsRegion());
+
 Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
   ProgramPoint::PostLValueKind);
 


Index: clang/test/Analysis/structured_bindings.cpp
===
--- clang/test/Analysis/structured_bindings.cpp
+++ clang/test/Analysis/structured_bindings.cpp
@@ -1,9 +1,32 @@
-// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core -verify %s
+// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(bool);
 
 struct s { int a; };
 int foo() {
-auto[a] = s{1}; // FIXME: proper modelling
-if (a) {
-}
+  auto [a] = s{1};
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
 } // expected-warning{{non-void function does not return a value}}
 
+struct s2 {
+  int 
+};
+
+int *foo2(s2 in) {
+  auto [a] = in;
+  return 
+}
+
+void bar() {
+  int i = 1;
+  s2 a{i};
+
+  auto *x = foo2(a);
+
+  clang_analyzer_eval(*x == i); // expected-warning{{TRUE}}
+
+  *x = 2;
+
+  clang_analyzer_eval(*x == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(i == 2);  // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2630,6 +2630,9 @@
 } else
   llvm_unreachable("An unknown case of structured binding encountered!");
 
+if (BD->getType()->isReferenceType())
+  V = state->getSVal(V.getAsRegion());
+
 Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
   ProgramPoint::PostLValueKind);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126613: [Static Analyzer] Structured binding to arrays

2022-06-23 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG8ef628088b54: [analyzer] Structured binding to arrays 
(authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126613

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/Analysis/array-init-loop.cpp
  clang/test/Analysis/uninit-structured-binding-array.cpp

Index: clang/test/Analysis/uninit-structured-binding-array.cpp
===
--- /dev/null
+++ clang/test/Analysis/uninit-structured-binding-array.cpp
@@ -0,0 +1,294 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
+
+void clang_analyzer_eval(bool);
+
+void array_value_a(void) {
+  int arr[2];
+  auto [a, b] = arr;
+  arr[0] = 0;
+
+  int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_value_b(void) {
+  int arr[] = {1, 2};
+  auto [a, b] = arr;
+
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+
+  int x = a; // no-warning
+}
+
+void array_value_c(void) {
+  int arr[3];
+
+  arr[1] = 1;
+
+  auto [a, b, c] = arr;
+
+  clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}}
+
+  int y = b; // no-warning
+  int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_value_d(void) {
+  int arr[3];
+
+  arr[1] = 1;
+
+  auto [a, b, c] = arr;
+
+  clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}}
+
+  int y = b; // no-warning
+  int x = c; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_value_e(void) {
+  int uninit[2];
+  int init[2] = {0};
+
+  uninit[0] = init[0];
+
+  auto [i, j] = init;
+
+  clang_analyzer_eval(i == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j == 0); // expected-warning{{TRUE}}
+
+  int a = i; // no-warning
+  int b = j; // no-warning
+}
+
+void array_value_f(void) {
+  int uninit[2];
+  int init[2] = {0};
+
+  uninit[0] = init[0];
+
+  auto [i, j] = uninit;
+
+  clang_analyzer_eval(i == 0); // expected-warning{{TRUE}}
+
+  int a = i; // no-warning
+  int b = j; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_lref_a(void) {
+  int arr[2];
+  auto &[a, b] = arr;
+  int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_lref_b(void) {
+  int arr[] = {1, 2};
+  auto &[a, b] = arr;
+
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+
+  int x = a; // no-warning
+}
+
+void array_lref_c(void) {
+  int arr[2];
+  auto &[a, b] = arr;
+
+  arr[0] = 1;
+
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+
+  int x = a; // no-warning
+  int y = b; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_lref_d(void) {
+  int arr[3];
+
+  arr[1] = 1;
+
+  auto &[a, b, c] = arr;
+
+  clang_analyzer_eval(b == 1); // expected-warning{{TRUE}}
+
+  int y = b; // no-warning
+  int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_lref_e(void) {
+  int arr[3];
+
+  arr[1] = 1;
+
+  auto &[a, b, c] = arr;
+
+  clang_analyzer_eval(b == 1); // expected-warning{{TRUE}}
+
+  int y = b; // no-warning
+  int x = c; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_lref_f(void) {
+  int uninit[2];
+  int init[2] = {0};
+
+  uninit[0] = init[0];
+
+  auto &[i, j] = init;
+
+  clang_analyzer_eval(i == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j == 0); // expected-warning{{TRUE}}
+
+  int a = i; // no-warning
+  int b = j; // no-warning
+}
+
+void array_lref_g(void) {
+  int uninit[2];
+  int init[2] = {0};
+
+  uninit[0] = init[0];
+
+  auto &[i, j] = uninit;
+
+  clang_analyzer_eval(i == 0); // expected-warning{{TRUE}}
+
+  int a = i; // no-warning
+  int b = j; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_rref_a(void) {
+  int arr[2];
+  auto &&[a, b] = arr;
+  int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_rref_b(void) {
+  int arr[] = {1, 2};
+  auto &&[a, b] = arr;
+
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+
+  int x = a; // no-warning
+}
+
+void array_rref_c(void) {
+  int arr[2];
+  auto &&[a, b] = arr;
+
+  arr[0] = 1;
+
+  clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+
+  int x = a; // no-warning
+  int y = b; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void array_rref_d(void) {
+  int arr[3];
+
+  arr[1] = 1;
+
+  auto &&[a, b, c] = arr;
+
+  clang_analyzer_eval(b == 1); // 

[PATCH] D128064: [Static Analyzer] Small array binding policy

2022-06-18 Thread Domján Dániel via Phabricator via cfe-commits
isuckatcs added a comment.

What's out desired approach for that? Create a new patch, or update this one? 
Also, should I commit it as usual, or revert this commit first?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128064

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


[PATCH] D127643: [Static Analyzer] Structured bindings to data members

2022-06-17 Thread Domján Dániel 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 rGe77ac66b8c1c: [Static Analyzer] Structured binding to data 
members (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127643

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/Analysis/uninit-structured-binding-struct.cpp

Index: clang/test/Analysis/uninit-structured-binding-struct.cpp
===
--- /dev/null
+++ clang/test/Analysis/uninit-structured-binding-struct.cpp
@@ -0,0 +1,116 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
+
+void clang_analyzer_eval(bool);
+
+struct s {
+  int a;
+  int b;
+};
+
+void a(void) {
+  s tst;
+
+  auto [i, j] = tst;
+
+  int x = i; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void b(void) {
+  s tst;
+  tst.a = 1;
+
+  auto [i, j] = tst;
+
+  clang_analyzer_eval(i == 1); // expected-warning{{TRUE}}
+  int y = j;   // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void c(void) {
+  s tst;
+
+  auto &[i, j] = tst;
+
+  int x = i; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void d(void) {
+  s tst;
+  tst.a = 1;
+
+  auto &[i, j] = tst;
+
+  clang_analyzer_eval(i == 1); // expected-warning{{TRUE}}
+  i = 2;
+  clang_analyzer_eval(tst.a == 2); // expected-warning{{TRUE}}
+
+  int y = j; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void e(void) {
+  s tst;
+  tst.a = 1;
+
+  auto &[i, j] = tst;
+
+  clang_analyzer_eval(i == 1); // expected-warning{{TRUE}}
+
+  tst.b = 2;
+  clang_analyzer_eval(j == 2); // expected-warning{{TRUE}}
+}
+
+void f(void) {
+  s tst;
+
+  auto &&[i, j] = tst;
+
+  int x = i; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void g(void) {
+  s tst;
+  tst.a = 1;
+
+  auto &&[i, j] = tst;
+
+  clang_analyzer_eval(i == 1); // expected-warning{{TRUE}}
+  int y = j;   // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+struct s2 {
+  int a = 1;
+  int b = 2;
+};
+
+struct s3 {
+  s x;
+  s2 y;
+};
+
+void h(void) {
+  s3 tst;
+
+  clang_analyzer_eval(tst.y.a == 1); // expected-warning{{TRUE}}
+
+  auto [i, j] = tst;
+
+  // FIXME: These should be undefined, but we have to fix
+  // reading undefined from lazy compound values first.
+  clang_analyzer_eval(i.a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(i.b); // expected-warning{{UNKNOWN}}
+
+  clang_analyzer_eval(j.a == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j.b == 2); // expected-warning{{TRUE}}
+}
+
+void i(void) {
+  s3 tst;
+
+  clang_analyzer_eval(tst.y.a == 1); // expected-warning{{TRUE}}
+
+  auto &[i, j] = tst;
+  j.a = 3;
+
+  clang_analyzer_eval(tst.y.a == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(tst.y.b == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j.b == 2); // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2591,9 +2591,22 @@
 // operator&.
 return;
   }
-  if (isa(D)) {
-// FIXME: proper support for bound declarations.
-// For now, let's just prevent crashing.
+  if (const auto *BD = dyn_cast(D)) {
+const auto *DD = cast(BD->getDecomposedDecl());
+
+if (const auto *ME = dyn_cast(BD->getBinding())) {
+  const auto *Field = cast(ME->getMemberDecl());
+
+  SVal Base = state->getLValue(DD, LCtx);
+  if (DD->getType()->isReferenceType()) {
+Base = state->getSVal(Base.getAsRegion());
+  }
+
+  SVal V = state->getLValue(Field, Base);
+
+  Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
+}
+
 return;
   }
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D128064: [Static Analyzer] Small array binding policy

2022-06-17 Thread Domján Dániel 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 rG92bf652d4074: [Static Analyzer] Small array binding policy 
(authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Changed prior to commit:
  https://reviews.llvm.org/D128064?vs=437912=437950#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128064

Files:
  clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/analyzer-config.c

Index: clang/test/Analysis/analyzer-config.c
===
--- clang/test/Analysis/analyzer-config.c
+++ clang/test/Analysis/analyzer-config.c
@@ -114,6 +114,7 @@
 // CHECK-NEXT: osx.NumberObjectConversion:Pedantic = false
 // CHECK-NEXT: osx.cocoa.RetainCount:TrackNSCFStartParam = false
 // CHECK-NEXT: prune-paths = true
+// CHECK-NEXT: region-store-small-array-limit = 5
 // CHECK-NEXT: region-store-small-struct-limit = 2
 // CHECK-NEXT: report-in-main-source-file = false
 // CHECK-NEXT: serialize-stats = false
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -345,6 +345,16 @@
   /// To disable all small-struct-dependent behavior, set the option to "0".
   unsigned SmallStructLimit;
 
+  /// The largest number of element an array can have and still be
+  /// considered "small".
+  ///
+  /// This is currently used to decide whether or not it is worth "forcing" a
+  /// LazyCompoundVal on bind.
+  ///
+  /// This is controlled by 'region-store-small-struct-limit' option.
+  /// To disable all small-struct-dependent behavior, set the option to "0".
+  unsigned SmallArrayLimit;
+
   /// A helper used to populate the work list with the given set of
   /// regions.
   void populateWorkList(InvalidateRegionsWorker ,
@@ -354,10 +364,11 @@
 public:
   RegionStoreManager(ProgramStateManager )
   : StoreManager(mgr), RBFactory(mgr.getAllocator()),
-CBFactory(mgr.getAllocator()), SmallStructLimit(0) {
+CBFactory(mgr.getAllocator()), SmallStructLimit(0), SmallArrayLimit(0) {
 ExprEngine  = StateMgr.getOwningEngine();
 AnalyzerOptions  = Eng.getAnalysisManager().options;
 SmallStructLimit = Options.RegionStoreSmallStructLimit;
+SmallArrayLimit = Options.RegionStoreSmallArrayLimit;
   }
 
   /// setImplicitDefaultValue - Set the default binding for the provided
@@ -487,6 +498,11 @@
   RegionBindingsRef bindVector(RegionBindingsConstRef B,
const TypedValueRegion* R, SVal V);
 
+  Optional tryBindSmallArray(RegionBindingsConstRef B,
+const TypedValueRegion *R,
+const ArrayType *AT,
+nonloc::LazyCompoundVal LCV);
+
   RegionBindingsRef bindArray(RegionBindingsConstRef B,
   const TypedValueRegion* R,
   SVal V);
@@ -2392,6 +2408,40 @@
   return B.addBinding(R, BindingKey::Default, V);
 }
 
+Optional RegionStoreManager::tryBindSmallArray(
+RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT,
+nonloc::LazyCompoundVal LCV) {
+
+  auto CAT = dyn_cast(AT);
+
+  // If we don't know the size, create a lazyCompoundVal instead.
+  if (!CAT)
+return None;
+
+  QualType Ty = CAT->getElementType();
+  if (!(Ty->isScalarType() || Ty->isReferenceType()))
+return None;
+
+  // If the array is too big, create a LCV instead.
+  uint64_t ArrSize = CAT->getSize().getLimitedValue();
+  if (ArrSize > SmallArrayLimit)
+return None;
+
+  RegionBindingsRef NewB = B;
+
+  for (uint64_t i = 0; i < ArrSize; ++i) {
+auto Idx = svalBuilder.makeArrayIndex(i);
+const ElementRegion *SrcER =
+MRMgr.getElementRegion(Ty, Idx, LCV.getRegion(), Ctx);
+SVal V = getBindingForElement(getRegionBindings(LCV.getStore()), SrcER);
+
+const ElementRegion *DstER = MRMgr.getElementRegion(Ty, Idx, R, Ctx);
+NewB = bind(NewB, loc::MemRegionVal(DstER), V);
+  }
+
+  return NewB;
+}
+
 RegionBindingsRef
 RegionStoreManager::bindArray(RegionBindingsConstRef B,
   const TypedValueRegion* R,
@@ -2413,8 +2463,13 @@
   }
 
   // Handle lazy compound values.
-  if (isa(Init))
+  if (Optional LCV =
+  Init.getAs()) {
+if (Optional NewB = tryBindSmallArray(B, R, AT, *LCV))
+  return *NewB;
+
 return bindAggregate(B, R, Init);
+  }
 
   if (Init.isUnknown())
 return bindAggregate(B, R, UnknownVal());
Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
===

[PATCH] D127993: [Static Analyzer][CFG] Introducing the source array in the CFG of DecompositionDecl

2022-06-17 Thread Domján Dániel 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 rGfc6b2281bfd7: [Static Analyzer][CFG] Introducing the source 
array in the CFG of… (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Changed prior to commit:
  https://reviews.llvm.org/D127993?vs=437859=437943#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D127993

Files:
  clang/lib/Analysis/CFG.cpp
  clang/test/Analysis/cfg.cpp


Index: clang/test/Analysis/cfg.cpp
===
--- clang/test/Analysis/cfg.cpp
+++ clang/test/Analysis/cfg.cpp
@@ -650,6 +650,22 @@
   return 0;
 }
 
+// CHECK-LABEL: void DecompositionDecl()
+// CHECK:   [B1]
+// CHECK-NEXT:1: int arr[2];
+// CHECK-NEXT:2: arr
+// CHECK-NEXT:3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *)
+// CHECK-NEXT:4: *
+// CHECK-NEXT:5: [B1.3]{{\[\[}}B1.4]]
+// CHECK-NEXT:6: [B1.5] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT:7: {{\{}}[B1.6]{{(\})}}
+// CHECK-NEXT:8: auto = {{\{}}arr[*]{{(\})}};
+void DecompositionDecl() {
+  int arr[2];
+
+  auto [a, b] = arr;
+}
+
 // CHECK-LABEL: template<> int *PR18472()
 // CHECK: [B2 (ENTRY)]
 // CHECK-NEXT:   Succs (1): B1
Index: clang/lib/Analysis/CFG.cpp
===
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -609,6 +609,7 @@
   AddStmtChoice asc);
   CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc);
   CFGBlock *VisitWhileStmt(WhileStmt *W);
+  CFGBlock *VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, AddStmtChoice asc);
 
   CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd,
   bool ExternallyDestructed = false);
@@ -2330,6 +2331,9 @@
 
 case Stmt::WhileStmtClass:
   return VisitWhileStmt(cast(S));
+
+case Stmt::ArrayInitLoopExprClass:
+  return VisitArrayInitLoopExpr(cast(S), asc);
   }
 }
 
@@ -3881,6 +3885,27 @@
   return EntryConditionBlock;
 }
 
+CFGBlock *CFGBuilder::VisitArrayInitLoopExpr(ArrayInitLoopExpr *A,
+ AddStmtChoice asc) {
+  if (asc.alwaysAdd(*this, A)) {
+autoCreateBlock();
+appendStmt(Block, A);
+  }
+
+  CFGBlock *B = Block;
+
+  if (CFGBlock *R = Visit(A->getSubExpr()))
+B = R;
+
+  auto *OVE = dyn_cast(A->getCommonExpr());
+  assert(OVE && "ArrayInitLoopExpr->getCommonExpr() should be wrapped in an "
+"OpaqueValueExpr!");
+  if (CFGBlock *R = Visit(OVE->getSourceExpr()))
+B = R;
+
+  return B;
+}
+
 CFGBlock *CFGBuilder::VisitObjCAtCatchStmt(ObjCAtCatchStmt *CS) {
   // ObjCAtCatchStmt are treated like labels, so they are the first statement
   // in a block.


Index: clang/test/Analysis/cfg.cpp
===
--- clang/test/Analysis/cfg.cpp
+++ clang/test/Analysis/cfg.cpp
@@ -650,6 +650,22 @@
   return 0;
 }
 
+// CHECK-LABEL: void DecompositionDecl()
+// CHECK:   [B1]
+// CHECK-NEXT:1: int arr[2];
+// CHECK-NEXT:2: arr
+// CHECK-NEXT:3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *)
+// CHECK-NEXT:4: *
+// CHECK-NEXT:5: [B1.3]{{\[\[}}B1.4]]
+// CHECK-NEXT:6: [B1.5] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT:7: {{\{}}[B1.6]{{(\})}}
+// CHECK-NEXT:8: auto = {{\{}}arr[*]{{(\})}};
+void DecompositionDecl() {
+  int arr[2];
+
+  auto [a, b] = arr;
+}
+
 // CHECK-LABEL: template<> int *PR18472()
 // CHECK: [B2 (ENTRY)]
 // CHECK-NEXT:   Succs (1): B1
Index: clang/lib/Analysis/CFG.cpp
===
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -609,6 +609,7 @@
   AddStmtChoice asc);
   CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc);
   CFGBlock *VisitWhileStmt(WhileStmt *W);
+  CFGBlock *VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, AddStmtChoice asc);
 
   CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd,
   bool ExternallyDestructed = false);
@@ -2330,6 +2331,9 @@
 
 case Stmt::WhileStmtClass:
   return VisitWhileStmt(cast(S));
+
+case Stmt::ArrayInitLoopExprClass:
+  return VisitArrayInitLoopExpr(cast(S), asc);
   }
 }
 
@@ -3881,6 +3885,27 @@
   return EntryConditionBlock;
 }
 
+CFGBlock *CFGBuilder::VisitArrayInitLoopExpr(ArrayInitLoopExpr *A,
+ AddStmtChoice asc) {
+  if (asc.alwaysAdd(*this, A)) {
+autoCreateBlock();
+appendStmt(Block, A);
+  }
+
+  CFGBlock *B = Block;
+
+  if (CFGBlock *R = Visit(A->getSubExpr()))
+B = R;
+
+  auto *OVE = dyn_cast(A->getCommonExpr());
+  assert(OVE && "ArrayInitLoopExpr->getCommonExpr() 

[PATCH] D126131: [Clang][AST] BindingDecl ASTDump for tuple like structure

2022-06-14 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGcaf2767c47c3: [Clang][AST] Fixed BindingDecl AST-dump for 
tuple like structures (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126131

Files:
  clang/include/clang/AST/ASTNodeTraverser.h
  clang/unittests/AST/ASTTraverserTest.cpp

Index: clang/unittests/AST/ASTTraverserTest.cpp
===
--- clang/unittests/AST/ASTTraverserTest.cpp
+++ clang/unittests/AST/ASTTraverserTest.cpp
@@ -1157,6 +1157,46 @@
 f = 42;
 }
 
+typedef __typeof(sizeof(int)) size_t;
+
+struct Pair
+{
+int x, y;
+};
+
+// Note: these utilities are required to force binding to tuple like structure
+namespace std
+{
+template 
+struct tuple_size
+{
+};
+
+template <>
+struct tuple_size
+{
+static constexpr size_t value = 2;
+};
+
+template 
+struct tuple_element
+{
+using type = int;
+};
+
+};
+
+template 
+int &(Pair &);
+
+void decompTuple()
+{
+Pair p{1, 2};
+auto [a, b] = p;
+
+a = 3;
+}
+
 )cpp",
{"-std=c++20"});
 
@@ -1492,6 +1532,48 @@
 |-BindingDecl 'f'
 |-BindingDecl 's'
 `-BindingDecl 't'
+)cpp");
+  }
+
+  {
+auto FN = ast_matchers::match(
+functionDecl(hasName("decompTuple"),
+ hasDescendant(decompositionDecl().bind("decomp"))),
+AST2->getASTContext());
+EXPECT_EQ(FN.size(), 1u);
+
+EXPECT_EQ(
+dumpASTString(TK_AsIs, FN[0].getNodeAs("decomp")),
+R"cpp(
+DecompositionDecl ''
+|-CXXConstructExpr
+| `-ImplicitCastExpr
+|   `-DeclRefExpr 'p'
+|-BindingDecl 'a'
+| |-VarDecl 'a'
+| | `-CallExpr
+| |   |-ImplicitCastExpr
+| |   | `-DeclRefExpr 'get'
+| |   `-ImplicitCastExpr
+| | `-DeclRefExpr ''
+| `-DeclRefExpr 'a'
+`-BindingDecl 'b'
+  |-VarDecl 'b'
+  | `-CallExpr
+  |   |-ImplicitCastExpr
+  |   | `-DeclRefExpr 'get'
+  |   `-ImplicitCastExpr
+  | `-DeclRefExpr ''
+  `-DeclRefExpr 'b'
+)cpp");
+
+EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+FN[0].getNodeAs("decomp")),
+  R"cpp(
+DecompositionDecl ''
+|-DeclRefExpr 'p'
+|-BindingDecl 'a'
+`-BindingDecl 'b'
 )cpp");
   }
 }
Index: clang/include/clang/AST/ASTNodeTraverser.h
===
--- clang/include/clang/AST/ASTNodeTraverser.h
+++ clang/include/clang/AST/ASTNodeTraverser.h
@@ -467,6 +467,10 @@
   void VisitBindingDecl(const BindingDecl *D) {
 if (Traversal == TK_IgnoreUnlessSpelledInSource)
   return;
+
+if (const auto *V = D->getHoldingVar())
+  Visit(V);
+
 if (const auto *E = D->getBinding())
   Visit(E);
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D124738: [Documentation][Clang] Clang LibASTMatchers tutorial typo fix

2022-06-10 Thread Domján Dániel via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG1d3d5ecea5f0: [Documentation] Fixed typos in LibASTMatchers 
tutorial (authored by isuckatcs).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124738

Files:
  clang/docs/LibASTMatchersTutorial.rst


Index: clang/docs/LibASTMatchersTutorial.rst
===
--- clang/docs/LibASTMatchersTutorial.rst
+++ clang/docs/LibASTMatchersTutorial.rst
@@ -399,7 +399,7 @@
 
 .. code-block:: c++
 
-  hasCondition(binaryOperator(hasOperatorName("<"))
+  hasCondition(binaryOperator(hasOperatorName("<")))
 
 It makes sense to ensure that the left-hand side is a reference to a
 variable, and that the right-hand side has integer type.
@@ -529,7 +529,7 @@
   }
 
 If execution reaches the end of ``LoopPrinter::run()``, we know that the
-loop shell that looks like
+loop shell looks like
 
 .. code-block:: c++
 


Index: clang/docs/LibASTMatchersTutorial.rst
===
--- clang/docs/LibASTMatchersTutorial.rst
+++ clang/docs/LibASTMatchersTutorial.rst
@@ -399,7 +399,7 @@
 
 .. code-block:: c++
 
-  hasCondition(binaryOperator(hasOperatorName("<"))
+  hasCondition(binaryOperator(hasOperatorName("<")))
 
 It makes sense to ensure that the left-hand side is a reference to a
 variable, and that the right-hand side has integer type.
@@ -529,7 +529,7 @@
   }
 
 If execution reaches the end of ``LoopPrinter::run()``, we know that the
-loop shell that looks like
+loop shell looks like
 
 .. code-block:: c++
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits