[PATCH] D124486: [clangd] ExtractVariable support for C and Objective-C

Sam McCall via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 28 11:27:20 PDT 2022


sammccall added a comment.

Ok, this looks pretty good to me! Just nits



================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:170
+  if (E->hasPlaceholderType(BuiltinType::PseudoObject)) {
+    if (const auto *PR = dyn_cast<ObjCPropertyRefExpr>(E)) {
+      if (PR->isMessagingSetter()) {
----------------
dgoldman wrote:
> sammccall wrote:
> > E->getObjCProperty()?
> Hmm I'm not sure when that would be useful looking at the logic there, can you give an example just to make sure I understand what it would handle?
it's similar to the cast you have, but in addition to handling `foo.bar` it handles parens like `(foo.bar)`, commas like `(0, foo.bar)` and casts. All together I think this covers more cases where a pseudo-object can be used as an lvalue and potentially modified.


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:208
+  std::string ExtractedVarDecl =
+      Type + " " + VarName.str() + " = " + ExtractionCode.str() + "; ";
   return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
----------------
dgoldman wrote:
> sammccall wrote:
> > Type + Varname isn't correct for types in general, particularly those that use declarator syntax.
> > printType with varname as the placeholder should do what you want.
> > 
> > Can you add a testcase where the variable has function pointer type?
> I went ahead and added this but I couldn't think of a way to get a result like this without enabling this for MemberExprs, so I went ahead and re-enabled for them since I think it could be useful for them. LMK if I should revert that change. I guess if we don't want to support MemberExprs I should disable the ObjC property support since it probably makes to match the C++ equivalent?
Try:

```
int (*foo(char))(int);
void bar() {
  (void)foo('c');
}
```

extracting foo('c') to subexpr should yield `int (*placeholder)(int) = foo('c');`


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:54
   const clang::Expr *Expr;
+  llvm::Optional<QualType> ExprType;
   const SelectionTree::Node *ExprNode;
----------------
just QualType, it's internally nullable already
(some value in being explicit, but the code below handling two different null values is more confusing imo)

And this is more clearly VarType - it's the type you intend for the variable, and it's not necessarily the type of the extracted expression (see pseudo-object stuff)



================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:95
     Extractable = true;
+  ExprType = Expr->getType();
+  // No need to fix up the type if we're just going to use "auto" later on.
----------------
sorry, please do pull this back out into a function

(my question was just can this be a function rather than a method, it seemed self-contained)


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:97
+  // No need to fix up the type if we're just going to use "auto" later on.
+  if (Ctx.getLangOpts().CPlusPlus11)
+    return;
----------------
If you set ExprType to Ctx.getAutoDeductType(), I think you can avoid treating this as a special case when printing


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:99
+    return;
+  if (ExprType->getTypePtrOrNull() && Expr->hasPlaceholderType(BuiltinType::PseudoObject)) {
+    if (const auto *PR = dyn_cast<ObjCPropertyRefExpr>(Expr)) {
----------------
nit: ExprType->getTypePtrOrNull() as a boolean is more directly !ExprType->isNull()


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:374
   // FIXME: really? What if it's e.g. `std::is_same<void, void>::value`?
-  if (llvm::isa<DeclRefExpr>(E) || llvm::isa<MemberExpr>(E))
     return false;
----------------
The reason MemberExpr is banned is that we don't want to suggest extracting `foo` if it happens to be a member.
You can leave it banned, or allow it but disallow the case where the MemberExpr's base is a CXXThisExpr which is implicit.


================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp:401
+  const Type *ExprType = E->getType().getTypePtrOrNull();
+  if (ExprType && ExprType->isVoidType())
+    return false;
----------------
I'd just change this to `if (!ExprType || ExprType->isVoidType())`

I don't think there's a real null-type case that does anything useful.


================
Comment at: clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp:338
+             })cpp"},
+      // We don't preserve types through typedefs.
+      {R"cpp(typedef long NSInteger;
----------------
This comment is inaccurate: if the expression has type `NSInteger`, that code will be written.

The issue is that the type of the expression **isn't** `NSInteger`, it's just `int`. **arithmetic** doesn't produce typedef types in the way I think you're expecting.

If the expression was something that **was** an `NSInteger` (like a call to a function whose declared return type is NSInteger) then that type would be preserved.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124486



More information about the cfe-commits mailing list