[clang] f10d271 - [clang][dataflow] Handle null pointers of type std::nullptr_t

2022-07-05 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-07-05T13:49:26Z
New Revision: f10d271ae27f35cd56535ff7e832a358732c8fcd

URL: 
https://github.com/llvm/llvm-project/commit/f10d271ae27f35cd56535ff7e832a358732c8fcd
DIFF: 
https://github.com/llvm/llvm-project/commit/f10d271ae27f35cd56535ff7e832a358732c8fcd.diff

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

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

Differential Revision: https://reviews.llvm.org/D129097

Added: 


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

Removed: 




diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index c1100d8474aa4..d87b9cc37b996 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -155,6 +155,7 @@ class DataflowAnalysisContext {
 
   /// 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 &getOrCreateNullPointerValue(QualType PointeeType);
 
   /// Returns a symbolic boolean value that models a boolean literal equal to
@@ -251,6 +252,17 @@ class DataflowAnalysisContext {
   bool equivalentBoolValues(BoolValue &Val1, BoolValue &Val2);
 
 private:
+  struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo {
+static QualType getEmptyKey() {
+  // Allow a NULL `QualType` by using a 
diff erent 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 @@ class DataflowAnalysisContext {
   // required to initialize the `PointeeLoc` field in `PointerValue`. Consider
   // creating a type-independent `NullPointerValue` without a `PointeeLoc`
   // field.
-  llvm::DenseMap NullPointerVals;
+  llvm::DenseMap
+  NullPointerVals;
 
   AtomicBoolValue &TrueVal;
   AtomicBoolValue &FalseVal;

diff  --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index e08fc71c51dc7..cd87e87a6acab 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -24,8 +24,8 @@ namespace dataflow {
 
 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 @@ DataflowAnalysisContext::getStableStorageLocation(const Expr 
&E) {
 
 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 &PointeeLoc = getStableStorageLocation(CanonicalPointeeType);

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 04710b1795ef4..c4a42061edd90 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2213,12 +2213,14 @@ TEST(TransferTest, IntegralToBooleanCastFromBool) {
 
 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 @@ TEST(TransferTest, NullToPointerCast) {
 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
 ASSERT_THAT(BazDecl, NotNull());
 

[clang] a29fd4d - [libTooling] Fix indentation. NFC.

2022-03-28 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-03-28T18:34:45Z
New Revision: a29fd4d4dad3f4c62dbcb31bc097535620455208

URL: 
https://github.com/llvm/llvm-project/commit/a29fd4d4dad3f4c62dbcb31bc097535620455208
DIFF: 
https://github.com/llvm/llvm-project/commit/a29fd4d4dad3f4c62dbcb31bc097535620455208.diff

LOG: [libTooling] Fix indentation. NFC.

Added: 


Modified: 
clang/unittests/Tooling/TransformerTest.cpp

Removed: 




diff  --git a/clang/unittests/Tooling/TransformerTest.cpp 
b/clang/unittests/Tooling/TransformerTest.cpp
index 75ba9b919bbf9..9bc372b97d807 100644
--- a/clang/unittests/Tooling/TransformerTest.cpp
+++ b/clang/unittests/Tooling/TransformerTest.cpp
@@ -82,7 +82,7 @@ static std::string format(StringRef Code) {
 }
 
 static void compareSnippets(StringRef Expected,
- const llvm::Optional &MaybeActual) {
+const llvm::Optional &MaybeActual) {
   ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
   auto Actual = *MaybeActual;
   std::string HL = "#include \"header.h\"\n";
@@ -1265,31 +1265,29 @@ void testIt()
   auto RewriteOutput =
   CodePrefix + RangeLoop + LoopBody + RangeLoop + LoopBody + CodeSuffix;
 
-auto MatchedLoop = forStmt(
-has(declStmt(
-hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0
-  .bind("loopVar",
-has(binaryOperator(hasOperatorName("!="),
-   hasLHS(ignoringImplicit(declRefExpr(
-   to(varDecl(equalsBoundNode("loopVar")),
-   hasRHS(expr().bind("upperBoundExpr",
-has(unaryOperator(hasOperatorName("++"),
-  hasUnaryOperand(declRefExpr(
-  to(varDecl(equalsBoundNode("loopVar"))
-.bind("incrementOp")));
+  auto MatchedLoop = forStmt(
+  has(declStmt(hasSingleDecl(
+  
varDecl(hasInitializer(integerLiteral(equals(0.bind("loopVar",
+  has(binaryOperator(hasOperatorName("!="),
+ hasLHS(ignoringImplicit(declRefExpr(
+ to(varDecl(equalsBoundNode("loopVar")),
+ hasRHS(expr().bind("upperBoundExpr",
+  has(unaryOperator(hasOperatorName("++"),
+hasUnaryOperand(declRefExpr(
+to(varDecl(equalsBoundNode("loopVar"))
+  .bind("incrementOp")));
 
-auto RewriteRule =
-changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
- cat("auto ", name("loopVar"), " : boost::irange(",
- node("upperBoundExpr"), ")"));
-
-testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),
-  RewriteRule),
- RewriteInput, RewriteOutput);
+  auto RewriteRule =
+  changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
+   cat("auto ", name("loopVar"), " : boost::irange(",
+   node("upperBoundExpr"), ")"));
 
-testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),
-RewriteInput);
+  testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),
+RewriteRule),
+   RewriteInput, RewriteOutput);
 
+  testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),
+  RewriteInput);
 }
 
 TEST_F(TransformerTest, ImplicitNodes_ForStmt2) {
@@ -1338,31 +1336,29 @@ void testIt()
 
   auto RewriteOutput =
   CodePrefix + RangeLoop + LoopBody + RangeLoop + LoopBody + CodeSuffix;
-auto MatchedLoop = forStmt(
-hasLoopInit(declStmt(
-hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0
-  .bind("loopVar",
-hasCondition(binaryOperator(hasOperatorName("!="),
-hasLHS(ignoringImplicit(declRefExpr(to(
-
varDecl(equalsBoundNode("loopVar")),
-hasRHS(expr().bind("upperBoundExpr",
-hasIncrement(unaryOperator(hasOperatorName("++"),
-   hasUnaryOperand(declRefExpr(to(
-   varDecl(equalsBoundNode("loopVar"))
- .bind("incrementOp")));
-
-auto RewriteRule =
-changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
- cat("auto ", name("loopVar"), " : boost::irange(",
- node("upperBoundExpr"), ")"));
-
-testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),
-  RewriteRule),
- RewriteInput, RewriteOutput);
-
-testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),
-RewriteInput);
+  a

[clang] 5e28923 - [clang][dataflow][NFC] Remove last use of deprecated ctor

2022-07-27 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-07-27T14:23:35-04:00
New Revision: 5e28923e332f2e738d17d35f1978df3391ee10af

URL: 
https://github.com/llvm/llvm-project/commit/5e28923e332f2e738d17d35f1978df3391ee10af
DIFF: 
https://github.com/llvm/llvm-project/commit/5e28923e332f2e738d17d35f1978df3391ee10af.diff

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

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

Differential Revision: https://reviews.llvm.org/D130653

Added: 


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

Removed: 




diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
index ef8f7a51496c9..a8785c554eb2f 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -66,7 +66,8 @@ class DataflowAnalysis : public TypeErasedDataflowAnalysis {
 
   /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
   explicit DataflowAnalysis(ASTContext &Context, bool ApplyBuiltinTransfer)
-  : TypeErasedDataflowAnalysis(ApplyBuiltinTransfer), Context(Context) {}
+  : DataflowAnalysis(Context, DataflowAnalysisOptions{ApplyBuiltinTransfer,
+  TransferOptions{}}) 
{}
 
   explicit DataflowAnalysis(ASTContext &Context,
 DataflowAnalysisOptions Options)

diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h 
b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
index 92700f164e7bd..3a108402ab159 100644
--- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -59,10 +59,6 @@ class TypeErasedDataflowAnalysis : public 
Environment::ValueModel {
 public:
   TypeErasedDataflowAnalysis() : Options({}) {}
 
-  /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead.
-  TypeErasedDataflowAnalysis(bool ApplyBuiltinTransfer)
-  : Options({ApplyBuiltinTransfer, TransferOptions{}}) {}
-
   TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
   : Options(Options) {}
 



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


[clang] 54d24ea - [clang][dataflow][NFC] Fix outdated comment on getStableStorageLocation

2022-08-04 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-08-04T11:15:14-04:00
New Revision: 54d24eae98726e37867bc3a683cd58af6ec128df

URL: 
https://github.com/llvm/llvm-project/commit/54d24eae98726e37867bc3a683cd58af6ec128df
DIFF: 
https://github.com/llvm/llvm-project/commit/54d24eae98726e37867bc3a683cd58af6ec128df.diff

LOG: [clang][dataflow][NFC] Fix outdated comment on getStableStorageLocation

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.

Differential Revision: https://reviews.llvm.org/D131109

Added: 


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

Removed: 




diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index a31e08fd16c48..ab60d313f242c 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -93,9 +93,7 @@ class DataflowAnalysisContext {
 
   /// 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 &createStorageLocation(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


[clang] 5659908 - [clang][dataflow][NFC] Resize vector directly with ctor

2022-08-04 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-08-04T13:12:37-04:00
New Revision: 5659908f4c6d32d3f7d1ebbe014fbd9419cdfb9c

URL: 
https://github.com/llvm/llvm-project/commit/5659908f4c6d32d3f7d1ebbe014fbd9419cdfb9c
DIFF: 
https://github.com/llvm/llvm-project/commit/5659908f4c6d32d3f7d1ebbe014fbd9419cdfb9c.diff

LOG: [clang][dataflow][NFC] Resize vector directly with ctor

Differential Revision: https://reviews.llvm.org/D131177

Added: 


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

Removed: 




diff  --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp 
b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index 32fb48470b14..fe650200e4e3 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -339,8 +339,8 @@ runTypeErasedDataflowAnalysis(
   PostOrderCFGView POV(&CFCtx.getCFG());
   ForwardDataflowWorklist Worklist(CFCtx.getCFG(), &POV);
 
-  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 &Entry = CFCtx.getCFG().getEntry();



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


[clang] 18034ae - [clang][dataflow][NFC] Convert mutable vector references to ArrayRef

2022-08-04 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-08-04T13:12:37-04:00
New Revision: 18034aee63eeac673496a88d9e90c8dd73d15927

URL: 
https://github.com/llvm/llvm-project/commit/18034aee63eeac673496a88d9e90c8dd73d15927
DIFF: 
https://github.com/llvm/llvm-project/commit/18034aee63eeac673496a88d9e90c8dd73d15927.diff

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

`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`.

Added: 


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

Removed: 




diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h 
b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
index c563a2cd076b0..3bd1c8e12e9b2 100644
--- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -121,7 +121,7 @@ struct TypeErasedDataflowAnalysisState {
 ///   `llvm::None` represent basic blocks that are not evaluated yet.
 TypeErasedDataflowAnalysisState transferBlock(
 const ControlFlowContext &CFCtx,
-std::vector> &BlockStates,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock &Block, const Environment &InitEnv,
 TypeErasedDataflowAnalysis &Analysis,
 std::function {
 ///   `llvm::None` represent basic blocks that are not evaluated yet.
 static TypeErasedDataflowAnalysisState computeBlockInputState(
 const ControlFlowContext &CFCtx,
-std::vector> &BlockStates,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock &Block, const Environment &InitEnv,
 TypeErasedDataflowAnalysis &Analysis) {
   llvm::DenseSet Preds;
@@ -303,7 +303,7 @@ static void transferCFGInitializer(const CFGInitializer 
&CfgInit,
 
 TypeErasedDataflowAnalysisState transferBlock(
 const ControlFlowContext &CFCtx,
-std::vector> &BlockStates,
+llvm::ArrayRef> 
BlockStates,
 const CFGBlock &Block, const Environment &InitEnv,
 TypeErasedDataflowAnalysis &Analysis,
 std::functionhttps://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] 62b2a47 - [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

2022-05-04 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-04T15:31:49Z
New Revision: 62b2a47a9f15ed2f1dc4b39c924341c7b9bd7cf8

URL: 
https://github.com/llvm/llvm-project/commit/62b2a47a9f15ed2f1dc4b39c924341c7b9bd7cf8
DIFF: 
https://github.com/llvm/llvm-project/commit/62b2a47a9f15ed2f1dc4b39c924341c7b9bd7cf8.diff

LOG: [clang][dataflow] Only skip ExprWithCleanups when visiting terminators

`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, as the pointer
expression will not have a `BoolValue`.

Instead, we only skip `ExprWithCleanups` and `ParenExpr` nodes, as the
CFG does not emit them.

Differential Revision: https://reviews.llvm.org/D124807

Added: 


Modified: 
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

Removed: 




diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 0a2c75f804c2a..019d07c9b7f72 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -172,6 +172,10 @@ class Environment {
   /// Creates a storage location for `E`. Does not assign the returned storage
   /// location to `E` in the environment. Does not assign a value to the
   /// returned storage location in the environment.
+  ///
+  /// Requirements:
+  ///
+  ///  `E` must not be a `ExprWithCleanups`.
   StorageLocation &createStorageLocation(const Expr &E);
 
   /// Assigns `Loc` as the storage location of `D` in the environment.
@@ -191,11 +195,16 @@ class Environment {
   /// Requirements:
   ///
   ///  `E` must not be assigned a storage location in the environment.
+  ///  `E` must not be a `ExprWithCleanups`.
   void setStorageLocation(const Expr &E, StorageLocation &Loc);
 
   /// Returns the storage location assigned to `E` in the environment, applying
   /// the `SP` policy for skipping past indirections, or null if `E` isn't
   /// assigned a storage location in the environment.
+  ///
+  /// Requirements:
+  ///
+  ///  `E` must not be a `ExprWithCleanups`.
   StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
 
   /// Returns the storage location assigned to the `this` pointee in the
@@ -226,6 +235,12 @@ class Environment {
 
   /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if 
`E`
   /// is assigned a storage location in the environment, otherwise returns 
null.
+  ///
+  /// Requirements:
+  ///
+  ///  `E` must not be a `ExprWithCleanups`.
+  ///
+  /// FIXME: `Environment` should ignore any `ExprWithCleanups` it sees.
   Value *getValue(const Expr &E, SkipPast SP) const;
 
   /// Transfers ownership of `Loc` to the analysis context and returns a

diff  --git a/clang/include/clang/Analysis/FlowSensitive/Transfer.h 
b/clang/include/clang/Analysis/FlowSensitive/Transfer.h
index a6b663b997fd6..c5b1086c451fd 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Transfer.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Transfer.h
@@ -35,9 +35,19 @@ class StmtToEnvMap {
 ///
 /// Requirements:
 ///
-///  The type of `S` must not be `ParenExpr`.
+///  `S` must not be `ParenExpr` or `ExprWithCleanups`.
 void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env);
 
+/// Skip past a `ExprWithCleanups` which might surround `E`. Returns null if 
`E`
+/// is null.
+///
+/// The CFG omits `ExprWithCleanups` nodes (as it does with `ParenExpr`), and 
so
+/// the transfer function doesn't accept them as valid input. Manual traversal
+/// of the AST should skip and unwrap any `ExprWithCleanups` it might expect to
+/// see. They are safe to skip, as the CFG will emit calls to destructors as
+/// appropriate.
+const Expr *ignoreExprWithCleanups(const Expr *E);
+
 } // namespace dataflow
 } // namespace clang
 

diff  --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 469696643d319..ad917f18b2570 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -15,6 +15,7 @@
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/StorageLocat

[clang] 58abe36 - [clang][dataflow] Add flowConditionIsTautology function

2022-05-04 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-05T03:57:43Z
New Revision: 58abe36ae7654987f5af793e3e261ac0b43c870b

URL: 
https://github.com/llvm/llvm-project/commit/58abe36ae7654987f5af793e3e261ac0b43c870b
DIFF: 
https://github.com/llvm/llvm-project/commit/58abe36ae7654987f5af793e3e261ac0b43c870b.diff

LOG: [clang][dataflow] Add flowConditionIsTautology function

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

Differential Revision: https://reviews.llvm.org/D124943

Added: 


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

Removed: 




diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index 5cf681e4e489c..a762cb9de48bd 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -173,6 +173,10 @@ class DataflowAnalysisContext {
   /// identified by `Token` imply that `Val` is true.
   bool flowConditionImplies(AtomicBoolValue &Token, BoolValue &Val);
 
+  /// Returns true if and only if the constraints of the flow condition
+  /// identified by `Token` are always true.
+  bool flowConditionIsTautology(AtomicBoolValue &Token);
+
 private:
   /// Adds all constraints of the flow condition identified by `Token` and all
   /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used

diff  --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index 4274fca4e3c14..b29983eed79f9 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -117,6 +117,19 @@ bool 
DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token,
   return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
 }
 
+bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) 
{
+  // Returns true if and only if we cannot prove that the flow condition can
+  // ever be false.
+  llvm::DenseSet Constraints = {
+  &getBoolLiteralValue(true),
+  &getOrCreateNegationValue(getBoolLiteralValue(false)),
+  &getOrCreateNegationValue(Token),
+  };
+  llvm::DenseSet VisitedTokens;
+  addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
+  return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
+}
+
 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
 AtomicBoolValue &Token, llvm::DenseSet &Constraints,
 llvm::DenseSet &VisitedTokens) const {

diff  --git 
a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
index b4b9b169f440e..b8a9ef52504af 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@ TEST_F(DataflowAnalysisContextTest, JoinFlowConditions) {
   EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
 }
 
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+  // Fresh flow condition with empty/no constraints is always true.
+  auto &FC1 = Context.makeFlowConditionToken();
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+  // Literal `true` is always true.
+  auto &FC2 = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+  // Literal `false` is never true.
+  auto &FC3 = 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 &C1 = Context.createAtomicBoolValue();
+  auto &FC4 = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC4, C1);
+  EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
+
+  // ... but we can prove A || !A is true.
+  auto &FC5 = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(
+  FC5, Context.getOrCreateDisjunctionValue(
+   C1, Context.getOrCreateNegationValue(C1)));
+  EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
+}
+
 } // namespace



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


[clang] 45643cf - [clang][dataflow] Centralize expression skipping logic

2022-05-05 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-05T20:28:11Z
New Revision: 45643cfcc12ea6152fb9516ed24f5adf7469eb4c

URL: 
https://github.com/llvm/llvm-project/commit/45643cfcc12ea6152fb9516ed24f5adf7469eb4c
DIFF: 
https://github.com/llvm/llvm-project/commit/45643cfcc12ea6152fb9516ed24f5adf7469eb4c.diff

LOG: [clang][dataflow] Centralize expression skipping logic

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.

Differential Revision: https://reviews.llvm.org/D124965

Added: 


Modified: 
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

Removed: 




diff  --git 
a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index a762cb9de48bd..e2820fcb55655 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -31,6 +31,19 @@
 namespace clang {
 namespace dataflow {
 
+/// Skip past nodes that the CFG does not emit. These nodes are invisible to
+/// flow-sensitive analysis, and should be ignored as they will effectively not
+/// exist.
+///
+///   * `ParenExpr` - The CFG takes the operator precedence into account, but
+///   otherwise omits the node afterwards.
+///
+///   * `ExprWithCleanups` - The CFG will generate the appropriate calls to
+///   destructors and then omit the node.
+///
+const Expr &ignoreCFGOmittedNodes(const Expr &E);
+const Stmt &ignoreCFGOmittedNodes(const Stmt &S);
+
 /// Owns objects that encompass the state of a program and stores context that
 /// is used during dataflow analysis.
 class DataflowAnalysisContext {
@@ -95,14 +108,15 @@ class DataflowAnalysisContext {
   ///
   ///  `E` must not be assigned a storage location.
   void setStorageLocation(const Expr &E, StorageLocation &Loc) {
-assert(ExprToLoc.find(&E) == ExprToLoc.end());
-ExprToLoc[&E] = &Loc;
+const Expr &CanonE = ignoreCFGOmittedNodes(E);
+assert(ExprToLoc.find(&CanonE) == ExprToLoc.end());
+ExprToLoc[&CanonE] = &Loc;
   }
 
   /// Returns the storage location assigned to `E` or null if `E` has no
   /// assigned storage location.
   StorageLocation *getStorageLocation(const Expr &E) const {
-auto It = ExprToLoc.find(&E);
+auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
 return It == ExprToLoc.end() ? nullptr : It->second;
   }
 

diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 019d07c9b7f72..0a2c75f804c2a 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -172,10 +172,6 @@ class Environment {
   /// Creates a storage location for `E`. Does not assign the returned storage
   /// location to `E` in the environment. Does not assign a value to the
   /// returned storage location in the environment.
-  ///
-  /// Requirements:
-  ///
-  ///  `E` must not be a `ExprWithCleanups`.
   StorageLocation &createStorageLocation(const Expr &E);
 
   /// Assigns `Loc` as the storage location of `D` in the environment.
@@ -195,16 +191,11 @@ class Environment {
   /// Requirements:
   ///
   ///  `E` must not be assigned a storage location in the environment.
-  ///  `E` must not be a `ExprWithCleanups`.
   void setStorageLocation(const Expr &E, StorageLocation &Loc);
 
   /// Returns the storage location assigned to `E` in the environment, applying
   /// the `SP` policy for skipping past indirections, or null if `E` isn't
   /// assigned a storage location in the environment.
-  ///
-  /// Requirements:
-  ///
-  ///  `E` must not be a `ExprWithCleanups`.
   StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
 
   /// Returns the storage location assigned to the `this` pointee in the
@@ -235,12 +226,6 @@ class Environment {
 
   /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if 
`E`
   /// is assigned a storage location in the environment, otherwise returns 
null.
-  ///
-  /// Requirements:
-  ///
-  ///  `E` must not be a `ExprWithCleanups`.
-  ///
-  /// FIXME: `Environment` should ignore any `ExprWithCleanups` it sees.
   Value *getValue(const Expr &E, SkipPast SP

[clang] 29d35ec - [clang][dataflow] Fix MapLattice::insert() to not drop return value

2022-07-25 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-07-25T14:24:33-04:00
New Revision: 29d35ece8249f2d8a51437a5c008e6bf63da9874

URL: 
https://github.com/llvm/llvm-project/commit/29d35ece8249f2d8a51437a5c008e6bf63da9874
DIFF: 
https://github.com/llvm/llvm-project/commit/29d35ece8249f2d8a51437a5c008e6bf63da9874.diff

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

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

Differential Revision: https://reviews.llvm.org/D130497

Added: 


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

Removed: 




diff  --git a/clang/include/clang/Analysis/FlowSensitive/MapLattice.h 
b/clang/include/clang/Analysis/FlowSensitive/MapLattice.h
index 014cd60841ee7..16b0c978779a7 100644
--- a/clang/include/clang/Analysis/FlowSensitive/MapLattice.h
+++ b/clang/include/clang/Analysis/FlowSensitive/MapLattice.h
@@ -54,10 +54,13 @@ template  class 
MapLattice {
   // The `bottom` element is the empty map.
   static MapLattice bottom() { return MapLattice(); }
 
-  void insert(const std::pair &P) { C.insert(P); }
+  std::pair
+  insert(const std::pair &P) {
+return C.insert(P);
+  }
 
-  void insert(std::pair &&P) {
-C.insert(std::move(P));
+  std::pair insert(std::pair &&P) 
{
+return C.insert(std::move(P));
   }
 
   unsigned size() const { return C.size(); }

diff  --git a/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
index d3436e8f9496b..f6fe81e0dfc5f 100644
--- a/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -50,13 +50,18 @@ static constexpr int Key1 = 0;
 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;



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


[clang] 854c273 - [clang][dataflow] Weaken guard to only check for storage location

2022-05-17 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-17T18:58:07Z
New Revision: 854c273cbb7ec6745dc63cfe1951b51e9092e8ee

URL: 
https://github.com/llvm/llvm-project/commit/854c273cbb7ec6745dc63cfe1951b51e9092e8ee
DIFF: 
https://github.com/llvm/llvm-project/commit/854c273cbb7ec6745dc63cfe1951b51e9092e8ee.diff

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

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).

Differential Revision: https://reviews.llvm.org/D125823

Added: 


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

Removed: 




diff  --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp 
b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index a8d739c155ab..aebc9dd6f6fa 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -104,7 +104,7 @@ class TerminatorVisitor : public 
ConstStmtVisitor {
 private:
   void extendFlowCondition(const Expr &Cond) {
 // 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


[clang] 5bbef2e - [clang][dataflow] Fix double visitation of nested logical operators

2022-05-17 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-17T20:28:48Z
New Revision: 5bbef2e3fff123293ed9c2037e2662e352bf37af

URL: 
https://github.com/llvm/llvm-project/commit/5bbef2e3fff123293ed9c2037e2662e352bf37af
DIFF: 
https://github.com/llvm/llvm-project/commit/5bbef2e3fff123293ed9c2037e2662e352bf37af.diff

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

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.

Differential Revision: https://reviews.llvm.org/D125821

Added: 


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

Removed: 




diff  --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 3a94434b7aeb7..f88406595d728 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -571,12 +571,15 @@ class TransferVisitor : public 
ConstStmtVisitor {
 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(&SubExpr);
+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(&SubExpr);
+}
+
 if (auto *Val = dyn_cast_or_null(
 Env.getValue(SubExpr, SkipPast::Reference)))
   return *Val;

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index db1f1cff311c7..9819409291040 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2467,6 +2467,67 @@ TEST_F(TransferTest, AssignFromCompositeBoolExpression) {
   EXPECT_EQ(&BazVal->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 &ASTCtx) {
+  ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+  const Environment &Env = 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 &FooLeftSubVal =
+  cast(FooVal->getLeftSubValue());
+  const auto &FooLeftLeftSubVal =
+  cast(FooLeftSubVal.getLeftSubValue());
+  EXPECT_EQ(&FooLeftLeftSubVal.getLeftSubValue(), AVal);
+  EXPECT_EQ(&FooLeftLeftSubVal.getRightSubValue(), BVal);
+  EXPECT_EQ(&FooLeftSubVal.getRightSubValue(), CVal);
+

[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

2024-03-20 Thread Eric Li via cfe-commits

https://github.com/tJener created 
https://github.com/llvm/llvm-project/pull/85957

A coroutine function body (`CoroutineBodyStmt`) may have null children, which 
causes `isa` to segfault.

>From 386e7dea15739df65ad9ecb3dd7f7dcd23b6acae Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:15:45 -0400
Subject: [PATCH] [clang][dataflow] Fix crash when analyzing a coroutine

A coroutine function body (`CoroutineBodyStmt`) may have null
children, which causes `isa` to segfault.
---
 .../lib/Analysis/FlowSensitive/AdornedCFG.cpp |  2 +-
 .../Analysis/FlowSensitive/TransferTest.cpp   | 53 ++-
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp 
b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
index 3813b8c3ee8a23..daa73bed1bd9f5 100644
--- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
+++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
@@ -103,7 +103,7 @@ buildContainsExprConsumedInDifferentBlock(
   auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,
  const CFGBlock *Block) {
 for (const Stmt *Child : S->children()) {
-  if (!isa(Child))
+  if (!isa_and_nonnull(Child))
 continue;
   const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);
   if (ChildBlock != Block)
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a243535d387257..2dc01a655264f3 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -37,6 +37,40 @@ using ::testing::Ne;
 using ::testing::NotNull;
 using ::testing::UnorderedElementsAre;
 
+// Declares a minimal coroutine library.
+constexpr llvm::StringRef CoroutineLibrary = R"cc(
+struct promise;
+struct task;
+
+namespace std {
+template 
+struct coroutine_traits {};
+template <>
+struct coroutine_traits {
+using promise_type = promise;
+};
+
+template 
+struct coroutine_handle {
+static constexpr coroutine_handle from_address(void *addr) { return {}; }
+};
+}  // namespace std
+
+struct awaitable {
+bool await_ready() const noexcept;
+void await_suspend(std::coroutine_handle) const noexcept;
+void await_resume() const noexcept;
+};
+struct task {};
+struct promise {
+task get_return_object();
+awaitable initial_suspend();
+awaitable final_suspend() noexcept;
+void unhandled_exception();
+void return_void();
+};
+)cc";
+
 void runDataflow(
 llvm::StringRef Code,
 std::function<
@@ -4607,7 +4641,7 @@ TEST(TransferTest, LoopCanProveInvariantForBoolean) {
 }
 
 TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
-  std::string Code = R"(
+  std::string Code = R"cc(
 union Union {
   int A;
   float B;
@@ -4618,7 +4652,7 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   Union B;
   A = B;
 }
-  )";
+  )cc";
   // This is a crash regression test when calling the transfer function on a
   // `CXXThisExpr` that refers to a union.
   runDataflow(
@@ -4628,6 +4662,21 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
 }
 
+TEST(TransferTest, DoesNotCrashOnNullChildren) {
+  std::string Code = (CoroutineLibrary + R"cc(
+task foo() noexcept {
+  co_return;
+}
+  )cc").str();
+  // This is a crash regression test when calling `AdornedCFG::build` on a
+  // statement (in this case, the `CoroutineBodyStmt`) with null children.
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> &,
+ ASTContext &) {},
+  LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true, "foo");
+}
+
 TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
   std::string Code = R"(
 struct A {

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


[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

2024-03-20 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/85957

>From 386e7dea15739df65ad9ecb3dd7f7dcd23b6acae Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:15:45 -0400
Subject: [PATCH 1/2] [clang][dataflow] Fix crash when analyzing a coroutine

A coroutine function body (`CoroutineBodyStmt`) may have null
children, which causes `isa` to segfault.
---
 .../lib/Analysis/FlowSensitive/AdornedCFG.cpp |  2 +-
 .../Analysis/FlowSensitive/TransferTest.cpp   | 53 ++-
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp 
b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
index 3813b8c3ee8a23..daa73bed1bd9f5 100644
--- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
+++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
@@ -103,7 +103,7 @@ buildContainsExprConsumedInDifferentBlock(
   auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,
  const CFGBlock *Block) {
 for (const Stmt *Child : S->children()) {
-  if (!isa(Child))
+  if (!isa_and_nonnull(Child))
 continue;
   const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);
   if (ChildBlock != Block)
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a243535d387257..2dc01a655264f3 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -37,6 +37,40 @@ using ::testing::Ne;
 using ::testing::NotNull;
 using ::testing::UnorderedElementsAre;
 
+// Declares a minimal coroutine library.
+constexpr llvm::StringRef CoroutineLibrary = R"cc(
+struct promise;
+struct task;
+
+namespace std {
+template 
+struct coroutine_traits {};
+template <>
+struct coroutine_traits {
+using promise_type = promise;
+};
+
+template 
+struct coroutine_handle {
+static constexpr coroutine_handle from_address(void *addr) { return {}; }
+};
+}  // namespace std
+
+struct awaitable {
+bool await_ready() const noexcept;
+void await_suspend(std::coroutine_handle) const noexcept;
+void await_resume() const noexcept;
+};
+struct task {};
+struct promise {
+task get_return_object();
+awaitable initial_suspend();
+awaitable final_suspend() noexcept;
+void unhandled_exception();
+void return_void();
+};
+)cc";
+
 void runDataflow(
 llvm::StringRef Code,
 std::function<
@@ -4607,7 +4641,7 @@ TEST(TransferTest, LoopCanProveInvariantForBoolean) {
 }
 
 TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
-  std::string Code = R"(
+  std::string Code = R"cc(
 union Union {
   int A;
   float B;
@@ -4618,7 +4652,7 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   Union B;
   A = B;
 }
-  )";
+  )cc";
   // This is a crash regression test when calling the transfer function on a
   // `CXXThisExpr` that refers to a union.
   runDataflow(
@@ -4628,6 +4662,21 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
 }
 
+TEST(TransferTest, DoesNotCrashOnNullChildren) {
+  std::string Code = (CoroutineLibrary + R"cc(
+task foo() noexcept {
+  co_return;
+}
+  )cc").str();
+  // This is a crash regression test when calling `AdornedCFG::build` on a
+  // statement (in this case, the `CoroutineBodyStmt`) with null children.
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> &,
+ ASTContext &) {},
+  LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true, "foo");
+}
+
 TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
   std::string Code = R"(
 struct A {

>From 71a0ade85a22597d74dbd9507115ee74f6d3a2d4 Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:32:42 -0400
Subject: [PATCH 2/2] fixup! [clang][dataflow] Fix crash when analyzing a
 coroutine

---
 clang/unittests/Analysis/FlowSensitive/TransferTest.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 2dc01a655264f3..b8f8f1ada2fb98 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -4667,7 +4667,8 @@ TEST(TransferTest, DoesNotCrashOnNullChildren) {
 task foo() noexcept {
   co_return;
 }
-  )cc").str();
+  )cc")
+ .str();
   // This is a crash regression test when calling `AdornedCFG::build` on a
   // statement (in this case, the `CoroutineBodyStmt`) with null children.
   runDataflow(

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


[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

2024-03-20 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/85957

>From 386e7dea15739df65ad9ecb3dd7f7dcd23b6acae Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:15:45 -0400
Subject: [PATCH 1/3] [clang][dataflow] Fix crash when analyzing a coroutine

A coroutine function body (`CoroutineBodyStmt`) may have null
children, which causes `isa` to segfault.
---
 .../lib/Analysis/FlowSensitive/AdornedCFG.cpp |  2 +-
 .../Analysis/FlowSensitive/TransferTest.cpp   | 53 ++-
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp 
b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
index 3813b8c3ee8a23..daa73bed1bd9f5 100644
--- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
+++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
@@ -103,7 +103,7 @@ buildContainsExprConsumedInDifferentBlock(
   auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,
  const CFGBlock *Block) {
 for (const Stmt *Child : S->children()) {
-  if (!isa(Child))
+  if (!isa_and_nonnull(Child))
 continue;
   const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);
   if (ChildBlock != Block)
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a243535d387257..2dc01a655264f3 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -37,6 +37,40 @@ using ::testing::Ne;
 using ::testing::NotNull;
 using ::testing::UnorderedElementsAre;
 
+// Declares a minimal coroutine library.
+constexpr llvm::StringRef CoroutineLibrary = R"cc(
+struct promise;
+struct task;
+
+namespace std {
+template 
+struct coroutine_traits {};
+template <>
+struct coroutine_traits {
+using promise_type = promise;
+};
+
+template 
+struct coroutine_handle {
+static constexpr coroutine_handle from_address(void *addr) { return {}; }
+};
+}  // namespace std
+
+struct awaitable {
+bool await_ready() const noexcept;
+void await_suspend(std::coroutine_handle) const noexcept;
+void await_resume() const noexcept;
+};
+struct task {};
+struct promise {
+task get_return_object();
+awaitable initial_suspend();
+awaitable final_suspend() noexcept;
+void unhandled_exception();
+void return_void();
+};
+)cc";
+
 void runDataflow(
 llvm::StringRef Code,
 std::function<
@@ -4607,7 +4641,7 @@ TEST(TransferTest, LoopCanProveInvariantForBoolean) {
 }
 
 TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
-  std::string Code = R"(
+  std::string Code = R"cc(
 union Union {
   int A;
   float B;
@@ -4618,7 +4652,7 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   Union B;
   A = B;
 }
-  )";
+  )cc";
   // This is a crash regression test when calling the transfer function on a
   // `CXXThisExpr` that refers to a union.
   runDataflow(
@@ -4628,6 +4662,21 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
 }
 
+TEST(TransferTest, DoesNotCrashOnNullChildren) {
+  std::string Code = (CoroutineLibrary + R"cc(
+task foo() noexcept {
+  co_return;
+}
+  )cc").str();
+  // This is a crash regression test when calling `AdornedCFG::build` on a
+  // statement (in this case, the `CoroutineBodyStmt`) with null children.
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> &,
+ ASTContext &) {},
+  LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true, "foo");
+}
+
 TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
   std::string Code = R"(
 struct A {

>From 71a0ade85a22597d74dbd9507115ee74f6d3a2d4 Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:32:42 -0400
Subject: [PATCH 2/3] fixup! [clang][dataflow] Fix crash when analyzing a
 coroutine

---
 clang/unittests/Analysis/FlowSensitive/TransferTest.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 2dc01a655264f3..b8f8f1ada2fb98 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -4667,7 +4667,8 @@ TEST(TransferTest, DoesNotCrashOnNullChildren) {
 task foo() noexcept {
   co_return;
 }
-  )cc").str();
+  )cc")
+ .str();
   // This is a crash regression test when calling `AdornedCFG::build` on a
   // statement (in this case, the `CoroutineBodyStmt`) with null children.
   runDataflow(

>From a08e5818f5db6e4d4e08f5e4863454c06a731acf Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 20 Mar 2024 12:38:18 -0400
Subject: [PATCH 3/3] fixup! [clang][dataflow] Fix crash when analyzing a
 coroutine

---
 clang/unittests/Analysis/Flow

[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

2024-03-20 Thread Eric Li via cfe-commits


@@ -4628,6 +4662,21 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
   LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
 }
 
+TEST(TransferTest, DoesNotCrashOnNullChildren) {
+  std::string Code = (CoroutineLibrary + R"cc(
+task foo() noexcept {
+  co_return;
+}
+  )cc").str();
+  // This is a crash regression test when calling `AdornedCFG::build` on a
+  // statement (in this case, the `CoroutineBodyStmt`) with null children.
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> &,
+ ASTContext &) {},
+  LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true, "foo");

tJener wrote:

Done.

https://github.com/llvm/llvm-project/pull/85957
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

2024-03-20 Thread Eric Li via cfe-commits

https://github.com/tJener closed https://github.com/llvm/llvm-project/pull/85957
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] 576283c - [clang] Support `constexpr` for some `ASTNodeKind` member functions

2022-10-13 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-10-13T13:00:48-04:00
New Revision: 576283c3a8ef5078b3ec12fa442c14f3a1b5fea2

URL: 
https://github.com/llvm/llvm-project/commit/576283c3a8ef5078b3ec12fa442c14f3a1b5fea2
DIFF: 
https://github.com/llvm/llvm-project/commit/576283c3a8ef5078b3ec12fa442c14f3a1b5fea2.diff

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

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.

Differential Revision: https://reviews.llvm.org/D135816

Added: 


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

Removed: 




diff  --git a/clang/include/clang/AST/ASTTypeTraits.h 
b/clang/include/clang/AST/ASTTypeTraits.h
index cd6b5143bf79..8713221a7378 100644
--- a/clang/include/clang/AST/ASTTypeTraits.h
+++ b/clang/include/clang/AST/ASTTypeTraits.h
@@ -51,11 +51,10 @@ enum TraversalKind {
 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 @@ class ASTNodeKind {
   /// \}
 
   /// 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 @@ class ASTNodeKind {
   StringRef asStringRef() const;
 
   /// Strict weak ordering for ASTNodeKind.
-  bool operator<(const ASTNodeKind &Other) const {
+  constexpr bool operator<(const ASTNodeKind &Other) const {
 return KindId < Other.KindId;
   }
 
@@ -121,7 +120,7 @@ class ASTNodeKind {
 
   /// 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 @@ class ASTNodeKind {
   };
 
   /// 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.

diff  --git a/clang/unittests/AST/ASTTypeTraitsTest.cpp 
b/clang/unittests/AST/ASTTypeTraitsTest.cpp
index 9fcb00630209..dcea32d1f1c0 100644
--- a/clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ b/clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -117,6 +117,47 @@ TEST(ASTNodeKind, UnknownKind) {
   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());



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


[clang] 7d0cdbf - [libTooling] Add `getFileRange` as an alternative to `getRangeForEdit`

2023-01-12 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2023-01-12T17:44:29-05:00
New Revision: 7d0cdbf69edf5b14101d9c11fe9ca4cf5228e81d

URL: 
https://github.com/llvm/llvm-project/commit/7d0cdbf69edf5b14101d9c11fe9ca4cf5228e81d
DIFF: 
https://github.com/llvm/llvm-project/commit/7d0cdbf69edf5b14101d9c11fe9ca4cf5228e81d.diff

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

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.

Differential Revision: https://reviews.llvm.org/D141634

Added: 


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

Removed: 




diff  --git a/clang/include/clang/Tooling/Transformer/SourceCode.h 
b/clang/include/clang/Tooling/Transformer/SourceCode.h
index d95cb9b4ae187..266aae09d27d5 100644
--- a/clang/include/clang/Tooling/Transformer/SourceCode.h
+++ b/clang/include/clang/Tooling/Transformer/SourceCode.h
@@ -112,6 +112,27 @@ getRangeForEdit(const CharSourceRange &EditRange, const 
ASTContext &Context,
   return getRangeForEdit(EditRange, Context.getSourceManager(),
  Context.getLangOpts(), IncludeMacroExpansion);
 }
+
+/// Attempts to resolve the given range to one that starts and ends in a
+/// particular file.
+///
+/// If \c IncludeMacroExpansion is true, a limited set of cases involving 
source
+/// locations in macro expansions is supported. For example, if we're looking 
to
+/// get the range of the int literal 3, and we have the following definition:
+///#define DO_NOTHING(x) x
+///foo(DO_NOTHING(3))
+/// the returned range will hold the source text `DO_NOTHING(3)`.
+std::optional getFileRange(const CharSourceRange &EditRange,
+const SourceManager &SM,
+const LangOptions &LangOpts,
+bool IncludeMacroExpansion);
+inline std::optional
+getFileRange(const CharSourceRange &EditRange, const ASTContext &Context,
+ bool IncludeMacroExpansion) {
+  return getFileRange(EditRange, Context.getSourceManager(),
+  Context.getLangOpts(), IncludeMacroExpansion);
+}
+
 } // namespace tooling
 } // namespace clang
 #endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCECODE_H

diff  --git a/clang/lib/Tooling/Transformer/SourceCode.cpp 
b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 008a83cde42c4..262ec3557f7be 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@ CharSourceRange 
clang::tooling::maybeExtendRange(CharSourceRange Range,
   return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
 }
 
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
-  const SourceManager &SM) {
+static llvm::Error validateRange(const CharSourceRange &Range,
+ const SourceManager &SM,
+ bool AllowSystemHeaders) {
   if (Range.isInvalid())
 return llvm::make_error(errc::invalid_argument,
  "Invalid range");
@@ -60,10 +61,12 @@ llvm::Error clang::tooling::validateEditRange(const 
CharSourceRange &Range,
 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 @@ llvm::Error clang::tooling::validateEditRange(const 
CharSourceRange &Range,
 errc::invalid_argument, "Range begins and ends in 
diff erent 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::validat

[clang] 2307029 - [libTooling] Rename `getRangeForEdit` as `getFileRangeForEdit`

2023-01-18 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2023-01-18T13:58:26-05:00
New Revision: 2307029b1a43a86aa6614c33aa198addf82d486b

URL: 
https://github.com/llvm/llvm-project/commit/2307029b1a43a86aa6614c33aa198addf82d486b
DIFF: 
https://github.com/llvm/llvm-project/commit/2307029b1a43a86aa6614c33aa198addf82d486b.diff

LOG: [libTooling] Rename `getRangeForEdit` as `getFileRangeForEdit`

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

Depends on D141634

Differential Revision: https://reviews.llvm.org/D141636

Added: 


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

Removed: 




diff  --git a/clang/include/clang/Tooling/Transformer/SourceCode.h 
b/clang/include/clang/Tooling/Transformer/SourceCode.h
index 266aae09d27d5..44a4749db74c9 100644
--- a/clang/include/clang/Tooling/Transformer/SourceCode.h
+++ b/clang/include/clang/Tooling/Transformer/SourceCode.h
@@ -104,13 +104,14 @@ llvm::Error validateEditRange(const CharSourceRange 
&Range,
 /// will be rewritten to
 ///foo(6)
 std::optional
-getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM,
-const LangOptions &LangOpts, bool IncludeMacroExpansion = 
true);
+getFileRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM,
+const LangOptions &LangOpts,
+bool IncludeMacroExpansion = true);
 inline std::optional
-getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context,
-bool IncludeMacroExpansion = true) {
-  return getRangeForEdit(EditRange, Context.getSourceManager(),
- Context.getLangOpts(), IncludeMacroExpansion);
+getFileRangeForEdit(const CharSourceRange &EditRange, const ASTContext 
&Context,
+bool IncludeMacroExpansion = true) {
+  return getFileRangeForEdit(EditRange, Context.getSourceManager(),
+ Context.getLangOpts(), IncludeMacroExpansion);
 }
 
 /// Attempts to resolve the given range to one that starts and ends in a

diff  --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp 
b/clang/lib/Tooling/Transformer/RewriteRule.cpp
index afc281d5fa622..eefddc3494048 100644
--- a/clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -39,7 +39,7 @@ translateEdits(const MatchResult &Result, ArrayRef 
ASTEdits) {
 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 
diff ers from
@@ -449,7 +449,7 @@ SourceLocation transformer::detail::getRuleMatchLoc(const 
MatchResult &Result) {
   auto &NodesMap = 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 (RootRange)

diff  --git a/clang/lib/Tooling/Transformer/SourceCode.cpp 
b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 262ec3557f7be..35edc261ef096 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -122,7 +122,7 @@ static CharSourceRange getRange(const CharSourceRange 
&EditRange,
   return Range;
 }
 
-std::optional clang::tooling::getRangeForEdit(
+std::optional clang::tooling::getFileRangeForEdit(
 const CharSourceRange &EditRange, const SourceManager &SM,
 const LangOptions &LangOpts, bool IncludeMacroExpansion) {
   CharSourceRange Range =

diff  --git a/clang/unittests/Tooling/SourceCodeTest.cpp 
b/clang/unittests/Tooling/SourceCodeTest.cpp
index 7a9bd329e8d46..3d1dbceb63a7f 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -25,7 +25,7 @@ using llvm::ValueIs;
 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 @@ TEST(SourceCodeTest, 
getAssociatedRangeInvalidForPartialExpansions) {
   Visitor.runOver(Code);
 }
 
-class GetRangeForEditTest : public testing::TestWithParam {};
-INSTANTIATE_TEST_SUITE_P(WithAndWithoutExpansions, GetRangeForEd

[clang] 33b598a - [clang][dataflow] Relax assert on existence of `this` pointee storage

2022-05-25 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-25T20:58:02Z
New Revision: 33b598a808b9ef1a019e798f19855f203e09ad48

URL: 
https://github.com/llvm/llvm-project/commit/33b598a808b9ef1a019e798f19855f203e09ad48
DIFF: 
https://github.com/llvm/llvm-project/commit/33b598a808b9ef1a019e798f19855f203e09ad48.diff

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

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.

Differential Revision: https://reviews.llvm.org/D126405

Added: 


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

Removed: 




diff  --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index f88406595d728..fc04e0b55ffc7 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -279,7 +279,10 @@ class TransferVisitor : public 
ConstStmtVisitor {
 
   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 &Loc = Env.createStorageLocation(*S);
 Env.setStorageLocation(*S, Loc);

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index b6d2940a98daa..bb6e269969da7 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -42,10 +42,11 @@ class TransferTest : public ::testing::Test {
   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 &C, Environment &) {
   return NoopAnalysis(C, ApplyBuiltinTransfer);
 },
@@ -3175,4 +3176,27 @@ TEST_F(TransferTest, 
LoopWithStructReferenceAssignmentConverges) {
 });
 }
 
+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



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


[clang] 5520c58 - [clang][dataflow] Fix incorrect CXXThisExpr pointee for lambdas

2022-05-25 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-05-25T20:58:02Z
New Revision: 5520c5839016d1d98d8e01789eb17c910381bd6f

URL: 
https://github.com/llvm/llvm-project/commit/5520c5839016d1d98d8e01789eb17c910381bd6f
DIFF: 
https://github.com/llvm/llvm-project/commit/5520c5839016d1d98d8e01789eb17c910381bd6f.diff

LOG: [clang][dataflow] Fix incorrect CXXThisExpr pointee for lambdas

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.

Differential Revision: https://reviews.llvm.org/D126413

Added: 


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

Removed: 




diff  --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index e555bee0e8bf..fe573bbf9f37 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -216,7 +216,12 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
   }
 
   if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
-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()) {

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index bb6e269969da..bed6b975974c 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1476,6 +1476,112 @@ TEST_F(TransferTest, ClassThisMember) {
   });
 }
 
+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 &ASTCtx) {
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _)));
+const Environment &Env = 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(&ThisLoc->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 &ASTCtx) {
+ASSERT_THAT(Results, ElementsAre(Pair("p2", _)));
+const Environment &Env = 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(&ThisLoc->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::Ar

[clang] 3cb6ead - [clang][TypePrinter] Add option to skip over elaborated types

2023-06-06 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2023-06-06T15:11:09-04:00
New Revision: 3cb6ead77c6668fcd1362763b2603752c6c595fa

URL: 
https://github.com/llvm/llvm-project/commit/3cb6ead77c6668fcd1362763b2603752c6c595fa
DIFF: 
https://github.com/llvm/llvm-project/commit/3cb6ead77c6668fcd1362763b2603752c6c595fa.diff

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

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.

Differential Revision: https://reviews.llvm.org/D149677

Added: 


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

Removed: 




diff  --git a/clang/include/clang/AST/PrettyPrinter.h 
b/clang/include/clang/AST/PrettyPrinter.h
index 5aeaca7beda2f..8a0bc6dfb57b8 100644
--- a/clang/include/clang/AST/PrettyPrinter.h
+++ b/clang/include/clang/AST/PrettyPrinter.h
@@ -60,14 +60,15 @@ struct PrintingPolicy {
   : 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 @@ struct PrintingPolicy {
   /// 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

diff  --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 00f5fe0da8cdf..1b62f6630928c 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1576,6 +1576,11 @@ void TypePrinter::printElaboratedBefore(const 
ElaboratedType *T,
 return;
   }
 
+  if (Policy.SuppressElaboration) {
+printBefore(T->getNamedType(), OS);
+return;
+  }
+
   // The tag definition will take care of these.
   if (!Policy.IncludeTagDefinition)
   {
@@ -1595,6 +1600,12 @@ void TypePrinter::printElaboratedAfter(const 
ElaboratedType *T,
 raw_ostream &OS) {
   if (Policy.IncludeTagDefinition && T->getOwnedTagDecl())
 return;
+
+  if (Policy.SuppressElaboration) {
+printAfter(T->getNamedType(), OS);
+return;
+  }
+
   ElaboratedTypePolicyRAII PolicyRAII(Policy);
   printAfter(T->getNamedType(), OS);
 }

diff  --git a/clang/unittests/AST/TypePrinterTest.cpp 
b/clang/unittests/AST/TypePrinterTest.cpp
index 77ff442236686..f0a6eb7e9fd8c 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -97,6 +97,33 @@ TEST(TypePrinter, ParamsUglified) {
  "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) { Policy.FullyQualifiedName = true; }));
+  ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
+ "shared::a::S",
+ [](PrintingPolicy &Policy) {
+   

[clang] a78d4b5 - [libTooling] Add flag to getRangeForEdit to ignore macro expansions

2022-12-08 Thread Eric Li via cfe-commits

Author: Eric Li
Date: 2022-12-08T22:40:10-05:00
New Revision: a78d4b5ba716d88a90b905c261f53e74e67a7367

URL: 
https://github.com/llvm/llvm-project/commit/a78d4b5ba716d88a90b905c261f53e74e67a7367
DIFF: 
https://github.com/llvm/llvm-project/commit/a78d4b5ba716d88a90b905c261f53e74e67a7367.diff

LOG: [libTooling] Add flag to getRangeForEdit to ignore macro expansions

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.

Differential Revision: https://reviews.llvm.org/D139676

Added: 


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

Removed: 




diff  --git a/clang/include/clang/Tooling/Transformer/SourceCode.h 
b/clang/include/clang/Tooling/Transformer/SourceCode.h
index f3d119c86074..911c1bdb0f5d 100644
--- a/clang/include/clang/Tooling/Transformer/SourceCode.h
+++ b/clang/include/clang/Tooling/Transformer/SourceCode.h
@@ -91,16 +91,25 @@ llvm::Error validateEditRange(const CharSourceRange &Range,
   const SourceManager &SM);
 
 /// Attempts to resolve the given range to one that can be edited by a rewrite;
-/// generally, one that starts and ends within a particular file. It supports a
-/// limited set of cases involving source locations in macro expansions. If a
-/// value is returned, it satisfies \c validateEditRange.
+/// generally, one that starts and ends within a particular file. If a value is
+/// returned, it satisfies \c validateEditRange.
+///
+/// If \c IncludeMacroExpansion is true, a limited set of cases involving 
source
+/// locations in macro expansions is supported. For example, if we're looking 
to
+/// rewrite the int literal 3 to 6, and we have the following definition:
+///#define DO_NOTHING(x) x
+/// then
+///foo(DO_NOTHING(3))
+/// will be rewritten to
+///foo(6)
 llvm::Optional
 getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM,
-const LangOptions &LangOpts);
+const LangOptions &LangOpts, bool IncludeMacroExpansion = 
true);
 inline llvm::Optional
-getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context) {
+getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context,
+bool IncludeMacroExpansion = true) {
   return getRangeForEdit(EditRange, Context.getSourceManager(),
- Context.getLangOpts());
+ Context.getLangOpts(), IncludeMacroExpansion);
 }
 } // namespace tooling
 } // namespace clang

diff  --git a/clang/lib/Tooling/Transformer/SourceCode.cpp 
b/clang/lib/Tooling/Transformer/SourceCode.cpp
index b89d74a314bf..1aff94ac5077 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -78,27 +78,43 @@ llvm::Error clang::tooling::validateEditRange(const 
CharSourceRange &Range,
   return llvm::Error::success();
 }
 
-llvm::Optional
-clang::tooling::getRangeForEdit(const CharSourceRange &EditRange,
-const SourceManager &SM,
-const LangOptions &LangOpts) {
-  // 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 following definition:
-  //#define DO_NOTHING(x) x
-  // then
-  //foo(DO_NOTHING(3))
-  // will be rewritten to
-  //foo(6)
-  // rather than the arguably better
-  //foo(DO_NOTHING(6))
-  // Decide whether the current behavior is desirable and modify if not.
-  CharSourceRange Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
+static bool SpelledInMacroDefinition(SourceLocation Loc,
+ const SourceManager &SM) {
+  while (Loc.isMacroID()) {
+const auto &Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
+if (Expansion.isMacroArgExpansion()) {
+  // Check the spelling location of the macro arg, in case the arg itself 
is
+  // in a macro expansion.
+  Loc = Expansion.getSpellingLoc();
+} else {
+  return true;
+}
+  }
+  return false;
+}
+
+llvm::Optional clang::tooling::getRangeForEdit(
+const CharSourceRange &EditRange, const SourceManager &SM,
+const LangOptions &LangOpts, bool IncludeMacroExpansion) {
+  CharSourceRange Range;
+  if (IncludeMacroExpansion) {
+Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
+  } else {
+if (SpelledInMacroDefinition(EditRange.getBegin(), SM) ||
+SpelledInMa

[clang] [libTooling] Fix `getFileRangeForEdit` for inner nested template types (PR #87673)

2024-04-04 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/87673

>From 697551dd155cd8ada0d93ce8aa2951a9f9f3d4b4 Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Thu, 4 Apr 2024 14:04:25 -0400
Subject: [PATCH 1/2] [libTooling] Fix `getFileRangeForEdit` for inner nested
 template types

When there is `>>` in source from the right brackets of a nested
template, the end location of the inner template points intto a
scratch space with a single `>` token. This prevents the lexer from
seeing the `>>` token in the original source.

However, this causes the range to appear to be partially in a macro,
and is problematic if we are trying to avoid ranges with any macro
expansions.

This change detects this odd case in token ranges, converting it to
the corresponding character range without the expansion.
---
 clang/lib/Tooling/Transformer/SourceCode.cpp | 61 ++--
 clang/unittests/Tooling/SourceCodeTest.cpp   | 29 ++
 2 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/SourceCode.cpp 
b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 6aae834b0db563..114f6ba4833cb0 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -101,6 +101,56 @@ static bool spelledInMacroDefinition(SourceLocation Loc,
   return false;
 }
 
+// Returns the expansion loc of `Loc` if `Loc` is the location of the `>` token
+// of an inner nested template, where the inner and outer templates end 
brackets
+// are spelled as `>>`.
+//
+// Clang handles the `>>` in nested templates by placing the `SourceLocation`
+// of the inner template end bracket in scratch space. This forces it to be a
+// separate token (otherwise it would be lexed as `>>`), but that means it also
+// looks like a macro.
+static std::optional getExpansionForNestedTemplateGreater(
+SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) {
+  if (Loc.isMacroID()) {
+auto SpellingLoc = SM.getSpellingLoc(Loc);
+auto ExpansionLoc = SM.getExpansionLoc(Loc);
+Token SpellingTok, ExpansionTok;
+if (Lexer::getRawToken(SpellingLoc, SpellingTok, SM, LangOpts,
+   /*IgnoreWhiteSpace=*/false)) {
+  return std::nullopt;
+}
+if (Lexer::getRawToken(ExpansionLoc, ExpansionTok, SM, LangOpts,
+   /*IgnoreWhiteSpace=*/false)) {
+  return std::nullopt;
+}
+if (SpellingTok.getKind() == tok::greater &&
+ExpansionTok.getKind() == tok::greatergreater) {
+  return ExpansionLoc;
+}
+  }
+  return std::nullopt;
+}
+
+// Returns `Range`, but adjusted to smooth over oddities introduced by Clang.
+static CharSourceRange
+cleanRangeForAvoidingMacros(CharSourceRange Range, const SourceManager &SM,
+const LangOptions &LangOpts) {
+  if (Range.isTokenRange()) {
+auto B =
+getExpansionForNestedTemplateGreater(Range.getBegin(), SM, LangOpts);
+auto E = getExpansionForNestedTemplateGreater(Range.getEnd(), SM, 
LangOpts);
+if (E) {
+  // We can't use the expansion location with a token range, because that
+  // will lex as `>>`. So we instead convert to a char range.
+  return CharSourceRange::getCharRange(B.value_or(Range.getBegin()),
+   E->getLocWithOffset(1));
+} else if (B) {
+  return CharSourceRange::getTokenRange(*B, Range.getEnd());
+}
+  }
+  return Range;
+}
+
 static CharSourceRange getRange(const CharSourceRange &EditRange,
 const SourceManager &SM,
 const LangOptions &LangOpts,
@@ -109,13 +159,14 @@ static CharSourceRange getRange(const CharSourceRange 
&EditRange,
   if (IncludeMacroExpansion) {
 Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
-if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
-spelledInMacroDefinition(EditRange.getEnd(), SM))
+auto AdjustedRange = cleanRangeForAvoidingMacros(EditRange, SM, LangOpts);
+if (spelledInMacroDefinition(AdjustedRange.getBegin(), SM) ||
+spelledInMacroDefinition(AdjustedRange.getEnd(), SM))
   return {};
 
-auto B = SM.getSpellingLoc(EditRange.getBegin());
-auto E = SM.getSpellingLoc(EditRange.getEnd());
-if (EditRange.isTokenRange())
+auto B = SM.getSpellingLoc(AdjustedRange.getBegin());
+auto E = SM.getSpellingLoc(AdjustedRange.getEnd());
+if (AdjustedRange.isTokenRange())
   E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
 Range = CharSourceRange::getCharRange(B, E);
   }
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp 
b/clang/unittests/Tooling/SourceCodeTest.cpp
index 3641d2ee453f4a..def9701ccdf1e0 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -50,6 +50,15 @@ struct CallsVisitor : TestVisitor {
   std::function OnCall;
 };
 
+struct TypeLocVisitor : 

[clang] [libTooling] Fix `getFileRangeForEdit` for inner nested template types (PR #87673)

2024-04-04 Thread Eric Li via cfe-commits

https://github.com/tJener edited https://github.com/llvm/llvm-project/pull/87673
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [libTooling] Fix `getFileRangeForEdit` for inner nested template types (PR #87673)

2024-04-05 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/87673

>From 697551dd155cd8ada0d93ce8aa2951a9f9f3d4b4 Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Thu, 4 Apr 2024 14:04:25 -0400
Subject: [PATCH 1/3] [libTooling] Fix `getFileRangeForEdit` for inner nested
 template types

When there is `>>` in source from the right brackets of a nested
template, the end location of the inner template points intto a
scratch space with a single `>` token. This prevents the lexer from
seeing the `>>` token in the original source.

However, this causes the range to appear to be partially in a macro,
and is problematic if we are trying to avoid ranges with any macro
expansions.

This change detects this odd case in token ranges, converting it to
the corresponding character range without the expansion.
---
 clang/lib/Tooling/Transformer/SourceCode.cpp | 61 ++--
 clang/unittests/Tooling/SourceCodeTest.cpp   | 29 ++
 2 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/SourceCode.cpp 
b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 6aae834b0db563..114f6ba4833cb0 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -101,6 +101,56 @@ static bool spelledInMacroDefinition(SourceLocation Loc,
   return false;
 }
 
+// Returns the expansion loc of `Loc` if `Loc` is the location of the `>` token
+// of an inner nested template, where the inner and outer templates end 
brackets
+// are spelled as `>>`.
+//
+// Clang handles the `>>` in nested templates by placing the `SourceLocation`
+// of the inner template end bracket in scratch space. This forces it to be a
+// separate token (otherwise it would be lexed as `>>`), but that means it also
+// looks like a macro.
+static std::optional getExpansionForNestedTemplateGreater(
+SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) {
+  if (Loc.isMacroID()) {
+auto SpellingLoc = SM.getSpellingLoc(Loc);
+auto ExpansionLoc = SM.getExpansionLoc(Loc);
+Token SpellingTok, ExpansionTok;
+if (Lexer::getRawToken(SpellingLoc, SpellingTok, SM, LangOpts,
+   /*IgnoreWhiteSpace=*/false)) {
+  return std::nullopt;
+}
+if (Lexer::getRawToken(ExpansionLoc, ExpansionTok, SM, LangOpts,
+   /*IgnoreWhiteSpace=*/false)) {
+  return std::nullopt;
+}
+if (SpellingTok.getKind() == tok::greater &&
+ExpansionTok.getKind() == tok::greatergreater) {
+  return ExpansionLoc;
+}
+  }
+  return std::nullopt;
+}
+
+// Returns `Range`, but adjusted to smooth over oddities introduced by Clang.
+static CharSourceRange
+cleanRangeForAvoidingMacros(CharSourceRange Range, const SourceManager &SM,
+const LangOptions &LangOpts) {
+  if (Range.isTokenRange()) {
+auto B =
+getExpansionForNestedTemplateGreater(Range.getBegin(), SM, LangOpts);
+auto E = getExpansionForNestedTemplateGreater(Range.getEnd(), SM, 
LangOpts);
+if (E) {
+  // We can't use the expansion location with a token range, because that
+  // will lex as `>>`. So we instead convert to a char range.
+  return CharSourceRange::getCharRange(B.value_or(Range.getBegin()),
+   E->getLocWithOffset(1));
+} else if (B) {
+  return CharSourceRange::getTokenRange(*B, Range.getEnd());
+}
+  }
+  return Range;
+}
+
 static CharSourceRange getRange(const CharSourceRange &EditRange,
 const SourceManager &SM,
 const LangOptions &LangOpts,
@@ -109,13 +159,14 @@ static CharSourceRange getRange(const CharSourceRange 
&EditRange,
   if (IncludeMacroExpansion) {
 Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
-if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
-spelledInMacroDefinition(EditRange.getEnd(), SM))
+auto AdjustedRange = cleanRangeForAvoidingMacros(EditRange, SM, LangOpts);
+if (spelledInMacroDefinition(AdjustedRange.getBegin(), SM) ||
+spelledInMacroDefinition(AdjustedRange.getEnd(), SM))
   return {};
 
-auto B = SM.getSpellingLoc(EditRange.getBegin());
-auto E = SM.getSpellingLoc(EditRange.getEnd());
-if (EditRange.isTokenRange())
+auto B = SM.getSpellingLoc(AdjustedRange.getBegin());
+auto E = SM.getSpellingLoc(AdjustedRange.getEnd());
+if (AdjustedRange.isTokenRange())
   E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
 Range = CharSourceRange::getCharRange(B, E);
   }
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp 
b/clang/unittests/Tooling/SourceCodeTest.cpp
index 3641d2ee453f4a..def9701ccdf1e0 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -50,6 +50,15 @@ struct CallsVisitor : TestVisitor {
   std::function OnCall;
 };
 
+struct TypeLocVisitor : 

[clang] [libTooling] Fix `getFileRangeForEdit` for inner nested template types (PR #87673)

2024-04-05 Thread Eric Li via cfe-commits


@@ -510,6 +519,26 @@ int c = M3(3);
   Visitor.runOver(Code.code());
 }
 
+TEST(SourceCodeTest, InnerNestedTemplate) {

tJener wrote:

Done.

https://github.com/llvm/llvm-project/pull/87673
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [libTooling] Fix `getFileRangeForEdit` for inner nested template types (PR #87673)

2024-04-05 Thread Eric Li via cfe-commits

https://github.com/tJener closed https://github.com/llvm/llvm-project/pull/87673
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-14 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/139990

>From c7f23d4280de89810606b5c34d7fb8d03d7a9c3f Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 14 May 2025 21:21:07 -0400
Subject: [PATCH] [libTooling] Fix `constructExprArgs` for direct-init and
 implicit construction

Use `getParenOrBraceRange()` to get the location of the opening paren
or braces instead of searching backwards from the first argument.

For implicit construction, get the range surrounding the first and
last arguments.
---
 .../lib/Tooling/Transformer/RangeSelector.cpp | 73 ---
 clang/unittests/Tooling/RangeSelectorTest.cpp | 43 +++
 2 files changed, 89 insertions(+), 27 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/RangeSelector.cpp 
b/clang/lib/Tooling/Transformer/RangeSelector.cpp
index e84ddde74a707..73e6109da178e 100644
--- a/clang/lib/Tooling/Transformer/RangeSelector.cpp
+++ b/clang/lib/Tooling/Transformer/RangeSelector.cpp
@@ -281,49 +281,68 @@ RangeSelector transformer::statements(std::string ID) {
 
 namespace {
 
-SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }
-
-SourceLocation getRLoc(const CXXConstructExpr &E) {
-  return E.getParenOrBraceRange().getEnd();
-}
-
-tok::TokenKind getStartToken(const CallExpr &E) {
-  return tok::TokenKind::l_paren;
-}
-
-tok::TokenKind getStartToken(const CXXConstructExpr &E) {
-  return isa(E) ? tok::TokenKind::l_paren
-: tok::TokenKind::l_brace;
-}
-
-template 
-SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation 
RLoc,
+SourceLocation findArgStartDelimiter(const CallExpr &E, SourceLocation RLoc,
  const SourceManager &SM,
  const LangOptions &LangOpts) {
   SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();
-  return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));
+  return findPreviousTokenKind(Loc, SM, LangOpts, tok::TokenKind::l_paren);
 }
-// Returns the range of the source between the call's or construct expr's
-// parentheses/braces.
-template 
-CharSourceRange getArgumentsRange(const MatchResult &Result,
-  const ExprWithArgs &CE) {
-  const SourceLocation RLoc = getRLoc(CE);
+
+// Returns the location after the last argument of the construct expr. Returns
+// an invalid location if there are no arguments.
+SourceLocation findLastArgEnd(const CXXConstructExpr &CE,
+  const SourceManager &SM,
+  const LangOptions &LangOpts) {
+  for (int i = CE.getNumArgs() - 1; i >= 0; --i) {
+const Expr *Arg = CE.getArg(i);
+if (isa(Arg))
+  continue;
+return Lexer::getLocForEndOfToken(Arg->getEndLoc(), 0, SM, LangOpts);
+  }
+  return {};
+}
+
+// Returns the range of the source between the call's parentheses/braces.
+CharSourceRange getCallArgumentsRange(const MatchResult &Result,
+  const CallExpr &CE) {
+  const SourceLocation RLoc = CE.getRParenLoc();
   return CharSourceRange::getCharRange(
   findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
 Result.Context->getLangOpts())
   .getLocWithOffset(1),
   RLoc);
 }
+
+// Returns the range of the source between the construct expr's
+// parentheses/braces.
+CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
+   const CXXConstructExpr &CE) {
+  if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
+return CharSourceRange::getCharRange(
+Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
+   Result.Context->getLangOpts()),
+R.getEnd());
+  }
+
+  if (CE.getNumArgs() > 0) {
+return CharSourceRange::getCharRange(
+CE.getArg(0)->getBeginLoc(),
+findLastArgEnd(CE, *Result.SourceManager,
+   Result.Context->getLangOpts()));
+  }
+
+  return {};
+}
+
 } // namespace
 
 RangeSelector transformer::callArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(std::move(ID));
 }
 
 RangeSelector transformer::constructExprArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(
+  std::move(ID));
 }
 
 namespace {
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp 
b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 1bccf899f3f19..7abaa73b09e2c 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -693,6 +693,49 @@ TEST(RangeSelectorTest, ConstructExprNoArgs) {
   EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue(""));
 }
 
+TEST(RangeSelectorTest, ConstructExprArgsDirectInitialization) {
+  const StringRef Code = R"cc(
+struct C {
+  C(int, int);
+};
+void f() {

[clang] [libTooling] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-14 Thread Eric Li via cfe-commits

https://github.com/tJener edited 
https://github.com/llvm/llvm-project/pull/139990
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-14 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/139990



  



Rate limit · GitHub


  body {
background-color: #f6f8fa;
color: #24292e;
font-family: -apple-system,BlinkMacSystemFont,Segoe 
UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
font-size: 14px;
line-height: 1.5;
margin: 0;
  }

  .container { margin: 50px auto; max-width: 600px; text-align: center; 
padding: 0 24px; }

  a { color: #0366d6; text-decoration: none; }
  a:hover { text-decoration: underline; }

  h1 { line-height: 60px; font-size: 48px; font-weight: 300; margin: 0px; 
text-shadow: 0 1px 0 #fff; }
  p { color: rgba(0, 0, 0, 0.5); margin: 20px 0 40px; }

  ul { list-style: none; margin: 25px 0; padding: 0; }
  li { display: table-cell; font-weight: bold; width: 1%; }

  .logo { display: inline-block; margin-top: 35px; }
  .logo-img-2x { display: none; }
  @media
  only screen and (-webkit-min-device-pixel-ratio: 2),
  only screen and (   min--moz-device-pixel-ratio: 2),
  only screen and ( -o-min-device-pixel-ratio: 2/1),
  only screen and (min-device-pixel-ratio: 2),
  only screen and (min-resolution: 192dpi),
  only screen and (min-resolution: 2dppx) {
.logo-img-1x { display: none; }
.logo-img-2x { display: inline-block; }
  }

  #suggestions {
margin-top: 35px;
color: #ccc;
  }
  #suggestions a {
color: #66;
font-weight: 200;
font-size: 14px;
margin: 0 10px;
  }


  
  



  Whoa there!
  You have exceeded a secondary rate limit.
Please wait a few minutes before you try again;
in some cases this may take up to an hour.
  
  
https://support.github.com/contact";>Contact Support —
https://githubstatus.com";>GitHub Status —
https://twitter.com/githubstatus";>@githubstatus
  

  

  

  

  

  


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


[clang] [libTooling] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-15 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/139990

>From c7f23d4280de89810606b5c34d7fb8d03d7a9c3f Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 14 May 2025 21:21:07 -0400
Subject: [PATCH 1/2] [libTooling] Fix `constructExprArgs` for direct-init and
 implicit construction

Use `getParenOrBraceRange()` to get the location of the opening paren
or braces instead of searching backwards from the first argument.

For implicit construction, get the range surrounding the first and
last arguments.
---
 .../lib/Tooling/Transformer/RangeSelector.cpp | 73 ---
 clang/unittests/Tooling/RangeSelectorTest.cpp | 43 +++
 2 files changed, 89 insertions(+), 27 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/RangeSelector.cpp 
b/clang/lib/Tooling/Transformer/RangeSelector.cpp
index e84ddde74a707..73e6109da178e 100644
--- a/clang/lib/Tooling/Transformer/RangeSelector.cpp
+++ b/clang/lib/Tooling/Transformer/RangeSelector.cpp
@@ -281,49 +281,68 @@ RangeSelector transformer::statements(std::string ID) {
 
 namespace {
 
-SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }
-
-SourceLocation getRLoc(const CXXConstructExpr &E) {
-  return E.getParenOrBraceRange().getEnd();
-}
-
-tok::TokenKind getStartToken(const CallExpr &E) {
-  return tok::TokenKind::l_paren;
-}
-
-tok::TokenKind getStartToken(const CXXConstructExpr &E) {
-  return isa(E) ? tok::TokenKind::l_paren
-: tok::TokenKind::l_brace;
-}
-
-template 
-SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation 
RLoc,
+SourceLocation findArgStartDelimiter(const CallExpr &E, SourceLocation RLoc,
  const SourceManager &SM,
  const LangOptions &LangOpts) {
   SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();
-  return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));
+  return findPreviousTokenKind(Loc, SM, LangOpts, tok::TokenKind::l_paren);
 }
-// Returns the range of the source between the call's or construct expr's
-// parentheses/braces.
-template 
-CharSourceRange getArgumentsRange(const MatchResult &Result,
-  const ExprWithArgs &CE) {
-  const SourceLocation RLoc = getRLoc(CE);
+
+// Returns the location after the last argument of the construct expr. Returns
+// an invalid location if there are no arguments.
+SourceLocation findLastArgEnd(const CXXConstructExpr &CE,
+  const SourceManager &SM,
+  const LangOptions &LangOpts) {
+  for (int i = CE.getNumArgs() - 1; i >= 0; --i) {
+const Expr *Arg = CE.getArg(i);
+if (isa(Arg))
+  continue;
+return Lexer::getLocForEndOfToken(Arg->getEndLoc(), 0, SM, LangOpts);
+  }
+  return {};
+}
+
+// Returns the range of the source between the call's parentheses/braces.
+CharSourceRange getCallArgumentsRange(const MatchResult &Result,
+  const CallExpr &CE) {
+  const SourceLocation RLoc = CE.getRParenLoc();
   return CharSourceRange::getCharRange(
   findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
 Result.Context->getLangOpts())
   .getLocWithOffset(1),
   RLoc);
 }
+
+// Returns the range of the source between the construct expr's
+// parentheses/braces.
+CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
+   const CXXConstructExpr &CE) {
+  if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
+return CharSourceRange::getCharRange(
+Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
+   Result.Context->getLangOpts()),
+R.getEnd());
+  }
+
+  if (CE.getNumArgs() > 0) {
+return CharSourceRange::getCharRange(
+CE.getArg(0)->getBeginLoc(),
+findLastArgEnd(CE, *Result.SourceManager,
+   Result.Context->getLangOpts()));
+  }
+
+  return {};
+}
+
 } // namespace
 
 RangeSelector transformer::callArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(std::move(ID));
 }
 
 RangeSelector transformer::constructExprArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(
+  std::move(ID));
 }
 
 namespace {
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp 
b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 1bccf899f3f19..7abaa73b09e2c 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -693,6 +693,49 @@ TEST(RangeSelectorTest, ConstructExprNoArgs) {
   EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue(""));
 }
 
+TEST(RangeSelectorTest, ConstructExprArgsDirectInitialization) {
+  const StringRef Code = R"cc(
+struct C {
+  C(int, int);
+};
+void f(

[clang] [libTooling] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-15 Thread Eric Li via cfe-commits

https://github.com/tJener updated 
https://github.com/llvm/llvm-project/pull/139990

>From c7f23d4280de89810606b5c34d7fb8d03d7a9c3f Mon Sep 17 00:00:00 2001
From: Eric Li 
Date: Wed, 14 May 2025 21:21:07 -0400
Subject: [PATCH 1/3] [libTooling] Fix `constructExprArgs` for direct-init and
 implicit construction

Use `getParenOrBraceRange()` to get the location of the opening paren
or braces instead of searching backwards from the first argument.

For implicit construction, get the range surrounding the first and
last arguments.
---
 .../lib/Tooling/Transformer/RangeSelector.cpp | 73 ---
 clang/unittests/Tooling/RangeSelectorTest.cpp | 43 +++
 2 files changed, 89 insertions(+), 27 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/RangeSelector.cpp 
b/clang/lib/Tooling/Transformer/RangeSelector.cpp
index e84ddde74a707..73e6109da178e 100644
--- a/clang/lib/Tooling/Transformer/RangeSelector.cpp
+++ b/clang/lib/Tooling/Transformer/RangeSelector.cpp
@@ -281,49 +281,68 @@ RangeSelector transformer::statements(std::string ID) {
 
 namespace {
 
-SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }
-
-SourceLocation getRLoc(const CXXConstructExpr &E) {
-  return E.getParenOrBraceRange().getEnd();
-}
-
-tok::TokenKind getStartToken(const CallExpr &E) {
-  return tok::TokenKind::l_paren;
-}
-
-tok::TokenKind getStartToken(const CXXConstructExpr &E) {
-  return isa(E) ? tok::TokenKind::l_paren
-: tok::TokenKind::l_brace;
-}
-
-template 
-SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation 
RLoc,
+SourceLocation findArgStartDelimiter(const CallExpr &E, SourceLocation RLoc,
  const SourceManager &SM,
  const LangOptions &LangOpts) {
   SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();
-  return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));
+  return findPreviousTokenKind(Loc, SM, LangOpts, tok::TokenKind::l_paren);
 }
-// Returns the range of the source between the call's or construct expr's
-// parentheses/braces.
-template 
-CharSourceRange getArgumentsRange(const MatchResult &Result,
-  const ExprWithArgs &CE) {
-  const SourceLocation RLoc = getRLoc(CE);
+
+// Returns the location after the last argument of the construct expr. Returns
+// an invalid location if there are no arguments.
+SourceLocation findLastArgEnd(const CXXConstructExpr &CE,
+  const SourceManager &SM,
+  const LangOptions &LangOpts) {
+  for (int i = CE.getNumArgs() - 1; i >= 0; --i) {
+const Expr *Arg = CE.getArg(i);
+if (isa(Arg))
+  continue;
+return Lexer::getLocForEndOfToken(Arg->getEndLoc(), 0, SM, LangOpts);
+  }
+  return {};
+}
+
+// Returns the range of the source between the call's parentheses/braces.
+CharSourceRange getCallArgumentsRange(const MatchResult &Result,
+  const CallExpr &CE) {
+  const SourceLocation RLoc = CE.getRParenLoc();
   return CharSourceRange::getCharRange(
   findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
 Result.Context->getLangOpts())
   .getLocWithOffset(1),
   RLoc);
 }
+
+// Returns the range of the source between the construct expr's
+// parentheses/braces.
+CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
+   const CXXConstructExpr &CE) {
+  if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
+return CharSourceRange::getCharRange(
+Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
+   Result.Context->getLangOpts()),
+R.getEnd());
+  }
+
+  if (CE.getNumArgs() > 0) {
+return CharSourceRange::getCharRange(
+CE.getArg(0)->getBeginLoc(),
+findLastArgEnd(CE, *Result.SourceManager,
+   Result.Context->getLangOpts()));
+  }
+
+  return {};
+}
+
 } // namespace
 
 RangeSelector transformer::callArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(std::move(ID));
 }
 
 RangeSelector transformer::constructExprArgs(std::string ID) {
-  return RelativeSelector>(std::move(ID));
+  return RelativeSelector(
+  std::move(ID));
 }
 
 namespace {
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp 
b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 1bccf899f3f19..7abaa73b09e2c 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -693,6 +693,49 @@ TEST(RangeSelectorTest, ConstructExprNoArgs) {
   EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue(""));
 }
 
+TEST(RangeSelectorTest, ConstructExprArgsDirectInitialization) {
+  const StringRef Code = R"cc(
+struct C {
+  C(int, int);
+};
+void f(

[clang] [libTooling] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)

2025-05-15 Thread Eric Li via cfe-commits

tJener wrote:

5c57e88 should fix the test failure. Forgot that they are sometimes run in 
pre-C++17, so there was an extra elidable `CXXConstructExpr`.

https://github.com/llvm/llvm-project/pull/139990
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits