mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-21 05:16:24 +00:00

Without this patch, clang will not wrap in an ElaboratedType node types written without a keyword and nested name qualifier, which goes against the intent that we should produce an AST which retains enough details to recover how things are written. The lack of this sugar is incompatible with the intent of the type printer default policy, which is to print types as written, but to fall back and print them fully qualified when they are desugared. An ElaboratedTypeLoc without keyword / NNS uses no storage by itself, but still requires pointer alignment due to pre-existing bug in the TypeLoc buffer handling. Signed-off-by: Matheus Izvekov <mizvekov@gmail.com> Differential Revision: https://reviews.llvm.org/D112374
497 lines
13 KiB
C++
497 lines
13 KiB
C++
//=== unittests/Sema/CodeCompleteTest.cpp - Code Complete tests ==============//
|
|
//
|
|
// 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 "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Parse/ParseAST.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/SemaDiagnostic.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "llvm/Testing/Support/Annotations.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <cstddef>
|
|
#include <string>
|
|
|
|
namespace {
|
|
|
|
using namespace clang;
|
|
using namespace clang::tooling;
|
|
using ::testing::Each;
|
|
using ::testing::UnorderedElementsAre;
|
|
|
|
const char TestCCName[] = "test.cc";
|
|
|
|
struct CompletionContext {
|
|
std::vector<std::string> VisitedNamespaces;
|
|
std::string PreferredType;
|
|
// String representation of std::ptrdiff_t on a given platform. This is a hack
|
|
// to properly account for different configurations of clang.
|
|
std::string PtrDiffType;
|
|
};
|
|
|
|
class VisitedContextFinder : public CodeCompleteConsumer {
|
|
public:
|
|
VisitedContextFinder(CompletionContext &ResultCtx)
|
|
: CodeCompleteConsumer(/*CodeCompleteOpts=*/{}), ResultCtx(ResultCtx),
|
|
CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {}
|
|
|
|
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
|
CodeCompletionResult *Results,
|
|
unsigned NumResults) override {
|
|
ResultCtx.VisitedNamespaces =
|
|
getVisitedNamespace(Context.getVisitedContexts());
|
|
ResultCtx.PreferredType = Context.getPreferredType().getAsString();
|
|
ResultCtx.PtrDiffType =
|
|
S.getASTContext().getPointerDiffType().getAsString();
|
|
}
|
|
|
|
CodeCompletionAllocator &getAllocator() override {
|
|
return CCTUInfo.getAllocator();
|
|
}
|
|
|
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
|
|
|
private:
|
|
std::vector<std::string> getVisitedNamespace(
|
|
CodeCompletionContext::VisitedContextSet VisitedContexts) const {
|
|
std::vector<std::string> NSNames;
|
|
for (const auto *Context : VisitedContexts)
|
|
if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context))
|
|
NSNames.push_back(NS->getQualifiedNameAsString());
|
|
return NSNames;
|
|
}
|
|
|
|
CompletionContext &ResultCtx;
|
|
CodeCompletionTUInfo CCTUInfo;
|
|
};
|
|
|
|
class CodeCompleteAction : public SyntaxOnlyAction {
|
|
public:
|
|
CodeCompleteAction(ParsedSourceLocation P, CompletionContext &ResultCtx)
|
|
: CompletePosition(std::move(P)), ResultCtx(ResultCtx) {}
|
|
|
|
bool BeginInvocation(CompilerInstance &CI) override {
|
|
CI.getFrontendOpts().CodeCompletionAt = CompletePosition;
|
|
CI.setCodeCompletionConsumer(new VisitedContextFinder(ResultCtx));
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
// 1-based code complete position <Line, Col>;
|
|
ParsedSourceLocation CompletePosition;
|
|
CompletionContext &ResultCtx;
|
|
};
|
|
|
|
ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) {
|
|
Offset = std::min(Code.size(), Offset);
|
|
StringRef Before = Code.substr(0, Offset);
|
|
int Lines = Before.count('\n');
|
|
size_t PrevNL = Before.rfind('\n');
|
|
size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1);
|
|
return {TestCCName, static_cast<unsigned>(Lines + 1),
|
|
static_cast<unsigned>(Offset - StartOfLine + 1)};
|
|
}
|
|
|
|
CompletionContext runCompletion(StringRef Code, size_t Offset) {
|
|
CompletionContext ResultCtx;
|
|
clang::tooling::runToolOnCodeWithArgs(
|
|
std::make_unique<CodeCompleteAction>(offsetToPosition(Code, Offset),
|
|
ResultCtx),
|
|
Code, {"-std=c++11"}, TestCCName);
|
|
return ResultCtx;
|
|
}
|
|
|
|
CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) {
|
|
llvm::Annotations A(AnnotatedCode);
|
|
return runCompletion(A.code(), A.point());
|
|
}
|
|
|
|
std::vector<std::string>
|
|
collectPreferredTypes(StringRef AnnotatedCode,
|
|
std::string *PtrDiffType = nullptr) {
|
|
llvm::Annotations A(AnnotatedCode);
|
|
std::vector<std::string> Types;
|
|
for (size_t Point : A.points()) {
|
|
auto Results = runCompletion(A.code(), Point);
|
|
if (PtrDiffType) {
|
|
assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType);
|
|
*PtrDiffType = Results.PtrDiffType;
|
|
}
|
|
Types.push_back(Results.PreferredType);
|
|
}
|
|
return Types;
|
|
}
|
|
|
|
TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) {
|
|
auto VisitedNS = runCodeCompleteOnCode(R"cpp(
|
|
namespace ns1 {}
|
|
namespace ns2 {}
|
|
namespace ns3 {}
|
|
namespace ns3 { namespace nns3 {} }
|
|
|
|
namespace foo {
|
|
using namespace ns1;
|
|
namespace ns4 {} // not visited
|
|
namespace { using namespace ns2; }
|
|
inline namespace bar { using namespace ns3::nns3; }
|
|
} // foo
|
|
namespace ns { foo::^ }
|
|
)cpp")
|
|
.VisitedNamespaces;
|
|
EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3",
|
|
"foo::(anonymous)"));
|
|
}
|
|
|
|
TEST(SemaCodeCompleteTest, VisitedNSForInvalidQualifiedId) {
|
|
auto VisitedNS = runCodeCompleteOnCode(R"cpp(
|
|
namespace na {}
|
|
namespace ns1 {
|
|
using namespace na;
|
|
foo::^
|
|
}
|
|
)cpp")
|
|
.VisitedNamespaces;
|
|
EXPECT_THAT(VisitedNS, UnorderedElementsAre("ns1", "na"));
|
|
}
|
|
|
|
TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) {
|
|
auto VisitedNS = runCodeCompleteOnCode(R"cpp(
|
|
namespace n1 {
|
|
namespace n2 {
|
|
void f(^) {}
|
|
}
|
|
}
|
|
)cpp")
|
|
.VisitedNamespaces;
|
|
EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, BinaryExpr) {
|
|
// Check various operations for arithmetic types.
|
|
StringRef Code = R"cpp(
|
|
void test(int x) {
|
|
x = ^10;
|
|
x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;
|
|
x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;
|
|
})cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
|
|
|
|
Code = R"cpp(
|
|
void test(float x) {
|
|
x = ^10;
|
|
x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;
|
|
x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;
|
|
})cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("float"));
|
|
|
|
// Pointer types.
|
|
Code = R"cpp(
|
|
void test(int *ptr) {
|
|
ptr - ^ptr;
|
|
ptr = ^ptr;
|
|
})cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
|
|
|
|
Code = R"cpp(
|
|
void test(int *ptr) {
|
|
ptr + ^10;
|
|
ptr += ^10;
|
|
ptr -= ^10;
|
|
})cpp";
|
|
{
|
|
std::string PtrDiff;
|
|
auto Types = collectPreferredTypes(Code, &PtrDiff);
|
|
EXPECT_THAT(Types, Each(PtrDiff));
|
|
}
|
|
|
|
// Comparison operators.
|
|
Code = R"cpp(
|
|
void test(int i) {
|
|
i <= ^1; i < ^1; i >= ^1; i > ^1; i == ^1; i != ^1;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
|
|
|
|
Code = R"cpp(
|
|
void test(int *ptr) {
|
|
ptr <= ^ptr; ptr < ^ptr; ptr >= ^ptr; ptr > ^ptr;
|
|
ptr == ^ptr; ptr != ^ptr;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
|
|
|
|
// Relational operations.
|
|
Code = R"cpp(
|
|
void test(int i, int *ptr) {
|
|
i && ^1; i || ^1;
|
|
ptr && ^1; ptr || ^1;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
|
|
|
|
// Bitwise operations.
|
|
Code = R"cpp(
|
|
void test(long long ll) {
|
|
ll | ^1; ll & ^1;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));
|
|
|
|
Code = R"cpp(
|
|
enum A {};
|
|
void test(A a) {
|
|
a | ^1; a & ^1;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("A"));
|
|
|
|
Code = R"cpp(
|
|
enum class A {};
|
|
void test(A a) {
|
|
// This is technically illegal with the 'enum class' without overloaded
|
|
// operators, but we pretend it's fine.
|
|
a | ^a; a & ^a;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("A"));
|
|
|
|
// Binary shifts.
|
|
Code = R"cpp(
|
|
void test(int i, long long ll) {
|
|
i << ^1; ll << ^1;
|
|
i <<= ^1; i <<= ^1;
|
|
i >> ^1; ll >> ^1;
|
|
i >>= ^1; i >>= ^1;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
|
|
|
|
// Comma does not provide any useful information.
|
|
Code = R"cpp(
|
|
class Cls {};
|
|
void test(int i, int* ptr, Cls x) {
|
|
(i, ^i);
|
|
(ptr, ^ptr);
|
|
(x, ^x);
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
|
|
|
|
// User-defined types do not take operator overloading into account.
|
|
// However, they provide heuristics for some common cases.
|
|
Code = R"cpp(
|
|
class Cls {};
|
|
void test(Cls c) {
|
|
// we assume arithmetic and comparions ops take the same type.
|
|
c + ^c; c - ^c; c * ^c; c / ^c; c % ^c;
|
|
c == ^c; c != ^c; c < ^c; c <= ^c; c > ^c; c >= ^c;
|
|
// same for the assignments.
|
|
c = ^c; c += ^c; c -= ^c; c *= ^c; c /= ^c; c %= ^c;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("Cls"));
|
|
|
|
Code = R"cpp(
|
|
class Cls {};
|
|
void test(Cls c) {
|
|
// we assume relational ops operate on bools.
|
|
c && ^c; c || ^c;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
|
|
|
|
Code = R"cpp(
|
|
class Cls {};
|
|
void test(Cls c) {
|
|
// we make no assumptions about the following operators, since they are
|
|
// often overloaded with a non-standard meaning.
|
|
c << ^c; c >> ^c; c | ^c; c & ^c;
|
|
c <<= ^c; c >>= ^c; c |= ^c; c &= ^c;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, Members) {
|
|
StringRef Code = R"cpp(
|
|
struct vector {
|
|
int *begin();
|
|
vector clone();
|
|
};
|
|
|
|
void test(int *a) {
|
|
a = ^vector().^clone().^begin();
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, Conditions) {
|
|
StringRef Code = R"cpp(
|
|
struct vector {
|
|
bool empty();
|
|
};
|
|
|
|
void test() {
|
|
if (^vector().^empty()) {}
|
|
while (^vector().^empty()) {}
|
|
for (; ^vector().^empty();) {}
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, InitAndAssignment) {
|
|
StringRef Code = R"cpp(
|
|
struct vector {
|
|
int* begin();
|
|
};
|
|
|
|
void test() {
|
|
const int* x = ^vector().^begin();
|
|
x = ^vector().^begin();
|
|
|
|
if (const int* y = ^vector().^begin()) {}
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, UnaryExprs) {
|
|
StringRef Code = R"cpp(
|
|
void test(long long a) {
|
|
a = +^a;
|
|
a = -^a
|
|
a = ++^a;
|
|
a = --^a;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));
|
|
|
|
Code = R"cpp(
|
|
void test(int a, int *ptr) {
|
|
!^a;
|
|
!^ptr;
|
|
!!!^a;
|
|
|
|
a = !^a;
|
|
a = !^ptr;
|
|
a = !!!^a;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
|
|
|
|
Code = R"cpp(
|
|
void test(int a) {
|
|
const int* x = &^a;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("const int"));
|
|
|
|
Code = R"cpp(
|
|
void test(int *a) {
|
|
int x = *^a;
|
|
int &r = *^a;
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
|
|
|
|
Code = R"cpp(
|
|
void test(int a) {
|
|
*^a;
|
|
&^a;
|
|
}
|
|
|
|
)cpp";
|
|
}
|
|
|
|
TEST(PreferredTypeTest, ParenExpr) {
|
|
StringRef Code = R"cpp(
|
|
const int *i = ^(^(^(^10)));
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, FunctionArguments) {
|
|
StringRef Code = R"cpp(
|
|
void foo(const int*);
|
|
|
|
void bar(const int*);
|
|
void bar(const int*, int b);
|
|
|
|
struct vector {
|
|
const int *data();
|
|
};
|
|
void test() {
|
|
foo(^(^(^(^vec^tor^().^da^ta^()))));
|
|
bar(^(^(^(^vec^tor^().^da^ta^()))));
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
|
|
|
|
Code = R"cpp(
|
|
void bar(int, volatile double *);
|
|
void bar(int, volatile double *, int, int);
|
|
|
|
struct vector {
|
|
double *data();
|
|
};
|
|
|
|
struct class_members {
|
|
void bar(int, volatile double *);
|
|
void bar(int, volatile double *, int, int);
|
|
};
|
|
void test() {
|
|
bar(10, ^(^(^(^vec^tor^().^da^ta^()))));
|
|
class_members().bar(10, ^(^(^(^vec^tor^().^da^ta^()))));
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("volatile double *"));
|
|
|
|
Code = R"cpp(
|
|
namespace ns {
|
|
struct vector {
|
|
};
|
|
}
|
|
void accepts_vector(ns::vector);
|
|
|
|
void test() {
|
|
accepts_vector(^::^ns::^vector());
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("ns::vector"));
|
|
|
|
Code = R"cpp(
|
|
template <class T>
|
|
struct vector { using self = vector; };
|
|
|
|
void accepts_vector(vector<int>);
|
|
int foo(int);
|
|
|
|
void test() {
|
|
accepts_vector(^::^vector<decltype(foo(1))>::^self);
|
|
}
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("vector<int>"));
|
|
}
|
|
|
|
TEST(PreferredTypeTest, NoCrashOnInvalidTypes) {
|
|
StringRef Code = R"cpp(
|
|
auto x = decltype(&1)(^);
|
|
auto y = new decltype(&1)(^);
|
|
// GNU decimal type extension is not supported in clang.
|
|
auto z = new _Decimal128(^);
|
|
void foo() { (void)(foo)(^); }
|
|
)cpp";
|
|
EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
|
|
}
|
|
|
|
} // namespace
|