sammccall updated this revision to Diff 409902.
sammccall marked 4 inline comments as done.
sammccall added a comment.

address review comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D119537

Files:
  clang-tools-extra/clangd/AST.cpp
  clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
  clang-tools-extra/clangd/unittests/ASTTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp
  clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp

Index: clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp
+++ clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp
@@ -80,8 +80,18 @@
   EXPECT_THAT(apply("template <typename T> void x() { ^auto y = T::z(); }"),
               StartsWith("fail: Could not deduce type for 'auto' type"));
 
-  ExtraArgs.push_back("-std=c++17");
+  ExtraArgs.push_back("-std=c++20");
   EXPECT_UNAVAILABLE("template <au^to X> class Y;");
+
+  EXPECT_THAT(apply("auto X = [](^auto){};"),
+              StartsWith("fail: Could not deduce"));
+  EXPECT_EQ(apply("auto X = [](^auto){return 0;}; int Y = X(42);"),
+            "auto X = [](int){return 0;}; int Y = X(42);");
+  EXPECT_THAT(apply("auto X = [](^auto){return 0;}; int Y = X(42) + X('c');"),
+              StartsWith("fail: Could not deduce"));
+  // FIXME: should work on constrained auto params, once SourceRange is fixed.
+  EXPECT_UNAVAILABLE("template<class> concept C = true;"
+                     "auto X = [](C ^auto *){return 0;};");
 }
 
 } // namespace
Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -815,6 +815,12 @@
         }
       )cpp",
 
+      R"cpp(// auto lambda param where there's a single instantiation
+        struct [[Bar]] {};
+        auto Lambda = [](^auto){ return 0; };
+        int x = Lambda(Bar{});
+      )cpp",
+
       R"cpp(// decltype(auto) in function return
         struct [[Bar]] {};
         ^decltype(auto) test() {
Index: clang-tools-extra/clangd/unittests/ASTTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ASTTests.cpp
+++ clang-tools-extra/clangd/unittests/ASTTests.cpp
@@ -179,20 +179,76 @@
           )cpp",
           "Bar",
       },
+      {
+          R"cpp(
+            // Generic lambda param.
+            struct Foo{};
+            auto Generic = [](^auto x) { return 0; };
+            int m = Generic(Foo{});
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp(
+            // Generic lambda instantiated twice, matching deduction.
+            struct Foo{};
+            using Bar = Foo;
+            auto Generic = [](^auto x, auto y) { return 0; };
+            int m = Generic(Bar{}, "one");
+            int n = Generic(Foo{}, 2);
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp(
+            // Generic lambda instantiated twice, conflicting deduction.
+            struct Foo{};
+            auto Generic = [](^auto y) { return 0; };
+            int m = Generic("one");
+            int n = Generic(2);
+          )cpp",
+          nullptr,
+      },
+      {
+          R"cpp(
+            // Generic function param.
+            struct Foo{};
+            int generic(^auto x) { return 0; }
+            int m = generic(Foo{});
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp(
+            // More complicated param type involving auto.
+            template <class> concept C = true;
+            struct Foo{};
+            int generic(C ^auto *x) { return 0; }
+            const Foo *Ptr = nullptr;
+            int m = generic(Ptr);
+          )cpp",
+          "const struct Foo",
+      },
   };
   for (Test T : Tests) {
     Annotations File(T.AnnotatedCode);
-    auto AST = TestTU::withCode(File.code()).build();
+    auto TU = TestTU::withCode(File.code());
+    TU.ExtraArgs.push_back("-std=c++20");
+    auto AST = TU.build();
     SourceManagerForFile SM("foo.cpp", File.code());
 
-    SCOPED_TRACE(File.code());
+    SCOPED_TRACE(T.AnnotatedCode);
     EXPECT_FALSE(File.points().empty());
     for (Position Pos : File.points()) {
       auto Location = sourceLocationInMainFile(SM.get(), Pos);
       ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());
       auto DeducedType = getDeducedType(AST.getASTContext(), *Location);
-      ASSERT_TRUE(DeducedType);
-      EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
+      if (T.DeducedType == nullptr) {
+        EXPECT_FALSE(DeducedType);
+      } else {
+        ASSERT_TRUE(DeducedType);
+        EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
+      }
     }
   }
 }
Index: clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
@@ -45,8 +45,7 @@
   std::string title() const override;
 
 private:
-  /// Cache the AutoTypeLoc, so that we do not need to search twice.
-  llvm::Optional<clang::AutoTypeLoc> CachedLocation;
+  SourceRange AutoRange;
 };
 
 REGISTER_TWEAK(ExpandAutoType)
@@ -91,27 +90,35 @@
   return false;
 }
 
-bool ExpandAutoType::prepare(const Selection& Inputs) {
-  CachedLocation = llvm::None;
+bool ExpandAutoType::prepare(const Selection &Inputs) {
   if (auto *Node = Inputs.ASTSelection.commonAncestor()) {
     if (auto *TypeNode = Node->ASTNode.get<TypeLoc>()) {
       if (const AutoTypeLoc Result = TypeNode->getAs<AutoTypeLoc>()) {
         if (!isStructuredBindingType(Node) &&
             !isDeducedAsLambda(Node, Result.getBeginLoc()) &&
             !isTemplateParam(Node))
-          CachedLocation = Result;
+          AutoRange = Result.getSourceRange();
+      }
+      if (auto TTPAuto = TypeNode->getAs<TemplateTypeParmTypeLoc>()) {
+        // We exclude concept constraints for now, as the SourceRange is wrong.
+        // void foo(C auto x) {};
+        //            ^^^^
+        // TTPAuto->getSourceRange only covers "auto", not "C auto".
+        if (TTPAuto.getDecl()->isImplicit() &&
+            !TTPAuto.getDecl()->hasTypeConstraint())
+          AutoRange = TTPAuto.getSourceRange();
       }
     }
   }
 
-  return (bool) CachedLocation;
+  return AutoRange.isValid();
 }
 
 Expected<Tweak::Effect> ExpandAutoType::apply(const Selection& Inputs) {
   auto &SrcMgr = Inputs.AST->getSourceManager();
 
-  llvm::Optional<clang::QualType> DeducedType = getDeducedType(
-      Inputs.AST->getASTContext(), CachedLocation->getBeginLoc());
+  llvm::Optional<clang::QualType> DeducedType =
+      getDeducedType(Inputs.AST->getASTContext(), AutoRange.getBegin());
 
   // if we can't resolve the type, return an error message
   if (DeducedType == llvm::None || (*DeducedType)->isUndeducedAutoType())
@@ -133,9 +140,8 @@
   std::string PrettyTypeName = printType(*DeducedType,
       Inputs.ASTSelection.commonAncestor()->getDeclContext());
 
-  tooling::Replacement
-      Expansion(SrcMgr, CharSourceRange(CachedLocation->getSourceRange(), true),
-                PrettyTypeName);
+  tooling::Replacement Expansion(SrcMgr, CharSourceRange(AutoRange, true),
+                                 PrettyTypeName);
 
   return Effect::mainFileEdit(SrcMgr, tooling::Replacements(Expansion));
 }
Index: clang-tools-extra/clangd/AST.cpp
===================================================================
--- clang-tools-extra/clangd/AST.cpp
+++ clang-tools-extra/clangd/AST.cpp
@@ -486,6 +486,87 @@
     return true;
   }
 
+  // Handle functions/lambdas with `auto` typed parameters.
+  // We'll examine visible specializations and see if they yield a unique type.
+  bool VisitParmVarDecl(ParmVarDecl *PVD) {
+    if (!PVD->getType()->isDependentType())
+      return true;
+    // 'auto' here does not name an AutoType, but an implicit template param.
+    TemplateTypeParmTypeLoc Auto =
+        findContainedAutoTTPLoc(PVD->getTypeSourceInfo()->getTypeLoc());
+    if (Auto.isNull() || Auto.getNameLoc() != SearchedLocation)
+      return true;
+    // We expect the TTP to be attached to this function template.
+    // Find the template and the param index.
+    auto *FD = llvm::dyn_cast<FunctionDecl>(PVD->getDeclContext());
+    if (!FD)
+      return true;
+    auto *FTD = FD->getDescribedFunctionTemplate();
+    if (!FTD)
+      return true;
+    int ParamIndex = paramIndex(*FTD, *Auto.getDecl());
+    if (ParamIndex < 0) {
+      assert(false && "auto TTP is not from enclosing function?");
+      return true;
+    }
+
+    // Now determine the unique type arg among the implicit specializations.
+    const ASTContext &Ctx = PVD->getASTContext();
+    QualType UniqueType;
+    CanQualType CanUniqueType;
+    for (const FunctionDecl *Spec : FTD->specializations()) {
+      // Meaning `auto` is a bit overloaded if the function is specialized.
+      if (Spec->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+        return true;
+      // Find the type for this specialization.
+      const auto *Args = Spec->getTemplateSpecializationArgs();
+      if (Args->size() != FTD->getTemplateParameters()->size())
+        continue; // no weird variadic stuff
+      QualType SpecType = Args->get(ParamIndex).getAsType();
+      if (SpecType.isNull())
+        continue;
+
+      // Deduced types need only be *canonically* equal.
+      CanQualType CanSpecType = Ctx.getCanonicalType(SpecType);
+      if (CanUniqueType.isNull()) {
+        CanUniqueType = CanSpecType;
+        UniqueType = SpecType;
+        continue;
+      }
+      if (CanUniqueType != CanSpecType)
+        return true; // deduced type is not unique
+    }
+    DeducedType = UniqueType;
+    return true;
+  }
+
+  // Find the abbreviated-function-template `auto` within a type.
+  // Similar to getContainedAutoTypeLoc, but these `auto`s are
+  // TemplateTypeParmTypes for implicit TTPs, instead of AutoTypes.
+  // Also we don't look very hard, just stripping const, references, pointers.
+  // FIXME: handle more types: vector<auto>?
+  static TemplateTypeParmTypeLoc findContainedAutoTTPLoc(TypeLoc TL) {
+    if (auto QTL = TL.getAs<QualifiedTypeLoc>())
+      return findContainedAutoTTPLoc(QTL.getUnqualifiedLoc());
+    if (llvm::isa<PointerType, ReferenceType>(TL.getTypePtr()))
+      return findContainedAutoTTPLoc(TL.getNextTypeLoc());
+    if (auto TTPTL = TL.getAs<TemplateTypeParmTypeLoc>()) {
+      if (TTPTL.getTypePtr()->getDecl()->isImplicit())
+        return TTPTL;
+    }
+    return {};
+  }
+
+  static int paramIndex(const TemplateDecl &TD, NamedDecl &Param) {
+    unsigned I = 0;
+    for (auto *ND : *TD.getTemplateParameters()) {
+      if (&Param == ND)
+        return I;
+      ++I;
+    }
+    return -1;
+  }
+
   QualType DeducedType;
 };
 } // namespace
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to