[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-28 Thread Younan Zhang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG23ef8bf9c0f3: [clangd][CodeComplete] Improve 
FunctionCanBeCall (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  T generic(U, V);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+T foo(U, V);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, <#typename U#>>(<#U#>, <#V#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -310,6 +310,23 @@
   bool isInterestingDecl(const NamedDecl *ND,
  bool ) const;
 
+  /// Decide whether or not a use of function Decl can be a call.
+  ///
+  /// \param ND the function declaration.
+  ///
+  /// \param BaseExprType the object type in a member access expression,
+  /// if any.

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 557443.
zyounan added a comment.

Fix the CI


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  T generic(U, V);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+T foo(U, V);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, <#typename U#>>(<#U#>, <#V#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -310,6 +310,23 @@
   bool isInterestingDecl(const NamedDecl *ND,
  bool ) const;
 
+  /// Decide whether or not a use of function Decl can be a call.
+  ///
+  /// \param ND the function declaration.
+  ///
+  /// \param BaseExprType the object type in a member access expression,
+  /// if any.
+  bool canFunctionBeCalled(const NamedDecl *ND, QualType BaseExprType) const;
+
+  /// Decide 

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 557442.
zyounan added a comment.

Sorry, wrong patch


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  T generic(U, V);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+T foo(U, V);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#U#>, <#V#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -310,6 +310,23 @@
   bool isInterestingDecl(const NamedDecl *ND,
  bool ) const;
 
+  /// Decide whether or not a use of function Decl can be a call.
+  ///
+  /// \param ND the function declaration.
+  ///
+  /// \param BaseExprType the object type in a member access expression,
+  /// if any.
+  bool canFunctionBeCalled(const NamedDecl *ND, QualType 

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 557441.
zyounan added a comment.

Address nits; discard default template parameters


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaTemplateDeduction.cpp

Index: clang/lib/Sema/SemaTemplateDeduction.cpp
===
--- clang/lib/Sema/SemaTemplateDeduction.cpp
+++ clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2239,6 +2239,16 @@
   llvm_unreachable("Invalid Type Class!");
 }
 
+Sema::TemplateDeductionResult Sema::DeduceTemplateArgumentsByTypeMatch(
+Sema , TemplateParameterList *TemplateParams, QualType P, QualType A,
+sema::TemplateDeductionInfo ,
+SmallVectorImpl , unsigned TDF,
+bool PartialOrdering, bool DeducedFromArrayBound) {
+  return ::DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, P, A, Info,
+  Deduced, TDF, PartialOrdering,
+  DeducedFromArrayBound);
+}
+
 static Sema::TemplateDeductionResult
 DeduceTemplateArguments(Sema , TemplateParameterList *TemplateParams,
 const TemplateArgument , TemplateArgument A,
Index: clang/lib/Sema/SemaOverload.cpp
===
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -12460,6 +12460,7 @@
 //   overloaded functions considered.
 FunctionDecl *Specialization = nullptr;
 TemplateDeductionInfo Info(FailedCandidates.getLocation());
+// TargetFunctionType->dump(llvm::errs(), Context);
 if (Sema::TemplateDeductionResult Result
   = S.DeduceTemplateArguments(FunctionTemplate,
   ,
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -41,6 +41,7 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
+// #include "clang/Sema/Template.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallBitVector.h"
@@ -1463,6 +1464,38 @@
 OverloadSet.Add(Method, Results.size());
   }
 
+#if 0
+  const FunctionTemplateDecl *FTD = nullptr;
+  FTD = dyn_cast(R.Declaration);
+  llvm::errs() << "SCC 0: " << (void *)FTD << " " << PreferredType.isNull()
+   << "\n";
+  if (FTD && PreferredType) {
+auto Pointee = PreferredType->getTypePtr()->getPointeeType();
+llvm::errs() << "SCC 1\n";
+if (!Pointee.isNull()) {
+  llvm::errs() << "SCC 2\n";
+  if (const auto *FT = Pointee->getAs()) {
+llvm::errs() << "SCC 3\n";
+sema::TemplateDeductionInfo Info(SourceLocation{});
+llvm::SmallVector Deduced;
+Deduced.resize(FTD->getTemplateParameters()->size());
+FT->dump();
+if (auto Result = Sema::DeduceTemplateArgumentsByTypeMatch(
+SemaRef, FTD->getTemplateParameters(),
+FTD->getTemplatedDecl()->getType(),
+FT->getCanonicalTypeInternal(), Info, Deduced, 48)) {
+  llvm::errs() << FTD->getNameAsString()
+   << " could not be deduced from enclosing type: "
+   << Result << "\n";
+} else
+  llvm::errs() << FTD->getNameAsString()
+   << " could be deduced from enclosing type"
+   << "\n";
+  }
+}
+  }
+#endif
+
   R.FunctionCanBeCall = canFunctionBeCalled(R.getDeclaration(), BaseExprType);
 
   // Insert this result into the set of results.
Index: clang/include/clang/Sema/Sema.h
===
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -9138,7 +9138,7 @@
 /// a dependent parameter type did not match the corresponding element
 /// of the corresponding argument (when deducing from an initializer list).
 TDK_DeducedMismatchNested,
-/// A non-depnedent component of the parameter did not match the
+/// A non-dependent component of the parameter did not match the
 /// corresponding component of the argument.
 TDK_NonDeducedMismatch,
 /// When performing template argument deduction for a function
@@ -9232,6 +9232,12 @@
   sema::TemplateDeductionInfo ,
   bool IsAddressOfFunction = false);
 
+  static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
+  Sema , TemplateParameterList *TemplateParams, QualType P, QualType A,
+  sema::TemplateDeductionInfo ,
+  SmallVectorImpl , unsigned TDF,
+  bool PartialOrdering = false, bool DeducedFromArrayBound = false);
+
  

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan marked 3 inline comments as done.
zyounan added a comment.

Thank you guys again for the long and meticulous review! :)

For a quick note, here are points I've excerpted from Nathan's reviews that 
need improvements later:

1. Implement a heuristic that works for function template pointers whose 
template parameters could be deduced from the declaring type. 
https://reviews.llvm.org/D156605#inline-1546819

2. Currently, the deducible template parameters are discarded by default; it 
may be more consistent to put the deduced parameters into an optional chunk as 
well, and let the consumer of the CodeCompletionString decide whether to use 
them. https://reviews.llvm.org/D156605#inline-1548400




Comment at: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp:594
+Contains(AllOf(named("generic"),
+   signature("(U, V)"),
+   snippetSuffix("<${1:typename T}, ${2:typename U}>";

nridge wrote:
> It seems a bit inconsistent that the signature includes parameters with 
> default arguments in the non-call case, and not in the call case. I guess the 
> reason for this is that in the non-call case, the parameters with defaults 
> become a `CK_Optional` chunk which clangd adds to the signature 
> [here](https://searchfox.org/llvm/rev/4c241a9335c3046e418e1b4afc162bc576c072fd/clang-tools-extra/clangd/CodeCompletionStrings.cpp#213-214),
>  while in the call case the deduced parameters (which include the ones with 
> defaults) are omitted from the completion string altogether.
> 
> It may be more consistent to put the deduced parameters into an optional 
> chunk as well, and let the consumer of the CodeCompletionString decide 
> whether to use them?
> 
> Anyways, that's an idea for the future, no change needed now.
> It seems a bit inconsistent that the signature includes parameters with 
> default arguments in the non-call case, and not in the call case.

Indeed. If we just want consistent behavior (i.e., both get omitted) for 
default parameters, it is easy to fix at the moment. Note that

1) the loop for default template params will run iff the bit vector `Deduced` 
is not empty.

2) the vector would be resized in `Sema::MarkDeducedTemplateParameters` to fit 
the template params, which has been avoided in my patch to emit all template 
params for non-call cases. As a result, `LastDeducibleArgument` would always be 
0 for non-calls, even with default template params.

We can change the `Deduced` to a default initialized N-size bit vector, where N 
is the size of template params. This way, the default params can be omitted in 
the loop as desired (by reducing `LastDeducibleArgument` to limit the range it 
would dump to the CCS chunk later), while non-default params get preserved.

> It may be more consistent to put the deduced parameters into an optional 
> chunk as well, and let the consumer of the CodeCompletionString decide 
> whether to use them?

Sounds reasonable.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

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


[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-17 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thank you Nathan for your constructive opinions! I've updated this patch again, 
hopefully this becomes better.




Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1387
 
-  // When completing a non-static member function (and not via
-  // dot/arrow member access) and we're not inside that class' scope,
-  // it can't be a call.
+  // Decide whether or not a non-static member function can be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) 
{

nridge wrote:
> zyounan wrote:
> > sammccall wrote:
> > > This is confusing: the `CCC_Symbol` test is part of the specific 
> > > heuristics being used (it's the "not via dot/arrow member access" part, 
> > > right?) but you've moved the comment explaining what it does.
> > > 
> > > Also this function is just getting too long, and we're inlining more 
> > > complicated control flow here.
> > > Can we extract a function?
> > > 
> > > ```
> > > const auto *Method = ...;
> > > if (Method & !Method->isStatic()) {
> > >   R.FunctionCanBeCall = canMethodBeCalled(...);
> > > }
> > > ```
> > > it's the "not via dot/arrow member access" part
> > 
> > (Sorry for being unaware of the historical context). But I think 
> > `CCC_Symbol` should mean "we're completing a name such as a function or 
> > type name" per its comment. The heuristic for dot/arrow member access 
> > actually lies on the next line, i.e., if the completing decl is a 
> > CXXMethodDecl.
> > 
> > > Can we extract a function?
> > 
> > Sure. 
> The check for `CompletionContext.getKind()` is in fact a part of the 
> heuristic:
> 
>  * for `f.method`, the kind is `CCC_DotMemberAccess`
>  * for `f->method`, the kind is `CCC_ArrowMemberAccess`
>  * for `f.Foo::method` and `f->Foo::method`, the kind is `CCC_Symbol`
> 
> (I realize that's a bit inconsistent. Maybe the `f.Foo::` and `f->Foo::` 
> cases should be using `DotMemberAccess` and `ArrowMemberAccess` as well? 
> Anyways, that's a pre-existing issue.)
> 
> So, the `if (CompletionContext.getKind() == 
> clang::CodeCompletionContext::CCC_Symbol)` check is what currently makes sure 
> that in the `f.method` and `f->method` cases, we just keep `FunctionCanBeCall 
> = true` without having to check any context or expression type.
> 
> I think it may be clearest to move this entire `if` block into the new 
> function (whose name can be generalized to `canBeCall` or similar), and here 
> just unconditionally set `R.FunctionCanBeCall = canBeCall(CompletionContext, 
> /* other things */)`.
> I think it may be clearest to move this entire if block into the new function

Nice suggestion. I'll take it.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:3577
   // containing all of the arguments up to the first deducible argument.
+  // Or, if this isn't a call, emit all the template arguments
+  // to disambiguate the (potential) overloads.

zyounan wrote:
> nridge wrote:
> > zyounan wrote:
> > > nridge wrote:
> > > > 1. If this is not a call, we can avoid running the 
> > > > `Sema::MarkDeducedTemplateParameters` operation altogether.
> > > > 
> > > > 2. A future improvement we could consider: if this is not a call, try 
> > > > to detect cases where the template parameters can be deduced from the 
> > > > surrounding context (["Deducing template arguments taking the address 
> > > > of a function template 
> > > > "](https://eel.is/c++draft/temp.deduct.funcaddr)). Maybe add a FIXME 
> > > > for this?
> > > > avoid running the Sema::MarkDeducedTemplateParameters operation 
> > > > altogether
> > > 
> > > I think doing so could cause too many adjustments to the flow, and I'm 
> > > afraid that the `Sema::MarkDeducedTemplateParameters` would be required 
> > > again when we decide to implement point 2.
> > > 
> > > I'm adding a FIXME anyway but leave the first intact. However, I'd be 
> > > happy to rearrange the logic flow if you insist.
> > I realized there's actually an open question here: if `FunctionCanBeCall == 
> > false`, do we want to include **all** the template parameters, or just the 
> > non-deducible ones?
> > 
> > Let's consider an example:
> > 
> > ```
> > class Foo {
> >   template 
> >   T convertTo(U from);
> > };
> > 
> > void bar() {
> >   Foo::^
> > }
> > ```
> > 
> > Here, `U` can be deduced but `T` cannot.
> > 
> > The current behaviour is to complete `convertTo`. That seems appropriate 
> > for a **call**, since `U` will be deduced from the call. But since this is 
> > not a call, wouldn't it be  better to complete `convertTo`?
> > But since this is not a call, wouldn't it be better to complete 
> > convertTo?
> 
> (Ugh, this is exactly this patch's initial intention, but how...?)
> 
> Oh, I made a mistake here: I presumed `LastDeducibleArgument` would always be 
> 0 if we're in a non-callable context, but this is wrong! This variable is 
> calculated based on the function parameters, which ought to be 

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-17 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 556909.
zyounan marked 2 inline comments as done.
zyounan added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  T generic(U, V);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+T foo(U, V);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#U#>, <#V#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -310,6 +310,23 @@
   bool isInterestingDecl(const NamedDecl *ND,
  bool ) const;
 
+  /// Decide whether or not a use of function Decl can be a call.
+  ///
+  /// \param ND the function declaration.
+  ///
+  /// \param BaseExprType the object type in a member access expression,
+  /// if any.
+  bool 

[PATCH] D158926: [clangd] Show parameter hints for operator()

2023-09-10 Thread Younan Zhang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGcbd6ac6165e6: [clangd] Show parameter hints for operator() 
(authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158926

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -89,7 +89,7 @@
ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
-  TU.ExtraArgs.push_back("-std=c++20");
+  TU.ExtraArgs.push_back("-std=c++23");
   TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
@@ -807,6 +807,42 @@
   )cpp");
 }
 
+TEST(ParameterHints, FunctionCallOperator) {
+  assertParameterHints(R"cpp(
+struct W {
+  void operator()(int x);
+};
+struct S : W {
+  using W::operator();
+  static void operator()(int x, int y);
+};
+void bar() {
+  auto l1 = [](int x) {};
+  auto l2 = [](int x) static {};
+
+  S s;
+  s($1[[1]]);
+  s.operator()($2[[1]]);
+  s.operator()($3[[1]], $4[[2]]);
+  S::operator()($5[[1]], $6[[2]]);
+
+  l1($7[[1]]);
+  l1.operator()($8[[1]]);
+  l2($9[[1]]);
+  l2.operator()($10[[1]]);
+
+  void (*ptr)(int a, int b) = ::operator();
+  ptr($11[[1]], $12[[2]]);
+}
+  )cpp",
+   ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"},
+   ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"},
+   ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"},
+   ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"},
+   ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"},
+   ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"});
+}
+
 TEST(ParameterHints, Macros) {
   // Handling of macros depends on where the call's argument list comes from.
 
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -586,11 +586,13 @@
 if (!Cfg.InlayHints.Parameters)
   return true;
 
-// Do not show parameter hints for operator calls written using operator
-// syntax or user-defined literals. (Among other reasons, the resulting
+bool IsFunctor = isFunctionObjectCallExpr(E);
+// Do not show parameter hints for user-defined literals or
+// operator calls except for operator(). (Among other reasons, the resulting
 // hints can look awkward, e.g. the expression can itself be a function
 // argument and then we'd get two hints side by side).
-if (isa(E) || isa(E))
+if ((isa(E) && !IsFunctor) ||
+isa(E))
   return true;
 
 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
@@ -607,7 +609,22 @@
 else
   return true;
 
-processCall(Callee, {E->getArgs(), E->getNumArgs()});
+// N4868 [over.call.object]p3 says,
+// The argument list submitted to overload resolution consists of the
+// argument expressions present in the function call syntax preceded by the
+// implied object argument (E).
+//
+// However, we don't have the implied object argument for static
+// operator() per clang::Sema::BuildCallToObjectOfClassType.
+llvm::ArrayRef Args = {E->getArgs(), E->getNumArgs()};
+if (IsFunctor)
+  // We don't have the implied object argument through
+  // a function pointer either.
+  if (const CXXMethodDecl *Method =
+  dyn_cast_or_null(Callee.Decl);
+  Method && Method->isInstance())
+Args = Args.drop_front(1);
+processCall(Callee, Args);
 return true;
   }
 
@@ -1203,6 +1220,12 @@
 return Range{HintStart, HintEnd};
   }
 
+  static bool isFunctionObjectCallExpr(CallExpr *E) noexcept {
+if (auto *CallExpr = dyn_cast(E))
+  return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call;
+return false;
+  }
+
   std::vector 
   ASTContext 
   const syntax::TokenBuffer 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-09-10 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:3577
   // containing all of the arguments up to the first deducible argument.
+  // Or, if this isn't a call, emit all the template arguments
+  // to disambiguate the (potential) overloads.

nridge wrote:
> zyounan wrote:
> > nridge wrote:
> > > 1. If this is not a call, we can avoid running the 
> > > `Sema::MarkDeducedTemplateParameters` operation altogether.
> > > 
> > > 2. A future improvement we could consider: if this is not a call, try to 
> > > detect cases where the template parameters can be deduced from the 
> > > surrounding context (["Deducing template arguments taking the address of 
> > > a function template "](https://eel.is/c++draft/temp.deduct.funcaddr)). 
> > > Maybe add a FIXME for this?
> > > avoid running the Sema::MarkDeducedTemplateParameters operation altogether
> > 
> > I think doing so could cause too many adjustments to the flow, and I'm 
> > afraid that the `Sema::MarkDeducedTemplateParameters` would be required 
> > again when we decide to implement point 2.
> > 
> > I'm adding a FIXME anyway but leave the first intact. However, I'd be happy 
> > to rearrange the logic flow if you insist.
> I realized there's actually an open question here: if `FunctionCanBeCall == 
> false`, do we want to include **all** the template parameters, or just the 
> non-deducible ones?
> 
> Let's consider an example:
> 
> ```
> class Foo {
>   template 
>   T convertTo(U from);
> };
> 
> void bar() {
>   Foo::^
> }
> ```
> 
> Here, `U` can be deduced but `T` cannot.
> 
> The current behaviour is to complete `convertTo`. That seems appropriate 
> for a **call**, since `U` will be deduced from the call. But since this is 
> not a call, wouldn't it be  better to complete `convertTo`?
> But since this is not a call, wouldn't it be better to complete convertTo U>?

(Ugh, this is exactly this patch's initial intention, but how...?)

Oh, I made a mistake here: I presumed `LastDeducibleArgument` would always be 0 
if we're in a non-callable context, but this is wrong! This variable is 
calculated based on the function parameters, which ought to be equal to the 
number of template parameters deducible in function parameters.

( I used to duplicate this `if` block for the `!FunctionCanBeCall` case but 
finally, I merged these two erroneously. :-( )


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

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


[PATCH] D158926: [clangd] Show parameter hints for operator()

2023-09-09 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 556358.
zyounan marked an inline comment as done.
zyounan added a comment.

Address a nit comment


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158926

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -89,7 +89,7 @@
ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
-  TU.ExtraArgs.push_back("-std=c++20");
+  TU.ExtraArgs.push_back("-std=c++23");
   TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
@@ -807,6 +807,42 @@
   )cpp");
 }
 
+TEST(ParameterHints, FunctionCallOperator) {
+  assertParameterHints(R"cpp(
+struct W {
+  void operator()(int x);
+};
+struct S : W {
+  using W::operator();
+  static void operator()(int x, int y);
+};
+void bar() {
+  auto l1 = [](int x) {};
+  auto l2 = [](int x) static {};
+
+  S s;
+  s($1[[1]]);
+  s.operator()($2[[1]]);
+  s.operator()($3[[1]], $4[[2]]);
+  S::operator()($5[[1]], $6[[2]]);
+
+  l1($7[[1]]);
+  l1.operator()($8[[1]]);
+  l2($9[[1]]);
+  l2.operator()($10[[1]]);
+
+  void (*ptr)(int a, int b) = ::operator();
+  ptr($11[[1]], $12[[2]]);
+}
+  )cpp",
+   ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"},
+   ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"},
+   ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"},
+   ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"},
+   ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"},
+   ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"});
+}
+
 TEST(ParameterHints, Macros) {
   // Handling of macros depends on where the call's argument list comes from.
 
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -586,11 +586,13 @@
 if (!Cfg.InlayHints.Parameters)
   return true;
 
-// Do not show parameter hints for operator calls written using operator
-// syntax or user-defined literals. (Among other reasons, the resulting
+bool IsFunctor = isFunctionObjectCallExpr(E);
+// Do not show parameter hints for user-defined literals or
+// operator calls except for operator(). (Among other reasons, the resulting
 // hints can look awkward, e.g. the expression can itself be a function
 // argument and then we'd get two hints side by side).
-if (isa(E) || isa(E))
+if ((isa(E) && !IsFunctor) ||
+isa(E))
   return true;
 
 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
@@ -607,7 +609,22 @@
 else
   return true;
 
-processCall(Callee, {E->getArgs(), E->getNumArgs()});
+// N4868 [over.call.object]p3 says,
+// The argument list submitted to overload resolution consists of the
+// argument expressions present in the function call syntax preceded by the
+// implied object argument (E).
+//
+// However, we don't have the implied object argument for static
+// operator() per clang::Sema::BuildCallToObjectOfClassType.
+llvm::ArrayRef Args = {E->getArgs(), E->getNumArgs()};
+if (IsFunctor)
+  // We don't have the implied object argument through
+  // a function pointer either.
+  if (const CXXMethodDecl *Method =
+  dyn_cast_or_null(Callee.Decl);
+  Method && Method->isInstance())
+Args = Args.drop_front(1);
+processCall(Callee, Args);
 return true;
   }
 
@@ -1203,6 +1220,12 @@
 return Range{HintStart, HintEnd};
   }
 
+  static bool isFunctionObjectCallExpr(CallExpr *E) noexcept {
+if (auto *CallExpr = dyn_cast(E))
+  return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call;
+return false;
+  }
+
   std::vector 
   ASTContext 
   const syntax::TokenBuffer 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D158926: [clangd] Show parameter hints for operator()

2023-09-09 Thread Younan Zhang via Phabricator via cfe-commits
zyounan marked 2 inline comments as done.
zyounan added inline comments.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:626
+  Method && Method->isInstance())
+Args = Args.drop_front(1);
+processCall(Callee, Args);

nridge wrote:
> Huh, that's an interesting inconsistency between CXXMemberCallExpr and 
> CXXOperatorCallExpr (that one include th implied object argument in getArgs() 
> and the other doesn't)
> 
> As always, thank you for writing thorough tests that give us confidence we're 
> doing the right thing :)
I'm glad to hear that! Thank you.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158926

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


[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-09-02 Thread Younan Zhang via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGe257c0a91906: [clang][clangd] Ensure the stack bottom before 
building AST (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/index/Background.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/ClangdMain.cpp
  clang/lib/Frontend/FrontendAction.cpp

Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -29,6 +29,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
@@ -710,6 +711,9 @@
 };
 
 int clangdMain(int argc, char *argv[]) {
+  // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'
+  // is enabled.
+  clang::noteBottomOfStack();
   llvm::InitializeAllTargetInfos();
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   llvm::sys::AddSignalHandler(
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/index/Background.cpp
===
--- clang-tools-extra/clangd/index/Background.cpp
+++ clang-tools-extra/clangd/index/Background.cpp
@@ -30,6 +30,7 @@
 #include "support/Trace.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
@@ -108,6 +109,7 @@
   for (unsigned I = 0; I < Opts.ThreadPoolSize; ++I) {
 ThreadPool.runAsync("background-worker-" + llvm::Twine(I + 1),
 [this, Ctx(Context::current().clone())]() mutable {
+  clang::noteBottomOfStack();
   WithContext BGContext(std::move(Ctx));
   Queue.work([&] { Rebuilder.idle(); });
 });
Index: clang-tools-extra/clangd/TUScheduler.cpp
===
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -63,6 +63,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/FunctionExtras.h"
@@ -464,6 +465,10 @@
   }
 
   void run() {
+// We mark the current as the stack bottom so that clang running on this
+// thread can notice the stack usage and prevent stack overflow with best
+// efforts. Same applies to other calls thoughout clangd.
+clang::noteBottomOfStack();
 while (true) {
   std::optional Throttle;
   {
@@ -1383,6 +1388,7 @@
 }
 
 void ASTWorker::run() {
+  clang::noteBottomOfStack();
   while (true) {
 {
   std::unique_lock Lock(Mutex);
@@ -1777,6 +1783,7 @@
Ctx = Context::current().derive(FileBeingProcessed,
std::string(File)),
   

[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-09-02 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 77.
zyounan marked an inline comment as done.
zyounan added a comment.

Remove the stray header


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/index/Background.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/ClangdMain.cpp
  clang/lib/Frontend/FrontendAction.cpp

Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -29,6 +29,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
@@ -710,6 +711,9 @@
 };
 
 int clangdMain(int argc, char *argv[]) {
+  // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'
+  // is enabled.
+  clang::noteBottomOfStack();
   llvm::InitializeAllTargetInfos();
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   llvm::sys::AddSignalHandler(
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/index/Background.cpp
===
--- clang-tools-extra/clangd/index/Background.cpp
+++ clang-tools-extra/clangd/index/Background.cpp
@@ -30,6 +30,7 @@
 #include "support/Trace.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
@@ -108,6 +109,7 @@
   for (unsigned I = 0; I < Opts.ThreadPoolSize; ++I) {
 ThreadPool.runAsync("background-worker-" + llvm::Twine(I + 1),
 [this, Ctx(Context::current().clone())]() mutable {
+  clang::noteBottomOfStack();
   WithContext BGContext(std::move(Ctx));
   Queue.work([&] { Rebuilder.idle(); });
 });
Index: clang-tools-extra/clangd/TUScheduler.cpp
===
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -63,6 +63,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/FunctionExtras.h"
@@ -464,6 +465,10 @@
   }
 
   void run() {
+// We mark the current as the stack bottom so that clang running on this
+// thread can notice the stack usage and prevent stack overflow with best
+// efforts. Same applies to other calls thoughout clangd.
+clang::noteBottomOfStack();
 while (true) {
   std::optional Throttle;
   {
@@ -1383,6 +1388,7 @@
 }
 
 void ASTWorker::run() {
+  clang::noteBottomOfStack();
   while (true) {
 {
   std::unique_lock Lock(Mutex);
@@ -1777,6 +1783,7 @@
Ctx = Context::current().derive(FileBeingProcessed,
std::string(File)),
Action = std::move(Action), this]() mutable {
+clang::noteBottomOfStack();
 

[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-09-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thanks! I hope I'm getting things right. Specifically,

1. `UpdateIndexCallbacks::indexStdlib()::Task` in `ClangdServer.cpp`
2. `PreambleThread::run()`, `ASTWorker::run()` and 
`TUScheduler::runWithPreamble()::Task` in `TUScheduler.cpp`
3. The lambda of `runAsync` in `BackgroundIndex::BackgroundIndex()` in 
`index/Background.cpp`.




Comment at: clang-tools-extra/clangd/support/Threading.cpp:102
 llvm::set_thread_name(Name);
+// Mark the bottom of the stack for clang to be aware of the stack usage 
and
+// prevent stack overflow.

sammccall wrote:
> ugh, I forgot: this function is part of clangBasic (which is not small!) and 
> clangdSupport shouldn't depend on clang at all.
> 
> I'm afraid the easiest fix is to move this to the tasks in the relevant 
> callsites:
> - indexStdlib() in ClangdServer.cpp
> - ASTWorker::run(), PreambleWorker::run(), TUScheduler::runWithPreamble() in 
> TUScheduler.cpp
> - BackgroundIndex() in index/Background.cpp
> 
> (I guess principled thing would be to make noteBottomOfStack() part of llvm 
> Support, but that seems complicated)
Ah, I've never noticed clangdSupport is independent of clang.

> (I guess principled thing would be to make noteBottomOfStack() part of llvm 
> Support, but that seems complicated)

Agreed. And I think it requires an RFC before we have that migrate to 
llvmSupport.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-09-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 555356.
zyounan added a comment.
Herald added a subscriber: javed.absar.

Disperse the call over clangd tasks


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/index/Background.cpp
  clang-tools-extra/clangd/support/Threading.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/ClangdMain.cpp
  clang/lib/Frontend/FrontendAction.cpp

Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -29,6 +29,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
@@ -710,6 +711,9 @@
 };
 
 int clangdMain(int argc, char *argv[]) {
+  // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'
+  // is enabled.
+  clang::noteBottomOfStack();
   llvm::InitializeAllTargetInfos();
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   llvm::sys::AddSignalHandler(
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/support/Threading.cpp
===
--- clang-tools-extra/clangd/support/Threading.cpp
+++ clang-tools-extra/clangd/support/Threading.cpp
@@ -8,6 +8,7 @@
 
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
Index: clang-tools-extra/clangd/index/Background.cpp
===
--- clang-tools-extra/clangd/index/Background.cpp
+++ clang-tools-extra/clangd/index/Background.cpp
@@ -30,6 +30,7 @@
 #include "support/Trace.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
@@ -108,6 +109,7 @@
   for (unsigned I = 0; I < Opts.ThreadPoolSize; ++I) {
 ThreadPool.runAsync("background-worker-" + llvm::Twine(I + 1),
 [this, Ctx(Context::current().clone())]() mutable {
+  clang::noteBottomOfStack();
   WithContext BGContext(std::move(Ctx));
   Queue.work([&] { Rebuilder.idle(); });
 });
Index: clang-tools-extra/clangd/TUScheduler.cpp
===
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -63,6 +63,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/FunctionExtras.h"
@@ -464,6 +465,10 @@
   }
 
   void run() {
+// We mark the current as the stack bottom so that clang running on this
+// thread can notice the stack usage and prevent stack overflow with best
+// efforts. Same applies to other calls thoughout clangd.
+

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-09-01 Thread Younan Zhang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG2fd01d75a863: [clang] Construct ExprRequirement with 
SubstitutionDiagnostic on SubstFailure (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // expected-error {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // #1
+
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // #2
+{ lhs == rhs } -> convertible_to; // #3
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) { // #4
+  return false;
+}
+
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end); // expected-error {{no matching function for call to 'compare'}} #5
+}
+
+// expected-note@#1 {{in instantiation of variable template specialization 'is_convertible_v' requested here}}
+// expected-note@#1 {{substituting template arguments into constraint expression here}}
+// expected-note@#3 {{checking the satisfaction of concept 'convertible_to'}}
+// expected-note@#2 {{substituting template arguments into constraint expression here}}
+// expected-note@#5 {{checking constraint satisfaction for template 'compare'}}
+// expected-note@#5 {{in instantiation of function template specialization 'compare' requested here}}
+
+// expected-note@#4 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}}
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#3 {{because 'convertible_to' would be invalid}}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include 

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-09-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan marked an inline comment as done.
zyounan added a comment.

Thanks again for the review!




Comment at: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp:28
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+

erichkeane wrote:
> So I typically don't do the 'error' with a bookmark, just put the 'error' 
> next to the line that causes the error, and bookmark the note.  It makes it 
> easier to read.
Good suggestion!


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

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


[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-09-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 555277.
zyounan marked an inline comment as done.
zyounan added a comment.

Address one last nit comment.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // expected-error {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // #1
+
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // #2
+{ lhs == rhs } -> convertible_to; // #3
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) { // #4
+  return false;
+}
+
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end); // expected-error {{no matching function for call to 'compare'}} #5
+}
+
+// expected-note@#1 {{in instantiation of variable template specialization 'is_convertible_v' requested here}}
+// expected-note@#1 {{substituting template arguments into constraint expression here}}
+// expected-note@#3 {{checking the satisfaction of concept 'convertible_to'}}
+// expected-note@#2 {{substituting template arguments into constraint expression here}}
+// expected-note@#5 {{checking constraint satisfaction for template 'compare'}}
+// expected-note@#5 {{in instantiation of function template specialization 'compare' requested here}}
+
+// expected-note@#4 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}}
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#3 {{because 'convertible_to' would be invalid}}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include 

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-31 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554919.
zyounan added a comment.

Rebase to main


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+template 
+concept convertible_to = is_convertible_v; // #2
+
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // #3
+{ lhs == rhs } -> convertible_to; // #4
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) { // #5
+  return false;
+}
+
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end); // #6
+}
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+// expected-note@#2 {{in instantiation of variable template specialization 'is_convertible_v' requested here}}
+// expected-note@#2 {{substituting template arguments into constraint expression here}}
+// expected-note@#4 {{checking the satisfaction of concept 'convertible_to'}}
+// expected-note@#3 {{substituting template arguments into constraint expression here}}
+// expected-note@#6 {{checking constraint satisfaction for template 'compare'}}
+// expected-note@#6 {{in instantiation of function template specialization 'compare' requested here}}
+
+// expected-error@#6 {{no matching function for call to 'compare'}}
+
+// expected-note@#5 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}}
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#4 {{because 'convertible_to' would be invalid}}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include 

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-31 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thanks for the review!




Comment at: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp:26
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}

erichkeane wrote:
> Please don't do this, actually write out the remaining diagnostics.  Same 
> with the notes above.
> 
> Additionally, please use the 'bookmark' feature of verify-consumer to make 
> sure the diagnostics/notes are in proper order (which makes it easier to 
> figure out when reviewing).
Thanks for reminding me. Will do that.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

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


[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-31 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554914.
zyounan marked an inline comment as done.
zyounan added a comment.

Expand diagnostics in the test.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+template 
+concept convertible_to = is_convertible_v; // #2
+
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // #3
+{ lhs == rhs } -> convertible_to; // #4
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) { // #5
+  return false;
+}
+
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end); // #6
+}
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+// expected-note@#2 {{in instantiation of variable template specialization 'is_convertible_v' requested here}}
+// expected-note@#2 {{substituting template arguments into constraint expression here}}
+// expected-note@#4 {{checking the satisfaction of concept 'convertible_to'}}
+// expected-note@#3 {{substituting template arguments into constraint expression here}}
+// expected-note@#6 {{checking constraint satisfaction for template 'compare'}}
+// expected-note@#6 {{in instantiation of function template specialization 'compare' requested here}}
+
+// expected-error@#6 {{no matching function for call to 'compare'}}
+
+// expected-note@#5 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}}
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#4 {{because 'convertible_to' would be invalid}}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 

[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-08-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thanks again for Richard and Sam's comments!

(I didn't add another lit test for asynchronous case since it essentially works 
the same as the synchronous one. Hope this isn't harmful.)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-08-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554674.
zyounan marked an inline comment as done.
zyounan added a comment.

Address comment


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/support/Threading.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/ClangdMain.cpp
  clang/lib/Frontend/FrontendAction.cpp


Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -29,6 +29,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
@@ -710,6 +711,9 @@
 };
 
 int clangdMain(int argc, char *argv[]) {
+  // Clang could run on the main thread. e.g., when the flag '-check' or 
'-sync'
+  // is enabled.
+  clang::noteBottomOfStack();
   llvm::InitializeAllTargetInfos();
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   llvm::sys::AddSignalHandler(
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/support/Threading.cpp
===
--- clang-tools-extra/clangd/support/Threading.cpp
+++ clang-tools-extra/clangd/support/Threading.cpp
@@ -8,6 +8,7 @@
 
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
@@ -98,6 +99,9 @@
   auto Task = [Name = Name.str(), Action = std::move(Action),
Cleanup = std::move(CleanupTask)]() mutable {
 llvm::set_thread_name(Name);
+// Mark the bottom of the stack for clang to be aware of the stack usage 
and
+// prevent stack overflow.
+clang::noteBottomOfStack();
 Action();
 // Make sure function stored by ThreadFunc is destroyed before Cleanup 
runs.
 Action = nullptr;


Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -29,6 +29,7 @@
 #include "support/ThreadCrashReporter.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include 

[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-08-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554285.
zyounan added a comment.

Oops, something went wrong accidently.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/support/Threading.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/Check.cpp
  clang/lib/Frontend/FrontendAction.cpp


Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/Check.cpp
===
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -57,6 +57,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ -441,6 +442,7 @@
 
 bool check(llvm::StringRef File, const ThreadsafeFS ,
const ClangdLSPServer::Options ) {
+  clang::noteBottomOfStack();
   std::optional LineRange;
   if (!CheckFileLines.empty()) {
 uint32_t Begin = 0, End = std::numeric_limits::max();
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/support/Threading.cpp
===
--- clang-tools-extra/clangd/support/Threading.cpp
+++ clang-tools-extra/clangd/support/Threading.cpp
@@ -8,6 +8,7 @@
 
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
@@ -98,6 +99,9 @@
   auto Task = [Name = Name.str(), Action = std::move(Action),
Cleanup = std::move(CleanupTask)]() mutable {
 llvm::set_thread_name(Name);
+// Mark the bottom of the stack for clang to be aware of the stack usage 
and
+// prevent stack overflow.
+clang::noteBottomOfStack();
 Action();
 // Make sure function stored by ThreadFunc is destroyed before Cleanup 
runs.
 Action = nullptr;


Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/Check.cpp
===
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -57,6 +57,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ 

[PATCH] D158967: [clang][clangd] Ensure the stack bottom before building AST

2023-08-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

@ilya-biryukov I've updated the patch following your suggestion. PTAL, thanks!


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554283.
zyounan added a comment.

.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/support/Threading.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/Check.cpp
  clang/lib/Frontend/FrontendAction.cpp

Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1155,6 +1156,10 @@
   CompilerInstance  = getCompilerInstance();
   if (!CI.hasPreprocessor())
 return;
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
 
   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
   // here so the source manager would be initialized.
Index: clang-tools-extra/clangd/tool/Check.cpp
===
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -57,6 +57,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ -441,6 +442,7 @@
 
 bool check(llvm::StringRef File, const ThreadsafeFS ,
const ClangdLSPServer::Options ) {
+  clang::noteBottomOfStack();
   std::optional LineRange;
   if (!CheckFileLines.empty()) {
 uint32_t Begin = 0, End = std::numeric_limits::max();
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/support/Threading.cpp
===
--- clang-tools-extra/clangd/support/Threading.cpp
+++ clang-tools-extra/clangd/support/Threading.cpp
@@ -8,6 +8,7 @@
 
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
@@ -98,6 +99,9 @@
   auto Task = [Name = Name.str(), Action = std::move(Action),
Cleanup = std::move(CleanupTask)]() mutable {
 llvm::set_thread_name(Name);
+// Mark the bottom of the stack for clang to be aware of the stack usage and
+// prevent stack overflow.
+clang::noteBottomOfStack();
 Action();
 // Make sure function stored by ThreadFunc is destroyed before Cleanup runs.
 Action = nullptr;
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -44,6 +44,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -388,6 +389,7 @@
  std::unique_ptr CI,
  llvm::ArrayRef CompilerInvocationDiags,
  std::shared_ptr Preamble) {
+  clang::noteBottomOfStack();
   trace::Span Tracer("BuildAST");
   SPAN_ATTACH(Tracer, "File", Filename);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 554279.
zyounan added a comment.
Herald added a project: clang.

Adopt the comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/support/Threading.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test
  clang-tools-extra/clangd/tool/Check.cpp
  clang/lib/Frontend/FrontendAction.cpp

Index: clang/lib/Frontend/FrontendAction.cpp
===
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/FileEntry.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/Sarif.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -1054,6 +1055,11 @@
 }
 
 llvm::Error FrontendAction::Execute() {
+  // This is a fallback: If the client forgets to invoke this, we mark the
+  // current stack as the bottom. Though not optimal, this could help prevent
+  // stack overflow during deep recursion.
+  clang::noteBottomOfStack();
+
   CompilerInstance  = getCompilerInstance();
 
   if (CI.hasFrontendTimer()) {
Index: clang-tools-extra/clangd/tool/Check.cpp
===
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -57,6 +57,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ -441,6 +442,7 @@
 
 bool check(llvm::StringRef File, const ThreadsafeFS ,
const ClangdLSPServer::Options ) {
+  clang::noteBottomOfStack();
   std::optional LineRange;
   if (!CheckFileLines.empty()) {
 uint32_t Begin = 0, End = std::numeric_limits::max();
Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/support/Threading.cpp
===
--- clang-tools-extra/clangd/support/Threading.cpp
+++ clang-tools-extra/clangd/support/Threading.cpp
@@ -8,6 +8,7 @@
 
 #include "support/Threading.h"
 #include "support/Trace.h"
+#include "clang/Basic/Stack.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
@@ -98,6 +99,9 @@
   auto Task = [Name = Name.str(), Action = std::move(Action),
Cleanup = std::move(CleanupTask)]() mutable {
 llvm::set_thread_name(Name);
+// Mark the bottom of the stack for clang to be aware of the stack usage and
+// prevent stack overflow.
+clang::noteBottomOfStack();
 Action();
 // Make sure function stored by ThreadFunc is destroyed before Cleanup runs.
 Action = nullptr;
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -44,6 +44,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -388,6 +389,7 @@
  std::unique_ptr CI,
  llvm::ArrayRef CompilerInvocationDiags,
  std::shared_ptr Preamble) {
+  clang::noteBottomOfStack();
   trace::Span Tracer("BuildAST");
   SPAN_ATTACH(Tracer, "File", Filename);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

> According to documentation of noteBottomOfStack, those should be tied to 
> thread creation, i.e. main and AsyncTaskRunner are two most common entry 
> points for threads that do parsing.

Yeah, I was about to put this call there. But they both boil down to the single 
`ParsedAST::build()`, right? (Admittedly, this breaks the "encapsulation" if we 
say 'the build process for AST should not bring any other side effects besides 
the AST itself', and this doesn't adhere to the document that it should be 
created at the beginning of the thread/program.)

> Currently the code in CompilerInstance::ExecuteAction seems to acknowledge 
> that there should be a fallback. I'm suggesting to move this fallback down to 
> a function that actually runs parsing.

Upon the implementation, the result will be more accurate if it occurs earlier 
before diving into parsing. Maybe we can do both, that put this call at clangd 
site and libclang's `ASTFrontendAction::ExecuteAction`?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a subscriber: rsmith.
zyounan added a comment.

Thanks for the reply. And I understand (and agree with) the point that it's 
better to solve this problem once and for all.

> Currently the code in CompilerInstance::ExecuteAction seems to acknowledge 
> that there should be a fallback. I'm suggesting to move this fallback down to 
> a function that actually runs parsing.

One thing I'm afraid of is, that there are/were some compatible reasons that 
made us decide not to insert this call at `ASTFrontendAction::ExecuteAction` 
nor `clang::ParseAST`. (I didn't see the explanation in D66361 
. Richard, could you kindly explain why? 
@rsmith)

> That's `CompilerInstance::ExecuteAction`

Sorry for my mistake. I thought placing that in `CompilerInstance` was enough 
for most tools since it's less likely for developers to invoke the 
FrontendAction on their own, and for "advanced" users (like clangd) who'd like 
to control every step handling the AST, maybe it's fine to give them the 
opportunity to decide if it should crash on infinite recursion?

However, creating such a discrepancy doesn't make much sense. I'm happy to move 
this call to libclang if this won't break many things.

> Could you clarify what kind of encapsulation would it break?

(That's just my spitballing, please don't mind. :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

> `clang::ParseAST` or `ASTFrontendAction::ExecuteAction` look like good 
> candidates to me.

We had already placed the initialization in ASTFrontendAction::ExecuteAction 
,
 but we don't have such if we prefer invoking the action outside the libclang. 
(Just as what we're doing now at the clangd site.)

I'm not 100% sure if `clang::ParseAST` is appropriate since this might cause a 
side effect to the global state. Would this break the encapsulation hierarchy?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158967

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


[PATCH] D158967: [clangd] Record the stack bottom before building AST

2023-08-28 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added reviewers: sammccall, nridge.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

`clang::runWithSufficientStackSpace` requires the address of the
initial stack bottom to prevent potential stack overflows.

Fixes https://github.com/clangd/clangd/issues/1745.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D158967

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/test/infinite-instantiation.test


Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -44,6 +44,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -388,6 +389,7 @@
  std::unique_ptr CI,
  llvm::ArrayRef CompilerInvocationDiags,
  std::shared_ptr Preamble) {
+  clang::noteBottomOfStack();
   trace::Span Tracer("BuildAST");
   SPAN_ATTACH(Tracer, "File", Filename);
 


Index: clang-tools-extra/clangd/test/infinite-instantiation.test
===
--- /dev/null
+++ clang-tools-extra/clangd/test/infinite-instantiation.test
@@ -0,0 +1,13 @@
+// RUN: cp %s %t.cpp
+// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s
+
+// CHECK: [template_recursion_depth_exceeded]
+
+template 
+constexpr int f(T... args) {
+  return f(0, args...);
+}
+
+int main() {
+  auto i = f();
+}
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -44,6 +44,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Stack.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -388,6 +389,7 @@
  std::unique_ptr CI,
  llvm::ArrayRef CompilerInvocationDiags,
  std::shared_ptr Preamble) {
+  clang::noteBottomOfStack();
   trace::Span Tracer("BuildAST");
   SPAN_ATTACH(Tracer, "File", Filename);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-26 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Gently ping~


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

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


[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-26 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Gently ping~


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

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


[PATCH] D158926: [clangd] Show parameter hints for operator()

2023-08-26 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added a reviewer: nridge.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Closes https://github.com/clangd/clangd/issues/1742


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D158926

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -89,7 +89,7 @@
ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
-  TU.ExtraArgs.push_back("-std=c++20");
+  TU.ExtraArgs.push_back("-std=c++23");
   TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
@@ -807,6 +807,42 @@
   )cpp");
 }
 
+TEST(ParameterHints, FunctionCallOperator) {
+  assertParameterHints(R"cpp(
+struct W {
+  void operator()(int x);
+};
+struct S : W {
+  using W::operator();
+  static void operator()(int x, int y);
+};
+void bar() {
+  auto l1 = [](int x) {};
+  auto l2 = [](int x) static {};
+
+  S s;
+  s($1[[1]]);
+  s.operator()($2[[1]]);
+  s.operator()($3[[1]], $4[[2]]);
+  S::operator()($5[[1]], $6[[2]]);
+
+  l1($7[[1]]);
+  l1.operator()($8[[1]]);
+  l2($9[[1]]);
+  l2.operator()($10[[1]]);
+
+  void (*ptr)(int a, int b) = ::operator();
+  ptr($11[[1]], $12[[2]]);
+}
+  )cpp",
+   ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"},
+   ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"},
+   ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"},
+   ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"},
+   ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"},
+   ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"});
+}
+
 TEST(ParameterHints, Macros) {
   // Handling of macros depends on where the call's argument list comes from.
 
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -586,11 +586,13 @@
 if (!Cfg.InlayHints.Parameters)
   return true;
 
-// Do not show parameter hints for operator calls written using operator
-// syntax or user-defined literals. (Among other reasons, the resulting
+bool IsFunctor = isFunctionObjectCallExpr(E);
+// Do not show parameter hints for user-defined literals or
+// operator calls except for operator(). (Among other reasons, the resulting
 // hints can look awkward, e.g. the expression can itself be a function
 // argument and then we'd get two hints side by side).
-if (isa(E) || isa(E))
+if ((isa(E) && !IsFunctor) ||
+isa(E))
   return true;
 
 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
@@ -607,7 +609,22 @@
 else
   return true;
 
-processCall(Callee, {E->getArgs(), E->getNumArgs()});
+// N4868 [over.call.object]p3 says,
+// The argument list submitted to overload resolution consists of the
+// argument expressions present in the function call syntax preceded by the
+// implied object argument (E).
+//
+// However, we don't have the implied object argument for static
+// operator() per clang::Sema::BuildCallToObjectOfClassType.
+llvm::ArrayRef Args = {E->getArgs(), E->getNumArgs()};
+if (IsFunctor)
+  // We don't have the implied object argument through
+  // a function pointer either.
+  if (const CXXMethodDecl *Method =
+  dyn_cast_or_null(Callee.Decl);
+  Method && Method->isInstance())
+Args = Args.drop_front(1);
+processCall(Callee, Args);
 return true;
   }
 
@@ -1202,6 +1219,12 @@
 return Range{HintStart, HintEnd};
   }
 
+  bool isFunctionObjectCallExpr(CallExpr *E) const noexcept {
+if (auto *CallExpr = dyn_cast(E))
+  return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call;
+return false;
+  }
+
   std::vector 
   ASTContext 
   const syntax::TokenBuffer 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 551736.
zyounan added a comment.

Sorry, forgot to update tests :(


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(U);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+V foo(T, U);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#V#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#T#>, <#U#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseExprType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+   

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 551730.
zyounan added a comment.

Fix the bool expression.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(U);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+V foo(T, U);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#V#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#T#>, <#U#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseExprType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ 

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 551728.
zyounan added a comment.

Rebase and Remove the stray status


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // expected-note 0+{{}}
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // expected-note 0+{{}}
+{ lhs == rhs } -> convertible_to; // #2
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) {
+  return false;
+}
+
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#2 {{'convertible_to'}}
+
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end);
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -9074,16 +9075,22 @@
 MLTAL.addOuterRetainedLevels(TPL->getDepth());
 const TypeConstraint *TC = Param->getTypeConstraint();
 assert(TC && "Type Constraint cannot be null here");
-ExprResult Constraint =
-SubstExpr(TC->getImmediatelyDeclaredConstraint(), MLTAL);
+auto *IDC = TC->getImmediatelyDeclaredConstraint();
+assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
+ExprResult Constraint = SubstExpr(IDC, MLTAL);
 if (Constraint.isInvalid()) {
-  Status = concepts::ExprRequirement::SS_ExprSubstitutionFailure;
-} else {
-  SubstitutedConstraintExpr =
-  

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 551726.
zyounan added a comment.

Add release note


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // expected-note 0+{{}}
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // expected-note 0+{{}}
+{ lhs == rhs } -> convertible_to; // #2
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) {
+  return false;
+}
+
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#2 {{'convertible_to'}}
+
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end);
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -9074,16 +9075,23 @@
 MLTAL.addOuterRetainedLevels(TPL->getDepth());
 const TypeConstraint *TC = Param->getTypeConstraint();
 assert(TC && "Type Constraint cannot be null here");
-ExprResult Constraint =
-SubstExpr(TC->getImmediatelyDeclaredConstraint(), MLTAL);
+auto *IDC = TC->getImmediatelyDeclaredConstraint();
+assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
+ExprResult Constraint = SubstExpr(IDC, MLTAL);
 if (Constraint.isInvalid()) {
   Status = concepts::ExprRequirement::SS_ExprSubstitutionFailure;
-} else {
-  SubstitutedConstraintExpr =
-  

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thanks for Sam and Nathan's thorough review! I've updated, and please take 
another look.
(Apologize for churning on weekends, but I don't have chunk time during the 
week.)




Comment at: clang-tools-extra/clangd/CodeCompletionStrings.cpp:258
 case CodeCompletionString::CK_RightParen:
+  if (DropFunctionArguments &&
+  ResultKind == CodeCompletionResult::RK_Declaration)

sammccall wrote:
> nridge wrote:
> > It looks like we are assuming two things:
> > 
> >  1. Any LeftParen in an RK_Declaration starts a function argument list
> >  2. Everything that comes after the function argument list can be discarded
> > 
> > I think these assumptions are fine to make, but let's be explicit about 
> > them in a comment
> Agree, but also I think the code could reflect this more directly:
>  - this should trigger only for CK_LeftParen, not CK_RightParen
> 
> Rather than redirecting output to a different string by reassigning the 
> param, I think it would be a bit more direct to have
> 
> ```
> optional TruncateSnippetAt;
> ...
> case CK_LeftBracket:
>   if (DropFunctionArguments && ... && !TruncateSnippetAt)
> TruncateSnippetAt = Snippet->size();
> ...
> if (TruncateSnippetAt)
>   Snippet->resize(*TruncateSnippetAt);
> }
> ```
> 
> (though still not totally clear)
> It looks like we are assuming two things

Honestly, I don't quite prefer this assumption. However, we have lost some of 
the semantics e.g., the structure of a call expression within this function, 
and unfortunately it's not trivial to pass these out from clang. :-(

> (though still not totally clear)

Yeah. I imagine a clearer way would be separating the calculation for 
`Signature` and `Snippet`, and we could bail out early for `Snippet`. But I'm 
afraid that making so many adjustments to `getSignature` for such a small 
feature is inappropriate.

Assuming the left parenthesis is the beginning of a call might be sufficient 
for the time being, and let's leave the refactoring to subsequent patches.




Comment at: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp:580
 auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
 EXPECT_THAT(Results.Completions,
 Contains(AllOf(named("method"), snippetSuffix("";

nridge wrote:
> Since you're updating this test anyways, could you add `signature()` matchers 
> for the non-template cases as well please?
Of course!



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:345
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ bool InBaseClass, QualType BaseType);
 

nridge wrote:
> The word "base" is a bit overloaded in this signature; in the parameter 
> `InBaseClass` it refers to inheritance, but in `BaseType` it refers to the 
> base expression of a `MemberExpr`.
> 
> Maybe we can name the new parameter `BaseExprType` to avoid confusion?
Makes sense to me. Thanks for the correction.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1285
 Result.ShadowDecl = Using;
 AddResult(Result, CurContext, Hiding);
 return;

nridge wrote:
> Should we propagate `BaseType` into this recursive call?
Yes. And thanks for spotting this. I added another test case reflecting it.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1387
 
-  // When completing a non-static member function (and not via
-  // dot/arrow member access) and we're not inside that class' scope,
-  // it can't be a call.
+  // Decide whether or not a non-static member function can be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) 
{

sammccall wrote:
> This is confusing: the `CCC_Symbol` test is part of the specific heuristics 
> being used (it's the "not via dot/arrow member access" part, right?) but 
> you've moved the comment explaining what it does.
> 
> Also this function is just getting too long, and we're inlining more 
> complicated control flow here.
> Can we extract a function?
> 
> ```
> const auto *Method = ...;
> if (Method & !Method->isStatic()) {
>   R.FunctionCanBeCall = canMethodBeCalled(...);
> }
> ```
> it's the "not via dot/arrow member access" part

(Sorry for being unaware of the historical context). But I think `CCC_Symbol` 
should mean "we're completing a name such as a function or type name" per its 
comment. The heuristic for dot/arrow member access actually lies on the next 
line, i.e., if the completing decl is a CXXMethodDecl.

> Can we extract a function?

Sure. 



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1425
+R.FunctionCanBeCall =
+MaybeDerived == MaybeBase || 
MaybeDerived->isDerivedFrom(MaybeBase);
+  }

nridge wrote:
> Is there any situation where `MaybeDerived == MaybeBase || 
> 

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-19 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 551723.
zyounan marked 14 inline comments as done.
zyounan added a comment.

Address many comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(U);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -199,6 +206,8 @@
 };
 
 struct Derived : Foo {
+  using Foo::method;
+  using Foo::generic;
   Derived() {
 Foo::$canBeCall^
   }
@@ -207,15 +216,29 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
+;
+d.Derived::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
+  ;
+  d.Derived::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +246,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +263,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+V foo(T, U);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#V#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#T#>, <#U#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseExprType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
-   

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-16 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang/lib/Sema/SemaExprCXX.cpp:9079
+auto *IDC = TC->getImmediatelyDeclaredConstraint();
+assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
+ExprResult Constraint = SubstExpr(IDC, MLTAL);

I'm adding another assertion here following up on D157554: If IDC is nullptr 
somehow, we will fall into the cast statement in the else block and crash 
anyway.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

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


[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-16 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 550713.
zyounan added a comment.

Adapt to D157554 


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // expected-note 0+{{}}
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // expected-note 0+{{}}
+{ lhs == rhs } -> convertible_to; // #2
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) {
+  return false;
+}
+
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#2 {{'convertible_to'}}
+
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end);
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -9074,16 +9075,23 @@
 MLTAL.addOuterRetainedLevels(TPL->getDepth());
 const TypeConstraint *TC = Param->getTypeConstraint();
 assert(TC && "Type Constraint cannot be null here");
-ExprResult Constraint =
-SubstExpr(TC->getImmediatelyDeclaredConstraint(), MLTAL);
+auto *IDC = TC->getImmediatelyDeclaredConstraint();
+assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
+ExprResult Constraint = SubstExpr(IDC, MLTAL);
 if (Constraint.isInvalid()) {
   Status = concepts::ExprRequirement::SS_ExprSubstitutionFailure;
-} else {
-  SubstitutedConstraintExpr =
-  

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-16 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 550683.
zyounan added a comment.

Rebase and format


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158061

Files:
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // expected-note 0+{{}}
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // expected-note 0+{{}}
+{ lhs == rhs } -> convertible_to; // #2
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) {
+  return false;
+}
+
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#2 {{'convertible_to'}}
+
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end);
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2271,9 +2271,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2297,6 +2297,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -9078,12 +9079,18 @@
 SubstExpr(TC->getImmediatelyDeclaredConstraint(), MLTAL);
 if (Constraint.isInvalid()) {
   Status = concepts::ExprRequirement::SS_ExprSubstitutionFailure;
-} else {
-  SubstitutedConstraintExpr =
-  cast(Constraint.get());
-  if (!SubstitutedConstraintExpr->isSatisfied())
-Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
-}
+  return new (Context) concepts::ExprRequirement(
+  concepts::createSubstDiagAt(*this, IDC->getExprLoc(),
+  [IDC, this](llvm::raw_ostream ) {
+IDC->printPretty(OS, 

[PATCH] D158061: [clang] Construct ExprRequirement with SubstitutionDiagnostic on SubstFailure

2023-08-16 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added reviewers: saar.raz, erichkeane, ilya-biryukov, aaron.ballman.
Herald added a subscriber: kadircet.
Herald added a project: All.
zyounan published this revision for review.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

We're expecting a SubstitutionDiagnostic in diagnoseUnsatisfiedRequirement
if the status of ExprRequirement is SubstFailure. Previously, the Requirement
was created with Expr on SubstFailure by mistake, which could lead to the
assertion failure in the subsequent diagnosis.

Fixes https://github.com/clangd/clangd/issues/1726
Fixes https://github.com/llvm/llvm-project/issues/64723
Fixes https://github.com/llvm/llvm-project/issues/64172

In addition, this patch also fixes an invalid test from D129499 
.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D158061

Files:
  clang/include/clang/AST/ExprConcepts.h
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
  clang/test/SemaCXX/concept-fatal-error.cpp

Index: clang/test/SemaCXX/concept-fatal-error.cpp
===
--- clang/test/SemaCXX/concept-fatal-error.cpp
+++ clang/test/SemaCXX/concept-fatal-error.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ferror-limit 1 -verify %s
 
 template 
 concept f = requires { 42; };
@@ -6,5 +6,5 @@
   // The missing semicolon will trigger an error and -ferror-limit=1 will make it fatal
   // We test that we do not crash in such cases (#55401)
   int i = requires { { i } f } // expected-error {{expected ';' at end of declaration list}}
-   // expected-error@* {{too many errros emitted}}
+   // expected-error@* {{too many errors emitted}}
 };
Index: clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+template  class normal_iterator {};
+
+template  struct is_convertible {};
+
+template 
+inline constexpr bool is_convertible_v = is_convertible::value; // #1
+
+// expected-error@#1 {{no member named 'value' in 'is_convertible'}}
+
+template 
+concept convertible_to = is_convertible_v; // expected-note 0+{{}}
+template 
+  requires requires(IteratorL lhs, IteratorR rhs) { // expected-note 0+{{}}
+{ lhs == rhs } -> convertible_to; // #2
+  }
+constexpr bool compare(normal_iterator lhs, normal_iterator rhs) {
+  return false;
+}
+
+// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
+// expected-note@#2 {{'convertible_to'}}
+
+// Consume remaining notes/errors.
+// expected-note@* 0+{{}}
+// expected-error@* 0+{{}}
+class Object;
+
+void function() {
+  normal_iterator begin, end;
+  compare(begin, end);
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2276,9 +2276,9 @@
   getPackIndex(Pack), Arg, TL.getNameLoc());
 }
 
-template
 static concepts::Requirement::SubstitutionDiagnostic *
-createSubstDiag(Sema , TemplateDeductionInfo , EntityPrinter Printer) {
+createSubstDiag(Sema , TemplateDeductionInfo ,
+concepts::EntityPrinter Printer) {
   SmallString<128> Message;
   SourceLocation ErrorLoc;
   if (Info.hasSFINAEDiagnostic()) {
@@ -2302,6 +2302,19 @@
   StringRef(MessageBuf, Message.size())};
 }
 
+concepts::Requirement::SubstitutionDiagnostic *
+concepts::createSubstDiagAt(Sema , SourceLocation Location,
+EntityPrinter Printer) {
+  SmallString<128> Entity;
+  llvm::raw_svector_ostream OS(Entity);
+  Printer(OS);
+  char *EntityBuf = new (S.Context) char[Entity.size()];
+  llvm::copy(Entity, EntityBuf);
+  return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
+  /*SubstitutedEntity=*/StringRef(EntityBuf, Entity.size()),
+  /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+}
+
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
 SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
 RequiresExprBodyDecl *Body, ArrayRef Params,
Index: clang/lib/Sema/SemaExprCXX.cpp
===
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -9076,12 

[PATCH] D156605: [clangd][CodeComplete] Improve FunctionCanBeCall

2023-08-06 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 547569.
zyounan added a comment.

Trigger the build


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(T);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -207,15 +214,25 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +240,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +257,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+V foo(T, U);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#V#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#T#>, <#U#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ bool InBaseClass, QualType BaseType);
 
   /// Add a new non-declaration result to this result set.
   void AddResult(Result R);
@@ -1262,7 +1265,8 @@
 }
 
 void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
-  

[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-08-06 Thread Younan Zhang via Phabricator via cfe-commits
zyounan abandoned this revision.
zyounan added a comment.

Closing this in favor of D156605 .


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D156605: [CodeComplete] Mark 'Derived().Base::foo()' CanBeCall

2023-08-06 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 547566.
zyounan added a comment.

Merge changes with function templates from D155370 



Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(T);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -207,15 +214,25 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
 }
 )cpp");
 
@@ -223,12 +240,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +257,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -341,3 +341,14 @@
   // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:339:10 %s -o - | FileCheck -check-prefix=CHECK-FIELD-DECLARED-VIA-USING %s
   // CHECK-FIELD-DECLARED-VIA-USING: [#int#]field (requires fix-it: {339:8-339:9} to "->")
 }
+
+namespace function_can_be_call {
+  struct S {
+template 
+V foo(T, U);
+  };
+
+  ::f
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:351:7 %s -o - | FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#V#]foo<<#typename T#>, <#typename U#>{#, <#typename V#>#}>(<#T#>, <#U#>)
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ bool InBaseClass, QualType BaseType);
 
   /// Add a new non-declaration result to this result set.
   void AddResult(Result R);
@@ -1262,7 +1265,8 @@
 }
 
 void 

[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-08-03 Thread Younan Zhang via Phabricator via cfe-commits
zyounan planned changes to this revision.
zyounan added a comment.

In D155370#4552967 , @nridge wrote:

> I was thinking of functions with different names (with a common prefix) and 
> different signatures, where the signature could be a useful piece of 
> information in addition to the name to help you choose the right one.

Ah, that makes sense! Honestly, I've never thought the signature could assist 
such a scenario.

> Note, we have a flag that affects this, `--completion-style=detailed`.

Thanks. Just learned this flag.

Apart from the `Signature`, I still have two improvements for `CanBeCall`. I 
think it's better to close this review and merge these two changes into one 
patch.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb1193c13a5f9: [clangd] Avoid unexpected desugaring in 
isSugaredTemplateParameter (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -84,11 +84,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -99,6 +101,13 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "",
+   std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1433,8 +1442,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1467,9 +1475,34 @@
 
 T elements[10];
   };
+  )cpp";
 
-  vector array;
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
+  vector array;
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1480,8 +1513,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1496,16 +1540,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,
+ExpectedHint{": Short", "short_name"},
+ExpectedHint{": static_vector", "vector_name"});
 }
 
 TEST(DesignatorHints, Basic) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- 

[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 546008.
zyounan added a comment.

Final update


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -84,11 +84,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -99,6 +101,13 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "",
+   std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1433,8 +1442,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1467,9 +1475,34 @@
 
 T elements[10];
   };
+  )cpp";
 
-  vector array;
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
+  vector array;
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1480,8 +1513,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1496,16 +1540,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,
+ExpectedHint{": Short", "short_name"},
+ExpectedHint{": static_vector", "vector_name"});
 }
 
 TEST(DesignatorHints, Basic) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -405,22 +405,47 @@
 // Determines if any intermediate type in desugaring QualType 

[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 546007.
zyounan added a comment.

.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -84,11 +84,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -99,6 +101,13 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "",
+   std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1433,8 +1442,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1467,9 +1475,34 @@
 
 T elements[10];
   };
+  )cpp";
 
-  vector array;
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
+  vector array;
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1480,8 +1513,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1496,16 +1540,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,
+ExpectedHint{": Short", "short_name"},
+ExpectedHint{": static_vector", "vector_name"});
 }
 
 TEST(DesignatorHints, Basic) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -405,22 +405,47 @@
 // Determines if any intermediate type in desugaring QualType QT is of
 // 

[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

In D155370#4545763 , @nridge wrote:

> (e.g. maybe you're looking for one with a particular parameter type).

I understand the second point that it'd be nice to offer the user a chance to 
see the arguments in order to help decide if the function is appropriate -- 
although in the context where `CanBeCall=false`, arguments don't disambiguate 
against the overloads, so we'd end up with the same function name after 
selecting the candidate. (An explicit cast may be required to perform overload 
resolution if necessary 
, but that 
should occur before the completion point `::Prefix^`.)

OTOH, we don't present arguments for overloads in the candidates:

F28542806: image.png 

(At least for VSCode, I'm not sure if others behave the same.)

So, I don't think it is that important to retain the `Signature`. Any thoughts?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Sorry, looks like I messed up with the commits before. Fixed it now.




Comment at: clang-tools-extra/clangd/InlayHints.cpp:207
+
+  // This is a bit tricky: we traverse the type structure and find whether or
+  // not a type in the desugaring process is of SubstTemplateTypeParmType.

nridge wrote:
> Nice find, this is indeed pretty tricky. I remember running into a similar 
> issue before in https://reviews.llvm.org/D124690#inline-1205484.
Cool. That looks far more intricate and I should take some time to understand 
the context. Thanks for the reference :)



Comment at: clang-tools-extra/clangd/unittests/InlayHintTests.cpp:1432
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
+  llvm::StringRef Header = R"cpp(

I feel like this test is becoming clunky: The validations here require an 
invented class template `vector` which I think should be better placed in a 
header; however, in the remaining snippet, it seems that I'm testing too many 
hints in one `assertHint` call. Should I split these cases into different 
snippets, or we just leave them as-is at the moment?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

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


[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 545922.
zyounan added a comment.

Fix the chaos in the previous update


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -81,11 +81,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -96,6 +98,13 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "",
+   std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1421,8 +1430,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1455,9 +1463,34 @@
 
 T elements[10];
   };
+  )cpp";
+
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
   vector array;
-
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1468,8 +1501,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1484,16 +1528,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,
+ExpectedHint{": Short", "short_name"},
+ExpectedHint{": static_vector", "vector_name"});
 }
 
 TEST(DesignatorHints, Basic) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -195,22 +195,47 @@
 // Determines if any intermediate type in desugaring 

[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-08-01 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 545920.
zyounan marked 2 inline comments as done.
zyounan added a comment.
Herald added a project: clang.

Update


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -214,15 +214,25 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
 }
 )cpp");
 
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ bool InBaseClass, QualType BaseType);
 
   /// Add a new non-declaration result to this result set.
   void AddResult(Result R);
@@ -1262,7 +1265,8 @@
 }
 
 void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
-  NamedDecl *Hiding, bool InBaseClass = false) {
+  NamedDecl *Hiding, bool InBaseClass = false,
+  QualType BaseType = QualType()) {
   if (R.Kind != Result::RK_Declaration) {
 // For non-declaration results, just add the result.
 Results.push_back(R);
@@ -1380,9 +1384,7 @@
 OverloadSet.Add(Method, Results.size());
   }
 
-  // When completing a non-static member function (and not via
-  // dot/arrow member access) and we're not inside that class' scope,
-  // it can't be a call.
+  // Decide whether or not a non-static member function can be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) {
 const NamedDecl *ND = R.getDeclaration();
 if (const auto *FuncTmpl = dyn_cast(ND)) {
@@ -1404,10 +1406,24 @@
 return nullptr;
   }();
 
+  // When completing a non-static member function (and not via
+  // dot/arrow member access) and we're not inside that class' scope,
+  // it can't be a call.
   R.FunctionCanBeCall =
   CurrentClassScope &&
   (CurrentClassScope == Method->getParent() ||
CurrentClassScope->isDerivedFrom(Method->getParent()));
+
+  // If the member access "." or "->" is followed by a qualified Id and the
+  // object type is derived from or the same as that of the Id, then
+  // the candidate functions should be perceived as calls.
+  if (const CXXRecordDecl *MaybeDerived = nullptr;
+  !BaseType.isNull() &&
+  (MaybeDerived = BaseType->getAsCXXRecordDecl())) {
+auto *MaybeBase = Method->getParent();
+R.FunctionCanBeCall =
+MaybeDerived == MaybeBase || MaybeDerived->isDerivedFrom(MaybeBase);
+  }
 }
   }
 
@@ -1683,7 +1699,7 @@
  bool InBaseClass) override {
 ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr,
  false, IsAccessible(ND, Ctx), FixIts);
-Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass);
+Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass, BaseType);
   }
 
   void EnteredContext(DeclContext *Ctx) override {
Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -81,11 +81,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   

[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-07-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1407
 
   R.FunctionCanBeCall =
   CurrentClassScope &&

zyounan wrote:
> The current heuristic results in the following:
> 
> ```
> struct Base {
>   void foo(int);
> };
> struct Derived : Base {
>   void foo(float);
> };
> 
> int main() {
>   Derived d;
>   d.Base::^// FunctionCanBeCall for `foo` is false.
> }
> ```
> 
> In situations where it is intended to invoke base member functions hidden by 
> subclasses, we shouldn't remove parentheses, IMO.
D156605 to address this.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D156605: [CodeComplete] Mark 'Derived().Base::foo()' CanBeCall

2023-07-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added reviewers: sammccall, nridge, hokein.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan published this revision for review.
Herald added projects: clang, clang-tools-extra.
Herald added a subscriber: cfe-commits.

This resolves https://reviews.llvm.org/D155370#4531274.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156605

Files:
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -214,15 +214,25 @@
 struct OtherClass {
   OtherClass() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+f.Foo::$canBeCall^
 ::$cannotBeCall^
+;
+d.Foo::$canBeCall^
   }
 };
 
 int main() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$cannotBeCall^
+  ;
+  d.Foo::$canBeCall^
 }
 )cpp");
 
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -338,8 +338,11 @@
   ///
   /// \param InBaseClass whether the result was found in a base
   /// class of the searched context.
+  ///
+  /// \param BaseType the type of expression that precedes the "." or "->"
+  /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
- bool InBaseClass);
+ bool InBaseClass, QualType BaseType);
 
   /// Add a new non-declaration result to this result set.
   void AddResult(Result R);
@@ -1262,7 +1265,8 @@
 }
 
 void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
-  NamedDecl *Hiding, bool InBaseClass = false) {
+  NamedDecl *Hiding, bool InBaseClass = false,
+  QualType BaseType = QualType()) {
   if (R.Kind != Result::RK_Declaration) {
 // For non-declaration results, just add the result.
 Results.push_back(R);
@@ -1380,9 +1384,7 @@
 OverloadSet.Add(Method, Results.size());
   }
 
-  // When completing a non-static member function (and not via
-  // dot/arrow member access) and we're not inside that class' scope,
-  // it can't be a call.
+  // Decide whether or not a non-static member function can be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) {
 const NamedDecl *ND = R.getDeclaration();
 if (const auto *FuncTmpl = dyn_cast(ND)) {
@@ -1404,10 +1406,24 @@
 return nullptr;
   }();
 
+  // When completing a non-static member function (and not via
+  // dot/arrow member access) and we're not inside that class' scope,
+  // it can't be a call.
   R.FunctionCanBeCall =
   CurrentClassScope &&
   (CurrentClassScope == Method->getParent() ||
CurrentClassScope->isDerivedFrom(Method->getParent()));
+
+  // If the member access "." or "->" is followed by a qualified Id and the
+  // object type is derived from or the same as that of the Id, then
+  // the candidate functions should be perceived as calls.
+  if (const CXXRecordDecl *MaybeDerived = nullptr;
+  !BaseType.isNull() &&
+  (MaybeDerived = BaseType->getAsCXXRecordDecl())) {
+auto *MaybeBase = Method->getParent();
+R.FunctionCanBeCall =
+MaybeDerived == MaybeBase || MaybeDerived->isDerivedFrom(MaybeBase);
+  }
 }
   }
 
@@ -1683,7 +1699,7 @@
  bool InBaseClass) override {
 ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr,
  false, IsAccessible(ND, Ctx), FixIts);
-Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass);
+Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass, BaseType);
   }
 
   void EnteredContext(DeclContext *Ctx) override {
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -552,15 +552,25 @@
   struct OtherClass {
 OtherClass() {
   Foo f;
+  Derived d;
   f.$canBeCall^
+  ; // Prevent parsing as 'f.f'
+  f.Foo::$canBeCall^
   ::$canNotBeCall^
+  ;
+  d.Foo::$canBeCall^
 }
   };
 
   int main() {
 Foo f;
+Derived d;
 f.$canBeCall^
+; // Prevent parsing as 'f.f'
+

[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-07-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Oops, my apologies for bothering you. Thanks again for the explanation and your 
dedication!


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

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


[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-07-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Ping~


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

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


[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-07-26 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 544252.
zyounan added a comment.

Format


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -81,11 +81,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -96,6 +98,13 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "",
+   std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1421,8 +1430,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1455,9 +1463,34 @@
 
 T elements[10];
   };
+  )cpp";
+
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
   vector array;
-
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1468,8 +1501,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1484,16 +1528,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,
+ExpectedHint{": Short", "short_name"},
+ExpectedHint{": static_vector", "vector_name"});
 }
 
 TEST(DesignatorHints, Basic) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -199,18 +199,45 @@
 // Neither `PointerType` nor `ReferenceType` is considered as sugared
 // 

[PATCH] D156300: [clangd] Avoid unexpected desugaring in isSugaredTemplateParameter

2023-07-26 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added a reviewer: nridge.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

This is a follow-up to D151785 , addressing 
https://github.com/clangd/clangd/issues/1703.

The previous approach of peeling pointer types during a traversal
using getPointeeType might have produced unexpected results; since
the method would implicitly desugar the type if the type being passed
in could not be cast to a Pointer-like Type.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156300

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -81,11 +81,13 @@
 }
 
 template 
-void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
- ExpectedHints... Expected) {
+void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+   llvm::StringRef HeaderContent,
+   ExpectedHints... Expected) {
   Annotations Source(AnnotatedSource);
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs.push_back("-std=c++20");
+  TU.HeaderCode = HeaderContent;
   auto AST = TU.build();
 
   EXPECT_THAT(hintsOfKind(AST, Kind),
@@ -96,6 +98,12 @@
   EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty());
 }
 
+template 
+void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
+ ExpectedHints... Expected) {
+  return assertHintsWithHeader(Kind, AnnotatedSource, "", std::move(Expected)...);
+}
+
 // Hack to allow expression-statements operating on parameter packs in C++14.
 template  void ignore(T &&...) {}
 
@@ -1421,8 +1429,7 @@
 }
 
 TEST(TypeHints, SubstTemplateParameterAliases) {
-  assertTypeHints(
-  R"cpp(
+  llvm::StringRef Header = R"cpp(
   template  struct allocator {};
 
   template 
@@ -1455,9 +1462,34 @@
 
 T elements[10];
   };
+  )cpp";
+
+  llvm::StringRef VectorIntPtr = R"cpp(
+vector array;
+auto $no_modifier[[x]] = array[3];
+auto* $ptr_modifier[[ptr]] = [3];
+auto& $ref_modifier[[ref]] = array[3];
+auto& $at[[immutable]] = array.at(3);
+
+auto $data[[data]] = array.data();
+auto $allocator[[alloc]] = array.get_allocator();
+auto $size[[size]] = array.size();
+auto $begin[[begin]] = array.begin();
+auto $end[[end]] = array.end();
+  )cpp";
+
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorIntPtr, Header,
+  ExpectedHint{": int *", "no_modifier"},
+  ExpectedHint{": int **", "ptr_modifier"},
+  ExpectedHint{": int *&", "ref_modifier"},
+  ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef VectorInt = R"cpp(
   vector array;
-
   auto $no_modifier[[by_value]] = array[3];
   auto* $ptr_modifier[[ptr]] = [3];
   auto& $ref_modifier[[ref]] = array[3];
@@ -1468,8 +1500,19 @@
   auto $size[[size]] = array.size();
   auto $begin[[begin]] = array.begin();
   auto $end[[end]] = array.end();
+  )cpp";
 
+  assertHintsWithHeader(
+  InlayHintKind::Type, VectorInt, Header,
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"});
 
+  llvm::StringRef TypeAlias = R"cpp(
   // If the type alias is not of substituted template parameter type,
   // do not show desugared type.
   using VeryLongLongTypeName = my_iterator;
@@ -1484,16 +1527,11 @@
   using static_vector = basic_static_vector>;
 
   auto $vector_name[[vec]] = static_vector();
-  )cpp",
-  ExpectedHint{": int", "no_modifier"},
-  ExpectedHint{": int *", "ptr_modifier"},
-  ExpectedHint{": int &", "ref_modifier"},
-  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
-  ExpectedHint{": allocator", "allocator"},
-  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
-  ExpectedHint{": non_template_iterator", "end"},
-  ExpectedHint{": Short", "short_name"},
-  ExpectedHint{": static_vector", "vector_name"});
+  )cpp";
+
+  assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,

[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-07-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang/lib/Sema/SemaCodeComplete.cpp:1407
 
   R.FunctionCanBeCall =
   CurrentClassScope &&

The current heuristic results in the following:

```
struct Base {
  void foo(int);
};
struct Derived : Base {
  void foo(float);
};

int main() {
  Derived d;
  d.Base::^// FunctionCanBeCall for `foo` is false.
}
```

In situations where it is intended to invoke base member functions hidden by 
subclasses, we shouldn't remove parentheses, IMO.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D155370: [CodeComplete] Improve FunctionCanBeCall

2023-07-23 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thanks for the insightful suggestions!
(Apologies for my late update. Just had a really busy week.)

As suggested, I left the CCR intact and handled these functions in 
`CodeCompletionResult::createCodeCompletionStringForDecl`. I think this 
preserves the Declaration, right? (While I think we //could// get the 
associated Decl if using `RK_Pattern`, however the current approach looks more 
terse to me.)

I also noticed that the previous implementation did not consider function 
templates. For example,

  struct S {
template 
void foo(T);
  
void bar();
  };
  
  ::bar^ // getting `S::bar`
  ::foo^ // getting `S::foo(T)`!

This brings a discrepancy, which I have also fixed in this patch. I hope this 
doesn't cause too much inconvenience for the review :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

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


[PATCH] D155370: [CodeComplete] Don't emit parameters when not FunctionCanBeCall

2023-07-23 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 543280.
zyounan added a comment.

Format


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/test/Index/complete-qualified.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(T);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -223,12 +230,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +247,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/Index/complete-qualified.cpp
===
--- clang/test/Index/complete-qualified.cpp
+++ clang/test/Index/complete-qualified.cpp
@@ -16,5 +16,5 @@
 // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35)
 // CHECK-CC1: ClassDecl:{TypedText Foo} (35)
-// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )}
-// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (80)
+// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}
+// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo} (80)
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -171,7 +171,7 @@
 template
 void dependentColonColonCompletion() {
   Template::staticFn();
-// CHECK-CC7: function : [#void#]function()
+// CHECK-CC7: function : [#void#]function
 // CHECK-CC7: Nested : Nested
 // CHECK-CC7: o1 : [#BaseTemplate#]o1
 // CHECK-CC7: o2 : [#BaseTemplate#]o2
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -1384,7 +1384,11 @@
   // dot/arrow member access) and we're not inside that class' scope,
   // it can't be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) {
-const auto *Method = dyn_cast(R.getDeclaration());
+const NamedDecl *ND = R.getDeclaration();
+if (const auto *FuncTmpl = dyn_cast(ND)) {
+  ND = FuncTmpl->getTemplatedDecl();
+}
+const auto *Method = dyn_cast(ND);
 if (Method && !Method->isStatic()) {
   // Find the class scope that we're currently in.
   // We could e.g. be inside a lambda, so walk up the DeclContext until we
@@ -3494,6 +3498,10 @@
 AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
Ctx, Policy);
 AddTypedNameChunk(Ctx, Policy, ND, Result);
+// We don't emit 

[PATCH] D155370: [CodeComplete] Don't emit parameters when not FunctionCanBeCall

2023-07-23 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 543277.
zyounan added a comment.

Don't change the CCR. Handle these in CreateCodeCompletionString. Emit template 
arguments if possible.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155370

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/test/Index/complete-qualified.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -60,7 +60,10 @@
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
   if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+auto *ND = R.getDeclaration();
+if (auto *Template = llvm::dyn_cast(ND))
+  ND = Template->getTemplatedDecl();
+if (const auto *FD = llvm::dyn_cast(ND)) {
   CompletedFunctionDecl D;
   D.Name = FD->getNameAsString();
   D.CanBeCall = R.FunctionCanBeCall;
@@ -191,6 +194,10 @@
 struct Foo {
   static int staticMethod();
   int method() const;
+  template 
+  void generic(T);
+  template 
+  static T staticGeneric();
   Foo() {
 this->$canBeCall^
 $canBeCall^
@@ -223,12 +230,16 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(true;
   }
 
   for (const auto  : Code.points("cannotBeCall")) {
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),
 canBeCall(false;
+EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),
+canBeCall(false;
   }
 
   // static method can always be a call
@@ -236,6 +247,8 @@
 auto Results = CollectCompletedFunctions(Code.code(), P);
 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),
 canBeCall(true;
+EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),
+canBeCall(true;
   }
 }
 
Index: clang/test/Index/complete-qualified.cpp
===
--- clang/test/Index/complete-qualified.cpp
+++ clang/test/Index/complete-qualified.cpp
@@ -16,5 +16,5 @@
 // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35)
 // CHECK-CC1: ClassDecl:{TypedText Foo} (35)
-// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )}
-// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (80)
+// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}
+// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo} (80)
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -171,7 +171,7 @@
 template
 void dependentColonColonCompletion() {
   Template::staticFn();
-// CHECK-CC7: function : [#void#]function()
+// CHECK-CC7: function : [#void#]function
 // CHECK-CC7: Nested : Nested
 // CHECK-CC7: o1 : [#BaseTemplate#]o1
 // CHECK-CC7: o2 : [#BaseTemplate#]o2
Index: clang/lib/Sema/SemaCodeComplete.cpp
===
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -1384,7 +1384,11 @@
   // dot/arrow member access) and we're not inside that class' scope,
   // it can't be a call.
   if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) {
-const auto *Method = dyn_cast(R.getDeclaration());
+const NamedDecl *ND = R.getDeclaration();
+if (const auto *FuncTmpl = dyn_cast(ND)) {
+  ND = FuncTmpl->getTemplatedDecl();
+}
+const auto *Method = dyn_cast(ND);
 if (Method && !Method->isStatic()) {
   // Find the class scope that we're currently in.
   // We could e.g. be inside a lambda, so walk up the DeclContext until we
@@ -3494,6 +3498,9 @@
 AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
 

[PATCH] D155370: [CodeComplete] Don't emit parameters when not FunctionCanBeCall

2023-07-15 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added reviewers: nridge, tom-anders, sammccall.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan added a comment.
zyounan updated this revision to Diff 540687.
zyounan published this revision for review.
Herald added projects: clang, clang-tools-extra.
Herald added a subscriber: cfe-commits.

Changing the code completion string to `RK_Pattern` makes the completion 
clearer, in a way that showing candidates with signatures in the completion 
items if `FunctionCanBeCall` while a mere name otherwise.

F28256820: image.png 

F28256841: image.png 

(Comparing to that we always show signatures even if no parameters are produced 
when hit the item.)

F28256859: image.png  -> F28256863: 
image.png 

---

P.S. Despite the fact that I changed many associated tests, I still hope people 
don't rely on the former behavior so that this patch could be marked as NFC. :D

I discovered that patch when trying to figure out why clangd doesn't complete 
function parameters for members in global scope while it produces parameters 
when I'm inside a call expression. Regard to the heuristic itself, it's 
currently a bit inconvenient that I have to switch headers and sources back and 
forth and do copy-pastes when I'm writing member function definitions outside 
of a class. I think we could be more lenient here e.g., don't set the flag on 
if we're in the global scope. What do you guys think? (I'm working on another 
follow-up patch to address this, although it is not strightforward to tell 
which scope the building Declarator is in inside Sema. )


zyounan added a comment.

Format


This tries to avoid extra calculations in D137040 
.

In that patch the extra completion strings were dropped at the client
site, after function parameters became available, to implement the
heuristic. However, we can make the process a bit more terse. In the
context where a call isn't required, we could teach the Sema to emit
the completion string without additional parameters by changing the Decl
to a Pattern with the function name.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155370

Files:
  clang-tools-extra/clangd/CodeComplete.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp
  clang/test/Index/complete-qualified.cpp
  clang/unittests/Sema/CodeCompleteTest.cpp

Index: clang/unittests/Sema/CodeCompleteTest.cpp
===
--- clang/unittests/Sema/CodeCompleteTest.cpp
+++ clang/unittests/Sema/CodeCompleteTest.cpp
@@ -59,14 +59,12 @@
   unsigned NumResults) override {
 for (unsigned I = 0; I < NumResults; ++I) {
   auto R = Results[I];
-  if (R.Kind == CodeCompletionResult::RK_Declaration) {
-if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
-  CompletedFunctionDecl D;
-  D.Name = FD->getNameAsString();
-  D.CanBeCall = R.FunctionCanBeCall;
-  D.IsStatic = FD->isStatic();
-  CompletedFuncDecls.emplace_back(std::move(D));
-}
+  if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) {
+CompletedFunctionDecl D;
+D.Name = FD->getNameAsString();
+D.CanBeCall = R.FunctionCanBeCall;
+D.IsStatic = FD->isStatic();
+CompletedFuncDecls.emplace_back(std::move(D));
   }
 }
   }
Index: clang/test/Index/complete-qualified.cpp
===
--- clang/test/Index/complete-qualified.cpp
+++ clang/test/Index/complete-qualified.cpp
@@ -16,5 +16,6 @@
 // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35)
 // CHECK-CC1: ClassDecl:{TypedText Foo} (35)
-// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )}
-// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (80)
+// CHECK-CC1: CXXMethod:{TypedText operator=} (50)
+// CHECK-CC1: CXXMethod:{TypedText operator=} (50)
+// CHECK-CC1: CXXMethod:{TypedText ~Foo} (50)
Index: clang/test/CodeCompletion/member-access.cpp
===
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -171,7 +171,7 @@
 template
 void dependentColonColonCompletion() {
   Template::staticFn();
-// CHECK-CC7: function : [#void#]function()
+// CHECK-CC7: Pattern : function
 // CHECK-CC7: Nested : Nested
 // CHECK-CC7: o1 : [#BaseTemplate#]o1
 // CHECK-CC7: o2 : [#BaseTemplate#]o2
Index: clang/lib/Sema/SemaCodeComplete.cpp

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG76d72a715038: [clang] Fix a crash on invalid destructor 
(authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #default
+} do_not_crash;
+// expected-error@#default {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#default {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is marked as `IneligibleOrNotSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -471,7 +471,7 @@
 - Fix crash when redefining a variable with an invalid type again with an
   invalid type. (`#62447 `_)
 - Fix a stack overflow issue when evaluating ``consteval`` default arguments.
-  (`#60082` `_)
+  (`#60082 `_)
 - Fix the assertion hit when generating code for global variable initializer of
   _BitInt(1) type.
   (`#62207 `_)
@@ -523,21 +523,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #default
+} do_not_crash;
+// expected-error@#default {{initializer on function does not look like a pure-specifier}}
+// expected-error@#default {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no 

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 534387.
zyounan added a comment.

.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #default
+} do_not_crash;
+// expected-error@#default {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#default {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is marked as `IneligibleOrNotSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -469,7 +469,7 @@
 - Fix crash when redefining a variable with an invalid type again with an
   invalid type. (`#62447 `_)
 - Fix a stack overflow issue when evaluating ``consteval`` default arguments.
-  (`#60082` `_)
+  (`#60082 `_)
 - Fix the assertion hit when generating code for global variable initializer of
   _BitInt(1) type.
   (`#62207 `_)
@@ -521,21 +521,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #default
+} do_not_crash;
+// expected-error@#default {{initializer on function does not look like a pure-specifier}}
+// expected-error@#default {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 534386.
zyounan added a comment.

Simplify test case


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is marked as `IneligibleOrNotSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -469,7 +469,7 @@
 - Fix crash when redefining a variable with an invalid type again with an
   invalid type. (`#62447 `_)
 - Fix a stack overflow issue when evaluating ``consteval`` default arguments.
-  (`#60082` `_)
+  (`#60082 `_)
 - Fix the assertion hit when generating code for global variable initializer of
   _BitInt(1) type.
   (`#62207 `_)
@@ -521,21 +521,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,18 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 534362.
zyounan added a comment.

Typo


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is marked as `IneligibleOrNotSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -469,7 +469,7 @@
 - Fix crash when redefining a variable with an invalid type again with an
   invalid type. (`#62447 `_)
 - Fix a stack overflow issue when evaluating ``consteval`` default arguments.
-  (`#60082` `_)
+  (`#60082 `_)
 - Fix the assertion hit when generating code for global variable initializer of
   _BitInt(1) type.
   (`#62207 `_)
@@ -521,21 +521,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
   struct A { virtual int f(); };
Index: 

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 534359.
zyounan added a comment.

And this


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is not marked as `IsEligibleOrSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -469,7 +469,7 @@
 - Fix crash when redefining a variable with an invalid type again with an
   invalid type. (`#62447 `_)
 - Fix a stack overflow issue when evaluating ``consteval`` default arguments.
-  (`#60082` `_)
+  (`#60082 `_)
 - Fix the assertion hit when generating code for global variable initializer of
   _BitInt(1) type.
   (`#62207 `_)
@@ -521,21 +521,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
   struct A { virtual int f(); };
Index: 

[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang/docs/ReleaseNotes.rst:524
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments

These underscores are not placed correctly so I revised them as well. Hope you 
don't mind. :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153724

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


[PATCH] D153724: [clang] Fix a crash on invalid destructor

2023-06-25 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added reviewers: royjacobson, aaron.ballman, erichkeane, shafik.
Herald added a project: All.
zyounan requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This is a follow-up patch to D126194  in 
order to
fix https://github.com/llvm/llvm-project/issues/63503.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153724

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/virtuals.cpp


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a 
pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a 
friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the destructor is
+  // invalid, in which case it is not marked as `IsEligibleOrSelected` and
+  // will not be selected by `CXXRecordDecl::getDestructor()`.
+  if (!Destructor)
+return;
   // If this is an array, we'll require the destructor during initialization, 
so
   // we can skip over this. We still want to emit exit-time destructor warnings
   // though.
Index: clang/docs/ReleaseNotes.rst
===
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -521,21 +521,23 @@
   (`#50534 `_).
 - CallExpr built for C error-recovery now is always type-dependent. Fixes a
   crash when we encounter a unresolved TypoExpr during diagnostic emission.
-  (`#50244 _`).
+  (`#50244 `_).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 `_).
 - Clang now properly diagnoses format string mismatches involving scoped
   enumeration types. A scoped enumeration type is not promoted to an integer
   type by the default argument promotions, and thus this is UB. Clang's
   behavior now matches GCC's behavior in C++.
-  (`#38717 _`).
+  (`#38717 `_).
 - Fixed a failing assertion when implicitly defining a function within a GNU
   statement expression that appears outside of a function block scope. The
   assertion was benign outside of asserts builds and would only fire in C.
-  (`#48579 _`).
+  (`#48579 `_).
 - Fixed a failing assertion when applying an attribute to an anonymous union.
   The assertion was benign outside of asserts builds and would only fire in 
C++.
-  (`#48512 _`).
+  (`#48512 `_).
+- Fixed a failing assertion when parsing incomplete destructor.
+  (`#63503 `_)
 
 Bug Fixes to Compiler Builtins
 ^^


Index: clang/test/SemaCXX/virtuals.cpp
===
--- clang/test/SemaCXX/virtuals.cpp
+++ clang/test/SemaCXX/virtuals.cpp
@@ -52,6 +52,19 @@
   };
 }
 
+namespace issue63503 {
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived final : Base {
+  using Base::Base;
+  virtual ~Derived() = defaul; // #62
+} do_not_crash;
+// expected-error@#62 {{initializer on function does not look like a pure-specifier}}
+// expected-error@#62 {{use of undeclared identifier 'defaul'}}
+}
+
 namespace VirtualFriend {
   // DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
   struct A { virtual int f(); };
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15807,7 +15807,11 @@
 return;
 
   CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
-
+  // The result of `LookupDestructor` might be nullptr if the 

[PATCH] D152880: [clang][NFC] Add a notice to desugarForDiagnostic

2023-06-14 Thread Younan Zhang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG0e08374abb2c: [clang][NFC] Add a notice to 
desugarForDiagnostic (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D152880

Files:
  clang/include/clang/AST/ASTDiagnostic.h
  clang/lib/AST/ASTDiagnostic.cpp


Index: clang/lib/AST/ASTDiagnostic.cpp
===
--- clang/lib/AST/ASTDiagnostic.cpp
+++ clang/lib/AST/ASTDiagnostic.cpp
@@ -25,7 +25,8 @@
 using namespace clang;
 
 // Returns a desugared version of the QualType, and marks ShouldAKA as true
-// whenever we remove significant sugar from the type.
+// whenever we remove significant sugar from the type. Make sure ShouldAKA
+// is initialized before passing it in.
 QualType clang::desugarForDiagnostic(ASTContext , QualType QT,
  bool ) {
   QualifierCollector QC;
Index: clang/include/clang/AST/ASTDiagnostic.h
===
--- clang/include/clang/AST/ASTDiagnostic.h
+++ clang/include/clang/AST/ASTDiagnostic.h
@@ -34,7 +34,8 @@
   ArrayRef QualTypeVals);
 
   /// Returns a desugared version of the QualType, and marks ShouldAKA as true
-  /// whenever we remove significant sugar from the type.
+  /// whenever we remove significant sugar from the type. Make sure ShouldAKA
+  /// is initialized before passing it in.
   QualType desugarForDiagnostic(ASTContext , QualType QT,
 bool );
 }  // end namespace clang


Index: clang/lib/AST/ASTDiagnostic.cpp
===
--- clang/lib/AST/ASTDiagnostic.cpp
+++ clang/lib/AST/ASTDiagnostic.cpp
@@ -25,7 +25,8 @@
 using namespace clang;
 
 // Returns a desugared version of the QualType, and marks ShouldAKA as true
-// whenever we remove significant sugar from the type.
+// whenever we remove significant sugar from the type. Make sure ShouldAKA
+// is initialized before passing it in.
 QualType clang::desugarForDiagnostic(ASTContext , QualType QT,
  bool ) {
   QualifierCollector QC;
Index: clang/include/clang/AST/ASTDiagnostic.h
===
--- clang/include/clang/AST/ASTDiagnostic.h
+++ clang/include/clang/AST/ASTDiagnostic.h
@@ -34,7 +34,8 @@
   ArrayRef QualTypeVals);
 
   /// Returns a desugared version of the QualType, and marks ShouldAKA as true
-  /// whenever we remove significant sugar from the type.
+  /// whenever we remove significant sugar from the type. Make sure ShouldAKA
+  /// is initialized before passing it in.
   QualType desugarForDiagnostic(ASTContext , QualType QT,
 bool );
 }  // end namespace clang
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D152880: [clang][NFC] Add a notice to desugarForDiagnostic

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
Herald added a project: All.
zyounan added reviewers: aaron.ballman, lh123.
zyounan published this revision for review.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

`desugarForDiagnostic` only sets ShouldAKA to true if desugaring
happens, otherwise ShouldAKA is left intact and might be uninitialized.

Victims (including me):

https://github.com/llvm/llvm-project/commit/25bf8cb3c0e3c41231289a6ff0a37b6d49b24011

https://github.com/llvm/llvm-project/commit/0e8384a0fe4f03d60cd92aba1cae074512481ca2


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152880

Files:
  clang/include/clang/AST/ASTDiagnostic.h
  clang/lib/AST/ASTDiagnostic.cpp


Index: clang/lib/AST/ASTDiagnostic.cpp
===
--- clang/lib/AST/ASTDiagnostic.cpp
+++ clang/lib/AST/ASTDiagnostic.cpp
@@ -25,7 +25,8 @@
 using namespace clang;
 
 // Returns a desugared version of the QualType, and marks ShouldAKA as true
-// whenever we remove significant sugar from the type.
+// whenever we remove significant sugar from the type. Make sure ShouldAKA
+// is initialized before passing it in.
 QualType clang::desugarForDiagnostic(ASTContext , QualType QT,
  bool ) {
   QualifierCollector QC;
Index: clang/include/clang/AST/ASTDiagnostic.h
===
--- clang/include/clang/AST/ASTDiagnostic.h
+++ clang/include/clang/AST/ASTDiagnostic.h
@@ -34,7 +34,8 @@
   ArrayRef QualTypeVals);
 
   /// Returns a desugared version of the QualType, and marks ShouldAKA as true
-  /// whenever we remove significant sugar from the type.
+  /// whenever we remove significant sugar from the type. Make sure ShouldAKA
+  /// is initialized before passing it in.
   QualType desugarForDiagnostic(ASTContext , QualType QT,
 bool );
 }  // end namespace clang


Index: clang/lib/AST/ASTDiagnostic.cpp
===
--- clang/lib/AST/ASTDiagnostic.cpp
+++ clang/lib/AST/ASTDiagnostic.cpp
@@ -25,7 +25,8 @@
 using namespace clang;
 
 // Returns a desugared version of the QualType, and marks ShouldAKA as true
-// whenever we remove significant sugar from the type.
+// whenever we remove significant sugar from the type. Make sure ShouldAKA
+// is initialized before passing it in.
 QualType clang::desugarForDiagnostic(ASTContext , QualType QT,
  bool ) {
   QualifierCollector QC;
Index: clang/include/clang/AST/ASTDiagnostic.h
===
--- clang/include/clang/AST/ASTDiagnostic.h
+++ clang/include/clang/AST/ASTDiagnostic.h
@@ -34,7 +34,8 @@
   ArrayRef QualTypeVals);
 
   /// Returns a desugared version of the QualType, and marks ShouldAKA as true
-  /// whenever we remove significant sugar from the type.
+  /// whenever we remove significant sugar from the type. Make sure ShouldAKA
+  /// is initialized before passing it in.
   QualType desugarForDiagnostic(ASTContext , QualType QT,
 bool );
 }  // end namespace clang
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D151785: [clangd] Desugar template parameter aliases in type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

In D151785#4417973 , @kstoimenov 
wrote:

> Looks like this might have broken a couple of sanitizer builds. Here is one 
> of them: https://lab.llvm.org/buildbot/#/builders/5/builds/34387. Could you 
> please revert or fix? 
> Thanks!

Sorry for the inconvenience. I think this issue had been fixed by 
https://github.com/llvm/llvm-project/commit/0e8384a0fe4f03d60cd92aba1cae074512481ca2.
 Does it work now? Please let me know if we still have problems.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

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


[PATCH] D152520: [clangd] Unify printing policy for type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5cdb906f1e40: [clangd] Unify printing policy for type hints 
(authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D152520

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1347,8 +1347,11 @@
 struct A {};
 A foo();
 auto $var[[var]] = foo();
+A bar[1];
+auto [$binding[[value]]] = bar;
   )cpp",
-  ExpectedHint{": A", "var"});
+  ExpectedHint{": A", "var"},
+  ExpectedHint{": A", "binding"});
 }
 
 TEST(TypeHints, Deduplication) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -258,8 +258,7 @@
 Cfg(Cfg), RestrictRange(std::move(RestrictRange)),
 MainFileID(AST.getSourceManager().getMainFileID()),
 Resolver(AST.getHeuristicResolver()),
-TypeHintPolicy(this->AST.getPrintingPolicy()),
-StructuredBindingPolicy(this->AST.getPrintingPolicy()) {
+TypeHintPolicy(this->AST.getPrintingPolicy()) {
 bool Invalid = false;
 llvm::StringRef Buf =
 AST.getSourceManager().getBufferData(MainFileID, );
@@ -269,14 +268,8 @@
 TypeHintPolicy.AnonymousTagLocations =
 false; // do not print lambda locations
 
-// For structured bindings, print canonical types. This is important because
-// for bindings that use the tuple_element protocol, the non-canonical types
-// would be "tuple_element::type".
-// For "auto", we often prefer sugared types.
 // Not setting PrintCanonicalTypes for "auto" allows
 // SuppressDefaultTemplateArgs (set by default) to have an effect.
-StructuredBindingPolicy = TypeHintPolicy;
-StructuredBindingPolicy.PrintCanonicalTypes = true;
   }
 
   bool VisitTypeLoc(TypeLoc TL) {
@@ -358,8 +351,12 @@
 // but show hints for the individual bindings.
 if (auto *DD = dyn_cast(D)) {
   for (auto *Binding : DD->bindings()) {
-addTypeHint(Binding->getLocation(), Binding->getType(), /*Prefix=*/": ",
-StructuredBindingPolicy);
+// For structured bindings, print canonical types. This is important
+// because for bindings that use the tuple_element protocol, the
+// non-canonical types would be "tuple_element::type".
+if (auto Type = Binding->getType(); !Type.isNull())
+  addTypeHint(Binding->getLocation(), Type.getCanonicalType(),
+  /*Prefix=*/": ");
   }
   return true;
 }
@@ -724,22 +721,17 @@
   }
 
   void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
-addTypeHint(R, T, Prefix, TypeHintPolicy);
-  }
-
-  void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix,
-   const PrintingPolicy ) {
 if (!Cfg.InlayHints.DeducedTypes || T.isNull())
   return;
 
 // The sugared type is more useful in some cases, and the canonical
 // type in other cases.
 auto Desugared = maybeDesugar(AST, T);
-std::string TypeName = Desugared.getAsString(Policy);
+std::string TypeName = Desugared.getAsString(TypeHintPolicy);
 if (T != Desugared && !shouldPrintTypeHint(TypeName)) {
   // If the desugared type is too long to display, fallback to the sugared
   // type.
-  TypeName = T.getAsString(Policy);
+  TypeName = T.getAsString(TypeHintPolicy);
 }
 if (shouldPrintTypeHint(TypeName))
   addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, TypeName,
@@ -764,14 +756,7 @@
   FileID MainFileID;
   StringRef MainFileBuf;
   const HeuristicResolver *Resolver;
-  // We want to suppress default template arguments, but otherwise print
-  // canonical types. Unfortunately, they're conflicting policies so we can't
-  // have both. For regular types, suppressing template arguments is more
-  // important, whereas printing canonical types is crucial for structured
-  // bindings, so we use two separate policies. (See the constructor where
-  // the policies are initialized for more details.)
   PrintingPolicy TypeHintPolicy;
-  PrintingPolicy StructuredBindingPolicy;
 };
 
 } // namespace
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D152520: [clangd] Unify printing policy for type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 530867.
zyounan added a comment.

Rebase onto D151785 .


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D152520

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1347,8 +1347,11 @@
 struct A {};
 A foo();
 auto $var[[var]] = foo();
+A bar[1];
+auto [$binding[[value]]] = bar;
   )cpp",
-  ExpectedHint{": A", "var"});
+  ExpectedHint{": A", "var"},
+  ExpectedHint{": A", "binding"});
 }
 
 TEST(TypeHints, Deduplication) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -258,8 +258,7 @@
 Cfg(Cfg), RestrictRange(std::move(RestrictRange)),
 MainFileID(AST.getSourceManager().getMainFileID()),
 Resolver(AST.getHeuristicResolver()),
-TypeHintPolicy(this->AST.getPrintingPolicy()),
-StructuredBindingPolicy(this->AST.getPrintingPolicy()) {
+TypeHintPolicy(this->AST.getPrintingPolicy()) {
 bool Invalid = false;
 llvm::StringRef Buf =
 AST.getSourceManager().getBufferData(MainFileID, );
@@ -269,14 +268,8 @@
 TypeHintPolicy.AnonymousTagLocations =
 false; // do not print lambda locations
 
-// For structured bindings, print canonical types. This is important because
-// for bindings that use the tuple_element protocol, the non-canonical types
-// would be "tuple_element::type".
-// For "auto", we often prefer sugared types.
 // Not setting PrintCanonicalTypes for "auto" allows
 // SuppressDefaultTemplateArgs (set by default) to have an effect.
-StructuredBindingPolicy = TypeHintPolicy;
-StructuredBindingPolicy.PrintCanonicalTypes = true;
   }
 
   bool VisitTypeLoc(TypeLoc TL) {
@@ -358,8 +351,12 @@
 // but show hints for the individual bindings.
 if (auto *DD = dyn_cast(D)) {
   for (auto *Binding : DD->bindings()) {
-addTypeHint(Binding->getLocation(), Binding->getType(), /*Prefix=*/": ",
-StructuredBindingPolicy);
+// For structured bindings, print canonical types. This is important
+// because for bindings that use the tuple_element protocol, the
+// non-canonical types would be "tuple_element::type".
+if (auto Type = Binding->getType(); !Type.isNull())
+  addTypeHint(Binding->getLocation(), Type.getCanonicalType(),
+  /*Prefix=*/": ");
   }
   return true;
 }
@@ -724,22 +721,17 @@
   }
 
   void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
-addTypeHint(R, T, Prefix, TypeHintPolicy);
-  }
-
-  void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix,
-   const PrintingPolicy ) {
 if (!Cfg.InlayHints.DeducedTypes || T.isNull())
   return;
 
 // The sugared type is more useful in some cases, and the canonical
 // type in other cases.
 auto Desugared = maybeDesugar(AST, T);
-std::string TypeName = Desugared.getAsString(Policy);
+std::string TypeName = Desugared.getAsString(TypeHintPolicy);
 if (T != Desugared && !shouldPrintTypeHint(TypeName)) {
   // If the desugared type is too long to display, fallback to the sugared
   // type.
-  TypeName = T.getAsString(Policy);
+  TypeName = T.getAsString(TypeHintPolicy);
 }
 if (shouldPrintTypeHint(TypeName))
   addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, TypeName,
@@ -764,14 +756,7 @@
   FileID MainFileID;
   StringRef MainFileBuf;
   const HeuristicResolver *Resolver;
-  // We want to suppress default template arguments, but otherwise print
-  // canonical types. Unfortunately, they're conflicting policies so we can't
-  // have both. For regular types, suppressing template arguments is more
-  // important, whereas printing canonical types is crucial for structured
-  // bindings, so we use two separate policies. (See the constructor where
-  // the policies are initialized for more details.)
   PrintingPolicy TypeHintPolicy;
-  PrintingPolicy StructuredBindingPolicy;
 };
 
 } // namespace
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D151785: [clangd] Desugar template parameter aliases in type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7d68f2ef411e: [clangd] Desugar template parameter aliases in 
type hints (authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1417,6 +1417,82 @@
   ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});
 }
 
+TEST(TypeHints, SubstTemplateParameterAliases) {
+  assertTypeHints(
+  R"cpp(
+  template  struct allocator {};
+
+  template 
+  struct vector_base {
+using pointer = T*;
+  };
+
+  template 
+  struct internal_iterator_type_template_we_dont_expect {};
+
+  struct my_iterator {};
+
+  template >
+  struct vector : vector_base {
+using base = vector_base;
+typedef T value_type;
+typedef base::pointer pointer;
+using allocator_type = A;
+using size_type = int;
+using iterator = internal_iterator_type_template_we_dont_expect;
+using non_template_iterator = my_iterator;
+
+value_type& operator[](int index) { return elements[index]; }
+const value_type& at(int index) const { return elements[index]; }
+pointer data() { return [0]; }
+allocator_type get_allocator() { return A(); }
+size_type size() const { return 10; }
+iterator begin() { return iterator(); }
+non_template_iterator end() { return non_template_iterator(); }
+
+T elements[10];
+  };
+
+  vector array;
+
+  auto $no_modifier[[by_value]] = array[3];
+  auto* $ptr_modifier[[ptr]] = [3];
+  auto& $ref_modifier[[ref]] = array[3];
+  auto& $at[[immutable]] = array.at(3);
+
+  auto $data[[data]] = array.data();
+  auto $allocator[[alloc]] = array.get_allocator();
+  auto $size[[size]] = array.size();
+  auto $begin[[begin]] = array.begin();
+  auto $end[[end]] = array.end();
+
+
+  // If the type alias is not of substituted template parameter type,
+  // do not show desugared type.
+  using VeryLongLongTypeName = my_iterator;
+  using Short = VeryLongLongTypeName;
+
+  auto $short_name[[my_value]] = Short();
+
+  // Same applies with templates.
+  template 
+  using basic_static_vector = vector;
+  template 
+  using static_vector = basic_static_vector>;
+
+  auto $vector_name[[vec]] = static_vector();
+  )cpp",
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"},
+  ExpectedHint{": Short", "short_name"},
+  ExpectedHint{": static_vector", "vector_name"});
+}
+
 TEST(DesignatorHints, Basic) {
   assertDesignatorHints(R"cpp(
 struct S { int x, y, z; };
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -11,10 +11,12 @@
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
 #include "SourceCode.h"
+#include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -190,6 +192,64 @@
   return Designators;
 }
 
+// Determines if any intermediate type in desugaring QualType QT is of
+// substituted template parameter type. Ignore pointer or reference wrappers.
+bool isSugaredTemplateParameter(QualType QT) {
+  static auto PeelWrappers = [](QualType QT) {
+// Neither `PointerType` nor `ReferenceType` is considered as sugared
+// type. Peel it.
+QualType Next;
+while (!(Next = QT->getPointeeType()).isNull())
+  QT = Next;
+return QT;
+  };
+  while (true) {
+QualType Desugared =
+PeelWrappers(QT->getLocallyUnqualifiedSingleStepDesugaredType());
+if (Desugared == QT)
+  break;
+if (Desugared->getAs())
+  return true;
+QT = Desugared;
+  }
+  return false;
+}
+
+// A simple wrapper for `clang::desugarForDiagnostic` that provides optional
+// semantic.
+std::optional desugar(ASTContext , QualType QT) {
+  bool ShouldAKA;
+  auto Desugared = clang::desugarForDiagnostic(AST, QT, ShouldAKA);
+  if (!ShouldAKA)
+return std::nullopt;
+  return Desugared;
+}

[PATCH] D151785: [clangd] Desugar template parameter aliases in type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 530851.
zyounan added a comment.

Oops. Remove extra debugging statement


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1417,6 +1417,82 @@
   ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});
 }
 
+TEST(TypeHints, SubstTemplateParameterAliases) {
+  assertTypeHints(
+  R"cpp(
+  template  struct allocator {};
+
+  template 
+  struct vector_base {
+using pointer = T*;
+  };
+
+  template 
+  struct internal_iterator_type_template_we_dont_expect {};
+
+  struct my_iterator {};
+
+  template >
+  struct vector : vector_base {
+using base = vector_base;
+typedef T value_type;
+typedef base::pointer pointer;
+using allocator_type = A;
+using size_type = int;
+using iterator = internal_iterator_type_template_we_dont_expect;
+using non_template_iterator = my_iterator;
+
+value_type& operator[](int index) { return elements[index]; }
+const value_type& at(int index) const { return elements[index]; }
+pointer data() { return [0]; }
+allocator_type get_allocator() { return A(); }
+size_type size() const { return 10; }
+iterator begin() { return iterator(); }
+non_template_iterator end() { return non_template_iterator(); }
+
+T elements[10];
+  };
+
+  vector array;
+
+  auto $no_modifier[[by_value]] = array[3];
+  auto* $ptr_modifier[[ptr]] = [3];
+  auto& $ref_modifier[[ref]] = array[3];
+  auto& $at[[immutable]] = array.at(3);
+
+  auto $data[[data]] = array.data();
+  auto $allocator[[alloc]] = array.get_allocator();
+  auto $size[[size]] = array.size();
+  auto $begin[[begin]] = array.begin();
+  auto $end[[end]] = array.end();
+
+
+  // If the type alias is not of substituted template parameter type,
+  // do not show desugared type.
+  using VeryLongLongTypeName = my_iterator;
+  using Short = VeryLongLongTypeName;
+
+  auto $short_name[[my_value]] = Short();
+
+  // Same applies with templates.
+  template 
+  using basic_static_vector = vector;
+  template 
+  using static_vector = basic_static_vector>;
+
+  auto $vector_name[[vec]] = static_vector();
+  )cpp",
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"},
+  ExpectedHint{": Short", "short_name"},
+  ExpectedHint{": static_vector", "vector_name"});
+}
+
 TEST(DesignatorHints, Basic) {
   assertDesignatorHints(R"cpp(
 struct S { int x, y, z; };
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -11,10 +11,12 @@
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
 #include "SourceCode.h"
+#include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -190,6 +192,64 @@
   return Designators;
 }
 
+// Determines if any intermediate type in desugaring QualType QT is of
+// substituted template parameter type. Ignore pointer or reference wrappers.
+bool isSugaredTemplateParameter(QualType QT) {
+  static auto PeelWrappers = [](QualType QT) {
+// Neither `PointerType` nor `ReferenceType` is considered as sugared
+// type. Peel it.
+QualType Next;
+while (!(Next = QT->getPointeeType()).isNull())
+  QT = Next;
+return QT;
+  };
+  while (true) {
+QualType Desugared =
+PeelWrappers(QT->getLocallyUnqualifiedSingleStepDesugaredType());
+if (Desugared == QT)
+  break;
+if (Desugared->getAs())
+  return true;
+QT = Desugared;
+  }
+  return false;
+}
+
+// A simple wrapper for `clang::desugarForDiagnostic` that provides optional
+// semantic.
+std::optional desugar(ASTContext , QualType QT) {
+  bool ShouldAKA;
+  auto Desugared = clang::desugarForDiagnostic(AST, QT, ShouldAKA);
+  if (!ShouldAKA)
+return std::nullopt;
+  return Desugared;
+}
+
+// Apply a series of heuristic methods to determine whether or not a QualType QT
+// is suitable for desugaring (e.g. getting the 

[PATCH] D151785: [clangd] Desugar template parameter aliases in type hints

2023-06-13 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 530850.
zyounan marked an inline comment as done.
zyounan added a comment.

Final update


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1417,6 +1417,82 @@
   ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});
 }
 
+TEST(TypeHints, SubstTemplateParameterAliases) {
+  assertTypeHints(
+  R"cpp(
+  template  struct allocator {};
+
+  template 
+  struct vector_base {
+using pointer = T*;
+  };
+
+  template 
+  struct internal_iterator_type_template_we_dont_expect {};
+
+  struct my_iterator {};
+
+  template >
+  struct vector : vector_base {
+using base = vector_base;
+typedef T value_type;
+typedef base::pointer pointer;
+using allocator_type = A;
+using size_type = int;
+using iterator = internal_iterator_type_template_we_dont_expect;
+using non_template_iterator = my_iterator;
+
+value_type& operator[](int index) { return elements[index]; }
+const value_type& at(int index) const { return elements[index]; }
+pointer data() { return [0]; }
+allocator_type get_allocator() { return A(); }
+size_type size() const { return 10; }
+iterator begin() { return iterator(); }
+non_template_iterator end() { return non_template_iterator(); }
+
+T elements[10];
+  };
+
+  vector array;
+
+  auto $no_modifier[[by_value]] = array[3];
+  auto* $ptr_modifier[[ptr]] = [3];
+  auto& $ref_modifier[[ref]] = array[3];
+  auto& $at[[immutable]] = array.at(3);
+
+  auto $data[[data]] = array.data();
+  auto $allocator[[alloc]] = array.get_allocator();
+  auto $size[[size]] = array.size();
+  auto $begin[[begin]] = array.begin();
+  auto $end[[end]] = array.end();
+
+
+  // If the type alias is not of substituted template parameter type,
+  // do not show desugared type.
+  using VeryLongLongTypeName = my_iterator;
+  using Short = VeryLongLongTypeName;
+
+  auto $short_name[[my_value]] = Short();
+
+  // Same applies with templates.
+  template 
+  using basic_static_vector = vector;
+  template 
+  using static_vector = basic_static_vector>;
+
+  auto $vector_name[[vec]] = static_vector();
+  )cpp",
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"},
+  ExpectedHint{": Short", "short_name"},
+  ExpectedHint{": static_vector", "vector_name"});
+}
+
 TEST(DesignatorHints, Basic) {
   assertDesignatorHints(R"cpp(
 struct S { int x, y, z; };
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -11,10 +11,12 @@
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
 #include "SourceCode.h"
+#include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -190,6 +192,64 @@
   return Designators;
 }
 
+// Determines if any intermediate type in desugaring QualType QT is of
+// substituted template parameter type. Ignore pointer or reference wrappers.
+bool isSugaredTemplateParameter(QualType QT) {
+  static auto PeelWrappers = [](QualType QT) {
+// Neither `PointerType` nor `ReferenceType` is considered as sugared
+// type. Peel it.
+QualType Next;
+while (!(Next = QT->getPointeeType()).isNull())
+  QT = Next;
+return QT;
+  };
+  while (true) {
+QualType Desugared =
+PeelWrappers(QT->getLocallyUnqualifiedSingleStepDesugaredType());
+if (Desugared == QT)
+  break;
+if (Desugared->getAs())
+  return true;
+QT = Desugared;
+  }
+  return false;
+}
+
+// A simple wrapper for `clang::desugarForDiagnostic` that provides optional
+// semantic.
+std::optional desugar(ASTContext , QualType QT) {
+  bool ShouldAKA;
+  auto Desugared = clang::desugarForDiagnostic(AST, QT, ShouldAKA);
+  if (!ShouldAKA)
+return std::nullopt;
+  return Desugared;
+}
+
+// Apply a series of heuristic methods to determine whether or not a QualType QT
+// is suitable for desugaring 

[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-06-10 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thank you very much for the insightful review! Regarding to your suggestion,

> We have an existing heuristic here, should we move the logic into 
> maybeDesugar() so it's all in one place?

once we merge these two functions, the overloaded `addTypeHint` without a 
PrintingPolicy parameter will merely remains one line for forwarding. As we're 
going to use a single `PrintingPolicy` after D152520 
, I think it would be better to eventually 
drop the parameter `PrintingPolicy` in `addTypeHint` in that patch.




Comment at: clang-tools-extra/clangd/InlayHints.cpp:198
+bool isDependentOrTrivialSugaredType(QualType QT) {
+  static auto PeelQualifier = [](QualType QT) {
+// Neither `PointerType` nor `ReferenceType` is considered as sugared

nridge wrote:
> nit: it sounds like this name (`PeelQualifier`) is using "qualifier" to mean 
> "pointer or reference", which is different from the meaning of the word in 
> "QualType" (where it means "const or volatile")
> 
> maybe `PeelWrappers` would be a better name, since we can think of 
> `PointerType` and `ReferenceType` as wrapping an underlying type?
Good catch! I looked up the standard and found out that pointers or references 
are of compound types, and that 'qualified' usually goes along with 'const or 
volatile'.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:201
+// type. Peel it.
+QualType Last = QT;
+for (QT = QT->getPointeeType(); !QT.isNull();

nridge wrote:
> a possible reformulation:
> 
> ```
> QualType Next;
> while (!(Next = QT->getPointeeType()).isNull()) {
>   QT = Next;
> }
> return QT;
> ```
> 
> this seems slightly cleaner to me, but I'll leave it up to you
Rephrased as the suggestion. (TBH, I didn't realize storing the Next instead of 
the Prev could make it more terse. Thanks for the hint!)



Comment at: clang-tools-extra/clangd/InlayHints.cpp:207
+  };
+  for (QualType Desugared =
+   PeelQualifier(QT->getLocallyUnqualifiedSingleStepDesugaredType());

nridge wrote:
> this loop can also be reformulated to avoid repeating the update step:
> 
> ```
> while (true) {
>   QualType Desugared = Peel(...);
>   if (Desugared == QT)
> break;
>   if (Desugared->getAs<...>)
> return true;
>   QT = Desugared;
> }
> ```
Cool.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:215
+  return true;
+if (Desugared->getAs())
+  return true;

nridge wrote:
> The only test case that seems to rely on this `BuiltinType` heuristic is 
> getting `int` instead of `size_type` for `array.size()`.
> 
> `size_type` doesn't seem so bad, maybe we can leave this out for now? Or do 
> you have a more compelling motivating case for it?
Not that I could think of except that case. It's possible that `size_type` 
varies across different platforms, so it seems okay to remove it.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:235
+// parameter QT.
+QualType tryDesugar(ASTContext , QualType QT) {
+  if (isDependentOrTrivialSugaredType(QT))

nridge wrote:
> name suggestion: `maybeDesugar`
> 
> (`try` sounds like it might fail and return something that needs to be 
> checked like `optional` or `Expected`)
Frankly speaking I used to mix up these two and just learned the differences. 
Thank you!



Comment at: clang-tools-extra/clangd/InlayHints.cpp:267
 StructuredBindingPolicy = TypeHintPolicy;
 StructuredBindingPolicy.PrintCanonicalTypes = true;
   }

nridge wrote:
> zyounan wrote:
> > nridge wrote:
> > > zyounan wrote:
> > > > `PrintCanonicalTypes` turns on printing default template arguments, 
> > > > which would prevent the patch from printing, e.g., 
> > > > `std::basic_string`, within the default length limitation.
> > > > 
> > > > ```
> > > > std::map Map;
> > > > 
> > > > for (auto &[Key, Value] : Map) // Key: basic_string > > > char_traits, allocator>, whose length exceeds the default 
> > > > threshold.
> > > > 
> > > > ```
> > > > 
> > > > Is it safe to drop it now? I believe this patch can handle the case the 
> > > > comment mentioned.
> > > Unfortunately, I don't think it works in general. I tried commenting out 
> > > this line and 
> > > `TypeHints.StructuredBindings_TupleLike` failed.
> > > 
> > > (Note, this was with the `BuiltinType` heuristic removed. If we keep the 
> > > `BuilinType` heuristic, a modified version of the testcase (e.g. struct 
> > > members are changed from `int` to a class type) still fails.)
> > > 
> > Thank you for providing me with the case. I think the flag 
> > `PrintCanonicalTypes` actually controls two aspects of styles, if I 
> > understand TypePrinter correctly:
> > 
> > 1. For type aliases (a.k.a. typedefs and using alias), the 'canonical' type 
> > (i.e., the "most" desugared type) is [[ 
> > 

[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-06-10 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 530201.
zyounan marked 7 inline comments as done.
zyounan added a comment.

Address many comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1417,6 +1417,82 @@
   ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});
 }
 
+TEST(TypeHints, SubstTemplateParameterAliases) {
+  assertTypeHints(
+  R"cpp(
+  template  struct allocator {};
+
+  template 
+  struct vector_base {
+using pointer = T*;
+  };
+
+  template 
+  struct internal_iterator_type_template_we_dont_expect {};
+
+  struct my_iterator {};
+
+  template >
+  struct vector : vector_base {
+using base = vector_base;
+typedef T value_type;
+typedef base::pointer pointer;
+using allocator_type = A;
+using size_type = int;
+using iterator = internal_iterator_type_template_we_dont_expect;
+using non_template_iterator = my_iterator;
+
+value_type& operator[](int index) { return elements[index]; }
+const value_type& at(int index) const { return elements[index]; }
+pointer data() { return [0]; }
+allocator_type get_allocator() { return A(); }
+size_type size() const { return 10; }
+iterator begin() { return iterator(); }
+non_template_iterator end() { return non_template_iterator(); }
+
+T elements[10];
+  };
+
+  vector array;
+
+  auto $no_modifier[[by_value]] = array[3];
+  auto* $ptr_modifier[[ptr]] = [3];
+  auto& $ref_modifier[[ref]] = array[3];
+  auto& $at[[immutable]] = array.at(3);
+
+  auto $data[[data]] = array.data();
+  auto $allocator[[alloc]] = array.get_allocator();
+  auto $size[[size]] = array.size();
+  auto $begin[[begin]] = array.begin();
+  auto $end[[end]] = array.end();
+
+
+  // If the type alias is not of substituted template parameter type,
+  // do not show desugared type.
+  using VeryLongLongTypeName = my_iterator;
+  using Short = VeryLongLongTypeName;
+
+  auto $short_name[[my_value]] = Short();
+
+  // Same applies with templates.
+  template 
+  using basic_static_vector = vector;
+  template 
+  using static_vector = basic_static_vector>;
+
+  auto $vector_name[[vec]] = static_vector();
+  )cpp",
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"},
+  ExpectedHint{": Short", "short_name"},
+  ExpectedHint{": static_vector", "vector_name"});
+}
+
 TEST(DesignatorHints, Basic) {
   assertDesignatorHints(R"cpp(
 struct S { int x, y, z; };
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -11,10 +11,12 @@
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
 #include "SourceCode.h"
+#include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -190,6 +192,64 @@
   return Designators;
 }
 
+// Determines if any intermediate type in desugaring QualType QT is of
+// substituted template parameter. Ignore pointer or reference wrappers.
+bool isSugaredTemplateParameter(QualType QT) {
+  static auto PeelWrappers = [](QualType QT) {
+// Neither `PointerType` nor `ReferenceType` is considered as sugared
+// type. Peel it.
+QualType Next;
+while (!(Next = QT->getPointeeType()).isNull())
+  QT = Next;
+return QT;
+  };
+  while (true) {
+QualType Desugared =
+PeelWrappers(QT->getLocallyUnqualifiedSingleStepDesugaredType());
+if (Desugared == QT)
+  break;
+if (Desugared->getAs())
+  return true;
+QT = Desugared;
+  }
+  return false;
+}
+
+// A simple wrapper for `clang::desugarForDiagnostic` that provides optional
+// semantic.
+std::optional desugar(ASTContext , QualType QT) {
+  bool ShouldAKA;
+  auto Desugared = clang::desugarForDiagnostic(AST, QT, ShouldAKA);
+  if (!ShouldAKA)
+return std::nullopt;
+  return Desugared;
+}
+
+// Apply a series of heuristic methods to determine whether or not a QualType QT
+// is suitable for 

[PATCH] D152520: [clangd] Unify printing policy for type hints

2023-06-09 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
zyounan added a reviewer: nridge.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
zyounan published this revision for review.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

(This patch addresses the comment from 
https://reviews.llvm.org/D151785#4402460.)

Previously, we used a special printing policy that enabled `PrintCanonicalTypes`
to print type hints for structure bindings. This was intended to
eliminate type aliases like `tuple_element::type`. However, this also
caused TypePrinter to print default template arguments, which could
result in losing the ability to see types like `std::basic_string`
if the fully expanded template-id exceeded the default inlay hint threshold.

Simply getting the canonical type at the call site could help us get rid of
the side effect.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152520

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp


Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1347,8 +1347,11 @@
 struct A {};
 A foo();
 auto $var[[var]] = foo();
+A bar[1];
+auto [$binding[[value]]] = bar;
   )cpp",
-  ExpectedHint{": A", "var"});
+  ExpectedHint{": A", "var"},
+  ExpectedHint{": A", "binding"});
 }
 
 TEST(TypeHints, Deduplication) {
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -198,8 +198,7 @@
 Cfg(Cfg), RestrictRange(std::move(RestrictRange)),
 MainFileID(AST.getSourceManager().getMainFileID()),
 Resolver(AST.getHeuristicResolver()),
-TypeHintPolicy(this->AST.getPrintingPolicy()),
-StructuredBindingPolicy(this->AST.getPrintingPolicy()) {
+TypeHintPolicy(this->AST.getPrintingPolicy()) {
 bool Invalid = false;
 llvm::StringRef Buf =
 AST.getSourceManager().getBufferData(MainFileID, );
@@ -209,14 +208,8 @@
 TypeHintPolicy.AnonymousTagLocations =
 false; // do not print lambda locations
 
-// For structured bindings, print canonical types. This is important 
because
-// for bindings that use the tuple_element protocol, the non-canonical 
types
-// would be "tuple_element::type".
-// For "auto", we often prefer sugared types.
 // Not setting PrintCanonicalTypes for "auto" allows
 // SuppressDefaultTemplateArgs (set by default) to have an effect.
-StructuredBindingPolicy = TypeHintPolicy;
-StructuredBindingPolicy.PrintCanonicalTypes = true;
   }
 
   bool VisitTypeLoc(TypeLoc TL) {
@@ -298,8 +291,12 @@
 // but show hints for the individual bindings.
 if (auto *DD = dyn_cast(D)) {
   for (auto *Binding : DD->bindings()) {
-addTypeHint(Binding->getLocation(), Binding->getType(), /*Prefix=*/": 
",
-StructuredBindingPolicy);
+// For structured bindings, print canonical types. This is important
+// because for bindings that use the tuple_element protocol, the
+// non-canonical types would be "tuple_element::type".
+if (auto Type = Binding->getType(); !Type.isNull())
+  addTypeHint(Binding->getLocation(), Type.getCanonicalType(),
+  /*Prefix=*/": ", TypeHintPolicy);
   }
   return true;
 }
@@ -707,14 +704,7 @@
   FileID MainFileID;
   StringRef MainFileBuf;
   const HeuristicResolver *Resolver;
-  // We want to suppress default template arguments, but otherwise print
-  // canonical types. Unfortunately, they're conflicting policies so we can't
-  // have both. For regular types, suppressing template arguments is more
-  // important, whereas printing canonical types is crucial for structured
-  // bindings, so we use two separate policies. (See the constructor where
-  // the policies are initialized for more details.)
   PrintingPolicy TypeHintPolicy;
-  PrintingPolicy StructuredBindingPolicy;
 };
 
 } // namespace


Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1347,8 +1347,11 @@
 struct A {};
 A foo();
 auto $var[[var]] = foo();
+A bar[1];
+auto [$binding[[value]]] = bar;
   )cpp",
-  ExpectedHint{": A", "var"});
+  ExpectedHint{": A", "var"},
+  ExpectedHint{": A", "binding"});
 }
 
 TEST(TypeHints, Deduplication) {
Index: clang-tools-extra/clangd/InlayHints.cpp

[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-06-07 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:267
 StructuredBindingPolicy = TypeHintPolicy;
 StructuredBindingPolicy.PrintCanonicalTypes = true;
   }

nridge wrote:
> zyounan wrote:
> > `PrintCanonicalTypes` turns on printing default template arguments, which 
> > would prevent the patch from printing, e.g., `std::basic_string`, 
> > within the default length limitation.
> > 
> > ```
> > std::map Map;
> > 
> > for (auto &[Key, Value] : Map) // Key: basic_string > char_traits, allocator>, whose length exceeds the default 
> > threshold.
> > 
> > ```
> > 
> > Is it safe to drop it now? I believe this patch can handle the case the 
> > comment mentioned.
> Unfortunately, I don't think it works in general. I tried commenting out this 
> line and 
> `TypeHints.StructuredBindings_TupleLike` failed.
> 
> (Note, this was with the `BuiltinType` heuristic removed. If we keep the 
> `BuilinType` heuristic, a modified version of the testcase (e.g. struct 
> members are changed from `int` to a class type) still fails.)
> 
Thank you for providing me with the case. I think the flag 
`PrintCanonicalTypes` actually controls two aspects of styles, if I understand 
TypePrinter correctly:

1. For type aliases (a.k.a. typedefs and using alias), the 'canonical' type 
(i.e., the "most" desugared type) is [[ 
https://searchfox.org/llvm/rev/3a458256ee22a0e7c31529de42fa6caa263d88fe/clang/lib/AST/TypePrinter.cpp#179
 | obtained ]] before printing.

2. For template arguments, print [[ 
https://searchfox.org/llvm/rev/3a458256ee22a0e7c31529de42fa6caa263d88fe/clang/lib/AST/TypePrinter.cpp#2158
 | default parameters ]] even they weren't specified.

For 1, we could achieve the same goal at the caller site: For 
`DecompositionDecl`, instead of creating another different printing policy, 
call `QualType::getCanonicalType()` to get the QualType to be passed into 
`addTypeHint`. I did a modification and found that test cases from `TypeHints` 
are all passed even we disable `StructuredBindingPolicy.PrintCanonicalTypes`.

For 2, I think for most of the cases, printing default template parameters is 
an unexpected side effect. (I didn't see any test cases requiring default 
template parameters on structure bindings, but please correct me if I'm missing 
something.)

What do you think? I'd like to submit another review addressing this if it 
looks fine to you.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

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


[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-06-04 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang-tools-extra/clangd/InlayHints.cpp:267
 StructuredBindingPolicy = TypeHintPolicy;
 StructuredBindingPolicy.PrintCanonicalTypes = true;
   }

`PrintCanonicalTypes` turns on printing default template arguments, which would 
prevent the patch from printing, e.g., `std::basic_string`, within the 
default length limitation.

```
std::map Map;

for (auto &[Key, Value] : Map) // Key: basic_string, 
allocator>, whose length exceeds the default threshold.

```

Is it safe to drop it now? I believe this patch can handle the case the comment 
mentioned.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

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


[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-05-31 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Before
F27754400: before.png 

After
F27754403: after.png 

(Sorry for posting screenshots directly and bringing any inconvenience for 
email users.)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D151785

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


[PATCH] D151785: [clangd] Desugar dependent type aliases for auto type hints

2023-05-31 Thread Younan Zhang via Phabricator via cfe-commits
zyounan created this revision.
Herald added subscribers: jeroen.dobbelaere, kadircet, arphaman.
Herald added a project: All.
zyounan requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

This patch alleviates https://github.com/clangd/clangd/issues/1298.

Containers in C++ such as `std::vector` or `llvm::SmallVector`,
introduce a series of type aliases to adapt to generic algorithms.

Currently, If we write an declarator involving expressions with
these containers and `auto` placeholder, we probably obtain opaque
type alias like following:

  c++
  std::vector v = {1, 2, 3};
  auto value = v[1]; // hint for `value`: value_type
  auto *ptr = [0]; // hint for `ptr`: value_type *

These hints are useless for most of the time. It would be nice if we
desugar the type of `value_type` and print `int`, `int *` respectively
in this situation. However, things are complicated if user introduces type-
alias for brevity: we don't want to make the length of hints be too long!

This patch introduces a heuristic method that displays the desugared type
for type-aliases that depend on some template parameters or simply
reduce to builtin types e.g. int, double.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D151785

Files:
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1417,6 +1417,81 @@
   ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});
 }
 
+TEST(TypeHints, DependentAliases) {
+  assertTypeHints(
+  R"cpp(
+  template  struct allocator {};
+
+  template 
+  struct vector_base {
+using pointer = T*;
+  };
+
+  template 
+  struct internal_iterator_type_template_we_dont_expect {};
+
+  struct my_iterator {};
+
+  template >
+  struct vector : vector_base {
+using base = vector_base;
+typedef T value_type;
+typedef base::pointer pointer;
+using allocator_type = A;
+using size_type = int;
+using iterator = internal_iterator_type_template_we_dont_expect;
+using non_template_iterator = my_iterator;
+
+value_type& operator[](int index) { return elements[index]; }
+const value_type& at(int index) const { return elements[index]; }
+pointer data() { return [0]; }
+allocator_type get_allocator() { return A(); }
+size_type size() const { return 10; }
+iterator begin() { return iterator(); }
+non_template_iterator end() { return non_template_iterator(); }
+
+T elements[10];
+  };
+
+  vector array;
+
+  auto $no_modifier[[by_value]] = array[3];
+  auto* $ptr_modifier[[ptr]] = [3];
+  auto& $ref_modifier[[ref]] = array[3];
+  auto& $at[[immutable]] = array.at(3);
+
+  auto $data[[data]] = array.data();
+  auto $allocator[[alloc]] = array.get_allocator();
+  auto $size[[size]] = array.size();
+  auto $begin[[begin]] = array.begin();
+  auto $end[[end]] = array.end();
+
+
+  // If the type alias is not type-dependent or non-trivial, do not show desugared type.
+  using VeryLongLongTypeName = my_iterator;
+  using Short = VeryLongLongTypeName;
+
+  auto $short_name[[my_value]] = Short();
+
+  // Same applies with templates.
+  template 
+  using basic_static_vector = vector;
+  template 
+  using static_vector = basic_static_vector>;
+
+  auto $vector_name[[vec]] = static_vector();
+  )cpp",
+  ExpectedHint{": int", "no_modifier"},
+  ExpectedHint{": int *", "ptr_modifier"},
+  ExpectedHint{": int &", "ref_modifier"},
+  ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},
+  ExpectedHint{": allocator", "allocator"},
+  ExpectedHint{": int", "size"}, ExpectedHint{": iterator", "begin"},
+  ExpectedHint{": non_template_iterator", "end"},
+  ExpectedHint{": Short", "short_name"},
+  ExpectedHint{": static_vector", "vector_name"});
+}
+
 TEST(DesignatorHints, Basic) {
   assertDesignatorHints(R"cpp(
 struct S { int x, y, z; };
Index: clang-tools-extra/clangd/InlayHints.cpp
===
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -11,10 +11,12 @@
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
 #include "SourceCode.h"
+#include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -190,6 +192,52 @@
   return Designators;
 }
 
+// Determines if any part in desugaring steps of QualType QT is a dependent or
+// builtin type. Ignore 

[PATCH] D150635: [clangd] Implement end-definition-comment inlay hints

2023-05-17 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thank you for the update. A few questions:

- Can you add a test with macro expansion? Would we get two inlay hints right 
after the spelling location? e.g.,

  #define Decl_Record \
struct Widget {  \
  [very long definition that reach `EndDefinitionCommentMinLines`]. \
  struct SubWidget { \
[another chunk of members.] \
  // not ending with a bracket here!
  
  //  but appears here:
  //  v   v
  Decl_Record } // struct SubWidget ; } // struct Widget (Hmm...) ;



- I was thinking if we should add some test cases for C. (I have learned that 
it is allowed to write a declaration in an expression while C++ doesn't.) For 
example,

  int main(void) {
_Alignof(struct S {
  [long definition]
} // Is it suitable to hint here? What do you think?
);
  }




Comment at: clang-tools-extra/clangd/InlayHints.cpp:285-286
+  bool VisitEnumDecl(EnumDecl *D) {
+if (Cfg.InlayHints.EndDefinitionComments &&
+D->isThisDeclarationADefinition()) {
+  StringRef DeclPrefix;

nit: bail out early?




Comment at: clang-tools-extra/clangd/InlayHints.cpp:797
+  // differently.
+  assert(Label.empty());
+  Label += printName(AST, D);

nit: `assert(Label.empty() && "Label should be empty with FunctionDecl")`. 
might be helpful for debugging.



Comment at: clang-tools-extra/clangd/unittests/InlayHintTests.cpp:1716
+
+// No hint becaus trailing ';' is only allowed for class/struct/union/enum
+void foo() {

typo: `becaus` -> `because`?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D150635

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


[PATCH] D150635: [clangd] Implement end-definition-comment inlay hints

2023-05-16 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Sorry for chiming in. Left a few nit comments and I hope you don't mind. :)




Comment at: clang-tools-extra/clangd/InlayHints.cpp:772
+  Label = printName(AST, D);
+} else {
+  // We handle type and namespace decls together.

Question: Should we restrict the type here? What will happen if `D` was 
something unexpected like a `TypedefDecl`? Would it fall into the logic that 
makes `Label` result to ``?



Comment at: clang-tools-extra/clangd/InlayHints.cpp:777-781
+  else if (isa(D)) {
+Label += "enum ";
+if (cast(D).isScopedUsingClassTag()) {
+  Label += "class ";
+}

Perhaps duplicate the logic from [[ 
https://clang.llvm.org/doxygen/DeclPrinter_8cpp_source.html#l00529 | 
DeclPrinter::VisitEnumDecl ]]? For `enum struct`, printing `enum` only might 
confuse users.




Comment at: clang-tools-extra/clangd/InlayHints.cpp:783-788
+if (RecordD->isStruct())
+  Label += "struct ";
+else if (RecordD->isClass())
+  Label += "class ";
+else if (RecordD->isUnion())
+  Label += "union ";

nit: How about using [[ 
https://clang.llvm.org/doxygen/Decl_8h_source.html#l03645 | 
tagDecl::getKindName() ]]?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D150635

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


[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-05-09 Thread Younan Zhang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7385cc389aba: [clangd] Support macro evaluation on hover 
(authored by zyounan).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1850,6 +1854,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3769,6 +3775,232 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define dg_left_bracket <:
+#define dg_right_bracket :>
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig^nOf(Y);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  /*Validator=*/
+  

[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-05-09 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 520638.
zyounan added a comment.

Final update


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1850,6 +1854,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3769,6 +3775,232 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define dg_left_bracket <:
+#define dg_right_bracket :>
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig^nOf(Y);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+

[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-05-08 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thank you for the opinions. I've updated and please take a look.




Comment at: clang-tools-extra/clangd/Hover.cpp:705
+
+  // If macro expands to one single token, rule out punctuator or digraph.
+  // E.g., for the case `array L_BRACKET 42 R_BRACKET;` where L_BRACKET and

nridge wrote:
> This check for punctuators actually makes the behaviour **more strict** for 
> macros than for non-macros in some cases.
> 
> For example:
> 
> ```
> #define PLUS +
> 
> constexpr int A = 2 + 3;  // hover over `+` shows `Value = 5`
> constexpr int B = 2 PLUS 3;  // hover over `PLUS` does not show `Value`
> ```
> 
> I don't think this is a particularly important use case (it's a bit of a hack 
> to get the expression's value this way, much better would be to select the 
> entire expression you want to evaluate in the editor, but unfortunately LSP 
> only sends a single position in `textDocument/hover`, not a full range...), 
> but perhaps we could consider relaxing this in the future. (I'm thinking, if 
> we switched to "allow partial selection via macros that expand to a single 
> token", we could probably drop this condition.)
Thanks for clarifying this intention (for any bug hunter in the future).
> if we switched to "allow partial selection via macros that expand to a single 
> token", we could probably drop this condition.

I'm afraid we can't. For the array case in the test,
```
vector left_b^racket 3 right_b^racket;
// left_bracket -> [
// right_bracket -> ]
```
the associated selection tree is,
```
 TranslationUnitDecl 
   FunctionDecl void function()
 CompoundStmt { …
  .ArraySubscriptExpr vector[3]
```
(`function` is the wrapper that facilitates writing single expression, doesn't 
matter here.)
It expands to one single token and is partially selected, which exactly matches 
the strategy. If we do allow evaluation, we'd get a value/type on `[` or `]`. 
Yes, it is a contrived and weird use case, but for now I think it's fine to 
keep it unevaluated to avoid issue aforementioned.

(But I'm willing to remove this restriction if you prefer a relaxed strategy.)



Comment at: clang-tools-extra/clangd/unittests/HoverTests.cpp:3832
+  };
+  Alig$3^nOf(Y);
+)cpp",

nridge wrote:
> I guess with this style of test you can simplify the `$3^` to `^`
Aha, that's a typo.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

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


[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-05-08 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 520555.
zyounan marked 2 inline comments as done.
zyounan added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1792,6 +1796,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3746,6 +3752,232 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define dg_left_bracket <:
+#define dg_right_bracket :>
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig^nOf(Y);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+

[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-05-06 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Gently ping


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

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


[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-04-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 518280.
zyounan added a comment.

Exclude the macro expands to single punctulator token.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1792,6 +1796,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3746,6 +3752,232 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define dg_left_bracket <:
+#define dg_right_bracket :>
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig$3^nOf(Y);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+

[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-04-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added inline comments.



Comment at: clang-tools-extra/clangd/unittests/HoverTests.cpp:3901
+case 1:
+  EXPECT_TRUE(HI->Type);
+  EXPECT_FALSE(HI->Value);

Oops, this should be FALSE I think.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

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


[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-04-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan added a comment.

Thank you very much for the ideas.

> (generalization) allow partial selection as long as it's of a single node - 
> the common ancestor is partially selected and no children are

This strategy looks reasonable to me and it passes my test cases. I've updated 
my patch again. :)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

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


[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-04-30 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 518277.
zyounan added a comment.

Do not evaluate on partial selection with children.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1792,6 +1796,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3746,6 +3752,230 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig$3^nOf(Y);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, 

[PATCH] D148457: [clangd] Support macro evaluation on hover

2023-04-29 Thread Younan Zhang via Phabricator via cfe-commits
zyounan updated this revision to Diff 518144.
zyounan added a comment.

Refactor tests. Obtain type from VarDecl.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D148457

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -18,6 +18,7 @@
 #include "clang/Format/Format.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
 
 #include "gtest/gtest.h"
 #include 
@@ -529,6 +530,8 @@
[](HoverInfo ) {
  HI.Name = "MACRO";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Value = "41 (0x29)";
+ HI.Type = "int";
  HI.Definition = "#define MACRO 41\n\n"
  "// Expands to\n"
  "41";
@@ -560,6 +563,7 @@
[](HoverInfo ) {
  HI.Name = "DECL_STR";
  HI.Kind = index::SymbolKind::Macro;
+ HI.Type = HoverInfo::PrintedType("const char *");
  HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
  "STRINGIFY(VALUE)\n\n"
  "// Expands to\n"
@@ -1792,6 +1796,8 @@
   )cpp",
   [](HoverInfo ) {
 HI.Name = "MACRO";
+HI.Value = "0";
+HI.Type = "int";
 HI.Kind = index::SymbolKind::Macro;
 HI.Definition = "#define MACRO 0\n\n"
 "// Expands to\n"
@@ -3746,6 +3752,213 @@
   EXPECT_EQ(H->Type->Type, "int");
   EXPECT_EQ(H->Definition, "using foo = type");
 }
+
+TEST(Hover, EvaluateMacros) {
+  llvm::StringRef PredefinedCXX = R"cpp(
+#define X 42
+#define SizeOf sizeof
+#define AlignOf alignof
+#define PLUS_TWO +2
+#define TWO 2
+
+using u64 = unsigned long long;
+// calculate (a ** b) % p
+constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
+  u64 ret = 1;
+  while (b) {
+if (b & 1)
+  ret = (ret * a) % p;
+a = (a * a) % p;
+b >>= 1;
+  }
+  return ret;
+}
+#define last_n_digit(x, y, n)  \
+  pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
+#define declare_struct(X, name, value) \
+  struct X {   \
+constexpr auto name() { return value; }\
+  }
+#define gnu_statement_expression(value)\
+  ({   \
+declare_struct(Widget, getter, value); \
+Widget().getter(); \
+  })
+#define define_lambda_begin(lambda, ...)   \
+  [&](__VA_ARGS__) {
+#define define_lambda_end() }
+
+#define left_bracket [
+#define right_bracket ]
+#define array_decl(type, name, size) type name left_bracket size right_bracket
+  )cpp";
+
+  struct {
+llvm::StringRef Code;
+const std::function, size_t /*Id*/)>
+Validator;
+  } Cases[] = {
+  {
+  /*Code=*/R"cpp(
+X^;
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
+  },
+  },
+  {
+  /*Code=*/R"cpp(
+Size^Of(int);
+  )cpp",
+  /*Validator=*/
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+// Don't validate type or value of `sizeof` and `alignof` as we're
+// getting different values or desugared types on different
+// platforms. Same as below.
+  },
+  },
+  {
+  R"cpp(
+  struct Y {
+int y;
+double z;
+  };
+  Alig$3^nOf(Y);
+)cpp",
+  [](std::optional HI, size_t) {
+EXPECT_TRUE(HI->Value);
+EXPECT_TRUE(HI->Type);
+  },
+  },
+  {
+  R"cpp(
+  // 2**32 == 4294967296
+  last_n_di^git(2, 32, 6);
+)cpp",
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "967296 (0xec280)");
+EXPECT_EQ(HI->Type, "u64");
+  },
+  },
+  {
+  R"cpp(
+  gnu_statement_exp^ression(42);
+)cpp",
+  [](std::optional HI, size_t) {
+EXPECT_EQ(HI->Value, "42 (0x2a)");
+EXPECT_EQ(HI->Type, "int");
+  },
+  },
+  {
+  R"cpp(
+  40 + PLU^S_TWO;
+)cpp",
+  

  1   2   >