blob: d109c790d73ff0046567181ad44b5ed0e072a903 [file] [log] [blame]
//===-- ExpectedTypeTest.cpp -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "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 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<ValueDecl>(findDecl(*AST, Name));
}
QualType typeOf(llvm::StringRef Name) {
return decl(Name)->getType().getCanonicalType();
}
/// An overload for convenience.
llvm::Optional<OpaqueType> 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<std::string>;
Matcher<std::map<std::string, EquivClass>>
ClassesAre(llvm::ArrayRef<EquivClass> Classes) {
using MapEntry = std::map<std::string, EquivClass>::value_type;
std::vector<Matcher<MapEntry>> Elements;
Elements.reserve(Classes.size());
for (auto &Cls : Classes)
Elements.push_back(Field(&MapEntry::second, Cls));
return UnorderedElementsAreArray(Elements);
}
// Groups \p Decls into equivalence classes based on the result of
// 'OpaqueType::fromCompletionResult'.
std::map<std::string, EquivClass>
buildEquivClasses(llvm::ArrayRef<llvm::StringRef> DeclNames) {
std::map<std::string, EquivClass> Classes;
for (llvm::StringRef Name : DeclNames) {
auto Type = OpaqueType::fromType(ASTCtx(), typeOf(Name));
Classes[Type->raw()].insert(Name);
}
return Classes;
}
ASTContext &ASTCtx() { return AST->getASTContext(); }
private:
// Set after calling build().
llvm::Optional<ParsedAST> 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 (&arr_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