[clang-tools-extra] [clangd] Don't ignore external HFI in `SymbolCollector` (PR #88446)

2024-04-11 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman approved this pull request.


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


[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-03-07 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From b0a2fb25c25ecfb20bb3f0aab2d398ea80caeff2 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/6] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 338 ++
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 380 insertions(+), 66 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 4e135801f6853d..b53c24b8331ddb 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-03-07 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From b0a2fb25c25ecfb20bb3f0aab2d398ea80caeff2 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/5] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 338 ++
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 380 insertions(+), 66 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 4e135801f6853d..b53c24b8331ddb 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Add metric for rename decl kind (PR #83867)

2024-03-05 Thread David Goldman via cfe-commits

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


[clang-tools-extra] [clangd] Add metric for rename decl kind (PR #83867)

2024-03-04 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman created 
https://github.com/llvm/llvm-project/pull/83867

This will give us insight into what users are renaming in practice - for 
instance, try to gauge the impact of the ObjC rename support.

>From 42dddf4a5fbd862bf2f122a6c6216fe0dbd34e54 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Mon, 4 Mar 2024 11:35:46 -0500
Subject: [PATCH] [clangd] Add metric for rename decl kind

This will give us insight into what users are
renaming in practice.
---
 clang-tools-extra/clangd/refactor/Rename.cpp | 4 
 1 file changed, 4 insertions(+)

diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 4e135801f6853d..0cc7eecd6212be 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -1072,6 +1072,10 @@ llvm::Expected rename(const RenameInputs 
) {
   if (Reject)
 return makeError(*Reject);
 
+  static constexpr trace::Metric RenameTriggerCounter(
+  "rename_trigger_count", trace::Metric::Counter, "decl_kind");
+  RenameTriggerCounter.record(1, RenameDecl.getDeclKindName());
+
   // We have two implementations of the rename:
   //   - AST-based rename: used for renaming local symbols, e.g. variables
   // defined in a function body;

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


[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-27 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From b0a2fb25c25ecfb20bb3f0aab2d398ea80caeff2 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/4] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 338 ++
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 380 insertions(+), 66 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 4e135801f6853d..b53c24b8331ddb 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Fix renaming single argument ObjC methods (PR #82396)

2024-02-23 Thread David Goldman via cfe-commits

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


[clang-tools-extra] [clangd] Fix renaming single argument ObjC methods (PR #82396)

2024-02-23 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/82396

>From 8a9c09575ed143e762faa7abf48285ed6b4b0335 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 20 Feb 2024 13:14:26 -0500
Subject: [PATCH 1/3] [clangd] Fix renaming single argument ObjC methods

Add a few more tests to verify this works (thanks to @ahoppen
for the tests and finding this bug).
---
 clang-tools-extra/clangd/refactor/Rename.cpp  |  15 +-
 .../clangd/unittests/RenameTests.cpp  | 138 ++
 2 files changed, 148 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..1f0e54c19e2be1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -811,8 +811,14 @@ renameWithinFile(ParsedAST , const NamedDecl 
,
   continue;
 Locs.push_back(RenameLoc);
   }
-  if (const auto *MD = dyn_cast())
-return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+  if (const auto *MD = dyn_cast()) {
+if (MD->getSelector().getNumArgs() > 1)
+  return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+
+// Eat trailing : for single argument methods since they're actually
+// considered a separate token during rename.
+NewName.consume_back(":");
+  }
   for (const auto  : Locs) {
 if (auto Err = FilteredChanges.add(tooling::Replacement(
 SM, CharSourceRange::getTokenRange(Loc), NewName)))
@@ -930,10 +936,9 @@ renameOutsideFile(const NamedDecl , 
llvm::StringRef MainFilePath,
 std::optional Selector = std::nullopt;
 llvm::SmallVector NewNames;
 if (const auto *MD = dyn_cast()) {
-  if (MD->getSelector().getNumArgs() > 1) {
-RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  if (MD->getSelector().getNumArgs() > 1)
 Selector = MD->getSelector();
-  }
 }
 NewName.split(NewNames, ":");
 
diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index d91ef85d672711..7d9252110b27df 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1943,6 +1943,144 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
   }
 }
 
+TEST(CrossFileRenameTests, ObjC) {
+  MockCompilationDatabase CDB;
+  CDB.ExtraClangFlags = {"-xobjective-c"};
+  // rename is runnning on all "^" points in FooH.
+  struct Case {
+llvm::StringRef FooH;
+llvm::StringRef FooM;
+llvm::StringRef NewName;
+llvm::StringRef ExpectedFooH;
+llvm::StringRef ExpectedFooM;
+  };
+  Case Cases[] = {// --- Zero arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction {
+  [self performAction];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction {
+  [self performNewAction];
+}
+@end
+  )cpp",
+  },
+  // --- Single arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction:(int)action {
+  [self performAction:action];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction:",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction:(int)action {
+  [self performNewAction:action];
+}
+@end
+  )cpp",
+  },
+  // --- Multi arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action with:(int)value;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction:(int)action with:(int)value {
+  [self performAction:action with:value];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction:by:",
+  // Expected
+ 

[clang-tools-extra] [clangd] Fix renaming single argument ObjC methods (PR #82396)

2024-02-22 Thread David Goldman via cfe-commits


@@ -811,8 +811,14 @@ renameWithinFile(ParsedAST , const NamedDecl 
,
   continue;
 Locs.push_back(RenameLoc);
   }
-  if (const auto *MD = dyn_cast())
-return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+  if (const auto *MD = dyn_cast()) {
+if (MD->getSelector().getNumArgs() > 1)

DavidGoldman wrote:

Done. It does work for the one arg selector case but I thought we might as well 
use the simpler logic.

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


[clang-tools-extra] [clangd] Fix renaming single argument ObjC methods (PR #82396)

2024-02-22 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/82396

>From 8a9c09575ed143e762faa7abf48285ed6b4b0335 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 20 Feb 2024 13:14:26 -0500
Subject: [PATCH 1/2] [clangd] Fix renaming single argument ObjC methods

Add a few more tests to verify this works (thanks to @ahoppen
for the tests and finding this bug).
---
 clang-tools-extra/clangd/refactor/Rename.cpp  |  15 +-
 .../clangd/unittests/RenameTests.cpp  | 138 ++
 2 files changed, 148 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..1f0e54c19e2be1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -811,8 +811,14 @@ renameWithinFile(ParsedAST , const NamedDecl 
,
   continue;
 Locs.push_back(RenameLoc);
   }
-  if (const auto *MD = dyn_cast())
-return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+  if (const auto *MD = dyn_cast()) {
+if (MD->getSelector().getNumArgs() > 1)
+  return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+
+// Eat trailing : for single argument methods since they're actually
+// considered a separate token during rename.
+NewName.consume_back(":");
+  }
   for (const auto  : Locs) {
 if (auto Err = FilteredChanges.add(tooling::Replacement(
 SM, CharSourceRange::getTokenRange(Loc), NewName)))
@@ -930,10 +936,9 @@ renameOutsideFile(const NamedDecl , 
llvm::StringRef MainFilePath,
 std::optional Selector = std::nullopt;
 llvm::SmallVector NewNames;
 if (const auto *MD = dyn_cast()) {
-  if (MD->getSelector().getNumArgs() > 1) {
-RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  if (MD->getSelector().getNumArgs() > 1)
 Selector = MD->getSelector();
-  }
 }
 NewName.split(NewNames, ":");
 
diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index d91ef85d672711..7d9252110b27df 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1943,6 +1943,144 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
   }
 }
 
+TEST(CrossFileRenameTests, ObjC) {
+  MockCompilationDatabase CDB;
+  CDB.ExtraClangFlags = {"-xobjective-c"};
+  // rename is runnning on all "^" points in FooH.
+  struct Case {
+llvm::StringRef FooH;
+llvm::StringRef FooM;
+llvm::StringRef NewName;
+llvm::StringRef ExpectedFooH;
+llvm::StringRef ExpectedFooM;
+  };
+  Case Cases[] = {// --- Zero arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction {
+  [self performAction];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction {
+  [self performNewAction];
+}
+@end
+  )cpp",
+  },
+  // --- Single arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction:(int)action {
+  [self performAction:action];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction:",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction:(int)action {
+  [self performNewAction:action];
+}
+@end
+  )cpp",
+  },
+  // --- Multi arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action with:(int)value;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction:(int)action with:(int)value {
+  [self performAction:action with:value];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction:by:",
+  // Expected
+ 

[clang-tools-extra] [clangd] Fix renaming single argument ObjC methods (PR #82396)

2024-02-20 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman created 
https://github.com/llvm/llvm-project/pull/82396

Use the legacy non-ObjC rename logic when dealing with selectors that have zero 
or one arguments. In addition, make sure we don't add an extra `:` during the 
rename.

Add a few more tests to verify this works (thanks to @ahoppen for the tests and 
finding this bug).

>From 8a9c09575ed143e762faa7abf48285ed6b4b0335 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 20 Feb 2024 13:14:26 -0500
Subject: [PATCH] [clangd] Fix renaming single argument ObjC methods

Add a few more tests to verify this works (thanks to @ahoppen
for the tests and finding this bug).
---
 clang-tools-extra/clangd/refactor/Rename.cpp  |  15 +-
 .../clangd/unittests/RenameTests.cpp  | 138 ++
 2 files changed, 148 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..1f0e54c19e2be1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -811,8 +811,14 @@ renameWithinFile(ParsedAST , const NamedDecl 
,
   continue;
 Locs.push_back(RenameLoc);
   }
-  if (const auto *MD = dyn_cast())
-return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+  if (const auto *MD = dyn_cast()) {
+if (MD->getSelector().getNumArgs() > 1)
+  return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
+
+// Eat trailing : for single argument methods since they're actually
+// considered a separate token during rename.
+NewName.consume_back(":");
+  }
   for (const auto  : Locs) {
 if (auto Err = FilteredChanges.add(tooling::Replacement(
 SM, CharSourceRange::getTokenRange(Loc), NewName)))
@@ -930,10 +936,9 @@ renameOutsideFile(const NamedDecl , 
llvm::StringRef MainFilePath,
 std::optional Selector = std::nullopt;
 llvm::SmallVector NewNames;
 if (const auto *MD = dyn_cast()) {
-  if (MD->getSelector().getNumArgs() > 1) {
-RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  if (MD->getSelector().getNumArgs() > 1)
 Selector = MD->getSelector();
-  }
 }
 NewName.split(NewNames, ":");
 
diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index d91ef85d672711..7d9252110b27df 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1943,6 +1943,144 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
   }
 }
 
+TEST(CrossFileRenameTests, ObjC) {
+  MockCompilationDatabase CDB;
+  CDB.ExtraClangFlags = {"-xobjective-c"};
+  // rename is runnning on all "^" points in FooH.
+  struct Case {
+llvm::StringRef FooH;
+llvm::StringRef FooM;
+llvm::StringRef NewName;
+llvm::StringRef ExpectedFooH;
+llvm::StringRef ExpectedFooM;
+  };
+  Case Cases[] = {// --- Zero arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction {
+  [self performAction];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction {
+  [self performNewAction];
+}
+@end
+  )cpp",
+  },
+  // --- Single arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performAction:(int)action {
+  [self performAction:action];
+}
+@end
+  )cpp",
+  // New name
+  "performNewAction:",
+  // Expected
+  R"cpp(
+@interface Foo
+- (int)performNewAction:(int)action;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+- (int)performNewAction:(int)action {
+  [self performNewAction:action];
+}
+@end
+  )cpp",
+  },
+  // --- Multi arg selector
+  {
+  // Input
+  R"cpp(
+@interface Foo
+- (int)performA^ction:(int)action with:(int)value;
+@end
+  )cpp",
+  R"cpp(
+@implementation Foo
+  

[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-20 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From 7a3859fc5f5f52fb283fd0b2feda57521ca88240 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/4] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 329 +++---
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 381 insertions(+), 56 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..8a3d664f8cb9b1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-16 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From 7a3859fc5f5f52fb283fd0b2feda57521ca88240 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/3] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 329 +++---
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 381 insertions(+), 56 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..8a3d664f8cb9b1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-16 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From 7a3859fc5f5f52fb283fd0b2feda57521ca88240 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH 1/2] Add support for ObjC property renaming + implicit
 property renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 329 +++---
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 381 insertions(+), 56 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..8a3d664f8cb9b1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive 

[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-16 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/81775

>From 7a3859fc5f5f52fb283fd0b2feda57521ca88240 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Fri, 26 Jan 2024 15:50:11 -0500
Subject: [PATCH] Add support for ObjC property renaming + implicit property
 renaming

Objective-C properties can generate multiple decls:
- an ivar decl
- a getter method decl
- a setter method decl

Triggering the rename from any of those decls should trigger
a rename of the property and use the proper name for each one
according to the ObjC property naming conventions.

I've added similar support for implicit properties, which
is when a getter or setter like method is referred to via
the property syntax (self.method) without an explicit
property decl.
---
 clang-tools-extra/clangd/FindTarget.cpp   |  16 +
 clang-tools-extra/clangd/refactor/Rename.cpp  | 329 +++---
 .../clangd/unittests/RenameTests.cpp  |  90 +
 .../unittests/SemanticHighlightingTests.cpp   |   2 +-
 4 files changed, 381 insertions(+), 56 deletions(-)

diff --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e702c6b3537a09..9def867011f696 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -740,6 +740,22 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OIMD}});
 }
+
+void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *OPID) {
+  // Skiped compiler synthesized property impl decls - they will always
+  // have an invalid loc.
+  if (OPID->getLocation().isInvalid())
+return;
+  if (OPID->isIvarNameSpecified())
+Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+OPID->getPropertyIvarDeclLoc(),
+/*IsDecl=*/false,
+{OPID->getPropertyIvarDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OPID->getLocation(),
+  /*IsDecl=*/false,
+  {OPID->getPropertyDecl()}});
+}
   };
 
   Visitor V{Resolver};
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 650862c99bcd12..8a3d664f8cb9b1 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/CharInfo.h"
@@ -153,8 +154,111 @@ const NamedDecl *pickInterestingTarget(const NamedDecl 
*D) {
   return D;
 }
 
-llvm::DenseSet locateDeclAt(ParsedAST ,
-   SourceLocation TokenStartLoc) {
+// Some AST nodes are synthesized by the compiler based on other nodes. e.g.
+// ObjC methods and ivars can be synthesized based on an Objective-C property.
+//
+// We perform this check outside of canonicalization since we need to know 
which
+// decl the user has actually triggered the rename on in order to remap all
+// derived decls properly, since the naming patterns can slightly differ for
+// every decl that the compiler synthesizes.
+const NamedDecl *findOriginDecl(const NamedDecl *D) {
+  if (const auto *MD = dyn_cast(D)) {
+if (const auto *PD = MD->findPropertyDecl(/*CheckOverrides=*/false))
+  // FIXME(davg): We should only map to the protocol if the user hasn't
+  // explicitly given a setter/getter for the method - if they have we
+  // should either fail the rename or support basic 1 arg selector renames.
+  return canonicalRenameDecl(PD);
+  }
+  if (const auto *ID = dyn_cast(D)) {
+for (const auto *PD : ID->getContainingInterface()->properties()) {
+  if (PD->getPropertyIvarDecl() == ID)
+return canonicalRenameDecl(PD);
+}
+  }
+  return D;
+}
+
+std::string propertySetterName(llvm::StringRef PropertyName) {
+  std::string Setter = PropertyName.str();
+  if (!Setter.empty())
+Setter[0] = llvm::toUpper(Setter[0]);
+  return "set" + Setter + ":";
+}
+
+// Returns a non-empty string if valid.
+std::string setterToPropertyName(llvm::StringRef Setter) {
+  std::string Result;
+  if (!Setter.consume_front("set")) {
+return Result;
+  }
+  Setter.consume_back(":"); // Optional.
+  Result = Setter.str();
+  if (!Result.empty())
+Result[0] = llvm::toLower(Result[0]);
+  return Result;
+}
+
+llvm::DenseMap
+computeAllDeclsToNewName(const NamedDecl *Selected, llvm::StringRef NewName,
+ const NamedDecl *Origin) {
+  llvm::DenseMap DeclToName;
+  DeclToName[Selected] = NewName.str();
+
+  if (const auto *PD = dyn_cast(Origin)) {
+// Need to derive the 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-15 Thread David Goldman via cfe-commits

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


[clang-tools-extra] [clangd] Add support for renaming ObjC properties and implicit properties (PR #81775)

2024-02-14 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman created 
https://github.com/llvm/llvm-project/pull/81775

Add support for renaming Objective-C properties. These are special since a 
single property decl could generate 3 different decls - a getter method, a 
setter method, and an instance variable. In addition, support renaming implicit 
properties, e.g. referring to a getter method `- (id)foo;` via `self.foo` or a 
setter method `- (void)setFoo:(id)foo;` via `self.foo =` without an explicit 
property decl.

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/9] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/9] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-08 Thread David Goldman via cfe-commits

DavidGoldman wrote:

Thanks, PTAL, I'll save the remaining comments for follow ups.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-08 Thread David Goldman via cfe-commits


@@ -569,8 +840,13 @@ renameWithinFile(ParsedAST , const NamedDecl 
,
 //   }
 if (!isInsideMainFile(RenameLoc, SM))
   continue;
+Locs.push_back(RenameLoc);
+  }
+  if (const auto *MD = dyn_cast())
+return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));

DavidGoldman wrote:

SGTM, I'll look into this in a follow up (+ add some tests for that).

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-08 Thread David Goldman via cfe-commits


@@ -53,13 +55,34 @@ struct RenameInputs {
 struct RenameResult {
   // The range of the symbol that the user can attempt to rename.
   Range Target;
+  // Placeholder text for the rename operation if non-empty.
+  std::string Placeholder;
   // Rename occurrences for the current main file.
   std::vector LocalChanges;
   // Complete edits for the rename, including LocalChanges.
   // If the full set of changes is unknown, this field is empty.
   FileEdits GlobalChanges;
 };
 
+/// Represents a symbol range where the symbol can potentially have multiple
+/// tokens.
+struct SymbolRange {
+  /// Ranges for the tokens that make up the symbol's name.
+  /// Usually a single range, but there can be multiple ranges if the tokens 
for
+  /// the symbol are split, e.g. ObjC selectors.
+  std::vector Ranges;

DavidGoldman wrote:

That makes sense, I think we might need to improve the empty selector case - 
I'll do that in a follow up.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-08 Thread David Goldman via cfe-commits


@@ -538,11 +564,205 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return llvm::Error::success();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return isSelectorLike(Cur, Next) && Cur.text(SM) == SelectorName;
+}
+
+std::vector findAllSelectorPieces(llvm::ArrayRef Tokens,

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-07 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {
+
+  unsigned NumArgs = Sel.getNumArgs();
+  llvm::SmallVector Closes;
+  SelectorPieces.clear();
+  while (Index < Last) {
+const auto  = Tokens[Index];
+
+if (Closes.empty()) {
+  auto PieceCount = SelectorPieces.size();
+  if (PieceCount < NumArgs &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Sel.getNameForSlot(PieceCount))) {
+// If 'foo:' instead of ':' (empty selector), we need to skip the ':'
+// token after the name.
+if (!Sel.getNameForSlot(PieceCount).empty()) {
+  ++Index;
+}
+SelectorPieces.push_back(
+halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+continue;
+  }
+  // If we've found all pieces but the current token looks like another
+  // selector piece, it means the method being renamed is a strict prefix 
of
+  // the selector we've found - should be skipped.
+  if (SelectorPieces.size() >= NumArgs &&
+  isSelectorLike(Tok, Tokens[Index + 1]))
+return false;
+}
+
+switch (Tok.kind()) {
+case tok::l_square:
+  Closes.push_back(']');
+  break;
+case tok::l_paren:
+  Closes.push_back(')');
+  break;
+case tok::l_brace:
+  Closes.push_back('}');
+  break;
+case tok::r_square:
+  if (Closes.empty())
+return SelectorPieces.size() == NumArgs;
+
+  if (Closes.back() != ']')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_paren:
+  if (Closes.empty() || Closes.back() != ')')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_brace:
+  if (Closes.empty() || Closes.back() != '}')
+return false;
+  Closes.pop_back();
+  break;
+case tok::semi:
+  // top level ; ends all statements.
+  if (Closes.empty())
+return false;
+  break;
+default:
+  break;
+}
+
+++Index;
+  }
+  return false;
+}
+
+/// Collects all ranges of the given identifier/selector in the source code.
+///
+/// If a selector is given, this does a full lex of the given source code in
+/// order to identify all selector fragments (e.g. in method exprs/decls) since
+/// they are non-contiguous.
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+auto IdentifierRanges =
+collectIdentifierRanges(Identifier, Content, LangOpts);
+for (const auto  : IdentifierRanges)
+  Ranges.emplace_back(R);
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  // We track parens and braces to ensure that we don't accidentally try 
parsing
+  // a method declaration or definition which isn't at the top level or similar
+  // looking expressions (e.g. an @selector() expression).
+  unsigned ParenCount = 0;
+  unsigned BraceCount = 0;
+  unsigned NumArgs = Selector->getNumArgs();
+
+  std::vector SelectorPieces;
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+  for (unsigned Index = 0; Index < Last; ++Index) {
+const auto  = Tokens[Index];
+

DavidGoldman wrote:

Done. This lexer logic runs independent of those 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-07 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {
+
+  unsigned NumArgs = Sel.getNumArgs();
+  llvm::SmallVector Closes;

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-07 Thread David Goldman via cfe-commits




DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-06 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/9] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad2..336bc3506bb36 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f32..1d4e1c1d75ea2 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/9] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea2..5c20b950e4eac 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-06 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/8] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/8] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-06 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {
+
+  unsigned NumArgs = Sel.getNumArgs();
+  llvm::SmallVector Closes;
+  SelectorPieces.clear();
+  while (Index < Last) {
+const auto  = Tokens[Index];
+
+if (Closes.empty()) {
+  auto PieceCount = SelectorPieces.size();
+  if (PieceCount < NumArgs &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Sel.getNameForSlot(PieceCount))) {
+// If 'foo:' instead of ':' (empty selector), we need to skip the ':'
+// token after the name.
+if (!Sel.getNameForSlot(PieceCount).empty()) {
+  ++Index;
+}
+SelectorPieces.push_back(
+halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+continue;
+  }
+  // If we've found all pieces but the current token looks like another
+  // selector piece, it means the method being renamed is a strict prefix 
of
+  // the selector we've found - should be skipped.
+  if (SelectorPieces.size() >= NumArgs &&
+  isSelectorLike(Tok, Tokens[Index + 1]))
+return false;
+}
+
+switch (Tok.kind()) {
+case tok::l_square:
+  Closes.push_back(']');
+  break;
+case tok::l_paren:
+  Closes.push_back(')');
+  break;
+case tok::l_brace:
+  Closes.push_back('}');
+  break;
+case tok::r_square:
+  if (Closes.empty())
+return SelectorPieces.size() == NumArgs;
+
+  if (Closes.back() != ']')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_paren:
+  if (Closes.empty() || Closes.back() != ')')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_brace:
+  if (Closes.empty() || Closes.back() != '}')
+return false;
+  Closes.pop_back();
+  break;
+case tok::semi:
+  // top level ; ends all statements.
+  if (Closes.empty())
+return false;
+  break;
+default:
+  break;
+}
+
+++Index;
+  }
+  return false;
+}
+
+/// Collects all ranges of the given identifier/selector in the source code.
+///
+/// If a selector is given, this does a full lex of the given source code in
+/// order to identify all selector fragments (e.g. in method exprs/decls) since
+/// they are non-contiguous.
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+auto IdentifierRanges =
+collectIdentifierRanges(Identifier, Content, LangOpts);
+for (const auto  : IdentifierRanges)
+  Ranges.emplace_back(R);
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  // We track parens and braces to ensure that we don't accidentally try 
parsing
+  // a method declaration or definition which isn't at the top level or similar
+  // looking expressions (e.g. an @selector() expression).
+  unsigned ParenCount = 0;
+  unsigned BraceCount = 0;
+  unsigned NumArgs = Selector->getNumArgs();
+
+  std::vector SelectorPieces;
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);

DavidGoldman wrote:

That's true, although a bit annoying since we need a SM to fetch this and I 
wouldn't want to force that into the caller. I could pass an optional Array ref 
of tokens to use to 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -508,24 +513,46 @@ static bool mayBeValidIdentifier(llvm::StringRef Ident) {
   !isAsciiIdentifierStart(Ident.front(), AllowDollar))
 return false;
   for (char C : Ident) {
+if (AllowColon && C == ':')

DavidGoldman wrote:

This doesn't check the entire selector - only a fragment, so I don't think 
there's a need to check spaces here. AFAIK selectors can have an empty first 
fragment too - any idea where that restriction is from?

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {
+
+  unsigned NumArgs = Sel.getNumArgs();
+  llvm::SmallVector Closes;
+  SelectorPieces.clear();
+  while (Index < Last) {
+const auto  = Tokens[Index];
+
+if (Closes.empty()) {
+  auto PieceCount = SelectorPieces.size();
+  if (PieceCount < NumArgs &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Sel.getNameForSlot(PieceCount))) {
+// If 'foo:' instead of ':' (empty selector), we need to skip the ':'
+// token after the name.
+if (!Sel.getNameForSlot(PieceCount).empty()) {
+  ++Index;
+}
+SelectorPieces.push_back(
+halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+continue;
+  }
+  // If we've found all pieces but the current token looks like another
+  // selector piece, it means the method being renamed is a strict prefix 
of
+  // the selector we've found - should be skipped.
+  if (SelectorPieces.size() >= NumArgs &&
+  isSelectorLike(Tok, Tokens[Index + 1]))
+return false;
+}
+
+switch (Tok.kind()) {
+case tok::l_square:
+  Closes.push_back(']');
+  break;
+case tok::l_paren:
+  Closes.push_back(')');
+  break;
+case tok::l_brace:
+  Closes.push_back('}');
+  break;
+case tok::r_square:
+  if (Closes.empty())
+return SelectorPieces.size() == NumArgs;
+
+  if (Closes.back() != ']')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_paren:
+  if (Closes.empty() || Closes.back() != ')')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_brace:
+  if (Closes.empty() || Closes.back() != '}')
+return false;
+  Closes.pop_back();
+  break;
+case tok::semi:
+  // top level ; ends all statements.
+  if (Closes.empty())
+return false;
+  break;
+default:
+  break;
+}
+
+++Index;
+  }
+  return false;
+}
+
+/// Collects all ranges of the given identifier/selector in the source code.
+///
+/// If a selector is given, this does a full lex of the given source code in
+/// order to identify all selector fragments (e.g. in method exprs/decls) since
+/// they are non-contiguous.
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+auto IdentifierRanges =
+collectIdentifierRanges(Identifier, Content, LangOpts);
+for (const auto  : IdentifierRanges)
+  Ranges.emplace_back(R);
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  // We track parens and braces to ensure that we don't accidentally try 
parsing
+  // a method declaration or definition which isn't at the top level or similar
+  // looking expressions (e.g. an @selector() expression).
+  unsigned ParenCount = 0;
+  unsigned BraceCount = 0;
+  unsigned NumArgs = Selector->getNumArgs();
+
+  std::vector SelectorPieces;
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+  for (unsigned Index = 0; Index < Last; ++Index) {
+const auto  = Tokens[Index];
+
+if (BraceCount == 0 && ParenCount == 0) {
+  auto PieceCount = 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -538,11 +565,254 @@ std::optional checkName(const NamedDecl 
,
 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
 }
   }
-  if (Result)
+  if (Result) {
 InvalidNameMetric.record(1, toString(Result->K));
+return makeError(*Result);
+  }
+  return std::nullopt;
+}
+
+bool isMatchingSelectorName(const syntax::Token , const syntax::Token 
,
+const SourceManager ,
+llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous to avoid
+ // potential conflicts with ternary expression.
+ //
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool isSelectorLike(const syntax::Token , const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+bool parseMessageExpression(llvm::ArrayRef Tokens,
+const SourceManager , unsigned Index,
+unsigned Last, Selector Sel,
+std::vector ) {
+
+  unsigned NumArgs = Sel.getNumArgs();
+  llvm::SmallVector Closes;
+  SelectorPieces.clear();
+  while (Index < Last) {
+const auto  = Tokens[Index];
+
+if (Closes.empty()) {
+  auto PieceCount = SelectorPieces.size();
+  if (PieceCount < NumArgs &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Sel.getNameForSlot(PieceCount))) {
+// If 'foo:' instead of ':' (empty selector), we need to skip the ':'
+// token after the name.
+if (!Sel.getNameForSlot(PieceCount).empty()) {
+  ++Index;
+}
+SelectorPieces.push_back(
+halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+continue;
+  }
+  // If we've found all pieces but the current token looks like another
+  // selector piece, it means the method being renamed is a strict prefix 
of
+  // the selector we've found - should be skipped.
+  if (SelectorPieces.size() >= NumArgs &&
+  isSelectorLike(Tok, Tokens[Index + 1]))
+return false;
+}
+
+switch (Tok.kind()) {
+case tok::l_square:
+  Closes.push_back(']');
+  break;
+case tok::l_paren:
+  Closes.push_back(')');
+  break;
+case tok::l_brace:
+  Closes.push_back('}');
+  break;
+case tok::r_square:
+  if (Closes.empty())
+return SelectorPieces.size() == NumArgs;
+
+  if (Closes.back() != ']')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_paren:
+  if (Closes.empty() || Closes.back() != ')')
+return false;
+  Closes.pop_back();
+  break;
+case tok::r_brace:
+  if (Closes.empty() || Closes.back() != '}')
+return false;
+  Closes.pop_back();
+  break;
+case tok::semi:
+  // top level ; ends all statements.
+  if (Closes.empty())
+return false;
+  break;
+default:
+  break;
+}
+
+++Index;
+  }
+  return false;
+}
+
+/// Collects all ranges of the given identifier/selector in the source code.
+///
+/// If a selector is given, this does a full lex of the given source code in
+/// order to identify all selector fragments (e.g. in method exprs/decls) since
+/// they are non-contiguous.
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+auto IdentifierRanges =
+collectIdentifierRanges(Identifier, Content, LangOpts);
+for (const auto  : IdentifierRanges)
+  Ranges.emplace_back(R);
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  // We track parens and braces to ensure that we don't accidentally try 
parsing
+  // a method declaration or definition which isn't at the top level or similar
+  // looking expressions (e.g. an @selector() expression).
+  unsigned ParenCount = 0;
+  unsigned BraceCount = 0;
+  unsigned NumArgs = Selector->getNumArgs();
+
+  std::vector SelectorPieces;
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+  for (unsigned Index = 0; Index < Last; ++Index) {
+const auto  = Tokens[Index];
+

DavidGoldman wrote:

The main edge case I mentioned is having one 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-02-05 Thread David Goldman via cfe-commits


@@ -508,24 +513,46 @@ static bool mayBeValidIdentifier(llvm::StringRef Ident) {
   !isAsciiIdentifierStart(Ident.front(), AllowDollar))
 return false;
   for (char C : Ident) {
+if (AllowColon && C == ':')
+  continue;
 if (llvm::isASCII(C) && !isAsciiIdentifierContinue(C, AllowDollar))
   return false;
   }
   return true;
 }
 
+std::string getName(const NamedDecl ) {
+  if (const auto *MD = dyn_cast())
+return MD->getSelector().getAsString();
+  if (const auto *ID = RenameDecl.getIdentifier())
+return ID->getName().str();
+  return "";
+}
+
 // Check if we can rename the given RenameDecl into NewName.
 // Return details if the rename would produce a conflict.
-std::optional checkName(const NamedDecl ,
- llvm::StringRef NewName) {
+std::optional checkName(const NamedDecl ,

DavidGoldman wrote:

Done

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


[clang-tools-extra] [clangd][SymbolCollector] Treat ObjC methods as spelled (PR #76410)

2024-02-01 Thread David Goldman via cfe-commits

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/7] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22f..336bc3506bb3608 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f3276..1d4e1c1d75ea230 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/7] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea230..5c20b950e4eac0d 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/6] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22f..336bc3506bb3608 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f3276..1d4e1c1d75ea230 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/6] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea230..5c20b950e4eac0d 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/5] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22f..336bc3506bb3608 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f3276..1d4e1c1d75ea230 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/5] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea230..5c20b950e4eac0d 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -38,6 +38,11 @@
 
 namespace clang {
 namespace clangd {
+
+std::vector collectRenameIdentifierRanges(

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+unsigned ParenCount = 0;
+unsigned BraceCount = 0;
+std::vector Pieces;
+  };
+
+  // We have to track square brackets, parens and braces as we want to skip the
+  // tokens inside them. This ensures that we don't use identical selector
+  // pieces in inner message sends, blocks, lambdas and @selector expressions.
+  std::vector States = {ParserState()};
+  unsigned NumPieces = Selector->getNumArgs();
+
+  for (unsigned Index = 0; Index < Last; ++Index) {

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.

DavidGoldman wrote:

Removed

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+unsigned ParenCount = 0;
+unsigned BraceCount = 0;
+std::vector Pieces;
+  };
+
+  // We have to track square brackets, parens and braces as we want to skip the
+  // tokens inside them. This ensures that we don't use identical selector
+  // pieces in inner message sends, blocks, lambdas and @selector expressions.
+  std::vector States = {ParserState()};
+  unsigned NumPieces = Selector->getNumArgs();
+
+  for (unsigned Index = 0; Index < Last; ++Index) {
+auto  = States.back();
+auto  = State.Pieces;
+const auto  = Tokens[Index];
+const auto Kind = Tok.kind();
+auto PieceCount = Pieces.size();
+
+if (State.ParenCount == 0) {
+  // Check for matches until we find all selector pieces.
+  if (PieceCount < NumPieces &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Selector->getNameForSlot(PieceCount))) {
+if (!Selector->getNameForSlot(PieceCount).empty()) {
+  // Skip the ':' after the name. This ensures that it won't match a
+  // follow-up selector piece with an empty name.
+  ++Index;
+}
+Pieces.push_back(halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+continue;
+  }
+  // If we've found all pieces, we still need to try to consume more pieces
+  // as it's possible the selector being renamed is a prefix of this method
+  // name.

DavidGoldman wrote:

Updated, parseMessageExpression can return early but the top level parse can't.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+unsigned ParenCount = 0;
+unsigned BraceCount = 0;
+std::vector Pieces;
+  };
+
+  // We have to track square brackets, parens and braces as we want to skip the
+  // tokens inside them. This ensures that we don't use identical selector
+  // pieces in inner message sends, blocks, lambdas and @selector expressions.
+  std::vector States = {ParserState()};
+  unsigned NumPieces = Selector->getNumArgs();
+
+  for (unsigned Index = 0; Index < Last; ++Index) {
+auto  = States.back();
+auto  = State.Pieces;
+const auto  = Tokens[Index];
+const auto Kind = Tok.kind();
+auto PieceCount = Pieces.size();
+
+if (State.ParenCount == 0) {
+  // Check for matches until we find all selector pieces.
+  if (PieceCount < NumPieces &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+ Selector->getNameForSlot(PieceCount))) {
+if (!Selector->getNameForSlot(PieceCount).empty()) {

DavidGoldman wrote:

Invalid or empty selector piece (foo::)

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+unsigned ParenCount = 0;
+unsigned BraceCount = 0;
+std::vector Pieces;
+  };
+
+  // We have to track square brackets, parens and braces as we want to skip the
+  // tokens inside them. This ensures that we don't use identical selector
+  // pieces in inner message sends, blocks, lambdas and @selector expressions.
+  std::vector States = {ParserState()};
+  unsigned NumPieces = Selector->getNumArgs();
+
+  for (unsigned Index = 0; Index < Last; ++Index) {
+auto  = States.back();
+auto  = State.Pieces;
+const auto  = Tokens[Index];
+const auto Kind = Tok.kind();
+auto PieceCount = Pieces.size();
+
+if (State.ParenCount == 0) {
+  // Check for matches until we find all selector pieces.
+  if (PieceCount < NumPieces &&
+  isMatchingSelectorName(Tok, Tokens[Index + 1], SM,

DavidGoldman wrote:

It's not, Last = Len -1 and we go until < Last, so Len - 2.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-25 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+unsigned ParenCount = 0;
+unsigned BraceCount = 0;
+std::vector Pieces;

DavidGoldman wrote:

Updated with a comment, selector pieces/ranges as well as ( and { count

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -1017,9 +1159,10 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   int LastDLine = 0, LastDColumn = 0;
   int Cost = 0;
   for (size_t I = 0; I < Indexed.size(); ++I) {
-int DLine = Indexed[I].start.line - Lexed[MappedIndex[I]].start.line;
-int DColumn =
-Indexed[I].start.character - Lexed[MappedIndex[I]].start.character;
+int DLine =
+Indexed[I].start.line - Lexed[MappedIndex[I]].range().start.line;
+int DColumn = Indexed[I].start.character -
+  Lexed[MappedIndex[I]].range().start.character;

DavidGoldman wrote:

This was formatted because it was changed, .start -> .range().start

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -1007,7 +1148,8 @@ std::optional> 
getMappedRanges(ArrayRef Indexed,
 //   diff[0]: line + 1  <- insert a line before edit 0.
 //   diff[1]: column + 1 <- remove a line between edits 0 and 1, and insert a
 //   character on edit 1.
-size_t renameRangeAdjustmentCost(ArrayRef Indexed, ArrayRef 
Lexed,
+size_t renameRangeAdjustmentCost(ArrayRef Indexed,
+ ArrayRef Lexed,

DavidGoldman wrote:

These are formatted now because Range -> SymbolRange

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token ,
+   const syntax::Token ) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.
+ Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions ,
+llvm::function_ref
+Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto  = FileSM.get();
+  for (const auto  : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+Action(Tok, SM);
+}
+
+std::vector collectRenameIdentifierRanges(
+llvm::StringRef Identifier, llvm::StringRef Content,
+const LangOptions , std::optional Selector) {
+  std::vector Ranges;
+  if (!Selector) {
+lex(Content, LangOpts,
+[&](const syntax::Token , const SourceManager ) {
+  if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+return;
+  Ranges.emplace_back(
+  halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+});
+return Ranges;

DavidGoldman wrote:

Done.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef 
Indexed, ArrayRef Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token ,
+   const syntax::Token ,
+   const SourceManager ,
+   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+ Cur.text(SM) == SelectorName &&
+ // We require the selector name and : to be contiguous.
+ // e.g. support `foo:` but not `foo :`.

DavidGoldman wrote:

Done.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});
+if (Sel.getNumArgs() > 1)
+  Placeholder = Sel.getAsString();

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});
+if (Sel.getNumArgs() > 1)
+  Placeholder = Sel.getAsString();
+
+// See if the token under the cursor should actually be renamed.
+if (RInputs.NewName != "__clangd_rename_placeholder") {
+  llvm::StringRef NewName = RInputs.NewName;
+  llvm::SmallVector NewNames;
+  NewName.split(NewNames, ":");
+
+  unsigned NumSelectorLocs = MD->getNumSelectorLocs();
+  for (unsigned I = 0; I < NumSelectorLocs; ++I) {
+if (MD->getSelectorLoc(I) == IdentifierToken->location()) {
+  RenamingCurToken = Sel.getNameForSlot(I) != NewNames[I];
+  break;
+}
+  }
+}
+  } else {
+const auto *ID = RenameDecl.getIdentifier();
+if (!ID)
+  return makeError(ReasonToReject::UnsupportedSymbol);
+if (ID->getName() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});
+if (Sel.getNumArgs() > 1)
+  Placeholder = Sel.getAsString();
+
+// See if the token under the cursor should actually be renamed.
+if (RInputs.NewName != "__clangd_rename_placeholder") {
+  llvm::StringRef NewName = RInputs.NewName;
+  llvm::SmallVector NewNames;
+  NewName.split(NewNames, ":");
+
+  unsigned NumSelectorLocs = MD->getNumSelectorLocs();
+  for (unsigned I = 0; I < NumSelectorLocs; ++I) {
+if (MD->getSelectorLoc(I) == IdentifierToken->location()) {
+  RenamingCurToken = Sel.getNameForSlot(I) != NewNames[I];
+  break;
+}
+  }
+}
+  } else {
+const auto *ID = RenameDecl.getIdentifier();
+if (!ID)
+  return makeError(ReasonToReject::UnsupportedSymbol);

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});

DavidGoldman wrote:

Never mind, didn't see your comments below

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -778,12 +868,44 @@ llvm::Expected rename(const RenameInputs 
) {
 return makeError(ReasonToReject::NoSymbolFound);
   if (DeclsUnderCursor.size() > 1)
 return makeError(ReasonToReject::AmbiguousSymbol);
+  std::string Placeholder;
+  // We expect the token under the cursor to be changed unless the user is
+  // renaming an Objective-C selector with multiple pieces and only renames
+  // some of the selector piece(s).
+  bool RenamingCurToken = true;
   const auto  = **DeclsUnderCursor.begin();
-  const auto *ID = RenameDecl.getIdentifier();
-  if (!ID)
-return makeError(ReasonToReject::UnsupportedSymbol);
-  if (ID->getName() == RInputs.NewName)
-return makeError(ReasonToReject::SameName);
+  if (const auto *MD = dyn_cast()) {
+const auto Sel = MD->getSelector();
+if (Sel.getAsString() == RInputs.NewName)
+  return makeError(ReasonToReject::SameName);
+if (Sel.getNumArgs() != RInputs.NewName.count(':') &&
+RInputs.NewName != "__clangd_rename_placeholder")
+  return makeError(
+  InvalidName{InvalidName::BadIdentifier, RInputs.NewName.str()});

DavidGoldman wrote:

Are you sure? I kept this separate since the else statement below performs 
similar logic outside of checkName. Should I move both into checkName?

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -746,6 +812,30 @@ void findNearMiss(
 
 } // namespace
 
+SymbolRange::SymbolRange(Range R) : Ranges({R}) {}
+
+SymbolRange::SymbolRange(std::vector Ranges)
+: Ranges(std::move(Ranges)) {}
+
+Range SymbolRange::range() const { return Ranges.front(); }
+
+bool operator==(const SymbolRange , const SymbolRange ) {
+  return LHS.Ranges == RHS.Ranges;
+}
+bool operator!=(const SymbolRange , const SymbolRange ) {
+  return !(LHS == RHS);
+}
+bool operator<(const SymbolRange , const SymbolRange ) {
+  return LHS.range() < RHS.range();
+}
+
+std::vector symbolRanges(const std::vector Ranges) {

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -543,6 +550,45 @@ std::optional checkName(const NamedDecl 
,
   return Result;
 }
 
+clangd::Range tokenRangeForLoc(ParsedAST , SourceLocation TokLoc,
+   const SourceManager ,
+   const LangOptions ) {
+  const auto *Token = AST.getTokens().spelledTokenAt(TokLoc);
+  assert(Token && "got inclusion at wrong offset");
+  clangd::Range Result;
+  Result.start = sourceLocToPosition(SM, Token->location());
+  Result.end = sourceLocToPosition(SM, Token->endLocation());
+  return Result;
+}
+
+// AST-based ObjC method rename, it renames all occurrences in the main file
+// even for selectors which may have multiple tokens.
+llvm::Expected
+renameObjCMethodWithinFile(ParsedAST , const ObjCMethodDecl *MD,
+   llvm::StringRef NewName,
+   std::vector Locs) {
+  const SourceManager  = AST.getSourceManager();
+  auto Code = SM.getBufferData(SM.getMainFileID());
+  auto RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  llvm::SmallVector NewNames;
+  NewName.split(NewNames, ":");
+  if (NewNames.empty())
+NewNames.push_back(NewName);
+
+  std::vector Ranges;
+  const auto  = MD->getASTContext().getLangOpts();
+  for (const auto  : Locs)
+Ranges.push_back(tokenRangeForLoc(AST, Loc, SM, LangOpts));
+  auto FilePath = AST.tuPath();
+  auto RenameRanges = collectRenameIdentifierRanges(

DavidGoldman wrote:

Hmm, what do you think? we could call adjustRenameRanges to make sure what 
we've lexed matches. It's a bit more complex to guide lexing since we need to 
differentiate between method decls/exprs but maybe that could work. Just not 
sure it's worth adding much on top of this logic - we originally had a purely 
AST-visitor based approach but I'd prefer not to add more complexity here.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -543,6 +550,45 @@ std::optional checkName(const NamedDecl 
,
   return Result;
 }
 
+clangd::Range tokenRangeForLoc(ParsedAST , SourceLocation TokLoc,
+   const SourceManager ,
+   const LangOptions ) {
+  const auto *Token = AST.getTokens().spelledTokenAt(TokLoc);
+  assert(Token && "got inclusion at wrong offset");
+  clangd::Range Result;
+  Result.start = sourceLocToPosition(SM, Token->location());
+  Result.end = sourceLocToPosition(SM, Token->endLocation());
+  return Result;
+}
+
+// AST-based ObjC method rename, it renames all occurrences in the main file
+// even for selectors which may have multiple tokens.

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -543,6 +550,45 @@ std::optional checkName(const NamedDecl 
,
   return Result;
 }
 
+clangd::Range tokenRangeForLoc(ParsedAST , SourceLocation TokLoc,
+   const SourceManager ,
+   const LangOptions ) {
+  const auto *Token = AST.getTokens().spelledTokenAt(TokLoc);
+  assert(Token && "got inclusion at wrong offset");
+  clangd::Range Result;
+  Result.start = sourceLocToPosition(SM, Token->location());
+  Result.end = sourceLocToPosition(SM, Token->endLocation());
+  return Result;
+}
+
+// AST-based ObjC method rename, it renames all occurrences in the main file
+// even for selectors which may have multiple tokens.
+llvm::Expected
+renameObjCMethodWithinFile(ParsedAST , const ObjCMethodDecl *MD,
+   llvm::StringRef NewName,
+   std::vector Locs) {
+  const SourceManager  = AST.getSourceManager();
+  auto Code = SM.getBufferData(SM.getMainFileID());
+  auto RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+  llvm::SmallVector NewNames;
+  NewName.split(NewNames, ":");
+  if (NewNames.empty())

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -543,6 +550,45 @@ std::optional checkName(const NamedDecl 
,
   return Result;
 }
 
+clangd::Range tokenRangeForLoc(ParsedAST , SourceLocation TokLoc,
+   const SourceManager ,
+   const LangOptions ) {
+  const auto *Token = AST.getTokens().spelledTokenAt(TokLoc);
+  assert(Token && "got inclusion at wrong offset");

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-24 Thread David Goldman via cfe-commits


@@ -681,12 +718,26 @@ renameOutsideFile(const NamedDecl , 
llvm::StringRef MainFilePath,
ExpBuffer.getError().message());
   continue;
 }
+std::string RenameIdentifier = RenameDecl.getNameAsString();
+std::optional Selector = std::nullopt;
+llvm::SmallVector NewNames;
+if (const auto *MD = dyn_cast()) {
+  if (MD->getSelector().getNumArgs() > 1) {
+RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
+Selector = MD->getSelector();
+NewName.split(NewNames, ":");

DavidGoldman wrote:

Yep, you can have empty selector fragments.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-23 Thread David Goldman via cfe-commits


@@ -893,22 +964,36 @@ llvm::Expected buildRenameEdit(llvm::StringRef 
AbsFilePath,
 return LastOffset;
   };
 
-  std::vector> OccurrencesOffsets;
-  for (const auto  : Occurrences) {
-auto StartOffset = Offset(R.start);
-if (!StartOffset)
-  return StartOffset.takeError();
-auto EndOffset = Offset(R.end);
-if (!EndOffset)
-  return EndOffset.takeError();
-OccurrencesOffsets.push_back({*StartOffset, *EndOffset});
+  struct OccurrenceOffset {
+size_t Start;
+size_t End;
+llvm::StringRef NewName;
+
+OccurrenceOffset(size_t Start, size_t End, llvm::StringRef NewName) :
+  Start(Start), End(End), NewName(NewName) {}
+  };
+
+  std::vector OccurrencesOffsets;
+  for (const auto  : Occurrences) {
+for (auto It = SR.Ranges.begin(); It != SR.Ranges.end(); ++It) {

DavidGoldman wrote:

Done

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-23 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/4] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22f..336bc3506bb3608 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f3276..1d4e1c1d75ea230 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/4] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea230..5c20b950e4eac0d 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-05 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/3] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/3] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-05 Thread David Goldman via cfe-commits


@@ -893,22 +964,36 @@ llvm::Expected buildRenameEdit(llvm::StringRef 
AbsFilePath,
 return LastOffset;
   };
 
-  std::vector> OccurrencesOffsets;
-  for (const auto  : Occurrences) {
-auto StartOffset = Offset(R.start);
-if (!StartOffset)
-  return StartOffset.takeError();
-auto EndOffset = Offset(R.end);
-if (!EndOffset)
-  return EndOffset.takeError();
-OccurrencesOffsets.push_back({*StartOffset, *EndOffset});
+  struct OccurrenceOffset {
+size_t Start;
+size_t End;
+llvm::StringRef NewName;
+
+OccurrenceOffset(size_t Start, size_t End, llvm::StringRef NewName) :
+  Start(Start), End(End), NewName(NewName) {}
+  };
+
+  std::vector OccurrencesOffsets;
+  for (const auto  : Occurrences) {
+for (auto It = SR.Ranges.begin(); It != SR.Ranges.end(); ++It) {

DavidGoldman wrote:

Not sure I follow, the loop construction doesn't use NewNames at all, you might 
have misread it?

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-05 Thread David Goldman via cfe-commits


@@ -1436,6 +1436,14 @@ struct RenameParams {
 };
 bool fromJSON(const llvm::json::Value &, RenameParams &, llvm::json::Path);
 
+struct PrepareRenameResult {
+  /// Range of the string to rename.
+  Range range;
+  /// Placeholder text to use in the editor, if set.
+  std::optional placeholder;

DavidGoldman wrote:

I think it'll be encoded slightly different but VS Code did appear to handle an 
empty placeholder properly, but I'll have clangd omit encoding it if empty to 
be safe.

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


[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2024-01-02 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/7] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/7] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2023-12-27 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/6] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/6] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2023-12-27 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76466

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/5] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/5] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] Add support for renaming objc methods, even those with multiple selector pieces (PR #76466)

2023-12-27 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman created 
https://github.com/llvm/llvm-project/pull/76466

This is based on  top of #76410 

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/4] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/4] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, 

[clang-tools-extra] [clangd][SymbolCollector] Treat ObjC methods as spelled (PR #76410)

2023-12-26 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman updated 
https://github.com/llvm/llvm-project/pull/76410

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH 1/2] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

>From 1b6a09464ff5c7b1988fcb479d0a4ff876f696e6 Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 16:12:03 -0500
Subject: [PATCH 2/2] Run clang-format

---
 .../clangd/unittests/SymbolCollectorTests.cpp  | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1d4e1c1d75ea23..5c20b950e4eac0 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -560,10 +560,12 @@ TEST_F(SymbolCollectorTest, ObjCRefs) {
   haveRanges(Main.ranges("talk");
   EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
   haveRanges(Main.ranges("say");
-  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 

[clang-tools-extra] [clangd][SymbolCollector] Treat ObjC methods as spelled (PR #76410)

2023-12-26 Thread David Goldman via cfe-commits

https://github.com/DavidGoldman created 
https://github.com/llvm/llvm-project/pull/76410

We'll treat multi-arg methods as spelled once we have full rename support for 
them.

>From 4caf5b3c779bf18236b4b0be5bc7147d10339f2b Mon Sep 17 00:00:00 2001
From: David Goldman 
Date: Tue, 26 Dec 2023 15:59:01 -0500
Subject: [PATCH] [clangd][SymbolCollector] Treat ObjC methods as spelled

We'll treat multi-arg methods as spelled once we have full rename
support for them.
---
 .../clangd/index/SymbolCollector.cpp  |  6 ++-
 .../clangd/unittests/SymbolCollectorTests.cpp | 42 +++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ef4b15febad22..336bc3506bb360 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -174,7 +174,9 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   auto Name = ND.getDeclName();
   const auto NameKind = Name.getNameKind();
   if (NameKind != DeclarationName::Identifier &&
-  NameKind != DeclarationName::CXXConstructorName)
+  NameKind != DeclarationName::CXXConstructorName &&
+  NameKind != DeclarationName::ObjCZeroArgSelector &&
+  NameKind != DeclarationName::ObjCOneArgSelector)
 return false;
   const auto  = ND.getASTContext();
   const auto  = AST.getSourceManager();
@@ -183,6 +185,8 @@ bool isSpelled(SourceLocation Loc, const NamedDecl ) {
   if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
 return false;
   auto StrName = Name.getAsString();
+  if (const auto *MD = dyn_cast())
+StrName = MD->getSelector().getNameForSlot(0).str();
   return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
 }
 } // namespace
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 9cdc57ec01f327..1d4e1c1d75ea23 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -105,6 +105,9 @@ MATCHER(refRange, "") {
 MATCHER_P2(OverriddenBy, Subject, Object, "") {
   return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
 }
+MATCHER(isSpelled, "") {
+  return static_cast(arg.Kind & RefKind::Spelled);
+}
 ::testing::Matcher &>
 haveRanges(const std::vector Ranges) {
   return ::testing::UnorderedPointwise(refRange(), Ranges);
@@ -524,6 +527,45 @@ TEST_F(SymbolCollectorTest, templateArgs) {
  forCodeCompletion(false);
 }
 
+TEST_F(SymbolCollectorTest, ObjCRefs) {
+  Annotations Header(R"(
+  @interface Person
+  - (void)$talk[[talk]];
+  - (void)$say[[say]]:(id)something;
+  @end
+  @interface Person (Category)
+  - (void)categoryMethod;
+  - (void)multiArg:(id)a method:(id)b;
+  @end
+  )");
+  Annotations Main(R"(
+  @implementation Person
+  - (void)$talk[[talk]] {}
+  - (void)$say[[say]]:(id)something {}
+  @end
+
+  void fff(Person *p) {
+[p $talk[[talk]]];
+[p $say[[say]]:0];
+[p categoryMethod];
+[p multiArg:0 method:0];
+  }
+  )");
+  CollectorOpts.RefFilter = RefKind::All;
+  CollectorOpts.CollectMainFileRefs = true;
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header.code(), Main.code(),
+ {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
+  haveRanges(Main.ranges("talk");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
+  haveRanges(Main.ranges("say");
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::categoryMethod").ID,
+  ElementsAre(isSpelled();
+  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, 
"Person::multiArg:method:").ID,
+  ElementsAre(Not(isSpelled());
+}
+
 TEST_F(SymbolCollectorTest, ObjCSymbols) {
   const std::string Header = R"(
 @interface Person

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


[clang] 9fe632b - [clang][HeaderSearch] Treat framework headers as Angled for suggestPath

2023-08-09 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2023-08-09T15:51:02-04:00
New Revision: 9fe632ba18f1398aa9e6fd531a2ed98fd79eba40

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

LOG: [clang][HeaderSearch] Treat framework headers as Angled for suggestPath

- Rename the IsSystem flag to be IsAngled since that's how callers
  actually use the flag.

- Since frameworks by convention use <> style includes, make sure
  we treat them as Angled

Also update clangd's custom logic for frameworks accordingly.

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

Added: 


Modified: 
clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
clang-tools-extra/clangd/Headers.cpp
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
clang-tools-extra/include-cleaner/lib/IncludeSpeller.cpp
clang/include/clang/Lex/HeaderSearch.h
clang/lib/Lex/HeaderSearch.cpp
clang/lib/Sema/SemaLookup.cpp
clang/unittests/Lex/HeaderSearchTest.cpp

Removed: 




diff  --git a/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp 
b/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
index 45fc15619ecab0..1bb5cf756b95b4 100644
--- a/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
+++ b/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
@@ -314,11 +314,11 @@ std::string IncludeFixerSemaSource::minimizeInclude(
   if (!Entry)
 return std::string(Include);
 
-  bool IsSystem = false;
+  bool IsAngled = false;
   std::string Suggestion =
-  HeaderSearch.suggestPathToFileForDiagnostics(*Entry, "", );
+  HeaderSearch.suggestPathToFileForDiagnostics(*Entry, "", );
 
-  return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
+  return IsAngled ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
 }
 
 /// Get the include fixer context for the queried symbol.

diff  --git a/clang-tools-extra/clangd/Headers.cpp 
b/clang-tools-extra/clangd/Headers.cpp
index 83fba21b1d931a..6005069be01160 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -286,11 +286,11 @@ IncludeInserter::calculateIncludePath(const HeaderFile 
,
   assert(InsertedHeader.valid());
   if (InsertedHeader.Verbatim)
 return InsertedHeader.File;
-  bool IsSystem = false;
+  bool IsAngled = false;
   std::string Suggested;
   if (HeaderSearchInfo) {
 Suggested = HeaderSearchInfo->suggestPathToFileForDiagnostics(
-InsertedHeader.File, BuildDir, IncludingFile, );
+InsertedHeader.File, BuildDir, IncludingFile, );
   } else {
 // Calculate include relative to including file only.
 StringRef IncludingDir = llvm::sys::path::parent_path(IncludingFile);
@@ -303,7 +303,7 @@ IncludeInserter::calculateIncludePath(const HeaderFile 
,
   // FIXME: should we allow (some limited number of) "../header.h"?
   if (llvm::sys::path::is_absolute(Suggested))
 return std::nullopt;
-  if (IsSystem)
+  if (IsAngled)
 Suggested = "<" + Suggested + ">";
   else
 Suggested = "\"" + Suggested + "\"";

diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index c9a211b9c4fc2c..e843413601f5a2 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -328,42 +328,33 @@ class SymbolCollector::HeaderFileURICache {
   //  instead of
   //  which should be used instead of directly
   // importing the header.
-  std::optional getFrameworkUmbrellaSpelling(
-  llvm::StringRef Framework, SrcMgr::CharacteristicKind HeadersDirKind,
-  const HeaderSearch , FrameworkHeaderPath ) {
+  std::optional
+  getFrameworkUmbrellaSpelling(llvm::StringRef Framework,
+   const HeaderSearch ,
+   FrameworkHeaderPath ) {
 auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
 auto *CachedSpelling = >second;
 if (!Res.second) {
   return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
 : CachedSpelling->PublicHeader;
 }
-bool IsSystem = isSystem(HeadersDirKind);
 SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir);
 llvm::sys::path::append(UmbrellaPath, "Headers", Framework + ".h");
 
 llvm::vfs::Status Status;
 auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
-if (!StatErr) {
-  if (IsSystem)
-CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework);
-  else
-CachedSpelling->PublicHeader =
-llvm::formatv("\"{0}/{0}.h\"", Framework);
-}
+if (!StatErr)
+  

[clang-tools-extra] a42ce09 - [clang][Sema] Add CodeCompletionContext::CCC_ObjCClassForwardDecl

2023-06-27 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2023-06-27T16:25:40-04:00
New Revision: a42ce094d90341f88a845740b2e5783060f23e3e

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

LOG: [clang][Sema] Add CodeCompletionContext::CCC_ObjCClassForwardDecl

- Use this new context in Sema to limit completions to seen ObjC class
  names

- Use this new context in clangd to disable include insertions when
  completing ObjC forward decls

Reviewed By: kadircet

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang/include/clang/Sema/CodeCompleteConsumer.h
clang/include/clang/Sema/Sema.h
clang/lib/Frontend/ASTUnit.cpp
clang/lib/Parse/ParseObjc.cpp
clang/lib/Sema/CodeCompleteConsumer.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/tools/libclang/CIndexCodeCompletion.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index f4f93a01ec90e..70f2634aa7763 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -214,7 +214,8 @@ struct CompletionCandidate {
   // Returns a token identifying the overload set this is part of.
   // 0 indicates it's not part of any overload set.
   size_t overloadSet(const CodeCompleteOptions , llvm::StringRef FileName,
- IncludeInserter *Inserter) const {
+ IncludeInserter *Inserter,
+ CodeCompletionContext::Kind CCContextKind) const {
 if (!Opts.BundleOverloads.value_or(false))
   return 0;
 
@@ -223,7 +224,7 @@ struct CompletionCandidate {
 // bundle those, so we must resolve the header to be included here.
 std::string HeaderForHash;
 if (Inserter) {
-  if (auto Header = headerToInsertIfAllowed(Opts)) {
+  if (auto Header = headerToInsertIfAllowed(Opts, CCContextKind)) {
 if (auto HeaderFile = toHeaderFile(*Header, FileName)) {
   if (auto Spelled =
   Inserter->calculateIncludePath(*HeaderFile, FileName))
@@ -271,11 +272,21 @@ struct CompletionCandidate {
 return 0;
   }
 
+  bool contextAllowsHeaderInsertion(CodeCompletionContext::Kind Kind) const {
+// Explicitly disable insertions for forward declarations since they don't
+// reference the declaration.
+if (Kind == CodeCompletionContext::CCC_ObjCClassForwardDecl)
+  return false;
+return true;
+  }
+
   // The best header to include if include insertion is allowed.
   std::optional
-  headerToInsertIfAllowed(const CodeCompleteOptions ) const {
+  headerToInsertIfAllowed(const CodeCompleteOptions ,
+  CodeCompletionContext::Kind ContextKind) const {
 if (Opts.InsertIncludes == CodeCompleteOptions::NeverInsert ||
-RankedIncludeHeaders.empty())
+RankedIncludeHeaders.empty() ||
+!contextAllowsHeaderInsertion(ContextKind))
   return std::nullopt;
 if (SemaResult && SemaResult->Declaration) {
   // Avoid inserting new #include if the declaration is found in the 
current
@@ -401,7 +412,8 @@ struct CodeCompletionBuilder {
   std::move(*Spelled),
   Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
 };
-bool ShouldInsert = C.headerToInsertIfAllowed(Opts).has_value();
+bool ShouldInsert =
+C.headerToInsertIfAllowed(Opts, ContextKind).has_value();
 Symbol::IncludeDirective Directive = insertionDirective(Opts);
 // Calculate include paths and edits for all possible headers.
 for (const auto  : C.RankedIncludeHeaders) {
@@ -780,6 +792,7 @@ bool contextAllowsIndex(enum CodeCompletionContext::Kind K) 
{
   case CodeCompletionContext::CCC_ObjCInterfaceName:
   case CodeCompletionContext::CCC_Symbol:
   case CodeCompletionContext::CCC_SymbolOrNewName:
+  case CodeCompletionContext::CCC_ObjCClassForwardDecl:
 return true;
   case CodeCompletionContext::CCC_OtherWithMacros:
   case CodeCompletionContext::CCC_DotMemberAccess:
@@ -1422,6 +1435,10 @@ bool includeSymbolFromIndex(CodeCompletionContext::Kind 
Kind,
   else if (Kind == CodeCompletionContext::CCC_ObjCProtocolName)
 // Don't show anything else in ObjC protocol completions.
 return false;
+
+  if (Kind == CodeCompletionContext::CCC_ObjCClassForwardDecl)
+return Sym.SymInfo.Kind == index::SymbolKind::Class &&
+   Sym.SymInfo.Lang == index::SymbolLanguage::ObjC;
   return true;
 }
 
@@ -1832,8 +1849,8 @@ class CodeCompleteFlow {
 assert(IdentifierResult);
 C.Name = IdentifierResult->Name;
   }
-  if (auto OverloadSet =
-  C.overloadSet(Opts, FileName, Inserter ? &*Inserter : nullptr)) {
+  

[clang-tools-extra] 1b66840 - [clangd][ObjC] Support ObjC class rename from implementation decls

2023-06-26 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2023-06-26T14:43:37-04:00
New Revision: 1b66840f71030f5d5934e99398a59c3485ba111e

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

LOG: [clangd][ObjC] Support ObjC class rename from implementation decls

Reviewed By: kadircet

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

Added: 


Modified: 
clang-tools-extra/clangd/FindTarget.cpp
clang-tools-extra/clangd/SemanticHighlighting.cpp
clang-tools-extra/clangd/refactor/Rename.cpp
clang-tools-extra/clangd/unittests/FindTargetTests.cpp
clang-tools-extra/clangd/unittests/RenameTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index eead9e6a3a7a4..630b75059b6ba 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -708,8 +708,23 @@ llvm::SmallVector refInDecl(const Decl *D,
   {OCID->getClassInterface()}});
   Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
   OCID->getCategoryNameLoc(),
-  /*IsDecl=*/true,
+  /*IsDecl=*/false,
   {OCID->getCategoryDecl()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OCID->getCategoryNameLoc(),
+  /*IsDecl=*/true,
+  {OCID}});
+}
+
+void VisitObjCImplementationDecl(const ObjCImplementationDecl *OIMD) {
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OIMD->getLocation(),
+  /*IsDecl=*/false,
+  {OIMD->getClassInterface()}});
+  Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+  OIMD->getLocation(),
+  /*IsDecl=*/true,
+  {OIMD}});
 }
   };
 

diff  --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp 
b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 3826170892e8c..f6a3f7ac66aa0 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -128,7 +128,7 @@ std::optional kindForDecl(const NamedDecl 
*D,
 return HighlightingKind::Class;
   if (isa(D))
 return HighlightingKind::Interface;
-  if (isa(D))
+  if (isa(D))
 return HighlightingKind::Namespace;
   if (auto *MD = dyn_cast(D))
 return MD->isStatic() ? HighlightingKind::StaticMethod

diff  --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index b3270534b13b1..97ea5e1836579 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/Stmt.h"
@@ -140,6 +141,18 @@ const NamedDecl *canonicalRenameDecl(const NamedDecl *D) {
   return dyn_cast(D->getCanonicalDecl());
 }
 
+// Some AST nodes can reference multiple declarations. We try to pick the
+// relevant one to rename here.
+const NamedDecl *pickInterestingTarget(const NamedDecl *D) {
+  // We only support renaming the class name, not the category name. This has
+  // to be done outside of canonicalization since we don't want a category name
+  // reference to be canonicalized to the class.
+  if (const auto *CD = dyn_cast(D))
+if (const auto CI = CD->getClassInterface())
+  return CI;
+  return D;
+}
+
 llvm::DenseSet locateDeclAt(ParsedAST ,
SourceLocation TokenStartLoc) {
   unsigned Offset =
@@ -156,6 +169,7 @@ llvm::DenseSet locateDeclAt(ParsedAST 
,
targetDecl(SelectedNode->ASTNode,
   DeclRelation::Alias | DeclRelation::TemplatePattern,
   AST.getHeuristicResolver())) {
+D = pickInterestingTarget(D);
 Result.insert(canonicalRenameDecl(D));
   }
   return Result;

diff  --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp 
b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 9979628941bfe..64ac524fc5187 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -1127,6 +1127,16 @@ TEST_F(TargetDeclTest, ObjC) {
   )cpp";
   EXPECT_DECLS("ObjCCategoryImplDecl", "@interface Foo(Ext)");
 
+  Code = R"cpp(
+@interface Foo
+@end
+@interface Foo (Ext)
+@end
+@implementation Foo 

[clang-tools-extra] 042dd99 - [clangd] Full support for #import insertions

2023-01-09 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2023-01-09T09:48:30-05:00
New Revision: 042dd99484d6f393cc8a365def250e9d74c24d37

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

LOG: [clangd] Full support for #import insertions

These are still disabled by default, but will work in ObjC code if you
enable the `-import-insertions` flag.

Completion requires ASTSignals to be available; before ASTSignals are
available, we will always use #include. Once they are available, the
behavior varies as follows:

- For source files, use #import if the ObjC language flag is enabled
- For header files:
  - If the ObjC language flag is disabled, use #include
  - If the header file contains any #imports, use #import
  - If the header file references any ObjC decls, use #import
  - Otherwise, use #include

IncludeFixer support is similar, but it does not rely upon ASTSignals,
instead it does the above checks excluding the scan for ObjC symbols.

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

Added: 


Modified: 
clang-tools-extra/clangd/AST.cpp
clang-tools-extra/clangd/AST.h
clang-tools-extra/clangd/ASTSignals.cpp
clang-tools-extra/clangd/ASTSignals.h
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/Compiler.h
clang-tools-extra/clangd/IncludeFixer.cpp
clang-tools-extra/clangd/IncludeFixer.h
clang-tools-extra/clangd/ParsedAST.cpp
clang-tools-extra/clangd/ParsedAST.h
clang-tools-extra/clangd/tool/ClangdMain.cpp
clang-tools-extra/clangd/unittests/ASTSignalsTests.cpp
clang-tools-extra/clangd/unittests/ASTTests.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/AST.cpp 
b/clang-tools-extra/clangd/AST.cpp
index ab4cf4e7a82f4..17c4dbf6dbe70 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -376,6 +376,38 @@ const ObjCImplDecl *getCorrespondingObjCImpl(const 
ObjCContainerDecl *D) {
   return nullptr;
 }
 
+Symbol::IncludeDirective
+preferredIncludeDirective(llvm::StringRef FileName, const LangOptions 
,
+  ArrayRef MainFileIncludes,
+  ArrayRef TopLevelDecls) {
+  // Always prefer #include for non-ObjC code.
+  if (!LangOpts.ObjC)
+return Symbol::IncludeDirective::Include;
+  // If this is not a header file and has ObjC set as the language, prefer
+  // #import.
+  if (!isHeaderFile(FileName, LangOpts))
+return Symbol::IncludeDirective::Import;
+
+  // Headers lack proper compile flags most of the time, so we might treat a
+  // header as ObjC accidentally. Perform some extra checks to make sure this
+  // works.
+
+  // Any file with a #import, should keep #import-ing.
+  for (auto  : MainFileIncludes)
+if (Inc.Directive == tok::pp_import)
+  return Symbol::IncludeDirective::Import;
+
+  // Any file declaring an ObjC decl should also be #import-ing.
+  // No need to look over the references, as the file doesn't have any 
#imports,
+  // it must be declaring interesting ObjC-like decls.
+  for (const Decl *D : TopLevelDecls)
+if (isa(
+D))
+  return Symbol::IncludeDirective::Import;
+
+  return Symbol::IncludeDirective::Include;
+}
+
 std::string printType(const QualType QT, const DeclContext ,
   const llvm::StringRef Placeholder) {
   std::string Result;

diff  --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index bdc0862e96347..fb0722d697cd0 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -13,6 +13,8 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_AST_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_AST_H
 
+#include "Headers.h"
+#include "index/Symbol.h"
 #include "index/SymbolID.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
@@ -118,6 +120,18 @@ SymbolID getSymbolID(const llvm::StringRef MacroName, 
const MacroInfo *MI,
 /// return nullptr as protocols don't have an implementation.
 const ObjCImplDecl *getCorrespondingObjCImpl(const ObjCContainerDecl *D);
 
+/// Infer the include directive to use for the given \p FileName. It aims for
+/// #import for ObjC files and #include for the rest.
+///
+/// - For source files we use LangOpts directly to infer ObjC-ness.
+/// - For header files we also check for symbols declared by the file and
+///   existing include directives, as the language can be set to ObjC++ as a
+///   fallback in the absence of compile flags.
+Symbol::IncludeDirective
+preferredIncludeDirective(llvm::StringRef FileName, const LangOptions 
,
+  ArrayRef MainFileIncludes,
+  ArrayRef TopLevelDecls);
+
 /// Returns a 

[clang-tools-extra] 814c0bb - [clangd] Add flag to control #import include insertions

2023-01-09 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2023-01-09T09:48:29-05:00
New Revision: 814c0bb31660b2441c9a9a6eeaafc2e33c416842

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

LOG: [clangd] Add flag to control #import include insertions

This will be disabled by default, hopefully we can enable for the next
major release.

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.h
clang-tools-extra/clangd/tool/ClangdMain.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.h 
b/clang-tools-extra/clangd/CodeComplete.h
index 44482d1047294..a7c1ae95dcbf4 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -70,6 +70,10 @@ struct CodeCompleteOptions {
 NeverInsert,
   } InsertIncludes = IncludeInsertion::IWYU;
 
+  /// Whether include insertions for Objective-C code should use #import 
instead
+  /// of #include.
+  bool ImportInsertions = false;
+
   /// A visual indicator to prepend to the completion label to indicate whether
   /// completion result would trigger an #include insertion or not.
   struct IncludeInsertionIndicator {

diff  --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp 
b/clang-tools-extra/clangd/tool/ClangdMain.cpp
index 4cdfb1a8b4449..06cfdcc1ca233 100644
--- a/clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -264,6 +264,14 @@ opt HeaderInsertion{
 "Never insert #include directives as part of code completion")),
 };
 
+opt ImportInsertions{
+"import-insertions",
+cat(Features),
+desc("If header insertion is enabled, add #import directives when "
+ "accepting code completions or fixing includes in Objective-C code"),
+init(CodeCompleteOptions().ImportInsertions),
+};
+
 opt IncludeCleanerStdlib{
 "include-cleaner-stdlib",
 cat(Features),
@@ -913,6 +921,7 @@ clangd accepts flags on the commandline, and in the 
CLANGD_FLAGS environment var
 Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
   Opts.CodeComplete.ShowOrigins = ShowOrigins;
   Opts.CodeComplete.InsertIncludes = HeaderInsertion;
+  Opts.CodeComplete.ImportInsertions = ImportInsertions;
   if (!HeaderInsertionDecorators) {
 Opts.CodeComplete.IncludeIndicator.Insert.clear();
 Opts.CodeComplete.IncludeIndicator.NoInsert.clear();



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


[clang] fc46d6e - [clang][Tooling] Add support for generating #import edits

2022-12-06 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-12-06T13:47:07-05:00
New Revision: fc46d6e67fab06d54c8948ebf959d62984116bc3

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

LOG: [clang][Tooling] Add support for generating #import edits

And make use of this from clangd's CodeComplete and IncludeFixer, although 
currently they are both restricted only to #include symbols.

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/Headers.cpp
clang-tools-extra/clangd/Headers.h
clang-tools-extra/clangd/IncludeFixer.cpp
clang-tools-extra/clangd/IncludeFixer.h
clang-tools-extra/clangd/unittests/HeadersTests.cpp
clang/include/clang/Tooling/Inclusions/HeaderIncludes.h
clang/lib/Format/Format.cpp
clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
clang/unittests/Tooling/HeaderIncludesTest.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 32a80ac054cbe..b445854e57255 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -395,7 +395,8 @@ struct CodeCompletionBuilder {
 CodeCompletion::IncludeCandidate Include;
 Include.Header = ToInclude->first;
 if (ToInclude->second && ShouldInsert)
-  Include.Insertion = Includes.insert(ToInclude->first);
+  Include.Insertion = Includes.insert(
+  ToInclude->first, tooling::IncludeDirective::Include);
 Completion.Includes.push_back(std::move(Include));
   } else
 log("Failed to generate include insertion edits for adding header "

diff  --git a/clang-tools-extra/clangd/Headers.cpp 
b/clang-tools-extra/clangd/Headers.cpp
index 95813894d5aa3..4314ef15d3eb3 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -345,10 +345,12 @@ IncludeInserter::calculateIncludePath(const HeaderFile 
,
 }
 
 llvm::Optional
-IncludeInserter::insert(llvm::StringRef VerbatimHeader) const {
+IncludeInserter::insert(llvm::StringRef VerbatimHeader,
+tooling::IncludeDirective Directive) const {
   llvm::Optional Edit;
-  if (auto Insertion = Inserter.insert(VerbatimHeader.trim("\"<>"),
-   VerbatimHeader.startswith("<")))
+  if (auto Insertion =
+  Inserter.insert(VerbatimHeader.trim("\"<>"),
+  VerbatimHeader.startswith("<"), Directive))
 Edit = replacementToEdit(Code, *Insertion);
   return Edit;
 }

diff  --git a/clang-tools-extra/clangd/Headers.h 
b/clang-tools-extra/clangd/Headers.h
index 0901a23f4dd83..5611b32b11aa1 100644
--- a/clang-tools-extra/clangd/Headers.h
+++ b/clang-tools-extra/clangd/Headers.h
@@ -50,7 +50,7 @@ struct SymbolInclude {
   /// The header to include. This is either a URI or a verbatim include which 
is
   /// quoted with <> or "".
   llvm::StringRef Header;
-  /// The include directive to use, e.g. #import or #include.
+  /// The include directive(s) that can be used, e.g. #import and/or #include.
   Symbol::IncludeDirective Directive;
 };
 
@@ -248,7 +248,8 @@ class IncludeInserter {
 
   /// Calculates an edit that inserts \p VerbatimHeader into code. If the 
header
   /// is already included, this returns std::nullopt.
-  llvm::Optional insert(llvm::StringRef VerbatimHeader) const;
+  llvm::Optional insert(llvm::StringRef VerbatimHeader,
+  tooling::IncludeDirective Directive) const;
 
 private:
   StringRef FileName;

diff  --git a/clang-tools-extra/clangd/IncludeFixer.cpp 
b/clang-tools-extra/clangd/IncludeFixer.cpp
index 5ba5a4e45ba04..45811e10e192a 100644
--- a/clang-tools-extra/clangd/IncludeFixer.cpp
+++ b/clang-tools-extra/clangd/IncludeFixer.cpp
@@ -249,18 +249,22 @@ std::vector 
IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
 }
 
 llvm::Optional IncludeFixer::insertHeader(llvm::StringRef Spelled,
-   llvm::StringRef Symbol) const {
+   llvm::StringRef Symbol,
+   tooling::IncludeDirective 
Directive) const {
   Fix F;
 
-  if (auto Edit = Inserter->insert(Spelled))
+  if (auto Edit = Inserter->insert(Spelled, Directive))
 F.Edits.push_back(std::move(*Edit));
   else
 return std::nullopt;
 
+  llvm::StringRef DirectiveSpelling =
+  Directive == tooling::IncludeDirective::Include ? "Include" : "Import";
   if (Symbol.empty())
-F.Message = llvm::formatv("Include {0}", Spelled);
+F.Message = llvm::formatv("{0} {1}", DirectiveSpelling, Spelled);
   else
-F.Message = llvm::formatv("Include {0} for symbol {1}", 

[clang] 51f1ae5 - [clangd] Add new IncludeDirective to IncludeHeaderWithReferences

2022-12-06 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-12-06T13:47:07-05:00
New Revision: 51f1ae52b0c92a9783e7df328d05b1f95dca74d1

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

LOG: [clangd] Add new IncludeDirective to IncludeHeaderWithReferences

The IncludeDirective contains both Include (the current behavior) and Import,
which we can use in the future to provide #import suggestions for
Objective-C files/symbols.

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/Headers.cpp
clang-tools-extra/clangd/Headers.h
clang-tools-extra/clangd/IncludeFixer.cpp
clang-tools-extra/clangd/index/Merge.cpp
clang-tools-extra/clangd/index/Serialization.cpp
clang-tools-extra/clangd/index/Symbol.h
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/index/SymbolCollector.h
clang-tools-extra/clangd/index/YAMLSerialization.cpp
clang-tools-extra/clangd/index/remote/Index.proto
clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp
clang-tools-extra/clangd/test/index-serialization/Inputs/sample.idx
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
clang-tools-extra/clangd/unittests/IndexTests.cpp
clang-tools-extra/clangd/unittests/SerializationTests.cpp
clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h
clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
clang/unittests/Tooling/HeaderAnalysisTest.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 33088e08387fb..32a80ac054cbe 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -191,7 +191,7 @@ struct CompletionCandidate {
   const CodeCompletionResult *SemaResult = nullptr;
   const Symbol *IndexResult = nullptr;
   const RawIdentifier *IdentifierResult = nullptr;
-  llvm::SmallVector RankedIncludeHeaders;
+  llvm::SmallVector RankedIncludeHeaders;
 
   // Returns a token identifying the overload set this is part of.
   // 0 indicates it's not part of any overload set.
@@ -267,7 +267,11 @@ struct CompletionCandidate {
 if (SM.isInMainFile(SM.getExpansionLoc(RD->getBeginLoc(
   return std::nullopt;
 }
-return RankedIncludeHeaders[0];
+for (const auto  : RankedIncludeHeaders)
+  // FIXME: We should support #import directives here.
+  if ((Inc.Directive & clang::clangd::Symbol::Include) != 0)
+return Inc.Header;
+return None;
   }
 
   using Bundle = llvm::SmallVector;
@@ -383,7 +387,11 @@ struct CodeCompletionBuilder {
 bool ShouldInsert = C.headerToInsertIfAllowed(Opts).has_value();
 // Calculate include paths and edits for all possible headers.
 for (const auto  : C.RankedIncludeHeaders) {
-  if (auto ToInclude = Inserted(Inc)) {
+  // FIXME: We should support #import directives here.
+  if ((Inc.Directive & clang::clangd::Symbol::Include) == 0)
+continue;
+
+  if (auto ToInclude = Inserted(Inc.Header)) {
 CodeCompletion::IncludeCandidate Include;
 Include.Header = ToInclude->first;
 if (ToInclude->second && ShouldInsert)
@@ -392,7 +400,7 @@ struct CodeCompletionBuilder {
   } else
 log("Failed to generate include insertion edits for adding header "
 "(FileURI='{0}', IncludeHeader='{1}') into {2}: {3}",
-C.IndexResult->CanonicalDeclaration.FileURI, Inc, FileName,
+C.IndexResult->CanonicalDeclaration.FileURI, Inc.Header, FileName,
 ToInclude.takeError());
 }
 // Prefer includes that do not need edits (i.e. already exist).

diff  --git a/clang-tools-extra/clangd/Headers.cpp 
b/clang-tools-extra/clangd/Headers.cpp
index a531197290702..95813894d5aa3 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -211,7 +211,7 @@ llvm::Expected toHeaderFile(llvm::StringRef 
Header,
   return HeaderFile{std::move(*Resolved), /*Verbatim=*/false};
 }
 
-llvm::SmallVector getRankedIncludes(const Symbol ) {
+llvm::SmallVector getRankedIncludes(const Symbol ) {
   auto Includes = Sym.IncludeHeaders;
   // Sort in descending order by reference count and header length.
   llvm::sort(Includes, [](const Symbol::IncludeHeaderWithReferences ,
@@ -220,9 +220,9 @@ llvm::SmallVector 
getRankedIncludes(const Symbol ) {
   return LHS.IncludeHeader.size() < RHS.IncludeHeader.size();
 return LHS.References > RHS.References;
   });
-  llvm::SmallVector Headers;
+  llvm::SmallVector Headers;
   for (const auto  : Includes)
-Headers.push_back(Include.IncludeHeader);
+

[clang-tools-extra] 2d5c4b8 - Fix use of dangling stack allocated string in IncludeFixer

2022-11-16 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-11-16T11:09:06-08:00
New Revision: 2d5c4b8f6e040eef33d4f49820868a81c4c9f1e9

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

LOG: Fix use of dangling stack allocated string in IncludeFixer

IncludeFixer uses this BuildDir string later on if given relative paths.

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

Added: 


Modified: 
clang-tools-extra/clangd/ParsedAST.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/ParsedAST.cpp 
b/clang-tools-extra/clangd/ParsedAST.cpp
index 50923c862c583..870a22cd7b1ef 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -465,6 +465,8 @@ ParsedAST::build(llvm::StringRef Filename, const 
ParseInputs ,
   std::vector> CTChecks;
   ast_matchers::MatchFinder CTFinder;
   llvm::Optional CTContext;
+  // Must outlive FixIncludes.
+  auto BuildDir = VFS->getCurrentWorkingDirectory();
   llvm::Optional FixIncludes;
   llvm::DenseMap OverriddenSeverity;
   // No need to run clang-tidy or IncludeFixerif we are not going to surface
@@ -551,7 +553,6 @@ ParsedAST::build(llvm::StringRef Filename, const 
ParseInputs ,
 
 // Add IncludeFixer which can recover diagnostics caused by missing 
includes
 // (e.g. incomplete type) and attach include insertion fixes to 
diagnostics.
-auto BuildDir = VFS->getCurrentWorkingDirectory();
 if (Inputs.Index && !BuildDir.getError()) {
   auto Style =
   getFormatStyleForFile(Filename, Inputs.Contents, *Inputs.TFS);



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


[clang-tools-extra] e09c750 - [clangd][ObjC] Improve completions for protocols + category names

2022-09-08 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-09-08T11:30:26-04:00
New Revision: e09c75049854fee251e99c4c3c55f3f391f52a10

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

LOG: [clangd][ObjC] Improve completions for protocols + category names

- Render protocols as interfaces to differentiate them from classes
  since a protocol and class can have the same name. Take this one step
  further though, and only recommend protocols in ObjC protocol completions.

- Properly call `includeSymbolFromIndex` even with a cached
  speculative fuzzy find request

- Don't use the index to provide completions for category names,
  symbols there don't make sense

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/TestIndex.cpp
clang-tools-extra/clangd/unittests/TestIndex.h

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index f10235894190..9d9b0e748153 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -94,10 +94,14 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind 
Kind) {
   case SK::Struct:
 return CompletionItemKind::Struct;
   case SK::Class:
-  case SK::Protocol:
   case SK::Extension:
   case SK::Union:
 return CompletionItemKind::Class;
+  case SK::Protocol:
+// Use interface instead of class for 
diff erentiation of classes and
+// protocols with the same name (e.g. @interface NSObject vs. @protocol
+// NSObject).
+return CompletionItemKind::Interface;
   case SK::TypeAlias:
 // We use the same kind as the VSCode C++ extension.
 // FIXME: pick a better option when we have one.
@@ -712,13 +716,13 @@ bool contextAllowsIndex(enum CodeCompletionContext::Kind 
K) {
   case CodeCompletionContext::CCC_Type:
   case CodeCompletionContext::CCC_ParenthesizedExpression:
   case CodeCompletionContext::CCC_ObjCInterfaceName:
-  case CodeCompletionContext::CCC_ObjCCategoryName:
   case CodeCompletionContext::CCC_Symbol:
   case CodeCompletionContext::CCC_SymbolOrNewName:
 return true;
   case CodeCompletionContext::CCC_OtherWithMacros:
   case CodeCompletionContext::CCC_DotMemberAccess:
   case CodeCompletionContext::CCC_ArrowMemberAccess:
+  case CodeCompletionContext::CCC_ObjCCategoryName:
   case CodeCompletionContext::CCC_ObjCPropertyAccess:
   case CodeCompletionContext::CCC_MacroName:
   case CodeCompletionContext::CCC_MacroNameUse:
@@ -1343,6 +1347,22 @@ bool allowIndex(CodeCompletionContext ) {
   llvm_unreachable("invalid NestedNameSpecifier kind");
 }
 
+// Should we include a symbol from the index given the completion kind?
+// FIXME: Ideally we can filter in the fuzzy find request itself.
+bool includeSymbolFromIndex(CodeCompletionContext::Kind Kind,
+const Symbol ) {
+  // Objective-C protocols are only useful in ObjC protocol completions,
+  // in other places they're confusing, especially when they share the same
+  // identifier with a class.
+  if (Sym.SymInfo.Kind == index::SymbolKind::Protocol &&
+  Sym.SymInfo.Lang == index::SymbolLanguage::ObjC)
+return Kind == CodeCompletionContext::CCC_ObjCProtocolName;
+  else if (Kind == CodeCompletionContext::CCC_ObjCProtocolName)
+// Don't show anything else in ObjC protocol completions.
+return false;
+  return true;
+}
+
 std::future startAsyncFuzzyFind(const SymbolIndex ,
 const FuzzyFindRequest ) {
   return runAsync([, Req]() {
@@ -1675,14 +1695,6 @@ class CodeCompleteFlow {
 return Output;
   }
 
-  bool includeSymbolFromIndex(const Symbol ) {
-if (CCContextKind == CodeCompletionContext::CCC_ObjCProtocolName) {
-  return Sym.SymInfo.Lang == index::SymbolLanguage::ObjC &&
-  Sym.SymInfo.Kind == index::SymbolKind::Protocol;
-}
-return true;
-  }
-
   SymbolSlab queryIndex() {
 trace::Span Tracer("Query index");
 SPAN_ATTACH(Tracer, "limit", int64_t(Opts.Limit));
@@ -1716,11 +1728,8 @@ class CodeCompleteFlow {
 
 // Run the query against the index.
 SymbolSlab::Builder ResultsBuilder;
-if (Opts.Index->fuzzyFind(Req, [&](const Symbol ) {
-  if (includeSymbolFromIndex(Sym))
-ResultsBuilder.insert(Sym);
-}))
-  Incomplete = true;
+Incomplete |= Opts.Index->fuzzyFind(
+Req, [&](const Symbol ) { ResultsBuilder.insert(Sym); });
 return std::move(ResultsBuilder).build();
   }
 
@@ -1783,6 +1792,8 @@ class CodeCompleteFlow {
 for (const auto  : IndexResults) {
   if (UsedIndexResults.count())
 continue;
+  if 

[clang-tools-extra] 61ef0ab - [clangd] Add decl/def support to SymbolDetails

2022-08-01 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-08-01T14:42:19-04:00
New Revision: 61ef0ab70196bfdc4c301e12384aa91938b15cc4

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

LOG: [clangd] Add decl/def support to SymbolDetails

Add an optional declarationRange and definitionRange to SymbolDetails.

This will allow SourceKit-LSP to implement toggling between goto
definition/declaration based on whether the symbol at the cursor
is a definition or declaration.

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

Added: 


Modified: 
clang-tools-extra/clangd/AST.cpp
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/test/symbol-info.test
clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/AST.cpp 
b/clang-tools-extra/clangd/AST.cpp
index 85c32574f9e67..31db4cf0f3cf5 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"

diff  --git a/clang-tools-extra/clangd/Protocol.cpp 
b/clang-tools-extra/clangd/Protocol.cpp
index ab75faaf98586..622f527f75f4f 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -734,7 +734,9 @@ llvm::raw_ostream <<(llvm::raw_ostream ,
 
 bool operator==(const SymbolDetails , const SymbolDetails ) {
   return LHS.name == RHS.name && LHS.containerName == RHS.containerName &&
- LHS.USR == RHS.USR && LHS.ID == RHS.ID;
+ LHS.USR == RHS.USR && LHS.ID == RHS.ID &&
+ LHS.declarationRange == RHS.declarationRange &&
+ LHS.definitionRange == RHS.definitionRange;
 }
 
 llvm::json::Value toJSON(const SymbolDetails ) {
@@ -755,6 +757,12 @@ llvm::json::Value toJSON(const SymbolDetails ) {
   if (P.ID)
 Result["id"] = P.ID.str();
 
+  if (P.declarationRange)
+Result["declarationRange"] = *P.declarationRange;
+
+  if (P.definitionRange)
+Result["definitionRange"] = *P.definitionRange;
+
   // FIXME: workaround for older gcc/clang
   return std::move(Result);
 }

diff  --git a/clang-tools-extra/clangd/Protocol.h 
b/clang-tools-extra/clangd/Protocol.h
index 0087017efad4f..add24daa1bc6d 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1093,6 +1093,10 @@ struct SymbolDetails {
   std::string USR;
 
   SymbolID ID;
+
+  llvm::Optional declarationRange;
+
+  llvm::Optional definitionRange;
 };
 llvm::json::Value toJSON(const SymbolDetails &);
 llvm::raw_ostream <<(llvm::raw_ostream &, const SymbolDetails &);

diff  --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index c871ea0c437f4..c6a843ec1db43 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -1481,7 +1481,7 @@ std::vector getSymbolInfo(ParsedAST , 
Position Pos) {
 llvm::consumeError(CurLoc.takeError());
 return {};
   }
-
+  auto MainFilePath = AST.tuPath();
   std::vector Results;
 
   // We also want the targets of using-decls, so we include
@@ -1489,6 +1489,8 @@ std::vector getSymbolInfo(ParsedAST , 
Position Pos) {
   DeclRelationSet Relations = DeclRelation::TemplatePattern |
   DeclRelation::Alias | DeclRelation::Underlying;
   for (const NamedDecl *D : getDeclAtPosition(AST, *CurLoc, Relations)) {
+D = getPreferredDecl(D);
+
 SymbolDetails NewSymbol;
 std::string QName = printQualifiedName(*D);
 auto SplitQName = splitQualifiedName(QName);
@@ -1505,6 +1507,12 @@ std::vector getSymbolInfo(ParsedAST , 
Position Pos) {
   NewSymbol.USR = std::string(USR.str());
   NewSymbol.ID = SymbolID(NewSymbol.USR);
 }
+if (const NamedDecl *Def = getDefinition(D))
+  NewSymbol.definitionRange = makeLocation(
+  AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
+NewSymbol.declarationRange =
+makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
+
 Results.push_back(std::move(NewSymbol));
   }
 

diff  --git a/clang-tools-extra/clangd/test/symbol-info.test 
b/clang-tools-extra/clangd/test/symbol-info.test
index 1142f3b80edb7..ba788e1c398eb 100644
--- a/clang-tools-extra/clangd/test/symbol-info.test
+++ b/clang-tools-extra/clangd/test/symbol-info.test
@@ -1,10 +1,36 @@
 # RUN: clangd -lit-test < %s | FileCheck %s
 
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
 ---

[clang-tools-extra] 88a2ac6 - [clangd] Improve XRefs support for ObjCMethodDecl

2022-07-26 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-07-26T12:11:26-04:00
New Revision: 88a2ac6ad623cd7519070f6b0bf2de2793bb90dd

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

LOG: [clangd] Improve XRefs support for ObjCMethodDecl

- Correct nameLocation to point to the first selector fragment instead
  of the - or +

- getDefinition now searches through the proper impl decls to find
  the definition of the ObjCMethodDecl if one exists

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

Added: 


Modified: 
clang-tools-extra/clangd/AST.cpp
clang-tools-extra/clangd/AST.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/unittests/XRefsTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/AST.cpp 
b/clang-tools-extra/clangd/AST.cpp
index f7d526fab0963..4ddfca328eaed 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -171,6 +171,9 @@ bool isImplementationDetail(const Decl *D) {
 
 SourceLocation nameLocation(const clang::Decl , const SourceManager ) {
   auto L = D.getLocation();
+  // For `- (void)foo` we want `foo` not the `-`.
+  if (const auto *MD = dyn_cast())
+L = MD->getSelectorStartLoc();
   if (isSpelledInSource(L, SM))
 return SM.getSpellingLoc(L);
   return SM.getExpansionLoc(L);
@@ -356,6 +359,20 @@ SymbolID getSymbolID(const llvm::StringRef MacroName, 
const MacroInfo *MI,
   return SymbolID(USR);
 }
 
+const ObjCImplDecl *getCorrespondingObjCImpl(const ObjCContainerDecl *D) {
+  if (const auto *ID = dyn_cast(D))
+return ID->getImplementation();
+  if (const auto *CD = dyn_cast(D)) {
+if (CD->IsClassExtension()) {
+  if (const auto *ID = CD->getClassInterface())
+return ID->getImplementation();
+  return nullptr;
+}
+return CD->getImplementation();
+  }
+  return nullptr;
+}
+
 std::string printType(const QualType QT, const DeclContext ,
   const llvm::StringRef Placeholder) {
   std::string Result;

diff  --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index e0024c27aca3a..f313161b6c608 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -93,6 +93,30 @@ SymbolID getSymbolID(const Decl *D);
 SymbolID getSymbolID(const llvm::StringRef MacroName, const MacroInfo *MI,
  const SourceManager );
 
+/// Return the corresponding implementation/definition for the given ObjC
+/// container if it has one, otherwise, return nullptr.
+///
+/// Objective-C classes can have three types of declarations:
+///
+/// - forward declaration: @class MyClass;
+/// - true declaration (interface definition): @interface MyClass ... @end
+/// - true definition (implementation): @implementation MyClass ... @end
+///
+/// Objective-C categories are extensions on classes:
+///
+/// - declaration: @interface MyClass (Ext) ... @end
+/// - definition: @implementation MyClass (Ext) ... @end
+///
+/// With one special case, a class extension, which is normally used to keep
+/// some declarations internal to a file without exposing them in a header.
+///
+/// - class extension declaration: @interface MyClass () ... @end
+/// - which really links to class definition: @implementation MyClass ... @end
+///
+/// For Objective-C protocols, e.g. @protocol MyProtocol ... @end this will
+/// return nullptr as protocols don't have an implementation.
+const ObjCImplDecl *getCorrespondingObjCImpl(const ObjCContainerDecl *D);
+
 /// Returns a QualType as string. The result doesn't contain unwritten scopes
 /// like anonymous/inline namespace.
 std::string printType(const QualType QT, const DeclContext ,

diff  --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index c620b3897f6de..46827433e136d 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -83,32 +83,20 @@ const NamedDecl *getDefinition(const NamedDecl *D) {
   if (const auto *CTD = dyn_cast(D))
 if (const auto *RD = CTD->getTemplatedDecl())
   return RD->getDefinition();
-  // Objective-C classes can have three types of declarations:
-  //
-  // - forward declaration: @class MyClass;
-  // - true declaration (interface definition): @interface MyClass ... @end
-  // - true definition (implementation): @implementation MyClass ... @end
-  //
-  // Objective-C categories are extensions are on classes:
-  //
-  // - declaration: @interface MyClass (Ext) ... @end
-  // - definition: @implementation MyClass (Ext) ... @end
-  //
-  // With one special case, a class extension, which is normally used to keep
-  // some declarations internal to a file without exposing them in a header.
-  //
-  // - class extension declaration: @interface MyClass () ... @end
-  // - which really links to class 

[clang-tools-extra] dc6c1f1 - [clangd][ObjC] Fix ObjC method definition completion

2022-07-01 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-07-01T10:02:47-04:00
New Revision: dc6c1f181b8a95b959f590423ce007b819532290

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

LOG: [clangd][ObjC] Fix ObjC method definition completion

D124637 improved filtering of method expressions, but not method
definitions. With this change, clangd will now filter ObjC method
definition completions based on their entire selector instead of
only the first selector fragment.

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index fbd7488c07d21..2da83c05e9702 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -861,7 +861,7 @@ struct CompletionRecorder : public CodeCompleteConsumer {
 case CodeCompletionResult::RK_Macro:
   return Result.Macro->getName();
 case CodeCompletionResult::RK_Pattern:
-  return Result.Pattern->getTypedText();
+  break;
 }
 auto *CCS = codeCompletionString(Result);
 const CodeCompletionString::Chunk *OnlyText = nullptr;

diff  --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp 
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index f962c3f4ff336..6084a024bea38 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -3131,6 +3131,26 @@ TEST(CompletionTest, ObjectiveCMethodDeclaration) {
   EXPECT_THAT(C, ElementsAre(signature("(char)c secondArgument:(id)object")));
 }
 
+TEST(CompletionTest, ObjectiveCMethodDeclarationFilterOnEntireSelector) {
+  auto Results = completions(R"objc(
+  @interface Foo
+  - (int)valueForCharacter:(char)c secondArgument:(id)object;
+  @end
+  @implementation Foo
+  secondArg^
+  @end
+)objc",
+ /*IndexSymbols=*/{},
+ /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, ElementsAre(named("valueForCharacter:")));
+  EXPECT_THAT(C, ElementsAre(filterText("valueForCharacter:secondArgument:")));
+  EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
+  EXPECT_THAT(C, ElementsAre(qualifier("- (int)")));
+  EXPECT_THAT(C, ElementsAre(signature("(char)c secondArgument:(id)object")));
+}
+
 TEST(CompletionTest, ObjectiveCMethodDeclarationPrefixTyped) {
   auto Results = completions(R"objc(
   @interface Foo



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


[clang-tools-extra] bc1f243 - [clangd] Improve ObjC protocol suggestions from the index

2022-06-15 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-06-15T15:02:37-04:00
New Revision: bc1f24332af3dedccb84a975e57adca495ea016d

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

LOG: [clangd] Improve ObjC protocol suggestions from the index

When querying the index during an ObjC protocol name lookup for code
completion, we should only suggest ObjC protocols.

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/TestIndex.cpp
clang-tools-extra/clangd/unittests/TestIndex.h

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 527b6cdc304db..09e516919c807 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1673,6 +1673,14 @@ class CodeCompleteFlow {
 return Output;
   }
 
+  bool includeSymbolFromIndex(const Symbol ) {
+if (CCContextKind == CodeCompletionContext::CCC_ObjCProtocolName) {
+  return Sym.SymInfo.Lang == index::SymbolLanguage::ObjC &&
+  Sym.SymInfo.Kind == index::SymbolKind::Protocol;
+}
+return true;
+  }
+
   SymbolSlab queryIndex() {
 trace::Span Tracer("Query index");
 SPAN_ATTACH(Tracer, "limit", int64_t(Opts.Limit));
@@ -1706,8 +1714,10 @@ class CodeCompleteFlow {
 
 // Run the query against the index.
 SymbolSlab::Builder ResultsBuilder;
-if (Opts.Index->fuzzyFind(
-Req, [&](const Symbol ) { ResultsBuilder.insert(Sym); }))
+if (Opts.Index->fuzzyFind(Req, [&](const Symbol ) {
+  if (includeSymbolFromIndex(Sym))
+ResultsBuilder.insert(Sym);
+}))
   Incomplete = true;
 return std::move(ResultsBuilder).build();
   }

diff  --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp 
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 18b02af48095f..88698d3177168 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -3167,6 +3167,20 @@ TEST(CompletionTest, 
ObjectiveCMethodDeclarationFromMiddle) {
   EXPECT_THAT(C, ElementsAre(signature("(id)object")));
 }
 
+TEST(CompletionTest, ObjectiveCProtocolFromIndex) {
+  Symbol FoodClass = objcClass("FoodClass");
+  Symbol SymFood = objcProtocol("Food");
+  Symbol SymFooey = objcProtocol("Fooey");
+  auto Results = completions(R"objc(
+  id
+)objc",
+ {SymFood, FoodClass, SymFooey},
+ /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, UnorderedElementsAre(named("Food"), named("Fooey")));
+}
+
 TEST(CompletionTest, CursorInSnippets) {
   clangd::CodeCompleteOptions Options;
   Options.EnableSnippets = true;

diff  --git a/clang-tools-extra/clangd/unittests/TestIndex.cpp 
b/clang-tools-extra/clangd/unittests/TestIndex.cpp
index 3378ac856fc6d..6f7bd3aac207b 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.cpp
+++ b/clang-tools-extra/clangd/unittests/TestIndex.cpp
@@ -81,6 +81,28 @@ Symbol conceptSym(llvm::StringRef Name) {
   return sym(Name, index::SymbolKind::Concept, "@CT@\\0");
 }
 
+Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
+   llvm::StringRef USRPrefix) {
+  Symbol Sym;
+  std::string USR = USRPrefix.str() + Name.str();
+  Sym.Name = Name;
+  Sym.Scope = "";
+  Sym.ID = SymbolID(USR);
+  Sym.SymInfo.Kind = Kind;
+  Sym.SymInfo.Lang = index::SymbolLanguage::ObjC;
+  Sym.Flags |= Symbol::IndexedForCodeCompletion;
+  Sym.Origin = SymbolOrigin::Static;
+  return Sym;
+}
+
+Symbol objcClass(llvm::StringRef Name) {
+  return objcSym(Name, index::SymbolKind::Class, "objc(cs)");
+}
+
+Symbol objcProtocol(llvm::StringRef Name) {
+  return objcSym(Name, index::SymbolKind::Protocol, "objc(pl)");
+}
+
 SymbolSlab generateSymbols(std::vector QualifiedNames) {
   SymbolSlab::Builder Slab;
   for (llvm::StringRef QName : QualifiedNames)

diff  --git a/clang-tools-extra/clangd/unittests/TestIndex.h 
b/clang-tools-extra/clangd/unittests/TestIndex.h
index b55f82eac3292..6a4d2cb5cdd01 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.h
+++ b/clang-tools-extra/clangd/unittests/TestIndex.h
@@ -34,6 +34,14 @@ Symbol ns(llvm::StringRef Name);
 // Create a C++20 concept symbol.
 Symbol conceptSym(llvm::StringRef Name);
 
+// Create an Objective-C symbol.
+Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
+   llvm::StringRef USRPrefix);
+// Create an @interface or @implementation.
+Symbol objcClass(llvm::StringRef Name);
+// Create an @protocol.
+Symbol objcProtocol(llvm::StringRef Name);
+
 // Create a slab of symbols 

[clang-tools-extra] c797952 - [clangd] Minor fixes to ExtractVariableTests missed in D124486

2022-05-31 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-05-31T11:34:35-04:00
New Revision: c797952d4f012275b2e23f5ffcab1f39eacd184d

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

LOG: [clangd] Minor fixes to ExtractVariableTests missed in D124486

Added: 


Modified: 
clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp

Removed: 




diff  --git 
a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
index bd06f318b2a9..70ecc202d0b2 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
@@ -305,22 +305,7 @@ TEST_F(ExtractVariableTest, Test) {
 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
   }
 
-  ExtraArgs = {"-xobjective-c"};
-  EXPECT_UNAVAILABLE(R"cpp(
-  __attribute__((objc_root_class))
-  @interface Foo
-  - (void)setMethod1:(int)a;
-  - (int)method1;
-  @property int prop1;
-  @end
-  @implementation Foo
-  - (void)method {
-[[self.method1]] = 1;
-[[self.method1]] += 1;
-[[self.prop1]] = 1;
-[[self.prop1]] += 1;
-  }
-  @end)cpp");
+  ExtraArgs = {"-xc"};
   InputOutputs = {
   // Function Pointers
   {R"cpp(struct Handlers {
@@ -345,6 +330,7 @@ TEST_F(ExtractVariableTest, Test) {
  void bar() {
int (*placeholder)(int) = foo('c'); (void)placeholder;
  })cpp"},
+  // Arithmetic on typedef types yields plain integer types
   {R"cpp(typedef long NSInteger;
  void varDecl() {
 NSInteger a = 2 * 5;
@@ -355,6 +341,28 @@ TEST_F(ExtractVariableTest, Test) {
 NSInteger a = 2 * 5;
 long placeholder = a * 7; NSInteger b = placeholder + 3;
  })cpp"},
+  };
+  for (const auto  : InputOutputs) {
+EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
+  }
+
+  ExtraArgs = {"-xobjective-c"};
+  EXPECT_UNAVAILABLE(R"cpp(
+  __attribute__((objc_root_class))
+  @interface Foo
+  - (void)setMethod1:(int)a;
+  - (int)method1;
+  @property int prop1;
+  @end
+  @implementation Foo
+  - (void)method {
+[[self.method1]] = 1;
+[[self.method1]] += 1;
+[[self.prop1]] = 1;
+[[self.prop1]] += 1;
+  }
+  @end)cpp");
+  InputOutputs = {
   // Support ObjC property references (explicit property getter).
   {R"cpp(__attribute__((objc_root_class))
  @interface Foo



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


[clang-tools-extra] ae67984 - [clangd] ExtractVariable support for C and Objective-C

2022-05-31 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-05-31T11:14:51-04:00
New Revision: ae67984ca6d89c7ccdbdca258cd05c151d8b6431

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

LOG: [clangd] ExtractVariable support for C and Objective-C

- Use the expression's type for non-C++ as the variable type. This works
  well, but might not preserve the typedefs due to type
  canonicalization.

- Improve support for Objective-C property references which are
  represented using `ObjCPropertyRefExpr` and `BuiltinType::PseudoObject`.

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

Added: 


Modified: 
clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
index 942d7eec6dbd8..5efe09fb3cb27 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -5,6 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 
//===--===//
+#include "AST.h"
 #include "ParsedAST.h"
 #include "Protocol.h"
 #include "Selection.h"
@@ -50,6 +51,7 @@ class ExtractionContext {
 private:
   bool Extractable = false;
   const clang::Expr *Expr;
+  QualType VarType;
   const SelectionTree::Node *ExprNode;
   // Stmt before which we will extract
   const clang::Stmt *InsertionPoint = nullptr;
@@ -81,6 +83,31 @@ computeReferencedDecls(const clang::Expr *Expr) {
   return Visitor.ReferencedDecls;
 }
 
+static QualType computeVariableType(const Expr *Expr, const ASTContext ) {
+  if (Ctx.getLangOpts().CPlusPlus11)
+return Ctx.getAutoDeductType();
+
+  if (Expr->hasPlaceholderType(BuiltinType::PseudoObject)) {
+if (const auto *PR = dyn_cast(Expr)) {
+  if (PR->isMessagingSetter()) {
+// Don't support extracting a compound reference like `self.prop += 1`
+// since the meaning changes after extraction since we'll no longer 
call
+// the setter. Non compound access like `self.prop = 1` is invalid 
since
+// it returns nil (setter method must have a void return type).
+return QualType();
+  } else if (PR->isMessagingGetter()) {
+if (PR->isExplicitProperty())
+  return PR->getExplicitProperty()->getType();
+else
+  return PR->getImplicitPropertyGetter()->getReturnType();
+  }
+} else {
+  return QualType();
+}
+  }
+  return Expr->getType();
+}
+
 ExtractionContext::ExtractionContext(const SelectionTree::Node *Node,
  const SourceManager ,
  const ASTContext )
@@ -90,6 +117,12 @@ ExtractionContext::ExtractionContext(const 
SelectionTree::Node *Node,
   InsertionPoint = computeInsertionPoint();
   if (InsertionPoint)
 Extractable = true;
+  VarType = computeVariableType(Expr, Ctx);
+  if (VarType.isNull())
+Extractable = false;
+  else
+// Strip the outer nullability since it's not common for local variables.
+AttributedType::stripOuterNullability(VarType);
 }
 
 // checks whether extracting before InsertionPoint will take a
@@ -173,9 +206,9 @@ ExtractionContext::insertDeclaration(llvm::StringRef 
VarName,
   toHalfOpenFileRange(SM, Ctx.getLangOpts(),
   InsertionPoint->getSourceRange())
   ->getBegin();
-  // FIXME: Replace auto with explicit type and add &/&& as necessary
-  std::string ExtractedVarDecl = std::string("auto ") + VarName.str() + " = " +
- ExtractionCode.str() + "; ";
+  std::string ExtractedVarDecl =
+  printType(VarType, ExprNode->getDeclContext(), VarName) + " = " +
+  ExtractionCode.str() + "; ";
   return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
 }
 
@@ -365,15 +398,27 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
 return false;
 
   // Void expressions can't be assigned to variables.
-  if (const Type *ExprType = E->getType().getTypePtrOrNull())
-if (ExprType->isVoidType())
-  return false;
+  const Type *ExprType = E->getType().getTypePtrOrNull();
+  if (!ExprType || ExprType->isVoidType())
+return false;
+
+  // Must know the type of the result in order to spell it, or instead use
+  // `auto` in C++.
+  if (!N->getDeclContext().getParentASTContext().getLangOpts().CPlusPlus11 &&
+  !ExprType)
+return false;
 
   // A plain reference to a name (e.g. variable) isn't  worth extracting.
   // FIXME: really? What if it's e.g. `std::is_same::value`?
-  if (llvm::isa(E) || llvm::isa(E))
+  if 

[clang-tools-extra] 322e2a3 - [clangd][ObjC] Filter ObjC method completions on the remaining selector

2022-05-20 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-05-20T11:49:16-04:00
New Revision: 322e2a3b40fa5f6bfcb868e827960c812b185710

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

LOG: [clangd][ObjC] Filter ObjC method completions on the remaining selector

Previously, clangd would filter completions only on the first part of
the selector (first typed chunk) instead of all remaining selector
fragments (all typed chunks).

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

Added: 


Modified: 
clang-tools-extra/clangd/CodeComplete.cpp
clang-tools-extra/clangd/CodeComplete.h
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang/include/clang/Sema/CodeCompleteConsumer.h
clang/lib/Sema/CodeCompleteConsumer.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 920fb77f07dee..527b6cdc304db 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -303,6 +303,7 @@ struct CodeCompletionBuilder {
   assert(ASTCtx);
   Completion.Origin |= SymbolOrigin::AST;
   Completion.Name = std::string(llvm::StringRef(SemaCCS->getTypedText()));
+  Completion.FilterText = SemaCCS->getAllTypedText();
   if (Completion.Scope.empty()) {
 if ((C.SemaResult->Kind == CodeCompletionResult::RK_Declaration) ||
 (C.SemaResult->Kind == CodeCompletionResult::RK_Pattern))
@@ -335,6 +336,8 @@ struct CodeCompletionBuilder {
 Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind);
   if (Completion.Name.empty())
 Completion.Name = std::string(C.IndexResult->Name);
+  if (Completion.FilterText.empty())
+Completion.FilterText = Completion.Name;
   // If the completion was visible to Sema, no qualifier is needed. This
   // avoids unneeded qualifiers in cases like with `using ns::X`.
   if (Completion.RequiredQualifier.empty() && !C.SemaResult) {
@@ -352,6 +355,7 @@ struct CodeCompletionBuilder {
   Completion.Origin |= SymbolOrigin::Identifier;
   Completion.Kind = CompletionItemKind::Text;
   Completion.Name = std::string(C.IdentifierResult->Name);
+  Completion.FilterText = Completion.Name;
 }
 
 // Turn absolute path into a literal string that can be #included.
@@ -860,7 +864,15 @@ struct CompletionRecorder : public CodeCompleteConsumer {
   return Result.Pattern->getTypedText();
 }
 auto *CCS = codeCompletionString(Result);
-return CCS->getTypedText();
+const CodeCompletionString::Chunk *OnlyText = nullptr;
+for (auto  : *CCS) {
+  if (C.Kind != CodeCompletionString::CK_TypedText)
+continue;
+  if (OnlyText)
+return CCAllocator->CopyString(CCS->getAllTypedText());
+  OnlyText = 
+}
+return OnlyText ? OnlyText->Text : llvm::StringRef();
   }
 
   // Build a CodeCompletion string for R, which must be from Results.
@@ -1980,6 +1992,7 @@ CodeCompleteResult codeCompleteComment(PathRef FileName, 
unsigned Offset,
   continue;
 CodeCompletion Item;
 Item.Name = Name.str() + "=";
+Item.FilterText = Item.Name;
 Item.Kind = CompletionItemKind::Text;
 Result.Completions.push_back(Item);
   }
@@ -2118,8 +2131,8 @@ CompletionItem CodeCompletion::render(const 
CodeCompleteOptions ) const {
   Doc.append(*Documentation);
 LSP.documentation = renderDoc(Doc, Opts.DocumentationFormat);
   }
-  LSP.sortText = sortText(Score.Total, Name);
-  LSP.filterText = Name;
+  LSP.sortText = sortText(Score.Total, FilterText);
+  LSP.filterText = FilterText;
   LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name};
   // Merge continuous additionalTextEdits into main edit. The main motivation
   // behind this is to help LSP clients, it seems most of them are confused 
when

diff  --git a/clang-tools-extra/clangd/CodeComplete.h 
b/clang-tools-extra/clangd/CodeComplete.h
index b76dd642943a9..73139ba40e765 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -155,6 +155,10 @@ struct CodeCompleteOptions {
 struct CodeCompletion {
   // The unqualified name of the symbol or other completion item.
   std::string Name;
+  // The name of the symbol for filtering and sorting purposes. Typically the
+  // same as `Name`, but may be 
diff erent e.g. for ObjC methods, `Name` is the
+  // first selector fragment but the `FilterText` is the entire selector.
+  std::string FilterText;
   // The scope qualifier for the symbol name. e.g. "ns1::ns2::"
   // Empty for non-symbol completions. Not inserted, but may be displayed.
   std::string Scope;

diff  --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp 

[clang] d9739f2 - Serialize PragmaAssumeNonNullLoc to support preambles

2022-03-31 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-03-31T11:08:01-04:00
New Revision: d9739f29cdd4c066763275d09e1d26ee315cfdf5

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

LOG: Serialize PragmaAssumeNonNullLoc to support preambles

Previously, if a `#pragma clang assume_nonnull begin` was at the
end of a premable with a `#pragma clang assume_nonnull end` at the
end of the main file, clang would diagnose an unterminated begin in
the preamble and an unbalanced end in the main file.

With this change, those errors no longer occur and the case above is
now properly handled. I've added a corresponding test to clangd,
which makes use of preambles, in order to verify this works as
expected.

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

Added: 
clang/test/Index/preamble-assume-nonnull.c

Modified: 
clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
clang/include/clang/Lex/Preprocessor.h
clang/include/clang/Lex/PreprocessorOptions.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTWriter.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp 
b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
index 097ada51b2e9a..5a5a53679facc 100644
--- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -747,6 +747,40 @@ TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
   EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
 }
 
+TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
+  auto TU = TestTU::withCode(R"cpp(
+#pragma clang assume_nonnull begin
+void foo(int *x);
+#pragma clang assume_nonnull end
+)cpp");
+  auto AST = TU.build();
+  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  const auto *X = cast(findDecl(AST, "foo")).getParamDecl(0);
+  ASSERT_TRUE(X->getOriginalType()->getNullability(X->getASTContext()) ==
+  NullabilityKind::NonNull);
+}
+
+TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
+  Annotations Header(R"cpp(
+#pragma clang assume_nonnull begin  // error-ok
+void foo(int *X);
+)cpp");
+  auto TU = TestTU::withCode(R"cpp(
+#include "foo.h"  // unterminated assume_nonnull should not affect bar.
+void bar(int *Y);
+)cpp");
+  TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
+  auto AST = TU.build();
+  EXPECT_THAT(*AST.getDiagnostics(),
+  ElementsAre(diagName("pp_eof_in_assume_nonnull")));
+  const auto *X = cast(findDecl(AST, "foo")).getParamDecl(0);
+  ASSERT_TRUE(X->getOriginalType()->getNullability(X->getASTContext()) ==
+  NullabilityKind::NonNull);
+  const auto *Y = cast(findDecl(AST, "bar")).getParamDecl(0);
+  ASSERT_FALSE(
+  Y->getOriginalType()->getNullability(X->getASTContext()).hasValue());
+}
+
 TEST(DiagnosticsTest, InsideMacros) {
   Annotations Test(R"cpp(
 #define TEN 10

diff  --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index 36bf8ed64c2bc..02b94872cd8ae 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -409,6 +409,14 @@ class Preprocessor {
   /// \#pragma clang assume_nonnull begin.
   SourceLocation PragmaAssumeNonNullLoc;
 
+  /// Set only for preambles which end with an active
+  /// \#pragma clang assume_nonnull begin.
+  ///
+  /// When the preamble is loaded into the main file,
+  /// `PragmaAssumeNonNullLoc` will be set to this to
+  /// replay the unterminated assume_nonnull.
+  SourceLocation PreambleRecordedPragmaAssumeNonNullLoc;
+
   /// True if we hit the code-completion point.
   bool CodeCompletionReached = false;
 
@@ -1762,6 +1770,21 @@ class Preprocessor {
 PragmaAssumeNonNullLoc = Loc;
   }
 
+  /// Get the location of the recorded unterminated \#pragma clang
+  /// assume_nonnull begin in the preamble, if one exists.
+  ///
+  /// Returns an invalid location if the premable did not end with
+  /// such a pragma active or if there is no recorded preamble.
+  SourceLocation getPreambleRecordedPragmaAssumeNonNullLoc() const {
+return PreambleRecordedPragmaAssumeNonNullLoc;
+  }
+
+  /// Record the location of the unterminated \#pragma clang
+  /// assume_nonnull begin in the preamble.
+  void setPreambleRecordedPragmaAssumeNonNullLoc(SourceLocation Loc) {
+PreambleRecordedPragmaAssumeNonNullLoc = Loc;
+  }
+
   /// Set the directory in which the main file should be considered
   /// to have been found, if it is not a real file.
   void setMainFileDir(const DirectoryEntry *Dir) {

diff  --git a/clang/include/clang/Lex/PreprocessorOptions.h 
b/clang/include/clang/Lex/PreprocessorOptions.h
index 

[clang-tools-extra] 8522a01 - Attempt forward fix for Linux buildbots for D116385

2022-03-17 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-03-17T12:49:16-04:00
New Revision: 8522a01e842e6791545864f2d255dc48190a7e34

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

LOG: Attempt forward fix for Linux buildbots for D116385

Added: 


Modified: 
clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp

Removed: 




diff  --git 
a/clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp
index dc78d696bebd8..05235e01efad8 100644
--- 
a/clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp
+++ 
b/clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp
@@ -27,6 +27,7 @@ TEST_F(ObjCMemberwiseInitializerTest, TestAvailability) {
 @end
   )cpp");
 
+  ExtraArgs.push_back("-fobjc-runtime=macosx");
   ExtraArgs.push_back("-fobjc-arc");
 
   // Ensure the action can be initiated on the interface and implementation,
@@ -88,6 +89,7 @@ TEST_F(ObjCMemberwiseInitializerTest, TestAvailability) {
 
 TEST_F(ObjCMemberwiseInitializerTest, Test) {
   FileName = "TestTU.m";
+  ExtraArgs.push_back("-fobjc-runtime=macosx");
   ExtraArgs.push_back("-fobjc-arc");
 
   const char *Input = R"cpp(



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


[clang-tools-extra] 1af5fbd - [clangd] Code action for creating an ObjC initializer

2022-03-17 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-03-17T11:31:14-04:00
New Revision: 1af5fbd5c605372963f78351f721fa28ee5ba60e

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

LOG: [clangd] Code action for creating an ObjC initializer

The code action creates an initializer for the selected
ivars/properties, defaulting to all if only the interface/implementation
container is selected.

We add it based on the position of the first non initializer that we
see, and default to adding it where the @end token is.

We also use the ObjC parameter form of (nullable id) instead of
(id _Nullable) if the property has the nullable attribute.

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

Added: 
clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp
clang-tools-extra/clangd/unittests/tweaks/ObjCMemberwiseInitializerTests.cpp

Modified: 
clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
clang-tools-extra/clangd/unittests/CMakeLists.txt

Removed: 




diff  --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index caa69947c47fb..ae279781a6f52 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -22,6 +22,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   ExtractFunction.cpp
   ExtractVariable.cpp
   ObjCLocalizeStringLiteral.cpp
+  ObjCMemberwiseInitializer.cpp
   PopulateSwitch.cpp
   RawStringLiteral.cpp
   RemoveUsingNamespace.cpp

diff  --git 
a/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp
new file mode 100644
index 0..2f8f8f7863409
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp
@@ -0,0 +1,329 @@
+//===--- ObjCMemberwiseInitializer.cpp ---*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/InsertionPoint.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+static std::string capitalize(std::string Message) {
+  if (!Message.empty())
+Message[0] = llvm::toUpper(Message[0]);
+  return Message;
+}
+
+static std::string getTypeStr(const QualType , const Decl ,
+  unsigned PropertyAttributes) {
+  QualType T = OrigT;
+  PrintingPolicy Policy(D.getASTContext().getLangOpts());
+  Policy.SuppressStrongLifetime = true;
+  std::string Prefix = "";
+  // If the nullability is specified via a property attribute, use the shorter
+  // `nullable` form for the method parameter.
+  if (PropertyAttributes & ObjCPropertyAttribute::kind_nullability) {
+if (auto Kind = AttributedType::stripOuterNullability(T)) {
+  switch (Kind.getValue()) {
+  case NullabilityKind::Nullable:
+Prefix = "nullable ";
+break;
+  case NullabilityKind::NonNull:
+Prefix = "nonnull ";
+break;
+  case NullabilityKind::Unspecified:
+Prefix = "null_unspecified ";
+break;
+  case NullabilityKind::NullableResult:
+T = OrigT;
+break;
+  }
+}
+  }
+  return Prefix + T.getAsString(Policy);
+}
+
+struct MethodParameter {
+  // Parameter name.
+  llvm::StringRef Name;
+
+  // Type of the parameter.
+  std::string Type;
+
+  // Assignment target (LHS).
+  std::string Assignee;
+
+  MethodParameter(const ObjCIvarDecl ) {
+// Convention maps `@property int foo` to ivar `int _foo`, so drop the
+// leading `_` if there is one.
+Name = ID.getName();
+Name.consume_front("_");
+Type = getTypeStr(ID.getType(), ID, ObjCPropertyAttribute::kind_noattr);
+Assignee = ID.getName().str();
+  }
+  MethodParameter(const ObjCPropertyDecl ) {
+Name = PD.getName();
+Type = getTypeStr(PD.getType(), PD, PD.getPropertyAttributes());
+if (const auto *ID = PD.getPropertyIvarDecl())
+  Assignee = 

[clang-tools-extra] 54a962b - [clangd] Use `ObjCProtocolLoc` for generalized ObjC protocol support

2022-02-18 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-18T15:24:00-05:00
New Revision: 54a962bbfee86d5af90d5fdd39b4ff4ec8030f12

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

LOG: [clangd] Use `ObjCProtocolLoc` for generalized ObjC protocol support

This removes clangd's existing workaround in favor of proper support
via the newly added `ObjCProtocolLoc`. This improves support by
allowing clangd to properly identify which protocol is selected
now that `ObjCProtocolLoc` gets its own ASTNode.

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

Added: 


Modified: 
clang-tools-extra/clangd/FindTarget.cpp
clang-tools-extra/clangd/Selection.cpp
clang-tools-extra/clangd/unittests/FindTargetTests.cpp
clang-tools-extra/clangd/unittests/HoverTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/FindTarget.cpp 
b/clang-tools-extra/clangd/FindTarget.cpp
index e96aa25fd780c..1b7b7de4f9047 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -453,15 +453,6 @@ struct TargetFinder {
   void VisitObjCInterfaceType(const ObjCInterfaceType *OIT) {
 Outer.add(OIT->getDecl(), Flags);
   }
-  void VisitObjCObjectType(const ObjCObjectType *OOT) {
-// Make all of the protocols targets since there's no child nodes for
-// protocols. This isn't needed for the base type, which *does* have a
-// child `ObjCInterfaceTypeLoc`. This structure is a hack, but it works
-// well for go-to-definition.
-unsigned NumProtocols = OOT->getNumProtocols();
-for (unsigned I = 0; I < NumProtocols; I++)
-  Outer.add(OOT->getProtocol(I), Flags);
-  }
 };
 Visitor(*this, Flags).Visit(T.getTypePtr());
   }
@@ -547,6 +538,8 @@ allTargetDecls(const DynTypedNode , const 
HeuristicResolver *Resolver) {
 Finder.add(TAL->getArgument(), Flags);
   else if (const CXXBaseSpecifier *CBS = N.get())
 Finder.add(CBS->getTypeSourceInfo()->getType(), Flags);
+  else if (const ObjCProtocolLoc *PL = N.get())
+Finder.add(PL->getProtocol(), Flags);
   return Finder.takeDecls();
 }
 
@@ -669,25 +662,7 @@ llvm::SmallVector refInDecl(const Decl *D,
   {OMD}});
 }
 
-void visitProtocolList(
-llvm::iterator_range Protocols,
-llvm::iterator_range Locations) {
-  for (const auto  : llvm::zip(Protocols, Locations)) {
-Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
-std::get<1>(P),
-/*IsDecl=*/false,
-{std::get<0>(P)}});
-  }
-}
-
-void VisitObjCInterfaceDecl(const ObjCInterfaceDecl *OID) {
-  if (OID->isThisDeclarationADefinition())
-visitProtocolList(OID->protocols(), OID->protocol_locs());
-  Base::VisitObjCInterfaceDecl(OID); // Visit the interface's name.
-}
-
 void VisitObjCCategoryDecl(const ObjCCategoryDecl *OCD) {
-  visitProtocolList(OCD->protocols(), OCD->protocol_locs());
   // getLocation is the extended class's location, not the category's.
   Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
   OCD->getLocation(),
@@ -709,12 +684,6 @@ llvm::SmallVector refInDecl(const Decl *D,
   /*IsDecl=*/true,
   {OCID->getCategoryDecl()}});
 }
-
-void VisitObjCProtocolDecl(const ObjCProtocolDecl *OPD) {
-  if (OPD->isThisDeclarationADefinition())
-visitProtocolList(OPD->protocols(), OPD->protocol_locs());
-  Base::VisitObjCProtocolDecl(OPD); // Visit the protocol's name.
-}
   };
 
   Visitor V{Resolver};
@@ -944,16 +913,6 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) 
{
   /*IsDecl=*/false,
   {L.getIFaceDecl()}});
 }
-
-void VisitObjCObjectTypeLoc(ObjCObjectTypeLoc L) {
-  unsigned NumProtocols = L.getNumProtocols();
-  for (unsigned I = 0; I < NumProtocols; I++) {
-Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
-L.getProtocolLoc(I),
-/*IsDecl=*/false,
-{L.getProtocol(I)}});
-  }
-}
   };
 
   Visitor V{Resolver};
@@ -1049,6 +1008,11 @@ class ExplicitReferenceCollector
 return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L);
   }
 
+  bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc) {
+visitNode(DynTypedNode::create(ProtocolLoc));
+return true;
+  }
+
   bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
 visitNode(DynTypedNode::create(*Init));
 return 

[clang] 805f7a4 - [clang] Add `ObjCProtocolLoc` to represent protocol references

2022-02-18 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-18T15:24:00-05:00
New Revision: 805f7a4fa4ce97277c3b73d0c204fc3aa4b072e1

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

LOG: [clang] Add `ObjCProtocolLoc` to represent protocol references

Add `ObjCProtocolLoc` which behaves like `TypeLoc` but for
`ObjCProtocolDecl` references.

RecursiveASTVisitor now synthesizes `ObjCProtocolLoc` during traversal
and the `ObjCProtocolLoc` can be stored in a `DynTypedNode`.

In a follow up patch, I'll update clangd to make use of this
to properly support protocol references for hover + goto definition.

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

Added: 


Modified: 
clang/include/clang/AST/ASTFwd.h
clang/include/clang/AST/ASTTypeTraits.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/AST/TypeLoc.h
clang/lib/AST/ASTTypeTraits.cpp
clang/lib/AST/ParentMapContext.cpp
clang/unittests/AST/RecursiveASTVisitorTest.cpp

Removed: 




diff  --git a/clang/include/clang/AST/ASTFwd.h 
b/clang/include/clang/AST/ASTFwd.h
index fdbd603ce5d04..f84b3238e32b5 100644
--- a/clang/include/clang/AST/ASTFwd.h
+++ b/clang/include/clang/AST/ASTFwd.h
@@ -33,6 +33,7 @@ class OMPClause;
 class Attr;
 #define ATTR(A) class A##Attr;
 #include "clang/Basic/AttrList.inc"
+class ObjCProtocolLoc;
 
 } // end namespace clang
 

diff  --git a/clang/include/clang/AST/ASTTypeTraits.h 
b/clang/include/clang/AST/ASTTypeTraits.h
index 6d96146a4d455..cd6b5143bf790 100644
--- a/clang/include/clang/AST/ASTTypeTraits.h
+++ b/clang/include/clang/AST/ASTTypeTraits.h
@@ -160,6 +160,7 @@ class ASTNodeKind {
 NKI_Attr,
 #define ATTR(A) NKI_##A##Attr,
 #include "clang/Basic/AttrList.inc"
+NKI_ObjCProtocolLoc,
 NKI_NumberOfKinds
   };
 
@@ -213,6 +214,7 @@ KIND_TO_KIND_ID(Stmt)
 KIND_TO_KIND_ID(Type)
 KIND_TO_KIND_ID(OMPClause)
 KIND_TO_KIND_ID(Attr)
+KIND_TO_KIND_ID(ObjCProtocolLoc)
 KIND_TO_KIND_ID(CXXBaseSpecifier)
 #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl)
 #include "clang/AST/DeclNodes.inc"
@@ -499,7 +501,7 @@ class DynTypedNode {
   /// have storage or unique pointers and thus need to be stored by value.
   llvm::AlignedCharArrayUnion
+  QualType, TypeLoc, ObjCProtocolLoc>
   Storage;
 };
 
@@ -570,6 +572,10 @@ template <>
 struct DynTypedNode::BaseConverter
 : public PtrConverter {};
 
+template <>
+struct DynTypedNode::BaseConverter
+: public ValueConverter {};
+
 // The only operation we allow on unsupported types is \c get.
 // This allows to conveniently use \c DynTypedNode when having an arbitrary
 // AST node that is not supported, but prevents misuse - a user cannot create

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index f62dc36de556e..16da64100d424 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -324,6 +324,12 @@ template  class RecursiveASTVisitor {
   /// \returns false if the visitation was terminated early, true otherwise.
   bool TraverseConceptReference(const ConceptReference );
 
+  /// Recursively visit an Objective-C protocol reference with location
+  /// information.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc);
+
   //  Methods on Attrs 
 
   // Visit an attribute.
@@ -1340,7 +1346,12 @@ 
DEF_TRAVERSE_TYPELOC(DependentTemplateSpecializationType, {
 DEF_TRAVERSE_TYPELOC(PackExpansionType,
  { TRY_TO(TraverseTypeLoc(TL.getPatternLoc())); })
 
-DEF_TRAVERSE_TYPELOC(ObjCTypeParamType, {})
+DEF_TRAVERSE_TYPELOC(ObjCTypeParamType, {
+  for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) {
+ObjCProtocolLoc ProtocolLoc(TL.getProtocol(I), TL.getProtocolLoc(I));
+TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+  }
+})
 
 DEF_TRAVERSE_TYPELOC(ObjCInterfaceType, {})
 
@@ -1351,6 +1362,10 @@ DEF_TRAVERSE_TYPELOC(ObjCObjectType, {
 TRY_TO(TraverseTypeLoc(TL.getBaseLoc()));
   for (unsigned i = 0, n = TL.getNumTypeArgs(); i != n; ++i)
 TRY_TO(TraverseTypeLoc(TL.getTypeArgTInfo(i)->getTypeLoc()));
+  for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) {
+ObjCProtocolLoc ProtocolLoc(TL.getProtocol(I), TL.getProtocolLoc(I));
+TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+  }
 })
 
 DEF_TRAVERSE_TYPELOC(ObjCObjectPointerType,
@@ -1541,12 +1556,16 @@ DEF_TRAVERSE_DECL(
 DEF_TRAVERSE_DECL(ObjCCompatibleAliasDecl, {// FIXME: implement
})
 
-DEF_TRAVERSE_DECL(ObjCCategoryDecl, {// FIXME: implement
+DEF_TRAVERSE_DECL(ObjCCategoryDecl, {
   if (ObjCTypeParamList *typeParamList = 

[clang-tools-extra] f98bf92 - Reland "[clangd] Properly compute framework-style include spelling"

2022-02-07 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-07T11:21:23-05:00
New Revision: f98bf92b624152c9f09e77137896f7e18dca365c

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

LOG: Reland "[clangd] Properly compute framework-style include spelling"

Roll forward of https://reviews.llvm.org/D117056 with a fix. Proper
initialization of `IsPrivateHeader` was missing, causing failures
on Linux.

Added: 


Modified: 
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 3257041ffa0e3..ec0aa3c6f015a 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -183,6 +183,13 @@ const Decl *getRefContainer(const Decl *Enclosing,
 // including filename normalization, URI conversion etc.
 // Expensive checks are cached internally.
 class SymbolCollector::HeaderFileURICache {
+  struct FrameworkUmbrellaSpelling {
+// Spelling for the public umbrella header, e.g. 
+llvm::Optional PublicHeader;
+// Spelling for the private umbrella header, e.g.
+// 
+llvm::Optional PrivateHeader;
+  };
   // Weird double-indirect access to PP, which might not be ready yet when
   // HeaderFiles is created but will be by the time it's used.
   // (IndexDataConsumer::setPreprocessor can happen before or after initialize)
@@ -193,6 +200,9 @@ class SymbolCollector::HeaderFileURICache {
   llvm::DenseMap CacheFEToURI;
   llvm::StringMap CachePathToURI;
   llvm::DenseMap CacheFIDToInclude;
+  llvm::StringMap CachePathToFrameworkSpelling;
+  llvm::StringMap
+  CacheFrameworkToUmbrellaHeaderSpelling;
 
 public:
   HeaderFileURICache(Preprocessor *, const SourceManager ,
@@ -249,6 +259,126 @@ class SymbolCollector::HeaderFileURICache {
 return R.first->second;
   }
 
+  struct FrameworkHeaderPath {
+// Path to the framework directory containing the Headers/PrivateHeaders
+// directories  e.g. /Frameworks/Foundation.framework/
+llvm::StringRef HeadersParentDir;
+// Subpath relative to the Headers or PrivateHeaders dir, e.g. NSObject.h
+// Note: This is NOT relative to the `HeadersParentDir`.
+llvm::StringRef HeaderSubpath;
+// Whether this header is under the PrivateHeaders dir
+bool IsPrivateHeader;
+  };
+
+  llvm::Optional
+  splitFrameworkHeaderPath(llvm::StringRef Path) {
+using namespace llvm::sys;
+path::reverse_iterator I = path::rbegin(Path);
+path::reverse_iterator Prev = I;
+path::reverse_iterator E = path::rend(Path);
+while (I != E) {
+  if (*I == "Headers") {
+FrameworkHeaderPath HeaderPath;
+HeaderPath.HeadersParentDir = Path.substr(0, I - E);
+HeaderPath.HeaderSubpath = Path.substr(Prev - E);
+HeaderPath.IsPrivateHeader = false;
+return HeaderPath;
+  }
+  if (*I == "PrivateHeaders") {
+FrameworkHeaderPath HeaderPath;
+HeaderPath.HeadersParentDir = Path.substr(0, I - E);
+HeaderPath.HeaderSubpath = Path.substr(Prev - E);
+HeaderPath.IsPrivateHeader = true;
+return HeaderPath;
+  }
+  Prev = I;
+  ++I;
+}
+// Unexpected, must not be a framework header.
+return llvm::None;
+  }
+
+  // Frameworks typically have an umbrella header of the same name, e.g.
+  //  instead of  or
+  //  instead of
+  //  which should be used instead of directly
+  // importing the header.
+  llvm::Optional getFrameworkUmbrellaSpelling(
+  llvm::StringRef Framework, SrcMgr::CharacteristicKind HeadersDirKind,
+  HeaderSearch , FrameworkHeaderPath ) {
+auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
+auto *CachedSpelling = >second;
+if (!Res.second) {
+  return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
+: CachedSpelling->PublicHeader;
+}
+bool IsSystem = isSystem(HeadersDirKind);
+SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir);
+llvm::sys::path::append(UmbrellaPath, "Headers", Framework + ".h");
+
+llvm::vfs::Status Status;
+auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
+if (!StatErr) {
+  if (IsSystem)
+CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework);
+  else
+CachedSpelling->PublicHeader =
+llvm::formatv("\"{0}/{0}.h\"", Framework);
+}
+
+UmbrellaPath = HeaderPath.HeadersParentDir;
+llvm::sys::path::append(UmbrellaPath, "PrivateHeaders",
+Framework + "_Private.h");
+
+StatErr = 

[clang-tools-extra] fb7ddd0 - Revert "[clangd] Properly compute framework-style include spelling"

2022-02-04 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-04T18:02:32-05:00
New Revision: fb7ddd0628f4894f9d14a2d1f84830607c5f946e

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

LOG: Revert "[clangd] Properly compute framework-style include spelling"

This reverts commit 4dfd11324eb05d167392958c0f0f273cae6386c6
due to the failures on Linux CI:
https://lab.llvm.org/buildbot/#/builders/188/builds/9296

Added: 


Modified: 
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 7ae77d35ad7a1..3257041ffa0e3 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -183,13 +183,6 @@ const Decl *getRefContainer(const Decl *Enclosing,
 // including filename normalization, URI conversion etc.
 // Expensive checks are cached internally.
 class SymbolCollector::HeaderFileURICache {
-  struct FrameworkUmbrellaSpelling {
-// Spelling for the public umbrella header, e.g. 
-llvm::Optional PublicHeader;
-// Spelling for the private umbrella header, e.g.
-// 
-llvm::Optional PrivateHeader;
-  };
   // Weird double-indirect access to PP, which might not be ready yet when
   // HeaderFiles is created but will be by the time it's used.
   // (IndexDataConsumer::setPreprocessor can happen before or after initialize)
@@ -200,9 +193,6 @@ class SymbolCollector::HeaderFileURICache {
   llvm::DenseMap CacheFEToURI;
   llvm::StringMap CachePathToURI;
   llvm::DenseMap CacheFIDToInclude;
-  llvm::StringMap CachePathToFrameworkSpelling;
-  llvm::StringMap
-  CacheFrameworkToUmbrellaHeaderSpelling;
 
 public:
   HeaderFileURICache(Preprocessor *, const SourceManager ,
@@ -259,125 +249,6 @@ class SymbolCollector::HeaderFileURICache {
 return R.first->second;
   }
 
-  struct FrameworkHeaderPath {
-// Path to the framework directory containing the Headers/PrivateHeaders
-// directories  e.g. /Frameworks/Foundation.framework/
-llvm::StringRef HeadersParentDir;
-// Subpath relative to the Headers or PrivateHeaders dir, e.g. NSObject.h
-// Note: This is NOT relative to the `HeadersParentDir`.
-llvm::StringRef HeaderSubpath;
-// Whether this header is under the PrivateHeaders dir
-bool IsPrivateHeader;
-  };
-
-  llvm::Optional
-  splitFrameworkHeaderPath(llvm::StringRef Path) {
-using namespace llvm::sys;
-path::reverse_iterator I = path::rbegin(Path);
-path::reverse_iterator Prev = I;
-path::reverse_iterator E = path::rend(Path);
-while (I != E) {
-  if (*I == "Headers") {
-FrameworkHeaderPath HeaderPath;
-HeaderPath.HeadersParentDir = Path.substr(0, I - E);
-HeaderPath.HeaderSubpath = Path.substr(Prev - E);
-return HeaderPath;
-  }
-  if (*I == "PrivateHeaders") {
-FrameworkHeaderPath HeaderPath;
-HeaderPath.HeadersParentDir = Path.substr(0, I - E);
-HeaderPath.HeaderSubpath = Path.substr(Prev - E);
-HeaderPath.IsPrivateHeader = true;
-return HeaderPath;
-  }
-  Prev = I;
-  ++I;
-}
-// Unexpected, must not be a framework header.
-return llvm::None;
-  }
-
-  // Frameworks typically have an umbrella header of the same name, e.g.
-  //  instead of  or
-  //  instead of
-  //  which should be used instead of directly
-  // importing the header.
-  llvm::Optional getFrameworkUmbrellaSpelling(
-  llvm::StringRef Framework, SrcMgr::CharacteristicKind HeadersDirKind,
-  HeaderSearch , FrameworkHeaderPath ) {
-auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
-auto *CachedSpelling = >second;
-if (!Res.second) {
-  return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
-: CachedSpelling->PublicHeader;
-}
-bool IsSystem = isSystem(HeadersDirKind);
-SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir);
-llvm::sys::path::append(UmbrellaPath, "Headers", Framework + ".h");
-
-llvm::vfs::Status Status;
-auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
-if (!StatErr) {
-  if (IsSystem)
-CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework);
-  else
-CachedSpelling->PublicHeader =
-llvm::formatv("\"{0}/{0}.h\"", Framework);
-}
-
-UmbrellaPath = HeaderPath.HeadersParentDir;
-llvm::sys::path::append(UmbrellaPath, "PrivateHeaders",
-Framework + "_Private.h");
-
-StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
-if 

[clang-tools-extra] 6abb70c - Attempt forward fix after 4dfd113

2022-02-04 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-04T17:47:38-05:00
New Revision: 6abb70c2d00812350153f4299a2491fdc3810ac3

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

LOG: Attempt forward fix after 4dfd113

If this doesn't work will just revert the change,
can't seem to repro on macOS.

Added: 


Modified: 
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 1b934bd2342eb..7c1e1a96db003 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -675,14 +675,11 @@ TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
   testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
   llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
   std::string PrivateFrameworkHeader = R"(
-#import 
+#import 
 
 @interface PrivateClass : NSObject
 @end
   )";
-  InMemoryFileSystem->addFile(
-  testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
-  llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
   InMemoryFileSystem->addFile(
   testPath(
   "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),



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


[clang-tools-extra] 4dfd113 - [clangd] Properly compute framework-style include spelling

2022-02-04 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-04T16:40:56-05:00
New Revision: 4dfd11324eb05d167392958c0f0f273cae6386c6

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

LOG: [clangd] Properly compute framework-style include spelling

With this change, clangd now computes framework-style includes
for framework headers at indexing time.

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

Added: 


Modified: 
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp

Removed: 




diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 3257041ffa0e3..7ae77d35ad7a1 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -183,6 +183,13 @@ const Decl *getRefContainer(const Decl *Enclosing,
 // including filename normalization, URI conversion etc.
 // Expensive checks are cached internally.
 class SymbolCollector::HeaderFileURICache {
+  struct FrameworkUmbrellaSpelling {
+// Spelling for the public umbrella header, e.g. 
+llvm::Optional PublicHeader;
+// Spelling for the private umbrella header, e.g.
+// 
+llvm::Optional PrivateHeader;
+  };
   // Weird double-indirect access to PP, which might not be ready yet when
   // HeaderFiles is created but will be by the time it's used.
   // (IndexDataConsumer::setPreprocessor can happen before or after initialize)
@@ -193,6 +200,9 @@ class SymbolCollector::HeaderFileURICache {
   llvm::DenseMap CacheFEToURI;
   llvm::StringMap CachePathToURI;
   llvm::DenseMap CacheFIDToInclude;
+  llvm::StringMap CachePathToFrameworkSpelling;
+  llvm::StringMap
+  CacheFrameworkToUmbrellaHeaderSpelling;
 
 public:
   HeaderFileURICache(Preprocessor *, const SourceManager ,
@@ -249,6 +259,125 @@ class SymbolCollector::HeaderFileURICache {
 return R.first->second;
   }
 
+  struct FrameworkHeaderPath {
+// Path to the framework directory containing the Headers/PrivateHeaders
+// directories  e.g. /Frameworks/Foundation.framework/
+llvm::StringRef HeadersParentDir;
+// Subpath relative to the Headers or PrivateHeaders dir, e.g. NSObject.h
+// Note: This is NOT relative to the `HeadersParentDir`.
+llvm::StringRef HeaderSubpath;
+// Whether this header is under the PrivateHeaders dir
+bool IsPrivateHeader;
+  };
+
+  llvm::Optional
+  splitFrameworkHeaderPath(llvm::StringRef Path) {
+using namespace llvm::sys;
+path::reverse_iterator I = path::rbegin(Path);
+path::reverse_iterator Prev = I;
+path::reverse_iterator E = path::rend(Path);
+while (I != E) {
+  if (*I == "Headers") {
+FrameworkHeaderPath HeaderPath;
+HeaderPath.HeadersParentDir = Path.substr(0, I - E);
+HeaderPath.HeaderSubpath = Path.substr(Prev - E);
+return HeaderPath;
+  }
+  if (*I == "PrivateHeaders") {
+FrameworkHeaderPath HeaderPath;
+HeaderPath.HeadersParentDir = Path.substr(0, I - E);
+HeaderPath.HeaderSubpath = Path.substr(Prev - E);
+HeaderPath.IsPrivateHeader = true;
+return HeaderPath;
+  }
+  Prev = I;
+  ++I;
+}
+// Unexpected, must not be a framework header.
+return llvm::None;
+  }
+
+  // Frameworks typically have an umbrella header of the same name, e.g.
+  //  instead of  or
+  //  instead of
+  //  which should be used instead of directly
+  // importing the header.
+  llvm::Optional getFrameworkUmbrellaSpelling(
+  llvm::StringRef Framework, SrcMgr::CharacteristicKind HeadersDirKind,
+  HeaderSearch , FrameworkHeaderPath ) {
+auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
+auto *CachedSpelling = >second;
+if (!Res.second) {
+  return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
+: CachedSpelling->PublicHeader;
+}
+bool IsSystem = isSystem(HeadersDirKind);
+SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir);
+llvm::sys::path::append(UmbrellaPath, "Headers", Framework + ".h");
+
+llvm::vfs::Status Status;
+auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
+if (!StatErr) {
+  if (IsSystem)
+CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework);
+  else
+CachedSpelling->PublicHeader =
+llvm::formatv("\"{0}/{0}.h\"", Framework);
+}
+
+UmbrellaPath = HeaderPath.HeadersParentDir;
+llvm::sys::path::append(UmbrellaPath, "PrivateHeaders",
+Framework + "_Private.h");
+
+StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
+if 

[clang] 9385ece - [HeaderSearch] Track framework name in LookupFile

2022-02-04 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-02-04T13:32:39-05:00
New Revision: 9385ece95a4a342ca4f4c46ea747f79d4ede9783

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

LOG: [HeaderSearch] Track framework name in LookupFile

Previously, the Framework name was only set if the file
came from a header mapped framework; now we'll always
set the framework name if the file is in a framework.

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

Added: 


Modified: 
clang/lib/Lex/HeaderSearch.cpp
clang/unittests/Lex/HeaderSearchTest.cpp

Removed: 




diff  --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index 39c125c395ef8..19e284f04b38c 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -1037,8 +1037,9 @@ Optional HeaderSearch::LookupFile(
   }
 }
 
-// If this file is found in a header map and uses the framework style of
-// includes, then this header is part of a framework we're building.
+// Set the `Framework` info if this file is in a header map with framework
+// style include spelling or found in a framework dir. The header map case
+// is possible when building frameworks which use header maps.
 if (CurDir->isHeaderMap() && isAngled) {
   size_t SlashPos = Filename.find('/');
   if (SlashPos != StringRef::npos)
@@ -1046,6 +1047,11 @@ Optional HeaderSearch::LookupFile(
 getUniqueFrameworkName(StringRef(Filename.begin(), SlashPos));
   if (CurDir->isIndexHeaderMap())
 HFI.IndexHeaderMapHeader = 1;
+} else if (CurDir->isFramework()) {
+  size_t SlashPos = Filename.find('/');
+  if (SlashPos != StringRef::npos)
+HFI.Framework =
+getUniqueFrameworkName(StringRef(Filename.begin(), SlashPos));
 }
 
 if (checkMSVCHeaderSearch(Diags, MSFE ? >getFileEntry() : nullptr,

diff  --git a/clang/unittests/Lex/HeaderSearchTest.cpp 
b/clang/unittests/Lex/HeaderSearchTest.cpp
index 10ecbbd26659e..fbb5cb30754a7 100644
--- a/clang/unittests/Lex/HeaderSearchTest.cpp
+++ b/clang/unittests/Lex/HeaderSearchTest.cpp
@@ -186,6 +186,29 @@ TEST_F(HeaderSearchTest, NestedFramework) {
 "Sub/Sub.h");
 }
 
+TEST_F(HeaderSearchTest, HeaderFrameworkLookup) {
+  std::string HeaderPath = "/tmp/Frameworks/Foo.framework/Headers/Foo.h";
+  addSystemFrameworkSearchDir("/tmp/Frameworks");
+  VFS->addFile(
+  HeaderPath, 0, llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath),
+  /*User=*/None, /*Group=*/None, llvm::sys::fs::file_type::regular_file);
+
+  bool IsFrameworkFound = false;
+  auto FoundFile = Search.LookupFile(
+  "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr,
+  /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr,
+  /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr,
+  /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr, );
+
+  EXPECT_TRUE(FoundFile.hasValue());
+  EXPECT_TRUE(IsFrameworkFound);
+  auto  = FoundFile.getValue();
+  auto FI = Search.getExistingFileInfo(FE);
+  EXPECT_TRUE(FI);
+  EXPECT_TRUE(FI->IsValid);
+  EXPECT_EQ(FI->Framework.str(), "Foo");
+}
+
 // Helper struct with null terminator character to make MemoryBuffer happy.
 template 
 struct NullTerminatedFile : public FileTy {



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


[clang] 0cf7e61 - [clang][HeaderSearch] Support framework includes in suggestPath...

2022-01-10 Thread David Goldman via cfe-commits

Author: David Goldman
Date: 2022-01-10T12:25:53-05:00
New Revision: 0cf7e61a42c7a08b0332204f1b60aa9fd1562089

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

LOG: [clang][HeaderSearch] Support framework includes in suggestPath...

Clang will now search through the framework includes to identify
the framework include path to a file, and then suggest a framework
style include spelling for the file.

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

Added: 


Modified: 
clang/include/clang/Lex/HeaderSearch.h
clang/lib/Lex/HeaderSearch.cpp
clang/test/Modules/double-quotes.m
clang/unittests/Lex/HeaderSearchTest.cpp

Removed: 




diff  --git a/clang/include/clang/Lex/HeaderSearch.h 
b/clang/include/clang/Lex/HeaderSearch.h
index 0482bf6c053f..e056f009eae9 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -286,6 +286,12 @@ class HeaderSearch {
   /// Add an additional search path.
   void AddSearchPath(const DirectoryLookup , bool isAngled);
 
+  /// Add an additional system search path.
+  void AddSystemSearchPath(const DirectoryLookup ) {
+SearchDirs.push_back(dir);
+SearchDirsUsage.push_back(false);
+  }
+
   /// Set the list of system header prefixes.
   void SetSystemHeaderPrefixes(ArrayRef> P) {
 SystemHeaderPrefixes.assign(P.begin(), P.end());

diff  --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index f9d61ecef796..d4de4ed156b6 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -745,7 +745,8 @@ static const char *copyString(StringRef Str, 
llvm::BumpPtrAllocator ) {
 }
 
 static bool isFrameworkStylePath(StringRef Path, bool ,
- SmallVectorImpl ) {
+ SmallVectorImpl ,
+ SmallVectorImpl ) {
   using namespace llvm::sys;
   path::const_iterator I = path::begin(Path);
   path::const_iterator E = path::end(Path);
@@ -761,15 +762,22 @@ static bool isFrameworkStylePath(StringRef Path, bool 
,
   // and some other variations among these lines.
   int FoundComp = 0;
   while (I != E) {
-if (*I == "Headers")
+if (*I == "Headers") {
   ++FoundComp;
-if (I->endswith(".framework")) {
-  FrameworkName.append(I->begin(), I->end());
-  ++FoundComp;
-}
-if (*I == "PrivateHeaders") {
+} else if (*I == "PrivateHeaders") {
   ++FoundComp;
   IsPrivateHeader = true;
+} else if (I->endswith(".framework")) {
+  StringRef Name = I->drop_back(10); // Drop .framework
+  // Need to reset the strings and counter to support nested frameworks.
+  FrameworkName.clear();
+  FrameworkName.append(Name.begin(), Name.end());
+  IncludeSpelling.clear();
+  IncludeSpelling.append(Name.begin(), Name.end());
+  FoundComp = 1;
+} else if (FoundComp >= 2) {
+  IncludeSpelling.push_back('/');
+  IncludeSpelling.append(I->begin(), I->end());
 }
 ++I;
   }
@@ -784,20 +792,24 @@ diagnoseFrameworkInclude(DiagnosticsEngine , 
SourceLocation IncludeLoc,
  bool FoundByHeaderMap = false) {
   bool IsIncluderPrivateHeader = false;
   SmallString<128> FromFramework, ToFramework;
-  if (!isFrameworkStylePath(Includer, IsIncluderPrivateHeader, FromFramework))
+  SmallString<128> FromIncludeSpelling, ToIncludeSpelling;
+  if (!isFrameworkStylePath(Includer, IsIncluderPrivateHeader, FromFramework,
+FromIncludeSpelling))
 return;
   bool IsIncludeePrivateHeader = false;
-  bool IsIncludeeInFramework = isFrameworkStylePath(
-  IncludeFE->getName(), IsIncludeePrivateHeader, ToFramework);
+  bool IsIncludeeInFramework =
+  isFrameworkStylePath(IncludeFE->getName(), IsIncludeePrivateHeader,
+   ToFramework, ToIncludeSpelling);
 
   if (!isAngled && !FoundByHeaderMap) {
 SmallString<128> NewInclude("<");
 if (IsIncludeeInFramework) {
-  NewInclude += ToFramework.str().drop_back(10); // drop .framework
-  NewInclude += "/";
+  NewInclude += ToIncludeSpelling;
+  NewInclude += ">";
+} else {
+  NewInclude += IncludeFilename;
+  NewInclude += ">";
 }
-NewInclude += IncludeFilename;
-NewInclude += ">";
 Diags.Report(IncludeLoc, diag::warn_quoted_include_in_framework_header)
 << IncludeFilename
 << FixItHint::CreateReplacement(IncludeLoc, NewInclude);
@@ -1865,9 +1877,9 @@ std::string HeaderSearch::suggestPathToFileForDiagnostics(
   using namespace llvm::sys;
 
   unsigned BestPrefixLength = 0;
-  // Checks whether Dir and File shares a common prefix, if they do and that's
-  // the longest prefix we've seen so for it returns true and updates the
-  // 

  1   2   >