[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-26 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL347559: [clangd] Initial implementation of expected types 
(authored by ibiryukov, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

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

https://reviews.llvm.org/D52273

Files:
  clang-tools-extra/trunk/clangd/CMakeLists.txt
  clang-tools-extra/trunk/clangd/ExpectedTypes.cpp
  clang-tools-extra/trunk/clangd/ExpectedTypes.h
  clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
  clang-tools-extra/trunk/unittests/clangd/ExpectedTypeTest.cpp

Index: clang-tools-extra/trunk/unittests/clangd/ExpectedTypeTest.cpp
===
--- clang-tools-extra/trunk/unittests/clangd/ExpectedTypeTest.cpp
+++ clang-tools-extra/trunk/unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,157 @@
+//===-- ExpectedTypeTest.cpp  ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAreArray;
+
+class ExpectedTypeConversionTest : public ::testing::Test {
+protected:
+  void build(StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(StringRef Name) {
+return (findDecl(*AST, Name));
+  }
+
+  QualType typeOf(StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  /// An overload for convenience.
+  Optional fromCompletionResult(const ValueDecl *D) {
+return OpaqueType::fromCompletionResult(
+ASTCtx(), CodeCompletionResult(D, CCP_Declaration));
+  }
+
+  /// A set of DeclNames whose type match each other computed by
+  /// OpaqueType::fromCompletionResult.
+  using EquivClass = std::set;
+
+  Matcher>
+  ClassesAre(ArrayRef Classes) {
+using MapEntry = std::map::value_type;
+
+std::vector> Elements;
+Elements.reserve(Classes.size());
+for (auto  : Classes)
+  Elements.push_back(Field(::second, Cls));
+return UnorderedElementsAreArray(Elements);
+  }
+
+  // Groups \p Decls into equivalence classes based on the result of
+  // 'OpaqueType::fromCompletionResult'.
+  std::map
+  buildEquivClasses(ArrayRef DeclNames) {
+std::map Classes;
+for (StringRef Name : DeclNames) {
+  auto Type = OpaqueType::fromType(ASTCtx(), typeOf(Name));
+  Classes[Type->raw()].insert(Name);
+}
+return Classes;
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  Optional AST;
+};
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(
+// ints.
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+
+// floats.
+float f;
+double d;
+
+// pointers
+int* iptr;
+bool* bptr;
+
+// user-defined types.
+struct X {};
+X user_type;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"b", "i", "ui", "ll", "f", "d", "iptr", "bptr",
+ "user_type"}),
+  ClassesAre({{"b"},
+  {"i", "ui", "ll"},
+  {"f", "d"},
+  {"iptr"},
+  {"bptr"},
+  {"user_type"}}));
+}
+
+TEST_F(ExpectedTypeConversionTest, ReferencesDontMatter) {
+  build(R"cpp(
+int noref;
+int & ref = noref;
+const int & const_ref = noref;
+int && rv_ref = 10;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"noref", "ref", "const_ref", "rv_ref"}),
+  SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, ArraysDecay) {
+  build(R"cpp(
+ int arr[2];
+ int (_ref)[2] = arr;
+ int *ptr;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"arr", "arr_ref", "ptr"}), SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, FunctionReturns) {
+  build(R"cpp(
+ int returns_int();
+ int* returns_ptr();
+
+ int int_;
+ int* int_ptr;
+  )cpp");
+
+  OpaqueType IntTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_int")), IntTy);
+
+  OpaqueType IntPtrTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_ptr"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_ptr")), IntPtrTy);
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-23 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov updated this revision to Diff 175098.
ilya-biryukov marked 2 inline comments as done.
ilya-biryukov added a comment.

- Add using namespace llvm, get rid of llvm::


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273

Files:
  clangd/CMakeLists.txt
  clangd/ExpectedTypes.cpp
  clangd/ExpectedTypes.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ExpectedTypeTest.cpp

Index: unittests/clangd/ExpectedTypeTest.cpp
===
--- /dev/null
+++ unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,157 @@
+//===-- ExpectedTypeTest.cpp  ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAreArray;
+
+class ExpectedTypeConversionTest : public ::testing::Test {
+protected:
+  void build(StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(StringRef Name) {
+return (findDecl(*AST, Name));
+  }
+
+  QualType typeOf(StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  /// An overload for convenience.
+  Optional fromCompletionResult(const ValueDecl *D) {
+return OpaqueType::fromCompletionResult(
+ASTCtx(), CodeCompletionResult(D, CCP_Declaration));
+  }
+
+  /// A set of DeclNames whose type match each other computed by
+  /// OpaqueType::fromCompletionResult.
+  using EquivClass = std::set;
+
+  Matcher>
+  ClassesAre(ArrayRef Classes) {
+using MapEntry = std::map::value_type;
+
+std::vector> Elements;
+Elements.reserve(Classes.size());
+for (auto  : Classes)
+  Elements.push_back(Field(::second, Cls));
+return UnorderedElementsAreArray(Elements);
+  }
+
+  // Groups \p Decls into equivalence classes based on the result of
+  // 'OpaqueType::fromCompletionResult'.
+  std::map
+  buildEquivClasses(ArrayRef DeclNames) {
+std::map Classes;
+for (StringRef Name : DeclNames) {
+  auto Type = OpaqueType::fromType(ASTCtx(), typeOf(Name));
+  Classes[Type->raw()].insert(Name);
+}
+return Classes;
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  Optional AST;
+};
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(
+// ints.
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+
+// floats.
+float f;
+double d;
+
+// pointers
+int* iptr;
+bool* bptr;
+
+// user-defined types.
+struct X {};
+X user_type;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"b", "i", "ui", "ll", "f", "d", "iptr", "bptr",
+ "user_type"}),
+  ClassesAre({{"b"},
+  {"i", "ui", "ll"},
+  {"f", "d"},
+  {"iptr"},
+  {"bptr"},
+  {"user_type"}}));
+}
+
+TEST_F(ExpectedTypeConversionTest, ReferencesDontMatter) {
+  build(R"cpp(
+int noref;
+int & ref = noref;
+const int & const_ref = noref;
+int && rv_ref = 10;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"noref", "ref", "const_ref", "rv_ref"}),
+  SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, ArraysDecay) {
+  build(R"cpp(
+ int arr[2];
+ int (_ref)[2] = arr;
+ int *ptr;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"arr", "arr_ref", "ptr"}), SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, FunctionReturns) {
+  build(R"cpp(
+ int returns_int();
+ int* returns_ptr();
+
+ int int_;
+ int* int_ptr;
+  )cpp");
+
+  OpaqueType IntTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_int")), IntTy);
+
+  OpaqueType IntPtrTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_ptr"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_ptr")), IntPtrTy);
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -19,6 +19,7 @@
   ContextTests.cpp
   DexTests.cpp
   DraftStoreTests.cpp
+  ExpectedTypeTest.cpp
   FileDistanceTests.cpp
   FileIndexTests.cpp
   

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall accepted this revision.
sammccall added inline comments.
This revision is now accepted and ready to land.



Comment at: clangd/ExpectedTypes.cpp:8
+
+namespace clang {
+namespace clangd {

nit: using namespace llvm (until/unless we switch other files)



Comment at: unittests/clangd/ExpectedTypeTest.cpp:33
+protected:
+  void build(llvm::StringRef Code) {
+assert(!AST && "AST built twice");

drop llvm:: here and below?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-22 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added inline comments.



Comment at: clangd/ExpectedTypes.h:32
+/// this allows the representation to be stored in the index and compared with
+/// types coming from a different AST later.
+class OpaqueType {

sammccall wrote:
> ilya-biryukov wrote:
> > sammccall wrote:
> > > Does this need to be a separate class rather than using `std::string`?
> > > There are echoes of `SymbolID` here, but there were some factors that 
> > > don't apply here:
> > >  - it was fixed-width
> > >  - memory layout was important as we stored lots of these in memory
> > >  - we hashed them a lot and wanted a specific hash function
> > > 
> > > I suspect at least initially producing a somewhat readable std::string a 
> > > la USRGeneration would be enough.
> > Would still want to keep it as a marker type just for the sake of 
> > indicating what we return and documentation purposes.
> > It also adds some type safety (granted, not much) for some use-cases.
> > 
> > There's still an option to go strings with `rawStr()` if needed.
> For documentation purposes, `using OpaqueType = std::string` or so seems like 
> a reasonable compromise?
> 
> This is very heavyweight for the amount of typesafety we get.
> (Apart from the class itself, you've got `==` and `!=`, we should definitely 
> have `<<` as well, `DenseMapInfo<>` and `<` may get added down the line...)
As discussed offline, kept the class with an expectation that we'll use the 
fixed-size representation at some point. Added a comment that it can be viewed 
as a strong typedef to string for now.



Comment at: clangd/ExpectedTypes.h:42
+  /// completion context.
+  static llvm::Optional fromPreferredType(ASTContext ,
+  QualType Type);

sammccall wrote:
> I'd suggest just `fromType`, exposing this as the primary method, and then on 
> `fromCompletionResult` document why it's different.
> 
> Having the names suggest the underlying structure (that `fromType` is "more 
> fundamental")  aids understanding, and doesn't really feel like we're 
> painting ourselves into a corner.
> 
> Alternately, `fromCompletionContext` and `fromCompletionResult` would be more 
> clearly symmetrical.
Done. Using `fromType` now.



Comment at: clangd/ExpectedTypes.h:59
+private:
+  static llvm::Optional encode(ASTContext , QualType Type);
+  explicit OpaqueType(std::string Data);

sammccall wrote:
> any reason to put this in the header?
It uses a private constructor of the class, so it seems natural for it to be a 
private static function.




Comment at: unittests/clangd/ExpectedTypeTest.cpp:51
+
+class ConvertibleToMatcher
+: public ::testing::MatcherInterface {

sammccall wrote:
> "convertible to" is a problematic description for a couple of reasons:
>  - it's a relationship between types, but encapsulates unrelated semantics to 
> do with completions
>  - it's a higher level of abstraction than the code under test
> 
> As discussed offline/below, I think the best remedy here is just to drop this 
> matcher - it's only used in one test that can now live with something much 
> simpler.
Done. It was needed only for one test, testing it diretly now.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:142
+  decl("iptr"), decl("bptr"), decl("user_type")};
+  EXPECT_THAT(buildEquivClasses(Decls), ClassesAre({{"b", "i", "ui", "ll"},
+{"f", "d"},

sammccall wrote:
> Ooh, we should avoid folding bool with other integer types I think!
> 
> You hardly ever want to pass a bool where an int is expected. (The reverse 
> int -> bool is somewhat common, but no more than pointer -> bool... type 
> equivalence isn't the right hammer to solve that case).
Fair point, changed this. Bool requires a whole different handling anyway, e.g. 
I definitely want my pointers to be boosted in if conditions.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:173
+
+TEST_F(ExpectedTypeConversionTest, FunctionReturns) {
+  build(R"cpp(

sammccall wrote:
> I think this test is a bit too high-level - there are big abstractions 
> between the test code and the code under test (which is pretty simple).
> 
> I'd suggest just
> `EXPECT_EQ(
> OpaqueType::fromCompletionResult(ASTCtx(), decl("returns_int")),
> OpaqueType::fromExpectedType(ASTCtx(), decl("int_"));`
> 
> (If you think there's something worth testing for the pointer case, I'd do 
> that instead rather than as well)
Done. There is still a helper variable per case (I think it improves the 
readability a little), but otherwise the test is more straightforward now.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



___
cfe-commits mailing list

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-22 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov updated this revision to Diff 175050.
ilya-biryukov marked 11 inline comments as done.
ilya-biryukov added a comment.

- Address comments


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273

Files:
  clangd/CMakeLists.txt
  clangd/ExpectedTypes.cpp
  clangd/ExpectedTypes.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ExpectedTypeTest.cpp

Index: unittests/clangd/ExpectedTypeTest.cpp
===
--- /dev/null
+++ unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,157 @@
+//===-- ExpectedTypeTest.cpp  ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAreArray;
+
+class ExpectedTypeConversionTest : public ::testing::Test {
+protected:
+  void build(llvm::StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(llvm::StringRef Name) {
+return ::cast(findDecl(*AST, Name));
+  }
+
+  QualType typeOf(llvm::StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  /// An overload for convenience.
+  Optional fromCompletionResult(const ValueDecl *D) {
+return OpaqueType::fromCompletionResult(
+ASTCtx(), CodeCompletionResult(D, CCP_Declaration));
+  }
+
+  /// A set of DeclNames whose type match each other computed by
+  /// OpaqueType::fromCompletionResult.
+  using EquivClass = std::set;
+
+  Matcher>
+  ClassesAre(llvm::ArrayRef Classes) {
+using MapEntry = std::map::value_type;
+
+std::vector> Elements;
+Elements.reserve(Classes.size());
+for (auto  : Classes)
+  Elements.push_back(Field(::second, Cls));
+return UnorderedElementsAreArray(Elements);
+  }
+
+  // Groups \p Decls into equivalence classes based on the result of
+  // 'OpaqueType::fromCompletionResult'.
+  std::map
+  buildEquivClasses(llvm::ArrayRef DeclNames) {
+std::map Classes;
+for (llvm::StringRef Name : DeclNames) {
+  auto Type = OpaqueType::fromType(ASTCtx(), typeOf(Name));
+  Classes[Type->raw()].insert(Name);
+}
+return Classes;
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  llvm::Optional AST;
+};
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(
+// ints.
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+
+// floats.
+float f;
+double d;
+
+// pointers
+int* iptr;
+bool* bptr;
+
+// user-defined types.
+struct X {};
+X user_type;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"b", "i", "ui", "ll", "f", "d", "iptr", "bptr",
+ "user_type"}),
+  ClassesAre({{"b"},
+  {"i", "ui", "ll"},
+  {"f", "d"},
+  {"iptr"},
+  {"bptr"},
+  {"user_type"}}));
+}
+
+TEST_F(ExpectedTypeConversionTest, ReferencesDontMatter) {
+  build(R"cpp(
+int noref;
+int & ref = noref;
+const int & const_ref = noref;
+int && rv_ref = 10;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"noref", "ref", "const_ref", "rv_ref"}),
+  SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, ArraysDecay) {
+  build(R"cpp(
+ int arr[2];
+ int (_ref)[2] = arr;
+ int *ptr;
+  )cpp");
+
+  EXPECT_THAT(buildEquivClasses({"arr", "arr_ref", "ptr"}), SizeIs(1));
+}
+
+TEST_F(ExpectedTypeConversionTest, FunctionReturns) {
+  build(R"cpp(
+ int returns_int();
+ int* returns_ptr();
+
+ int int_;
+ int* int_ptr;
+  )cpp");
+
+  OpaqueType IntTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_int")), IntTy);
+
+  OpaqueType IntPtrTy = *OpaqueType::fromType(ASTCtx(), typeOf("int_ptr"));
+  EXPECT_EQ(fromCompletionResult(decl("returns_ptr")), IntPtrTy);
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -19,6 +19,7 @@
   ContextTests.cpp
   DexTests.cpp
   DraftStoreTests.cpp
+  ExpectedTypeTest.cpp
   FileDistanceTests.cpp
   

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-20 Thread Sam McCall via Phabricator via cfe-commits
sammccall added inline comments.



Comment at: clangd/ExpectedTypes.h:32
+/// this allows the representation to be stored in the index and compared with
+/// types coming from a different AST later.
+class OpaqueType {

ilya-biryukov wrote:
> sammccall wrote:
> > Does this need to be a separate class rather than using `std::string`?
> > There are echoes of `SymbolID` here, but there were some factors that don't 
> > apply here:
> >  - it was fixed-width
> >  - memory layout was important as we stored lots of these in memory
> >  - we hashed them a lot and wanted a specific hash function
> > 
> > I suspect at least initially producing a somewhat readable std::string a la 
> > USRGeneration would be enough.
> Would still want to keep it as a marker type just for the sake of indicating 
> what we return and documentation purposes.
> It also adds some type safety (granted, not much) for some use-cases.
> 
> There's still an option to go strings with `rawStr()` if needed.
For documentation purposes, `using OpaqueType = std::string` or so seems like a 
reasonable compromise?

This is very heavyweight for the amount of typesafety we get.
(Apart from the class itself, you've got `==` and `!=`, we should definitely 
have `<<` as well, `DenseMapInfo<>` and `<` may get added down the line...)



Comment at: clangd/ExpectedTypes.h:15
+//
+// When using clang APIs, we cannot determine if a type coming from an AST is
+// convertible to another type without looking at both types in the same AST.

I think this largely rehashes the second sentence of the above para. I'd 
suggest this one focus more closely on what our model *is*:

  We define an encoding of AST types as opaque strings, which can be stored in 
the index.
  Similar types (such as `string` and `const string&`) are folded together, 
forming equivalence classes with the same encoding.



Comment at: clangd/ExpectedTypes.h:18
+// Unfortunately, we do not have ASTs for index-based completion, so we have 
use
+// a stable encoding for the C++ types and map them into equivalence classes
+// based on convertibility.

("stable" might suggest across versions)



Comment at: clangd/ExpectedTypes.h:42
+  /// completion context.
+  static llvm::Optional fromPreferredType(ASTContext ,
+  QualType Type);

I'd suggest just `fromType`, exposing this as the primary method, and then on 
`fromCompletionResult` document why it's different.

Having the names suggest the underlying structure (that `fromType` is "more 
fundamental")  aids understanding, and doesn't really feel like we're painting 
ourselves into a corner.

Alternately, `fromCompletionContext` and `fromCompletionResult` would be more 
clearly symmetrical.



Comment at: clangd/ExpectedTypes.h:49
+  /// rely on it.
+  llvm::StringRef rawStr() const { return Data; }
+

nit: if you keep this class, call this raw() for consistency with symbolid?(



Comment at: clangd/ExpectedTypes.h:59
+private:
+  static llvm::Optional encode(ASTContext , QualType Type);
+  explicit OpaqueType(std::string Data);

any reason to put this in the header?



Comment at: unittests/clangd/ExpectedTypeTest.cpp:29
+
+class ASTTest : public ::testing::Test {
+protected:

This seems fine as a fixture, but I'd merge with the subclass - tests should be 
easy to read!



Comment at: unittests/clangd/ExpectedTypeTest.cpp:51
+
+class ConvertibleToMatcher
+: public ::testing::MatcherInterface {

"convertible to" is a problematic description for a couple of reasons:
 - it's a relationship between types, but encapsulates unrelated semantics to 
do with completions
 - it's a higher level of abstraction than the code under test

As discussed offline/below, I think the best remedy here is just to drop this 
matcher - it's only used in one test that can now live with something much 
simpler.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:107
+  std::map
+  buildEquivClasses(llvm::ArrayRef Decls) {
+std::map Classes;

nit: any reason this takes Decl*s instead of strings? would be a bit terser not 
to wrap the args in decl()



Comment at: unittests/clangd/ExpectedTypeTest.cpp:110
+for (auto *D : Decls) {
+  auto Type = OpaqueType::fromCompletionResult(
+  ASTCtx(), CodeCompletionResult(D, CCP_Declaration));

I think we could simplify by only testing the type encodings/equiv classes 
here, and relying on the function -> return type conversion happening elsewhere.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:142
+  decl("iptr"), decl("bptr"), decl("user_type")};
+  EXPECT_THAT(buildEquivClasses(Decls), 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-15 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov marked 2 inline comments as done.
ilya-biryukov added inline comments.



Comment at: clangd/ExpectedTypes.cpp:12
+
+static llvm::Optional toEquivClass(ASTContext , QualType T) {
+  if (T.isNull() || T->isDependentType())

sammccall wrote:
> returning QualType vs Type*? It seems we strip all qualifiers, seems clearest 
> for the return type to reflect that.
Done. That produces a bit more trouble at the callsites, so not sure if it's an 
improvement overall.



Comment at: clangd/ExpectedTypes.cpp:15
+return llvm::None;
+  T = T.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  if (T->isIntegerType() && !T->isEnumeralType())

sammccall wrote:
> Maybe we want Ctx.getUnqualifiedArrayType here or (more likely?) do 
> array-to-pointer decay?
Added array-to-pointer decays, they should improve ranking when assigning from 
an array to a pointer, which is nice.
Also added a FIXME that we should drop qualifiers from inner types of the 
pointers (since we do this for arrays). I think it's fine to leave it for the 
later improvements.



Comment at: clangd/ExpectedTypes.cpp:16
+  T = T.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  if (T->isIntegerType() && !T->isEnumeralType())
+return QualType(Ctx.IntTy);

sammccall wrote:
> wow, "enumeral" might be my favorite c++-made-up word, displacing "emplace"...
 ¯\_(ツ)_/¯



Comment at: clangd/ExpectedTypes.cpp:30
+return llvm::None;
+  QualType T = VD->getType().getCanonicalType().getNonReferenceType();
+  if (!T->isFunctionType())

sammccall wrote:
> nit: is canonicalization necessary here? you do it in toEquivClass
> (I guess dropping references is, for the function type check)
It was not important, removed it.



Comment at: clangd/ExpectedTypes.cpp:46
+  llvm::SmallString<128> Out;
+  if (index::generateUSRForType(T, Ctx, Out))
+return llvm::None;

sammccall wrote:
> I think ultimately we may want to replace this with a custom walker:
>  - we may want to ignore attributes (e.g. const) or bail out in some cases
>  - generateUSRForType may not have the exact semantics we want for other 
> random reasons
>  - we can do tricks with hash_combine to avoid actually building huge strings 
> we don't care about
> 
> not something for this patch, but maybe a FIXME?
> 
USRs actually seems like a pretty good fit here. I'm not sure dropping 
attributes for internal types would make a big difference in the scoring and 
not sure how big of a problem the strings are, would be nice to actually learn 
it's a problem (in memory consumption, memory alloc rates, etc) before changing 
this.

It's definitely possible to do that, of course, we have a room to change the 
encoding whenever we want, but would avoid adding a FIXME and committing to 
this approach in the initial patch.



Comment at: clangd/ExpectedTypes.h:10
+// A simplified model of C++ types that can be used to check whether they are
+// convertible between each other without looking at the ASTs.
+// Used for code completion ranking.

ilya-biryukov wrote:
> ioeric wrote:
> > We might want to formalize what "convertible" means here. E.g. does it 
> > cover conversion between base and derived class? Does it cover double <-> 
> > int conversion?
> I want to leave it vague for now. Convertible means whatever we think is good 
> for code completion ranking.
> Formalizing means we'll either dig into the C++ encoding or be imprecise. 
> 
> Happy to add the docs, but they'll probably get outdated on every change. 
> Reading the code is actually simpler to get what's going on at this point.
Added a clarification that we want "convertible for the purpose of code 
completion".



Comment at: clangd/ExpectedTypes.h:29
+namespace clangd {
+/// An opaque representation of a type that can be computed based on clang AST
+/// and compared for equality. The encoding is stable between different ASTs,

ioeric wrote:
> The name seems opaque ;) Why is it `opaque`? 
Removed the "opaque" from the comment, hopefully this causes less confusion.
The idea is that users shouldn't rely on this representation in any other way 
than comparing it for equality.



Comment at: clangd/ExpectedTypes.h:32
+/// this allows the representation to be stored in the index and compared with
+/// types coming from a different AST later.
+class OpaqueType {

sammccall wrote:
> Does this need to be a separate class rather than using `std::string`?
> There are echoes of `SymbolID` here, but there were some factors that don't 
> apply here:
>  - it was fixed-width
>  - memory layout was important as we stored lots of these in memory
>  - we hashed them a lot and wanted a specific hash function
> 
> I suspect at least initially producing a somewhat readable 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-15 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov updated this revision to Diff 174217.
ilya-biryukov marked 10 inline comments as done.
ilya-biryukov added a comment.

- Address comments


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273

Files:
  clangd/CMakeLists.txt
  clangd/ExpectedTypes.cpp
  clangd/ExpectedTypes.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ExpectedTypeTest.cpp

Index: unittests/clangd/ExpectedTypeTest.cpp
===
--- /dev/null
+++ unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,198 @@
+//===-- ExpectedTypeTest.cpp  ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAreArray;
+
+class ASTTest : public ::testing::Test {
+protected:
+  void build(llvm::StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(llvm::StringRef Name) {
+return ::cast(findDecl(*AST, Name));
+  }
+
+  QualType typeOf(llvm::StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  llvm::Optional AST;
+};
+
+class ConvertibleToMatcher
+: public ::testing::MatcherInterface {
+  ASTContext 
+  QualType To;
+  OpaqueType PreferredType;
+
+public:
+  ConvertibleToMatcher(ASTContext , QualType To)
+  : Ctx(Ctx), To(To.getCanonicalType()),
+PreferredType(*OpaqueType::fromPreferredType(Ctx, To)) {}
+
+  void DescribeTo(std::ostream *OS) const override {
+*OS << "Is convertible to type '" << To.getAsString() << "'";
+  }
+
+  bool MatchAndExplain(const ValueDecl *V,
+   ::testing::MatchResultListener *L) const override {
+assert(V);
+assert(>getASTContext() ==  && "different ASTs?");
+auto ConvertibleTo = OpaqueType::fromCompletionResult(
+Ctx, CodeCompletionResult(V, CCP_Declaration));
+
+bool Matched = PreferredType == ConvertibleTo;
+if (L->IsInterested())
+  *L << "Types of source and target "
+ << (Matched ? "matched" : "did not match")
+ << "\n\tTarget type: " << To.getAsString()
+ << "\n\tSource value type: " << V->getType().getAsString();
+return Matched;
+  }
+};
+
+class ExpectedTypeConversionTest : public ASTTest {
+protected:
+  Matcher isConvertibleTo(QualType To) {
+return ::testing::MakeMatcher(new ConvertibleToMatcher(ASTCtx(), To));
+  }
+
+  // A set of DeclNames whose type match each other computed by
+  // OpaqueType::fromCompletionResult.
+  using EquivClass = std::set;
+
+  Matcher>
+  ClassesAre(llvm::ArrayRef Classes) {
+using MapEntry = std::map::value_type;
+
+std::vector> Elements;
+Elements.reserve(Classes.size());
+for (auto  : Classes)
+  Elements.push_back(Field(::second, Cls));
+return UnorderedElementsAreArray(Elements);
+  }
+
+  // Groups \p Decls into equivalence classes based on the result of
+  // 'OpaqueType::fromCompletionResult'.
+  std::map
+  buildEquivClasses(llvm::ArrayRef Decls) {
+std::map Classes;
+for (auto *D : Decls) {
+  auto Type = OpaqueType::fromCompletionResult(
+  ASTCtx(), CodeCompletionResult(D, CCP_Declaration));
+  Classes[Type->rawStr()].insert(D->getNameAsString());
+}
+return Classes;
+  }
+};
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(
+// ints.
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+
+// floats.
+float f;
+double d;
+
+// pointers
+int* iptr;
+bool* bptr;
+
+// user-defined types.
+struct X {};
+X user_type;
+  )cpp");
+
+  const ValueDecl *Decls[] = {decl("b"),decl("i"),decl("ui"),
+  decl("ll"),   decl("f"),decl("d"),
+  decl("iptr"), decl("bptr"), decl("user_type")};
+  EXPECT_THAT(buildEquivClasses(Decls), ClassesAre({{"b", "i", "ui", "ll"},
+{"f", "d"},
+{"iptr"},
+{"bptr"},
+{"user_type"}}));
+}
+
+TEST_F(ExpectedTypeConversionTest, ReferencesDontMatter) {
+  build(R"cpp(
+int noref;
+int 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-13 Thread Sam McCall via Phabricator via cfe-commits
sammccall added inline comments.



Comment at: clangd/ExpectedTypes.cpp:12
+
+static llvm::Optional toEquivClass(ASTContext , QualType T) {
+  if (T.isNull() || T->isDependentType())

returning QualType vs Type*? It seems we strip all qualifiers, seems clearest 
for the return type to reflect that.



Comment at: clangd/ExpectedTypes.cpp:15
+return llvm::None;
+  T = T.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  if (T->isIntegerType() && !T->isEnumeralType())

Maybe we want Ctx.getUnqualifiedArrayType here or (more likely?) do 
array-to-pointer decay?



Comment at: clangd/ExpectedTypes.cpp:16
+  T = T.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  if (T->isIntegerType() && !T->isEnumeralType())
+return QualType(Ctx.IntTy);

wow, "enumeral" might be my favorite c++-made-up word, displacing "emplace"...



Comment at: clangd/ExpectedTypes.cpp:25
+typeOfCompletion(const CodeCompletionResult ) {
+  if (!R.Declaration)
+return llvm::None;

nit: dyn_cast_or_null below instead?



Comment at: clangd/ExpectedTypes.cpp:30
+return llvm::None;
+  QualType T = VD->getType().getCanonicalType().getNonReferenceType();
+  if (!T->isFunctionType())

nit: is canonicalization necessary here? you do it in toEquivClass
(I guess dropping references is, for the function type check)



Comment at: clangd/ExpectedTypes.cpp:33
+return T;
+  // Functions are a special case. They are completed as 'foo()' and we want to
+  // match their return type rather than the function type itself.

nit: I'd put the special case in the if() block, but up to you



Comment at: clangd/ExpectedTypes.cpp:37
+  // after the function name, e.g. `std::cout << std::endl`.
+  return T->getAs()->getReturnType().getNonReferenceType();
+}

dropping references seems redundant here, as you do it again later



Comment at: clangd/ExpectedTypes.cpp:46
+  llvm::SmallString<128> Out;
+  if (index::generateUSRForType(T, Ctx, Out))
+return llvm::None;

I think ultimately we may want to replace this with a custom walker:
 - we may want to ignore attributes (e.g. const) or bail out in some cases
 - generateUSRForType may not have the exact semantics we want for other random 
reasons
 - we can do tricks with hash_combine to avoid actually building huge strings 
we don't care about

not something for this patch, but maybe a FIXME?




Comment at: clangd/ExpectedTypes.cpp:71
+return llvm::None;
+  T = toEquivClass(Ctx, *T);
+  if (!T)

can you reuse fromPreferredType for the rest?



Comment at: clangd/ExpectedTypes.h:32
+/// this allows the representation to be stored in the index and compared with
+/// types coming from a different AST later.
+class OpaqueType {

Does this need to be a separate class rather than using `std::string`?
There are echoes of `SymbolID` here, but there were some factors that don't 
apply here:
 - it was fixed-width
 - memory layout was important as we stored lots of these in memory
 - we hashed them a lot and wanted a specific hash function

I suspect at least initially producing a somewhat readable std::string a la 
USRGeneration would be enough.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:79
+ << (Matched ? "matched" : "did not match")
+ << "\n\tTarget type: " << To.getAsString()
+ << "\n\tSource value type: " << V->getType().getAsString();

note that if you think it's useful you can To.dump(*L->stream())
Maybe this is more interesting if/when we have a custom visitor.



Comment at: unittests/clangd/ExpectedTypeTest.cpp:92
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(

I really like the declarative equivalence-class setup of the tests.

A couple of suggestions:
 - maybe store the equivalence classes as groups of strings rather than decls, 
and lazily grab the decls. It's easier to tersely represent them...
 - I think the "convertibleTo" DSL obscures/abstracts the actual APIs you're 
testing - they build opaque types, and you're asserting equality.
 - pairwise assertion messages may not give enough context: if you expect a == 
b == c, and a != b, then whether a == c and b == c are probably relevant

I'd consider actually building up the equivalence classes `map>` and writing a `MATCHER_P2(ClassesAre, 
/*vector>*/Classes, /*ParsedAST*/AST, "classes are " + 
testing::PrintToString(Classes))`

That way the actual and expected equivalence classes will be dumped on failure, 
and you can still grab the decls/types from the AST to dump their details.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273




[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-13 Thread Sam McCall via Phabricator via cfe-commits
sammccall added a comment.

Forgot to say - the scope here looks just right, thanks for slimming this down!


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-12 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added a comment.

@ioeric, thanks for the review round!
Answering the most important comments, will shortly send changes to actually 
address the rest.




Comment at: clangd/ExpectedTypes.cpp:40
+
+llvm::Optional encodeType(ASTContext , QualType T) {
+  assert(!T.isNull());

ioeric wrote:
> IIUC, we also encode the qualifiers into the final representation? If so, 
> have you considered the underlying type without qualifiers? It seems to me 
> this might be too restrictive for type-based boosting. For code completion 
> ranking, I think type qualifiers (`const` etc) can be separate signals.
This function's responsibility is to encode the type. There is code to strip 
the qualifiers from the types in `toEquivClass`.
The initial patch does not take qualifiers into account as none of the 
complicated conversion logic (qualifiers were taken into account there) the 
original patch had made much difference in the ranking measurements I made.
That said, this change does not aim to finalize the type encoding. I'll be 
looking into improving the type-based ranking after this lands, might re-add 
qualifiers if they turn out to be an improvement. Want to prove this with 
measurements, though.



Comment at: clangd/ExpectedTypes.h:10
+// A simplified model of C++ types that can be used to check whether they are
+// convertible between each other without looking at the ASTs.
+// Used for code completion ranking.

ioeric wrote:
> We might want to formalize what "convertible" means here. E.g. does it cover 
> conversion between base and derived class? Does it cover double <-> int 
> conversion?
I want to leave it vague for now. Convertible means whatever we think is good 
for code completion ranking.
Formalizing means we'll either dig into the C++ encoding or be imprecise. 

Happy to add the docs, but they'll probably get outdated on every change. 
Reading the code is actually simpler to get what's going on at this point.



Comment at: clangd/ExpectedTypes.h:37
+  fromCompletionResult(ASTContext , const CodeCompletionResult );
+  static llvm::Optional fromPreferredType(ASTContext ,
+  QualType Type);

ioeric wrote:
> why "preferred type"? maybe add a comment?
That's the terminology that clang uses for completion's context type. Will add 
a comment, thanks!



Comment at: clangd/ExpectedTypes.h:40
+
+  /// Get the raw byte representation of the type. Bitwise equality of the raw
+  /// data is equivalent to equality operators of SType itself. The raw

ioeric wrote:
> What is the raw representation? A hash or the type name or USR?
A string representation of the usr, but users shouldn't rely on it.
The contract is: you can use it to compare for equality and nothing else, so 
the comment is actually accurate :-)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-12 Thread Eric Liu via Phabricator via cfe-commits
ioeric added inline comments.



Comment at: clangd/ExpectedTypes.cpp:27
+return llvm::None;
+  auto *VD = llvm::dyn_cast(R.Declaration);
+  if (!VD)

maybe add a comment what `ValueDecl` covers roughly? E.g. functions, classes, 
variables etc.



Comment at: clangd/ExpectedTypes.cpp:40
+
+llvm::Optional encodeType(ASTContext , QualType T) {
+  assert(!T.isNull());

IIUC, we also encode the qualifiers into the final representation? If so, have 
you considered the underlying type without qualifiers? It seems to me this 
might be too restrictive for type-based boosting. For code completion ranking, 
I think type qualifiers (`const` etc) can be separate signals.



Comment at: clangd/ExpectedTypes.h:10
+// A simplified model of C++ types that can be used to check whether they are
+// convertible between each other without looking at the ASTs.
+// Used for code completion ranking.

We might want to formalize what "convertible" means here. E.g. does it cover 
conversion between base and derived class? Does it cover double <-> int 
conversion?



Comment at: clangd/ExpectedTypes.h:29
+namespace clangd {
+/// An opaque representation of a type that can be computed based on clang AST
+/// and compared for equality. The encoding is stable between different ASTs,

The name seems opaque ;) Why is it `opaque`? 



Comment at: clangd/ExpectedTypes.h:37
+  fromCompletionResult(ASTContext , const CodeCompletionResult );
+  static llvm::Optional fromPreferredType(ASTContext ,
+  QualType Type);

why "preferred type"? maybe add a comment?



Comment at: clangd/ExpectedTypes.h:40
+
+  /// Get the raw byte representation of the type. Bitwise equality of the raw
+  /// data is equivalent to equality operators of SType itself. The raw

What is the raw representation? A hash or the type name or USR?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-12 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added a comment.

In https://reviews.llvm.org/D52273#1294767, @malaperle wrote:

> What is the goal for doing this without the AST? Is the goal to not have to 
> keep the AST and save memory?


We don't have AST for index completions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-11 Thread Marc-Andre Laperle via Phabricator via cfe-commits
malaperle added a comment.

What is the goal for doing this without the AST? Is the goal to not have to 
keep the AST and save memory?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-09 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added a comment.

I've run the measurements on a highly simplified vs the original complicated 
model and got roughly the same results wrt to ranking improvements, so sending 
a new version of the patch with highly simplified mode for the type 
representation.
I believe there are still gains to be had from a more thorough treatment of C++ 
conversions, but there is definitely much to do in other areas that should 
provide better ground for seeing the actual improvements with the more 
complicated model.

In any case, starting with something simple is definitely a better ground. 
Thanks for initial review and suggestions!
And please take a look at the new version, it is significantly simpler and 
should be pretty easy to review :-)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-11-09 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov updated this revision to Diff 173337.
ilya-biryukov added a comment.

- Simplify the initial implementation
- Rename SType to OpaqueType


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273

Files:
  clangd/CMakeLists.txt
  clangd/ExpectedTypes.cpp
  clangd/ExpectedTypes.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ExpectedTypeTest.cpp

Index: unittests/clangd/ExpectedTypeTest.cpp
===
--- /dev/null
+++ unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,191 @@
+//===-- ExpectedTypeTest.cpp  ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::UnorderedElementsAre;
+using ::testing::UnorderedElementsAreArray;
+
+class ASTTest : public ::testing::Test {
+protected:
+  void build(llvm::StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(llvm::StringRef Name) {
+return ::cast(findDecl(*AST, Name));
+  }
+
+  QualType typeOf(llvm::StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  llvm::Optional AST;
+};
+
+class ConvertibleToMatcher
+: public ::testing::MatcherInterface {
+  ASTContext 
+  QualType To;
+  OpaqueType PreferredType;
+
+public:
+  ConvertibleToMatcher(ASTContext , QualType To)
+  : Ctx(Ctx), To(To.getCanonicalType()),
+PreferredType(*OpaqueType::fromPreferredType(Ctx, To)) {}
+
+  void DescribeTo(std::ostream *OS) const override {
+*OS << "Is convertible to type '" << To.getAsString() << "'";
+  }
+
+  bool MatchAndExplain(const ValueDecl *V,
+   ::testing::MatchResultListener *L) const override {
+assert(V);
+assert(>getASTContext() ==  && "different ASTs?");
+auto ConvertibleTo = OpaqueType::fromCompletionResult(
+Ctx, CodeCompletionResult(V, CCP_Declaration));
+
+bool Matched = PreferredType == ConvertibleTo;
+if (L->IsInterested())
+  *L << "Types of source and target "
+ << (Matched ? "matched" : "did not match")
+ << "\n\tTarget type: " << To.getAsString()
+ << "\n\tSource value type: " << V->getType().getAsString();
+return Matched;
+  }
+};
+
+class ExpectedTypeConversionTest : public ASTTest {
+protected:
+  Matcher isConvertibleTo(QualType To) {
+return ::testing::MakeMatcher(new ConvertibleToMatcher(ASTCtx(), To));
+  }
+};
+
+TEST_F(ExpectedTypeConversionTest, BasicTypes) {
+  build(R"cpp(
+// ints.
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+
+// floats.
+float f;
+double d;
+
+// pointers
+int* iptr;
+bool* bptr;
+
+// user-defined types.
+struct X {};
+X user_type;
+  )cpp");
+
+  const ValueDecl *Ints[] = {decl("b"), decl("i"), decl("ui"), decl("ll")};
+  const ValueDecl *Floats[] = {decl("f"), decl("d")};
+  const ValueDecl *IntPtr = decl("iptr");
+  const ValueDecl *BoolPtr = decl("bptr");
+  const ValueDecl *UserType = decl("user_type");
+
+  // Split all decls into equivalence groups. Decls inside the same equivalence
+  // group contain items that are convertible between each other.
+  llvm::ArrayRef EquivGroups[] = {
+  Ints, Floats, llvm::makeArrayRef(IntPtr), llvm::makeArrayRef(BoolPtr),
+  llvm::makeArrayRef(UserType)};
+
+  // Checks decls inside the same equivalence class can convert to each other.
+  for (auto Group : EquivGroups) {
+for (auto Decl : Group) {
+  for (auto OtherDecl : Group)
+EXPECT_THAT(Decl, isConvertibleTo(OtherDecl->getType()));
+}
+  }
+
+  // Checks items from different equivalence classes are not convertible between
+  // each other.
+  for (auto Group : EquivGroups) {
+for (auto OtherGroup : EquivGroups) {
+  if (Group.data() == OtherGroup.data())
+continue;
+
+  for (auto Decl1 : Group) {
+for (auto Decl2 : OtherGroup) {
+  if (Decl1 == Decl2)
+continue;
+  EXPECT_THAT(Decl1, Not(isConvertibleTo(Decl2->getType(;
+}
+  }
+}
+  }
+}
+
+TEST_F(ExpectedTypeConversionTest, FunctionReturns) {
+  build(R"cpp(
+ int returns_int();
+ int* returns_ptr();
+
+ int int_;
+ 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-24 Thread Sam McCall via Phabricator via cfe-commits
sammccall added a comment.

Happy to speculate about what might work here, but I strongly believe the path 
forward here is to build the simplest version of this feature, without 
conversions, and try to avoid complicated conversion logic if we can get most 
of the benefit in simpler ways.

In https://reviews.llvm.org/D52273#1243652, @ilya-biryukov wrote:

> > This seems very clever, but extremely complicated - you've implemented much 
> > of C++'s conversion logic, it's not clear to me which parts are actually 
> > necessary to completion quality.
>
> Clearly the model that supports C++ conversions is something that **will** 
> improve code completion quality.


It's not clear that will be significant. This isn't hard to measure, so I'm not 
sure why we should guess. And I'm not sure why it all has to go in the first 
patch.

> I do agree it's not trivial, but would argue we at least want:
> 
> - qualification conversions (i.e. adding const)

Another approach here is just always dropping const. (And refs, and so on). 
This will create some false positives, but maybe they don't hurt much. This 
handles some true cases too, like invoking copy constructors.

> - user-defined conversions (e.g. operator bool is commonly useful think)

My **guess** is you're not going to measure a difference here, bool has lots of 
false positives and others are rare.

> - derived-to-base conversions (Derived* should convert to Base*)

Yes, probably. If this ends up being the only "chain" we have to follow, we're 
probably in good shape complexity-wise.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-24 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added inline comments.



Comment at: clangd/ExpectedTypes.h:68
+
+/// Represents a type of partially applied conversion. Should be treated as an
+/// opaque value and can only be used to check whether the types are converible

sammccall wrote:
> this represents a type (in the c++ sense), not a conversion, right?
It's an "expression" with an extra data with some extra data (whether the user 
conversion was applied to get this expression)



Comment at: clangd/ExpectedTypes.h:82
+  static llvm::SmallVector
+  fromCompletionResult(ASTContext , const CodeCompletionResult );
+

sammccall wrote:
> coupling to CompletionResult seems premature here, can we stick to passing 
> getExpectedType() until we know that abstraction needs to be broken?
There's some useful logic that is tied to completion results, e.g. to extract 
function return type `CompletionResult`.
Happy to accept a decl, but would keep the name `fromCompletionResult`. Does 
that LG?



Comment at: clangd/ExpectedTypes.h:213
+
+void collectConvertibleFrom(ASTContext , MockExpr Source,
+llvm::function_ref OutF);

sammccall wrote:
> sammccall wrote:
> > why is implementing one of these directions not enough?
> > 
> > It should probably be:
> > As far as I can tell, derived-to-base is the tricky one here: it's an 
> > important conversion (albeit one we should leave out of the first patch), 
> > and you can't ask "what's convertible to base" since the answer is an open 
> > set you can't see.
> > 
> > So it seems the minimal set you need for handling pointer to base is `Type 
> > getRepresentative(Type)` and `set 
> > getRepresentativesAfterConversion(Type)` or so...
> names are unclear: is `collectConvertibleFrom(T)` the convertible-from types 
> for T (i.e the types T is convertible from), or the types that are 
> convertible from T?
Derived-to-base and user conversions.

We can't enumerate all derived classes for some type, so instead need to 
enumerate all bases when adding a symbol to the index.
We can't enumerate all types that have user-defined conversions to some type T, 
so we need to enumerate all user-defined conversions when adding a symbol 
instead.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-24 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added a comment.

> This seems very clever, but extremely complicated - you've implemented much 
> of C++'s conversion logic, it's not clear to me which parts are actually 
> necessary to completion quality.

Clearly the model that supports C++ conversions is something that **will** 
improve code completion quality.
I do agree it's not trivial, but would argue we at least want:

- qualification conversions (i.e. adding const)
- user-defined conversions (e.g. operator bool is commonly useful think)
- derived-to-base conversions (Derived* should convert to Base*)

Without those, we don't support a bunch of useful cases.

> As chatted offline, I think the return type can be split into multiple 
> orthogonal signals. For example, const T & can be split into 3 independent 
> signals {const, type T, reference}. I think this can make the reasoning of 
> boosting/scoring easier for both index and code completion. Agree with Sam 
> that we should start with something simple (e.g. type matching without 
> conversing) and land basic components to make further evaluation possible.

Yeah, I do keep it in mind and I think it's a great idea. E.g., we can put all 
numeric types into one equivalence class and get rid of all numeric conversions.
That adds some complexity to the interface, though, I wanted to measure how the 
trivial solution (enumerate all types) works. To make sure we actually can't 
get away without it.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-21 Thread Eric Liu via Phabricator via cfe-commits
ioeric added a comment.

In https://reviews.llvm.org/D52273#1241281, @sammccall wrote:

> This seems very clever, but extremely complicated - you've implemented much 
> of C++'s conversion logic, it's not clear to me which parts are actually 
> necessary to completion quality.
>  (Honestly this applies to expected types overall - it seems intuitively 
> likely that it's a good signal, it seems less obvious that it pulls its 
> weight if it can't be made simple).
>
> From the outside it seems much of it is YAGNI, and if we do then we need to 
> build it up slowly with an eye for maintainability.
>  Can we start with expected type boosting (no conversions) as previously 
> discussed, and later measure which other parts make a difference? (I think 
> we'll need/want the simple model anyway, for this to work with Dex and other 
> token-based indexes).


+1 to a simpler model.

As chatted offline, I think the return type can be split into multiple 
orthogonal signals. For example, `const T &` can be split into 3 independent 
signals {`const`, `type T`, `reference`}. I think this can make the reasoning 
of boosting/scoring easier for both index and code completion. Agree with Sam 
that we should start with something simple (e.g. type matching without 
conversing) and land basic components to make further evaluation possible.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-20 Thread Sam McCall via Phabricator via cfe-commits
sammccall added a comment.

This seems very clever, but extremely complicated - you've implemented much of 
C++'s conversion logic, it's not clear to me which parts are actually necessary 
to completion quality.
(Honestly this applies to expected types overall - it seems intuitively likely 
that it's a good signal, it seems less obvious that it pulls its weight if it 
can't be made simple).

From the outside it seems much of it is YAGNI, and if we do then we need to 
build it up slowly with an eye for maintainability.
Can we start with expected type boosting (no conversions) as previously 
discussed, and later measure which other parts make a difference? (I think 
we'll need/want the simple model anyway, for this to work with Dex and other 
token-based indexes).




Comment at: clangd/ExpectedTypes.h:66
+using SHA1Array = std::array;
+SHA1Array computeSHA1(llvm::StringRef Input);
+

While a hash of a string might be a reasonable choice in the long term, I worry 
about debuggability. (With SymbolID we can just look up the symbol).

You could make the hashing an implementation detail of the index, and have the 
APIs speak in terms of opaque strings. But that forces the index to be able to 
report the full opaque string of each returned symbol (for scoring), so the 
index now has to have a lookup table... messy.

Another fun thing about this representation is that you're storing 20 bytes of 
data (+ overhead) for common types like "void" where we could get away with one.



Comment at: clangd/ExpectedTypes.h:66
+using SHA1Array = std::array;
+SHA1Array computeSHA1(llvm::StringRef Input);
+

sammccall wrote:
> While a hash of a string might be a reasonable choice in the long term, I 
> worry about debuggability. (With SymbolID we can just look up the symbol).
> 
> You could make the hashing an implementation detail of the index, and have 
> the APIs speak in terms of opaque strings. But that forces the index to be 
> able to report the full opaque string of each returned symbol (for scoring), 
> so the index now has to have a lookup table... messy.
> 
> Another fun thing about this representation is that you're storing 20 bytes 
> of data (+ overhead) for common types like "void" where we could get away 
> with one.
in the *short run* I'd suggest just printing the type name and using that as 
the representation.
I'm happy to (eventually) learn about the semantics of USRs in types, but not 
today :-)



Comment at: clangd/ExpectedTypes.h:68
+
+/// Represents a type of partially applied conversion. Should be treated as an
+/// opaque value and can only be used to check whether the types are converible

this represents a type (in the c++ sense), not a conversion, right?



Comment at: clangd/ExpectedTypes.h:69
+/// Represents a type of partially applied conversion. Should be treated as an
+/// opaque value and can only be used to check whether the types are converible
+/// between each other (by using the equality operator).

"convertible (using equality)" is confusing.

It sounds like "this is actually an equivalence class of types" but I think 
that's not true, because it's not symmetric.

Isn't the model here just "SType is a serializable token representing a type. 
They can be compared for equality."



Comment at: clangd/ExpectedTypes.h:72
+/// Representation is fixed-size, small and cheap to copy.
+class SType {
+public:

Is this a placeholder name? It's not clear what it means.

Suggest OpaqueType or ExpectedType



Comment at: clangd/ExpectedTypes.h:81
+  /// implementation attempts to store as little types as possible.
+  static llvm::SmallVector
+  fromCompletionResult(ASTContext , const CodeCompletionResult );

can we separate "get the representative set of types for R" from "encode them 
as SType"?
Seems like the APIs would be easier to test and understand.

(I think at least the former should be a non-member function BTW, to keep clear 
that SType itself isn't aware of any clever folding or whatnot)



Comment at: clangd/ExpectedTypes.h:82
+  static llvm::SmallVector
+  fromCompletionResult(ASTContext , const CodeCompletionResult );
+

coupling to CompletionResult seems premature here, can we stick to passing 
getExpectedType() until we know that abstraction needs to be broken?



Comment at: clangd/ExpectedTypes.h:91
+  ///
+  /// The result is a map from a type to a multiplier (>= 1) that denotes the
+  /// quality of conversion that had to be applied (better conversion receive

I don't understand the scale here. If better conversions get higher numbers, 
what number does "no conversion" get?
The code looks like worse conversions get higher numbers.
I'd suggest using an additive penalty to avoid confusion with scores, but 
really...

this all 

[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-19 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added inline comments.



Comment at: clangd/ExpectedTypes.h:119
+  explicit SType(SHA1Array Data);
+  SHA1Array Data;
+};

I assume this will be controversial. Happy to discuss/change.
We are currently building this representation based on USRs for types, the 
alternative is to store the USRs directly. Would be a bit more 
debuggable/explainable in case of failures, but also not particularly readable.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-19 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added a comment.

The implementation might look a bit scary, please feel free to ask for 
comments/clarifications!


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273



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


[PATCH] D52273: [clangd] Initial implementation of expected types

2018-09-19 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov created this revision.
ilya-biryukov added reviewers: sammccall, ioeric.
Herald added subscribers: kadircet, arphaman, jkorous, MaskRay, mgorny.

Provides facilities to model the C++ conversion rules without the AST.
The introduced representation can be stored in the index and used to
implement type-based ranking improvements for index-based completions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52273

Files:
  clangd/CMakeLists.txt
  clangd/ExpectedTypes.cpp
  clangd/ExpectedTypes.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ExpectedTypeTest.cpp

Index: unittests/clangd/ExpectedTypeTest.cpp
===
--- /dev/null
+++ unittests/clangd/ExpectedTypeTest.cpp
@@ -0,0 +1,475 @@
+//===-- SimpleTypeTests.cpp  *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "ClangdUnit.h"
+#include "ExpectedTypes.h"
+#include "TestTU.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using detail::MockExpr;
+using detail::PartialConv;
+using detail::ValueCategory;
+
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::UnorderedElementsAre;
+using ::testing::UnorderedElementsAreArray;
+
+class ASTTest : public ::testing::Test {
+protected:
+  void build(llvm::StringRef Code) {
+assert(!AST && "AST built twice");
+AST = TestTU::withCode(Code).build();
+  }
+
+  const ValueDecl *decl(llvm::StringRef Name) {
+return ::cast(findDecl(*AST, Name));
+  }
+
+  QualType typeOf(llvm::StringRef Name) {
+return decl(Name)->getType().getCanonicalType();
+  }
+
+  ASTContext () { return AST->getASTContext(); }
+
+private:
+  // Set after calling build().
+  llvm::Optional AST;
+};
+
+class ExpectedTypeCollectorTest : public ASTTest {
+protected:
+  std::vector convertibleTo(QualType To) {
+std::vector Result;
+detail::collectConvertibleTo(ASTCtx(), To,
+ [&](PartialConv C) { Result.push_back(C); });
+return Result;
+  }
+
+  std::vector convertibleFrom(const NamedDecl *D) {
+std::vector Result;
+detail::collectConvertibleFrom(
+ASTCtx(),
+*MockExpr::forCompletion(CodeCompletionResult(D, CCP_Declaration)),
+[&](PartialConv C) { Result.push_back(C); });
+return Result;
+  }
+};
+
+// Matchers for l-values and r-values, which don't come from user-defined
+// conversions.
+MATCHER_P(lv, TypeStr, "") {
+  return arg.Cat == ValueCategory::LVal && arg.Type.getAsString() == TypeStr;
+}
+MATCHER_P(rv, TypeStr, "") {
+  return arg.Cat == ValueCategory::RVal && arg.Type.getAsString() == TypeStr;
+}
+
+Matcher converted(Matcher M) {
+  return AllOf(M, Field(::AfterUserConv, true));
+}
+
+std::vector>
+alsoConverted(std::vector> Matchers) {
+  std::vector> Result;
+  Result.reserve(Matchers.size() * 2);
+  for (auto M : Matchers) {
+Result.push_back(M);
+Result.push_back(converted(M));
+  }
+  return Result;
+}
+
+template 
+Matcher> stdConversions(StrT... TypeStrs) {
+  return UnorderedElementsAreArray(
+  alsoConverted({lv(TypeStrs)..., rv(TypeStrs)...}));
+}
+
+std::vector> concat(std::vector> L,
+ std::vector> R) {
+  L.reserve(L.size() + R.size());
+  L.insert(L.end(), R.begin(), R.end());
+  return L;
+}
+
+TEST_F(ExpectedTypeCollectorTest, NumericTypes) {
+  build(R"cpp(
+bool b;
+int i;
+unsigned int ui;
+long long ll;
+float f;
+double d;
+  )cpp");
+
+  EXPECT_THAT(convertibleTo(decl("i")->getType()),
+  stdConversions("int", "float", "const int", "const float"));
+  EXPECT_THAT(convertibleFrom(decl("i")), UnorderedElementsAre(lv("int")));
+
+  const ValueDecl *Ints[] = {decl("b"), decl("ui"), decl("ll")};
+  for (const auto *D : Ints) {
+std::string DType = D->getType().getAsString();
+EXPECT_THAT(
+convertibleTo(D->getType()),
+stdConversions(DType, "int", "float", "const int", "const float"));
+EXPECT_THAT(convertibleFrom(D), UnorderedElementsAre(lv(DType), rv("int")));
+  }
+
+  // Check float.
+  EXPECT_THAT(convertibleTo(decl("f")->getType()),
+  stdConversions("int", "float", "const int", "const float"));
+  EXPECT_THAT(convertibleFrom(decl("f")), UnorderedElementsAre(lv("float")));
+  // Check double.
+  EXPECT_THAT(
+  convertibleTo(decl("d")->getType()),
+  stdConversions("int", "double", "float", "const int", "const float"));
+