[PATCH] D158771: [clang][tooling] Fix `name` range-selector to handle TypeLocs correctly.

2023-08-24 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua accepted this revision.
li.zhe.hua added inline comments.
This revision is now accepted and ready to land.



Comment at: clang/lib/Tooling/Transformer/RangeSelector.cpp:239-240
+  !SpecLoc.isNull())
+return CharSourceRange::getTokenRange(SpecLoc.getTemplateNameLoc(),
+  SpecLoc.getTemplateNameLoc());
   return CharSourceRange::getTokenRange(Loc.getSourceRange());

Optional: If the token range is only one token, you can specify a single 
location that is the start of the token.



Comment at: clang/unittests/Tooling/RangeSelectorTest.cpp:493
+template 
+struct Foo { T x; };
+}  // namespace ns

Nit: Is the data member necessary?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158771

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


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-06-06 Thread Eric Li 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 rG3cb6ead77c66: [clang][TypePrinter] Add option to skip over 
elaborated types (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

Files:
  clang/include/clang/AST/PrettyPrinter.h
  clang/lib/AST/TypePrinter.cpp
  clang/unittests/AST/TypePrinterTest.cpp

Index: clang/unittests/AST/TypePrinterTest.cpp
===
--- clang/unittests/AST/TypePrinterTest.cpp
+++ clang/unittests/AST/TypePrinterTest.cpp
@@ -97,6 +97,33 @@
  "const f *", Clean));
 }
 
+TEST(TypePrinter, SuppressElaboration) {
+  llvm::StringLiteral Code = R"cpp(
+namespace shared {
+namespace a {
+template 
+struct S {};
+}  // namespace a
+namespace b {
+struct Foo {};
+}  // namespace b
+using Alias = a::S;
+}  // namespace shared
+  )cpp";
+
+  auto Matcher = typedefNameDecl(hasName("::shared::Alias"),
+ hasType(qualType().bind("id")));
+  ASSERT_TRUE(PrintedTypeMatches(
+  Code, {}, Matcher, "a::S",
+  [](PrintingPolicy ) { Policy.FullyQualifiedName = true; }));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
+ "shared::a::S",
+ [](PrintingPolicy ) {
+   Policy.SuppressElaboration = true;
+   Policy.FullyQualifiedName = true;
+ }));
+}
+
 TEST(TypePrinter, TemplateIdWithNTTP) {
   constexpr char Code[] = R"cpp(
 template 
Index: clang/lib/AST/TypePrinter.cpp
===
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1576,6 +1576,11 @@
 return;
   }
 
+  if (Policy.SuppressElaboration) {
+printBefore(T->getNamedType(), OS);
+return;
+  }
+
   // The tag definition will take care of these.
   if (!Policy.IncludeTagDefinition)
   {
@@ -1595,6 +1600,12 @@
 raw_ostream ) {
   if (Policy.IncludeTagDefinition && T->getOwnedTagDecl())
 return;
+
+  if (Policy.SuppressElaboration) {
+printAfter(T->getNamedType(), OS);
+return;
+  }
+
   ElaboratedTypePolicyRAII PolicyRAII(Policy);
   printAfter(T->getNamedType(), OS);
 }
Index: clang/include/clang/AST/PrettyPrinter.h
===
--- clang/include/clang/AST/PrettyPrinter.h
+++ clang/include/clang/AST/PrettyPrinter.h
@@ -60,14 +60,15 @@
   : Indentation(2), SuppressSpecifiers(false),
 SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
 SuppressScope(false), SuppressUnwrittenScope(false),
-SuppressInlineNamespace(true), SuppressInitializers(false),
-ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
-SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
+SuppressInlineNamespace(true), SuppressElaboration(false),
+SuppressInitializers(false), ConstantArraySizeAsWritten(false),
+AnonymousTagLocations(true), SuppressStrongLifetime(false),
+SuppressLifetimeQualifiers(false),
 SuppressTemplateArgsInCXXConstructors(false),
 SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
 Nullptr(LO.CPlusPlus11 || LO.C2x), NullptrTypeInNamespace(LO.CPlusPlus),
-Restrict(LO.C99), Alignof(LO.CPlusPlus11),
-UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
+Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11),
+UseVoidForZeroParams(!LO.CPlusPlus),
 SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
 PolishForDeclaration(false), Half(LO.Half),
 MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
@@ -139,6 +140,10 @@
   /// removed.
   unsigned SuppressInlineNamespace : 1;
 
+  /// Ignore qualifiers and tag keywords as specified by elaborated type sugar,
+  /// instead letting the underlying type print as normal.
+  unsigned SuppressElaboration : 1;
+
   /// Suppress printing of variable initializers.
   ///
   /// This flag is used when printing the loop variable in a for-range
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-26 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/lib/AST/TypePrinter.cpp:1570
 
+  if (Policy.IgnoreElaboratedQualifiers) {
+printBefore(T->getNamedType(), OS);

aaron.ballman wrote:
> So, effectively, the idea here is: you want the ability to skip printing the 
> elaborated keyword and nested name specifier, and you additionally want to 
> skip use of `ElaboratedTypePolicyRAII` when calling 
> `printBefore()`/`printAfter()` because that forces suppression of tags and 
> scopes?
> 
> I think what I'm struggling with a bit is that this is doing three things at 
> once; one is the elaboration keyword (so we no longer print `typename` or 
> `struct` when `IncludeTagDefinition` is false), another is the nested name 
> specifier (we no longer print the leading foo::bar), and the third is that we 
> no longer suppress tags and scopes when printing the underlying type. That 
> makes it tricky to figure out how to name this thing, but the best I could 
> come up with is: `SuppressElaboration` which isn't really different from 
> `IgnoreElaboration` at all. So I think either of those names is "fine", but 
> I'm still a bit uncomfortable about how complex the interactions are becoming.
> another is the nested name specifier (we no longer print the leading 
> foo::bar), and the third is that we no longer suppress tags and scopes when 
> printing the underlying type

I would say that this is the primary desired behavior of the option. More 
specifically, it's //not// that we don't print the nested name specifier and no 
longer suppress those options, but rather, we print the underlying type with 
the options as specified by the policy. `SuppressTagKeyword`, `SuppressScope`, 
and `FullyQualifiedName` work as expected (from my perspective). The 
interaction with `IncludeTagDefinition` dropping the keyword wasn't explicitly 
intended, but it is still consistent with printing the canonical type (which 
would also drop the keyword).

One last name idea to throw out there, in case it seems worth discussing: 
`DesugarElaboratedTypes`. In that case, I'd probably also move the check up 
above L1559 and we treat the entire `ElaboratedType` as if it did not exist.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

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


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-26 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 526128.
li.zhe.hua added a comment.

Switch option name to SuppressElaboration


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

Files:
  clang/include/clang/AST/PrettyPrinter.h
  clang/lib/AST/TypePrinter.cpp
  clang/unittests/AST/TypePrinterTest.cpp

Index: clang/unittests/AST/TypePrinterTest.cpp
===
--- clang/unittests/AST/TypePrinterTest.cpp
+++ clang/unittests/AST/TypePrinterTest.cpp
@@ -97,6 +97,33 @@
  "const f *", Clean));
 }
 
+TEST(TypePrinter, SuppressElaboration) {
+  llvm::StringLiteral Code = R"cpp(
+namespace shared {
+namespace a {
+template 
+struct S {};
+}  // namespace a
+namespace b {
+struct Foo {};
+}  // namespace b
+using Alias = a::S;
+}  // namespace shared
+  )cpp";
+
+  auto Matcher = typedefNameDecl(hasName("::shared::Alias"),
+ hasType(qualType().bind("id")));
+  ASSERT_TRUE(PrintedTypeMatches(
+  Code, {}, Matcher, "a::S",
+  [](PrintingPolicy ) { Policy.FullyQualifiedName = true; }));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
+ "shared::a::S",
+ [](PrintingPolicy ) {
+   Policy.SuppressElaboration = true;
+   Policy.FullyQualifiedName = true;
+ }));
+}
+
 TEST(TypePrinter, TemplateIdWithNTTP) {
   constexpr char Code[] = R"cpp(
 template 
Index: clang/lib/AST/TypePrinter.cpp
===
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1567,6 +1567,11 @@
 return;
   }
 
+  if (Policy.SuppressElaboration) {
+printBefore(T->getNamedType(), OS);
+return;
+  }
+
   // The tag definition will take care of these.
   if (!Policy.IncludeTagDefinition)
   {
@@ -1586,6 +1591,12 @@
 raw_ostream ) {
   if (Policy.IncludeTagDefinition && T->getOwnedTagDecl())
 return;
+
+  if (Policy.SuppressElaboration) {
+printAfter(T->getNamedType(), OS);
+return;
+  }
+
   ElaboratedTypePolicyRAII PolicyRAII(Policy);
   printAfter(T->getNamedType(), OS);
 }
Index: clang/include/clang/AST/PrettyPrinter.h
===
--- clang/include/clang/AST/PrettyPrinter.h
+++ clang/include/clang/AST/PrettyPrinter.h
@@ -60,14 +60,15 @@
   : Indentation(2), SuppressSpecifiers(false),
 SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
 SuppressScope(false), SuppressUnwrittenScope(false),
-SuppressInlineNamespace(true), SuppressInitializers(false),
-ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
-SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
+SuppressInlineNamespace(true), SuppressElaboration(false),
+SuppressInitializers(false), ConstantArraySizeAsWritten(false),
+AnonymousTagLocations(true), SuppressStrongLifetime(false),
+SuppressLifetimeQualifiers(false),
 SuppressTemplateArgsInCXXConstructors(false),
 SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
 Nullptr(LO.CPlusPlus11 || LO.C2x), NullptrTypeInNamespace(LO.CPlusPlus),
-Restrict(LO.C99), Alignof(LO.CPlusPlus11),
-UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
+Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11),
+UseVoidForZeroParams(!LO.CPlusPlus),
 SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
 PolishForDeclaration(false), Half(LO.Half),
 MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
@@ -139,6 +140,10 @@
   /// removed.
   unsigned SuppressInlineNamespace : 1;
 
+  /// Ignore qualifiers and tag keywords as specified by elaborated type sugar,
+  /// instead letting the underlying type print as normal.
+  unsigned SuppressElaboration : 1;
+
   /// Suppress printing of variable initializers.
   ///
   /// This flag is used when printing the loop variable in a for-range
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-17 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

In D149677#4349523 , @aaron.ballman 
wrote:

> In D149677#4320178 , @li.zhe.hua 
> wrote:
>
>> Some additional context: I'm working on a refactoring tool to, in part, 
>> deprecate an alias by replacing it with its definition. That functionality 
>> by itself could be provided through something like a clang-tidy, so I say 
>> "initially".
>
> We typically don't add new printing policies in tree without some need for 
> them in the project itself (otherwise this becomes a maintenance problem), 
> and I'm not certain I see a strong need for this to live in Clang. I think it 
> might be best to wait until you're closer to having a clang-tidy check that 
> would use this functionality before we continue with this patch. WDYT?

I believe there is value for having this option, as without it, the 
`PrintingCallbacks::isScopeVisible` callback is effectively useless in 
practice. It won't be triggered in non-canonical types because the 
`ElaboratedType` represents a fixed qualification on the name.

Note: there may be an argument for folding this functionality directly into 
`FullyQualifiedName` (which currently affects template-ids despite the comment 
on it suggesting it only is for function names). If the template name was 
elaborated, `FullyQualifiedName` does nothing, which seems 
inconsistent/incorrect.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

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


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

In D149677#4319646 , @aaron.ballman 
wrote:

> Will this new printing policy be used in tree?

Not initially, no. (I'm understanding "in tree" as "in the LLVM project".)

Some additional context: I'm working on a refactoring tool to, in part, 
deprecate an alias by replacing it with its definition. That functionality by 
itself could be provided through something like a clang-tidy, so I say 
"initially".




Comment at: clang/include/clang/AST/PrettyPrinter.h:143-145
+  /// Ignore qualifiers as specified by elaborated type sugar, instead letting
+  /// the underlying type handle printing the qualifiers.
+  unsigned IgnoreElaboratedQualifiers : 1;

aaron.ballman wrote:
> The name is somewhat confusing to me, because tag names are also elaborations 
> and those are handled by `SuppressTagKeyword` -- so what is the interaction 
> between those when the user specifies true for `IgnoreElaboratedQualifiers` 
> and false for `SuppressTagKeyword`?
Ah, I hadn't considered tag names. Let me see...

I tried this out with the template specialization test case I added. If I have

```
using Alias = struct a::S;
```

then printing the aliased type of `Alias` with true for 
`IgnoreElaboratedQualifiers` and false for `SuppressTagKeyword` gives 
`"S"`. (I'm guessing that printing the template 
specialization with `SuppressTagKeyword` disabled is weird or unexpected?) So 
it would seem that `SuppressTagKeyword` still functions, as long as the 
desugared type supports it?

Back to the topic of names, it seems like "qualifiers" is not sufficient, as it 
also ignores elaborated tag keywords. Perhaps `IgnoreElaboration`?

---
Note: I initially had the name as `IgnoreElaboratedTypes`, but there was also 
the [[ 
https://github.com/llvm/llvm-project/blob/1b05e74982242da81d257f660302585cee691f3b/clang/lib/AST/TypePrinter.cpp#L1559-L1568
 | tag definition printing logic ]] that I didn't fully understand, so I 
thought just saying "qualifiers" would be more correct.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

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


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 519643.
li.zhe.hua marked an inline comment as done.
li.zhe.hua added a comment.

Accept suggested edits.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D149677

Files:
  clang/include/clang/AST/PrettyPrinter.h
  clang/lib/AST/TypePrinter.cpp
  clang/unittests/AST/TypePrinterTest.cpp

Index: clang/unittests/AST/TypePrinterTest.cpp
===
--- clang/unittests/AST/TypePrinterTest.cpp
+++ clang/unittests/AST/TypePrinterTest.cpp
@@ -97,6 +97,33 @@
  "const f *", Clean));
 }
 
+TEST(TypePrinter, IgnoreElaboratedQualifiers) {
+  llvm::StringLiteral Code = R"cpp(
+namespace shared {
+namespace a {
+template 
+struct S {};
+}  // namespace a
+namespace b {
+struct Foo {};
+}  // namespace b
+using Alias = a::S;
+}  // namespace shared
+  )cpp";
+
+  auto Matcher = typedefNameDecl(hasName("::shared::Alias"),
+ hasType(qualType().bind("id")));
+  ASSERT_TRUE(PrintedTypeMatches(
+  Code, {}, Matcher, "a::S",
+  [](PrintingPolicy ) { Policy.FullyQualifiedName = true; }));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
+ "shared::a::S",
+ [](PrintingPolicy ) {
+   Policy.IgnoreElaboratedQualifiers = true;
+   Policy.FullyQualifiedName = true;
+ }));
+}
+
 TEST(TypePrinter, TemplateIdWithNTTP) {
   constexpr char Code[] = R"cpp(
 template 
Index: clang/lib/AST/TypePrinter.cpp
===
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1567,6 +1567,11 @@
 return;
   }
 
+  if (Policy.IgnoreElaboratedQualifiers) {
+printBefore(T->getNamedType(), OS);
+return;
+  }
+
   // The tag definition will take care of these.
   if (!Policy.IncludeTagDefinition)
   {
@@ -1586,6 +1591,12 @@
 raw_ostream ) {
   if (Policy.IncludeTagDefinition && T->getOwnedTagDecl())
 return;
+
+  if (Policy.IgnoreElaboratedQualifiers) {
+printAfter(T->getNamedType(), OS);
+return;
+  }
+
   ElaboratedTypePolicyRAII PolicyRAII(Policy);
   printAfter(T->getNamedType(), OS);
 }
Index: clang/include/clang/AST/PrettyPrinter.h
===
--- clang/include/clang/AST/PrettyPrinter.h
+++ clang/include/clang/AST/PrettyPrinter.h
@@ -60,14 +60,15 @@
   : Indentation(2), SuppressSpecifiers(false),
 SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
 SuppressScope(false), SuppressUnwrittenScope(false),
-SuppressInlineNamespace(true), SuppressInitializers(false),
-ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
-SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
+SuppressInlineNamespace(true), IgnoreElaboratedQualifiers(false),
+SuppressInitializers(false), ConstantArraySizeAsWritten(false),
+AnonymousTagLocations(true), SuppressStrongLifetime(false),
+SuppressLifetimeQualifiers(false),
 SuppressTemplateArgsInCXXConstructors(false),
 SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
 Nullptr(LO.CPlusPlus11 || LO.C2x), NullptrTypeInNamespace(LO.CPlusPlus),
-Restrict(LO.C99), Alignof(LO.CPlusPlus11),
-UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
+Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11),
+UseVoidForZeroParams(!LO.CPlusPlus),
 SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
 PolishForDeclaration(false), Half(LO.Half),
 MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
@@ -139,6 +140,10 @@
   /// removed.
   unsigned SuppressInlineNamespace : 1;
 
+  /// Ignore qualifiers as specified by elaborated type sugar, instead letting
+  /// the underlying type handle printing the qualifiers.
+  unsigned IgnoreElaboratedQualifiers : 1;
+
   /// Suppress printing of variable initializers.
   ///
   /// This flag is used when printing the loop variable in a for-range
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D149677: [clang][TypePrinter] Add option to skip over elaborated types

2023-05-02 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Elaborated types are sugar that represent how the type was spelled in
the original source. When printing a type outside of that original
context, the qualifiers as saved in the elaborated type will be
incorrect. Additionally, their existence also inhibits the use of
`PrintingCallbacks::isScopeVisible` as a customization point.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D149677

Files:
  clang/include/clang/AST/PrettyPrinter.h
  clang/lib/AST/TypePrinter.cpp
  clang/unittests/AST/TypePrinterTest.cpp

Index: clang/unittests/AST/TypePrinterTest.cpp
===
--- clang/unittests/AST/TypePrinterTest.cpp
+++ clang/unittests/AST/TypePrinterTest.cpp
@@ -97,6 +97,35 @@
  "const f *", Clean));
 }
 
+TEST(TypePrinter, IgnoreElaboratedQualifiers) {
+  llvm::StringLiteral Code = R"cpp(
+namespace shared {
+namespace a {
+template 
+struct S {};
+}  // namespace a
+namespace b {
+struct Foo {};
+}  // namespace b
+using Alias = a::S;
+}  // namespace shared
+  )cpp";
+
+  auto Matcher = typedefNameDecl(hasName("::shared::Alias"),
+ hasType(qualType().bind("id")));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, //
+ "a::S",//
+ [](PrintingPolicy ) {
+   Policy.FullyQualifiedName = true; //
+ }));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
+ "shared::a::S",
+ [](PrintingPolicy ) {
+   Policy.IgnoreElaboratedQualifiers = true;
+   Policy.FullyQualifiedName = true;
+ }));
+}
+
 TEST(TypePrinter, TemplateIdWithNTTP) {
   constexpr char Code[] = R"cpp(
 template 
Index: clang/lib/AST/TypePrinter.cpp
===
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1567,6 +1567,11 @@
 return;
   }
 
+  if (Policy.IgnoreElaboratedQualifiers) {
+printBefore(T->getNamedType(), OS);
+return;
+  }
+
   // The tag definition will take care of these.
   if (!Policy.IncludeTagDefinition)
   {
@@ -1586,6 +1591,12 @@
 raw_ostream ) {
   if (Policy.IncludeTagDefinition && T->getOwnedTagDecl())
 return;
+
+  if (Policy.IgnoreElaboratedQualifiers) {
+printAfter(T->getNamedType(), OS);
+return;
+  }
+
   ElaboratedTypePolicyRAII PolicyRAII(Policy);
   printAfter(T->getNamedType(), OS);
 }
Index: clang/include/clang/AST/PrettyPrinter.h
===
--- clang/include/clang/AST/PrettyPrinter.h
+++ clang/include/clang/AST/PrettyPrinter.h
@@ -60,14 +60,15 @@
   : Indentation(2), SuppressSpecifiers(false),
 SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
 SuppressScope(false), SuppressUnwrittenScope(false),
-SuppressInlineNamespace(true), SuppressInitializers(false),
-ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
-SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
+SuppressInlineNamespace(true), IgnoreElaboratedQualifiers(false),
+SuppressInitializers(false), ConstantArraySizeAsWritten(false),
+AnonymousTagLocations(true), SuppressStrongLifetime(false),
+SuppressLifetimeQualifiers(false),
 SuppressTemplateArgsInCXXConstructors(false),
 SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
 Nullptr(LO.CPlusPlus11 || LO.C2x), NullptrTypeInNamespace(LO.CPlusPlus),
-Restrict(LO.C99), Alignof(LO.CPlusPlus11),
-UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
+Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11),
+UseVoidForZeroParams(!LO.CPlusPlus),
 SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
 PolishForDeclaration(false), Half(LO.Half),
 MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
@@ -139,6 +140,10 @@
   /// removed.
   unsigned SuppressInlineNamespace : 1;
 
+  /// Ignore qualifiers as specified by elaborated type sugar, instead letting
+  /// the underlying type handle printing the qualifiers.
+  unsigned IgnoreElaboratedQualifiers : 1;
+
   /// Suppress printing of variable initializers.
   ///
   /// This flag is used when printing the loop variable in a for-range
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D141636: [libTooling] Rename `getRangeForEdit` as `getFileRangeForEdit`

2023-01-18 Thread Eric Li 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 rG2307029b1a43: [libTooling] Rename `getRangeForEdit` as 
`getFileRangeForEdit` (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141636

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@
 using tooling::getAssociatedRange;
 using tooling::getExtendedRange;
 using tooling::getExtendedText;
-using tooling::getRangeForEdit;
+using tooling::getFileRangeForEdit;
 using tooling::getText;
 using tooling::maybeExtendRange;
 using tooling::validateEditRange;
@@ -453,11 +453,11 @@
   Visitor.runOver(Code);
 }
 
-class GetRangeForEditTest : public testing::TestWithParam {};
-INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEditTest,
+class GetFileRangeForEditTest : public testing::TestWithParam {};
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetFileRangeForEditTest,
  testing::Bool());
 
-TEST_P(GetRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
   // The call expression, whose range we are extracting, includes two macro
   // expansions.
   llvm::Annotations Code(R"cpp(
@@ -469,7 +469,7 @@
   CallsVisitor Visitor;
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -484,7 +484,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -507,12 +507,12 @@
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_FALSE(
-getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+getFileRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
   };
   Visitor.runOver(Code.code());
 }
 
-TEST_P(GetRangeForEditTest, EditPartialMacroExpansionShouldFail) {
+TEST_P(GetFileRangeForEditTest, EditPartialMacroExpansionShouldFail) {
   std::string Code = R"cpp(
 #define BAR 10+
 int c = BAR 3.0;
@@ -521,12 +521,12 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
+EXPECT_FALSE(getFileRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
 
-TEST_P(GetRangeForEditTest, EditWholeMacroArgShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditWholeMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]]);
@@ -535,13 +535,13 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
 }
 
-TEST_P(GetRangeForEditTest, EditPartialMacroArgShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditPartialMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]] + 10.0);
@@ -550,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- 

[PATCH] D141636: [libTooling] Rename `getRangeForEdit` as `getFileRangeForEdit`

2023-01-18 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 490248.
li.zhe.hua added a comment.

Update test name


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141636

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@
 using tooling::getAssociatedRange;
 using tooling::getExtendedRange;
 using tooling::getExtendedText;
-using tooling::getRangeForEdit;
+using tooling::getFileRangeForEdit;
 using tooling::getText;
 using tooling::maybeExtendRange;
 using tooling::validateEditRange;
@@ -453,11 +453,11 @@
   Visitor.runOver(Code);
 }
 
-class GetRangeForEditTest : public testing::TestWithParam {};
-INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEditTest,
+class GetFileRangeForEditTest : public testing::TestWithParam {};
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetFileRangeForEditTest,
  testing::Bool());
 
-TEST_P(GetRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
   // The call expression, whose range we are extracting, includes two macro
   // expansions.
   llvm::Annotations Code(R"cpp(
@@ -469,7 +469,7 @@
   CallsVisitor Visitor;
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -484,7 +484,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -507,12 +507,12 @@
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_FALSE(
-getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+getFileRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
   };
   Visitor.runOver(Code.code());
 }
 
-TEST_P(GetRangeForEditTest, EditPartialMacroExpansionShouldFail) {
+TEST_P(GetFileRangeForEditTest, EditPartialMacroExpansionShouldFail) {
   std::string Code = R"cpp(
 #define BAR 10+
 int c = BAR 3.0;
@@ -521,12 +521,12 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
+EXPECT_FALSE(getFileRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
 
-TEST_P(GetRangeForEditTest, EditWholeMacroArgShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditWholeMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]]);
@@ -535,13 +535,13 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
 }
 
-TEST_P(GetRangeForEditTest, EditPartialMacroArgShouldSucceed) {
+TEST_P(GetFileRangeForEditTest, EditPartialMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]] + 10.0);
@@ -550,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -122,7 +122,7 @@
   return Range;
 }
 
-std::optional 

[PATCH] D141634: [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li 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 rG7d0cdbf69edf: [libTooling] Add `getFileRange` as an 
alternative to `getRangeForEdit` (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141634

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp

Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@
   return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
 }
 
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
-  const SourceManager ) {
+static llvm::Error validateRange(const CharSourceRange ,
+ const SourceManager ,
+ bool AllowSystemHeaders) {
   if (Range.isInvalid())
 return llvm::make_error(errc::invalid_argument,
  "Invalid range");
@@ -60,10 +61,12 @@
 return llvm::make_error(
 errc::invalid_argument, "Range starts or ends in a macro expansion");
 
-  if (SM.isInSystemHeader(Range.getBegin()) ||
-  SM.isInSystemHeader(Range.getEnd()))
-return llvm::make_error(errc::invalid_argument,
- "Range is in system header");
+  if (!AllowSystemHeaders) {
+if (SM.isInSystemHeader(Range.getBegin()) ||
+SM.isInSystemHeader(Range.getEnd()))
+  return llvm::make_error(errc::invalid_argument,
+   "Range is in system header");
+  }
 
   std::pair BeginInfo = SM.getDecomposedLoc(Range.getBegin());
   std::pair EndInfo = SM.getDecomposedLoc(Range.getEnd());
@@ -72,13 +75,18 @@
 errc::invalid_argument, "Range begins and ends in different files");
 
   if (BeginInfo.second > EndInfo.second)
-return llvm::make_error(
-errc::invalid_argument, "Range's begin is past its end");
+return llvm::make_error(errc::invalid_argument,
+ "Range's begin is past its end");
 
   return llvm::Error::success();
 }
 
-static bool SpelledInMacroDefinition(SourceLocation Loc,
+llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
+  const SourceManager ) {
+  return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
+}
+
+static bool spelledInMacroDefinition(SourceLocation Loc,
  const SourceManager ) {
   while (Loc.isMacroID()) {
 const auto  = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
@@ -93,16 +101,17 @@
   return false;
 }
 
-std::optional clang::tooling::getRangeForEdit(
-const CharSourceRange , const SourceManager ,
-const LangOptions , bool IncludeMacroExpansion) {
+static CharSourceRange getRange(const CharSourceRange ,
+const SourceManager ,
+const LangOptions ,
+bool IncludeMacroExpansion) {
   CharSourceRange Range;
   if (IncludeMacroExpansion) {
 Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
-if (SpelledInMacroDefinition(EditRange.getBegin(), SM) ||
-SpelledInMacroDefinition(EditRange.getEnd(), SM))
-  return std::nullopt;
+if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
+spelledInMacroDefinition(EditRange.getEnd(), SM))
+  return {};
 
 auto B = SM.getSpellingLoc(EditRange.getBegin());
 auto E = SM.getSpellingLoc(EditRange.getEnd());
@@ -110,13 +119,32 @@
   E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
 Range = CharSourceRange::getCharRange(B, E);
   }
+  return Range;
+}
 
+std::optional clang::tooling::getRangeForEdit(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
   bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM));
   if (IsInvalid)
 return std::nullopt;
   return Range;
 }
 
+std::optional clang::tooling::getFileRange(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
+  bool IsInvalid =
+  llvm::errorToBool(validateRange(Range, SM, /*AllowSystemHeaders=*/true));
+  if (IsInvalid)
+return std::nullopt;
+  return Range;
+}
+
 static bool startsWithNewline(const SourceManager , const Token ) {
   return isVerticalWhitespace(SM.getCharacterData(Tok.getLocation())[0]);
 }
Index: 

[PATCH] D141634: [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 488780.
li.zhe.hua added a comment.

Try this again...


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141634

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp

Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@
   return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
 }
 
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
-  const SourceManager ) {
+static llvm::Error validateRange(const CharSourceRange ,
+ const SourceManager ,
+ bool AllowSystemHeaders) {
   if (Range.isInvalid())
 return llvm::make_error(errc::invalid_argument,
  "Invalid range");
@@ -60,10 +61,12 @@
 return llvm::make_error(
 errc::invalid_argument, "Range starts or ends in a macro expansion");
 
-  if (SM.isInSystemHeader(Range.getBegin()) ||
-  SM.isInSystemHeader(Range.getEnd()))
-return llvm::make_error(errc::invalid_argument,
- "Range is in system header");
+  if (!AllowSystemHeaders) {
+if (SM.isInSystemHeader(Range.getBegin()) ||
+SM.isInSystemHeader(Range.getEnd()))
+  return llvm::make_error(errc::invalid_argument,
+   "Range is in system header");
+  }
 
   std::pair BeginInfo = SM.getDecomposedLoc(Range.getBegin());
   std::pair EndInfo = SM.getDecomposedLoc(Range.getEnd());
@@ -72,13 +75,18 @@
 errc::invalid_argument, "Range begins and ends in different files");
 
   if (BeginInfo.second > EndInfo.second)
-return llvm::make_error(
-errc::invalid_argument, "Range's begin is past its end");
+return llvm::make_error(errc::invalid_argument,
+ "Range's begin is past its end");
 
   return llvm::Error::success();
 }
 
-static bool SpelledInMacroDefinition(SourceLocation Loc,
+llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
+  const SourceManager ) {
+  return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
+}
+
+static bool spelledInMacroDefinition(SourceLocation Loc,
  const SourceManager ) {
   while (Loc.isMacroID()) {
 const auto  = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
@@ -93,16 +101,17 @@
   return false;
 }
 
-std::optional clang::tooling::getRangeForEdit(
-const CharSourceRange , const SourceManager ,
-const LangOptions , bool IncludeMacroExpansion) {
+static CharSourceRange getRange(const CharSourceRange ,
+const SourceManager ,
+const LangOptions ,
+bool IncludeMacroExpansion) {
   CharSourceRange Range;
   if (IncludeMacroExpansion) {
 Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
-if (SpelledInMacroDefinition(EditRange.getBegin(), SM) ||
-SpelledInMacroDefinition(EditRange.getEnd(), SM))
-  return std::nullopt;
+if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
+spelledInMacroDefinition(EditRange.getEnd(), SM))
+  return {};
 
 auto B = SM.getSpellingLoc(EditRange.getBegin());
 auto E = SM.getSpellingLoc(EditRange.getEnd());
@@ -110,13 +119,32 @@
   E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
 Range = CharSourceRange::getCharRange(B, E);
   }
+  return Range;
+}
 
+std::optional clang::tooling::getRangeForEdit(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
   bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM));
   if (IsInvalid)
 return std::nullopt;
   return Range;
 }
 
+std::optional clang::tooling::getFileRange(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
+  bool IsInvalid =
+  llvm::errorToBool(validateRange(Range, SM, /*AllowSystemHeaders=*/true));
+  if (IsInvalid)
+return std::nullopt;
+  return Range;
+}
+
 static bool startsWithNewline(const SourceManager , const Token ) {
   return isVerticalWhitespace(SM.getCharacterData(Tok.getLocation())[0]);
 }
Index: clang/include/clang/Tooling/Transformer/SourceCode.h
===
--- clang/include/clang/Tooling/Transformer/SourceCode.h

[PATCH] D141634: [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 488779.
li.zhe.hua added a comment.

Upload correct commit this time...


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141634

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@
 using tooling::getAssociatedRange;
 using tooling::getExtendedRange;
 using tooling::getExtendedText;
-using tooling::getRangeForEdit;
+using tooling::getFileRangeForEdit;
 using tooling::getText;
 using tooling::maybeExtendRange;
 using tooling::validateEditRange;
@@ -469,7 +469,7 @@
   CallsVisitor Visitor;
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -484,7 +484,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -507,7 +507,7 @@
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_FALSE(
-getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+getFileRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
   };
   Visitor.runOver(Code.code());
 }
@@ -521,7 +521,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
+EXPECT_FALSE(getFileRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
@@ -535,7 +535,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -550,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@
   return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
 }
 
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
-  const SourceManager ) {
+static llvm::Error validateRange(const CharSourceRange ,
+ const SourceManager ,
+ bool AllowSystemHeaders) {
   if (Range.isInvalid())
 return llvm::make_error(errc::invalid_argument,
  "Invalid range");
@@ -60,10 +61,12 @@
 return llvm::make_error(
 errc::invalid_argument, "Range starts or ends in a macro expansion");
 
-  if (SM.isInSystemHeader(Range.getBegin()) ||
-  SM.isInSystemHeader(Range.getEnd()))
-return llvm::make_error(errc::invalid_argument,
- "Range is in system header");
+  if (!AllowSystemHeaders) {
+if (SM.isInSystemHeader(Range.getBegin()) ||
+SM.isInSystemHeader(Range.getEnd()))
+  return llvm::make_error(errc::invalid_argument,
+   "Range is in system header");
+  }
 
   std::pair BeginInfo = SM.getDecomposedLoc(Range.getBegin());
   std::pair EndInfo = SM.getDecomposedLoc(Range.getEnd());
@@ -72,13 +75,18 @@
 errc::invalid_argument, "Range begins and ends 

[PATCH] D141634: [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 488778.
li.zhe.hua added a comment.

Fix description.
Remove default argument.
Fix copy/paste error.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D141634

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@
 using tooling::getAssociatedRange;
 using tooling::getExtendedRange;
 using tooling::getExtendedText;
-using tooling::getRangeForEdit;
+using tooling::getFileRangeForEdit;
 using tooling::getText;
 using tooling::maybeExtendRange;
 using tooling::validateEditRange;
@@ -469,7 +469,7 @@
   CallsVisitor Visitor;
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -484,7 +484,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -507,7 +507,7 @@
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_FALSE(
-getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+getFileRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
   };
   Visitor.runOver(Code.code());
 }
@@ -521,7 +521,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
+EXPECT_FALSE(getFileRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
@@ -535,7 +535,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -550,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -122,7 +122,7 @@
   return Range;
 }
 
-std::optional clang::tooling::getRangeForEdit(
+std::optional clang::tooling::getFileRangeForEdit(
 const CharSourceRange , const SourceManager ,
 const LangOptions , bool IncludeMacroExpansion) {
   CharSourceRange Range =
Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -39,7 +39,7 @@
 if (!Range)
   return Range.takeError();
 std::optional EditRange =
-tooling::getRangeForEdit(*Range, *Result.Context);
+tooling::getFileRangeForEdit(*Range, *Result.Context);
 // FIXME: let user specify whether to treat this case as an error or ignore
 // it as is currently done. This behavior is problematic in that it hides
 // failures from bad ranges. Also, the behavior here differs from
@@ -449,7 +449,7 @@
   auto  = Result.Nodes.getMap();
   auto Root = NodesMap.find(RootID);
   assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
-  std::optional RootRange = tooling::getRangeForEdit(
+  std::optional RootRange = tooling::getFileRangeForEdit(
   CharSourceRange::getTokenRange(Root->second.getSourceRange()),
   *Result.Context);
   if 

[PATCH] D141636: [libTooling] Rename `getRangeForEdit` as `getFileRangeForEdit`

2023-01-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

With the addition of `getFileRange`, we rename `getRangeForEdit` as
`getFileRangeForEdit` for consistency in the API.

Depends on D141634 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D141636

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/RewriteRule.cpp
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@
 using tooling::getAssociatedRange;
 using tooling::getExtendedRange;
 using tooling::getExtendedText;
-using tooling::getRangeForEdit;
+using tooling::getFileRangeForEdit;
 using tooling::getText;
 using tooling::maybeExtendRange;
 using tooling::validateEditRange;
@@ -469,7 +469,7 @@
   CallsVisitor Visitor;
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -484,7 +484,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -507,7 +507,7 @@
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_FALSE(
-getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+getFileRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
   };
   Visitor.runOver(Code.code());
 }
@@ -521,7 +521,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
+EXPECT_FALSE(getFileRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
@@ -535,7 +535,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -550,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
+EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -122,7 +122,7 @@
   return Range;
 }
 
-std::optional clang::tooling::getRangeForEdit(
+std::optional clang::tooling::getFileRangeForEdit(
 const CharSourceRange , const SourceManager ,
 const LangOptions , bool IncludeMacroExpansion) {
   CharSourceRange Range =
Index: clang/lib/Tooling/Transformer/RewriteRule.cpp
===
--- clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -39,7 +39,7 @@
 if (!Range)
   return Range.takeError();
 std::optional EditRange =
-tooling::getRangeForEdit(*Range, *Result.Context);
+tooling::getFileRangeForEdit(*Range, *Result.Context);
 // FIXME: let user specify whether to treat this case as an error or ignore
 // it as is currently done. This behavior is problematic in that it hides
 // failures from bad ranges. Also, the behavior here differs from
@@ -449,7 +449,7 @@
   auto  = Result.Nodes.getMap();
   auto Root = NodesMap.find(RootID);
   assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
-  std::optional RootRange = 

[PATCH] D141634: [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Add a `getFileRange` function alongside the existing `getRangeForEdit`
as a way to get a contiguous range within a single file (similar to
`getRangeForEdit`) but without the restriction that it cannot be in a
system header.

This can be used where a tool may want to use the range to extract the
source text. In such cases, we don't want to restrict this from
pulling from system headers.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D141634

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp

Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@
   return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
 }
 
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
-  const SourceManager ) {
+static llvm::Error validateRange(const CharSourceRange ,
+ const SourceManager ,
+ bool AllowSystemHeaders) {
   if (Range.isInvalid())
 return llvm::make_error(errc::invalid_argument,
  "Invalid range");
@@ -60,10 +61,12 @@
 return llvm::make_error(
 errc::invalid_argument, "Range starts or ends in a macro expansion");
 
-  if (SM.isInSystemHeader(Range.getBegin()) ||
-  SM.isInSystemHeader(Range.getEnd()))
-return llvm::make_error(errc::invalid_argument,
- "Range is in system header");
+  if (!AllowSystemHeaders) {
+if (SM.isInSystemHeader(Range.getBegin()) ||
+SM.isInSystemHeader(Range.getEnd()))
+  return llvm::make_error(errc::invalid_argument,
+   "Range is in system header");
+  }
 
   std::pair BeginInfo = SM.getDecomposedLoc(Range.getBegin());
   std::pair EndInfo = SM.getDecomposedLoc(Range.getEnd());
@@ -72,13 +75,18 @@
 errc::invalid_argument, "Range begins and ends in different files");
 
   if (BeginInfo.second > EndInfo.second)
-return llvm::make_error(
-errc::invalid_argument, "Range's begin is past its end");
+return llvm::make_error(errc::invalid_argument,
+ "Range's begin is past its end");
 
   return llvm::Error::success();
 }
 
-static bool SpelledInMacroDefinition(SourceLocation Loc,
+llvm::Error clang::tooling::validateEditRange(const CharSourceRange ,
+  const SourceManager ) {
+  return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
+}
+
+static bool spelledInMacroDefinition(SourceLocation Loc,
  const SourceManager ) {
   while (Loc.isMacroID()) {
 const auto  = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
@@ -93,16 +101,17 @@
   return false;
 }
 
-std::optional clang::tooling::getRangeForEdit(
-const CharSourceRange , const SourceManager ,
-const LangOptions , bool IncludeMacroExpansion) {
+static CharSourceRange getRange(const CharSourceRange ,
+const SourceManager ,
+const LangOptions ,
+bool IncludeMacroExpansion) {
   CharSourceRange Range;
   if (IncludeMacroExpansion) {
 Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
-if (SpelledInMacroDefinition(EditRange.getBegin(), SM) ||
-SpelledInMacroDefinition(EditRange.getEnd(), SM))
-  return std::nullopt;
+if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
+spelledInMacroDefinition(EditRange.getEnd(), SM))
+  return {};
 
 auto B = SM.getSpellingLoc(EditRange.getBegin());
 auto E = SM.getSpellingLoc(EditRange.getEnd());
@@ -110,13 +119,32 @@
   E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
 Range = CharSourceRange::getCharRange(B, E);
   }
+  return Range;
+}
 
+std::optional clang::tooling::getRangeForEdit(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
   bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM));
   if (IsInvalid)
 return std::nullopt;
   return Range;
 }
 
+std::optional clang::tooling::getFileRange(
+const CharSourceRange , const SourceManager ,
+const LangOptions , bool IncludeMacroExpansion) {
+  CharSourceRange Range =
+  getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
+  bool IsInvalid =
+  

[PATCH] D139676: [libTooling] Add flag to getRangeForEdit to ignore macro expansions

2022-12-08 Thread Eric Li via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
li.zhe.hua marked an inline comment as done.
Closed by commit rGa78d4b5ba716: [libTooling] Add flag to getRangeForEdit to 
ignore macro expansions (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139676

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -453,7 +453,11 @@
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditRangeWithMacroExpansionsShouldSucceed) {
+class GetRangeForEditTest : public testing::TestWithParam {};
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEditTest,
+ testing::Bool());
+
+TEST_P(GetRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
   // The call expression, whose range we are extracting, includes two macro
   // expansions.
   llvm::Annotations Code(R"cpp(
@@ -463,10 +467,9 @@
 )cpp");
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -487,7 +490,29 @@
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroExpansionShouldFail) {
+TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
+  // If we specify to ignore macro expansions, none of these call expressions
+  // should have an editable range.
+  llvm::Annotations Code(R"cpp(
+#define M1(x) x(1)
+#define M2(x, y) x ## y
+#define M3(x) foobar(x)
+int foobar(int);
+int a = M1(foobar);
+int b = M2(foo, bar(2));
+int c = M3(3);
+)cpp");
+
+  CallsVisitor Visitor;
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
+EXPECT_FALSE(
+getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+  };
+  Visitor.runOver(Code.code());
+}
+
+TEST_P(GetRangeForEditTest, EditPartialMacroExpansionShouldFail) {
   std::string Code = R"cpp(
 #define BAR 10+
 int c = BAR 3.0;
@@ -496,12 +521,12 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context));
+EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditWholeMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditWholeMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]]);
@@ -510,13 +535,13 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditPartialMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]] + 10.0);
@@ -525,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -541,7 +566,6 @@
 )cpp";
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_THAT_ERROR(validateEditRange(Range, Context->getSourceManager()),
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -78,27 +78,43 @@
   return llvm::Error::success();
 }
 
-llvm::Optional
-clang::tooling::getRangeForEdit(const CharSourceRange ,
-const SourceManager ,
-

[PATCH] D139676: [libTooling] Add flag to getRangeForEdit to ignore macro expansions

2022-12-08 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added inline comments.



Comment at: clang/lib/Tooling/Transformer/SourceCode.cpp:90-92
+  Loc = Expansion.getExpansionLocStart();
+  if (Loc.isFileID())
+return true;

ymandel wrote:
> can you comment on the logic here? Why isn't this just "return true", since 
> it's in a macro but not a part of a macro arg, I'd think it must be part of a 
> macro definition.
Good catch, it should just return true.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139676

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


[PATCH] D139676: [libTooling] Add flag to getRangeForEdit to ignore macro expansions

2022-12-08 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 481506.
li.zhe.hua added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139676

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -453,7 +453,11 @@
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditRangeWithMacroExpansionsShouldSucceed) {
+class GetRangeForEditTest : public testing::TestWithParam {};
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEditTest,
+ testing::Bool());
+
+TEST_P(GetRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
   // The call expression, whose range we are extracting, includes two macro
   // expansions.
   llvm::Annotations Code(R"cpp(
@@ -463,10 +467,9 @@
 )cpp");
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -487,7 +490,29 @@
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroExpansionShouldFail) {
+TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
+  // If we specify to ignore macro expansions, none of these call expressions
+  // should have an editable range.
+  llvm::Annotations Code(R"cpp(
+#define M1(x) x(1)
+#define M2(x, y) x ## y
+#define M3(x) foobar(x)
+int foobar(int);
+int a = M1(foobar);
+int b = M2(foo, bar(2));
+int c = M3(3);
+)cpp");
+
+  CallsVisitor Visitor;
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
+EXPECT_FALSE(
+getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+  };
+  Visitor.runOver(Code.code());
+}
+
+TEST_P(GetRangeForEditTest, EditPartialMacroExpansionShouldFail) {
   std::string Code = R"cpp(
 #define BAR 10+
 int c = BAR 3.0;
@@ -496,12 +521,12 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context));
+EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditWholeMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditWholeMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]]);
@@ -510,13 +535,13 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditPartialMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]] + 10.0);
@@ -525,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -541,7 +566,6 @@
 )cpp";
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_THAT_ERROR(validateEditRange(Range, Context->getSourceManager()),
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -78,27 +78,43 @@
   return llvm::Error::success();
 }
 
-llvm::Optional
-clang::tooling::getRangeForEdit(const CharSourceRange ,
-const SourceManager ,
-const LangOptions ) {
-  // FIXME: makeFileCharRange() has the disadvantage of stripping off "identity"
-  // macros. For example, if we're looking to rewrite the int literal 3 to 6,
-  // and we have the 

[PATCH] D139676: [libTooling] Add flag to getRangeForEdit to ignore macro expansions

2022-12-08 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This commit resolves the FIXME around the behavior of
`Lexer::makeFileCharRange` that `getRangeForEdit` inherits around
source locations in macro expansions.

We add a flag to `getRangeForEdit` that allows a caller to disable the
behavior, and instead uses the spelling location instead, with checks
to ensure that the source locations are not within a macro definition.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D139676

Files:
  clang/include/clang/Tooling/Transformer/SourceCode.h
  clang/lib/Tooling/Transformer/SourceCode.cpp
  clang/unittests/Tooling/SourceCodeTest.cpp

Index: clang/unittests/Tooling/SourceCodeTest.cpp
===
--- clang/unittests/Tooling/SourceCodeTest.cpp
+++ clang/unittests/Tooling/SourceCodeTest.cpp
@@ -453,7 +453,11 @@
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditRangeWithMacroExpansionsShouldSucceed) {
+class GetRangeForEditTest : public testing::TestWithParam {};
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEditTest,
+ testing::Bool());
+
+TEST_P(GetRangeForEditTest, EditRangeWithMacroExpansionsShouldSucceed) {
   // The call expression, whose range we are extracting, includes two macro
   // expansions.
   llvm::Annotations Code(R"cpp(
@@ -463,10 +467,9 @@
 )cpp");
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -487,7 +490,29 @@
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroExpansionShouldFail) {
+TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
+  // If we specify to ignore macro expansions, none of these call expressions
+  // should have an editable range.
+  llvm::Annotations Code(R"cpp(
+#define M1(x) x(1)
+#define M2(x, y) x ## y
+#define M3(x) foobar(x)
+int foobar(int);
+int a = M1(foobar);
+int b = M2(foo, bar(2));
+int c = M3(3);
+)cpp");
+
+  CallsVisitor Visitor;
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
+EXPECT_FALSE(
+getRangeForEdit(Range, *Context, /*IncludeMacroExpansion=*/false));
+  };
+  Visitor.runOver(Code.code());
+}
+
+TEST_P(GetRangeForEditTest, EditPartialMacroExpansionShouldFail) {
   std::string Code = R"cpp(
 #define BAR 10+
 int c = BAR 3.0;
@@ -496,12 +521,12 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_FALSE(getRangeForEdit(Range, *Context));
+EXPECT_FALSE(getRangeForEdit(Range, *Context, GetParam()));
   };
   Visitor.runOver(Code);
 }
 
-TEST(SourceCodeTest, EditWholeMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditWholeMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]]);
@@ -510,13 +535,13 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
 }
 
-TEST(SourceCodeTest, EditPartialMacroArgShouldSucceed) {
+TEST_P(GetRangeForEditTest, EditPartialMacroArgShouldSucceed) {
   llvm::Annotations Code(R"cpp(
 #define FOO(a) a + 7.0;
 int a = FOO($r[[10]] + 10.0);
@@ -525,7 +550,7 @@
   IntLitVisitor Visitor;
   Visitor.OnIntLit = [](IntegerLiteral *Expr, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
-EXPECT_THAT(getRangeForEdit(Range, *Context),
+EXPECT_THAT(getRangeForEdit(Range, *Context, GetParam()),
 ValueIs(AsRange(Context->getSourceManager(), Code.range("r";
   };
   Visitor.runOver(Code.code());
@@ -541,7 +566,6 @@
 )cpp";
 
   CallsVisitor Visitor;
-
   Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
 auto Range = CharSourceRange::getTokenRange(CE->getSourceRange());
 EXPECT_THAT_ERROR(validateEditRange(Range, Context->getSourceManager()),
Index: clang/lib/Tooling/Transformer/SourceCode.cpp
===
--- clang/lib/Tooling/Transformer/SourceCode.cpp
+++ clang/lib/Tooling/Transformer/SourceCode.cpp

[PATCH] D137948: [clang][dataflow] Add widening API and implement it for built-in boolean model.

2022-11-15 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h:166
+
+  // The second-choice implementation: `transferBranch` is implemented. No-op.
+  template 

I think this is missing a word.



Comment at: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h:227
+  /// Widens the environment point-wise, using `PrevEnv` as needed to inform 
the
+  /// approximation. by taking the intersection of storage locations and values
+  /// that are stored in them. Distinct values that are assigned to the same




Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D137948

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


[PATCH] D135816: [clang] Support `constexpr` for some `ASTNodeKind` member functions

2022-10-13 Thread Eric Li via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
li.zhe.hua marked an inline comment as done.
Closed by commit rG576283c3a8ef: [clang] Support `constexpr` for some 
`ASTNodeKind` member functions (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135816

Files:
  clang/include/clang/AST/ASTTypeTraits.h
  clang/unittests/AST/ASTTypeTraitsTest.cpp

Index: clang/unittests/AST/ASTTypeTraitsTest.cpp
===
--- clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -117,6 +117,47 @@
   EXPECT_FALSE(DNT().isSame(DNT()));
 }
 
+template 
+constexpr bool HasPointerIdentity =
+ASTNodeKind::getFromNodeKind().hasPointerIdentity();
+
+TEST(ASTNodeKind, ConstexprHasPointerIdentity) {
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+
+  constexpr bool DefaultConstructedHasPointerIdentity =
+  ASTNodeKind().hasPointerIdentity();
+  EXPECT_FALSE(DefaultConstructedHasPointerIdentity);
+}
+
+template 
+constexpr bool NodeKindIsSame =
+ASTNodeKind::getFromNodeKind().isSame(ASTNodeKind::getFromNodeKind());
+
+TEST(ASTNodeKind, ConstexprIsSame) {
+  EXPECT_TRUE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+
+  constexpr bool DefaultConstructedIsSameToDefaultConstructed =
+  ASTNodeKind().isSame(ASTNodeKind());
+  EXPECT_FALSE(DefaultConstructedIsSameToDefaultConstructed);
+}
+
+template 
+constexpr bool NodeKindIsNone = ASTNodeKind::getFromNodeKind().isNone();
+
+TEST(ASTNodeKind, ConstexprIsNone) {
+  EXPECT_FALSE(NodeKindIsNone);
+  EXPECT_TRUE(NodeKindIsNone);
+
+  constexpr bool DefaultConstructedIsNone = ASTNodeKind().isNone();
+  EXPECT_TRUE(DefaultConstructedIsNone);
+}
+
 TEST(ASTNodeKind, Name) {
   EXPECT_EQ("", ASTNodeKind().asStringRef());
 #define VERIFY_NAME(Node) EXPECT_EQ(#Node, DNT().asStringRef());
Index: clang/include/clang/AST/ASTTypeTraits.h
===
--- clang/include/clang/AST/ASTTypeTraits.h
+++ clang/include/clang/AST/ASTTypeTraits.h
@@ -51,11 +51,10 @@
 class ASTNodeKind {
 public:
   /// Empty identifier. It matches nothing.
-  ASTNodeKind() : KindId(NKI_None) {}
+  constexpr ASTNodeKind() : KindId(NKI_None) {}
 
   /// Construct an identifier for T.
-  template 
-  static ASTNodeKind getFromNodeKind() {
+  template  static constexpr ASTNodeKind getFromNodeKind() {
 return ASTNodeKind(KindToKindId::Id);
   }
 
@@ -71,12 +70,12 @@
   /// \}
 
   /// Returns \c true if \c this and \c Other represent the same kind.
-  bool isSame(ASTNodeKind Other) const {
+  constexpr bool isSame(ASTNodeKind Other) const {
 return KindId != NKI_None && KindId == Other.KindId;
   }
 
   /// Returns \c true only for the default \c ASTNodeKind()
-  bool isNone() const { return KindId == NKI_None; }
+  constexpr bool isNone() const { return KindId == NKI_None; }
 
   /// Returns \c true if \c this is a base kind of (or same as) \c Other.
   /// \param Distance If non-null, used to return the distance between \c this
@@ -87,7 +86,7 @@
   StringRef asStringRef() const;
 
   /// Strict weak ordering for ASTNodeKind.
-  bool operator<(const ASTNodeKind ) const {
+  constexpr bool operator<(const ASTNodeKind ) const {
 return KindId < Other.KindId;
   }
 
@@ -121,7 +120,7 @@
 
   /// Check if the given ASTNodeKind identifies a type that offers pointer
   /// identity. This is useful for the fast path in DynTypedNode.
-  bool hasPointerIdentity() const {
+  constexpr bool hasPointerIdentity() const {
 return KindId > NKI_LastKindWithoutPointerIdentity;
   }
 
@@ -165,7 +164,7 @@
   };
 
   /// Use getFromNodeKind() to construct the kind.
-  ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
+  constexpr ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
 
   /// Returns \c true if \c Base is a base kind of (or same as) \c
   ///   Derived.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D135816: [clang] Support `constexpr` for some `ASTNodeKind` member functions

2022-10-13 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: clang/include/clang/AST/ASTTypeTraits.h:90-92
   bool operator<(const ASTNodeKind ) const {
 return KindId < Other.KindId;
   }

aaron.ballman wrote:
> Any reason for this to not be `constexpr` as well?
Not in particular. I really only needed `hasPointerIdentity`, but figured it'd 
be odd not to support `isSame` or `isNone`, so the same probably goes for 
`operator<`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135816

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


[PATCH] D135816: [clang] Support `constexpr` for some `ASTNodeKind` member functions

2022-10-13 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 467512.
li.zhe.hua added a comment.

Appease clang-format, add constexpr to operator<.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135816

Files:
  clang/include/clang/AST/ASTTypeTraits.h
  clang/unittests/AST/ASTTypeTraitsTest.cpp

Index: clang/unittests/AST/ASTTypeTraitsTest.cpp
===
--- clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -117,6 +117,47 @@
   EXPECT_FALSE(DNT().isSame(DNT()));
 }
 
+template 
+constexpr bool HasPointerIdentity =
+ASTNodeKind::getFromNodeKind().hasPointerIdentity();
+
+TEST(ASTNodeKind, ConstexprHasPointerIdentity) {
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+
+  constexpr bool DefaultConstructedHasPointerIdentity =
+  ASTNodeKind().hasPointerIdentity();
+  EXPECT_FALSE(DefaultConstructedHasPointerIdentity);
+}
+
+template 
+constexpr bool NodeKindIsSame =
+ASTNodeKind::getFromNodeKind().isSame(ASTNodeKind::getFromNodeKind());
+
+TEST(ASTNodeKind, ConstexprIsSame) {
+  EXPECT_TRUE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+
+  constexpr bool DefaultConstructedIsSameToDefaultConstructed =
+  ASTNodeKind().isSame(ASTNodeKind());
+  EXPECT_FALSE(DefaultConstructedIsSameToDefaultConstructed);
+}
+
+template 
+constexpr bool NodeKindIsNone = ASTNodeKind::getFromNodeKind().isNone();
+
+TEST(ASTNodeKind, ConstexprIsNone) {
+  EXPECT_FALSE(NodeKindIsNone);
+  EXPECT_TRUE(NodeKindIsNone);
+
+  constexpr bool DefaultConstructedIsNone = ASTNodeKind().isNone();
+  EXPECT_TRUE(DefaultConstructedIsNone);
+}
+
 TEST(ASTNodeKind, Name) {
   EXPECT_EQ("", ASTNodeKind().asStringRef());
 #define VERIFY_NAME(Node) EXPECT_EQ(#Node, DNT().asStringRef());
Index: clang/include/clang/AST/ASTTypeTraits.h
===
--- clang/include/clang/AST/ASTTypeTraits.h
+++ clang/include/clang/AST/ASTTypeTraits.h
@@ -51,11 +51,10 @@
 class ASTNodeKind {
 public:
   /// Empty identifier. It matches nothing.
-  ASTNodeKind() : KindId(NKI_None) {}
+  constexpr ASTNodeKind() : KindId(NKI_None) {}
 
   /// Construct an identifier for T.
-  template 
-  static ASTNodeKind getFromNodeKind() {
+  template  static constexpr ASTNodeKind getFromNodeKind() {
 return ASTNodeKind(KindToKindId::Id);
   }
 
@@ -71,12 +70,12 @@
   /// \}
 
   /// Returns \c true if \c this and \c Other represent the same kind.
-  bool isSame(ASTNodeKind Other) const {
+  constexpr bool isSame(ASTNodeKind Other) const {
 return KindId != NKI_None && KindId == Other.KindId;
   }
 
   /// Returns \c true only for the default \c ASTNodeKind()
-  bool isNone() const { return KindId == NKI_None; }
+  constexpr bool isNone() const { return KindId == NKI_None; }
 
   /// Returns \c true if \c this is a base kind of (or same as) \c Other.
   /// \param Distance If non-null, used to return the distance between \c this
@@ -87,7 +86,7 @@
   StringRef asStringRef() const;
 
   /// Strict weak ordering for ASTNodeKind.
-  bool operator<(const ASTNodeKind ) const {
+  constexpr bool operator<(const ASTNodeKind ) const {
 return KindId < Other.KindId;
   }
 
@@ -121,7 +120,7 @@
 
   /// Check if the given ASTNodeKind identifies a type that offers pointer
   /// identity. This is useful for the fast path in DynTypedNode.
-  bool hasPointerIdentity() const {
+  constexpr bool hasPointerIdentity() const {
 return KindId > NKI_LastKindWithoutPointerIdentity;
   }
 
@@ -165,7 +164,7 @@
   };
 
   /// Use getFromNodeKind() to construct the kind.
-  ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
+  constexpr ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
 
   /// Returns \c true if \c Base is a base kind of (or same as) \c
   ///   Derived.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D135816: [clang] Support `constexpr` for some `ASTNodeKind` member functions

2022-10-12 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: aaron.ballman.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Add `constexpr` support for:

- The `getFromNodeKind` factory function
- `isSame`
- `isNone`
- `hasPointerIdentity`

This enables these functions to be used in SFINAE context for AST node
types.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D135816

Files:
  clang/include/clang/AST/ASTTypeTraits.h
  clang/unittests/AST/ASTTypeTraitsTest.cpp

Index: clang/unittests/AST/ASTTypeTraitsTest.cpp
===
--- clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -117,6 +117,47 @@
   EXPECT_FALSE(DNT().isSame(DNT()));
 }
 
+template 
+constexpr bool HasPointerIdentity =
+ASTNodeKind::getFromNodeKind().hasPointerIdentity();
+
+TEST(ASTNodeKind, ConstexprHasPointerIdentity) {
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_TRUE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+  EXPECT_FALSE(HasPointerIdentity);
+
+  constexpr bool DefaultConstructedHasPointerIdentity =
+  ASTNodeKind().hasPointerIdentity();
+  EXPECT_FALSE(DefaultConstructedHasPointerIdentity);
+}
+
+template 
+constexpr bool NodeKindIsSame =
+ASTNodeKind::getFromNodeKind().isSame(ASTNodeKind::getFromNodeKind());
+
+TEST(ASTNodeKind, ConstexprIsSame) {
+  EXPECT_TRUE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+  EXPECT_FALSE((NodeKindIsSame));
+
+  constexpr bool DefaultConstructedIsSameToDefaultConstructed =
+  ASTNodeKind().isSame(ASTNodeKind());
+  EXPECT_FALSE(DefaultConstructedIsSameToDefaultConstructed);
+}
+
+template 
+constexpr bool NodeKindIsNone = ASTNodeKind::getFromNodeKind().isNone();
+
+TEST(ASTNodeKind, ConstexprIsNone) {
+  EXPECT_FALSE(NodeKindIsNone);
+  EXPECT_TRUE(NodeKindIsNone);
+
+  constexpr bool DefaultConstructedIsNone = ASTNodeKind().isNone();
+  EXPECT_TRUE(DefaultConstructedIsNone);
+}
+
 TEST(ASTNodeKind, Name) {
   EXPECT_EQ("", ASTNodeKind().asStringRef());
 #define VERIFY_NAME(Node) EXPECT_EQ(#Node, DNT().asStringRef());
Index: clang/include/clang/AST/ASTTypeTraits.h
===
--- clang/include/clang/AST/ASTTypeTraits.h
+++ clang/include/clang/AST/ASTTypeTraits.h
@@ -51,11 +51,11 @@
 class ASTNodeKind {
 public:
   /// Empty identifier. It matches nothing.
-  ASTNodeKind() : KindId(NKI_None) {}
+  constexpr ASTNodeKind() : KindId(NKI_None) {}
 
   /// Construct an identifier for T.
   template 
-  static ASTNodeKind getFromNodeKind() {
+  static constexpr ASTNodeKind getFromNodeKind() {
 return ASTNodeKind(KindToKindId::Id);
   }
 
@@ -71,12 +71,12 @@
   /// \}
 
   /// Returns \c true if \c this and \c Other represent the same kind.
-  bool isSame(ASTNodeKind Other) const {
+  constexpr bool isSame(ASTNodeKind Other) const {
 return KindId != NKI_None && KindId == Other.KindId;
   }
 
   /// Returns \c true only for the default \c ASTNodeKind()
-  bool isNone() const { return KindId == NKI_None; }
+  constexpr bool isNone() const { return KindId == NKI_None; }
 
   /// Returns \c true if \c this is a base kind of (or same as) \c Other.
   /// \param Distance If non-null, used to return the distance between \c this
@@ -121,7 +121,7 @@
 
   /// Check if the given ASTNodeKind identifies a type that offers pointer
   /// identity. This is useful for the fast path in DynTypedNode.
-  bool hasPointerIdentity() const {
+  constexpr bool hasPointerIdentity() const {
 return KindId > NKI_LastKindWithoutPointerIdentity;
   }
 
@@ -165,7 +165,7 @@
   };
 
   /// Use getFromNodeKind() to construct the kind.
-  ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
+  constexpr ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
 
   /// Returns \c true if \c Base is a base kind of (or same as) \c
   ///   Derived.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-19 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: 
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:168-169
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {

sgatev wrote:
> li.zhe.hua wrote:
> > xazax.hun wrote:
> > > Is this also true when we have multiple `continue` statements in the loop?
> > Yes. The end of the loop, and each of the `continue` statements, target the 
> > back edge block. They all get funneled through that back edge to `Block`, 
> > such that `Block` only has two predecessors. However, I haven't verified 
> > this in the CFG code, only by not finding a counterexample.
> Does that hold for back edges stemming from `goto` statements?
Ah, `goto` does throw a wrench in all of this. Specifically, it is problematic 
only for `do ... while` loops, where the labeled statement is the first 
statement of the `do` block.

I took out the sentence, took out the `assert`, and added a `FIXME` for 
`prepareBackEdges`.



Comment at: 
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:228-234
+  // If we are at the start of a loop, we will have two precessors, but we 
don't
+  // want to join these two predecessors. Instead, we want to take the back 
edge
+  // block (i.e. the result of the previous iteration) and use that directly as
+  // the input block.
+  //
+  // For the first iteration of loop, the "zeroth" iteration state is set up by
+  // `prepareBackEdges`.

xazax.hun wrote:
> xazax.hun wrote:
> > li.zhe.hua wrote:
> > > xazax.hun wrote:
> > > > Could you elaborate on this? Let's consider this loop:
> > > > 
> > > > ```
> > > >  Pred
> > > >|
> > > >v
> > > >  LoopHeader <---BackEdge
> > > > ```
> > > > 
> > > > Do we ignore the state coming from `Pred` on purpose? Is that sound?
> > > > 
> > > > I would expect the analysis to always compute `join(PredState, 
> > > > BackEdgeState)`, and I would expect the widening to happen between the 
> > > > previous iteration of `BackEdgeState` and the current iteration of 
> > > > `BackEdgeState`. So, I wonder if we already invoke the widening 
> > > > operator along back edges, wouldn't the regular logic work just fine? 
> > > > Do I miss something?
> > > > 
> > > > Do we ignore the state coming from `Pred` on purpose? Is that sound?
> > > 
> > > We don't, and this is what the comment
> > > 
> > > ```
> > >   // For the first iteration of loop, the "zeroth" iteration state is set 
> > > up by
> > >   // `prepareBackEdges`.
> > > ```
> > > failed to explain. After transferring `PredState`, we copy `PredState` 
> > > into `BackEdgeState`, which is done in `prepareBackEdges`.
> > > 
> > > > I would expect the analysis to always compute `join(PredState, 
> > > > BackEdgeState)`
> > > 
> > > I'm not sure I see that we should always join `PredState` and 
> > > `BackEdgeState`. Execution doesn't go from `Pred` into the Nth iteration 
> > > of the loop, it only goes from `Pred` into the first iteration of the 
> > > loop, e.g. the predecessor for the 4th iteration of the loop is only the 
> > > back-edge from the 3rd iteration of the loop, not `Pred`.
> > > 
> > > Let me know if this makes sense.
> > > I'm not sure I see that we should always join `PredState` and 
> > > `BackEdgeState`. Execution doesn't go from `Pred` into the Nth iteration 
> > > of the loop, it only goes from `Pred` into the first iteration of the 
> > > loop, e.g. the predecessor for the 4th iteration of the loop is only the 
> > > back-edge from the 3rd iteration of the loop, not `Pred`.
> > > 
> > > Let me know if this makes sense.
> > 
> > The analysis state of the dataflow analysis supposed to overestimate all of 
> > the paths. Consider the following loop and imagine we want to calculate 
> > intervals for integer variables:
> > 
> > ```
> > int i = 0;
> > while (...) {
> >   [[program point A]];
> >   ++i;
> > }
> > ```
> > 
> > During the analysis of this loop, the value `i ==0` flows to `[[program 
> > point A]]`. This is the motivation to join the state from the back edge and 
> > from PredState. As far as I understand, you want to achieve this by copying 
> > PredState to the BackEdgeState before the first iteration. But this also 
> > means that we will use the widening operator to combine PredState with the 
> > state after N iterations instead of the regular join operation. I am not 
> > entirely sure whether these two approaches always produce the same results. 
> > 
> > 
> A contrived example (just came up with this in a couple minutes so feel free 
> to double check if it is OK):
> Consider:
> ```
> int i = 0;
> while (...) {
>   if (i == 0)
> i = -2;
>   ++i;
> }
> ```
> 
> Some states:
> ```
> PredState = i : [0, 0]
> 

[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-19 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 454089.
li.zhe.hua marked 4 inline comments as done.
li.zhe.hua added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -42,13 +42,17 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
+using ::llvm::HasValue;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Ref;
+using ::testing::SizeIs;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -222,6 +226,71 @@
 "maximum number of iterations reached");
 }
 
+struct ConvergesOnWidenLattice {
+  int State = 0;
+  bool Top = false;
+
+  bool operator==(const ConvergesOnWidenLattice ) const {
+if (Top)
+  return Other.Top;
+return State == Other.State;
+  }
+
+  LatticeJoinEffect join(const ConvergesOnWidenLattice ) {
+auto Prev = *this;
+Top = Top || Other.Top;
+State += Other.State;
+return Prev == *this ? LatticeJoinEffect::Unchanged
+ : LatticeJoinEffect::Changed;
+  }
+
+  void widen(const ConvergesOnWidenLattice ) { Top = true; }
+
+  friend std::ostream <<(std::ostream ,
+  const ConvergesOnWidenLattice ) {
+return OS << "{" << L.State << "," << (L.Top ? "true" : "false") << "}";
+  }
+};
+
+class ConvergesOnWidenAnalysis
+: public DataflowAnalysis {
+public:
+  explicit ConvergesOnWidenAnalysis(ASTContext )
+  : DataflowAnalysis(Context,
+ /*ApplyBuiltinTransfer=*/false) {}
+
+  static ConvergesOnWidenLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *S, ConvergesOnWidenLattice , Environment ) {
+++E.State;
+  }
+};
+
+TEST(DataflowAnalysisTest, WhileLoopConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  while(true) {}
+}
+  )";
+  EXPECT_THAT_EXPECTED(
+  runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }),
+  HasValue(AllOf(SizeIs(4), Each(Optional(_);
+}
+
+TEST(DataflowAnalysisTest, ForLoopConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  for (int i = 0; i > -1; ++i) {}
+}
+  )";
+  EXPECT_THAT_EXPECTED(
+  runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }),
+  HasValue(AllOf(SizeIs(5), Each(Optional(_);
+}
+
 struct FunctionCallLattice {
   llvm::SmallSet CalledFunctions;
 
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -154,6 +154,30 @@
   TransferOptions TransferOpts;
 };
 
+/// Returns whether `Block` is a "back edge" in the CFG. Such a block has only
+/// one successor, the start of the loop.
+static bool isBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  return Block->LoopTarget != nullptr;
+}
+
+/// Returns the predecessor to `Block` that comes from a "back edge", if one
+/// exists.
+///
+/// If this function returns a non-null pointer, that means `Block` dominates
+/// the back edge block. That is, all paths from the entry block to the back
+/// edge block must go through `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  for (const auto  : Block->preds()) {
+if (!Pred.isReachable())
+  continue;
+if (isBackEdge(Pred))
+  return Pred;
+  }
+  return nullptr;
+}
+
 /// Computes the input state for a given basic block by joining the output
 /// states of its predecessors.
 ///
@@ -208,7 +232,7 @@
   continue;
 
 // Skip if `Pred` was not evaluated yet. This could happen if `Pred` has a
-// loop back edge to `Block`.
+// a noreturn element.
 const llvm::Optional  =
 BlockStates[Pred->getBlockID()];
 if (!MaybePredState)
@@ -311,6 +335,7 @@
 HandleTransferredStmt) {
   TypeErasedDataflowAnalysisState State =
   computeBlockInputState(CFCtx, BlockStates, Block, InitEnv, Analysis);
+
   for (const CFGElement  : Block) {
 switch (Element.getKind()) {
 case CFGElement::Statement:
@@ -326,9 +351,56 @@
   break;
 

[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: 
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:168-169
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {

xazax.hun wrote:
> Is this also true when we have multiple `continue` statements in the loop?
Yes. The end of the loop, and each of the `continue` statements, target the 
back edge block. They all get funneled through that back edge to `Block`, such 
that `Block` only has two predecessors. However, I haven't verified this in the 
CFG code, only by not finding a counterexample.



Comment at: 
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:228-234
+  // If we are at the start of a loop, we will have two precessors, but we 
don't
+  // want to join these two predecessors. Instead, we want to take the back 
edge
+  // block (i.e. the result of the previous iteration) and use that directly as
+  // the input block.
+  //
+  // For the first iteration of loop, the "zeroth" iteration state is set up by
+  // `prepareBackEdges`.

xazax.hun wrote:
> Could you elaborate on this? Let's consider this loop:
> 
> ```
>  Pred
>|
>v
>  LoopHeader <---BackEdge
> ```
> 
> Do we ignore the state coming from `Pred` on purpose? Is that sound?
> 
> I would expect the analysis to always compute `join(PredState, 
> BackEdgeState)`, and I would expect the widening to happen between the 
> previous iteration of `BackEdgeState` and the current iteration of 
> `BackEdgeState`. So, I wonder if we already invoke the widening operator 
> along back edges, wouldn't the regular logic work just fine? Do I miss 
> something?
> 
> Do we ignore the state coming from `Pred` on purpose? Is that sound?

We don't, and this is what the comment

```
  // For the first iteration of loop, the "zeroth" iteration state is set up by
  // `prepareBackEdges`.
```
failed to explain. After transferring `PredState`, we copy `PredState` into 
`BackEdgeState`, which is done in `prepareBackEdges`.

> I would expect the analysis to always compute `join(PredState, BackEdgeState)`

I'm not sure I see that we should always join `PredState` and `BackEdgeState`. 
Execution doesn't go from `Pred` into the Nth iteration of the loop, it only 
goes from `Pred` into the first iteration of the loop, e.g. the predecessor for 
the 4th iteration of the loop is only the back-edge from the 3rd iteration of 
the loop, not `Pred`.

Let me know if this makes sense.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

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


[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 451926.
li.zhe.hua added a comment.

Fix incorrect assumption that back edge blocks have an empty body.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -42,13 +42,17 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
+using ::llvm::HasValue;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Ref;
+using ::testing::SizeIs;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -222,6 +226,71 @@
 "maximum number of iterations reached");
 }
 
+struct ConvergesOnWidenLattice {
+  int State = 0;
+  bool Top = false;
+
+  bool operator==(const ConvergesOnWidenLattice ) const {
+if (Top)
+  return Other.Top;
+return State == Other.State;
+  }
+
+  LatticeJoinEffect join(const ConvergesOnWidenLattice ) {
+auto Prev = *this;
+Top = Top || Other.Top;
+State += Other.State;
+return Prev == *this ? LatticeJoinEffect::Unchanged
+ : LatticeJoinEffect::Changed;
+  }
+
+  void widen(const ConvergesOnWidenLattice ) { Top = true; }
+
+  friend std::ostream <<(std::ostream ,
+  const ConvergesOnWidenLattice ) {
+return OS << "{" << L.State << "," << (L.Top ? "true" : "false") << "}";
+  }
+};
+
+class ConvergesOnWidenAnalysis
+: public DataflowAnalysis {
+public:
+  explicit ConvergesOnWidenAnalysis(ASTContext )
+  : DataflowAnalysis(Context,
+ /*ApplyBuiltinTransfer=*/false) {}
+
+  static ConvergesOnWidenLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *S, ConvergesOnWidenLattice , Environment ) {
+++E.State;
+  }
+};
+
+TEST(DataflowAnalysisTest, WhileLoopConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  while(true) {}
+}
+  )";
+  EXPECT_THAT_EXPECTED(
+  runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }),
+  HasValue(AllOf(SizeIs(4), Each(Optional(_);
+}
+
+TEST(DataflowAnalysisTest, ForLoopConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  for (int i = 0; i > -1; ++i) {}
+}
+  )";
+  EXPECT_THAT_EXPECTED(
+  runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }),
+  HasValue(AllOf(SizeIs(5), Each(Optional(_);
+}
+
 struct FunctionCallLattice {
   llvm::SmallSet CalledFunctions;
 
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -154,6 +154,32 @@
   TransferOptions TransferOpts;
 };
 
+// Returns whether `Block` is a "back edge" in the CFG. Such a block has only
+// one successor, the start of the loop.
+static bool isBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  return Block->LoopTarget != nullptr;
+}
+
+// Returns the predecessor to `Block` that is a "back edge", if one exists.
+//
+// If this function returns a non-null pointer, that means `Block` dominates the
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  for (const auto  : Block->preds()) {
+if (!Pred.isReachable())
+  continue;
+if (isBackEdge(Pred)) {
+  assert(Block->pred_size() == 2);
+  return Pred;
+}
+  }
+  return nullptr;
+}
+
 /// Computes the input state for a given basic block by joining the output
 /// states of its predecessors.
 ///
@@ -199,6 +225,21 @@
 }
   }
 
+  // If we are at the start of a loop, we will have two precessors, but we don't
+  // want to join these two predecessors. Instead, we want to take the back edge
+  // block (i.e. the result of the previous iteration) and use that directly as
+  // the input block.
+  //
+  // For the first iteration of loop, the "zeroth" iteration state is set up by
+  // `prepareBackEdges`.
+  if (const CFGBlock 

[PATCH] D131645: [clang][dataflow] Allow user-provided lattices to provide a widen operator

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

In D131645#3716464 , @xazax.hun wrote:

> probably you are already aware of most of this

Actually, more likely than not I don't! I don't have a strong background in PL 
(or, really any background), so I'm really just learning as I go, mostly from 
talking to @ymandel and reading through Introduction to Static Analysis 
 (I'm still struggling through Chapter 
3!).

> - Some frameworks have the option to delay widening. I.e., only invoke the 
> widening operator when the analysis of a loop did not converge after a 
> certain number of iterations.

There's a `FIXME` in D131646  that talks 
about unrolling, which sounds like this.

> - Some frameworks implement narrowing. In the narrowing phase, we would do a 
> couple more iterations but using the regular join instead of the widening 
> operator. Sometimes doing multiple narrow/widen phases can be beneficial.

Good to know. I'll keep this in mind and try to dig up some more about this.

> - Writing a widening operator can be tricky. It would be nice to add some 
> guidance to the documentation. E.g., listing some of the algebraic properties 
> that we expect a widening operator to have can be a lot of help for 
> newcommers (like `bottom.widen(lat) == lat`).

Agreed. FWIW, I'm still struggling to grok this in the abstract, and don't have 
a concrete example readily available that makes sense to me.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131645

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


[PATCH] D131645: [clang][dataflow] Allow user-provided lattices to provide a widen operator

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 451887.
li.zhe.hua marked an inline comment as done.
li.zhe.hua added a comment.

Add FIXME


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131645

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -48,6 +48,7 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 using ::testing::Pair;
+using ::testing::Ref;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -86,6 +87,99 @@
   EXPECT_TRUE(BlockStates[1].has_value());
 }
 
+struct HasWidenLattice {
+  HasWidenLattice() {
+ON_CALL(*this, join).WillByDefault([](const HasWidenLattice &) {
+  return LatticeJoinEffect::Unchanged;
+});
+  }
+  // Mock objects are not copyable by default. Since this is a monostate,
+  // delegate to the default ctor.
+  HasWidenLattice(const HasWidenLattice &) : HasWidenLattice() {}
+  HasWidenLattice =(const HasWidenLattice &) { return *this; }
+
+  MOCK_METHOD(LatticeJoinEffect, join, (const HasWidenLattice &));
+  MOCK_METHOD(void, widen, (const HasWidenLattice &));
+
+  friend bool operator==(const HasWidenLattice &, const HasWidenLattice &) {
+return true;
+  }
+};
+
+class HasWidenAnalysis
+: public DataflowAnalysis {
+public:
+  static HasWidenLattice initialElement() { return {}; }
+  using DataflowAnalysis::DataflowAnalysis;
+  void transfer(const Stmt *, HasWidenLattice &, Environment &) {}
+};
+
+TEST(DataflowAnalysisTest, WidenPrefersWidenWhenProvided) {
+  std::unique_ptr AST =
+  tooling::buildASTFromCodeWithArgs("int x = 0;", {"-std=c++11"});
+  HasWidenAnalysis Analysis(AST->getASTContext(),
+/*ApplyBuiltinTransfer=*/false);
+
+  TypeErasedLattice A = {HasWidenLattice()};
+  TypeErasedLattice B = {HasWidenLattice()};
+  HasWidenLattice  = *llvm::any_cast();
+  HasWidenLattice  = *llvm::any_cast();
+
+  // Expect only `LA.widen(LB)` is called, and nothing else.
+  EXPECT_CALL(LA, widen).Times(0);
+  EXPECT_CALL(LB, widen).Times(0);
+  EXPECT_CALL(LA, join).Times(0);
+  EXPECT_CALL(LB, join).Times(0);
+  EXPECT_CALL(LA, widen(Ref(LB))).Times(1);
+
+  Analysis.widenTypeErased(A, B);
+}
+
+struct OnlyJoinLattice {
+  OnlyJoinLattice() {
+ON_CALL(*this, join).WillByDefault([](const OnlyJoinLattice &) {
+  return LatticeJoinEffect::Unchanged;
+});
+  }
+  // Mock objects are not copyable by default. Since this is a monostate,
+  // delegate to the default ctor.
+  OnlyJoinLattice(const OnlyJoinLattice &) : OnlyJoinLattice() {}
+  OnlyJoinLattice =(const OnlyJoinLattice &) { return *this; }
+
+  MOCK_METHOD(LatticeJoinEffect, join, (const OnlyJoinLattice &));
+
+  friend bool operator==(const OnlyJoinLattice &, const OnlyJoinLattice &) {
+return true;
+  }
+};
+
+class OnlyJoinAnalysis
+: public DataflowAnalysis {
+public:
+  static OnlyJoinLattice initialElement() { return {}; }
+  using DataflowAnalysis::DataflowAnalysis;
+  void transfer(const Stmt *, OnlyJoinLattice &, Environment &) {}
+};
+
+TEST(DataflowAnalysisTest, WidenFallsBackToJoin) {
+  std::unique_ptr AST =
+  tooling::buildASTFromCodeWithArgs("int x = 0;", {"-std=c++11"});
+  OnlyJoinAnalysis Analysis(AST->getASTContext(),
+/*ApplyBuiltinTransfer=*/false);
+
+  TypeErasedLattice A = {OnlyJoinLattice()};
+  TypeErasedLattice B = {OnlyJoinLattice()};
+  OnlyJoinLattice  = *llvm::any_cast();
+  OnlyJoinLattice  = *llvm::any_cast();
+
+  // Expect only `LA.join(LB)` is called, and nothing else.
+  EXPECT_CALL(LA, join).Times(0);
+  EXPECT_CALL(LB, join).Times(0);
+  EXPECT_CALL(LA, join(Ref(LB))).Times(1);
+
+  Analysis.widenTypeErased(A, B);
+}
+
 struct NonConvergingLattice {
   int State;
 
Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -77,6 +77,10 @@
   virtual LatticeJoinEffect joinTypeErased(TypeErasedLattice &,
const TypeErasedLattice &) = 0;
 
+  /// Relaxes the constraints in `A` to subsume the state in `B`.
+  virtual void widenTypeErased(TypeErasedLattice ,
+   const TypeErasedLattice ) = 0;
+
   /// Returns true if and only if the two given type-erased lattice elements are
   /// equal.
   virtual bool 

[PATCH] D131691: [clang][dataflow] Remove references of LatticeJoinEffect from dataflow

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

A follow-up to D131688  to remove references 
to the enum within the
dataflow framework and its tests.

Depends on D131688 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131691

Files:
  clang/include/clang/Analysis/FlowSensitive/MapLattice.h
  clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
  clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -16,7 +16,6 @@
 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
@@ -92,17 +91,13 @@
 }
 
 struct HasWidenLattice {
-  HasWidenLattice() {
-ON_CALL(*this, join).WillByDefault([](const HasWidenLattice &) {
-  return LatticeJoinEffect::Unchanged;
-});
-  }
+  HasWidenLattice() = default;
   // Mock objects are not copyable by default. Since this is a monostate,
   // delegate to the default ctor.
   HasWidenLattice(const HasWidenLattice &) : HasWidenLattice() {}
   HasWidenLattice =(const HasWidenLattice &) { return *this; }
 
-  MOCK_METHOD(LatticeJoinEffect, join, (const HasWidenLattice &));
+  MOCK_METHOD(void, join, (const HasWidenLattice &));
   MOCK_METHOD(void, widen, (const HasWidenLattice &));
 
   friend bool operator==(const HasWidenLattice &, const HasWidenLattice &) {
@@ -140,17 +135,13 @@
 }
 
 struct OnlyJoinLattice {
-  OnlyJoinLattice() {
-ON_CALL(*this, join).WillByDefault([](const OnlyJoinLattice &) {
-  return LatticeJoinEffect::Unchanged;
-});
-  }
+  OnlyJoinLattice() = default;
   // Mock objects are not copyable by default. Since this is a monostate,
   // delegate to the default ctor.
   OnlyJoinLattice(const OnlyJoinLattice &) : OnlyJoinLattice() {}
   OnlyJoinLattice =(const OnlyJoinLattice &) { return *this; }
 
-  MOCK_METHOD(LatticeJoinEffect, join, (const OnlyJoinLattice &));
+  MOCK_METHOD(void, join, (const OnlyJoinLattice &));
 
   friend bool operator==(const OnlyJoinLattice &, const OnlyJoinLattice &) {
 return true;
@@ -191,12 +182,7 @@
 return State == Other.State;
   }
 
-  LatticeJoinEffect join(const NonConvergingLattice ) {
-if (Other.State == 0)
-  return LatticeJoinEffect::Unchanged;
-State += Other.State;
-return LatticeJoinEffect::Changed;
-  }
+  void join(const NonConvergingLattice ) { State += Other.State; }
 };
 
 class NonConvergingAnalysis
@@ -236,12 +222,9 @@
 return State == Other.State;
   }
 
-  LatticeJoinEffect join(const ConvergesOnWidenLattice ) {
-auto Prev = *this;
+  void join(const ConvergesOnWidenLattice ) {
 Top = Top || Other.Top;
 State += Other.State;
-return Prev == *this ? LatticeJoinEffect::Unchanged
- : LatticeJoinEffect::Changed;
   }
 
   void widen(const ConvergesOnWidenLattice ) { Top = true; }
@@ -286,14 +269,9 @@
 return CalledFunctions == Other.CalledFunctions;
   }
 
-  LatticeJoinEffect join(const FunctionCallLattice ) {
-if (Other.CalledFunctions.empty())
-  return LatticeJoinEffect::Unchanged;
-const size_t size_before = CalledFunctions.size();
+  void join(const FunctionCallLattice ) {
 CalledFunctions.insert(Other.CalledFunctions.begin(),
Other.CalledFunctions.end());
-return CalledFunctions.size() == size_before ? LatticeJoinEffect::Unchanged
- : LatticeJoinEffect::Changed;
   }
 };
 
Index: clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
@@ -21,14 +21,11 @@
 #include 

[PATCH] D131688: [clang][dataflow][NFC] Remove LatticeJoinEffect from framework interfaces

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Though we request users implementing `join` to return a meaningful
`LatticeJoinEffect`, we never use the returned value. Requiring users
(and the framework) to continue calculating an unused value adds
unnecessary complexity.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131688

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -16,7 +16,6 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Type.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
@@ -299,33 +298,21 @@
   return true;
 }
 
-LatticeJoinEffect Environment::join(const Environment ,
-Environment::ValueModel ) {
+void Environment::join(const Environment ,
+   Environment::ValueModel ) {
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
 
-  auto Effect = LatticeJoinEffect::Unchanged;
-
   Environment JoinedEnv(*DACtx);
 
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
   JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
-  if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
-Effect = LatticeJoinEffect::Changed;
-
   JoinedEnv.ExprToLoc = intersectDenseMaps(ExprToLoc, Other.ExprToLoc);
-  if (ExprToLoc.size() != JoinedEnv.ExprToLoc.size())
-Effect = LatticeJoinEffect::Changed;
-
   JoinedEnv.MemberLocToStruct =
   intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct);
-  if (MemberLocToStruct.size() != JoinedEnv.MemberLocToStruct.size())
-Effect = LatticeJoinEffect::Changed;
-
-  // FIXME: set `Effect` as needed.
   JoinedEnv.FlowConditionToken = >joinFlowConditions(
   *FlowConditionToken, *Other.FlowConditionToken);
 
@@ -350,12 +337,8 @@
 Loc->getType(), Val, *this, It->second, Other, JoinedEnv, Model))
   JoinedEnv.LocToVal.insert({Loc, MergedVal});
   }
-  if (LocToVal.size() != JoinedEnv.LocToVal.size())
-Effect = LatticeJoinEffect::Changed;
 
   *this = std::move(JoinedEnv);
-
-  return Effect;
 }
 
 StorageLocation ::createStorageLocation(QualType Type) {
Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -22,7 +22,6 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/Transfer.h"
 #include "llvm/ADT/Any.h"
 #include "llvm/ADT/Optional.h"
@@ -74,8 +73,8 @@
   /// Joins two type-erased lattice elements by computing their least upper
   /// bound. Places the join result in the left element and returns an effect
   /// indicating whether any changes were made to it.
-  virtual LatticeJoinEffect joinTypeErased(TypeErasedLattice &,
-   const TypeErasedLattice &) = 0;
+  virtual void joinTypeErased(TypeErasedLattice &,
+  const TypeErasedLattice &) = 0;
 
   /// Relaxes the constraints in `A` to subsume the state in `B`.
   virtual void widenTypeErased(TypeErasedLattice ,
Index: clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h
@@ -17,7 +17,11 @@
 namespace clang {
 namespace dataflow {
 
+/// DEPRECATED: Not meaningfully used.
+///
 /// Effect indicating whether a lattice join operation resulted in a new value.
+///
+/// FIXME: Delete this file once all references are removed.
 enum class LatticeJoinEffect {
   Unchanged,
   Changed,
Index: 

[PATCH] D131645: [clang][dataflow] Allow user-provided lattices to provide a widen operator

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: 
clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:119
+  std::unique_ptr AST =
+  tooling::buildASTFromCodeWithArgs("int x = 0;", {"-std=c++11"});
+  HasWidenAnalysis Analysis(AST->getASTContext(),

ymandel wrote:
> nit: why c++11 (vs something later)?
Consistency with `runAnalysis` defined above. FWIW, I don't think `int x = 0;` 
is materially different in any standard, and all I really want out of this is 
an `ASTContext` to pass to `TypeErasedDataflowAnalysis` to satisfy its 
constructor.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131645

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


[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:180
+  assert(Block->pred_size() == 2);
+  BackEdge = Pred;
+}

ymandel wrote:
> Might it be worth simply returning the backedge when you find it? Or is the 
> assertion (above) sufficiently important to keep it as is?
So, I couldn't prove to myself that a block couldn't have two backedge 
predecessors from staring at the CFG code, but it seems conceptually unlikely 
enough that I'm OK simplifying this.



Comment at: 
clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:275
+  )";
+  auto BlockStates = llvm::cantFail(runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }));

ymandel wrote:
> Might a (googletest) assertion here be better than `llvm::cantFail`? I would 
> think that this line is the crux of checking whether it converges or not.
Ah, yes. I hadn't thought to look for `llvm::Expected` matchers; good call.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

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


[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-11 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 451861.
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -42,13 +42,17 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
+using ::llvm::HasValue;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Ref;
+using ::testing::SizeIs;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -222,6 +226,59 @@
 "maximum number of iterations reached");
 }
 
+struct ConvergesOnWidenLattice {
+  int State = 0;
+  bool Top = false;
+
+  bool operator==(const ConvergesOnWidenLattice ) const {
+if (Top)
+  return Other.Top;
+return State == Other.State;
+  }
+
+  LatticeJoinEffect join(const ConvergesOnWidenLattice ) {
+auto Prev = *this;
+Top = Top || Other.Top;
+State += Other.State;
+return Prev == *this ? LatticeJoinEffect::Unchanged
+ : LatticeJoinEffect::Changed;
+  }
+
+  void widen(const ConvergesOnWidenLattice ) { Top = true; }
+
+  friend std::ostream <<(std::ostream ,
+  const ConvergesOnWidenLattice ) {
+return OS << "{" << L.State << "," << (L.Top ? "true" : "false") << "}";
+  }
+};
+
+class ConvergesOnWidenAnalysis
+: public DataflowAnalysis {
+public:
+  explicit ConvergesOnWidenAnalysis(ASTContext )
+  : DataflowAnalysis(Context,
+ /*ApplyBuiltinTransfer=*/false) {}
+
+  static ConvergesOnWidenLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *S, ConvergesOnWidenLattice , Environment ) {
+++E.State;
+  }
+};
+
+TEST(DataflowAnalysisTest, ConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  while(true) {}
+}
+  )";
+  EXPECT_THAT_EXPECTED(
+  runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }),
+  HasValue(AllOf(SizeIs(4), Each(Optional(_);
+}
+
 struct FunctionCallLattice {
   llvm::SmallSet CalledFunctions;
 
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -154,6 +154,32 @@
   TransferOptions TransferOpts;
 };
 
+// Returns whether `Block` is a "back edge" in the CFG. Such a block is empty
+// and has only one successor, the start of the loop.
+static bool isBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  return Block->LoopTarget != nullptr;
+}
+
+// Returns the predecessor to `Block` that is a "back edge", if one exists.
+//
+// If this function returns a non-null pointer, that means `Block` dominates the
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  for (const auto  : Block->preds()) {
+if (!Pred.isReachable())
+  continue;
+if (isBackEdge(Pred)) {
+  assert(Block->pred_size() == 2);
+  return Pred;
+}
+  }
+  return nullptr;
+}
+
 /// Computes the input state for a given basic block by joining the output
 /// states of its predecessors.
 ///
@@ -199,6 +225,21 @@
 }
   }
 
+  // If we are at the start of a loop, we will have two precessors, but we don't
+  // want to join these two predecessors. Instead, we want to take the back edge
+  // block (i.e. the result of the previous iteration) and use that directly as
+  // the input block.
+  //
+  // For the first iteration of loop, the "zeroth" iteration state is set up by
+  // `prepareBackEdges`.
+  if (const CFGBlock *BackEdge = findBackEdge()) {
+const llvm::Optional  =
+BlockStates[BackEdge->getBlockID()];
+assert(MaybePredState.has_value());
+assert(BackEdge->getTerminatorStmt() == nullptr);
+return *MaybePredState;
+  }
+
   llvm::Optional MaybeState;
   bool ApplyBuiltinTransfer = Analysis.applyBuiltinTransfer();
 
@@ -311,6 +352,23 

[PATCH] D131644: [clang][dataflow] Don't skip the entry block

2022-08-10 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 451721.
li.zhe.hua added a comment.

Fix commit message


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131644

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -342,11 +342,8 @@
   std::vector> BlockStates(
   CFCtx.getCFG().size(), llvm::None);
 
-  // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
-  BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
- InitEnv};
-  Worklist.enqueueSuccessors();
+  Worklist.enqueueBlock();
 
   // Bugs in lattices and transfer functions can prevent the analysis from
   // converging. To limit the damage (infinite loops) that these bugs can 
cause,


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -342,11 +342,8 @@
   std::vector> BlockStates(
   CFCtx.getCFG().size(), llvm::None);
 
-  // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
-  BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
- InitEnv};
-  Worklist.enqueueSuccessors();
+  Worklist.enqueueBlock();
 
   // Bugs in lattices and transfer functions can prevent the analysis from
   // converging. To limit the damage (infinite loops) that these bugs can cause,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131646: [clang][dataflow] Restructure loops to call widen on back edges

2022-08-10 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When navigating a loop block, we call the lattice's widen operator,
which gives a lattice of infinite height the opportunity to reach
convergence.

As we enter the loop, we store the block state in the back edge block,
which represents the state after the zeroth iteration. Then, after
each loop iteration, we widen the previous iteration's state with the
new iteration's state.

Tracking bug: #56931

Depends on D131645 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131646

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -43,12 +43,15 @@
 using namespace test;
 using namespace ast_matchers;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Ref;
+using ::testing::SizeIs;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -222,6 +225,58 @@
 "maximum number of iterations reached");
 }
 
+struct ConvergesOnWidenLattice {
+  int State = 0;
+  bool Top = false;
+
+  bool operator==(const ConvergesOnWidenLattice ) const {
+if (Top)
+  return Other.Top;
+return State == Other.State;
+  }
+
+  LatticeJoinEffect join(const ConvergesOnWidenLattice ) {
+auto Prev = *this;
+Top = Top || Other.Top;
+State += Other.State;
+return Prev == *this ? LatticeJoinEffect::Unchanged
+ : LatticeJoinEffect::Changed;
+  }
+
+  void widen(const ConvergesOnWidenLattice ) { Top = true; }
+
+  friend std::ostream <<(std::ostream ,
+  const ConvergesOnWidenLattice ) {
+return OS << "{" << L.State << "," << (L.Top ? "true" : "false") << "}";
+  }
+};
+
+class ConvergesOnWidenAnalysis
+: public DataflowAnalysis {
+public:
+  explicit ConvergesOnWidenAnalysis(ASTContext )
+  : DataflowAnalysis(Context,
+ /*ApplyBuiltinTransfer=*/false) {}
+
+  static ConvergesOnWidenLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *S, ConvergesOnWidenLattice , Environment ) {
+++E.State;
+  }
+};
+
+TEST(DataflowAnalysisTest, ConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+void target() {
+  while(true) {}
+}
+  )";
+  auto BlockStates = llvm::cantFail(runAnalysis(
+  Code, [](ASTContext ) { return ConvergesOnWidenAnalysis(C); }));
+  EXPECT_THAT(BlockStates, AllOf(SizeIs(4), Each(Optional(_;
+}
+
 struct FunctionCallLattice {
   llvm::SmallSet CalledFunctions;
 
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -154,6 +154,35 @@
   TransferOptions TransferOpts;
 };
 
+// Returns whether `Block` is a "back edge" in the CFG. Such a block is empty
+// and has only one successor, the start of the loop.
+static bool isBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  return Block->LoopTarget != nullptr;
+}
+
+// Returns the predecessor to `Block` that is a "back edge", if one exists.
+//
+// If this function returns a non-null pointer, that means `Block` dominates the
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+
+  const CFGBlock *BackEdge = nullptr;
+  for (const auto  : Block->preds()) {
+if (!Pred.isReachable())
+  continue;
+if (isBackEdge(Pred)) {
+  assert(BackEdge == nullptr);
+  assert(Block->pred_size() == 2);
+  BackEdge = Pred;
+}
+  }
+  return BackEdge;
+}
+
 /// Computes the input state for a given basic block by joining the output
 /// states of its predecessors.
 ///
@@ -199,6 +228,21 @@
 }
   }
 
+  // If we are at the start of a loop, we will have two precessors, but we don't
+  // want to join these two predecessors. Instead, we want to take the back edge
+  // block (i.e. the result of the 

[PATCH] D131645: [clang][dataflow] Allow user-provided lattices to provide a widen operator

2022-08-10 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

In order to better support convergence in a sound way, we allow users
to provide a widen operator for their lattice type. This can be
implemented for lattices of infinite (or sufficiently large) height in
order to reach convergence in loops.

If not provided, this defaults to the existing `join` operation that
is required to be defined. This is a sound default, as `join` would be
at least more precise than a theoretical `widen`.

Tracking bug: #56931

Depends on D131644 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131645

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -48,6 +48,7 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 using ::testing::Pair;
+using ::testing::Ref;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -86,6 +87,99 @@
   EXPECT_TRUE(BlockStates[1].has_value());
 }
 
+struct HasWidenLattice {
+  HasWidenLattice() {
+ON_CALL(*this, join).WillByDefault([](const HasWidenLattice &) {
+  return LatticeJoinEffect::Unchanged;
+});
+  }
+  // Mock objects are not copyable by default. Since this is a monostate,
+  // delegate to the default ctor.
+  HasWidenLattice(const HasWidenLattice &) : HasWidenLattice() {}
+  HasWidenLattice =(const HasWidenLattice &) { return *this; }
+
+  MOCK_METHOD(LatticeJoinEffect, join, (const HasWidenLattice &));
+  MOCK_METHOD(void, widen, (const HasWidenLattice &));
+
+  friend bool operator==(const HasWidenLattice &, const HasWidenLattice &) {
+return true;
+  }
+};
+
+class HasWidenAnalysis
+: public DataflowAnalysis {
+public:
+  static HasWidenLattice initialElement() { return {}; }
+  using DataflowAnalysis::DataflowAnalysis;
+  void transfer(const Stmt *, HasWidenLattice &, Environment &) {}
+};
+
+TEST(DataflowAnalysisTest, WidenPrefersWidenWhenProvided) {
+  std::unique_ptr AST =
+  tooling::buildASTFromCodeWithArgs("int x = 0;", {"-std=c++11"});
+  HasWidenAnalysis Analysis(AST->getASTContext(),
+/*ApplyBuiltinTransfer=*/false);
+
+  TypeErasedLattice A = {HasWidenLattice()};
+  TypeErasedLattice B = {HasWidenLattice()};
+  HasWidenLattice  = *llvm::any_cast();
+  HasWidenLattice  = *llvm::any_cast();
+
+  // Expect only `LA.widen(LB)` is called, and nothing else.
+  EXPECT_CALL(LA, widen).Times(0);
+  EXPECT_CALL(LB, widen).Times(0);
+  EXPECT_CALL(LA, join).Times(0);
+  EXPECT_CALL(LB, join).Times(0);
+  EXPECT_CALL(LA, widen(Ref(LB))).Times(1);
+
+  Analysis.widenTypeErased(A, B);
+}
+
+struct OnlyJoinLattice {
+  OnlyJoinLattice() {
+ON_CALL(*this, join).WillByDefault([](const OnlyJoinLattice &) {
+  return LatticeJoinEffect::Unchanged;
+});
+  }
+  // Mock objects are not copyable by default. Since this is a monostate,
+  // delegate to the default ctor.
+  OnlyJoinLattice(const OnlyJoinLattice &) : OnlyJoinLattice() {}
+  OnlyJoinLattice =(const OnlyJoinLattice &) { return *this; }
+
+  MOCK_METHOD(LatticeJoinEffect, join, (const OnlyJoinLattice &));
+
+  friend bool operator==(const OnlyJoinLattice &, const OnlyJoinLattice &) {
+return true;
+  }
+};
+
+class OnlyJoinAnalysis
+: public DataflowAnalysis {
+public:
+  static OnlyJoinLattice initialElement() { return {}; }
+  using DataflowAnalysis::DataflowAnalysis;
+  void transfer(const Stmt *, OnlyJoinLattice &, Environment &) {}
+};
+
+TEST(DataflowAnalysisTest, WidenFallsBackToJoin) {
+  std::unique_ptr AST =
+  tooling::buildASTFromCodeWithArgs("int x = 0;", {"-std=c++11"});
+  OnlyJoinAnalysis Analysis(AST->getASTContext(),
+/*ApplyBuiltinTransfer=*/false);
+
+  TypeErasedLattice A = {OnlyJoinLattice()};
+  TypeErasedLattice B = {OnlyJoinLattice()};
+  OnlyJoinLattice  = *llvm::any_cast();
+  OnlyJoinLattice  = *llvm::any_cast();
+
+  // Expect only `LA.join(LB)` is called, and nothing else.
+  EXPECT_CALL(LA, join).Times(0);
+  EXPECT_CALL(LB, join).Times(0);
+  EXPECT_CALL(LA, join(Ref(LB))).Times(1);
+
+  Analysis.widenTypeErased(A, B);
+}
+
 struct NonConvergingLattice {
   int State;
 
Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- 

[PATCH] D131644: [clang][dataflow] Don't skip the entry block

2022-08-10 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

In DXXX, we implement widen by taking the block state upon entry
into the loop and copying it to the block that represents the
back-edge in order to initialize the state for the "0th" iteration of
the loop. If the loop is the first thing after the entry block,
skipping the entry block means we don't have an opportunity to do this
initialization, so we want to analyze the entry block, even as it has
zero statements, to keep the logic consistent.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131644

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -342,11 +342,8 @@
   std::vector> BlockStates(
   CFCtx.getCFG().size(), llvm::None);
 
-  // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
-  BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
- InitEnv};
-  Worklist.enqueueSuccessors();
+  Worklist.enqueueBlock();
 
   // Bugs in lattices and transfer functions can prevent the analysis from
   // converging. To limit the damage (infinite loops) that these bugs can 
cause,


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -342,11 +342,8 @@
   std::vector> BlockStates(
   CFCtx.getCFG().size(), llvm::None);
 
-  // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
-  BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
- InitEnv};
-  Worklist.enqueueSuccessors();
+  Worklist.enqueueBlock();
 
   // Bugs in lattices and transfer functions can prevent the analysis from
   // converging. To limit the damage (infinite loops) that these bugs can cause,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131178: [clang][dataflow][NFC] Convert mutable vector references to ArrayRef

2022-08-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua closed this revision.
li.zhe.hua added a comment.

Bah, I think I screwed up `arc` at some point. This was committed as 
18034aee63eeac673496a88d9e90c8dd73d15927 
.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131178

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


[PATCH] D131177: [clang][dataflow][NFC] Resize vector directly with ctor

2022-08-04 Thread Eric Li 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 rG5659908f4c6d: [clang][dataflow][NFC] Resize vector directly 
with ctor (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131177

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -339,8 +339,8 @@
   PostOrderCFGView POV(());
   ForwardDataflowWorklist Worklist(CFCtx.getCFG(), );
 
-  std::vector> BlockStates;
-  BlockStates.resize(CFCtx.getCFG().size(), llvm::None);
+  std::vector> BlockStates(
+  CFCtx.getCFG().size(), llvm::None);
 
   // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -339,8 +339,8 @@
   PostOrderCFGView POV(());
   ForwardDataflowWorklist Worklist(CFCtx.getCFG(), );
 
-  std::vector> BlockStates;
-  BlockStates.resize(CFCtx.getCFG().size(), llvm::None);
+  std::vector> BlockStates(
+  CFCtx.getCFG().size(), llvm::None);
 
   // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131177: [clang][dataflow][NFC] Resize vector directly with ctor

2022-08-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131177

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -339,8 +339,8 @@
   PostOrderCFGView POV(());
   ForwardDataflowWorklist Worklist(CFCtx.getCFG(), );
 
-  std::vector> BlockStates;
-  BlockStates.resize(CFCtx.getCFG().size(), llvm::None);
+  std::vector> BlockStates(
+  CFCtx.getCFG().size(), llvm::None);
 
   // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -339,8 +339,8 @@
   PostOrderCFGView POV(());
   ForwardDataflowWorklist Worklist(CFCtx.getCFG(), );
 
-  std::vector> BlockStates;
-  BlockStates.resize(CFCtx.getCFG().size(), llvm::None);
+  std::vector> BlockStates(
+  CFCtx.getCFG().size(), llvm::None);
 
   // The entry basic block doesn't contain statements so it can be skipped.
   const CFGBlock  = CFCtx.getCFG().getEntry();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131178: [clang][dataflow][NFC] Convert mutable vector references to ArrayRef

2022-08-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

`transferBlock` and `computeBlockInputState` only read the
`BlockStates` vector for the predecessor block(s), and do not need to
mutate any of the contents. Only `runTypeErasedDataflowAnalysis`
writes into the `vector`, so simply down to an `ArrayRef`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131178

Files:
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -164,7 +164,7 @@
 ///   `llvm::None` represent basic blocks that are not evaluated yet.
 static TypeErasedDataflowAnalysisState computeBlockInputState(
 const ControlFlowContext ,
-std::vector> ,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ) {
   llvm::DenseSet Preds;
@@ -303,7 +303,7 @@
 
 TypeErasedDataflowAnalysisState transferBlock(
 const ControlFlowContext ,
-std::vector> ,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ,
 std::function> ,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ,
 std::functionIndex: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -164,7 +164,7 @@
 ///   `llvm::None` represent basic blocks that are not evaluated yet.
 static TypeErasedDataflowAnalysisState computeBlockInputState(
 const ControlFlowContext ,
-std::vector> ,
+llvm::ArrayRef> BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ) {
   llvm::DenseSet Preds;
@@ -303,7 +303,7 @@
 
 TypeErasedDataflowAnalysisState transferBlock(
 const ControlFlowContext ,
-std::vector> ,
+llvm::ArrayRef> BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ,
 std::function> ,
+llvm::ArrayRef> BlockStates,
 const CFGBlock , const Environment ,
 TypeErasedDataflowAnalysis ,
 std::function___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131109: [clang][dataflow][NFC] Fix outdated comment on getStableStorageLocation

2022-08-04 Thread Eric Li via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG54d24eae9872: [clang][dataflow][NFC] Fix outdated comment on 
getStableStorageLocation (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131109

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h


Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -93,9 +93,7 @@
 
   /// Returns a new storage location appropriate for `Type`.
   ///
-  /// Requirements:
-  ///
-  ///  `Type` must not be null.
+  /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`.
   StorageLocation (QualType Type);
 
   /// Returns a stable storage location for `D`.


Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -93,9 +93,7 @@
 
   /// Returns a new storage location appropriate for `Type`.
   ///
-  /// Requirements:
-  ///
-  ///  `Type` must not be null.
+  /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`.
   StorageLocation (QualType Type);
 
   /// Returns a stable storage location for `D`.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131109: [clang][dataflow][NFC] Fix outdated comment on getStableStorageLocation

2022-08-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: sgatev.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Follow-up to D129097 .

It is no longer a requirement that the `QualType` passed to to
`DataflowAnalysisContext::getStableStorageLocation()` is not null. A
null type pass as an argument is only applicable as the pointee type
of a `std::nullptr_t` pointer.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131109

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h


Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -93,9 +93,7 @@
 
   /// Returns a new storage location appropriate for `Type`.
   ///
-  /// Requirements:
-  ///
-  ///  `Type` must not be null.
+  /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`.
   StorageLocation (QualType Type);
 
   /// Returns a stable storage location for `D`.


Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -93,9 +93,7 @@
 
   /// Returns a new storage location appropriate for `Type`.
   ///
-  /// Requirements:
-  ///
-  ///  `Type` must not be null.
+  /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`.
   StorageLocation (QualType Type);
 
   /// Returns a stable storage location for `D`.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D129097: [clang][dataflow] Handle null pointers of type std::nullptr_t

2022-08-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: 
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h:96
   ///
   ///  `Type` must not be null.
   StorageLocation (QualType Type);

sgatev wrote:
> This is inconsistent with the change introduced by this patch.
Ah, you're correct. I can mail a fix out later today.



Comment at: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp:27
 DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
-  assert(!Type.isNull());
-  if (Type->isStructureOrClassType() || Type->isUnionType()) {
+  if (!Type.isNull() &&
+  (Type->isStructureOrClassType() || Type->isUnionType())) {

sgatev wrote:
> What does that mean? We are analyzing an incomplete translation unit? Why 
> would the type ever be null here?
See the motivating test case:

```
// Alternatively, use `std::nullptr_t` instead of `my_nullptr_t`.
using my_nullptr_t = decltype(nullptr);
my_nullptr_t Null = 0;
```

This triggers `getOrCreateNullPointerValue` to be called with a null pointee 
type.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129097

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


[PATCH] D130600: [clang][dataflow] Handle return statements

2022-07-27 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: 
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h:114
+  ///
+  ///  `return` must not be assigned a storage location.
+  void setReturnStorageLocation(StorageLocation ) {

samestep wrote:
> li.zhe.hua wrote:
> > Fix this as well? A reader shouldn't need to root around in private 
> > implementation details to understand the requirements for calling a 
> > function.
> Could you clarify what you mean? I was simply copying the signature and 
> docstring from `setThisPointeeStorageLocation`.
You've marked `return` in backticks. There is no parameter named `return` and 
it is unclear what `return` refers to. My best guess is that this is a typo of 
`ReturnLoc`, which is a private data member. So this is a public interface with 
a requirement that a private data member has some property. This should instead 
reframe the requirement as properties from the external reader's perspective.



Comment at: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp:3908
+  Var = true;
+  return;
+}

samestep wrote:
> li.zhe.hua wrote:
> > Why is this change to the test necessary?
> This is mentioned in the patch description (updated earlier today); it's not 
> necessary, but I added it to get a bit of extra coverage for some cases in 
> the `VisitReturnStmt` method this patch adds.
Please add the coverage as a separate test. Separate behaviors should be tested 
as separate tests. go/unit-testing-practices#behavior-testing


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130600

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


[PATCH] D130600: [clang][dataflow] Handle return statements

2022-07-27 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: 
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h:114
+  ///
+  ///  `return` must not be assigned a storage location.
+  void setReturnStorageLocation(StorageLocation ) {

Fix this as well? A reader shouldn't need to root around in private 
implementation details to understand the requirements for calling a function.



Comment at: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp:221
+
   // FIXME: Currently this only works if the callee is never a method and the
   // same callee is never analyzed from multiple separate callsites. To

I'm a little unclear on what "this" is. Is it this entire function, or just the 
call to `getDirectCallee()`? Can this comment be moved somewhere more 
appropriate, and specifically, so it is touching the code that is most relevant 
to it? It is currently floating in the middle of the function, and it's unclear 
to me why new code is being added above it vs. below it.



Comment at: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp:3908
+  Var = true;
+  return;
+}

Why is this change to the test necessary?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130600

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


[PATCH] D130653: [clang][dataflow][NFC] Remove last use of deprecated ctor

2022-07-27 Thread Eric Li via Phabricator via cfe-commits
This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5e28923e332f: [clang][dataflow][NFC] Remove last use of 
deprecated ctor (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130653

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context, DataflowAnalysisOptions{ApplyBuiltinTransfer,
+  TransferOptions{}}) 
{}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context, DataflowAnalysisOptions{ApplyBuiltinTransfer,
+  TransferOptions{}}) {}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130653: [clang][dataflow][NFC] Remove last use of deprecated ctor

2022-07-27 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 448105.
li.zhe.hua added a comment.

Explicitly name `TransferOptions`


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130653

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context, DataflowAnalysisOptions{ApplyBuiltinTransfer,
+  TransferOptions{}}) 
{}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context, DataflowAnalysisOptions{ApplyBuiltinTransfer,
+  TransferOptions{}}) {}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130653: [clang][dataflow][NFC] Remove last use of deprecated ctor

2022-07-27 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Use a delegating constructor to remove the last use of the deprecated
ctor of `TypeErasedDataflowAnalysis`, and then delete it.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130653

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context,
+ DataflowAnalysisOptions{ApplyBuiltinTransfer, {}}) {}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)


Index: clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext , bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context,
+ DataflowAnalysisOptions{ApplyBuiltinTransfer, {}}) {}
 
   explicit DataflowAnalysis(ASTContext ,
 DataflowAnalysisOptions Options)
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130497: [clang][dataflow] Fix MapLattice::insert() to not drop return value

2022-07-25 Thread Eric Li 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 rG29d35ece8249: [clang][dataflow] Fix MapLattice::insert() to 
not drop return value (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130497

Files:
  clang/include/clang/Analysis/FlowSensitive/MapLattice.h
  clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -50,13 +50,18 @@
 static constexpr int Key2 = 1;
 
 namespace {
+using ::testing::_;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
 TEST(MapLatticeTest, InsertWorks) {
   MapLattice Lattice;
-  Lattice.insert({Key1, BooleanLattice(false)});
-  Lattice.insert({Key2, BooleanLattice(false)});
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, true));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, true));
+
+  // Insertion fails on collision.
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, false));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, false));
 
   EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
 Pair(Key2, 
BooleanLattice(false;
Index: clang/include/clang/Analysis/FlowSensitive/MapLattice.h
===
--- clang/include/clang/Analysis/FlowSensitive/MapLattice.h
+++ clang/include/clang/Analysis/FlowSensitive/MapLattice.h
@@ -54,10 +54,13 @@
   // The `bottom` element is the empty map.
   static MapLattice bottom() { return MapLattice(); }
 
-  void insert(const std::pair ) { C.insert(P); }
+  std::pair
+  insert(const std::pair ) {
+return C.insert(P);
+  }
 
-  void insert(std::pair &) {
-C.insert(std::move(P));
+  std::pair insert(std::pair &) 
{
+return C.insert(std::move(P));
   }
 
   unsigned size() const { return C.size(); }


Index: clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -50,13 +50,18 @@
 static constexpr int Key2 = 1;
 
 namespace {
+using ::testing::_;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
 TEST(MapLatticeTest, InsertWorks) {
   MapLattice Lattice;
-  Lattice.insert({Key1, BooleanLattice(false)});
-  Lattice.insert({Key2, BooleanLattice(false)});
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, true));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, true));
+
+  // Insertion fails on collision.
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, false));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, false));
 
   EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
 Pair(Key2, BooleanLattice(false;
Index: clang/include/clang/Analysis/FlowSensitive/MapLattice.h
===
--- clang/include/clang/Analysis/FlowSensitive/MapLattice.h
+++ clang/include/clang/Analysis/FlowSensitive/MapLattice.h
@@ -54,10 +54,13 @@
   // The `bottom` element is the empty map.
   static MapLattice bottom() { return MapLattice(); }
 
-  void insert(const std::pair ) { C.insert(P); }
+  std::pair
+  insert(const std::pair ) {
+return C.insert(P);
+  }
 
-  void insert(std::pair &) {
-C.insert(std::move(P));
+  std::pair insert(std::pair &) {
+return C.insert(std::move(P));
   }
 
   unsigned size() const { return C.size(); }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130497: [clang][dataflow] Fix MapLattice::insert() to not drop return value

2022-07-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Fix `MapLattice` API to return `std::pair`,
allowing users to detect when an element has been inserted without
performing a redundant map lookup.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130497

Files:
  clang/include/clang/Analysis/FlowSensitive/MapLattice.h
  clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -50,13 +50,18 @@
 static constexpr int Key2 = 1;
 
 namespace {
+using ::testing::_;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
 TEST(MapLatticeTest, InsertWorks) {
   MapLattice Lattice;
-  Lattice.insert({Key1, BooleanLattice(false)});
-  Lattice.insert({Key2, BooleanLattice(false)});
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, true));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, true));
+
+  // Insertion fails on collision.
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, false));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, false));
 
   EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
 Pair(Key2, 
BooleanLattice(false;
Index: clang/include/clang/Analysis/FlowSensitive/MapLattice.h
===
--- clang/include/clang/Analysis/FlowSensitive/MapLattice.h
+++ clang/include/clang/Analysis/FlowSensitive/MapLattice.h
@@ -54,10 +54,13 @@
   // The `bottom` element is the empty map.
   static MapLattice bottom() { return MapLattice(); }
 
-  void insert(const std::pair ) { C.insert(P); }
+  std::pair
+  insert(const std::pair ) {
+return C.insert(P);
+  }
 
-  void insert(std::pair &) {
-C.insert(std::move(P));
+  std::pair insert(std::pair &) 
{
+return C.insert(std::move(P));
   }
 
   unsigned size() const { return C.size(); }


Index: clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -50,13 +50,18 @@
 static constexpr int Key2 = 1;
 
 namespace {
+using ::testing::_;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
 TEST(MapLatticeTest, InsertWorks) {
   MapLattice Lattice;
-  Lattice.insert({Key1, BooleanLattice(false)});
-  Lattice.insert({Key2, BooleanLattice(false)});
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, true));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, true));
+
+  // Insertion fails on collision.
+  EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, false));
+  EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, false));
 
   EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
 Pair(Key2, BooleanLattice(false;
Index: clang/include/clang/Analysis/FlowSensitive/MapLattice.h
===
--- clang/include/clang/Analysis/FlowSensitive/MapLattice.h
+++ clang/include/clang/Analysis/FlowSensitive/MapLattice.h
@@ -54,10 +54,13 @@
   // The `bottom` element is the empty map.
   static MapLattice bottom() { return MapLattice(); }
 
-  void insert(const std::pair ) { C.insert(P); }
+  std::pair
+  insert(const std::pair ) {
+return C.insert(P);
+  }
 
-  void insert(std::pair &) {
-C.insert(std::move(P));
+  std::pair insert(std::pair &) {
+return C.insert(std::move(P));
   }
 
   unsigned size() const { return C.size(); }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D130306: [clang][dataflow] Analyze calls to in-TU functions

2022-07-22 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: 
clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h:64
   TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
   : Options({ApplyBuiltinTransfer}) {}
 

Nit: `-Wmissing-field-initializers` is apparently enabled, and starts warning 
on this.



Comment at: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp:42
 void runDataflow(llvm::StringRef Code, Matcher Match,
-  LangStandard::Kind Std = LangStandard::lang_cxx17,
-  bool ApplyBuiltinTransfer = true,
-  llvm::StringRef TargetFun = "target") {
+ DataflowAnalysisOptions Options,
+ LangStandard::Kind Std = LangStandard::lang_cxx17,

For the purposes of the test, there's really only 3 states:

  # No built-in transfer
  # Built-in transfer, no context-sensitive
  # Built-in transfer, with context-sensitive

It may be more readable for tests to have a 3-state enum, that `runDataFlow` 
will then use to produce the corresponding `DataflowAnalysisOptions`. As is, a 
snippet like

```
  {/*.ApplyBuiltinTransfer=*/true,
   /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/false}});
```

is rough to read. Good enum names with good comments would probably make this 
much better. WDYT?



Comment at: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp:67
+ llvm::StringRef TargetFun = "target") {
+  runDataflow(Code, Match, {ApplyBuiltinTransfer}, Std, TargetFun);
+}

Nit: `-Wmissing-field-initializers` is apparently enabled, and starts warning 
on this.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D130306

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


[PATCH] D129097: [clang][dataflow] Handle null pointers of type std::nullptr_t

2022-07-05 Thread Eric Li 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 rGf10d271ae27f: [clang][dataflow] Handle null pointers of type 
std::nullptr_t (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129097

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2213,12 +2213,14 @@
 
 TEST(TransferTest, NullToPointerCast) {
   std::string Code = R"(
+using my_nullptr_t = decltype(nullptr);
 struct Baz {};
 void target() {
   int *FooX = nullptr;
   int *FooY = nullptr;
   bool **Bar = nullptr;
   Baz *Baz = nullptr;
+  my_nullptr_t Null = 0;
   // [[p]]
 }
   )";
@@ -2242,6 +2244,9 @@
 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
 ASSERT_THAT(BazDecl, NotNull());
 
+const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null");
+ASSERT_THAT(NullDecl, NotNull());
+
 const auto *FooXVal =
 cast(Env.getValue(*FooXDecl, SkipPast::None));
 const auto *FooYVal =
@@ -2250,6 +2255,8 @@
 cast(Env.getValue(*BarDecl, SkipPast::None));
 const auto *BazVal =
 cast(Env.getValue(*BazDecl, SkipPast::None));
+const auto *NullVal =
+cast(Env.getValue(*NullDecl, SkipPast::None));
 
 EXPECT_EQ(FooXVal, FooYVal);
 EXPECT_NE(FooXVal, BarVal);
@@ -2267,6 +2274,11 @@
 const StorageLocation  = BazVal->getPointeeLoc();
 EXPECT_TRUE(isa(BazPointeeLoc));
 EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
+
+const StorageLocation  =
+NullVal->getPointeeLoc();
+EXPECT_TRUE(isa(NullPointeeLoc));
+EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
   });
 }
 
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -24,8 +24,8 @@
 
 StorageLocation &
 DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
-  assert(!Type.isNull());
-  if (Type->isStructureOrClassType() || Type->isUnionType()) {
+  if (!Type.isNull() &&
+  (Type->isStructureOrClassType() || Type->isUnionType())) {
 // FIXME: Explore options to avoid eager initialization of fields as some of
 // them might not be needed for a particular analysis.
 llvm::DenseMap FieldLocs;
@@ -57,8 +57,8 @@
 
 PointerValue &
 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
-  assert(!PointeeType.isNull());
-  auto CanonicalPointeeType = PointeeType.getCanonicalType();
+  auto CanonicalPointeeType =
+  PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
   if (Res.second) {
 auto  = getStableStorageLocation(CanonicalPointeeType);
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -155,6 +155,7 @@
 
   /// Returns a pointer value that represents a null pointer. Calls with
   /// `PointeeType` that are canonically equivalent will return the same result.
+  /// A null `PointeeType` can be used for the pointee of `std::nullptr_t`.
   PointerValue (QualType PointeeType);
 
   /// Returns a symbolic boolean value that models a boolean literal equal to
@@ -251,6 +252,17 @@
   bool equivalentBoolValues(BoolValue , BoolValue );
 
 private:
+  struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo {
+static QualType getEmptyKey() {
+  // Allow a NULL `QualType` by using a different value as the empty key.
+  return QualType::getFromOpaquePtr(reinterpret_cast(1));
+}
+
+using DenseMapInfo::getHashValue;
+using DenseMapInfo::getTombstoneKey;
+using DenseMapInfo::isEqual;
+  };
+
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
   /// to track tokens of flow conditions 

[PATCH] D129097: [clang][dataflow] Handle null pointers of type std::nullptr_t

2022-07-05 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 442291.
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129097

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2213,12 +2213,14 @@
 
 TEST(TransferTest, NullToPointerCast) {
   std::string Code = R"(
+using my_nullptr_t = decltype(nullptr);
 struct Baz {};
 void target() {
   int *FooX = nullptr;
   int *FooY = nullptr;
   bool **Bar = nullptr;
   Baz *Baz = nullptr;
+  my_nullptr_t Null = 0;
   // [[p]]
 }
   )";
@@ -2242,6 +2244,9 @@
 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
 ASSERT_THAT(BazDecl, NotNull());
 
+const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null");
+ASSERT_THAT(NullDecl, NotNull());
+
 const auto *FooXVal =
 cast(Env.getValue(*FooXDecl, SkipPast::None));
 const auto *FooYVal =
@@ -2250,6 +2255,8 @@
 cast(Env.getValue(*BarDecl, SkipPast::None));
 const auto *BazVal =
 cast(Env.getValue(*BazDecl, SkipPast::None));
+const auto *NullVal =
+cast(Env.getValue(*NullDecl, SkipPast::None));
 
 EXPECT_EQ(FooXVal, FooYVal);
 EXPECT_NE(FooXVal, BarVal);
@@ -2267,6 +2274,11 @@
 const StorageLocation  = BazVal->getPointeeLoc();
 EXPECT_TRUE(isa(BazPointeeLoc));
 EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
+
+const StorageLocation  =
+NullVal->getPointeeLoc();
+EXPECT_TRUE(isa(NullPointeeLoc));
+EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
   });
 }
 
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -24,8 +24,8 @@
 
 StorageLocation &
 DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
-  assert(!Type.isNull());
-  if (Type->isStructureOrClassType() || Type->isUnionType()) {
+  if (!Type.isNull() &&
+  (Type->isStructureOrClassType() || Type->isUnionType())) {
 // FIXME: Explore options to avoid eager initialization of fields as some of
 // them might not be needed for a particular analysis.
 llvm::DenseMap FieldLocs;
@@ -57,8 +57,8 @@
 
 PointerValue &
 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
-  assert(!PointeeType.isNull());
-  auto CanonicalPointeeType = PointeeType.getCanonicalType();
+  auto CanonicalPointeeType =
+  PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
   if (Res.second) {
 auto  = getStableStorageLocation(CanonicalPointeeType);
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -155,6 +155,7 @@
 
   /// Returns a pointer value that represents a null pointer. Calls with
   /// `PointeeType` that are canonically equivalent will return the same result.
+  /// A null `PointeeType` can be used for the pointee of `std::nullptr_t`.
   PointerValue (QualType PointeeType);
 
   /// Returns a symbolic boolean value that models a boolean literal equal to
@@ -251,6 +252,17 @@
   bool equivalentBoolValues(BoolValue , BoolValue );
 
 private:
+  struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo {
+static QualType getEmptyKey() {
+  // Allow a NULL `QualType` by using a different value as the empty key.
+  return QualType::getFromOpaquePtr(reinterpret_cast(1));
+}
+
+using DenseMapInfo::getHashValue;
+using DenseMapInfo::getTombstoneKey;
+using DenseMapInfo::isEqual;
+  };
+
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
   /// to track tokens of flow conditions that were already visited by recursive
@@ -311,7 +323,8 @@
   // required to initialize the `PointeeLoc` field in 

[PATCH] D129097: [clang][dataflow] Handle null pointers of type std::nullptr_t

2022-07-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: gribozavr2.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Treat `std::nullptr_t` as a regular scalar type to avoid tripping
assertions when analyzing code that uses `std::nullptr_t`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129097

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2219,6 +2219,9 @@
   int *FooY = nullptr;
   bool **Bar = nullptr;
   Baz *Baz = nullptr;
+  // Use decltype to avoid needing to include . This generates an
+  // implicit NullToPointer cast of type `std::nullptr_t`.
+  decltype(nullptr) Null = 0;
   // [[p]]
 }
   )";
@@ -2242,6 +2245,9 @@
 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
 ASSERT_THAT(BazDecl, NotNull());
 
+const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null");
+ASSERT_THAT(NullDecl, NotNull());
+
 const auto *FooXVal =
 cast(Env.getValue(*FooXDecl, SkipPast::None));
 const auto *FooYVal =
@@ -2250,6 +2256,8 @@
 cast(Env.getValue(*BarDecl, SkipPast::None));
 const auto *BazVal =
 cast(Env.getValue(*BazDecl, SkipPast::None));
+const auto *NullVal =
+cast(Env.getValue(*NullDecl, SkipPast::None));
 
 EXPECT_EQ(FooXVal, FooYVal);
 EXPECT_NE(FooXVal, BarVal);
@@ -2267,6 +2275,11 @@
 const StorageLocation  = BazVal->getPointeeLoc();
 EXPECT_TRUE(isa(BazPointeeLoc));
 EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
+
+const StorageLocation  =
+NullVal->getPointeeLoc();
+EXPECT_TRUE(isa(NullPointeeLoc));
+EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
   });
 }
 
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -24,8 +24,8 @@
 
 StorageLocation &
 DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
-  assert(!Type.isNull());
-  if (Type->isStructureOrClassType() || Type->isUnionType()) {
+  if (!Type.isNull() &&
+  (Type->isStructureOrClassType() || Type->isUnionType())) {
 // FIXME: Explore options to avoid eager initialization of fields as some of
 // them might not be needed for a particular analysis.
 llvm::DenseMap FieldLocs;
@@ -57,8 +57,8 @@
 
 PointerValue &
 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
-  assert(!PointeeType.isNull());
-  auto CanonicalPointeeType = PointeeType.getCanonicalType();
+  auto CanonicalPointeeType =
+  PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
   if (Res.second) {
 auto  = getStableStorageLocation(CanonicalPointeeType);
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -251,6 +251,17 @@
   bool equivalentBoolValues(BoolValue , BoolValue );
 
 private:
+  struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo {
+static QualType getEmptyKey() {
+  // Allow a NULL `QualType` by using a different value as the empty key.
+  return QualType::getFromOpaquePtr(reinterpret_cast(1));
+}
+
+using DenseMapInfo::getHashValue;
+using DenseMapInfo::getTombstoneKey;
+using DenseMapInfo::isEqual;
+  };
+
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
   /// to track tokens of flow conditions that were already visited by recursive
@@ -311,7 +322,8 @@
   // required to initialize the `PointeeLoc` field in `PointerValue`. Consider
   // creating a type-independent `NullPointerValue` without a `PointeeLoc`
   // field.
-  llvm::DenseMap NullPointerVals;
+  llvm::DenseMap
+  NullPointerVals;
 
   

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

2022-06-30 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

In D128807#3620573 , @ymandel wrote:

> Eric -- what do you think? You've thought a lot more about metadata recently.

(Apologies for the delay. Still struggling to figure out a good workflow for 
reviewing patches...)

Can you say more about what your intended use of the note is? Perhaps that can 
motivate some of the discussion.

From what I can see, here's my thoughts:

- A note being a part of an edit seems weird at best. An `ASTEdit` and `Edit` 
are fragments of a greater, logical change. That a note should semantically be 
associated with the insertion of an open paren "`(`" but not the close paren 
"`)`" seems odd to me.
- The note takes the location of the edit it is attached to. Perhaps that could 
be of some convenience when those coincide, but I don't believe that should 
necessarily be the case. I'm imagining notes could be used to point out 
//other// parts of the source that are interesting.

I don't have a lot of experience with how notes appear when surfaced, but I 
suspect that this interface might encourage callers to tack notes on without 
putting a lot of thought to //where// the note shows up. As is, I'm inclined to 
think that extending the metadata from `std::string` to something richer would 
be a better design.

Let me know if I'm misunderstanding the code or the intent of the patch.




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

Are we sure that this is what someone would want? Perhaps I am not creative 
enough, but this seems like it could explode a large number of notes that 
coincide to edits, which seems like an odd thing to want.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128807

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


[PATCH] D128848: Fix assertion when analyzing a for-loop with no condition expression

2022-06-29 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua abandoned this revision.
li.zhe.hua added a comment.

In D128848#3620185 , @ymandel wrote:

> Looks like you and Stanislav had the same idea?
> https://reviews.llvm.org/D128833

Seems like it.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128848

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


[PATCH] D128848: Fix assertion when analyzing a for-loop with no condition expression

2022-06-29 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

For loops are allowed to have an empty conditional expression; we
should not assert that it must have one.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128848

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -10,7 +10,6 @@
 #include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
@@ -3827,4 +3826,42 @@
   });
 }
 
+TEST_F(TransferTest, ForStmtEmptyBranchExtendsFlowCondition) {
+  std::string Code = R"cc(
+void target(bool Foo) {
+  for (;;) {
+if (!Foo) {
+  break;
+}
+(void)0;
+// [[loop_body]]
+  }
+  (void)0;
+  // [[after_loop]]
+}
+  )cc";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results,
+ElementsAre(Pair("after_loop", _), Pair("loop_body", _)));
+const Environment  = Results[1].second.Env;
+const Environment  = Results[0].second.Env;
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+BoolValue  =
+*cast(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
+EXPECT_TRUE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal));
+
+BoolValue  =
+*cast(AfterLoopEnv.getValue(*FooDecl, SkipPast::None));
+EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
+AfterLoopEnv.makeNot(AfterLoopFooVal)));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -97,7 +97,8 @@
 
   void VisitForStmt(const ForStmt *S) {
 auto *Cond = S->getCond();
-assert(Cond != nullptr);
+if (Cond == nullptr)
+  return;
 extendFlowCondition(*Cond);
   }
 


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -10,7 +10,6 @@
 #include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
@@ -3827,4 +3826,42 @@
   });
 }
 
+TEST_F(TransferTest, ForStmtEmptyBranchExtendsFlowCondition) {
+  std::string Code = R"cc(
+void target(bool Foo) {
+  for (;;) {
+if (!Foo) {
+  break;
+}
+(void)0;
+// [[loop_body]]
+  }
+  (void)0;
+  // [[after_loop]]
+}
+  )cc";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results,
+ElementsAre(Pair("after_loop", _), Pair("loop_body", _)));
+const Environment  = Results[1].second.Env;
+const Environment  = Results[0].second.Env;
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+BoolValue  =
+*cast(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
+EXPECT_TRUE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal));
+
+BoolValue  =
+*cast(AfterLoopEnv.getValue(*FooDecl, SkipPast::None));
+EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
+AfterLoopEnv.makeNot(AfterLoopFooVal)));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -97,7 +97,8 @@
 
   void VisitForStmt(const ForStmt *S) {
 auto *Cond = S->getCond();
-assert(Cond != nullptr);
+if (Cond == nullptr)
+   

[PATCH] D126413: [clang][dataflow] Fix incorrect CXXThisExpr pointee for lambdas

2022-05-25 Thread Eric Li 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 rG5520c5839016: [clang][dataflow] Fix incorrect CXXThisExpr 
pointee for lambdas (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126413

Files:
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1476,6 +1476,112 @@
   });
 }
 
+TEST_F(TransferTest, StructThisInLambda) {
+  std::string ThisCaptureCode = R"(
+struct A {
+  void frob() {
+[this]() {
+  int Foo = Bar;
+  // [[p1]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  ThisCaptureCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string RefCaptureDefaultCode = R"(
+struct A {
+  void frob() {
+[&]() {
+  int Foo = Bar;
+  // [[p2]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  RefCaptureDefaultCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string FreeFunctionLambdaCode = R"(
+void foo() {
+  int Bar;
+  [&]() {
+int Foo = Bar;
+// [[p3]]
+  }();
+}
+  )";
+  runDataflow(
+  FreeFunctionLambdaCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p3", _)));
+const Environment  = Results[0].second.Env;
+
+EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+}
+
 TEST_F(TransferTest, ConstructorInitializer) {
   std::string Code = R"(
 struct target {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -216,7 +216,12 @@
   }
 
   if (const auto *MethodDecl = dyn_cast()) {
-if (!MethodDecl->isStatic()) {
+auto *Parent = MethodDecl->getParent();
+assert(Parent != nullptr);
+if (Parent->isLambda())
+  MethodDecl = dyn_cast(Parent->getDeclContext());
+
+if (MethodDecl && !MethodDecl->isStatic()) {
   QualType ThisPointeeType = MethodDecl->getThisObjectType();
   // FIXME: Add support for union types.
   if (!ThisPointeeType->isUnionType()) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126405: [clang][dataflow] Relax assert on existence of `this` pointee storage

2022-05-25 Thread Eric Li 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 rG33b598a808b9: [clang][dataflow] Relax assert on existence of 
`this` pointee storage (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126405

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are not supported yet, and will not have a location for the
+  // `this` expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are not supported yet, and will not have a location for the
+  // `this` expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126413: [clang][dataflow] Fix incorrect CXXThisExpr pointee for lambdas

2022-05-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 432124.
li.zhe.hua added a comment.

Address reviewer comment.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126413

Files:
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1476,6 +1476,112 @@
   });
 }
 
+TEST_F(TransferTest, StructThisInLambda) {
+  std::string ThisCaptureCode = R"(
+struct A {
+  void frob() {
+[this]() {
+  int Foo = Bar;
+  // [[p1]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  ThisCaptureCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string RefCaptureDefaultCode = R"(
+struct A {
+  void frob() {
+[&]() {
+  int Foo = Bar;
+  // [[p2]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  RefCaptureDefaultCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string FreeFunctionLambdaCode = R"(
+void foo() {
+  int Bar;
+  [&]() {
+int Foo = Bar;
+// [[p3]]
+  }();
+}
+  )";
+  runDataflow(
+  FreeFunctionLambdaCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p3", _)));
+const Environment  = Results[0].second.Env;
+
+EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+}
+
 TEST_F(TransferTest, ConstructorInitializer) {
   std::string Code = R"(
 struct target {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -216,7 +216,12 @@
   }
 
   if (const auto *MethodDecl = dyn_cast()) {
-if (!MethodDecl->isStatic()) {
+auto *Parent = MethodDecl->getParent();
+assert(Parent != nullptr);
+if (Parent->isLambda())
+  MethodDecl = dyn_cast(Parent->getDeclContext());
+
+if (MethodDecl && !MethodDecl->isStatic()) {
   QualType ThisPointeeType = MethodDecl->getThisObjectType();
   // FIXME: Add support for union types.
   if (!ThisPointeeType->isUnionType()) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126405: [clang][dataflow] Relax assert on existence of `this` pointee storage

2022-05-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 432119.
li.zhe.hua added a comment.

Accidentally a word...


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126405

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are not supported yet, and will not have a location for the
+  // `this` expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are not supported yet, and will not have a location for the
+  // `this` expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126405: [clang][dataflow] Relax assert on existence of `this` pointee storage

2022-05-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 432117.
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added a comment.

Address reviewer comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D126405

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are supported yet, and will not have a location for the
+  // expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+  int A;
+  float B;
+};
+
+void foo() {
+  Union A;
+  Union B;
+  A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  // Unions are supported yet, and will not have a location for the
+  // expression's pointee.
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D126413: [clang][dataflow] Fix incorrect CXXThisExpr pointee for lambdas

2022-05-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When constructing the `Environment`, the `this` pointee is established
for a `CXXMethodDecl` by looking at its parent. However, inside of
lambdas, a `CXXThisExpr` refers to the captured `this` coming from the
enclosing member function.

When establishing the `this` pointee for a function, we check whether
the function is a lambda, and check for an enclosing member function
to establish the `this` pointee storage location.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D126413

Files:
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1476,6 +1476,112 @@
   });
 }
 
+TEST_F(TransferTest, StructThisInLambda) {
+  std::string ThisCaptureCode = R"(
+struct A {
+  void frob() {
+[this]() {
+  int Foo = Bar;
+  // [[p]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  ThisCaptureCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string RefCaptureDefaultCode = R"(
+struct A {
+  void frob() {
+[&]() {
+  int Foo = Bar;
+  // [[p]]
+}();
+  }
+
+  int Bar;
+};
+  )";
+  runDataflow(
+  RefCaptureDefaultCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+const Environment  = Results[0].second.Env;
+
+const auto *ThisLoc = dyn_cast(
+Env.getThisPointeeStorageLocation());
+ASSERT_THAT(ThisLoc, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const auto *BarLoc =
+cast(>getChild(*BarDecl));
+ASSERT_TRUE(isa_and_nonnull(BarLoc));
+
+const Value *BarVal = Env.getValue(*BarLoc);
+ASSERT_TRUE(isa_and_nonnull(BarVal));
+
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+
+  std::string FreeFunctionLambdaCode = R"(
+void foo() {
+  int Bar;
+  [&]() {
+int Foo = Bar;
+// [[p]]
+  }();
+}
+  )";
+  runDataflow(
+  FreeFunctionLambdaCode,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+const Environment  = Results[0].second.Env;
+
+EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
+  },
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
+}
+
 TEST_F(TransferTest, ConstructorInitializer) {
   std::string Code = R"(
 struct target {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -216,7 +216,12 @@
   }
 
   if (const auto *MethodDecl = dyn_cast()) {
-if (!MethodDecl->isStatic()) {
+auto *Parent = MethodDecl->getParent();
+assert(Parent != nullptr);
+if (Parent->isLambda())
+  MethodDecl = dyn_cast(Parent->getDeclContext());
+
+if (MethodDecl && !MethodDecl->isStatic()) {
   QualType ThisPointeeType = MethodDecl->getThisObjectType();
   // FIXME: Add support for union types.
   if 

[PATCH] D126405: [clang][dataflow] Relax assert on existence of `this` pointee storage

2022-05-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Support for unions is incomplete (per 99f7d55e 
) and the 
`this` pointee
storage location is not set for unions. The assert in
`VisitCXXThisExpr` is then guaranteed to trigger when analyzing member
functions of a union.

This commit changes the assert to an early-return. Any expression may
be undefined, and so having a value for the `CXXThisExpr` is not a
postcondition of the transfer function.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D126405

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+int A;
+float B;
+};
+
+void foo() {
+Union A;
+Union B;
+A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,8 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@
   template 
   void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
-   bool ApplyBuiltinTransfer = true) {
+   bool ApplyBuiltinTransfer = true,
+   llvm::StringRef TargetFun = "target") {
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
+Code, TargetFun,
 [ApplyBuiltinTransfer](ASTContext , Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@
 });
 }
 
+TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
+  std::string Code = R"(
+union Union {
+int A;
+float B;
+};
+
+void foo() {
+Union A;
+Union B;
+A = B;
+}
+  )";
+  // This is a crash regression test when calling the transfer function on a
+  // `CXXThisExpr` that refers to a union.
+  runDataflow(
+  Code,
+  [](llvm::ArrayRef<
+ std::pair>>,
+ ASTContext &) {},
+  LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,8 @@
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
-assert(ThisPointeeLoc != nullptr);
+if (ThisPointeeLoc == nullptr)
+  return;
 
 auto  = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org

[PATCH] D125821: [clang][dataflow] Fix double visitation of nested logical operators

2022-05-17 Thread Eric Li via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
li.zhe.hua marked an inline comment as done.
Closed by commit rG5bbef2e3fff1: [clang][dataflow] Fix double visitation of 
nested logical operators (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D125821

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@
   EXPECT_EQ(>getRightSubValue(), BarVal);
 });
   }
+
+  {
+std::string Code = R"(
+  void target(bool A, bool B, bool C, bool D) {
+bool Foo = ((A && B) && C) && D;
+// [[p]]
+  }
+)";
+runDataflow(
+Code, [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment  = Results[0].second.Env;
+
+  const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
+  ASSERT_THAT(ADecl, NotNull());
+
+  const auto *AVal =
+  dyn_cast_or_null(Env.getValue(*ADecl, 
SkipPast::None));
+  ASSERT_THAT(AVal, NotNull());
+
+  const ValueDecl *BDecl = findValueDecl(ASTCtx, "B");
+  ASSERT_THAT(BDecl, NotNull());
+
+  const auto *BVal =
+  dyn_cast_or_null(Env.getValue(*BDecl, 
SkipPast::None));
+  ASSERT_THAT(BVal, NotNull());
+
+  const ValueDecl *CDecl = findValueDecl(ASTCtx, "C");
+  ASSERT_THAT(CDecl, NotNull());
+
+  const auto *CVal =
+  dyn_cast_or_null(Env.getValue(*CDecl, 
SkipPast::None));
+  ASSERT_THAT(CVal, NotNull());
+
+  const ValueDecl *DDecl = findValueDecl(ASTCtx, "D");
+  ASSERT_THAT(DDecl, NotNull());
+
+  const auto *DVal =
+  dyn_cast_or_null(Env.getValue(*DDecl, 
SkipPast::None));
+  ASSERT_THAT(DVal, NotNull());
+
+  const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+  ASSERT_THAT(FooDecl, NotNull());
+
+  const auto *FooVal = dyn_cast_or_null(
+  Env.getValue(*FooDecl, SkipPast::None));
+  ASSERT_THAT(FooVal, NotNull());
+
+  const auto  =
+  cast(FooVal->getLeftSubValue());
+  const auto  =
+  cast(FooLeftSubVal.getLeftSubValue());
+  EXPECT_EQ((), AVal);
+  EXPECT_EQ((), BVal);
+  EXPECT_EQ((), CVal);
+  EXPECT_EQ(>getRightSubValue(), DVal);
+});
+  }
 }
 
 TEST_F(TransferTest, AssignFromBoolNegation) {
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -571,12 +571,15 @@
 return *Val;
 }
 
-// Sub-expressions that are logic operators are not added in basic blocks
-// (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
-// operator, it isn't evaluated and assigned a value yet. In that case, we
-// need to first visit `SubExpr` and then try to get the value that gets
-// assigned to it.
-Visit();
+if (Env.getStorageLocation(SubExpr, SkipPast::None) == nullptr) {
+  // Sub-expressions that are logic operators are not added in basic blocks
+  // (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
+  // operator, it may not have been evaluated and assigned a value yet. In
+  // that case, we need to first visit `SubExpr` and then try to get the
+  // value that gets assigned to it.
+  Visit();
+}
+
 if (auto *Val = dyn_cast_or_null(
 Env.getValue(SubExpr, SkipPast::Reference)))
   return *Val;


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@
   EXPECT_EQ(>getRightSubValue(), BarVal);
 });
   }
+
+  {
+std::string Code = R"(
+  void target(bool A, bool B, bool C, bool D) {
+bool Foo = ((A && B) && C) && D;
+// [[p]]
+  }
+)";
+runDataflow(
+Code, [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment  = Results[0].second.Env;
+
+  const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
+  ASSERT_THAT(ADecl, 

[PATCH] D125821: [clang][dataflow] Fix double visitation of nested logical operators

2022-05-17 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:574
 
-// Sub-expressions that are logic operators are not added in basic blocks
-// (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
-// operator, it isn't evaluated and assigned a value yet. In that case, we
-// need to first visit `SubExpr` and then try to get the value that gets
-// assigned to it.
-Visit();
-if (auto *Val = dyn_cast_or_null(
-Env.getValue(SubExpr, SkipPast::Reference)))
+auto *SubExprLoc = Env.getStorageLocation(SubExpr, SkipPast::Reference);
+if (SubExprLoc == nullptr) {

ymandel wrote:
> I'm afraid this may allow us to hide a bug.  Specifically: consider if 
> `SubExpr` was already evaluated to a RefenceValue and that value's 
> StorageLocation does *not* map to a value. Then, this line will be true, but 
> we won't want to revisit the `SubExrp`. Now, that may be impossible in 
> practice, so I'm not sure how big a problem this is, but it seems safer to 
> just use `SkipPast::None` and not rely on any guarantees.
> 
> That said, i see why  you want the uniformity of `Reference` because it is 
> used below. That said, any idea if we actually need to use `Reference` 
> skipping at all? I think so -- the case of a variable or field access as 
> sub-expression, but those probably have a cast around them anyhow, so i'm not 
> sure those will require reference skipping.
> I'm afraid this may allow us to hide a bug.

Ah, yes, that is definitely true.

> That said, any idea if we actually need to use `Reference` skipping at all?

Whatever we choose, I imagine it should be kept the same as the analogous line 
in [[ 
https://github.com/llvm/llvm-project/blob/118c5d1c97b4191678663bf2a938eee7dec6f0b1/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp#L112-L113
 | extendFlowCondition ]]? I've opted to not change it for now, given my 
uncertainty.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D125821

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


[PATCH] D125821: [clang][dataflow] Fix double visitation of nested logical operators

2022-05-17 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 430152.
li.zhe.hua added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D125821

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@
   EXPECT_EQ(>getRightSubValue(), BarVal);
 });
   }
+
+  {
+std::string Code = R"(
+  void target(bool A, bool B, bool C, bool D) {
+bool Foo = ((A && B) && C) && D;
+// [[p]]
+  }
+)";
+runDataflow(
+Code, [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment  = Results[0].second.Env;
+
+  const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
+  ASSERT_THAT(ADecl, NotNull());
+
+  const auto *AVal =
+  dyn_cast_or_null(Env.getValue(*ADecl, 
SkipPast::None));
+  ASSERT_THAT(AVal, NotNull());
+
+  const ValueDecl *BDecl = findValueDecl(ASTCtx, "B");
+  ASSERT_THAT(BDecl, NotNull());
+
+  const auto *BVal =
+  dyn_cast_or_null(Env.getValue(*BDecl, 
SkipPast::None));
+  ASSERT_THAT(BVal, NotNull());
+
+  const ValueDecl *CDecl = findValueDecl(ASTCtx, "C");
+  ASSERT_THAT(CDecl, NotNull());
+
+  const auto *CVal =
+  dyn_cast_or_null(Env.getValue(*CDecl, 
SkipPast::None));
+  ASSERT_THAT(CVal, NotNull());
+
+  const ValueDecl *DDecl = findValueDecl(ASTCtx, "D");
+  ASSERT_THAT(DDecl, NotNull());
+
+  const auto *DVal =
+  dyn_cast_or_null(Env.getValue(*DDecl, 
SkipPast::None));
+  ASSERT_THAT(DVal, NotNull());
+
+  const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+  ASSERT_THAT(FooDecl, NotNull());
+
+  const auto *FooVal = dyn_cast_or_null(
+  Env.getValue(*FooDecl, SkipPast::None));
+  ASSERT_THAT(FooVal, NotNull());
+
+  const auto  =
+  cast(FooVal->getLeftSubValue());
+  const auto  =
+  cast(FooLeftSubVal.getLeftSubValue());
+  EXPECT_EQ((), AVal);
+  EXPECT_EQ((), BVal);
+  EXPECT_EQ((), CVal);
+  EXPECT_EQ(>getRightSubValue(), DVal);
+});
+  }
 }
 
 TEST_F(TransferTest, AssignFromBoolNegation) {
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -571,12 +571,15 @@
 return *Val;
 }
 
-// Sub-expressions that are logic operators are not added in basic blocks
-// (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
-// operator, it isn't evaluated and assigned a value yet. In that case, we
-// need to first visit `SubExpr` and then try to get the value that gets
-// assigned to it.
-Visit();
+if (Env.getStorageLocation(SubExpr, SkipPast::None) == nullptr) {
+  // Sub-expressions that are logic operators are not added in basic blocks
+  // (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
+  // operator, it may not have been evaluated and assigned a value yet. In
+  // that case, we need to first visit `SubExpr` and then try to get the
+  // value that gets assigned to it.
+  Visit();
+}
+
 if (auto *Val = dyn_cast_or_null(
 Env.getValue(SubExpr, SkipPast::Reference)))
   return *Val;


Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@
   EXPECT_EQ(>getRightSubValue(), BarVal);
 });
   }
+
+  {
+std::string Code = R"(
+  void target(bool A, bool B, bool C, bool D) {
+bool Foo = ((A && B) && C) && D;
+// [[p]]
+  }
+)";
+runDataflow(
+Code, [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment  = Results[0].second.Env;
+
+  const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
+  ASSERT_THAT(ADecl, NotNull());
+
+  const auto *AVal =
+  dyn_cast_or_null(Env.getValue(*ADecl, SkipPast::None));
+  ASSERT_THAT(AVal, 

[PATCH] D125823: [clang][dataflow] Weaken guard to only check for storage location

2022-05-17 Thread Eric Li 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 rG854c273cbb7e: [clang][dataflow] Weaken guard to only check 
for storage location (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D125823

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -104,7 +104,7 @@
 private:
   void extendFlowCondition(const Expr ) {
 // The terminator sub-expression might not be evaluated.
-if (Env.getValue(Cond, SkipPast::None) == nullptr)
+if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
   transfer(StmtToEnv, Cond, Env);
 
 // FIXME: The flow condition must be an r-value, so `SkipPast::None` should


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -104,7 +104,7 @@
 private:
   void extendFlowCondition(const Expr ) {
 // The terminator sub-expression might not be evaluated.
-if (Env.getValue(Cond, SkipPast::None) == nullptr)
+if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
   transfer(StmtToEnv, Cond, Env);
 
 // FIXME: The flow condition must be an r-value, so `SkipPast::None` should
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D125823: [clang][dataflow] Weaken guard to only check for storage location

2022-05-17 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Weaken the guard for whether a sub-expression has been evaluated to
only check for the storage location, instead of checking for the
value. It should be sufficient to check for the storage location, as
we don't necessarily guarantee that a value will be set for the
location (although this is currently true right now).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D125823

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -104,7 +104,7 @@
 private:
   void extendFlowCondition(const Expr ) {
 // The terminator sub-expression might not be evaluated.
-if (Env.getValue(Cond, SkipPast::None) == nullptr)
+if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
   transfer(StmtToEnv, Cond, Env);
 
 // FIXME: The flow condition must be an r-value, so `SkipPast::None` should


Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -104,7 +104,7 @@
 private:
   void extendFlowCondition(const Expr ) {
 // The terminator sub-expression might not be evaluated.
-if (Env.getValue(Cond, SkipPast::None) == nullptr)
+if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
   transfer(StmtToEnv, Cond, Env);
 
 // FIXME: The flow condition must be an r-value, so `SkipPast::None` should
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D125821: [clang][dataflow] Fix double visitation of nested logical operators

2022-05-17 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Sub-expressions that are logical operators are not spelled out
separately in basic blocks, so we need to manually visit them when we
encounter them. We do this in both the `TerminatorVisitor`
(conditionally) and the `TransferVisitor` (unconditionally), which can
cause cause an expression to be visited twice when the binary
operators are nested 2+ times.

This changes the visit in `TransferVisitor` to check if it has been
evaluated before trying to visit the sub-expression.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D125821

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@
   EXPECT_EQ(>getRightSubValue(), BarVal);
 });
   }
+
+  {
+std::string Code = R"(
+  void target(bool A, bool B, bool C, bool D) {
+bool Foo = ((A && B) && C) && D;
+// [[p]]
+  }
+)";
+runDataflow(
+Code, [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment  = Results[0].second.Env;
+
+  const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
+  ASSERT_THAT(ADecl, NotNull());
+
+  const auto *AVal =
+  dyn_cast_or_null(Env.getValue(*ADecl, SkipPast::None));
+  ASSERT_THAT(AVal, NotNull());
+
+  const ValueDecl *BDecl = findValueDecl(ASTCtx, "B");
+  ASSERT_THAT(BDecl, NotNull());
+
+  const auto *BVal =
+  dyn_cast_or_null(Env.getValue(*BDecl, SkipPast::None));
+  ASSERT_THAT(BVal, NotNull());
+
+  const ValueDecl *CDecl = findValueDecl(ASTCtx, "C");
+  ASSERT_THAT(CDecl, NotNull());
+
+  const auto *CVal =
+  dyn_cast_or_null(Env.getValue(*CDecl, SkipPast::None));
+  ASSERT_THAT(CVal, NotNull());
+
+  const ValueDecl *DDecl = findValueDecl(ASTCtx, "D");
+  ASSERT_THAT(DDecl, NotNull());
+
+  const auto *DVal =
+  dyn_cast_or_null(Env.getValue(*DDecl, SkipPast::None));
+  ASSERT_THAT(DVal, NotNull());
+
+  const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+  ASSERT_THAT(FooDecl, NotNull());
+
+  const auto *FooVal = dyn_cast_or_null(
+  Env.getValue(*FooDecl, SkipPast::None));
+  ASSERT_THAT(FooVal, NotNull());
+
+  const auto  =
+  cast(FooVal->getLeftSubValue());
+  const auto  =
+  cast(FooLeftSubVal.getLeftSubValue());
+  EXPECT_EQ((), AVal);
+  EXPECT_EQ((), BVal);
+  EXPECT_EQ((), CVal);
+  EXPECT_EQ(>getRightSubValue(), DVal);
+});
+  }
 }
 
 TEST_F(TransferTest, AssignFromBoolNegation) {
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -571,14 +571,18 @@
 return *Val;
 }
 
-// Sub-expressions that are logic operators are not added in basic blocks
-// (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
-// operator, it isn't evaluated and assigned a value yet. In that case, we
-// need to first visit `SubExpr` and then try to get the value that gets
-// assigned to it.
-Visit();
-if (auto *Val = dyn_cast_or_null(
-Env.getValue(SubExpr, SkipPast::Reference)))
+auto *SubExprLoc = Env.getStorageLocation(SubExpr, SkipPast::Reference);
+if (SubExprLoc == nullptr) {
+  // Sub-expressions that are logic operators are not added in basic blocks
+  // (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
+  // operator, it may not have been evaluated and assigned a value yet. In
+  // that case, we need to first visit `SubExpr` and then try to get the
+  // value that gets assigned to it.
+  Visit();
+  SubExprLoc = Env.getStorageLocation(SubExpr, SkipPast::Reference);
+  assert(SubExprLoc != nullptr);
+}
+if (auto *Val = dyn_cast_or_null(Env.getValue(*SubExprLoc)))
   return *Val;
 
 // If the value of `SubExpr` is still unknown, we create a fresh symbolic
___
cfe-commits mailing list
cfe-commits@lists.llvm.org

[PATCH] D124965: [clang][dataflow] Centralize expression skipping logic

2022-05-05 Thread Eric Li 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 rG45643cfcc12e: [clang][dataflow] Centralize expression 
skipping logic (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124965

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -46,7 +46,7 @@
   : CFCtx(CFCtx), BlockToState(BlockToState) {}
 
   const Environment *getEnvironment(const Stmt ) const override {
-auto BlockIT = CFCtx.getStmtToBlock().find();
+auto BlockIT = CFCtx.getStmtToBlock().find((S));
 assert(BlockIT != CFCtx.getStmtToBlock().end());
 const auto  = BlockToState[BlockIT->getSecond()->getBlockID()];
 assert(State.hasValue());
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
+auto *LHS = S->getLHS();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,27 +33,12 @@
 namespace clang {
 namespace dataflow {
 
-const Expr *ignoreExprWithCleanups(const Expr *E) {
-  if (auto *C = dyn_cast_or_null(E))
-return C->getSubExpr();
-  return E;
-}
-
 static BoolValue (const Expr , const Expr ,
   Environment ) {
-  // Equality of booleans involves implicit integral casts. Ignore these casts
-  // for now and focus on the values associated with the wrapped expressions.
-  // FIXME: Consider changing this once the framework offers better support for
-  // integral casts.
-  const Expr *LHSNorm = LHS.IgnoreCasts();
-  const Expr *RHSNorm = RHS.IgnoreCasts();
-  assert(LHSNorm != nullptr);
-  assert(RHSNorm != nullptr);
-
-  if (auto *LHSValue = dyn_cast_or_null(
-  Env.getValue(*LHSNorm, SkipPast::Reference)))
-if (auto *RHSValue = dyn_cast_or_null(
-Env.getValue(*RHSNorm, SkipPast::Reference)))
+  if (auto *LHSValue =
+  dyn_cast_or_null(Env.getValue(LHS, SkipPast::Reference)))
+if (auto *RHSValue =
+dyn_cast_or_null(Env.getValue(RHS, SkipPast::Reference)))
   return Env.makeIff(*LHSValue, *RHSValue);
 
   return Env.makeAtomicBoolValue();
@@ -65,14 +50,10 @@
   : StmtToEnv(StmtToEnv), Env(Env) {}
 
   void VisitBinaryOperator(const BinaryOperator *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getLHS() != nullptr);
-const Expr *LHS = S->getLHS()->IgnoreParens();
+const Expr *LHS = S->getLHS();
 assert(LHS != nullptr);
 
-assert(S->getRHS() != nullptr);
-const Expr *RHS = S->getRHS()->IgnoreParens();
+const Expr *RHS = S->getRHS();
 assert(RHS != nullptr);
 
 switch (S->getOpcode()) {
@@ -155,7 +136,7 @@
   return;
 }
 
-InitExpr = ignoreExprWithCleanups(D.getInit());
+InitExpr = D.getInit();
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -476,8 +457,7 @@
   assert(S->getArg(0) != nullptr);
   // `__builtin_expect` returns by-value, so strip away any potential
   // references in the argument.
-  auto *ArgLoc = 

[PATCH] D124965: [clang][dataflow] Centralize expression skipping logic

2022-05-05 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked an inline comment as done.
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:38-41
+  if (auto *LHSValue =
+  dyn_cast_or_null(Env.getValue(LHS, SkipPast::Reference)))
+if (auto *RHSValue =
+dyn_cast_or_null(Env.getValue(RHS, 
SkipPast::Reference)))

ymandel wrote:
> li.zhe.hua wrote:
> > ymandel wrote:
> > > Is the idea that the skipping is now built into `getValue` via 
> > > `getStorageLocation`?
> > I don't believe I fixed this. d002495 addressed this by having integral 
> > casts route to the sub-expression.
> Great. Just to be sure I understand: you mean that the `IgnoreCasts()` calls 
> were already redundant because of d002495?
Correct, that's my understanding. This diff hunk could be separately committed 
first. (And maybe it should, for blame clarity.)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124965

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


[PATCH] D124965: [clang][dataflow] Centralize expression skipping logic

2022-05-05 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:38-41
+  if (auto *LHSValue =
+  dyn_cast_or_null(Env.getValue(LHS, SkipPast::Reference)))
+if (auto *RHSValue =
+dyn_cast_or_null(Env.getValue(RHS, 
SkipPast::Reference)))

ymandel wrote:
> Is the idea that the skipping is now built into `getValue` via 
> `getStorageLocation`?
I don't believe I fixed this. d002495 addressed this by having integral casts 
route to the sub-expression.



Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:545-561
+  void VisitParenExpr(const ParenExpr *S) {
+// The CFG does not contain `ParenExpr` as top-level statements in basic
+// blocks, however manual traversal to sub-expressions may encounter them.
+// Redirect to the sub-expression.
+auto *SubExpr = S->getSubExpr();
+assert(SubExpr != nullptr);
+Visit(SubExpr);

ymandel wrote:
> I thought this is the default behavior?
The default behavior of `StmtVisitor` is that `VisitFoo` will call 
`VisitFooBase` where `Foo` derives from `FooBase` (e.g. the default 
implementation of `VisitExprWithCleanups` calls `VisitFullExpr`). The base case 
is a `VisitStmt` implementation that does [approximately 
nothing](https://github.com/llvm/llvm-project/blob/c2a5a87500d92c7c2e76c595f1b0f439b98b0aff/clang/include/clang/AST/StmtVisitor.h#L170-L171),
 especially if `RetTy = void`.

So in this case, I'm changing this to automatically ignore `ParenExpr` and 
`ExprWithCleanups` and visit the sub-expression, which is not the default 
behavior.

This isn't used when we call the transfer function from CFG-provided 
statements, because the CFG doesn't emit these nodes. However, this is relevant 
when we manually call the transfer function, e.g. from 
[here](https://github.com/llvm/llvm-project/blob/c2a5a87500d92c7c2e76c595f1b0f439b98b0aff/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp#L108).


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124965

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


[PATCH] D124965: [clang][dataflow] Centralize expression skipping logic

2022-05-05 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 427417.
li.zhe.hua marked 3 inline comments as done.
li.zhe.hua added a comment.

Address reviewer comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124965

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -46,7 +46,7 @@
   : CFCtx(CFCtx), BlockToState(BlockToState) {}
 
   const Environment *getEnvironment(const Stmt ) const override {
-auto BlockIT = CFCtx.getStmtToBlock().find();
+auto BlockIT = CFCtx.getStmtToBlock().find((S));
 assert(BlockIT != CFCtx.getStmtToBlock().end());
 const auto  = BlockToState[BlockIT->getSecond()->getBlockID()];
 assert(State.hasValue());
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
+auto *LHS = S->getLHS();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,27 +33,12 @@
 namespace clang {
 namespace dataflow {
 
-const Expr *ignoreExprWithCleanups(const Expr *E) {
-  if (auto *C = dyn_cast_or_null(E))
-return C->getSubExpr();
-  return E;
-}
-
 static BoolValue (const Expr , const Expr ,
   Environment ) {
-  // Equality of booleans involves implicit integral casts. Ignore these casts
-  // for now and focus on the values associated with the wrapped expressions.
-  // FIXME: Consider changing this once the framework offers better support for
-  // integral casts.
-  const Expr *LHSNorm = LHS.IgnoreCasts();
-  const Expr *RHSNorm = RHS.IgnoreCasts();
-  assert(LHSNorm != nullptr);
-  assert(RHSNorm != nullptr);
-
-  if (auto *LHSValue = dyn_cast_or_null(
-  Env.getValue(*LHSNorm, SkipPast::Reference)))
-if (auto *RHSValue = dyn_cast_or_null(
-Env.getValue(*RHSNorm, SkipPast::Reference)))
+  if (auto *LHSValue =
+  dyn_cast_or_null(Env.getValue(LHS, SkipPast::Reference)))
+if (auto *RHSValue =
+dyn_cast_or_null(Env.getValue(RHS, SkipPast::Reference)))
   return Env.makeIff(*LHSValue, *RHSValue);
 
   return Env.makeAtomicBoolValue();
@@ -65,14 +50,10 @@
   : StmtToEnv(StmtToEnv), Env(Env) {}
 
   void VisitBinaryOperator(const BinaryOperator *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getLHS() != nullptr);
-const Expr *LHS = S->getLHS()->IgnoreParens();
+const Expr *LHS = S->getLHS();
 assert(LHS != nullptr);
 
-assert(S->getRHS() != nullptr);
-const Expr *RHS = S->getRHS()->IgnoreParens();
+const Expr *RHS = S->getRHS();
 assert(RHS != nullptr);
 
 switch (S->getOpcode()) {
@@ -155,7 +136,7 @@
   return;
 }
 
-InitExpr = ignoreExprWithCleanups(D.getInit());
+InitExpr = D.getInit();
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -476,8 +457,7 @@
   assert(S->getArg(0) != nullptr);
   // `__builtin_expect` returns by-value, so strip away any potential
   // references in the argument.
-  auto *ArgLoc = Env.getStorageLocation(
-  *S->getArg(0)->IgnoreParenImpCasts(), SkipPast::Reference);
+  auto *ArgLoc 

[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li 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 rG58abe36ae765: [clang][dataflow] Add flowConditionIsTautology 
function (authored by li.zhe.hua).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124943

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Fresh flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Literal `true` is always true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+  // Literal `false` is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
+
+  // We can't prove that an arbitrary bool A is always true...
+  auto  = Context.createAtomicBoolValue();
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC4, C1);
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
+
+  // ... but we can prove A || !A is true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(
+  FC5, Context.getOrCreateDisjunctionValue(
+   C1, Context.getOrCreateNegationValue(C1)));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -117,6 +117,19 @@
   return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
 }
 
+bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue ) 
{
+  // Returns true if and only if we cannot prove that the flow condition can
+  // ever be false.
+  llvm::DenseSet Constraints = {
+  (true),
+  (getBoolLiteralValue(false)),
+  (Token),
+  };
+  llvm::DenseSet VisitedTokens;
+  addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
+  return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
+}
+
 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
 AtomicBoolValue , llvm::DenseSet ,
 llvm::DenseSet ) const {
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -173,6 +173,10 @@
   /// identified by `Token` imply that `Val` is true.
   bool flowConditionImplies(AtomicBoolValue , BoolValue );
 
+  /// Returns true if and only if the constraints of the flow condition
+  /// identified by `Token` are always true.
+  bool flowConditionIsTautology(AtomicBoolValue );
+
 private:
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Fresh flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Literal `true` is always true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+  // Literal `false` is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
+  

[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked 4 inline comments as done.
li.zhe.hua added a comment.

Ah, good catch regarding the flow condition deps not being tracked. Thanks for 
the review!


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124943

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


[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 427183.
li.zhe.hua added a comment.

Address reviewer comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124943

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Fresh flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Literal `true` is always true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+  // Literal `false` is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
+
+  // We can't prove that an arbitrary bool A is always true...
+  auto  = Context.createAtomicBoolValue();
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC4, C1);
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
+
+  // ... but we can prove A || !A is true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(
+  FC5, Context.getOrCreateDisjunctionValue(
+   C1, Context.getOrCreateNegationValue(C1)));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -117,6 +117,19 @@
   return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
 }
 
+bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue ) 
{
+  // Returns true if and only if we cannot prove that the flow condition can
+  // ever be false.
+  llvm::DenseSet Constraints = {
+  (true),
+  (getBoolLiteralValue(false)),
+  (Token),
+  };
+  llvm::DenseSet VisitedTokens;
+  addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
+  return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
+}
+
 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
 AtomicBoolValue , llvm::DenseSet ,
 llvm::DenseSet ) const {
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -173,6 +173,10 @@
   /// identified by `Token` imply that `Val` is true.
   bool flowConditionImplies(AtomicBoolValue , BoolValue );
 
+  /// Returns true if and only if the constraints of the flow condition
+  /// identified by `Token` are always true.
+  bool flowConditionIsTautology(AtomicBoolValue );
+
 private:
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Fresh flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Literal `true` is always true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+  // Literal `false` is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
+
+  // We can't prove that an arbitrary bool A is always true...
+  auto  = 

[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

I'm playing around with a tool to take raw pointers and add a non-null 
annotation or convert them to references, for code that is assumed to be 
correct. So in this case, "interesting expression" is anything that would be UB 
if the pointer was null, e.g. dereferencing the pointer.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124943

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


[PATCH] D124965: [clang][dataflow] Centralize expression skipping logic

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added reviewers: sgatev, ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

A follow-up to 62b2a47 
 to 
centralize the logic that skips expressions
that the CFG does not emit. This allows client code to avoid
sprinkling this logic everywhere.

Add redirects in the transfer function to similarly skip such
expressions by forwarding the visit to the sub-expression.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D124965

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -46,7 +46,7 @@
   : CFCtx(CFCtx), BlockToState(BlockToState) {}
 
   const Environment *getEnvironment(const Stmt ) const override {
-auto BlockIT = CFCtx.getStmtToBlock().find();
+auto BlockIT = CFCtx.getStmtToBlock().find((S));
 assert(BlockIT != CFCtx.getStmtToBlock().end());
 const auto  = BlockToState[BlockIT->getSecond()->getBlockID()];
 assert(State.hasValue());
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
+auto *LHS = S->getLHS();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
+auto *Cond = S->getCond();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,27 +33,12 @@
 namespace clang {
 namespace dataflow {
 
-const Expr *ignoreExprWithCleanups(const Expr *E) {
-  if (auto *C = dyn_cast_or_null(E))
-return C->getSubExpr();
-  return E;
-}
-
 static BoolValue (const Expr , const Expr ,
   Environment ) {
-  // Equality of booleans involves implicit integral casts. Ignore these casts
-  // for now and focus on the values associated with the wrapped expressions.
-  // FIXME: Consider changing this once the framework offers better support for
-  // integral casts.
-  const Expr *LHSNorm = LHS.IgnoreCasts();
-  const Expr *RHSNorm = RHS.IgnoreCasts();
-  assert(LHSNorm != nullptr);
-  assert(RHSNorm != nullptr);
-
-  if (auto *LHSValue = dyn_cast_or_null(
-  Env.getValue(*LHSNorm, SkipPast::Reference)))
-if (auto *RHSValue = dyn_cast_or_null(
-Env.getValue(*RHSNorm, SkipPast::Reference)))
+  if (auto *LHSValue =
+  dyn_cast_or_null(Env.getValue(LHS, SkipPast::Reference)))
+if (auto *RHSValue =
+dyn_cast_or_null(Env.getValue(RHS, SkipPast::Reference)))
   return Env.makeIff(*LHSValue, *RHSValue);
 
   return Env.makeAtomicBoolValue();
@@ -65,14 +50,10 @@
   : StmtToEnv(StmtToEnv), Env(Env) {}
 
   void VisitBinaryOperator(const BinaryOperator *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getLHS() != nullptr);
-const Expr *LHS = S->getLHS()->IgnoreParens();
+const Expr *LHS = S->getLHS();
 assert(LHS != nullptr);
 
-assert(S->getRHS() != nullptr);
-const Expr *RHS = S->getRHS()->IgnoreParens();
+const Expr *RHS = S->getRHS();
 assert(RHS != nullptr);
 
 switch (S->getOpcode()) {
@@ -155,7 +136,7 @@
   return;
 }
 
-InitExpr = ignoreExprWithCleanups(D.getInit());
+  

[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added a comment.

This allows me to query when an expression is always encountered/executed. 
Alternatively, if I have a set of interesting expressions, I can note the flow 
condition at every expression, compute the disjunction of the set of flow 
conditions, and determine if execution will always encounter an interesting 
expression.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124943

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


[PATCH] D124943: [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added reviewers: ymandel, sgatev, xazax.hun.
Herald added subscribers: tschuett, steakhal, rnkovacs.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Provide a way for users to check if a flow condition is
unconditionally true.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D124943

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,43 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Default flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Negation of empty constraints is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2,
+ Context.getOrCreateNegationValue(FC1));
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC2));
+
+  // FC1 || !FC1 is always true.
+  EXPECT_TRUE(
+  Context.flowConditionIsTautology(Context.joinFlowConditions(FC1, FC2)));
+
+  // Literal `true` is always true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC3));
+
+  // Literal `false` is never true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC4, Context.getBoolLiteralValue(false));
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
+
+  // We can't prove that an arbitrary bool A is always true...
+  auto  = Context.createAtomicBoolValue();
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC5, C1);
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC5));
+
+  // ... but we can prove A || !A is true.
+  auto  = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(
+  FC6, Context.getOrCreateDisjunctionValue(
+   C1, Context.getOrCreateNegationValue(C1)));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC6));
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -117,6 +117,19 @@
   return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
 }
 
+bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue ) 
{
+  // Returns true if and only if we cannot prove that the flow condition can
+  // ever be false.
+  llvm::DenseSet Constraints = {
+  (true),
+  (getBoolLiteralValue(false)),
+  (Token),
+  };
+  llvm::DenseSet VisitedTokens;
+  addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
+  return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
+}
+
 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
 AtomicBoolValue , llvm::DenseSet ,
 llvm::DenseSet ) const {
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -173,6 +173,10 @@
   /// identified by `Token` imply that `Val` is true.
   bool flowConditionImplies(AtomicBoolValue , BoolValue );
 
+  /// Returns true if the constraints of the flow condition identified by
+  /// `Token` is always true.
+  bool flowConditionIsTautology(AtomicBoolValue );
+
 private:
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,43 @@
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Default flow condition with empty/no constraints is always true.
+  auto  = Context.makeFlowConditionToken();
+ 

[PATCH] D124807: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG62b2a47a9f15: [clang][dataflow] Only skip ExprWithCleanups 
when visiting terminators (authored by li.zhe.hua).

Changed prior to commit:
  https://reviews.llvm.org/D124807?vs=426987=427019#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,38 @@
   });
 }
 
+TEST_F(FlowConditionTest, PointerToBoolImplicitCast) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,7 +33,7 @@
 namespace clang {
 namespace dataflow {
 
-static const Expr *skipExprWithCleanups(const Expr *E) {
+const Expr *ignoreExprWithCleanups(const Expr *E) {
   if (auto *C = dyn_cast_or_null(E))
 return C->getSubExpr();
   return E;
@@ -155,9 +155,7 @@
   return;
 }
 
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+InitExpr = ignoreExprWithCleanups(D.getInit());
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -190,10 +188,7 @@
   }
 
   void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getSubExpr() != nullptr);
-const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
+const Expr *SubExpr = S->getSubExpr();
 assert(SubExpr != nullptr);
 
 switch (S->getCastKind()) {
@@ -252,10 +247,7 @@
   }
 
   void VisitUnaryOperator(const UnaryOperator *S) {
-// The CFG does not contain 

[PATCH] D124807: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426987.
li.zhe.hua added a comment.

Forgot to add a file... let's try this again...


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,7 +33,7 @@
 namespace clang {
 namespace dataflow {
 
-static const Expr *skipExprWithCleanups(const Expr *E) {
+const Expr *ignoreExprWithCleanups(const Expr *E) {
   if (auto *C = dyn_cast_or_null(E))
 return C->getSubExpr();
   return E;
@@ -155,9 +155,7 @@
   return;
 }
 
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+InitExpr = ignoreExprWithCleanups(D.getInit());
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -190,10 +188,7 @@
   }
 
   void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getSubExpr() != nullptr);
-const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
+const Expr *SubExpr = S->getSubExpr();
 assert(SubExpr != nullptr);
 
 switch (S->getCastKind()) {
@@ -252,10 +247,7 @@
   }
 
   void VisitUnaryOperator(const UnaryOperator *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however 

[PATCH] D124807: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h:230
+  ///
+  /// Requirements:
+  ///

sgatev wrote:
> Why add these as requirements instead of skipping past `ExprWithCleanups` 
> internally, as we currently do for parens? Should we add similar requirements 
> to `createStorageLocation`, `setStorageLocation`, and `getStorageLocation`? 
> Would that result in lots of client code sprinkled with `IgnoreParens` and 
> some form of `ignoreExprWithCleanups`?
Ah, I missed that we do this automatically for parens, in part because we 
//do// unnecessarily sprinkle `IgnoreParens` in a lot of places in 
`Transfer.cpp`.

That said, we can't avoid sprinkling them at least in some places for the time 
being. Fixing this greatly expands beyond the scope of fixing a `cast` assert 
firing. (That is still, aspirationally, the goal of this patch.)

Let me remove the `ParenExpr` requirement (that's incorrect), leave the 
`ExprWithCleanups` requirement (with `FIXME`), and leave the holistic cleanup 
for a separate patch.



Comment at: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp:429-430
 Value *Environment::getValue(const Expr , SkipPast SP) const {
+  assert(!isa());
+  assert(!isa());
   auto *Loc = getStorageLocation(E, SP);

sgatev wrote:
> 
Acknowledged. Obviated by removing the `ParenExpr` assert.



Comment at: 
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp:224
+  if (auto *OptionalVal = cast_or_null(State.Env.getValue(
+  *ObjectExpr->IgnoreParens(), SkipPast::ReferenceThenPointer))) {
 auto *HasValueVal = getHasValue(OptionalVal);

sgatev wrote:
> Do we have a test that fails if `IgnoreParens` is missing? If not, let's add 
> a test or extend one of the existing. Perhaps 
> https://github.com/llvm/llvm-project/blob/main/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp#L1286
I missed that we ignore parens automatically in `getValue`. Removing this.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

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


[PATCH] D124807: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-04 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426985.
li.zhe.hua marked 7 inline comments as done.
li.zhe.hua added a comment.

Add `ExprWithCleanups` requirement to `createStorageLocation`, 
`setStorageLocation`, and `getStorageLocation`.
Remove `ParenExpr` requirement from `getValue`.
Remove unnecessary calls to `IgnoreParens`.
Address misc. comments; apply suggestions.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,7 +33,7 @@
 namespace clang {
 namespace dataflow {
 
-static const Expr *skipExprWithCleanups(const Expr *E) {
+const Expr *ignoreExprWithCleanups(const Expr *E) {
   if (auto *C = dyn_cast_or_null(E))
 return C->getSubExpr();
   return E;
@@ -155,9 +155,7 @@
   return;
 }
 
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+InitExpr = ignoreExprWithCleanups(D.getInit());
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -190,10 +188,7 @@
   }
 
   void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
-// The CFG does not contain `ParenExpr` as top-level statements in basic
-// blocks, however sub-expressions can still be of that type.
-assert(S->getSubExpr() != nullptr);
-const Expr *SubExpr = 

[PATCH] D124807: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426860.
li.zhe.hua edited the summary of this revision.
li.zhe.hua added a comment.

Expand `ignoreExprWithCleanups` comment with more context.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,7 +33,7 @@
 namespace clang {
 namespace dataflow {
 
-static const Expr *skipExprWithCleanups(const Expr *E) {
+const Expr *ignoreExprWithCleanups(const Expr *E) {
   if (auto *C = dyn_cast_or_null(E))
 return C->getSubExpr();
   return E;
@@ -157,7 +157,7 @@
 
 // The CFG does not contain `ParenExpr` as top-level statements in basic
 // blocks, however sub-expressions can still be of that type.
-InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+InitExpr = ignoreExprWithCleanups(D.getInit()->IgnoreParens());
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -605,6 +605,7 @@
 
 void transfer(const StmtToEnvMap , const Stmt , Environment ) {
   assert(!isa());
+  assert(!isa());
   TransferVisitor(StmtToEnv, Env).Visit();
 }
 
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -220,8 +220,8 @@
 
 void 

[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked 4 inline comments as done.
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:78
+/// and integers in the framework.
+static const Expr *ignoreParenImpCastsExceptToBool(const Expr *E) {
+  const Expr *LastE = nullptr;

sgatev wrote:
> xazax.hun wrote:
> > li.zhe.hua wrote:
> > > sgatev wrote:
> > > > I don't recall why we need to ignore implicit casts here. Can't we 
> > > > ignore parens and rely on the built-in transfer function, possibly 
> > > > adding handling of some missing casts there? 
> > > > https://github.com/llvm/llvm-project/blob/main/clang/lib/Analysis/FlowSensitive/Transfer.cpp#L192
> > > If we only ignore parens, a test in the optional checker tests begins to 
> > > fail, specifically `UncheckedOptionalAccessTest.ValueOrComparison`. The 
> > > missing "cast" is an `ExprWithCleanups`. I didn't know how to deal with 
> > > that, so this patch just working around the assert.
> > In general, I prefer to handle as much as possible with transfer functions 
> > and skip as little as possible in the AST. We might skip `ExprWithCleanups` 
> > nodes today, but we will need them tomorrow to properly model where certain 
> > destructors are being invoked. 
> We already have `skipExprWithCleanups` [1]. I wonder if it makes sense to 
> replace that with a simple transfer function like the one for the `CK_NoOp` 
> implicit cast. Would that solve the problem and remove the need for 
> `ignoreParenImpCastsExceptToBool`? In the future we might replace that 
> transfer function with a proper one, as Gábor suggested.
> 
> [1] 
> https://github.com/llvm/llvm-project/blob/main/clang/lib/Analysis/FlowSensitive/Transfer.cpp#L36
I sat down with @ymandel to get a better understanding of what was happening 
here, which was really helpful in understanding why my random flailing was 
making tests pass, and what I believe to be a better way forward.

So, the CFG does not emit `ParenExpr` nodes. As such, the transfer function 
never sees them, and so when we do any kind of manual traversal (e.g. to look 
at a sub-expression), we use `ignoreParens()` because they effectively don't 
exist. The same thing happens with `ExprWithCleanups`. The CFG doesn't appear 
to emit them explicitly, and so we should similarly avoid them when manually 
traversing the AST.

Note: Their position in the AST is slightly more constrained, likely as the 
immediate children of `*Stmt` nodes. This means we don't need to sprinkle these 
skips as widely as `ignoreParens()`.

In terms of why adding `VisitFullExpr` "fixed" the failing test: 
`extendFlowCondition()` [[ 
https://github.com/llvm/llvm-project/blob/ed1b32791dbb4c02cd761742a63cdf1e9d644ae6/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp#L108
 | manually calls ]] `transfer` on the provided `Cond` expression, which is a 
`ExprWithCleanups` if the caller uses `ignoreParens()` instead of 
`ignoreParenImpCasts()`.

---

@xazax.hun:

> In general, I prefer to handle as much as possible with transfer functions 
> and skip as little as possible in the AST. We might skip `ExprWithCleanups` 
> nodes today, but we will need them tomorrow to properly model where certain 
> destructors are being invoked. 

The CFG already emits calls to destructors as a result of `ExprWithCleanups` 
([[ https://godbolt.org/z/fo846dcEa | godbolt ]]), so skipping them will not 
cause us to miss calls to destructors.

---

@sgatev:

> We already have `skipExprWithCleanups` [1]. I wonder if it makes sense to 
> replace that with a simple transfer function like the one for the `CK_NoOp` 
> implicit cast. Would that solve the problem and remove the need for 
> `ignoreParenImpCastsExceptToBool`? In the future we might replace that 
> transfer function with a proper one, as Gábor suggested.
> 
> [1] 
> https://github.com/llvm/llvm-project/blob/main/clang/lib/Analysis/FlowSensitive/Transfer.cpp#L36

I believe that `ParenExpr` and `ExprWithCleanups` are categorically the same 
within the framework (roughly, nodes that are not emitted by the CFG) and so we 
should handle them in the same way. The strategy right now for `ParenExpr` is 
to skip them, so I am inclined to do so as well with `ExprWithCleanups` for 
now. (I'm not necessarily opposed to having `ParenExpr` and `ExprWithCleanups` 
nodes being handled in the transfer function, but there's a question of how to 
get them in there if the CFG isn't going to emit them.)

That would mean //more// usage of `skipExprWithCleanups`, and is reflected in 
this most recent revision.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

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


[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426823.
li.zhe.hua added a comment.

Switch from implementing in the trasfer function to skipping `ExprWithCleanups`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Transfer.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -33,7 +33,7 @@
 namespace clang {
 namespace dataflow {
 
-static const Expr *skipExprWithCleanups(const Expr *E) {
+const Expr *ignoreExprWithCleanups(const Expr *E) {
   if (auto *C = dyn_cast_or_null(E))
 return C->getSubExpr();
   return E;
@@ -157,7 +157,7 @@
 
 // The CFG does not contain `ParenExpr` as top-level statements in basic
 // blocks, however sub-expressions can still be of that type.
-InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+InitExpr = ignoreExprWithCleanups(D.getInit()->IgnoreParens());
 assert(InitExpr != nullptr);
 
 if (D.getType()->isReferenceType()) {
@@ -605,6 +605,7 @@
 
 void transfer(const StmtToEnvMap , const Stmt , Environment ) {
   assert(!isa());
+  assert(!isa());
   TransferVisitor(StmtToEnv, Env).Visit();
 }
 
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -220,8 +220,8 @@
 
 void transferUnwrapCall(const Expr 

[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426783.
li.zhe.hua added a comment.

Handle FullExprs in the transfer function.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -77,26 +77,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = S->getCond()->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = S->getCond()->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = S->getLHS()->IgnoreParens();
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = S->getCond()->IgnoreParens();
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -251,6 +251,18 @@
 }
   }
 
+  void VisitFullExpr(const FullExpr *S) {
+assert(S->getSubExpr() != nullptr);
+const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
+assert(SubExpr != nullptr);
+
+auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
+if (SubExprLoc == nullptr)
+  return;
+
+Env.setStorageLocation(*S, *SubExprLoc);
+  }
+
   void VisitUnaryOperator(const UnaryOperator *S) {
 // The CFG does not contain `ParenExpr` as top-level statements in basic
 // blocks, however sub-expressions can still be of that type.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp:78
+/// and integers in the framework.
+static const Expr *ignoreParenImpCastsExceptToBool(const Expr *E) {
+  const Expr *LastE = nullptr;

sgatev wrote:
> I don't recall why we need to ignore implicit casts here. Can't we ignore 
> parens and rely on the built-in transfer function, possibly adding handling 
> of some missing casts there? 
> https://github.com/llvm/llvm-project/blob/main/clang/lib/Analysis/FlowSensitive/Transfer.cpp#L192
If we only ignore parens, a test in the optional checker tests begins to fail, 
specifically `UncheckedOptionalAccessTest.ValueOrComparison`. The missing 
"cast" is an `ExprWithCleanups`. I didn't know how to deal with that, so this 
patch just working around the assert.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

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


[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-03 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426711.
li.zhe.hua added a comment.

Update test to treat the PointerToBool conversion as an opaque boolean 
expression, and test it as such.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,39 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  bool Foo = false;
+  if (Ptr) {
+Foo = true;
+/*[[p1]]*/
+  }
+
+  (void)0;
+  /*[[p2]]*/
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ASSERT_THAT(FooDecl, NotNull());
+
+const Environment  = Results[1].second.Env;
+auto  =
+*cast(Env1.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
+
+const Environment  = Results[0].second.Env;
+auto  =
+*cast(Env2.getValue(*FooDecl, SkipPast::Reference));
+EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -17,6 +17,7 @@
 #include 
 
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/IgnoreExpr.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
@@ -68,6 +69,28 @@
   return BlockPos - Pred.succ_begin();
 }
 
+/// Skips past any parentheses and implicit casts for `E`, except if they would
+/// perform a type conversion to bool.
+///
+/// FIXME: Its hard to tell if this is a principled solution or workaround to an
+/// issue. Re-evaluate the necessity for this function after we model pointers
+/// and integers in the framework.
+static const Expr *ignoreParenImpCastsExceptToBool(const Expr *E) {
+  const Expr *LastE = nullptr;
+  while (E != LastE) {
+LastE = E;
+E = IgnoreParensSingleStep(const_cast(E));
+
+bool WasBool = E->getType()->isBooleanType();
+auto *Unwrapped = IgnoreImplicitCastsExtraSingleStep(const_cast(E));
+if (WasBool && !Unwrapped->getType()->isBooleanType())
+  return E;
+
+E = Unwrapped;
+  }
+  return E;
+}
+
 /// Extends the flow condition of an environment based on a terminator
 /// statement.
 class TerminatorVisitor : public ConstStmtVisitor {
@@ -77,26 +100,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreParenImpCastsExceptToBool(S->getLHS());
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-02 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 426528.
li.zhe.hua added a comment.

Remove else after return


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124807

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,26 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  if (Ptr) {
+(void)0;
+/*[[p1]]*/
+  } else {
+(void)1;
+/*[[p2]]*/
+  }
+}
+  )";
+  runDataflow(Code,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -17,6 +17,7 @@
 #include 
 
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/IgnoreExpr.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
@@ -68,6 +69,28 @@
   return BlockPos - Pred.succ_begin();
 }
 
+/// Skips past any parentheses and implicit casts for `E`, except if they would
+/// perform a type conversion to bool.
+///
+/// FIXME: Its hard to tell if this is a principled solution or workaround to an
+/// issue. Re-evaluate the necessity for this function after we model pointers
+/// and integers in the framework.
+static const Expr *ignoreParenImpCastsExceptToBool(const Expr *E) {
+  const Expr *LastE = nullptr;
+  while (E != LastE) {
+LastE = E;
+E = IgnoreParensSingleStep(const_cast(E));
+
+bool WasBool = E->getType()->isBooleanType();
+auto *Unwrapped = IgnoreImplicitCastsExtraSingleStep(const_cast(E));
+if (WasBool && !Unwrapped->getType()->isBooleanType())
+  return E;
+
+E = Unwrapped;
+  }
+  return E;
+}
+
 /// Extends the flow condition of an environment based on a terminator
 /// statement.
 class TerminatorVisitor : public ConstStmtVisitor {
@@ -77,26 +100,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreParenImpCastsExceptToBool(S->getLHS());
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D124807: [clang][dataflow] Avoid assert for invalid cast to BoolValue

2022-05-02 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua created this revision.
li.zhe.hua added a reviewer: ymandel.
Herald added subscribers: tschuett, steakhal.
Herald added a project: All.
li.zhe.hua requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

`IgnoreParenImpCasts` will remove implicit casts to bool
(e.g. `PointerToBoolean`), such that the resulting expression may not
be of the `bool` type. The `cast_or_null` in
`extendFlowCondition` will then trigger an assert.

We work around this by unwrapping implicit casts unless we encounter
one that changes the type to `bool`. It's hard to tell if this is a
principled approach or a workaround. We should re-evaluate the need
for this once we have better support for pointers and/or integers.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D124807

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -1152,4 +1152,26 @@
   });
 }
 
+// FIXME: Remove this test once it provides no additional test coverage.
+TEST_F(FlowConditionTest, DoesNotAssertForImplicitCastToBool) {
+  std::string Code = R"(
+void target(int *Ptr) {
+  if (Ptr) {
+(void)0;
+/*[[p1]]*/
+  } else {
+(void)1;
+/*[[p2]]*/
+  }
+}
+  )";
+  runDataflow(Code,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext ) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+  });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -17,6 +17,7 @@
 #include 
 
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/IgnoreExpr.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
@@ -68,6 +69,28 @@
   return BlockPos - Pred.succ_begin();
 }
 
+/// Skips past any parentheses and implicit casts for `E`, except if they would
+/// perform a type conversion to bool.
+///
+/// FIXME: Its hard to tell if this is a principled solution or workaround to an
+/// issue. Re-evaluate the necessity for this function after we model pointers
+/// and integers in the framework.
+static const Expr *ignoreParenImpCastsExceptToBool(const Expr *E) {
+  const Expr *LastE = nullptr;
+  while (E != LastE) {
+LastE = E;
+E = IgnoreParensSingleStep(const_cast(E));
+
+bool WasBool = E->getType()->isBooleanType();
+auto *Unwrapped = IgnoreImplicitCastsExtraSingleStep(const_cast(E));
+if (WasBool && !Unwrapped->getType()->isBooleanType())
+  return E;
+else
+  E = Unwrapped;
+  }
+  return E;
+}
+
 /// Extends the flow condition of an environment based on a terminator
 /// statement.
 class TerminatorVisitor : public ConstStmtVisitor {
@@ -77,26 +100,26 @@
   : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
 
   void VisitIfStmt(const IfStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
 
   void VisitBinaryOperator(const BinaryOperator *S) {
 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
-auto *LHS = S->getLHS()->IgnoreParenImpCasts();
+auto *LHS = ignoreParenImpCastsExceptToBool(S->getLHS());
 assert(LHS != nullptr);
 extendFlowCondition(*LHS);
   }
 
   void VisitConditionalOperator(const ConditionalOperator *S) {
-auto *Cond = S->getCond()->IgnoreParenImpCasts();
+auto *Cond = ignoreParenImpCastsExceptToBool(S->getCond());
 assert(Cond != nullptr);
 extendFlowCondition(*Cond);
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D122499: [libTooling] Support TransformerResult in consumer callbacks

2022-03-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added inline comments.



Comment at: clang/include/clang/Tooling/Transformer/Transformer.h:126-128
+template 
+std::enable_if_t::value, void>
+assertMetadataSet(const transformer::RewriteRuleWith &) {}

ymandel wrote:
> Why won't a simple specialization work?
Yup, totally. Done.

I guess I see SFINAE so much more often these days I just reach for it first.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D122499

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


[PATCH] D122499: [libTooling] Support TransformerResult in consumer callbacks

2022-03-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 418316.
li.zhe.hua added a comment.

Fix for comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D122499

Files:
  clang/include/clang/Tooling/Transformer/Transformer.h
  clang/lib/Tooling/Transformer/Transformer.cpp

Index: clang/lib/Tooling/Transformer/Transformer.cpp
===
--- clang/lib/Tooling/Transformer/Transformer.cpp
+++ clang/lib/Tooling/Transformer/Transformer.cpp
@@ -66,26 +66,6 @@
   return Changes;
 }
 
-void NoMetadataImpl::onMatchImpl(const MatchFinder::MatchResult ) {
-  size_t I = transformer::detail::findSelectedCase(Result, Rule);
-  auto Transformations = Rule.Cases[I].Edits(Result);
-  if (!Transformations) {
-Consumer(Transformations.takeError());
-return;
-  }
-
-  if (Transformations->empty())
-return;
-
-  auto Changes = convertToAtomicChanges(*Transformations, Result);
-  if (!Changes) {
-Consumer(Changes.takeError());
-return;
-  }
-
-  Consumer(llvm::MutableArrayRef(*Changes));
-}
-
 } // namespace detail
 
 void Transformer::registerMatchers(MatchFinder *MatchFinder) {
Index: clang/include/clang/Tooling/Transformer/Transformer.h
===
--- clang/include/clang/Tooling/Transformer/Transformer.h
+++ clang/include/clang/Tooling/Transformer/Transformer.h
@@ -21,7 +21,7 @@
 
 namespace detail {
 /// Implementation details of \c Transformer with type erasure around
-/// \c RewriteRule and \c RewriteRule as well as the corresponding consumers.
+/// \c RewriteRule as well as the corresponding consumers.
 class TransformerImpl {
 public:
   virtual ~TransformerImpl() = default;
@@ -43,33 +43,6 @@
   onMatchImpl(const ast_matchers::MatchFinder::MatchResult ) = 0;
 };
 
-/// Implementation for when no metadata is generated as a part of the
-/// \c RewriteRule.
-class NoMetadataImpl final : public TransformerImpl {
-  transformer::RewriteRule Rule;
-  std::function>)> Consumer;
-
-public:
-  explicit NoMetadataImpl(
-  transformer::RewriteRule R,
-  std::function>)>
-  Consumer)
-  : Rule(std::move(R)), Consumer(std::move(Consumer)) {
-assert(llvm::all_of(Rule.Cases,
-[](const transformer::RewriteRule::Case ) {
-  return Case.Edits;
-}) &&
-   "edit generator must be provided for each rule");
-  }
-
-private:
-  void onMatchImpl(const ast_matchers::MatchFinder::MatchResult ) final;
-  std::vector
-  buildMatchers() const final {
-return transformer::detail::buildMatchers(Rule);
-  }
-};
-
 // FIXME: Use std::type_identity or backport when available.
 template  struct type_identity {
   using type = T;
@@ -81,8 +54,6 @@
   T Metadata;
 };
 
-// Specialization provided only to avoid SFINAE on the Transformer
-// constructor; not intended for use.
 template <> struct TransformerResult {
   llvm::MutableArrayRef Changes;
 };
@@ -107,8 +78,14 @@
   /// other.
   explicit Transformer(transformer::RewriteRuleWith Rule,
ChangeSetConsumer Consumer)
-  : Impl(std::make_unique(std::move(Rule),
-  std::move(Consumer))) {}
+  : Transformer(std::move(Rule),
+[Consumer = std::move(Consumer)](
+llvm::Expected> Result) {
+  if (Result)
+Consumer(Result->Changes);
+  else
+Consumer(Result.takeError());
+}) {}
 
   /// \param Consumer receives all rewrites and the associated metadata for a
   /// single match, or an error. Will always be called for each match, even if
@@ -135,6 +112,44 @@
 };
 
 namespace detail {
+/// Asserts that all \c Metadata for the \c Rule is set.
+/// FIXME: Use constexpr-if in C++17.
+/// @{
+template 
+void assertMetadataSet(const transformer::RewriteRuleWith ) {
+  assert(llvm::all_of(Rule.Metadata,
+  [](const typename transformer::Generator )
+  -> bool { return !!Metadata; }) &&
+ "metadata generator must be provided for each rule");
+}
+template <>
+inline void assertMetadataSet(const transformer::RewriteRuleWith &) {}
+/// @}
+
+/// Runs the metadata generator on \c Rule and stuffs it into \c Result.
+/// FIXME: Use constexpr-if in C++17.
+/// @{
+template 
+llvm::Error
+populateMetadata(const transformer::RewriteRuleWith ,
+ size_t SelectedCase,
+ const ast_matchers::MatchFinder::MatchResult ,
+ TransformerResult ) {
+  auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
+  if (!Metadata)
+return Metadata.takeError();
+  Result.Metadata = std::move(*Metadata);
+  return llvm::Error::success();
+}
+template <>
+inline llvm::Error
+populateMetadata(const 

[PATCH] D122499: [libTooling] Support TransformerResult in consumer callbacks

2022-03-25 Thread Eric Li via Phabricator via cfe-commits
li.zhe.hua updated this revision to Diff 418295.
li.zhe.hua added a comment.

Fix comment.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D122499

Files:
  clang/include/clang/Tooling/Transformer/Transformer.h
  clang/lib/Tooling/Transformer/Transformer.cpp

Index: clang/lib/Tooling/Transformer/Transformer.cpp
===
--- clang/lib/Tooling/Transformer/Transformer.cpp
+++ clang/lib/Tooling/Transformer/Transformer.cpp
@@ -66,26 +66,6 @@
   return Changes;
 }
 
-void NoMetadataImpl::onMatchImpl(const MatchFinder::MatchResult ) {
-  size_t I = transformer::detail::findSelectedCase(Result, Rule);
-  auto Transformations = Rule.Cases[I].Edits(Result);
-  if (!Transformations) {
-Consumer(Transformations.takeError());
-return;
-  }
-
-  if (Transformations->empty())
-return;
-
-  auto Changes = convertToAtomicChanges(*Transformations, Result);
-  if (!Changes) {
-Consumer(Changes.takeError());
-return;
-  }
-
-  Consumer(llvm::MutableArrayRef(*Changes));
-}
-
 } // namespace detail
 
 void Transformer::registerMatchers(MatchFinder *MatchFinder) {
Index: clang/include/clang/Tooling/Transformer/Transformer.h
===
--- clang/include/clang/Tooling/Transformer/Transformer.h
+++ clang/include/clang/Tooling/Transformer/Transformer.h
@@ -21,7 +21,7 @@
 
 namespace detail {
 /// Implementation details of \c Transformer with type erasure around
-/// \c RewriteRule and \c RewriteRule as well as the corresponding consumers.
+/// \c RewriteRule as well as the corresponding consumers.
 class TransformerImpl {
 public:
   virtual ~TransformerImpl() = default;
@@ -43,33 +43,6 @@
   onMatchImpl(const ast_matchers::MatchFinder::MatchResult ) = 0;
 };
 
-/// Implementation for when no metadata is generated as a part of the
-/// \c RewriteRule.
-class NoMetadataImpl final : public TransformerImpl {
-  transformer::RewriteRule Rule;
-  std::function>)> Consumer;
-
-public:
-  explicit NoMetadataImpl(
-  transformer::RewriteRule R,
-  std::function>)>
-  Consumer)
-  : Rule(std::move(R)), Consumer(std::move(Consumer)) {
-assert(llvm::all_of(Rule.Cases,
-[](const transformer::RewriteRule::Case ) {
-  return Case.Edits;
-}) &&
-   "edit generator must be provided for each rule");
-  }
-
-private:
-  void onMatchImpl(const ast_matchers::MatchFinder::MatchResult ) final;
-  std::vector
-  buildMatchers() const final {
-return transformer::detail::buildMatchers(Rule);
-  }
-};
-
 // FIXME: Use std::type_identity or backport when available.
 template  struct type_identity {
   using type = T;
@@ -81,8 +54,6 @@
   T Metadata;
 };
 
-// Specialization provided only to avoid SFINAE on the Transformer
-// constructor; not intended for use.
 template <> struct TransformerResult {
   llvm::MutableArrayRef Changes;
 };
@@ -107,8 +78,14 @@
   /// other.
   explicit Transformer(transformer::RewriteRuleWith Rule,
ChangeSetConsumer Consumer)
-  : Impl(std::make_unique(std::move(Rule),
-  std::move(Consumer))) {}
+  : Transformer(std::move(Rule),
+[Consumer = std::move(Consumer)](
+llvm::Expected> Result) {
+  if (Result)
+Consumer(Result->Changes);
+  else
+Consumer(Result.takeError());
+}) {}
 
   /// \param Consumer receives all rewrites and the associated metadata for a
   /// single match, or an error. Will always be called for each match, even if
@@ -135,6 +112,46 @@
 };
 
 namespace detail {
+/// Asserts that all \c Metadata for the \c Rule is set.
+/// FIXME: Use constexpr-if in C++17.
+/// @{
+template 
+std::enable_if_t::value, void>
+assertMetadataSet(const transformer::RewriteRuleWith ) {
+  assert(llvm::all_of(Rule.Metadata,
+  [](const typename transformer::Generator )
+  -> bool { return !!Metadata; }) &&
+ "metadata generator must be provided for each rule");
+}
+template 
+std::enable_if_t::value, void>
+assertMetadataSet(const transformer::RewriteRuleWith &) {}
+/// @}
+
+/// Runs the metadata generator on \c Rule and stuffs it into \c Result.
+/// FIXME: Use constexpr-if in C++17.
+/// @{
+template 
+std::enable_if_t::value, llvm::Error>
+populateMetadata(const transformer::RewriteRuleWith ,
+ size_t SelectedCase,
+ const ast_matchers::MatchFinder::MatchResult ,
+ TransformerResult ) {
+  auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
+  if (!Metadata)
+return Metadata.takeError();
+  Result.Metadata = std::move(*Metadata);
+  return llvm::Error::success();

  1   2   >