[PATCH] D52273: [clangd] Initial implementation of expected types
Sam McCall via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 13 03:17:02 PST 2018
sammccall added inline comments.
================
Comment at: clangd/ExpectedTypes.cpp:12
+
+static llvm::Optional<QualType> toEquivClass(ASTContext &Ctx, 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 &R) {
+ 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<FunctionType>()->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<OpaqueType, set</*decl*/string>>` and writing a `MATCHER_P2(ClassesAre, /*vector<set<string>>*/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
More information about the cfe-commits
mailing list